galaxy-commits
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
March 2014
- 1 participants
- 170 discussions
commit/galaxy-central: jmchilton: Update LWR client through LWR commit 2fec198.
by commits-noreply@bitbucket.org 26 Mar '14
by commits-noreply@bitbucket.org 26 Mar '14
26 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/dc67550cf28a/
Changeset: dc67550cf28a
User: jmchilton
Date: 2014-03-26 16:00:22
Summary: Update LWR client through LWR commit 2fec198.
Makes parsing LWR host more robust - auto-adding http to beginning and trailing / if needed. Also makes LWR runn compatible with forthcoming job metrics pull request and pulls in Python 3 fix for job_script module.
Affected #: 3 files
diff -r 62146f92190c00c621a3ce2ed7860f3f0c2c7ec4 -r dc67550cf28a3e32f8cc867ce82cb8a7b1495335 lib/galaxy/jobs/runners/lwr_client/interface.py
--- a/lib/galaxy/jobs/runners/lwr_client/interface.py
+++ b/lib/galaxy/jobs/runners/lwr_client/interface.py
@@ -36,8 +36,13 @@
def __init__(self, destination_params, transport):
self.transport = transport
- self.remote_host = destination_params.get("url")
- assert self.remote_host is not None, "Failed to determine url for LWR client."
+ remote_host = destination_params.get("url")
+ assert remote_host is not None, "Failed to determine url for LWR client."
+ if not remote_host.endswith("/"):
+ remote_host = "%s/" % remote_host
+ if not remote_host.startswith("http"):
+ remote_host = "http://%s" % remote_host
+ self.remote_host = remote_host
self.private_key = destination_params.get("private_token", None)
def execute(self, command, args={}, data=None, input_path=None, output_path=None):
diff -r 62146f92190c00c621a3ce2ed7860f3f0c2c7ec4 -r dc67550cf28a3e32f8cc867ce82cb8a7b1495335 lib/galaxy/jobs/runners/lwr_client/staging/down.py
--- a/lib/galaxy/jobs/runners/lwr_client/staging/down.py
+++ b/lib/galaxy/jobs/runners/lwr_client/staging/down.py
@@ -14,7 +14,7 @@
# this pattern picks up attiditional files to copy back - such as those
# associated with multiple outputs and metadata configuration. Set to .* to just
# copy everything
-COPY_FROM_WORKING_DIRECTORY_PATTERN = compile(r"primary_.*|galaxy.json|metadata_.*|dataset_\d+\.dat|dataset_\d+_files.+")
+COPY_FROM_WORKING_DIRECTORY_PATTERN = compile(r"primary_.*|galaxy.json|metadata_.*|dataset_\d+\.dat|__instrument_.*|dataset_\d+_files.+")
def finish_job(client, cleanup_job, job_completed_normally, client_outputs, lwr_outputs):
diff -r 62146f92190c00c621a3ce2ed7860f3f0c2c7ec4 -r dc67550cf28a3e32f8cc867ce82cb8a7b1495335 lib/galaxy/jobs/runners/util/job_script/__init__.py
--- a/lib/galaxy/jobs/runners/util/job_script/__init__.py
+++ b/lib/galaxy/jobs/runners/util/job_script/__init__.py
@@ -2,11 +2,11 @@
from pkg_resources import resource_string
DEFAULT_JOB_FILE_TEMPLATE = Template(
- resource_string(__name__, 'DEFAULT_JOB_FILE_TEMPLATE.sh')
+ resource_string(__name__, 'DEFAULT_JOB_FILE_TEMPLATE.sh').decode('UTF-8')
)
SLOTS_STATEMENT_CLUSTER_DEFAULT = \
- resource_string(__name__, 'CLUSTER_SLOTS_STATEMENT.sh')
+ resource_string(__name__, 'CLUSTER_SLOTS_STATEMENT.sh').decode('UTF-8')
SLOTS_STATEMENT_SINGLE = """
GALAXY_SLOTS="1"
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.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/56be13c43a4e/
Changeset: 56be13c43a4e
Branch: stable
User: jmchilton
Date: 2014-03-26 15:24:08
Summary: Fix NoneType exception when using LWR with old-style tool runner URLs.
Affected #: 1 file
diff -r 6007992cbcd3626cbed38b0e883f516b016f98be -r 56be13c43a4e8c1ad391eab212982659e4845d94 lib/galaxy/jobs/runners/lwr.py
--- a/lib/galaxy/jobs/runners/lwr.py
+++ b/lib/galaxy/jobs/runners/lwr.py
@@ -156,7 +156,8 @@
job_id = "%s_%s" % (job_id, job_wrapper.task_id)
params = job_wrapper.job_destination.params.copy()
for key, value in params.iteritems():
- params[key] = model.User.expand_user_properties( job_wrapper.get_job().user, value )
+ if value:
+ params[key] = model.User.expand_user_properties( job_wrapper.get_job().user, value )
return self.get_client( params, job_id )
def get_client_from_state(self, job_state):
https://bitbucket.org/galaxy/galaxy-central/commits/62146f92190c/
Changeset: 62146f92190c
User: jmchilton
Date: 2014-03-26 15:27:44
Summary: Merge latest stable.
Affected #: 1 file
diff -r 66c7385957303447c8f5bb96731945bba179c83a -r 62146f92190c00c621a3ce2ed7860f3f0c2c7ec4 lib/galaxy/jobs/runners/lwr.py
--- a/lib/galaxy/jobs/runners/lwr.py
+++ b/lib/galaxy/jobs/runners/lwr.py
@@ -220,7 +220,8 @@
job_id = "%s_%s" % (job_id, job_wrapper.task_id)
params = job_wrapper.job_destination.params.copy()
for key, value in params.iteritems():
- params[key] = model.User.expand_user_properties( job_wrapper.get_job().user, value )
+ if value:
+ params[key] = model.User.expand_user_properties( job_wrapper.get_job().user, value )
return self.get_client( params, job_id )
def get_client_from_state(self, job_state):
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.
1
0
commit/galaxy-central: davebgx: Add check to ensure that repositories cannot be created unless at least one category has been defined.
by commits-noreply@bitbucket.org 26 Mar '14
by commits-noreply@bitbucket.org 26 Mar '14
26 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/66c738595730/
Changeset: 66c738595730
User: davebgx
Date: 2014-03-26 15:11:54
Summary: Add check to ensure that repositories cannot be created unless at least one category has been defined.
Affected #: 1 file
diff -r 561929fb2d7e827f20ea078904aeebe35173f3f9 -r 66c7385957303447c8f5bb96731945bba179c83a test/tool_shed/functional/test_0000_basic_repository_features.py
--- a/test/tool_shed/functional/test_0000_basic_repository_features.py
+++ b/test/tool_shed/functional/test_0000_basic_repository_features.py
@@ -2,6 +2,8 @@
repository_name = 'filtering_0000'
repository_description = "Galaxy's filtering tool for test 0000"
repository_long_description = "Long description of Galaxy's filtering tool for test 0000"
+import logging
+log = logging.getLogger( __name__ )
class TestBasicRepositoryFeatures( ShedTwillTestCase ):
'''Test core repository features.'''
@@ -24,12 +26,18 @@
assert admin_user is not None, 'Problem retrieving user with email %s from the database' % common.admin_email
admin_user_private_role = self.test_db_util.get_private_role( admin_user )
- def test_0005_create_categories( self ):
+ def test_0005_create_repository_without_categories( self ):
+ '''Verify that a repository cannot be created unless at least one category has been defined.'''
+ strings_displayed = [ 'No categories have been configured in this instance of the Galaxy Tool Shed' ]
+ self.visit_url( '/repository/create_repository' )
+ self.check_for_strings( strings_displayed=strings_displayed, strings_not_displayed=[] )
+
+ def test_0010_create_categories( self ):
"""Create categories for this test suite"""
self.create_category( name='Test 0000 Basic Repository Features 1', description='Test 0000 Basic Repository Features 1' )
self.create_category( name='Test 0000 Basic Repository Features 2', description='Test 0000 Basic Repository Features 2' )
- def test_0010_create_repository( self ):
+ def test_0015_create_repository( self ):
"""Create the filtering repository"""
self.logout()
self.login( email=common.test_user_1_email, username=common.test_user_1_name )
@@ -43,7 +51,7 @@
category_id=self.security.encode_id( category.id ),
strings_displayed=strings_displayed )
- def test_0015_edit_repository( self ):
+ def test_0020_edit_repository( self ):
"""Edit the repository name, description, and long description"""
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
new_name = "renamed_filtering"
@@ -51,20 +59,20 @@
new_long_description = "Edited long description"
self.edit_repository_information( repository, repo_name=new_name, description=new_description, long_description=new_long_description )
- def test_0020_change_repository_category( self ):
+ def test_0025_change_repository_category( self ):
"""Change the categories associated with the filtering repository"""
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.edit_repository_categories( repository,
categories_to_add=[ "Test 0000 Basic Repository Features 2" ],
categories_to_remove=[ "Test 0000 Basic Repository Features 1" ] )
- def test_0025_grant_write_access( self ):
+ def test_0030_grant_write_access( self ):
'''Grant write access to another user'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.grant_write_access( repository, usernames=[ common.test_user_2_name ] )
self.revoke_write_access( repository, common.test_user_2_name )
- def test_0030_upload_filtering_1_1_0( self ):
+ def test_0035_upload_filtering_1_1_0( self ):
"""Upload filtering_1.1.0.tar to the repository"""
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.upload_file( repository,
@@ -77,7 +85,7 @@
strings_displayed=[],
strings_not_displayed=[] )
- def test_0035_verify_repository( self ):
+ def test_0040_verify_repository( self ):
'''Display basic repository pages'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
latest_changeset_revision = self.get_repository_tip( repository )
@@ -101,7 +109,7 @@
repository_name,
strings_displayed=[ 'Uploaded filtering 1.1.0', latest_changeset_revision ] )
- def test_0040_alter_repository_states( self ):
+ def test_0045_alter_repository_states( self ):
'''Test toggling the malicious and deprecated repository flags.'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.logout()
@@ -127,7 +135,7 @@
strings_displayed = [ 'Mark repository as deprecated', 'Upload files', 'Reset all repository metadata' ]
self.display_manage_repository_page( repository, strings_displayed=strings_displayed )
- def test_0045_display_repository_tip_file( self ):
+ def test_0050_display_repository_tip_file( self ):
'''Display the contents of filtering.xml in the repository tip revision'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.display_repository_file_contents( repository=repository,
@@ -136,7 +144,7 @@
strings_displayed=[ '1.1.0' ],
strings_not_displayed=[] )
- def test_0050_upload_filtering_txt_file( self ):
+ def test_0055_upload_filtering_txt_file( self ):
'''Upload filtering.txt file associated with tool version 1.1.0.'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.upload_file( repository,
@@ -150,7 +158,7 @@
strings_not_displayed=[] )
self.display_manage_repository_page( repository, strings_displayed=[ 'Readme file for filtering 1.1.0' ] )
- def test_0055_upload_filtering_test_data( self ):
+ def test_0060_upload_filtering_test_data( self ):
'''Upload filtering test data.'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.upload_file( repository,
@@ -169,7 +177,7 @@
strings_not_displayed=[] )
self.check_repository_metadata( repository, tip_only=True )
- def test_0060_upload_filtering_2_2_0( self ):
+ def test_0065_upload_filtering_2_2_0( self ):
'''Upload filtering version 2.2.0'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.upload_file( repository,
@@ -182,7 +190,7 @@
strings_displayed=[],
strings_not_displayed=[] )
- def test_0065_verify_filtering_repository( self ):
+ def test_0070_verify_filtering_repository( self ):
'''Verify the new tool versions and repository metadata.'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
category = self.test_db_util.get_category_by_name( 'Test 0000 Basic Repository Features 1' )
@@ -204,7 +212,7 @@
tool_page_strings_displayed=tool_page_strings_displayed )
self.check_repository_metadata( repository, tip_only=False )
- def test_0070_upload_readme_txt_file( self ):
+ def test_0075_upload_readme_txt_file( self ):
'''Upload readme.txt file associated with tool version 2.2.0.'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.upload_file( repository,
@@ -223,26 +231,26 @@
strings_displayed=[ 'Readme file for filtering 1.1.0',
'This is a readme file.' ] )
- def test_0075_delete_readme_txt_file( self ):
+ def test_0080_delete_readme_txt_file( self ):
'''Delete the readme.txt file.'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.delete_files_from_repository( repository, filenames=[ 'readme.txt' ] )
self.check_count_of_metadata_revisions_associated_with_repository( repository, metadata_count=2 )
self.display_manage_repository_page( repository, strings_displayed=[ 'Readme file for filtering 1.1.0' ] )
- def test_0080_search_for_valid_filter_tool( self ):
+ def test_0085_search_for_valid_filter_tool( self ):
'''Search for the filtering tool by tool ID, name, and version.'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
tip_changeset = self.get_repository_tip( repository )
search_fields = dict( tool_id='Filter1', tool_name='filter', tool_version='2.2.0' )
self.search_for_valid_tools( search_fields=search_fields, strings_displayed=[ tip_changeset ], strings_not_displayed=[] )
- def test_0085_verify_repository_metadata( self ):
+ def test_0090_verify_repository_metadata( self ):
'''Verify that resetting the metadata does not change it.'''
repository = self.test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.verify_unchanged_repository_metadata( repository )
- def test_0090_verify_reserved_repository_name_handling( self ):
+ def test_0095_verify_reserved_repository_name_handling( self ):
'''Check that reserved repository names are handled correctly.'''
category = self.test_db_util.get_category_by_name( 'Test 0000 Basic Repository Features 1' )
error_message = 'The term <b>repos</b> is a reserved word in the tool shed, so it cannot be used as a repository name.'
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.
1
0
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c4950bc66225/
Changeset: c4950bc66225
User: carlfeberhard
Date: 2014-03-25 20:30:51
Summary: History API: show: add importable, username_and_slug to returned attributes, update: 1) allow setting importable and published, 2) published automatically makes a history importable, 3) in both cases slug is automatically set set_as_current: now correctly uses the HTTP method PUT (not POST), HDA API: all: histories that are importable and/or published now allow GET'ing the contents properly, create: you can now copy HDAs into your histories if the HDA and it's history are accessible
Affected #: 7 files
diff -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 -r c4950bc662256f013916b0aaabb73a47508213c5 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -773,7 +773,7 @@
class History( object, Dictifiable, UsesAnnotations, HasName ):
dict_collection_visible_keys = ( 'id', 'name', 'published', 'deleted' )
- dict_element_visible_keys = ( 'id', 'name', 'published', 'deleted', 'genome_build', 'purged' )
+ dict_element_visible_keys = ( 'id', 'name', 'published', 'deleted', 'genome_build', 'purged', 'importable', 'slug' )
default_name = 'Unnamed history'
def __init__( self, id=None, name=None, user=None ):
diff -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 -r c4950bc662256f013916b0aaabb73a47508213c5 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -187,8 +187,11 @@
def validate_and_sanitize_basestring_list( self, key, val ):
if not isinstance( val, list ):
- raise ValueError( '%s must be a list: %s' %( key, str( type( val ) ) ) )
- return [ unicode( sanitize_html( t, 'utf-8', 'text/html' ), 'utf-8' ) for t in val ]
+ raise ValueError( '%s must be a list of strings: %s' %( key, str( type( val ) ) ) )
+ try:
+ return [ unicode( sanitize_html( t, 'utf-8', 'text/html' ), 'utf-8' ) for t in val ]
+ except TypeError, type_err:
+ raise ValueError( '%s must be a list of strings: %s' %( key, str( type_err ) ) )
def validate_boolean( self, key, val ):
if not isinstance( val, bool ):
@@ -527,6 +530,10 @@
if not history_dict[ 'annotation' ]:
history_dict[ 'annotation' ] = ''
#TODO: item_slug url
+ if history_dict[ 'importable' ] and history_dict[ 'slug' ]:
+ #TODO: this should be in History (or a superclass of)
+ username_and_slug = ( '/' ).join(( 'u', history.user.username, 'h', history_dict[ 'slug' ] ))
+ history_dict[ 'username_and_slug' ] = username_and_slug
hda_summaries = hda_dictionaries if hda_dictionaries else self.get_hda_summary_dicts( trans, history )
#TODO remove the following in v2
@@ -541,26 +548,69 @@
"""
Changes history data using the given dictionary new_data.
"""
- # precondition: access of the history has already been checked
+ #precondition: ownership of the history has already been checked
+ #precondition: user is not None (many of these attributes require a user to set properly)
+ user = trans.get_user()
+ # published histories should always be importable
+ if 'published' in new_data and new_data[ 'published' ] and not history.importable:
+ new_data[ 'importable' ] = True
# send what we can down into the model
changed = history.set_from_dict( new_data )
+
# the rest (often involving the trans) - do here
- if 'annotation' in new_data.keys() and trans.get_user():
- history.add_item_annotation( trans.sa_session, trans.get_user(), history, new_data[ 'annotation' ] )
+ #TODO: the next two could be an aspect/mixin
+ #TODO: also need a way to check whether they've changed - assume they have for now
+ if 'annotation' in new_data:
+ history.add_item_annotation( trans.sa_session, user, history, new_data[ 'annotation' ] )
changed[ 'annotation' ] = new_data[ 'annotation' ]
- if 'tags' in new_data.keys() and trans.get_user():
- self.set_tags_from_list( trans, history, new_data[ 'tags' ], user=trans.user )
- # importable (ctrl.history.set_accessible_async)
- # sharing/permissions?
- # slugs?
- # purged - duh duh duhhhhhhnnnnnnnnnn
+
+ if 'tags' in new_data:
+ self.set_tags_from_list( trans, history, new_data[ 'tags' ], user=user )
+ changed[ 'tags' ] = new_data[ 'tags' ]
+
+ #TODO: sharing with user/permissions?
if changed.keys():
trans.sa_session.flush()
+ # create a slug if none exists (setting importable to false should not remove the slug)
+ if 'importable' in changed and changed[ 'importable' ] and not history.slug:
+ self._create_history_slug( trans, history )
+
return changed
+ def _create_history_slug( self, trans, history ):
+ #TODO: mixins need to die a quick, horrible death
+ # (this is duplicate from SharableMixin which can't be added to UsesHistory without exposing various urls)
+ cur_slug = history.slug
+
+ # Setup slug base.
+ if cur_slug is None or cur_slug == "":
+ # Item can have either a name or a title.
+ item_name = history.name
+ slug_base = util.ready_name_for_url( item_name.lower() )
+ else:
+ slug_base = cur_slug
+
+ # Using slug base, find a slug that is not taken. If slug is taken,
+ # add integer to end.
+ new_slug = slug_base
+ count = 1
+ while ( trans.sa_session.query( trans.app.model.History )
+ .filter_by( user=history.user, slug=new_slug, importable=True )
+ .count() != 0 ):
+ # Slug taken; choose a new slug based on count. This approach can
+ # handle numerous items with the same name gracefully.
+ new_slug = '%s-%i' % ( slug_base, count )
+ count += 1
+
+ # Set slug and return.
+ trans.sa_session.add( history )
+ history.slug = new_slug
+ trans.sa_session.flush()
+ return history.slug == cur_slug
+
class ExportsHistoryMixin:
@@ -685,9 +735,9 @@
else:
#TODO: do we really need the history?
history = self.get_history( trans, history_id,
- check_ownership=True, check_accessible=True, deleted=False )
+ check_ownership=False, check_accessible=True, deleted=False )
hda = self.get_history_dataset_association( trans, history, id,
- check_ownership=True, check_accessible=True )
+ check_ownership=False, check_accessible=True )
return hda
def get_hda_list( self, trans, hda_ids, check_ownership=True, check_accessible=False, check_state=True ):
diff -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 -r c4950bc662256f013916b0aaabb73a47508213c5 lib/galaxy/webapps/galaxy/api/histories.py
--- a/lib/galaxy/webapps/galaxy/api/histories.py
+++ b/lib/galaxy/webapps/galaxy/api/histories.py
@@ -6,20 +6,26 @@
import pkg_resources
pkg_resources.require( "Paste" )
-from paste.httpexceptions import HTTPBadRequest, HTTPForbidden, HTTPInternalServerError, HTTPException
+
+from paste.httpexceptions import HTTPBadRequest
+from paste.httpexceptions import HTTPForbidden
+from paste.httpexceptions import HTTPInternalServerError
+from paste.httpexceptions import HTTPException
from galaxy import exceptions
from galaxy import web
from galaxy.web import _future_expose_api as expose_api
from galaxy.web import _future_expose_api_anonymous as expose_api_anonymous
from galaxy.web import _future_expose_api_raw as expose_api_raw
-from galaxy.util import string_as_bool
-from galaxy.util import restore_text
+
from galaxy.web.base.controller import BaseAPIController
from galaxy.web.base.controller import UsesHistoryMixin
from galaxy.web.base.controller import UsesTagsMixin
from galaxy.web.base.controller import ExportsHistoryMixin
from galaxy.web.base.controller import ImportsHistoryMixin
+
+from galaxy.util import string_as_bool
+from galaxy.util import restore_text
from galaxy.web import url_for
import logging
@@ -48,27 +54,22 @@
#TODO: query (by name, date, etc.)
rval = []
deleted = string_as_bool( deleted )
- try:
- if trans.user:
- histories = self.get_user_histories( trans, user=trans.user, only_deleted=deleted )
- #for history in query:
- for history in histories:
- item = history.to_dict(value_mapper={'id': trans.security.encode_id})
- item['url'] = url_for( 'history', id=trans.security.encode_id( history.id ) )
- rval.append( item )
- elif trans.galaxy_session.current_history:
- #No user, this must be session authentication with an anonymous user.
- history = trans.galaxy_session.current_history
+ if trans.user:
+ histories = self.get_user_histories( trans, user=trans.user, only_deleted=deleted )
+
+ for history in histories:
item = history.to_dict(value_mapper={'id': trans.security.encode_id})
item['url'] = url_for( 'history', id=trans.security.encode_id( history.id ) )
- rval.append(item)
+ rval.append( item )
- except Exception, e:
- raise
- rval = "Error in history API"
- log.error( rval + ": %s" % str(e) )
- trans.response.status = 500
+ elif trans.galaxy_session.current_history:
+ #No user, this must be session authentication with an anonymous user.
+ history = trans.galaxy_session.current_history
+ item = history.to_dict(value_mapper={'id': trans.security.encode_id})
+ item['url'] = url_for( 'history', id=trans.security.encode_id( history.id ) )
+ rval.append(item)
+
return rval
@web.expose_api_anonymous
@@ -92,7 +93,6 @@
:returns: detailed history information from
:func:`galaxy.web.base.controller.UsesHistoryDatasetAssociationMixin.get_history_dict`
"""
- #TODO: GET /api/histories/{encoded_history_id}?as_archive=True
#TODO: GET /api/histories/s/{username}/{slug}
history_id = id
deleted = string_as_bool( deleted )
@@ -116,7 +116,7 @@
except HTTPBadRequest, bad_req:
trans.response.status = 400
- return str( bad_req )
+ raise exceptions.MessageException( bad_req.detail )
except Exception, e:
msg = "Error in history API at showing history detail: %s" % ( str( e ) )
@@ -444,7 +444,7 @@
continue
if key in ( 'name', 'genome_build', 'annotation' ):
validated_payload[ key ] = self.validate_and_sanitize_basestring( key, val )
- if key in ( 'deleted', 'published' ):
+ if key in ( 'deleted', 'published', 'importable' ):
validated_payload[ key ] = self.validate_boolean( key, val )
elif key == 'tags':
validated_payload[ key ] = self.validate_and_sanitize_basestring_list( key, val )
diff -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 -r c4950bc662256f013916b0aaabb73a47508213c5 lib/galaxy/webapps/galaxy/api/history_contents.py
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -2,20 +2,32 @@
API operations on the contents of a history.
"""
-import logging
-from galaxy import exceptions, util, web
-from galaxy.web.base.controller import ( BaseAPIController, url_for,
- UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, UsesLibraryMixin,
- UsesLibraryMixinItems, UsesTagsMixin )
+from galaxy import exceptions
+from galaxy import util
+from galaxy import web
+
+from galaxy.web import _future_expose_api as expose_api
+from galaxy.web import _future_expose_api_anonymous as expose_api_anonymous
+from galaxy.web import _future_expose_api_raw as expose_api_raw
+
+from galaxy.web.base.controller import BaseAPIController
+from galaxy.web.base.controller import UsesHistoryDatasetAssociationMixin
+from galaxy.web.base.controller import UsesHistoryMixin
+from galaxy.web.base.controller import UsesLibraryMixin
+from galaxy.web.base.controller import UsesLibraryMixinItems
+from galaxy.web.base.controller import UsesTagsMixin
+
+from galaxy.web.base.controller import url_for
from galaxy.util.sanitize_html import sanitize_html
+import logging
log = logging.getLogger( __name__ )
class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin,
UsesLibraryMixin, UsesLibraryMixinItems, UsesTagsMixin ):
- @web.expose_api_anonymous
+ @expose_api_anonymous
def index( self, trans, history_id, ids=None, **kwd ):
"""
index( self, trans, history_id, ids=None, **kwd )
@@ -52,7 +64,7 @@
history = trans.history
# otherwise, check permissions for the history first
else:
- history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
+ history = self.get_history( trans, history_id, check_ownership=False, check_accessible=True )
# Allow passing in type or types - for continuity rest of methods
# take in type - but this one can be passed multiple types and
@@ -134,7 +146,7 @@
hda.history_id, hda.id, type( exc ), str( exc ) )
return self.get_hda_dict_with_error( trans, hda=hda, error_msg=str( exc ) )
- @web.expose_api_anonymous
+ @expose_api_anonymous
def show( self, trans, id, history_id, **kwd ):
"""
show( self, trans, id, history_id, **kwd )
@@ -170,7 +182,7 @@
trans.response.status = 500
return msg
- @web.expose_api
+ @expose_api
def create( self, trans, history_id, payload, **kwd ):
"""
create( self, trans, history_id, payload, **kwd )
@@ -201,6 +213,8 @@
# retrieve history
try:
history = self.get_history( trans, history_id, check_ownership=True, check_accessible=False )
+ except exceptions.httpexceptions.HTTPException:
+ raise
except Exception, e:
# no way to tell if it failed bc of perms or other (all MessageExceptions)
trans.response.status = 500
@@ -236,7 +250,8 @@
elif source == 'hda':
try:
#NOTE: user currently only capable of copying one of their own datasets
- hda = self.get_dataset( trans, content )
+ hda = self.get_dataset( trans, content, check_ownership=False, check_accessible=True )
+ self.security_check( trans, hda.history, check_ownership=False, check_accessible=True )
except ( exceptions.httpexceptions.HTTPRequestRangeNotSatisfiable,
exceptions.httpexceptions.HTTPBadRequest ), id_exc:
# wot...
@@ -259,7 +274,7 @@
trans.response.status = 501
return
- @web.expose_api_anonymous
+ @expose_api_anonymous
def update( self, trans, history_id, id, payload, **kwd ):
"""
update( self, trans, history_id, id, payload, **kwd )
@@ -306,6 +321,7 @@
if hda.history != trans.history:
trans.response.status = 401
return { 'error': 'Anonymous users cannot edit datasets outside their current history' }
+
else:
payload = self._validate_and_parse_update_payload( payload )
# only check_state if not deleting, otherwise cannot delete uploading files
@@ -316,6 +332,7 @@
changed = self.set_hda_from_dict( trans, hda, payload )
if payload.get( 'deleted', False ):
self.stop_hda_creating_job( hda )
+
except Exception, exception:
log.error( 'Update of history (%s), HDA (%s) failed: %s',
history_id, id, str( exception ), exc_info=True )
@@ -330,7 +347,7 @@
return changed
#TODO: allow anonymous del/purge and test security on this
- @web.expose_api
+ @expose_api
def delete( self, trans, history_id, id, purge=False, **kwd ):
"""
delete( self, trans, history_id, id, **kwd )
diff -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 -r c4950bc662256f013916b0aaabb73a47508213c5 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -165,8 +165,9 @@
parent_resources=dict( member_name='page', collection_name='pages' ) )
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
+ #webapp.mapper.connect( "set_as_current", "/api/histories/set_as_current/{id}",
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
- controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
+ controller="histories", action="set_as_current", conditions=dict( method=["PUT"] ) )
webapp.mapper.connect( "history_archive_export", "/api/histories/{id}/exports",
controller="histories", action="archive_export", conditions=dict( method=[ "PUT" ] ) )
diff -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 -r c4950bc662256f013916b0aaabb73a47508213c5 static/scripts/mvc/history/current-history-panel.js
--- a/static/scripts/mvc/history/current-history-panel.js
+++ b/static/scripts/mvc/history/current-history-panel.js
@@ -84,7 +84,10 @@
var panel = this,
historyFn = function(){
// make this current and get history data with one call
- return jQuery.post( galaxy_config.root + 'api/histories/' + historyId + '/set_as_current' );
+ return jQuery.ajax({
+ url : galaxy_config.root + 'api/histories/' + historyId + '/set_as_current',
+ method : 'PUT'
+ });
};
return this.loadHistoryWithHDADetails( historyId, attributes, historyFn )
.then(function( historyData, hdaData ){
diff -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 -r c4950bc662256f013916b0aaabb73a47508213c5 static/scripts/packed/mvc/history/current-history-panel.js
--- a/static/scripts/packed/mvc/history/current-history-panel.js
+++ b/static/scripts/packed/mvc/history/current-history-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-edit","mvc/history/history-panel","mvc/base-mvc"],function(b,f,c){var d=c.SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});d.storageKey=function e(){return("history-panel")};var a=f.HistoryPanel.extend({HDAViewClass:b.HDAEditView,emptyMsg:_l("This history is empty. Click 'Get Data' on the left tool menu to start"),noneFoundMsg:_l("No matching datasets found"),initialize:function(g){g=g||{};this.preferences=new d(_.extend({id:d.storageKey()},_.pick(g,_.keys(d.prototype.defaults))));f.HistoryPanel.prototype.initialize.call(this,g)},loadCurrentHistory:function(h){var g=this;return this.loadHistoryWithHDADetails("current",h).then(function(j,i){g.trigger("current-history",g)})},switchToHistory:function(j,i){var g=this,h=function(){return jQuery.post(galaxy_config.root+"api/histories/"+j+"/set_as_current")};return this.loadHistoryWithHDADetails(j,i,h).then(function(l,k){g.trigger("switched-history",g)})},createNewHistory:function(i){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var g=this,h=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,i,h).then(function(k,j){g.trigger("new-history",g)})},setModel:function(h,g,i){f.HistoryPanel.prototype.setModel.call(this,h,g,i);if(this.model){this.log("checking for updates");this.model.checkForUpdates()}return this},_setUpModelEventHandlers:function(){f.HistoryPanel.prototype._setUpModelEventHandlers.call(this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("state:ready",function(h,i,g){if((!h.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[h.id])}},this)},render:function(i,j){i=(i===undefined)?(this.fxSpeed):(i);var g=this,h;if(this.model){h=this.renderModel()}else{h=this.renderWithoutModel()}$(g).queue("fx",[function(k){if(i&&g.$el.is(":visible")){g.$el.fadeOut(i,k)}else{k()}},function(k){g.$el.empty();if(h){g.$el.append(h.children());g.renderBasedOnPrefs()}k()},function(k){if(i&&!g.$el.is(":visible")){g.$el.fadeIn(i,k)}else{k()}},function(k){if(j){j.call(this)}g.trigger("rendered",this);k()}]);return this},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.toggleSearchControls(0,true)}},_renderEmptyMsg:function(i){var h=this,g=h.$emptyMessage(i),j=$(".toolMenuContainer");if((_.isEmpty(h.hdaViews)&&!h.searchFor)&&(Galaxy&&Galaxy.upload&&j.size())){g.empty();g.html([_l("This history is empty. "),_l("You can "),'<a class="uploader-link" href="javascript:void(0)">',_l("load your own data"),"</a>",_l(" or "),'<a class="get-data-link" href="javascript:void(0)">',_l("get data from an external source"),"</a>"].join(""));g.find(".uploader-link").click(function(k){Galaxy.upload._eventShow(k)});g.find(".get-data-link").click(function(k){j.parent().scrollTop(0);j.find('span:contains("Get Data")').click()});g.show()}else{f.HistoryPanel.prototype._renderEmptyMsg.call(this,i)}return this},toggleSearchControls:function(h,g){var i=f.HistoryPanel.prototype.toggleSearchControls.call(this,h,g);this.preferences.set("searching",i)},_renderTags:function(g){var h=this;f.HistoryPanel.prototype._renderTags.call(this,g);if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}this.tagsEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(i){h.preferences.set("tagsEditorShown",i.hidden)})},_renderAnnotation:function(g){var h=this;f.HistoryPanel.prototype._renderAnnotation.call(this,g);if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}this.annotationEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(i){h.preferences.set("annotationEditorShown",i.hidden)})},connectToQuotaMeter:function(g){if(!g){return this}this.listenTo(g,"quota:over",this.showQuotaMessage);this.listenTo(g,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(g&&g.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var g=this.$el.find(".quota-message");if(g.is(":hidden")){g.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var g=this.$el.find(".quota-message");if(!g.is(":hidden")){g.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(g){if(!g){return this}this.on("new-storage",function(i,h){if(g&&i){g.findItemByHtml(_l("Include Deleted Datasets")).checked=i.get("show_deleted");g.findItemByHtml(_l("Include Hidden Datasets")).checked=i.get("show_hidden")}});return this},toString:function(){return"CurrentHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{CurrentHistoryPanel:a}});
\ No newline at end of file
+define(["mvc/dataset/hda-edit","mvc/history/history-panel","mvc/base-mvc"],function(b,f,c){var d=c.SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});d.storageKey=function e(){return("history-panel")};var a=f.HistoryPanel.extend({HDAViewClass:b.HDAEditView,emptyMsg:_l("This history is empty. Click 'Get Data' on the left tool menu to start"),noneFoundMsg:_l("No matching datasets found"),initialize:function(g){g=g||{};this.preferences=new d(_.extend({id:d.storageKey()},_.pick(g,_.keys(d.prototype.defaults))));f.HistoryPanel.prototype.initialize.call(this,g)},loadCurrentHistory:function(h){var g=this;return this.loadHistoryWithHDADetails("current",h).then(function(j,i){g.trigger("current-history",g)})},switchToHistory:function(j,i){var g=this,h=function(){return jQuery.ajax({url:galaxy_config.root+"api/histories/"+j+"/set_as_current",method:"PUT"})};return this.loadHistoryWithHDADetails(j,i,h).then(function(l,k){g.trigger("switched-history",g)})},createNewHistory:function(i){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var g=this,h=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,i,h).then(function(k,j){g.trigger("new-history",g)})},setModel:function(h,g,i){f.HistoryPanel.prototype.setModel.call(this,h,g,i);if(this.model){this.log("checking for updates");this.model.checkForUpdates()}return this},_setUpModelEventHandlers:function(){f.HistoryPanel.prototype._setUpModelEventHandlers.call(this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("state:ready",function(h,i,g){if((!h.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[h.id])}},this)},render:function(i,j){i=(i===undefined)?(this.fxSpeed):(i);var g=this,h;if(this.model){h=this.renderModel()}else{h=this.renderWithoutModel()}$(g).queue("fx",[function(k){if(i&&g.$el.is(":visible")){g.$el.fadeOut(i,k)}else{k()}},function(k){g.$el.empty();if(h){g.$el.append(h.children());g.renderBasedOnPrefs()}k()},function(k){if(i&&!g.$el.is(":visible")){g.$el.fadeIn(i,k)}else{k()}},function(k){if(j){j.call(this)}g.trigger("rendered",this);k()}]);return this},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.toggleSearchControls(0,true)}},_renderEmptyMsg:function(i){var h=this,g=h.$emptyMessage(i),j=$(".toolMenuContainer");if((_.isEmpty(h.hdaViews)&&!h.searchFor)&&(Galaxy&&Galaxy.upload&&j.size())){g.empty();g.html([_l("This history is empty. "),_l("You can "),'<a class="uploader-link" href="javascript:void(0)">',_l("load your own data"),"</a>",_l(" or "),'<a class="get-data-link" href="javascript:void(0)">',_l("get data from an external source"),"</a>"].join(""));g.find(".uploader-link").click(function(k){Galaxy.upload._eventShow(k)});g.find(".get-data-link").click(function(k){j.parent().scrollTop(0);j.find('span:contains("Get Data")').click()});g.show()}else{f.HistoryPanel.prototype._renderEmptyMsg.call(this,i)}return this},toggleSearchControls:function(h,g){var i=f.HistoryPanel.prototype.toggleSearchControls.call(this,h,g);this.preferences.set("searching",i)},_renderTags:function(g){var h=this;f.HistoryPanel.prototype._renderTags.call(this,g);if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}this.tagsEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(i){h.preferences.set("tagsEditorShown",i.hidden)})},_renderAnnotation:function(g){var h=this;f.HistoryPanel.prototype._renderAnnotation.call(this,g);if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}this.annotationEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(i){h.preferences.set("annotationEditorShown",i.hidden)})},connectToQuotaMeter:function(g){if(!g){return this}this.listenTo(g,"quota:over",this.showQuotaMessage);this.listenTo(g,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(g&&g.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var g=this.$el.find(".quota-message");if(g.is(":hidden")){g.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var g=this.$el.find(".quota-message");if(!g.is(":hidden")){g.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(g){if(!g){return this}this.on("new-storage",function(i,h){if(g&&i){g.findItemByHtml(_l("Include Deleted Datasets")).checked=i.get("show_deleted");g.findItemByHtml(_l("Include Hidden Datasets")).checked=i.get("show_hidden")}});return this},toString:function(){return"CurrentHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{CurrentHistoryPanel:a}});
\ No newline at end of file
https://bitbucket.org/galaxy/galaxy-central/commits/79ab14af25e4/
Changeset: 79ab14af25e4
User: carlfeberhard
Date: 2014-03-25 21:59:27
Summary: History/HDA API: begin normalizing exceptions and return codes; Browser testing: update and expand history/hda testing, add importable/published permissions testing, transfer upload functions to api module
Affected #: 8 files
diff -r c4950bc662256f013916b0aaabb73a47508213c5 -r 79ab14af25e4182dac99ecad3918ed653c50f272 lib/galaxy/webapps/galaxy/api/histories.py
--- a/lib/galaxy/webapps/galaxy/api/histories.py
+++ b/lib/galaxy/webapps/galaxy/api/histories.py
@@ -97,32 +97,32 @@
history_id = id
deleted = string_as_bool( deleted )
- try:
- if history_id == "most_recently_used":
- if not trans.user or len( trans.user.galaxy_sessions ) <= 0:
- return None
- # Most recent active history for user sessions, not deleted
- history = trans.user.galaxy_sessions[0].histories[-1].history
+ #try:
+ if history_id == "most_recently_used":
+ if not trans.user or len( trans.user.galaxy_sessions ) <= 0:
+ return None
+ # Most recent active history for user sessions, not deleted
+ history = trans.user.galaxy_sessions[0].histories[-1].history
- elif history_id == "current":
- history = trans.get_history( create=True )
+ elif history_id == "current":
+ history = trans.get_history( create=True )
- else:
- history = self.get_history( trans, history_id, check_ownership=False,
- check_accessible=True, deleted=deleted )
+ else:
+ history = self.get_history( trans, history_id, check_ownership=False,
+ check_accessible=True, deleted=deleted )
- history_data = self.get_history_dict( trans, history )
- history_data[ 'contents_url' ] = url_for( 'history_contents', history_id=history_id )
+ history_data = self.get_history_dict( trans, history )
+ history_data[ 'contents_url' ] = url_for( 'history_contents', history_id=history_id )
- except HTTPBadRequest, bad_req:
- trans.response.status = 400
- raise exceptions.MessageException( bad_req.detail )
-
- except Exception, e:
- msg = "Error in history API at showing history detail: %s" % ( str( e ) )
- log.exception( msg )
- trans.response.status = 500
- return msg
+ #except HTTPBadRequest, bad_req:
+ # trans.response.status = 400
+ # raise exceptions.MessageException( bad_req.detail )
+ #
+ #except Exception, e:
+ # msg = "Error in history API at showing history detail: %s" % ( str( e ) )
+ # log.exception( msg )
+ # trans.response.status = 500
+ # return msg
return history_data
diff -r c4950bc662256f013916b0aaabb73a47508213c5 -r 79ab14af25e4182dac99ecad3918ed653c50f272 test/casperjs/api-hda-tests.js
--- a/test/casperjs/api-hda-tests.js
+++ b/test/casperjs/api-hda-tests.js
@@ -32,33 +32,12 @@
}
spaceghost.user.loginOrRegisterUser( email, password );
-var uploadFilename = '1.sam',
- uploadFilepath = '../../test-data/' + uploadFilename,
- upload = {};
-spaceghost.thenOpen( spaceghost.baseUrl ).tools.uploadFile( uploadFilepath, function( uploadInfo ){
- upload = uploadInfo;
+spaceghost.thenOpen( spaceghost.baseUrl, function(){
+ this.api.tools.thenUpload( spaceghost.api.histories.show( 'current' ).id, {
+ filepath: this.options.scriptDir + '/../../test-data/1.sam'
+ });
});
-function hasKeys( object, keysArray ){
- if( !utils.isObject( object ) ){ return false; }
- for( var i=0; i<keysArray.length; i += 1 ){
- if( !object.hasOwnProperty( keysArray[i] ) ){
- spaceghost.debug( 'object missing key: ' + keysArray[i] );
- return false;
- }
- }
- return true;
-}
-
-function countKeys( object ){
- if( !utils.isObject( object ) ){ return 0; }
- var count = 0;
- for( var key in object ){
- if( object.hasOwnProperty( key ) ){ count += 1; }
- }
- return count;
-}
-
// =================================================================== TESTS
var summaryKeys = [ 'id', 'name', 'type', 'url' ],
detailKeys = [
@@ -76,11 +55,8 @@
'metadata_comment_lines', 'metadata_data_lines'
];
-spaceghost.historypanel.waitForHdas().then( function(){
+spaceghost.then( function(){
- var uploaded = this.historypanel.hdaElementInfoByTitle( uploadFilename );
- this.info( 'found uploaded hda: ' + uploaded.attributes.id );
- this.debug( 'uploaded hda: ' + this.jsonStr( uploaded ) );
// ------------------------------------------------------------------------------------------- INDEX
this.test.comment( 'index should return a list of summary data for each hda' );
var histories = this.api.histories.index(),
@@ -92,18 +68,17 @@
this.test.assert( hdaIndex.length >= 1, 'Has at least one hda' );
var firstHda = hdaIndex[0];
- this.test.assert( hasKeys( firstHda, summaryKeys ), 'Has the proper keys' );
+ this.test.assert( this.hasKeys( firstHda, summaryKeys ), 'Has the proper keys' );
this.test.assert( this.api.isEncodedId( firstHda.id ), 'Id appears well-formed: ' + firstHda.id );
- this.test.assert( uploaded.text.indexOf( firstHda.name ) !== -1, 'Title matches: ' + firstHda.name );
- // not caring about type or url here
+ this.test.assert( firstHda.name === '1.sam', 'Title matches: ' + firstHda.name );
// ------------------------------------------------------------------------------------------- SHOW
this.test.comment( 'show should get an HDA details object' );
var hdaShow = this.api.hdas.show( lastHistory.id, firstHda.id );
//this.debug( this.jsonStr( hdaShow ) );
- this.test.assert( hasKeys( hdaShow, detailKeys ), 'Has the proper keys' );
+ this.test.assert( this.hasKeys( hdaShow, detailKeys ), 'Has the proper keys' );
//TODO: validate data in each hdaShow attribute...
@@ -117,7 +92,7 @@
this.test.assert( hdaIndex.length >= 1, 'Has at least one hda' );
firstHda = hdaIndex[0];
- this.test.assert( hasKeys( firstHda, detailKeys ), 'Has the proper keys' );
+ this.test.assert( this.hasKeys( firstHda, detailKeys ), 'Has the proper keys' );
//TODO??: validate data in firstHda attribute? we ASSUME it's from a common method as show...
@@ -133,29 +108,21 @@
var returned = this.api.hdas.update( lastHistory.id, firstHda.id, {
name : hdaShow.name
});
- this.test.assert( countKeys( returned ) === 0, "No changed returned: " + this.jsonStr( returned ) );
+ this.test.assert( this.countKeys( returned ) === 0, "No changed returned: " + this.jsonStr( returned ) );
this.test.comment( 'updating using a nonsense key should NOT fail with an error' );
returned = this.api.hdas.update( lastHistory.id, firstHda.id, {
konamiCode : 'uuddlrlrba'
});
- this.test.assert( countKeys( returned ) === 0, "No changed returned: " + this.jsonStr( returned ) );
+ this.test.assert( this.countKeys( returned ) === 0, "No changed returned: " + this.jsonStr( returned ) );
this.test.comment( 'updating by attempting to change type should cause an error' );
- err = {};
- try {
+ this.api.assertRaises( function(){
returned = this.api.hdas.update( lastHistory.id, firstHda.id, {
//name : false
deleted : 'sure why not'
});
- } catch( error ){
- err = error;
- //this.debug( this.jsonStr( err ) );
- }
- this.test.assert( !!err.message, "Error occurred: " + err.message );
- this.test.assert( err.status === 400, "Error status is 400: " + err.status );
- //TODO??: other type checks?
-
+ }, 400, 'deleted must be a boolean', 'changing deleted type failed' );
// ........................................................................................... name
this.test.comment( 'update should allow changing the name' );
@@ -330,25 +297,68 @@
// ------------------------------------------------------------------------------------------- ERRORS
this.test.comment( 'create should error with "Please define the source" when the param "from_ld_id" is not used' );
- var errored = false;
- try {
- // sending an empty object won't work
- var created = this.api.hdas.create( lastHistory.id, { bler: 'bler' } );
+ this.api.assertRaises( function(){
+ this.api.hdas.create( lastHistory.id, { bler: 'bler' } );
+ }, 400, 'Please define the source', 'create with no source failed' );
- } catch( err ){
- errored = true;
- this.test.assert( err.message.indexOf( 'Please define the source' ) !== -1,
- 'Error has the proper message: ' + err.message );
- this.test.assert( err.status === 400, 'Error has the proper status code: ' + err.status );
- }
- if( !errored ){
- this.test.fail( 'create without "from_ld_id" did not cause error' );
- }
+ this.test.comment( 'updating using a nonsense key should fail silently' );
+ returned = this.api.hdas.update( lastHistory.id, hdaShow.id, {
+ konamiCode : 'uuddlrlrba'
+ });
+ this.test.assert( returned.konamiCode === undefined, 'key was not set: ' + returned.konamiCode );
- //var returned = this.api.hdas.update( lastHistory.id, hdaIndex[0].id, { deleted: true, blerp: 'blerp' });
- //var returned = this.api.hdas.update( lastHistory.id, { deleted: true, blerp: 'blerp' });
- //this.debug( 'returned:' + this.jsonStr( returned ) );
- //this.debug( 'page:' + this.jsonStr( this.page ) );
+ spaceghost.test.comment( 'A bad id should throw an error when using show' );
+ this.api.assertRaises( function(){
+ this.api.hdas.show( lastHistory.id, '1234123412341234' );
+ }, 500, 'unable to decode', 'Bad Request with invalid id: show' );
+ spaceghost.test.comment( 'A bad id should throw an error when using update' );
+ this.api.assertRaises( function(){
+ this.api.hdas.update( lastHistory.id, '1234123412341234', {} );
+ }, 400, 'invalid literal for int', 'Bad Request with invalid id: update' );
+ spaceghost.test.comment( 'A bad id should throw an error when using delete' );
+ this.api.assertRaises( function(){
+ this.api.hdas.delete_( lastHistory.id, '1234123412341234' );
+ }, 500, 'invalid literal for int', 'Bad Request with invalid id: delete' );
+ spaceghost.test.comment( 'A bad id should throw an error when using undelete' );
+
+ this.test.comment( 'updating by attempting to change type should cause an error' );
+ [ 'name', 'annotation', 'genome_build', 'misc_info' ].forEach( function( key ){
+ var updatedAttrs = {};
+ updatedAttrs[ key ] = false;
+ spaceghost.api.assertRaises( function(){
+ returned = spaceghost.api.hdas.update( hdaShow.history_id, hdaShow.id, updatedAttrs );
+ }, 400, key + ' must be a string or unicode', 'type validation error' );
+ });
+ [ 'deleted', 'visible' ].forEach( function( key ){
+ var updatedAttrs = {};
+ updatedAttrs[ key ] = 'straaang';
+ spaceghost.api.assertRaises( function(){
+ returned = spaceghost.api.hdas.update( hdaShow.history_id, hdaShow.id, updatedAttrs );
+ }, 400, key + ' must be a boolean', 'type validation error' );
+ });
+ [ 'you\'re it', [ true ] ].forEach( function( badVal ){
+ spaceghost.api.assertRaises( function(){
+ returned = spaceghost.api.hdas.update( hdaShow.history_id, hdaShow.id, { tags: badVal });
+ }, 400, 'tags must be a list', 'type validation error' );
+ });
+
+ // ------------------------------------------------------------------------------------------- DELETE
+ this.test.comment( 'calling delete on an hda should mark it as deleted but not change the history size' );
+ lastHistory = this.api.histories.show( lastHistory.id );
+ var sizeBeforeDelete = lastHistory.nice_size;
+
+ returned = this.api.hdas.delete_( lastHistory.id, firstHda.id );
+ //this.debug( this.jsonStr( returned ) );
+
+ hdaShow = this.api.hdas.show( lastHistory.id, firstHda.id );
+ this.test.assert( hdaShow.deleted, 'hda is marked deleted' );
+ lastHistory = this.api.histories.show( lastHistory.id );
+ this.test.assert( lastHistory.nice_size === sizeBeforeDelete, 'history size has not changed' );
+
+ // by default, purging fails bc uni.ini:allow_user_dataset_purge=False
+ this.api.assertRaises( function(){
+ returned = this.api.hdas.delete_( lastHistory.id, firstHda.id, { purge : true });
+ }, 403, 'This instance does not allow user dataset purging', 'Purge failed' );
/*
*/
});
diff -r c4950bc662256f013916b0aaabb73a47508213c5 -r 79ab14af25e4182dac99ecad3918ed653c50f272 test/casperjs/api-history-permission-tests.js
--- /dev/null
+++ b/test/casperjs/api-history-permission-tests.js
@@ -0,0 +1,303 @@
+/* Utility to load a specific page and output html, page text, or a screenshot
+ * Optionally wait for some time, text, or dom selector
+ */
+try {
+ //...if there's a better way - please let me know, universe
+ var scriptDir = require( 'system' ).args[3]
+ // remove the script filename
+ .replace( /[\w|\.|\-|_]*$/, '' )
+ // if given rel. path, prepend the curr dir
+ .replace( /^(?!\/)/, './' ),
+ spaceghost = require( scriptDir + 'spaceghost' ).create({
+ // script options here (can be overridden by CLI)
+ //verbose: true,
+ //logLevel: debug,
+ scriptDir: scriptDir
+ });
+
+} catch( error ){
+ console.debug( error );
+ phantom.exit( 1 );
+}
+spaceghost.start();
+
+
+// =================================================================== SET UP
+var utils = require( 'utils' );
+
+var email = spaceghost.user.getRandomEmail(),
+ password = '123456';
+if( spaceghost.fixtureData.testUser ){
+ email = spaceghost.fixtureData.testUser.email;
+ password = spaceghost.fixtureData.testUser.password;
+}
+var email2 = spaceghost.user.getRandomEmail(),
+ password2 = '123456';
+if( spaceghost.fixtureData.testUser2 ){
+ email2 = spaceghost.fixtureData.testUser2.email;
+ password2 = spaceghost.fixtureData.testUser2.password;
+}
+
+var inaccessibleHistory, accessibleHistory, publishedHistory,
+ inaccessibleHdas, accessibleHdas, publishedHdas,
+ accessibleLink;
+
+// =================================================================== TESTS
+//// ------------------------------------------------------------------------------------------- create 3 histories
+spaceghost.user.loginOrRegisterUser( email, password );
+spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){
+ // create three histories: make the 2nd importable (via the API), and the third published
+
+ this.test.comment( 'importable, slug, and published should all be returned by show and initially off' );
+ // make the current the inaccessible one
+ inaccessibleHistory = this.api.histories.show( 'current' );
+ this.test.assert( this.hasKeys( inaccessibleHistory, [ 'id', 'name', 'slug', 'importable', 'published' ] ),
+ 'Has the proper keys' );
+ this.test.assert( inaccessibleHistory.slug === null,
+ 'initial slug is null: ' + inaccessibleHistory.slug );
+ this.test.assert( inaccessibleHistory.importable === false,
+ 'initial importable is false: ' + inaccessibleHistory.importable );
+ this.test.assert( inaccessibleHistory.published === false,
+ 'initial published is false: ' + inaccessibleHistory.published );
+ this.api.histories.update( inaccessibleHistory.id, { name: 'inaccessible' });
+
+ this.test.comment( 'Setting importable to true should create a slug, username_and_slug, and importable === true' );
+ accessibleHistory = this.api.histories.create({ name: 'accessible' });
+ var returned = this.api.histories.update( accessibleHistory.id, {
+ importable : true
+ });
+ this.debug( this.jsonStr( returned ) );
+ accessibleHistory = this.api.histories.show( accessibleHistory.id );
+ this.test.assert( this.hasKeys( accessibleHistory, [ 'username_and_slug' ] ),
+ 'Has the proper keys' );
+ this.test.assert( accessibleHistory.slug === 'accessible',
+ 'slug is not null: ' + accessibleHistory.slug );
+ this.test.assert( accessibleHistory.importable,
+ 'importable is true: ' + accessibleHistory.importable );
+ accessibleLink = 'u/' + email.replace( '@test.test', '' ) + '/h/accessible';
+ this.test.assert( accessibleHistory.username_and_slug === accessibleLink,
+ 'username_and_slug is proper: ' + accessibleHistory.username_and_slug );
+
+ this.test.comment( 'Setting published to true should create make accessible and published === true' );
+ publishedHistory = this.api.histories.create({ name: 'published' });
+ returned = this.api.histories.update( publishedHistory.id, {
+ published : true
+ });
+ this.debug( this.jsonStr( returned ) );
+ publishedHistory = this.api.histories.show( publishedHistory.id );
+ this.test.assert( this.hasKeys( publishedHistory, [ 'username_and_slug' ] ),
+ 'Has the proper keys' );
+ this.test.assert( publishedHistory.published,
+ 'published is true: ' + publishedHistory.published );
+ this.test.assert( publishedHistory.importable,
+ 'importable is true: ' + publishedHistory.importable );
+ this.test.assert( publishedHistory.slug === 'published',
+ 'slug is not null: ' + publishedHistory.slug );
+ accessibleLink = 'u/' + email.replace( '@test.test', '' ) + '/h/published';
+ this.test.assert( publishedHistory.username_and_slug === accessibleLink,
+ 'username_and_slug is proper: ' + publishedHistory.username_and_slug );
+
+});
+
+//// ------------------------------------------------------------------------------------------- upload some files
+spaceghost.then( function(){
+ this.api.tools.thenUpload( inaccessibleHistory.id, { filepath: this.options.scriptDir + '/../../test-data/1.bed' });
+ this.api.tools.thenUpload( accessibleHistory.id, { filepath: this.options.scriptDir + '/../../test-data/1.bed' });
+ this.api.tools.thenUpload( publishedHistory.id, { filepath: this.options.scriptDir + '/../../test-data/1.bed' });
+});
+spaceghost.then( function(){
+ // check that they're there
+ inaccessibleHdas = this.api.hdas.index( inaccessibleHistory.id ),
+ accessibleHdas = this.api.hdas.index( accessibleHistory.id ),
+ publishedHdas = this.api.hdas.index( publishedHistory.id );
+ this.test.assert( inaccessibleHdas.length === 1,
+ 'uploaded file to inaccessible: ' + inaccessibleHdas.length );
+ this.test.assert( accessibleHdas.length === 1,
+ 'uploaded file to accessible: ' + accessibleHdas.length );
+ this.test.assert( publishedHdas.length === 1,
+ 'uploaded file to published: ' + publishedHdas.length );
+});
+spaceghost.user.logout();
+
+//// ------------------------------------------------------------------------------------------- log in user2
+function ensureInaccessibility( history, hdas ){
+
+ this.test.comment( 'all four CRUD API calls should fail for user2 with history: ' + history.name );
+ this.api.assertRaises( function(){
+ this.api.histories.show( history.id );
+ }, 400, 'History is not accessible to the current user', 'show failed with error' );
+ this.api.assertRaises( function(){
+ this.api.histories.create({ history_id : history.id });
+ }, 403, 'History is not accessible to the current user', 'copy failed with error' );
+ this.api.assertRaises( function(){
+ this.api.histories.update( history.id, { deleted: true });
+ }, 500, 'History is not owned by the current user', 'update failed with error' );
+ this.api.assertRaises( function(){
+ this.api.histories.delete_( history.id );
+ }, 400, 'History is not owned by the current user', 'delete failed with error' );
+
+ this.test.comment( 'all four HDA CRUD API calls should fail for user2 with history: ' + history.name );
+ this.api.assertRaises( function(){
+ this.api.hdas.index( history.id );
+ }, 500, 'History is not accessible to the current user', 'index failed with error' );
+ this.api.assertRaises( function(){
+ this.api.hdas.show( history.id, hdas[0].id );
+ }, 500, 'History is not accessible to the current user', 'show failed with error' );
+ this.api.assertRaises( function(){
+ this.api.hdas.update( history.id, hdas[0].id, { deleted: true });
+ }, 500, 'HistoryDatasetAssociation is not owned by current user', 'update failed with error' );
+ this.api.assertRaises( function(){
+ this.api.hdas.delete_( history.id, hdas[0].id );
+ }, 500, 'HistoryDatasetAssociation is not owned by current user', 'delete failed with error' );
+
+ this.test.comment( 'Attempting to copy an accessible hda (default is accessible)'
+ + 'from an inaccessible history should fail' );
+ this.api.assertRaises( function(){
+ var returned = this.api.hdas.create( this.api.histories.show( 'current' ).id, {
+ source : 'hda',
+ content : hdas[0].id
+ });
+ this.debug( this.jsonStr( returned ) );
+ }, 403, 'History is not accessible to the current user', 'copy failed with error' );
+
+}
+spaceghost.user.loginOrRegisterUser( email2, password2 );
+spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){
+
+ // ----------------------------------------------------------------------------------------- user2 + inaccessible
+ ensureInaccessibility.call( spaceghost, inaccessibleHistory, inaccessibleHdas );
+
+ // ----------------------------------------------------------------------------------------- user2 + accessible
+ this.test.comment( 'show should work for the importable history' );
+ this.test.assert( this.api.histories.show( accessibleHistory.id ).id === accessibleHistory.id,
+ 'show worked' );
+
+ this.test.comment( 'create/copy should work for the importable history' );
+ var returned = this.api.histories.create({ history_id : accessibleHistory.id });
+ this.test.assert( returned.name === "Copy of '" + accessibleHistory.name + "'",
+ 'copied name matches: ' + returned.name );
+
+ this.test.comment( 'update should fail for the importable history' );
+ this.api.assertRaises( function(){
+ this.api.histories.update( accessibleHistory.id, { deleted: true });
+ }, 500, 'History is not owned by the current user', 'update failed with error' );
+ this.test.comment( 'delete should fail for the importable history' );
+ this.api.assertRaises( function(){
+ this.api.histories.delete_( accessibleHistory.id );
+ }, 400, 'History is not owned by the current user', 'delete failed with error' );
+
+ this.test.comment( 'indexing should work for the contents of the importable history' );
+ this.test.assert( this.api.hdas.index( accessibleHistory.id ).length === 1,
+ 'index worked' );
+ this.test.comment( 'showing should work for the contents of the importable history' );
+ this.test.assert( this.api.hdas.show( accessibleHistory.id, accessibleHdas[0].id ).id === accessibleHdas[0].id,
+ 'show worked' );
+ this.test.comment( 'updating should fail for the contents of the importable history' );
+ this.api.assertRaises( function(){
+ this.api.hdas.update( accessibleHistory.id, accessibleHdas[0].id, { deleted: true });
+ }, 500, 'HistoryDatasetAssociation is not owned by current user', 'update failed with error' );
+ this.test.comment( 'deleting should fail for the contents of the importable history' );
+ this.api.assertRaises( function(){
+ this.api.hdas.delete_( accessibleHistory.id, accessibleHdas[0].id );
+ }, 500, 'HistoryDatasetAssociation is not owned by current user', 'delete failed with error' );
+ this.test.comment( 'copying a dataset from the importable history should work' );
+ returned = this.api.hdas.create( this.api.histories.show( 'current' ).id, {
+ source : 'hda',
+ content : accessibleHdas[0].id
+ });
+ this.test.assert( returned.name === accessibleHdas[0].name, 'successful copy from: ' + returned.name );
+
+ this.test.comment( 'copying a dataset into the importable history should fail' );
+ this.api.assertRaises( function(){
+ this.api.hdas.create( accessibleHistory.id, {
+ source : 'hda',
+ // should error before it checks the id
+ content : 'bler'
+ });
+ }, 400, 'History is not owned by the current user', 'copy to failed' );
+
+ //// ----------------------------------------------------------------------------------------- user2 + published
+ this.test.comment( 'show should work for the published history' );
+ this.test.assert( this.api.histories.show( publishedHistory.id ).id === publishedHistory.id,
+ 'show worked' );
+ this.test.comment( 'create/copy should work for the published history' );
+ returned = this.api.histories.create({ history_id : publishedHistory.id });
+ this.test.assert( returned.name === "Copy of '" + publishedHistory.name + "'",
+ 'copied name matches: ' + returned.name );
+ this.test.comment( 'update should fail for the published history' );
+ this.api.assertRaises( function(){
+ this.api.histories.update( publishedHistory.id, { deleted: true });
+ }, 500, 'History is not owned by the current user', 'update failed with error' );
+ this.test.comment( 'delete should fail for the published history' );
+ this.api.assertRaises( function(){
+ this.api.histories.delete_( publishedHistory.id );
+ }, 400, 'History is not owned by the current user', 'delete failed with error' );
+
+ this.test.comment( 'indexing should work for the contents of the published history' );
+ this.test.assert( this.api.hdas.index( publishedHistory.id ).length === 1,
+ 'index worked' );
+ this.test.comment( 'showing should work for the contents of the published history' );
+ this.test.assert( this.api.hdas.show( publishedHistory.id, publishedHdas[0].id ).id === publishedHdas[0].id,
+ 'show worked' );
+ this.test.comment( 'updating should fail for the contents of the published history' );
+ this.api.assertRaises( function(){
+ this.api.hdas.update( publishedHistory.id, publishedHdas[0].id, { deleted: true });
+ }, 500, 'HistoryDatasetAssociation is not owned by current user', 'update failed with error' );
+ this.test.comment( 'deleting should fail for the contents of the published history' );
+ this.api.assertRaises( function(){
+ this.api.hdas.delete_( publishedHistory.id, publishedHdas[0].id );
+ }, 500, 'HistoryDatasetAssociation is not owned by current user', 'delete failed with error' );
+
+ this.test.comment( 'copying a dataset from the published history should work' );
+ returned = this.api.hdas.create( this.api.histories.show( 'current' ).id, {
+ source : 'hda',
+ content : publishedHdas[0].id
+ });
+ this.test.assert( returned.name === publishedHdas[0].name, 'successful copy from: ' + returned.name );
+
+ this.test.comment( 'copying a dataset into the published history should fail' );
+ this.api.assertRaises( function(){
+ this.api.hdas.create( publishedHistory.id, {
+ source : 'hda',
+ // should error before it checks the id
+ content : 'bler'
+ });
+ }, 400, 'History is not owned by the current user', 'copy to failed' );
+});
+spaceghost.user.logout();
+
+
+//// ------------------------------------------------------------------------------------------- user1 revoke perms
+spaceghost.user.loginOrRegisterUser( email, password );
+spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){
+ this.test.comment( 'revoking perms should prevent access' );
+ this.api.histories.update( accessibleHistory.id, {
+ importable : false
+ });
+ var returned = this.api.histories.show( accessibleHistory.id );
+ this.test.assert( !returned.importable, 'now not importable' );
+ this.test.assert( !returned.published, '(still not published)' );
+ this.test.assert( !!returned.slug, '(slug still set) ' + returned.slug );
+
+ this.api.histories.update( publishedHistory.id, {
+ importable : false,
+ published : false
+ });
+ returned = this.api.histories.show( publishedHistory.id );
+ this.test.assert( !returned.importable, 'now not importable' );
+ this.test.assert( !returned.published, 'now not published' );
+ this.test.assert( !!returned.slug, '(slug still set) ' + returned.slug );
+});
+spaceghost.user.logout();
+
+
+//// ------------------------------------------------------------------------------------------- user2 retry perms
+spaceghost.user.loginOrRegisterUser( email2, password2 );
+spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){
+ ensureInaccessibility.call( spaceghost, accessibleHistory, accessibleHdas );
+ ensureInaccessibility.call( spaceghost, publishedHistory, publishedHdas );
+});
+
+// ===================================================================
+spaceghost.run( function(){
+});
diff -r c4950bc662256f013916b0aaabb73a47508213c5 -r 79ab14af25e4182dac99ecad3918ed653c50f272 test/casperjs/api-history-tests.js
--- a/test/casperjs/api-history-tests.js
+++ b/test/casperjs/api-history-tests.js
@@ -33,25 +33,6 @@
}
spaceghost.user.loginOrRegisterUser( email, password );
-function hasKeys( object, keysArray ){
- if( !utils.isObject( object ) ){ return false; }
- for( var i=0; i<keysArray.length; i += 1 ){
- if( !object.hasOwnProperty( keysArray[i] ) ){
- spaceghost.debug( 'object missing key: ' + keysArray[i] );
- return false;
- }
- }
- return true;
-}
-
-function countKeys( object ){
- if( !utils.isObject( object ) ){ return 0; }
- var count = 0;
- for( var key in object ){
- if( object.hasOwnProperty( key ) ){ count += 1; }
- }
- return count;
-}
// =================================================================== TESTS
spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){
@@ -64,7 +45,7 @@
this.test.assert( historyIndex.length >= 1, 'Has at least one history' );
var firstHistory = historyIndex[0];
- this.test.assert( hasKeys( firstHistory, [ 'id', 'name', 'url' ] ), 'Has the proper keys' );
+ this.test.assert( this.hasKeys( firstHistory, [ 'id', 'name', 'url' ] ), 'Has the proper keys' );
this.test.assert( this.api.isEncodedId( firstHistory.id ), 'Id appears well-formed' );
@@ -72,7 +53,7 @@
this.test.comment( 'show should get a history details object' );
var historyShow = this.api.histories.show( firstHistory.id );
//this.debug( this.jsonStr( historyShow ) );
- this.test.assert( hasKeys( historyShow, [
+ this.test.assert( this.hasKeys( historyShow, [
'id', 'name', 'annotation', 'nice_size', 'contents_url',
'state', 'state_details', 'state_ids' ]),
'Has the proper keys' );
@@ -83,8 +64,8 @@
'ok', 'paused', 'queued', 'running', 'setting_metadata', 'upload' ],
state_details = historyShow.state_details,
state_ids = historyShow.state_ids;
- this.test.assert( hasKeys( state_details, states ), 'state_details has the proper keys' );
- this.test.assert( hasKeys( state_ids, states ), 'state_ids has the proper keys' );
+ this.test.assert( this.hasKeys( state_details, states ), 'state_details has the proper keys' );
+ this.test.assert( this.hasKeys( state_ids, states ), 'state_ids has the proper keys' );
var state_detailsAreNumbers = true;
state_idsAreArrays = true;
states.forEach( function( state ){
@@ -103,20 +84,19 @@
this.test.assert( this.api.histories.show( this.api.histories.index()[0].id ).id === firstHistory.id,
'combining function calls works' );
- // test server bad id protection
- this.test.comment( 'A bad id to show should throw an error' );
- this.assertRaises( function(){
- this.api.histories.show( '1234123412341234' );
- }, '400 Bad Request', 'Raises an exception' );
+ this.test.comment( 'Calling show with "current" should return the current history' );
+ this.test.assert( this.api.histories.show( 'current' ).id === historyShow.id, 'current returned same id' );
// ------------------------------------------------------------------------------------------- CREATE
- this.test.comment( 'Calling create should create a new history and allow setting the name' );
+ this.test.comment( 'Calling create should create a new history and allow setting the name and making it current' );
var newHistoryName = 'Created History',
- createdHistory = this.api.histories.create({ name: newHistoryName });
+ createdHistory = this.api.histories.create({ name: newHistoryName, current: true });
//this.debug( 'returned from create:\n' + this.jsonStr( createdHistory ) );
this.test.assert( createdHistory.name === newHistoryName,
"Name of created history (from create) is correct: " + createdHistory.name );
+ this.test.assert( this.api.histories.show( 'current' ).id === createdHistory.id,
+ "was made current" );
// check the index
var newFirstHistory = this.api.histories.index()[0];
@@ -156,50 +136,25 @@
"Undeletion returned 'OK' - even though that's not a great, informative response: " + undeletedHistory );
newFirstHistory = this.api.histories.index()[0];
- this.debug( 'newFirstHistory:\n' + this.jsonStr( newFirstHistory ) );
+ //this.debug( 'newFirstHistory:\n' + this.jsonStr( newFirstHistory ) );
this.test.assert( newFirstHistory.id === createdHistory.id,
"Id of last history (from index) DOES appear after undeletion: " + newFirstHistory.id );
//TODO: show, deleted flag
//TODO: delete, purge flag
+
+
+ // ------------------------------------------------------------------------------------------- set_as_current
+ this.test.comment( 'calling set_as_current on a non-current history will make it current' );
+ this.test.assert( this.api.histories.show( 'current' ).id !== historyShow.id, historyShow.id + ' is not current' );
+ var returned = this.api.histories.set_as_current( historyShow.id );
+ this.debug( this.jsonStr( returned ) );
+ this.test.assert( this.api.histories.show( 'current' ).id === historyShow.id, 'made current' );
+ this.api.histories.set_as_current( newFirstHistory.id );
+
+
// ------------------------------------------------------------------------------------------- UPDATE
- // ........................................................................................... idiot proofing
- this.test.comment( 'updating to the current value should return no value (no change)' );
- historyShow = this.api.histories.show( newFirstHistory.id );
- var returned = this.api.histories.update( newFirstHistory.id, {
- name : historyShow.name
- });
- this.test.assert( countKeys( returned ) === 0, "No changed returned: " + this.jsonStr( returned ) );
-
- this.test.comment( 'updating using a nonsense key should fail silently' );
- var err = null;
- try {
- returned = this.api.histories.update( newFirstHistory.id, {
- konamiCode : 'uuddlrlrba'
- });
- } catch( error ){
- err = error;
- //this.debug( this.jsonStr( err ) );
- }
- this.test.assert( err === null, "No error occurred: " + this.jsonStr( err ) );
-
- this.test.comment( 'updating by attempting to change type should cause an error' );
- err = {};
- try {
- returned = this.api.histories.update( newFirstHistory.id, {
- //name : false
- deleted : 'sure why not'
- });
- } catch( error ){
- err = error;
- //this.debug( this.jsonStr( err ) );
- }
- this.test.assert( !!err.message, "Error occurred: " + err.message );
- this.test.assert( err.status === 400, "Error status is 400: " + err.status );
- //TODO??: other type checks?
-
-
// ........................................................................................... name
this.test.comment( 'update should allow changing the name' );
returned = this.api.histories.update( newFirstHistory.id, {
@@ -339,8 +294,58 @@
// ------------------------------------------------------------------------------------------- ERRORS
- //TODO: make sure expected errors are being passed back (but no permissions checks here - different suite)
- // bad ids: index, show, update, delete, undelete
+ // ........................................................................................... idiot proofing
+ this.test.comment( 'updating to the current value should return no value (no change)' );
+ historyShow = this.api.histories.show( newFirstHistory.id );
+ returned = this.api.histories.update( newFirstHistory.id, {
+ name : historyShow.name
+ });
+ this.test.assert( this.countKeys( returned ) === 0, "No changed returned: " + this.jsonStr( returned ) );
+
+ this.test.comment( 'updating using a nonsense key should fail silently' );
+ returned = this.api.histories.update( newFirstHistory.id, {
+ konamiCode : 'uuddlrlrba'
+ });
+ this.test.assert( returned.konamiCode === undefined, 'key was not set: ' + returned.konamiCode );
+
+ // test server bad id protection
+ spaceghost.test.comment( 'A bad id should throw an error' );
+ this.api.assertRaises( function(){
+ this.api.histories.show( '1234123412341234' );
+ }, 400, 'unable to decode', 'Bad Request with invalid id: show' );
+ spaceghost.test.comment( 'A bad id should throw an error when using update' );
+ this.api.assertRaises( function(){
+ this.api.histories.update( '1234123412341234', {} );
+ }, 500, 'unable to decode', 'Bad Request with invalid id: update' );
+ spaceghost.test.comment( 'A bad id should throw an error when using delete' );
+ this.api.assertRaises( function(){
+ this.api.histories.delete_( '1234123412341234' );
+ }, 400, 'unable to decode', 'Bad Request with invalid id: delete' );
+ spaceghost.test.comment( 'A bad id should throw an error when using undelete' );
+ this.api.assertRaises( function(){
+ this.api.histories.undelete( '1234123412341234' );
+ }, 400, 'unable to decode', 'Bad Request with invalid id: undelete' );
+
+ this.test.comment( 'updating by attempting to change type should cause an error' );
+ [ 'name', 'annotation' ].forEach( function( key ){
+ var updatedAttrs = {};
+ updatedAttrs[ key ] = false;
+ spaceghost.api.assertRaises( function(){
+ returned = spaceghost.api.histories.update( newFirstHistory.id, updatedAttrs );
+ }, 400, key + ' must be a string or unicode', 'type validation error' );
+ });
+ [ 'deleted', 'importable', 'published' ].forEach( function( key ){
+ var updatedAttrs = {};
+ updatedAttrs[ key ] = 'straaang';
+ spaceghost.api.assertRaises( function(){
+ returned = spaceghost.api.histories.update( newFirstHistory.id, updatedAttrs );
+ }, 400, key + ' must be a boolean', 'type validation error' );
+ });
+ [ 'you\'re it', [ true ] ].forEach( function( badVal ){
+ spaceghost.api.assertRaises( function(){
+ returned = spaceghost.api.histories.update( newFirstHistory.id, { tags: badVal });
+ }, 400, 'tags must be a list', 'type validation error' );
+ });
/*
*/
//this.debug( this.jsonStr( historyShow ) );
diff -r c4950bc662256f013916b0aaabb73a47508213c5 -r 79ab14af25e4182dac99ecad3918ed653c50f272 test/casperjs/casperjs_runner.py
--- a/test/casperjs/casperjs_runner.py
+++ b/test/casperjs/casperjs_runner.py
@@ -81,6 +81,7 @@
"""
# debugging flag - set to true to have casperjs tests output with --verbose=true and --logLevel=debug
+ #debug = True
debug = False
# bit of a hack - this is the beginning of the last string when capserjs --verbose=true --logLevel=debug
# use this to get subprocess to stop waiting for output
@@ -191,6 +192,7 @@
except ValueError, val_err:
if str( val_err ) == 'No JSON object could be decoded':
log.debug( '(error parsing returned JSON from casperjs, dumping stdout...)\n:%s', stdout_output )
+ return HeadlessJSJavascriptError( 'see log for details' )
else:
raise
@@ -375,19 +377,29 @@
class Test_04_HDAs( CasperJSTestCase ):
"""Tests for HistoryDatasetAssociation fetching, rendering, and modeling.
"""
- def test_00_HDA_states( self ):
- """Test structure rendering of HDAs in all the possible HDA states
- """
- self.run_js_script( 'hda-state-tests.js' )
+ #def test_00_HDA_states( self ):
+ # """Test structure rendering of HDAs in all the possible HDA states
+ # """
+ # self.run_js_script( 'hda-state-tests.js' )
class Test_05_API( CasperJSTestCase ):
"""Tests for API functionality and security.
"""
- def test_00_history_api( self ):
- """Test history API.
+ #def test_00_history_api( self ):
+ # """Test history API.
+ # """
+ # self.run_js_script( 'api-history-tests.js' )
+
+ def test_01_hda_api( self ):
+ """Test HDA API.
"""
- self.run_js_script( 'api-history-tests.js' )
+ self.run_js_script( 'api-hda-tests.js' )
+
+ def test_02_history_permissions_api( self ):
+ """Test API permissions for importable, published histories.
+ """
+ self.run_js_script( 'api-history-permission-tests.js' )
# ==================================================================== MAIN
diff -r c4950bc662256f013916b0aaabb73a47508213c5 -r 79ab14af25e4182dac99ecad3918ed653c50f272 test/casperjs/modules/api.js
--- a/test/casperjs/modules/api.js
+++ b/test/casperjs/modules/api.js
@@ -75,12 +75,63 @@
if( resp.status !== 200 ){
// grrr... this doesn't lose the \n\r\t
//throw new APIError( resp.responseText.replace( /[\s\n\r\t]+/gm, ' ' ).replace( /"/, '' ) );
- this.spaceghost.debug( 'api error response status code: ' + resp.status );
+ this.spaceghost.debug( 'API error: code: ' + resp.status + ', responseText: ' + resp.responseText );
+ this.spaceghost.debug( '\t responseJSON: ' + this.spaceghost.jsonStr( resp.responseJSON ) );
throw new APIError( resp.responseText, resp.status );
}
return JSON.parse( resp.responseText );
};
+// =================================================================== TESTING
+/** Checks whether fn raises an error with a message that contains a status and given string.
+ * NOTE: DOES NOT work with steps. @see SpaceGhost#assertStepsRaise
+ * @param {Function} testFn a function that may throw an error
+ * @param {Integer} statusExpected the HTTP status code expected
+ * @param {String} errMsgContains some portion of the correct error msg
+ * @private
+ */
+API.prototype._APIRaises = function _APIRaises( testFn, statusExpected, errMsgContains ){
+ var failed = false;
+ try {
+ testFn.call( this.spaceghost );
+ } catch( err ){
+ this.spaceghost.debug( err.name + ': ' + err.status );
+ this.spaceghost.debug( err.message );
+ if( ( err.name === 'APIError' )
+ && ( err.status && err.status === statusExpected )
+ && ( err.message.indexOf( errMsgContains ) !== -1 ) ){
+ failed = true;
+
+ // re-raise other, non-searched-for errors
+ } else {
+ throw err;
+ }
+ }
+ return failed;
+};
+
+/** Simple assert raises.
+ * NOTE: DOES NOT work with steps. @see SpaceGhost#assertStepsRaise
+ * @param {Function} testFn a function that may throw an error
+ * @param {Integer} statusExpected the HTTP status code expected
+ * @param {String} errMsgContains some portion of the correct error msg
+ * @param {String} msg assertion message to display
+ */
+API.prototype.assertRaises = function assertRaises( testFn, statusExpected, errMsgContains, msg ){
+ return this.spaceghost.test.assert( this._APIRaises( testFn, statusExpected, errMsgContains ), msg );
+};
+
+/** Simple assert does not raise.
+ * NOTE: DOES NOT work with steps. @see SpaceGhost#assertStepsRaise
+ * @param {Function} testFn a function that may throw an error
+ * @param {Integer} statusExpected the HTTP status code expected
+ * @param {String} errMsgContains some portion of the correct error msg
+ * @param {String} msg assertion message to display
+ */
+API.prototype.assertDoesntRaise = function assertDoesntRaise( testFn, statusExpected, errMsgContains, msg ){
+ return this.spaceghost.test.assert( !this._APIRaises( testFn, statusExpected, errMsgContains ), msg );
+};
+
// =================================================================== MISC
API.prototype.isEncodedId = function isEncodedId( id ){
if( typeof id !== 'string' ){ return false; }
@@ -164,7 +215,8 @@
create : 'api/histories',
delete_ : 'api/histories/%s',
undelete: 'api/histories/deleted/%s/undelete',
- update : 'api/histories/%s'
+ update : 'api/histories/%s',
+ set_as_current : 'api/histories/%s/set_as_current'
};
HistoriesAPI.prototype.index = function index( deleted ){
@@ -179,7 +231,7 @@
HistoriesAPI.prototype.show = function show( id, deleted ){
this.api.spaceghost.info( 'histories.show: ' + [ id, (( deleted )?( 'w deleted' ):( '' )) ] );
- id = ( id === 'most_recently_used' )?( id ):( this.api.ensureId( id ) );
+ id = ( id === 'most_recently_used' || id === 'current' )?( id ):( this.api.ensureId( id ) );
deleted = deleted || false;
return this.api._ajax( utils.format( this.urlTpls.show, id ), {
data : { deleted: deleted }
@@ -217,7 +269,7 @@
});
};
-HistoriesAPI.prototype.update = function create( id, payload ){
+HistoriesAPI.prototype.update = function update( id, payload ){
this.api.spaceghost.info( 'histories.update: ' + id + ',' + this.api.spaceghost.jsonStr( payload ) );
// py.payload <-> ajax.data
@@ -231,6 +283,15 @@
});
};
+HistoriesAPI.prototype.set_as_current = function set_as_current( id ){
+ this.api.spaceghost.info( 'histories.set_as_current: ' + id );
+ id = this.api.ensureId( id );
+
+ return this.api._ajax( utils.format( this.urlTpls.set_as_current, id ), {
+ type : 'PUT'
+ });
+};
+
// =================================================================== HDAS
var HDAAPI = function HDAAPI( api ){
@@ -297,6 +358,23 @@
});
};
+HDAAPI.prototype.delete_ = function create( historyId, id, purge ){
+ this.api.spaceghost.info( 'hdas.delete_: ' + [ historyId, id ] );
+ historyId = this.api.ensureId( historyId );
+ id = this.api.ensureId( id );
+
+ // have to attach like GET param - due to body loss in jq
+ url = utils.format( this.urlTpls.update, historyId, id );
+ if( purge ){
+ url += '?purge=True';
+ }
+ return this.api._ajax( url, {
+ type : 'DELETE'
+ });
+};
+
+//TODO: delete_
+
// =================================================================== TOOLS
var ToolsAPI = function HDAAPI( api ){
@@ -350,6 +428,218 @@
});
};
+//ToolsAPI.prototype.uploadByForm = function upload( historyId, options ){
+// this.api.spaceghost.debug( '-------------------------------------------------' );
+// this.api.spaceghost.info( 'tools.upload: ' + [ historyId, '(contents)', this.api.spaceghost.jsonStr( options ) ] );
+// this.api.ensureId( historyId );
+// options = options || {};
+//
+// this.api.spaceghost.evaluate( function( url ){
+// var html = [
+// '<form action="', url, '" method="post" enctype="multipart/form-data">',
+// '<input type="file" name="files_0|file_data">',
+// '<input type="hidden" name="tool_id" />',
+// '<input type="hidden" name="history_id" />',
+// '<input type="hidden" name="inputs" />',
+// '<button type="submit">Submit</button>',
+// '</form>'
+// ];
+// document.write( html.join('') );
+// //document.getElementsByTagName( 'body' )[0].innerHTML = html.join('');
+// }, utils.format( this.urlTpls.create ) );
+//
+// this.api.spaceghost.fill( 'form', {
+// 'files_0|file_data' : '1.txt',
+// 'tool_id' : 'upload1',
+// 'history_id' : historyId,
+// 'inputs' : JSON.stringify({
+// 'file_type' : 'auto',
+// 'files_0|type' : 'upload_dataset',
+// 'to_posix_lines' : true,
+// 'space_to_tabs' : false,
+// 'dbkey' : '?'
+// })
+// }, true );
+// // this causes the page to switch...I think
+//};
+
+/** paste a file - either from a string (options.paste) or from a filesystem file (options.filepath) */
+ToolsAPI.prototype.uploadByPaste = function upload( historyId, options ){
+ this.api.spaceghost.info( 'tools.upload: ' + [ historyId, this.api.spaceghost.jsonStr( options ) ] );
+ this.api.ensureId( historyId );
+ options = options || {};
+
+ var inputs = {
+ 'files_0|NAME' : options.name || 'Test Dataset',
+ 'dbkey' : options.dbkey || '?',
+ 'file_type' : options.ext || 'auto'
+ };
+ if( options.filepath ){
+ var fs = require( 'fs' );
+ inputs[ 'files_0|url_paste' ] = fs.read( options.filepath );
+
+ } else if( options.paste ){
+ inputs[ 'files_0|url_paste' ] = options.paste;
+ }
+ if( options.posix ){
+ inputs[ 'files_0|to_posix_lines' ] = 'Yes';
+ }
+ if( options.tabs ){
+ inputs[ 'files_0|space_to_tab' ] = 'Yes';
+ }
+ return this.api._ajax( utils.format( this.urlTpls.create ), {
+ type : 'POST',
+ data : {
+ tool_id : 'upload1',
+ upload_type : 'upload_dataset',
+ history_id : historyId,
+ inputs : inputs
+ }
+ });
+};
+
+/** post a file to the upload1 tool over ajax */
+ToolsAPI.prototype.upload = function upload( historyId, options ){
+ this.api.spaceghost.info( 'tools.upload: ' + [ historyId, this.api.spaceghost.jsonStr( options ) ] );
+ this.api.ensureId( historyId );
+ options = options || {};
+
+ // We can post an upload using jquery and formdata (see below), the more
+ // difficult part is attaching the file without user intervention.
+ // To do this we need to (unfortunately) create a form phantom can attach the file to first.
+ this.api.spaceghost.evaluate( function(){
+ $( 'body' ).append( '<input type="file" name="casperjs-upload-file" />' );
+ });
+ this.api.spaceghost.page.uploadFile( 'input[name="casperjs-upload-file"]', options.filepath );
+
+ var inputs = {
+ 'file_type' : options.ext || 'auto',
+ 'files_0|type' : 'upload_dataset',
+ 'dbkey' : options.dbkey || '?'
+ };
+ if( options.posix ){
+ inputs[ 'files_0|to_posix_lines' ] = 'Yes';
+ }
+ if( options.tabs ){
+ inputs[ 'files_0|space_to_tab' ] = 'Yes';
+ }
+ var response = this.api.spaceghost.evaluate( function( url, historyId, inputs ){
+ var file = $( 'input[name="casperjs-upload-file"]' )[0].files[0],
+ formData = new FormData(),
+ response;
+
+ formData.append( 'files_0|file_data', file );
+ formData.append( 'history_id', historyId );
+ formData.append( 'tool_id', 'upload1' );
+ formData.append( 'inputs', JSON.stringify( inputs ) );
+ $.ajax({
+ url : url,
+ async : false,
+ type : 'POST',
+ data : formData,
+ // when sending FormData don't have jq process or cache the data
+ cache : false,
+ contentType : false,
+ processData : false,
+ // if we don't add this, payload isn't processed as JSON
+ headers : { 'Accept': 'application/json' }
+ }).done(function( resp ){
+ response = resp;
+ });
+ // this works only bc jq is async
+ return response;
+ }, utils.format( this.urlTpls.create ), historyId, inputs );
+
+ if( !response ){
+ throw new APIError( 'Failed to upload: ' + options.filepath, 0 );
+ }
+
+ return response;
+};
+
+/** amount of time allowed to upload a file (before erroring) */
+ToolsAPI.prototype.DEFAULT_UPLOAD_TIMEOUT = 12000;
+
+/** add two casperjs steps - upload a file, wait for the job to complete, and run 'then' when they are */
+ToolsAPI.prototype.thenUpload = function thenUpload( historyId, options, then ){
+ var spaceghost = this.api.spaceghost,
+ uploadedId;
+
+ spaceghost.then( function(){
+ var returned = this.api.tools.upload( historyId, options );
+ this.debug( 'returned: ' + this.jsonStr( returned ) );
+ uploadedId = returned.outputs[0].id;
+ this.debug( 'uploadedId: ' + uploadedId );
+ });
+
+ spaceghost.then( function(){
+ this.waitFor(
+ function testHdaState(){
+ var hda = spaceghost.api.hdas.show( historyId, uploadedId );
+ spaceghost.debug( spaceghost.jsonStr( hda.state ) );
+ return !( hda.state === 'upload' || hda.state === 'queued' || hda.state === 'running' );
+ },
+ function _then(){
+ spaceghost.info( 'upload finished: ' + uploadedId );
+ if( then ){
+ then.call( spaceghost, uploadedId );
+ }
+ //var hda = spaceghost.api.hdas.show( historyId, uploadedId );
+ //spaceghost.debug( spaceghost.jsonStr( hda ) );
+ },
+ function timeout(){
+ throw new APIError( 'timeout uploading file', 408 );
+ },
+ options.timeout || spaceghost.api.tools.DEFAULT_UPLOAD_TIMEOUT
+ );
+ });
+ return spaceghost;
+};
+
+/** add two casperjs steps - upload multiple files (described in optionsArray) and wait for all jobs to complete */
+ToolsAPI.prototype.thenUploadMultiple = function thenUploadMultiple( historyId, optionsArray, then ){
+ var spaceghost = this.api.spaceghost,
+ uploadedIds = [];
+
+ this.api.spaceghost.then( function(){
+ var spaceghost = this;
+ optionsArray.forEach( function( options ){
+ var returned = spaceghost.api.tools.upload( historyId, options );
+ spaceghost.debug( 'uploaded:' + spaceghost.jsonStr( returned ) );
+ uploadedIds.push( returned.outputs[0].id );
+ });
+ });
+
+ // wait for every hda to finish running - IOW, don't use uploadedIds
+ this.api.spaceghost.then( function(){
+ this.debug( this.jsonStr( uploadedIds ) );
+ this.waitFor(
+ function testHdaStates(){
+ var hdas = spaceghost.api.hdas.index( historyId ),
+ running = hdas.filter( function( hda ){
+ return ( hda.state === 'upload' || hda.state === 'queued' || hda.state === 'running' );
+ }).map( function( hda ){
+ return hda.id;
+ });
+ //spaceghost.debug( 'still uploading: ' + spaceghost.jsonStr( running ) );
+ return running.length === 0;
+ },
+ function _then(){
+ var hdas = spaceghost.api.hdas.index( historyId );
+ spaceghost.debug( spaceghost.jsonStr( hdas ) );
+ if( then ){
+ then.call( spaceghost, uploadedIds );
+ }
+ },
+ function timeout(){
+ throw new APIError( 'timeout uploading files', 408 );
+ },
+ ( options.timeout || spaceghost.api.tools.DEFAULT_UPLOAD_TIMEOUT ) * optionsArray.length
+ );
+ });
+ return spaceghost;
+};
+
// =================================================================== WORKFLOWS
var WorkflowsAPI = function WorkflowsAPI( api ){
@@ -495,7 +785,7 @@
};
-// =================================================================== HISTORIES
+// =================================================================== VISUALIZATIONS
var VisualizationsAPI = function VisualizationsAPI( api ){
this.api = api;
};
diff -r c4950bc662256f013916b0aaabb73a47508213c5 -r 79ab14af25e4182dac99ecad3918ed653c50f272 test/casperjs/modules/user.js
--- a/test/casperjs/modules/user.js
+++ b/test/casperjs/modules/user.js
@@ -280,6 +280,9 @@
User.prototype.getRandomEmail = function getRandomEmail( username, domain ){
username = username || 'test';
domain = domain || 'test.test';
- return username + Date.now() + '@' + domain;
+ var number = Math.ceil( Math.random() * 10000000000000 );
+ // doesn't work so well when creating two users at once
+ //var number = Date.now();
+ return username + number + '@' + domain;
};
diff -r c4950bc662256f013916b0aaabb73a47508213c5 -r 79ab14af25e4182dac99ecad3918ed653c50f272 test/casperjs/spaceghost.js
--- a/test/casperjs/spaceghost.js
+++ b/test/casperjs/spaceghost.js
@@ -886,6 +886,30 @@
this.test.assert( classes.indexOf( className ) === -1, msg );
};
+/** Return true if object has all keys in keysArray (useful in API testing of return values).
+ * @param {Object} object the object to test
+ * @param {String[]} keysArray an array of expected keys
+ */
+SpaceGhost.prototype.hasKeys = function hasKeys( object, keysArray ){
+ if( !utils.isObject( object ) ){ return false; }
+ for( var i=0; i<keysArray.length; i += 1 ){
+ if( !object.hasOwnProperty( keysArray[i] ) ){
+ return false;
+ }
+ }
+ return true;
+};
+
+/** Returns count of keys in object. */
+SpaceGhost.prototype.countKeys = function countKeys( object ){
+ if( !utils.isObject( object ) ){ return 0; }
+ var count = 0;
+ for( var key in object ){
+ if( object.hasOwnProperty( key ) ){ count += 1; }
+ }
+ return count;
+};
+
// =================================================================== CONVENIENCE
/** Wraps casper.getElementInfo in try, returning null if element not found instead of erroring.
https://bitbucket.org/galaxy/galaxy-central/commits/561929fb2d7e/
Changeset: 561929fb2d7e
User: carlfeberhard
Date: 2014-03-25 22:00:03
Summary: merge
Affected #: 3 files
diff -r 79ab14af25e4182dac99ecad3918ed653c50f272 -r 561929fb2d7e827f20ea078904aeebe35173f3f9 static/scripts/viz/trackster/tracks.js
--- a/static/scripts/viz/trackster/tracks.js
+++ b/static/scripts/viz/trackster/tracks.js
@@ -1106,6 +1106,10 @@
});
// For vertical alignment, track mouse with simple line.
+ // Fixes needed for this to work:
+ // (a) make work with embedded visualizations;
+ // (b) seems to get stuck on tile overlaps.
+ /*
var mouse_tracker_div = $('<div/>').addClass('mouse-pos').appendTo(parent_element);
// Show tracker only when hovering over view.
@@ -1125,6 +1129,7 @@
mouse_tracker_div.hide();
}
);
+ */
this.add_label_track( new LabelTrack( this, { content_div: this.top_labeltrack } ) );
this.add_label_track( new LabelTrack( this, { content_div: this.nav_labeltrack } ) );
@@ -3053,14 +3058,21 @@
var tile_drawn = $.Deferred();
track.tile_cache.set_elt(key, tile_drawn);
$.when.apply($, get_tile_data()).then( function() {
- // If deferred objects ever show up in tile data, that is likely because a
- // Deferred-subsetting interaction failed. Specifically, a Deferred for a superset
- // was returned but then couldn't be used). It's not clear whether this will happen
- // in practice, and currently the code doesn't handle it. It could probably handle it
- // by recursively calling draw_helper.
var tile_data = get_tile_data(),
tracks_data = tile_data,
seq_data;
+
+ // Deferreds may show up here if trying to fetch a subset of data from a superset data chunk
+ // that cannot be subsetted. This may occur if the superset has a message. If there is a
+ // Deferred, try again from the top. NOTE: this condition could (should?) be handled by the
+ // GenomeDataManager in visualization module.
+ if (_.find(tile_data, function(d) { return util.is_deferred(d); })) {
+ track.tile_cache.set_elt(key, undefined);
+ $.when(track.draw_helper(region, w_scale, options)).then(function(tile) {
+ tile_drawn.resolve(tile);
+ });
+ return;
+ }
// If sequence data is available, subset to get only data in region.
if (view.reference_track) {
diff -r 79ab14af25e4182dac99ecad3918ed653c50f272 -r 561929fb2d7e827f20ea078904aeebe35173f3f9 static/scripts/viz/trackster/util.js
--- a/static/scripts/viz/trackster/util.js
+++ b/static/scripts/viz/trackster/util.js
@@ -1,6 +1,13 @@
define(function(){
/**
+ * Helper to determine if object is jQuery deferred.
+ */
+var is_deferred = function ( d ) {
+ return ('promise' in d);
+};
+
+/**
* Implementation of a server-state based deferred. Server is repeatedly polled, and when
* condition is met, deferred is resolved.
*/
@@ -109,6 +116,7 @@
};
return {
+ is_deferred: is_deferred,
ServerStateDeferred : ServerStateDeferred,
get_random_color : get_random_color
};
diff -r 79ab14af25e4182dac99ecad3918ed653c50f272 -r 561929fb2d7e827f20ea078904aeebe35173f3f9 static/scripts/viz/visualization.js
--- a/static/scripts/viz/visualization.js
+++ b/static/scripts/viz/visualization.js
@@ -86,14 +86,6 @@
});
};
-
-/**
- * Helper to determine if object is jQuery deferred.
- */
-var is_deferred = function ( d ) {
- return ('promise' in d);
-};
-
// --------- Models ---------
/**
@@ -380,11 +372,10 @@
* Get data from dataset.
*/
get_data: function(region, mode, resolution, extra_params) {
-
// Look for entry and return if it's a deferred or if data available is compatible with mode.
var entry = this.get_elt(region);
if ( entry &&
- ( is_deferred(entry) || this.get('data_mode_compatible')(entry, mode) ) ) {
+ ( util_mod.is_deferred(entry) || this.get('data_mode_compatible')(entry, mode) ) ) {
return entry;
}
@@ -405,12 +396,12 @@
// This entry has data in the requested range. Return if data
// is compatible and can be subsetted.
entry = obj_cache[entry_region.toString()];
- if ( is_deferred(entry) ||
+ if ( util_mod.is_deferred(entry) ||
( this.get('data_mode_compatible')(entry, mode) && this.get('can_subset')(entry) ) ) {
this.move_key_to_end(entry_region, i);
// If there's data, subset it.
- if ( !is_deferred(entry) ) {
+ if ( !util_mod.is_deferred(entry) ) {
var subset_entry = this.subset_entry(entry, region);
this.set(region, subset_entry);
entry = subset_entry;
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.
1
0
commit/galaxy-central: Jeremy Goecks: Trackster: (a) fix bug when trying to subset from supersets with messages; (b) display vertical placeholder until bugs are resolved.
by commits-noreply@bitbucket.org 25 Mar '14
by commits-noreply@bitbucket.org 25 Mar '14
25 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/47211cedc8d0/
Changeset: 47211cedc8d0
User: Jeremy Goecks
Date: 2014-03-25 20:41:40
Summary: Trackster: (a) fix bug when trying to subset from supersets with messages; (b) display vertical placeholder until bugs are resolved.
Affected #: 3 files
diff -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 -r 47211cedc8d0db9a3d9189bc2d3bfa7e3fe11b86 static/scripts/viz/trackster/tracks.js
--- a/static/scripts/viz/trackster/tracks.js
+++ b/static/scripts/viz/trackster/tracks.js
@@ -1106,6 +1106,10 @@
});
// For vertical alignment, track mouse with simple line.
+ // Fixes needed for this to work:
+ // (a) make work with embedded visualizations;
+ // (b) seems to get stuck on tile overlaps.
+ /*
var mouse_tracker_div = $('<div/>').addClass('mouse-pos').appendTo(parent_element);
// Show tracker only when hovering over view.
@@ -1125,6 +1129,7 @@
mouse_tracker_div.hide();
}
);
+ */
this.add_label_track( new LabelTrack( this, { content_div: this.top_labeltrack } ) );
this.add_label_track( new LabelTrack( this, { content_div: this.nav_labeltrack } ) );
@@ -3053,14 +3058,21 @@
var tile_drawn = $.Deferred();
track.tile_cache.set_elt(key, tile_drawn);
$.when.apply($, get_tile_data()).then( function() {
- // If deferred objects ever show up in tile data, that is likely because a
- // Deferred-subsetting interaction failed. Specifically, a Deferred for a superset
- // was returned but then couldn't be used). It's not clear whether this will happen
- // in practice, and currently the code doesn't handle it. It could probably handle it
- // by recursively calling draw_helper.
var tile_data = get_tile_data(),
tracks_data = tile_data,
seq_data;
+
+ // Deferreds may show up here if trying to fetch a subset of data from a superset data chunk
+ // that cannot be subsetted. This may occur if the superset has a message. If there is a
+ // Deferred, try again from the top. NOTE: this condition could (should?) be handled by the
+ // GenomeDataManager in visualization module.
+ if (_.find(tile_data, function(d) { return util.is_deferred(d); })) {
+ track.tile_cache.set_elt(key, undefined);
+ $.when(track.draw_helper(region, w_scale, options)).then(function(tile) {
+ tile_drawn.resolve(tile);
+ });
+ return;
+ }
// If sequence data is available, subset to get only data in region.
if (view.reference_track) {
diff -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 -r 47211cedc8d0db9a3d9189bc2d3bfa7e3fe11b86 static/scripts/viz/trackster/util.js
--- a/static/scripts/viz/trackster/util.js
+++ b/static/scripts/viz/trackster/util.js
@@ -1,6 +1,13 @@
define(function(){
/**
+ * Helper to determine if object is jQuery deferred.
+ */
+var is_deferred = function ( d ) {
+ return ('promise' in d);
+};
+
+/**
* Implementation of a server-state based deferred. Server is repeatedly polled, and when
* condition is met, deferred is resolved.
*/
@@ -109,6 +116,7 @@
};
return {
+ is_deferred: is_deferred,
ServerStateDeferred : ServerStateDeferred,
get_random_color : get_random_color
};
diff -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 -r 47211cedc8d0db9a3d9189bc2d3bfa7e3fe11b86 static/scripts/viz/visualization.js
--- a/static/scripts/viz/visualization.js
+++ b/static/scripts/viz/visualization.js
@@ -86,14 +86,6 @@
});
};
-
-/**
- * Helper to determine if object is jQuery deferred.
- */
-var is_deferred = function ( d ) {
- return ('promise' in d);
-};
-
// --------- Models ---------
/**
@@ -380,11 +372,10 @@
* Get data from dataset.
*/
get_data: function(region, mode, resolution, extra_params) {
-
// Look for entry and return if it's a deferred or if data available is compatible with mode.
var entry = this.get_elt(region);
if ( entry &&
- ( is_deferred(entry) || this.get('data_mode_compatible')(entry, mode) ) ) {
+ ( util_mod.is_deferred(entry) || this.get('data_mode_compatible')(entry, mode) ) ) {
return entry;
}
@@ -405,12 +396,12 @@
// This entry has data in the requested range. Return if data
// is compatible and can be subsetted.
entry = obj_cache[entry_region.toString()];
- if ( is_deferred(entry) ||
+ if ( util_mod.is_deferred(entry) ||
( this.get('data_mode_compatible')(entry, mode) && this.get('can_subset')(entry) ) ) {
this.move_key_to_end(entry_region, i);
// If there's data, subset it.
- if ( !is_deferred(entry) ) {
+ if ( !util_mod.is_deferred(entry) ) {
var subset_entry = this.subset_entry(entry, region);
this.set(region, subset_entry);
entry = subset_entry;
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.
1
0
commit/galaxy-central: guerler: Ui/Upload: Close modal on escape
by commits-noreply@bitbucket.org 25 Mar '14
by commits-noreply@bitbucket.org 25 Mar '14
25 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4d2f3398ad6d/
Changeset: 4d2f3398ad6d
User: guerler
Date: 2014-03-25 19:01:26
Summary: Ui/Upload: Close modal on escape
Affected #: 2 files
diff -r 208533b58c7e135e99a74e7cf5183a63020b3341 -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 static/scripts/mvc/ui/ui-modal.js
--- a/static/scripts/mvc/ui/ui-modal.js
+++ b/static/scripts/mvc/ui/ui-modal.js
@@ -115,7 +115,7 @@
this.$el.remove();
// remove escape event
- $(document).off('keyup');
+ $(document).off('keyup.ui-modal');
}
// create new element
@@ -157,13 +157,11 @@
// bind additional closing events
if (this.options.closing_events) {
// bind the ESC key to hide() function
- if (!this.options.buttons.Pause){ // don't bind for the upload modal because of the side effects with system modal for file choosing
- $(document).on('keyup', function(e) {
- if (e.keyCode == 27) {
- self.hide();
- }
- });
- }
+ $(document).on('keyup.ui-modal', function(e) {
+ if (e.keyCode == 27) {
+ self.hide();
+ }
+ });
// hide modal if background is clicked
this.$el.find('.modal-backdrop').on('click', function() { self.hide(); });
diff -r 208533b58c7e135e99a74e7cf5183a63020b3341 -r 4d2f3398ad6d6eebfe084a184bf860dd7c6f9071 static/scripts/packed/mvc/ui/ui-modal.js
--- a/static/scripts/packed/mvc/ui/ui-modal.js
+++ b/static/scripts/packed/mvc/ui/ui-modal.js
@@ -1,1 +1,1 @@
-define(["utils/utils"],function(a){var b=Backbone.View.extend({elMain:"body",optionsDefault:{title:"ui-modal",body:"",backdrop:true,height:null,width:null,closing_events:false},buttonList:{},initialize:function(c){if(c){this._create(c)}},show:function(c){this.initialize(c);if(this.options.height){this.$body.css("height",this.options.height);this.$body.css("overflow","hidden")}else{this.$body.css("max-height",$(window).height()/2)}if(this.options.width){this.$dialog.css("width",this.options.width)}if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.visible=false;this.$el.fadeOut("fast")},enableButton:function(c){var d=this.buttonList[c];this.$buttons.find("#"+d).prop("disabled",false)},disableButton:function(c){var d=this.buttonList[c];this.$buttons.find("#"+d).prop("disabled",true)},showButton:function(c){var d=this.buttonList[c];this.$buttons.find("#"+d).show()},hideButton:function(c){var d=this.buttonList[c];this.$buttons.find("#"+d).hide()},getButton:function(c){var d=this.buttonList[c];return this.$buttons.find("#"+d)},scrollTop:function(){return this.$body.scrollTop()},_create:function(e){var d=this;this.options=_.defaults(e,this.optionsDefault);if(this.options.body=="progress"){this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')}if(this.$el){this.$el.remove();$(document).off("keyup")}this.setElement(this._template(this.options.title));this.$dialog=(this.$el).find(".modal-dialog");this.$body=(this.$el).find(".modal-body");this.$footer=(this.$el).find(".modal-footer");this.$buttons=(this.$el).find(".buttons");this.$backdrop=(this.$el).find(".modal-backdrop");this.$body.html(this.options.body);if(!this.options.backdrop){this.$backdrop.removeClass("in")}if(this.options.buttons){this.buttonList={};var c=0;$.each(this.options.buttons,function(f,h){var g="button-"+c++;d.$buttons.append($('<button id="'+g+'"></button>').text(f).click(h)).append(" ");d.buttonList[f]=g})}else{this.$footer.hide()}$(this.elMain).append($(this.el));if(this.options.closing_events){if(!this.options.buttons.Pause){$(document).on("keyup",function(f){if(f.keyCode==27){d.hide()}})}this.$el.find(".modal-backdrop").on("click",function(){d.hide()})}},_template:function(c){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+c+'</h4></div><div class="modal-body" style="position: static;"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{View:b}});
\ No newline at end of file
+define(["utils/utils"],function(a){var b=Backbone.View.extend({elMain:"body",optionsDefault:{title:"ui-modal",body:"",backdrop:true,height:null,width:null,closing_events:false},buttonList:{},initialize:function(c){if(c){this._create(c)}},show:function(c){this.initialize(c);if(this.options.height){this.$body.css("height",this.options.height);this.$body.css("overflow","hidden")}else{this.$body.css("max-height",$(window).height()/2)}if(this.options.width){this.$dialog.css("width",this.options.width)}if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.visible=false;this.$el.fadeOut("fast")},enableButton:function(c){var d=this.buttonList[c];this.$buttons.find("#"+d).prop("disabled",false)},disableButton:function(c){var d=this.buttonList[c];this.$buttons.find("#"+d).prop("disabled",true)},showButton:function(c){var d=this.buttonList[c];this.$buttons.find("#"+d).show()},hideButton:function(c){var d=this.buttonList[c];this.$buttons.find("#"+d).hide()},getButton:function(c){var d=this.buttonList[c];return this.$buttons.find("#"+d)},scrollTop:function(){return this.$body.scrollTop()},_create:function(e){var d=this;this.options=_.defaults(e,this.optionsDefault);if(this.options.body=="progress"){this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')}if(this.$el){this.$el.remove();$(document).off("keyup.ui-modal")}this.setElement(this._template(this.options.title));this.$dialog=(this.$el).find(".modal-dialog");this.$body=(this.$el).find(".modal-body");this.$footer=(this.$el).find(".modal-footer");this.$buttons=(this.$el).find(".buttons");this.$backdrop=(this.$el).find(".modal-backdrop");this.$body.html(this.options.body);if(!this.options.backdrop){this.$backdrop.removeClass("in")}if(this.options.buttons){this.buttonList={};var c=0;$.each(this.options.buttons,function(f,h){var g="button-"+c++;d.$buttons.append($('<button id="'+g+'"></button>').text(f).click(h)).append(" ");d.buttonList[f]=g})}else{this.$footer.hide()}$(this.elMain).append($(this.el));if(this.options.closing_events){$(document).on("keyup.ui-modal",function(f){if(f.keyCode==27){d.hide()}});this.$el.find(".modal-backdrop").on("click",function(){d.hide()})}},_template:function(c){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+c+'</h4></div><div class="modal-body" style="position: static;"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{View:b}});
\ No newline at end of file
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.
1
0
commit/galaxy-central: davebgx: Fix server error when attempting to create a repository before any categories have been created.
by commits-noreply@bitbucket.org 25 Mar '14
by commits-noreply@bitbucket.org 25 Mar '14
25 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/208533b58c7e/
Changeset: 208533b58c7e
User: davebgx
Date: 2014-03-25 18:25:19
Summary: Fix server error when attempting to create a repository before any categories have been created.
Affected #: 1 file
diff -r cab5878ce755b1736d09a9a74e419eddb12c0f2d -r 208533b58c7e135e99a74e7cf5183a63020b3341 lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -950,7 +950,7 @@
categories = suc.get_categories( trans )
if not categories:
message = 'No categories have been configured in this instance of the Galaxy Tool Shed. '
- message += 'An administrator needs to create some via the Administrator control panel before creating repositories.',
+ message += 'An administrator needs to create some via the Administrator control panel before creating repositories.'
status = 'error'
return trans.response.send_redirect( web.url_for( controller='repository',
action='browse_repositories',
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.
1
0
commit/galaxy-central: guerler: Charts: Improve fit and chart state handling, recalculate chart only if neccessary
by commits-noreply@bitbucket.org 25 Mar '14
by commits-noreply@bitbucket.org 25 Mar '14
25 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/cab5878ce755/
Changeset: cab5878ce755
User: guerler
Date: 2014-03-25 13:35:20
Summary: Charts: Improve fit and chart state handling, recalculate chart only if neccessary
Affected #: 9 files
diff -r ec715604b476c47703b90385062a6ec482cfd302 -r cab5878ce755b1736d09a9a74e419eddb12c0f2d config/plugins/visualizations/charts/static/build-app.js
--- a/config/plugins/visualizations/charts/static/build-app.js
+++ b/config/plugins/visualizations/charts/static/build-app.js
@@ -3,4 +3,4 @@
// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore may be freely distributed under the MIT license.
-(function(){var e=this,t=e._,n={},r=Array.prototype,i=Object.prototype,s=Function.prototype,o=r.push,u=r.slice,a=r.concat,f=i.toString,l=i.hasOwnProperty,c=r.forEach,h=r.map,p=r.reduce,d=r.reduceRight,v=r.filter,m=r.every,g=r.some,y=r.indexOf,b=r.lastIndexOf,w=Array.isArray,E=Object.keys,S=s.bind,x=function(e){if(e instanceof x)return e;if(!(this instanceof x))return new x(e);this._wrapped=e};typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(exports=module.exports=x),exports._=x):e._=x,x.VERSION="1.4.4";var T=x.each=x.forEach=function(e,t,r){if(e==null)return;if(c&&e.forEach===c)e.forEach(t,r);else if(e.length===+e.length){for(var i=0,s=e.length;i<s;i++)if(t.call(r,e[i],i,e)===n)return}else for(var o in e)if(x.has(e,o)&&t.call(r,e[o],o,e)===n)return};x.map=x.collect=function(e,t,n){var r=[];return e==null?r:h&&e.map===h?e.map(t,n):(T(e,function(e,i,s){r.push(t.call(n,e,i,s))}),r)};var N="Reduce of empty array with no initial value";x.reduce=x.foldl=x.inject=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(p&&e.reduce===p)return r&&(t=x.bind(t,r)),i?e.reduce(t,n):e.reduce(t);T(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError(N);return n},x.reduceRight=x.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(d&&e.reduceRight===d)return r&&(t=x.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=x.keys(e);s=o.length}T(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)});if(!i)throw new TypeError(N);return n},x.find=x.detect=function(e,t,n){var r;return C(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},x.filter=x.select=function(e,t,n){var r=[];return e==null?r:v&&e.filter===v?e.filter(t,n):(T(e,function(e,i,s){t.call(n,e,i,s)&&r.push(e)}),r)},x.reject=function(e,t,n){return x.filter(e,function(e,r,i){return!t.call(n,e,r,i)},n)},x.every=x.all=function(e,t,r){t||(t=x.identity);var i=!0;return e==null?i:m&&e.every===m?e.every(t,r):(T(e,function(e,s,o){if(!(i=i&&t.call(r,e,s,o)))return n}),!!i)};var C=x.some=x.any=function(e,t,r){t||(t=x.identity);var i=!1;return e==null?i:g&&e.some===g?e.some(t,r):(T(e,function(e,s,o){if(i||(i=t.call(r,e,s,o)))return n}),!!i)};x.contains=x.include=function(e,t){return e==null?!1:y&&e.indexOf===y?e.indexOf(t)!=-1:C(e,function(e){return e===t})},x.invoke=function(e,t){var n=u.call(arguments,2),r=x.isFunction(t);return x.map(e,function(e){return(r?t:e[t]).apply(e,n)})},x.pluck=function(e,t){return x.map(e,function(e){return e[t]})},x.where=function(e,t,n){return x.isEmpty(t)?n?void 0:[]:x[n?"find":"filter"](e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},x.findWhere=function(e,t){return x.where(e,t,!0)},x.max=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.max.apply(Math,e);if(!t&&x.isEmpty(e))return-Infinity;var r={computed:-Infinity,value:-Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},x.min=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.min.apply(Math,e);if(!t&&x.isEmpty(e))return Infinity;var r={computed:Infinity,value:Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o<r.computed&&(r={value:e,computed:o})}),r.value},x.shuffle=function(e){var t,n=0,r=[];return T(e,function(e){t=x.random(n++),r[n-1]=r[t],r[t]=e}),r};var k=function(e){return x.isFunction(e)?e:function(t){return t[e]}};x.sortBy=function(e,t,n){var r=k(t);return x.pluck(x.map(e,function(e,t,i){return{value:e,index:t,criteria:r.call(n,e,t,i)}}).sort(function(e,t){var n=e.criteria,r=t.criteria;if(n!==r){if(n>r||n===void 0)return 1;if(n<r||r===void 0)return-1}return e.index<t.index?-1:1}),"value")};var L=function(e,t,n,r){var i={},s=k(t==null?x.identity:t);return T(e,function(t,o){var u=s.call(n,t,o,e);r(i,u,t)}),i};x.groupBy=function(e,t,n){return L(e,t,n,function(e,t,n){(x.has(e,t)?e[t]:e[t]=[]).push(n)})},x.countBy=function(e,t,n){return L(e,t,n,function(e,t){x.has(e,t)||(e[t]=0),e[t]++})},x.sortedIndex=function(e,t,n,r){n=n==null?x.identity:k(n);var i=n.call(r,t),s=0,o=e.length;while(s<o){var u=s+o>>>1;n.call(r,e[u])<i?s=u+1:o=u}return s},x.toArray=function(e){return e?x.isArray(e)?u.call(e):e.length===+e.length?x.map(e,x.identity):x.values(e):[]},x.size=function(e){return e==null?0:e.length===+e.length?e.length:x.keys(e).length},x.first=x.head=x.take=function(e,t,n){return e==null?void 0:t!=null&&!n?u.call(e,0,t):e[0]},x.initial=function(e,t,n){return u.call(e,0,e.length-(t==null||n?1:t))},x.last=function(e,t,n){return e==null?void 0:t!=null&&!n?u.call(e,Math.max(e.length-t,0)):e[e.length-1]},x.rest=x.tail=x.drop=function(e,t,n){return u.call(e,t==null||n?1:t)},x.compact=function(e){return x.filter(e,x.identity)};var A=function(e,t,n){return T(e,function(e){x.isArray(e)?t?o.apply(n,e):A(e,t,n):n.push(e)}),n};x.flatten=function(e,t){return A(e,t,[])},x.without=function(e){return x.difference(e,u.call(arguments,1))},x.uniq=x.unique=function(e,t,n,r){x.isFunction(t)&&(r=n,n=t,t=!1);var i=n?x.map(e,n,r):e,s=[],o=[];return T(i,function(n,r){if(t?!r||o[o.length-1]!==n:!x.contains(o,n))o.push(n),s.push(e[r])}),s},x.union=function(){return x.uniq(a.apply(r,arguments))},x.intersection=function(e){var t=u.call(arguments,1);return x.filter(x.uniq(e),function(e){return x.every(t,function(t){return x.indexOf(t,e)>=0})})},x.difference=function(e){var t=a.apply(r,u.call(arguments,1));return x.filter(e,function(e){return!x.contains(t,e)})},x.zip=function(){var e=u.call(arguments),t=x.max(x.pluck(e,"length")),n=new Array(t);for(var r=0;r<t;r++)n[r]=x.pluck(e,""+r);return n},x.unzip=function(e){var t=[];return x.each(e,function(e,n){x.each(e,function(e,r){t.length<=r&&(t[r]=[]),t[r][n]=e})}),t},x.object=function(e,t){if(e==null)return{};var n={};for(var r=0,i=e.length;r<i;r++)t?n[e[r]]=t[r]:n[e[r][0]]=e[r][1];return n},x.indexOf=function(e,t,n){if(e==null)return-1;var r=0,i=e.length;if(n){if(typeof n!="number")return r=x.sortedIndex(e,t),e[r]===t?r:-1;r=n<0?Math.max(0,i+n):n}if(y&&e.indexOf===y)return e.indexOf(t,n);for(;r<i;r++)if(e[r]===t)return r;return-1},x.lastIndexOf=function(e,t,n){if(e==null)return-1;var r=n!=null;if(b&&e.lastIndexOf===b)return r?e.lastIndexOf(t,n):e.lastIndexOf(t);var i=r?n:e.length;while(i--)if(e[i]===t)return i;return-1},x.range=function(e,t,n){arguments.length<=1&&(t=e||0,e=0),n=arguments[2]||1;var r=Math.max(Math.ceil((t-e)/n),0),i=0,s=new Array(r);while(i<r)s[i++]=e,e+=n;return s};var O=function(){};x.bind=function(e,t){var n,r;if(e.bind===S&&S)return S.apply(e,u.call(arguments,1));if(!x.isFunction(e))throw new TypeError;return n=u.call(arguments,2),r=function(){if(this instanceof r){O.prototype=e.prototype;var i=new O;O.prototype=null;var s=e.apply(i,n.concat(u.call(arguments)));return Object(s)===s?s:i}return e.apply(t,n.concat(u.call(arguments)))}},x.partial=function(e){var t=u.call(arguments,1);return function(){return e.apply(this,t.concat(u.call(arguments)))}},x.bindAll=function(e){var t=u.call(arguments,1);if(t.length===0)throw new Error("bindAll must be passed function names");return T(t,function(t){e[t]=x.bind(e[t],e)}),e},x.memoize=function(e,t){var n={};return t||(t=x.identity),function(){var r=t.apply(this,arguments);return x.has(n,r)?n[r]:n[r]=e.apply(this,arguments)}},x.delay=function(e,t){var n=u.call(arguments,2);return setTimeout(function(){return e.apply(null,n)},t)},x.defer=function(e){return x.delay.apply(x,[e,1].concat(u.call(arguments,1)))},x.throttle=function(e,t,n){var r,i,s,o,u=0,a=function(){u=new Date,s=null,o=e.apply(r,i)};return function(){var f=new Date;!u&&n===!1&&(u=f);var l=t-(f-u);return r=this,i=arguments,l<=0?(clearTimeout(s),s=null,u=f,o=e.apply(r,i)):s||(s=setTimeout(a,l)),o}},x.debounce=function(e,t,n){var r,i;return function(){var s=this,o=arguments,u=function(){r=null,n||(i=e.apply(s,o))},a=n&&!r;return clearTimeout(r),r=setTimeout(u,t),a&&(i=e.apply(s,o)),i}},x.once=function(e){var t=!1,n;return function(){return t?n:(t=!0,n=e.apply(this,arguments),e=null,n)}},x.wrap=function(e,t){return function(){var n=[e];return o.apply(n,arguments),t.apply(this,n)}},x.compose=function(){var e=arguments;return function(){var t=arguments;for(var n=e.length-1;n>=0;n--)t=[e[n].apply(this,t)];return t[0]}},x.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},x.keys=E||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)x.has(e,n)&&t.push(n);return t},x.values=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push(e[n]);return t},x.pairs=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push([n,e[n]]);return t},x.invert=function(e){var t={};for(var n in e)x.has(e,n)&&(t[e[n]]=n);return t},x.functions=x.methods=function(e){var t=[];for(var n in e)x.isFunction(e[n])&&t.push(n);return t.sort()},x.extend=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},x.pick=function(e){var t={},n=a.apply(r,u.call(arguments,1));return T(n,function(n){n in e&&(t[n]=e[n])}),t},x.omit=function(e){var t={},n=a.apply(r,u.call(arguments,1));for(var i in e)x.contains(n,i)||(t[i]=e[i]);return t},x.defaults=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]===void 0&&(e[n]=t[n])}),e},x.clone=function(e){return x.isObject(e)?x.isArray(e)?e.slice():x.extend({},e):e},x.tap=function(e,t){return t(e),e};var M=function(e,t,n,r){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e instanceof x&&(e=e._wrapped),t instanceof x&&(t=t._wrapped);var i=f.call(e);if(i!=f.call(t))return!1;switch(i){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var s=n.length;while(s--)if(n[s]==e)return r[s]==t;n.push(e),r.push(t);var o=0,u=!0;if(i=="[object Array]"){o=e.length,u=o==t.length;if(u)while(o--)if(!(u=M(e[o],t[o],n,r)))break}else{var a=e.constructor,l=t.constructor;if(a!==l&&!(x.isFunction(a)&&a instanceof a&&x.isFunction(l)&&l instanceof l))return!1;for(var c in e)if(x.has(e,c)){o++;if(!(u=x.has(t,c)&&M(e[c],t[c],n,r)))break}if(u){for(c in t)if(x.has(t,c)&&!(o--))break;u=!o}}return n.pop(),r.pop(),u};x.isEqual=function(e,t){return M(e,t,[],[])},x.isEmpty=function(e){if(e==null)return!0;if(x.isArray(e)||x.isString(e))return e.length===0;for(var t in e)if(x.has(e,t))return!1;return!0},x.isElement=function(e){return!!e&&e.nodeType===1},x.isArray=w||function(e){return f.call(e)=="[object Array]"},x.isObject=function(e){return e===Object(e)},T(["Arguments","Function","String","Number","Date","RegExp"],function(e){x["is"+e]=function(t){return f.call(t)=="[object "+e+"]"}}),x.isArguments(arguments)||(x.isArguments=function(e){return!!e&&!!x.has(e,"callee")}),typeof /./!="function"&&(x.isFunction=function(e){return typeof e=="function"}),x.isFinite=function(e){return isFinite(e)&&!isNaN(parseFloat(e))},x.isNaN=function(e){return x.isNumber(e)&&e!=+e},x.isBoolean=function(e){return e===!0||e===!1||f.call(e)=="[object Boolean]"},x.isNull=function(e){return e===null},x.isUndefined=function(e){return e===void 0},x.has=function(e,t){return l.call(e,t)},x.noConflict=function(){return e._=t,this},x.identity=function(e){return e},x.times=function(e,t,n){var r=Array(e);for(var i=0;i<e;i++)r[i]=t.call(n,i);return r},x.random=function(e,t){return t==null&&(t=e,e=0),e+Math.floor(Math.random()*(t-e+1))};var _={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};_.unescape=x.invert(_.escape);var D={escape:new RegExp("["+x.keys(_.escape).join("")+"]","g"),unescape:new RegExp("("+x.keys(_.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(e){x[e]=function(t){return t==null?"":(""+t).replace(D[e],function(t){return _[e][t]})}}),x.result=function(e,t){if(e==null)return void 0;var n=e[t];return x.isFunction(n)?n.call(e):n},x.mixin=function(e){T(x.functions(e),function(t){var n=x[t]=e[t];x.prototype[t]=function(){var e=[this._wrapped];return o.apply(e,arguments),F.call(this,n.apply(x,e))}})};var P=0;x.uniqueId=function(e){var t=++P+"";return e?e+t:t},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var H=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},j=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(e,t,n){var r;n=x.defaults({},n,x.templateSettings);var i=new RegExp([(n.escape||H).source,(n.interpolate||H).source,(n.evaluate||H).source].join("|")+"|$","g"),s=0,o="__p+='";e.replace(i,function(t,n,r,i,u){return o+=e.slice(s,u).replace(j,function(e){return"\\"+B[e]}),n&&(o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),r&&(o+="'+\n((__t=("+r+"))==null?'':__t)+\n'"),i&&(o+="';\n"+i+"\n__p+='"),s=u+t.length,t}),o+="';\n",n.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{r=new Function(n.variable||"obj","_",o)}catch(u){throw u.source=o,u}if(t)return r(t,x);var a=function(e){return r.call(this,e,x)};return a.source="function("+(n.variable||"obj")+"){\n"+o+"}",a},x.chain=function(e){return x(e).chain()};var F=function(e){return this._chain?x(e).chain():e};x.mixin(x),T(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];x.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),(e=="shift"||e=="splice")&&n.length===0&&delete n[0],F.call(this,n)}}),T(["concat","join","slice"],function(e){var t=r[e];x.prototype[e]=function(){return F.call(this,t.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this),define("libs/underscore",function(e){return function(){var t,n;return t||e._}}(this)),define("utils/utils",["libs/underscore"],function(e){function t(e,t,r){n("GET",e,{},t,r)}function n(e,t,n,r,i){if(e=="GET"||e=="DELETE")t.indexOf("?")==-1?t+="?":t+="&",t+=$.param(n);var s=new XMLHttpRequest;s.open(e,t,!0),s.setRequestHeader("Accept","application/json"),s.setRequestHeader("Cache-Control","no-cache"),s.setRequestHeader("X-Requested-With","XMLHttpRequest"),s.setRequestHeader("Content-Type","application/json"),s.onloadend=function(){var e=s.status;try{response=jQuery.parseJSON(s.responseText)}catch(t){response=s.responseText}e==200?r&&r(response):i&&i(response)},e=="GET"||e=="DELETE"?s.send():s.send(JSON.stringify(n))}function r(e,t){var n=$('<div class="'+e+'"></div>');n.appendTo(":eq(0)");var r=n.css(t);return n.remove(),r}function i(e){$('link[href^="'+e+'"]').length||$('<link href="'+galaxy_config.root+e+'" rel="stylesheet">').appendTo("head")}function s(t,n){return t?e.defaults(t,n):n}function o(e,t){var n="";if(e>=1e11)e/=1e11,n="TB";else if(e>=1e8)e/=1e8,n="GB";else if(e>=1e5)e/=1e5,n="MB";else if(e>=100)e/=100,n="KB";else{if(!(e>0))return"<strong>-</strong>";e*=10,n="b"}var r=Math.round(e)/10;return t?r+" "+n:"<strong>"+r+"</strong> "+n}function u(){return(new Date).getTime().toString(36)}function a(e){var t=$("<p></p>");return t.append(e),t}function f(){var e=new Date,t=(e.getHours()<10?"0":"")+e.getHours(),n=(e.getMinutes()<10?"0":"")+e.getMinutes(),r=e.getDate()+"/"+(e.getMonth()+1)+"/"+e.getFullYear()+", "+t+":"+n;return r}return{cssLoadFile:i,cssGetAttribute:r,get:t,merge:s,bytesToString:o,uuid:u,time:f,wrap:a,request:n}}),define("mvc/ui/ui-modal",["utils/utils"],function(e){var t=Backbone.View.extend({elMain:"body",optionsDefault:{title:"ui-modal",body:"",backdrop:!0,height:null,width:null,closing_events:!1},buttonList:{},initialize:function(e){e&&this._create(e)},show:function(e){this.initialize(e),this.options.height?(this.$body.css("height",this.options.height),this.$body.css("overflow","hidden")):this.$body.css("max-height",$(window).height()/2),this.options.width&&this.$dialog.css("width",this.options.width),this.visible?this.$el.show():this.$el.fadeIn("fast"),this.visible=!0},hide:function(){this.visible=!1,this.$el.fadeOut("fast")},enableButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).prop("disabled",!1)},disableButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).prop("disabled",!0)},showButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).show()},hideButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).hide()},getButton:function(e){var t=this.buttonList[e];return this.$buttons.find("#"+t)},scrollTop:function(){return this.$body.scrollTop()},_create:function(e){var t=this;this.options=_.defaults(e,this.optionsDefault),this.options.body=="progress"&&(this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')),this.$el&&(this.$el.remove(),$(document).off("keyup")),this.setElement(this._template(this.options.title)),this.$dialog=this.$el.find(".modal-dialog"),this.$body=this.$el.find(".modal-body"),this.$footer=this.$el.find(".modal-footer"),this.$buttons=this.$el.find(".buttons"),this.$backdrop=this.$el.find(".modal-backdrop"),this.$body.html(this.options.body),this.options.backdrop||this.$backdrop.removeClass("in");if(this.options.buttons){this.buttonList={};var n=0;$.each(this.options.buttons,function(e,r){var i="button-"+n++;t.$buttons.append($('<button id="'+i+'"></button>').text(e).click(r)).append(" "),t.buttonList[e]=i})}else this.$footer.hide();$(this.elMain).append($(this.el)),this.options.closing_events&&(this.options.buttons.Pause||$(document).on("keyup",function(e){e.keyCode==27&&t.hide()}),this.$el.find(".modal-backdrop").on("click",function(){t.hide()}))},_template:function(e){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+e+"</h4>"+"</div>"+'<div class="modal-body" style="position: static;"></div>'+'<div class="modal-footer">'+'<div class="buttons" style="float: right;"></div>'+"</div>"+"</div"+"</div>"+"</div>"}});return{View:t}}),define("mvc/ui/ui-portlet",["utils/utils"],function(e){var t=Backbone.View.extend({visible:!1,optionsDefault:{title:"",icon:"",buttons:null,body:null,height:null,operations:null,placement:"bottom",overflow:"auto"},$title:null,$content:null,$buttons:null,$operations:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.$content=this.$el.find("#content"),this.$title=this.$el.find("#title-text"),this.options.height&&(this.$el.find("#body").css("height",this.options.height),this.$el.find("#content").css("overflow",this.options.overflow)),this.$buttons=$(this.el).find("#buttons");if(this.options.buttons){var n=this;$.each(this.options.buttons,function(e,t){t.$el.prop("id",e),n.$buttons.append(t.$el)})}else this.$buttons.remove();this.$operations=$(this.el).find("#operations");if(this.options.operations){var n=this;$.each(this.options.operations,function(e,t){t.$el.prop("id",e),n.$operations.append(t.$el)})}this.options.body&&this.append(this.options.body)},append:function(t){this.$content.append(e.wrap(t))},content:function(){return this.$content},show:function(){this.$el.fadeIn("fast"),this.visible=!0},hide:function(){this.$el.fadeOut("fast"),this.visible=!1},enableButton:function(e){this.$buttons.find("#"+e).prop("disabled",!1)},disableButton:function(e){this.$buttons.find("#"+e).prop("disabled",!0)},hideOperation:function(e){this.$operations.find("#"+e).hide()},showOperation:function(e){this.$operations.find("#"+e).show()},setOperation:function(e,t){var n=this.$operations.find("#"+e);n.off("click"),n.on("click",t)},title:function(e){var t=this.$title;return e&&t.html(e),t.html()},_template:function(e){var t='<div class="toolForm portlet-view no-highlight">';if(e.title||e.icon)t+='<div id="title" class="toolFormTitle portlet-title"><div id="operations" class="portlet-operations"/><div style="overflow: hidden;">',e.icon&&(t+='<div class="portlet-title-icon fa '+e.icon+'"> </div>'),t+='<div id="title-text" class="portlet-title-text">'+e.title+"</div>",t+="</div></div>";return t+='<div id="body" class="toolFormBody portlet-body">',e.placement=="top"&&(t+='<div id="buttons" class="portlet-buttons"/>'),t+='<div id="content" class="portlet-content"/>',e.placement=="bottom"&&(t+='<div id="buttons" class="portlet-buttons"/>'),t+="</div></div>",t}});return{View:t}}),define("plugin/library/ui-select",["utils/utils"],function(e){var t=Backbone.View.extend({optionsDefault:{id:"",cls:"",empty:"No data available",visible:!0,wait:!1},selected:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.$select=this.$el.find("#select"),this.$icon=this.$el.find("#icon"),this.selected=this.options.value;var n=this;this.options.onchange&&this.$select.on("change",function(){n.value(n.$select.val())}),this._refresh(),this.options.visible||this.hide(),this.options.wait?this.wait():this.show()},value:function(e){var t=this.selected;e!==undefined&&(this.selected=e,this.$select.val(e));var n=this.selected;return n&&n!=t&&this.options.onchange&&this.options.onchange(n),n},text:function(){return this.$select.find("option:selected").text()},show:function(){this.$icon.removeClass(),this.$icon.addClass("fa fa-caret-down"),this.$select.show(),this.$el.show()},hide:function(){this.$el.hide()},wait:function(){this.$icon.removeClass(),this.$icon.addClass("fa fa-spinner fa-spin"),this.$select.hide()},disabled:function(){return this.$select.is(":disabled")},enable:function(){this.$select.prop("disabled",!1)},disable:function(){this.$select.prop("disabled",!0)},add:function(e){this.$select.append(this._templateOption(e)),this._refresh()},del:function(e){this.$select.find("option[value="+e+"]").remove(),this.$select.trigger("change"),this._refresh()},update:function(e){this.$select.find("option").remove();for(var t in e)this.$select.append(this._templateOption(e[t]));!this.selected&&e.length>0&&this.value(e[0].value),this._refresh()},_refresh:function(){this.$select.find("option[value=null]").remove();var e=this.$select.find("option").length;e==0?(this.$select.append(this._templateOption({value:"null",label:this.options.empty})),this.disable()):(this.enable(),this.selected&&this.$select.val(this.selected))},_exists:function(e){return 0!=this.$select.find("option[value="+e+"]").length},_templateOption:function(e){return'<option value="'+e.value+'">'+e.label+"</option>"},_template:function(e){var t='<div id="'+e.id+'" class="styled-select">'+'<div class="button">'+'<i id="icon"/>'+"</div>"+'<select id="select" class="select '+e.cls+" "+e.id+'">';for(key in e.data){var n=e.data[key],r="";if(n.value==e.value||n.value=="")r="selected";t+='<option value="'+n.value+'" '+r+">"+n.label+"</option>"}return t+="</select></div>",t}});return{View:t}}),define("plugin/library/ui",["utils/utils","plugin/library/ui-select"],function(e,t){var n=Backbone.View.extend({optionsDefault:{title:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options))},title:function(e){this.$el.find("b").html(e)},_template:function(e){return"<label><b>"+e.title+"</b></label>"},value:function(){return options.title}}),r=Backbone.View.extend({optionsDefault:{id:null,title:"","float":"right",cls:"btn-default",icon:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){var t='<button id="'+e.id+'" type="submit" style="margin-right: 5px; float: '+e.float+';" type="button" class="btn '+e.cls+'">';return e.icon&&(t+='<i class="icon fa '+e.icon+'"></i> '),t+=e.title+"</button>",t}}),i=Backbone.View.extend({optionsDefault:{"float":"right",icon:"",tooltip:"",placement:"bottom",title:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){return'<div><span class="fa '+e.icon+'" style="font-size: 1.2em;"/> '+e.title+"</div>"}}),s=Backbone.View.extend({optionsDefault:{title:"",id:null,"float":"right",cls:"icon-btn",icon:"",tooltip:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){var t="";e.title&&(t="width: auto;");var n='<div id="'+e.id+'" style="margin-right: 5px; float: '+e.float+"; "+t+'" class="'+e.cls+'">';return e.title?n+='<div style="margin-right: 5px; margin-left: 5px;"><i class="icon fa '+e.icon+'"/> '+'<span style="position: relative; font-size: 0.8em; font-weight: normal; top: -1px;">'+e.title+"</span>"+"</div>":n+='<i class="icon fa '+e.icon+'"/>',n+="</div>",n}}),o=Backbone.View.extend({optionsDefault:{title:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick)},_template:function(e){return'<div><a href="javascript:void(0)">'+e.title+"</a></div>"}}),u=Backbone.View.extend({optionsDefault:{message:"",status:"info",persistent:!1},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement("<div></div>")},update:function(t){this.options=e.merge(t,this.optionsDefault);if(t.message!=""){this.$el.html(this._template(this.options)),this.$el.fadeIn();if(!t.persistent){var n=this;window.setTimeout(function(){n.$el.is(":visible")?n.$el.fadeOut():n.$el.hide()},3e3)}}else this.$el.fadeOut()},_template:function(e){return'<div class="alert alert-'+e.status+'" style="padding: 2px 2px 2px 10px;">'+e.message+"</div>"}}),a=Backbone.View.extend({optionsDefault:{onclick:null,searchword:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options));var n=this;this.options.onclick&&this.$el.on("submit",function(e){var t=n.$el.find("#search");n.options.onclick(t.val())})},_template:function(e){return'<div class="search"><form onsubmit="return false;"><input id="search" class="form-control input-sm" type="text" name="search" placeholder="Search..." value="'+e.searchword+'">'+'<button type="submit" class="btn search-btn">'+'<i class="fa fa-search"></i>'+"</button>"+"</form>"+"</div>"}}),f=Backbone.View.extend({optionsDefault:{title:"Unlabeled",body:null},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.options.body&&this.$el.find(".body").append(this.options.body)},_template:function(e){return'<div id="title" class="title">'+e.title+":"+"</div>"}}),l=Backbone.View.extend({optionsDefault:{id:"",title:"",target:"",href:"",onunload:null,onclick:null,visible:!0,icon:null,tag:""},$menu:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement($(this._template(this.options)));var n=$(this.el).find(".root"),r=this;n.on("click",function(e){e.preventDefault(),r.options.onclick&&r.options.onclick()}),this.options.visible||this.hide()},show:function(){$(this.el).show()},hide:function(){$(this.el).hide()},addMenu:function(t){var n={title:"",target:"",href:"",onclick:null,divider:!1,icon:null};n=e.merge(t,n),this.$menu||($(this.el).append(this._templateMenu()),this.$menu=$(this.el).find(".menu"));var r=$(this._templateMenuItem(n));r.on("click",function(e){e.preventDefault(),n.onclick&&n.onclick()}),this.$menu.append(r),n.divider&&this.$menu.append($(this._templateDivider()))},_templateMenuItem:function(e){var t='<li><a href="'+e.href+'" target="'+e.target+'">';return e.icon&&(t+='<i class="fa '+e.icon+'"></i>'),t+=" "+e.title+"</a>"+"</li>",t},_templateMenu:function(){return'<ul class="menu dropdown-menu pull-right" role="menu"></ul>'},_templateDivider:function(){return'<li class="divider"></li>'},_template:function(e){var t='<div id="'+e.id+'" class="button-menu btn-group">'+'<button type="button" class="root btn btn-default dropdown-toggle" data-toggle="dropdown">';return e.icon&&(t+='<i class="fa '+e.icon+'"></i>'),"</button></div>",t}}),c=Backbone.View.extend({optionsDefault:{value:"",type:"text",placeholder:"",disabled:!1,visible:!0},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.options.disabled&&this.$el.prop("disabled",!0),this.options.visible||this.$el.hide();var n=this;this.options.onchange&&this.$el.on("input",function(){n.options.onchange()})},value:function(e){return e!==undefined&&this.$el.val(e),this.$el.val()},_template:function(e){return'<input id="'+e.id+'" type="'+e.type+'" value="'+e.value+'" placeholder="'+e.placeholder+'" class="form-control">'}});return{Label:n,Button:r,Icon:i,ButtonIcon:s,Input:c,Anchor:o,Message:u,Searchbox:a,Title:f,Select:t,ButtonMenu:l}}),define("plugin/library/jobs",["utils/utils"],function(e){return Backbone.Model.extend({initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault)},cleanup:function(t){var n=t.get("dataset_id_job");if(n){var r=this;e.request("PUT",config.root+"api/histories/none/contents/"+n,{deleted:!0},function(){r._refreshHdas()})}},submit:function(t,n,r,i){var s=this,o=t.id,u=t.get("type"),a=this.app.types.get(u);data={tool_id:"rkit",inputs:{input:{id:t.get("dataset_id"),src:"hda"},module:u,columns:r,settings:n}},s.cleanup(t),t.state("submit","Sending job request..."),e.request("POST",config.root+"api/tools",data,function(e){if(!e.outputs||e.outputs.length==0)t.state("failed","Job submission failed. No response.");else{s._refreshHdas();var n=e.outputs[0];t.state("queued","Job has been queued..."),t.set("dataset_id_job",n.id),s._loop(n.id,function(e){switch(e.state){case"ok":return t.state("success","Job completed successfully..."),i(e),!0;case"error":return t.state("failed","Job has failed. Please check the history for details."),!0;case"running":return t.state("running","Job is running..."),!1}})}},function(e){var n="";e&&e.message&&e.message.data&&e.message.data.input&&(n=e.message.data.input+"."),t.state("failed","This visualization requires the R-kit. Please make sure it is installed. "+n)})},_loop:function(t,n){var r=this;e.request("GET",config.root+"api/jobs/"+t,{},function(e){n(e)||setTimeout(function(){r._loop(t,n)},r.app.config.get("query_timeout"))})},_refreshHdas:function(){Galaxy&&Galaxy.currHistoryPanel&&Galaxy.currHistoryPanel.refreshHdas()}})}),define("plugin/library/datasets",["utils/utils"],function(e){return Backbone.Collection.extend({list:{},initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault)},request:function(t,n,r){var i=this;if(t.groups)this._fetch(t,n);else{var s=this.list[t.id];if(s){n(s);return}e.request("GET",config.root+"api/datasets/"+t.id,{},function(e){switch(e.state){case"error":r&&r(e);break;default:i.list[t.id]=e,n(e)}})}},_fetch:function(t,n){var r=t.start?t.start:0,i=Math.abs(t.end-t.start),s=this.app.config.get("query_limit");if(!i||i>s)i=s;var o="",u={},a=0;for(var f in t.groups){var l=t.groups[f];for(var c in l.columns){var h=l.columns[c];o+=h+",",u[h]=a,a++}}if(a==0){n({});return}o=o.substring(0,o.length-1);var p=t.groups.slice(0);for(var f in p)p[f].values=[];var d=this;e.request("GET",config.root+"api/datasets/"+t.id,{data_type:"raw_data",provider:"dataset-column",limit:i,offset:r,indeces:o},function(e){for(var i in e.data){var s=e.data[i];for(var o in t.groups){var a=t.groups[o],f={x:parseInt(i)+r};for(var l in a.columns){var c=a.columns[l],h=u[c],d=s[h];if(isNaN(d)||!d)d=0;f[l]=d}p[o].values.push(f)}}n(p)})}})}),define("plugin/models/group",[],function(){return Backbone.Model.extend({defaults:{key:"Data label",date:""},reset:function(){this.clear({silent:!0}).set(this.defaults),this.trigger("reset",this)}})}),define("plugin/models/groups",["plugin/models/group"],function(e){return Backbone.Collection.extend({model:e})});var Visualization=Backbone.Model.extend({defaults:{config:{}},urlRoot:function(){var e="/api/visualizations";return window.galaxy_config&&galaxy_config.root?galaxy_config.root+e:e},initialize:function(e){_.isObject(e.config)&&_.isObject(this.defaults.config)&&_.defaults(e.config,this.defaults.config),this._setUpListeners()},_setUpListeners:function(){},set:function(e,t){if(e==="config"){var n=this.get("config");_.isObject(n)&&(t=_.extend(_.clone(n),t))}return Backbone.Model.prototype.set.call(this,e,t),this},toString:function(){var e=this.get("id")||"";return this.get("title")&&(e+=":"+this.get("title")),"Visualization("+e+")"}}),VisualizationCollection=Backbone.Collection.extend({model:Visualization,url:function(){return galaxy_config.root+"api/visualizations"},initialize:function(e,t){t=t||{}},set:function(e,t){var n=this;e=_.map(e,function(e){var t=n.get(e.id);if(!t)return e;var r=t.toJSON();return _.extend(r,e),r}),Backbone.Collection.prototype.set.call(this,e,t)},toString:function(){return["VisualizationCollection(",[this.historyId,this.length].join(),")"].join("")}});define("mvc/visualization/visualization-model",function(){}),define("plugin/models/chart",["plugin/models/groups","mvc/visualization/visualization-model"],function(e){return Backbone.Model.extend({defaults:{id:null,title:"",type:"",date:null,state:"ok",state_info:""},initialize:function(t){this.groups=new e,this.settings=new Backbone.Model},reset:function(){this.clear({silent:!0}).set(this.defaults),this.groups.reset(),this.settings.clear(),this.trigger("reset",this)},copy:function(e){var t=this;t.clear({silent:!0}).set(this.defaults),t.set(e.attributes),t.settings=e.settings.clone(),t.groups.reset(),e.groups.each(function(e){t.groups.add(e.clone())}),t.trigger("change",t)},state:function(e,t){this.set("state_info",t),this.set("state",e)},ready:function(){return this.get("state")=="ok"||this.get("state")=="failed"}})}),define("plugin/library/storage",["utils/utils","plugin/models/chart","plugin/models/group"],function(e,t,n){return Backbone.Model.extend({vis:null,initialize:function(e){this.app=e,this.chart=this.app.chart,this.options=this.app.options,this.id=this.options.id,this.vis=new Visualization({type:"charts",config:{dataset_id:this.options.config.dataset_id,chart_dict:{}}}),this.id&&(this.vis.id=this.id);var t=this.options.config.chart_dict;t&&(this.vis.get("config").chart_dict=t)},save:function(){var e=this.app.chart;this.vis.get("config").chart_dict={};var t=e.get("title");t!=""&&this.vis.set("title",t);var n={attributes:e.attributes,settings:e.settings.attributes,groups:[]};e.groups.each(function(e){n.groups.push(e.attributes)}),this.vis.get("config").chart_dict=n;var r=this;this.vis.save().fail(function(e,t,n){console.error(e,t,n),alert("Error loading data:\n"+e.responseText)}).then(function(e){e&&e.id&&(r.id=e.id)})},load:function(){var e=this.vis.get("config").chart_dict;if(!e.attributes)return!1;this.chart.set(e.attributes),this.chart.settings.set(e.settings);for(var t in e.groups)this.chart.groups.add(new n(e.groups[t]));return this.chart.state("ok","Loaded previously saved visualization."),!0}})}),define("plugin/views/viewport",["mvc/ui/ui-portlet","plugin/library/ui","utils/utils"],function(e,t,n){return Backbone.View.extend({height:300,initialize:function(e,t){this.app=e,this.chart=this.app.chart,this.options=n.merge(t,this.optionsDefault),this.setElement($(this._template()));var r=this;this.chart.on("redraw",function(){r._draw(r.chart)})},show:function(){this.$el.show()},hide:function(){this.$el.hide()},_draw:function(e){var t=this;if(!e.ready()){this.app.log("viewport:_drawChart()","Invalid attempt to draw chart before completion.");return}this.svg&&this.svg.remove(),this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.setAttribute("height",this.height),this.$el.append(this.svg),this.svg=d3.select(this.$el.find("svg")[0]),e.off("change:state"),e.on("change:state",function(){var n=t.$el.find("#info"),r=n.find("#icon");r.removeClass(),n.show(),n.find("#text").html(e.get("state_info"));var i=e.get("state");switch(i){case"ok":n.hide();break;case"failed":r.addClass("fa fa-warning");break;default:r.addClass("fa fa-spinner fa-spin")}}),e.state("wait","Please wait...");var n=e.get("type"),r=this.app.types.get(n),t=this;require(["plugin/charts/"+n+"/"+n],function(n){var i=new n(t.app,{svg:t.svg});r.execute?t.app.jobs.submit(e,t._defaultSettingsString(e),t._defaultRequestString(e),function(){i.draw(e,t._defaultRequestDictionary(e))}):i.draw(e,t._defaultRequestDictionary(e))})},_template:function(){return'<div><div id="info" style="text-align: center; margin-top: 20px;"><span id="icon" style="font-size: 1.2em; display: inline-block;"/><span id="text" style="position: relative; margin-left: 5px; top: -1px; font-size: 1.0em;"/></div></div>'},_defaultRequestString:function(e){var t=this.app.types.get(e.get("type")),n="",r=0;return e.groups.each(function(e){for(var i in t.columns)n+=i+"_"+ ++r+":"+(parseInt(e.get(i))+1)+", "}),n.substring(0,n.length-2)},_defaultSettingsString:function(e){var t="";for(key in e.settings.attributes)t+=key+":"+e.settings.get(key)+", ";return t.substring(0,t.length-2)},_defaultRequestDictionary:function(e){var t=this.app.types.get(e.get("type")),n={id:e.get("dataset_id"),groups:[]},r=0;return e.groups.each(function(e){var i={};for(var s in t.columns)i[s]=e.get(s);n.groups.push({key:++r+":"+e.get("key"),columns:i})}),n}})}),define("plugin/views/viewer",["utils/utils","plugin/library/ui","mvc/ui/ui-portlet","plugin/models/group","plugin/views/viewport"],function(e,t,n,r,i){return Backbone.View.extend({initialize:function(e,r){this.app=e,this.chart=this.app.chart,this.viewport_view=new i(e);var s=this;this.portlet=new n.View({icon:"fa-bar-chart-o",title:"Viewport",operations:{edit_button:new t.ButtonIcon({icon:"fa-gear",tooltip:"Customize Chart",title:"Customize",onclick:function(){s._wait(s.app.chart,function(){s.app.go("editor")})}})}}),this.portlet.append(this.viewport_view.$el),this.setElement(this.portlet.$el);var s=this;this.chart.on("change:title",function(){s._refreshTitle()})},show:function(){this.$el.show(),$(window).trigger("resize")},hide:function(){this.$el.hide()},_refreshTitle:function(){var e=this.chart.get("title");e&&(e=" - "+e),this.portlet.title("Charts"+e)},_wait:function(e,t){if(e.ready())t();else{var n=this;this.app.modal.show({title:"Please wait!",body:"Your chart is currently being processed. Please wait...",buttons:{Close:function(){n.app.modal.hide()},Retry:function(){n.app.modal.hide(),setTimeout(function(){n._wait(e,t)},n.app.config.get("query_timeout"))}}})}}})}),define("mvc/ui/ui-tabs",["utils/utils"],function(e){var t=Backbone.View.extend({visible:!1,list:{},$nav:null,$content:null,first_tab:null,optionsDefault:{title_new:"",operations:null,onnew:null},initialize:function(t){this.options=e.merge(t,this.optionsDefault);var n=$(this._template(this.options));this.$nav=n.find(".tab-navigation"),this.$content=n.find(".tab-content"),this.setElement(n),this.list={};var r=this;this.options.operations&&$.each(this.options.operations,function(e,t){t.$el.prop("id",e),r.$nav.find(".operations").append(t.$el)});if(this.options.onnew){var i=$(this._template_tab_new(this.options));this.$nav.append(i),i.tooltip({title:"Add a new tab",placement:"bottom",container:r.$el}),i.on("click",function(e){i.tooltip("hide"),r.options.onnew()})}},add:function(e){var t=e.id,n={$title:$(this._template_tab(e)),$content:$(this._template_tab_content(e)),removable:e.ondel?!0:!1};this.list[t]=n,this.options.onnew?this.$nav.find("#new-tab").before(n.$title):this.$nav.append(n.$title),n.$content.append(e.$el),this.$content.append(n.$content),_.size(this.list)==1&&(n.$title.addClass("active"),n.$content.addClass("active"),this.first_tab=t);if(e.ondel){var r=this,i=n.$title.find("#delete");i.tooltip({title:"Delete this tab",placement:"bottom",container:r.$el}),i.on("click",function(){return i.tooltip("destroy"),r.$el.find(".tooltip").remove(),e.ondel(),!1})}e.onclick&&n.$title.on("click",function(){e.onclick()})},del:function(e){var t=this.list[e];t.$title.remove(),t.$content.remove(),delete t,this.first_tab==e&&(this.first_tab=null),this.first_tab!=null&&this.show(this.first_tab)},delRemovable:function(){for(var e in this.list){var t=this.list[e];t.removable&&this.del(e)}},show:function(e){this.$el.fadeIn("fast"),this.visible=!0,e&&this.list[e].$title.find("a").tab("show")},hide:function(){this.$el.fadeOut("fast"),this.visible=!1},hideOperation:function(e){this.$nav.find("#"+e).hide()},showOperation:function(e){this.$nav.find("#"+e).show()},setOperation:function(e,t){var n=this.$nav.find("#"+e);n.off("click"),n.on("click",t)},title:function(e,t){var n=this.list[e].$title.find("#text");return t&&n.html(t),n.html()},_template:function(e){return'<div class="tabbable tabs-left"><ul class="tab-navigation nav nav-tabs"><div class="operations" style="float: right; margin-bottom: 4px;"></div></ul><div class="tab-content"/></div>'},_template_tab_new:function(e){return'<li id="new-tab"><a href="javascript:void(0);"><i style="font-size: 0.8em; margin-right: 5px;" class="fa fa-plus-circle"/>'+e.title_new+"</a>"+"</li>"},_template_tab:function(e){var t='<li id="title-'+e.id+'">'+'<a title="" href="#tab-'+e.id+'" data-toggle="tab" data-original-title="">'+'<span id="text">'+e.title+"</span>";return e.ondel&&(t+='<i id="delete" style="font-size: 0.8em; margin-left: 5px; cursor: pointer;" class="fa fa-minus-circle"/>'),t+="</a></li>",t},_template_tab_content:function(e){return'<div id="tab-'+e.id+'" class="tab-pane"/>'}});return{View:t}}),define("plugin/library/ui-table",["utils/utils"],function(e){var t=Backbone.View.extend({row:null,row_count:0,optionsDefault:{content:"No content available.",onchange:null,ondblclick:null,onconfirm:null},events:{click:"_onclick",dblclick:"_ondblclick"},first:!0,initialize:function(t){this.options=e.merge(t,this.optionsDefault);var n=$(this._template(t));this.$thead=n.find("thead"),this.$tbody=n.find("tbody"),this.$tmessage=n.find("tmessage"),this.setElement(n),this.row=$("<tr></tr>")},addHeader:function(e){var t=$("<th></th>");t.append(e),this.row.append(t)},appendHeader:function(){this.$thead.append(this.row),this.row=$("<tr></tr>")},add:function(e,t){var n=$("<td></td>");t&&n.css("width",t),n.append(e),this.row.append(n)},append:function(e){this._commit(e)},prepend:function(e){this._commit(e,!0)},remove:function(e){var t=this.$tbody.find("#"+e);t.length>0&&(t.remove(),this.row_count--,this._refresh())},removeAll:function(){this.$tbody.html(""),this.row_count=0,this._refresh()},value:function(e){this.before=this.$tbody.find(".current").attr("id"),e!==undefined&&(this.$tbody.find("tr").removeClass("current"),e&&this.$tbody.find("#"+e).addClass("current"));var t=this.$tbody.find(".current").attr("id");return t===undefined?null:(t!=this.before&&this.options.onchange&&this.options.onchange(e),t)},size:function(){return this.$tbody.find("tr").length},_commit:function(e,t){this.remove(e),this.row.attr("id",e),t?this.$tbody.prepend(this.row):this.$tbody.append(this.row),this.row=$("<tr></tr>"),this.row_count++,this._refresh()},_onclick:function(e){var t=this.value(),n=$(e.target).closest("tr").attr("id");n&&t!=n&&(this.options.onconfirm?this.options.onconfirm(n):this.value(n))},_ondblclick:function(e){var t=this.value();t&&this.options.ondblclick&&this.options.ondblclick(t)},_refresh:function(){this.row_count==0?this.$tmessage.show():this.$tmessage.hide()},_template:function(e){return'<div><table class="grid"><thead></thead><tbody style="cursor: pointer;"></tbody></table><tmessage>'+e.content+"</tmessage>"+"<div>"}});return{View:t}}),define("plugin/views/group",["plugin/library/ui-table","plugin/library/ui","utils/utils"],function(e,t,n){return Backbone.View.extend({columns:[],initialize:function(r,i){this.app=r;var s=this;this.chart=this.app.chart,this.group=i.group,this.group_key=new t.Input({placeholder:"Data label",onchange:function(){s.group.set("key",s.group_key.value())}}),this.table=new e.View({content:"No data column."});var o=$("<div/>");o.append(n.wrap((new t.Label({title:"Provide a label:"})).$el)),o.append(n.wrap(this.group_key.$el)),o.append(n.wrap((new t.Label({title:"Select columns:"})).$el)),o.append(n.wrap(this.table.$el)),this.setElement(o);var s=this;this.chart.on("change:dataset_id",function(){s._refreshTable()}),this.chart.on("change:type",function(){s._refreshTable()}),this.group.on("change:key",function(){s._refreshGroupKey()}),this.group.on("change",function(){s._refreshGroup()}),this._refreshTable(),this._refreshGroupKey(),this._refreshGroup()},_refreshTable:function(){var e=this.chart.get("dataset_id"),n=this.chart.get("type");if(!e||!n)return;var r=this,i=this.app.types.get(n);this.table.removeAll();var s={};for(var o in i.columns){var u=this.group.get(o);u||this.group.set(o,0);var a=i.columns[o],f=new t.Select.View({id:"select_"+o,gid:o,onchange:function(e){r.group.set(this.gid,e)},value:u,wait:!0});this.table.add(a.title,"25%"),this.table.add(f.$el),this.table.append(o),s[o]=f}this.app.datasets.request({id:e},function(e){r.columns=[];var t=e.metadata_column_types;for(var n in t)(t[n]=="int"||t[n]=="float")&&r.columns.push({label:"Column: "+(parseInt(n)+1)+" ["+t[n]+"]",value:n});for(var n in s)s[n].update(r.columns),s[n].show()})},_refreshGroup:function(){this.group.set("date",n.time())},_refreshGroupKey:function(){var e=this.group.get("key");e===undefined&&(e=""),this.group_key.value(e)}})}),define("plugin/library/ui-table-form",["plugin/library/ui-table","plugin/library/ui","utils/utils"],function(e,t,n){var r=Backbone.View.extend({initialize:function(r){this.table_title=new t.Label({title:r.title}),this.table=new e.View({content:r.content});var i=$("<div/>");i.append(n.wrap(this.table_title.$el)),i.append(n.wrap(this.table.$el)),this.setElement(i)},title:function(e){this.table_title.title(e)},update:function(e,t){this.table.removeAll();for(var n in e)this._add(n,e[n],t)},_add:function(e,n,r){var i=null,s=n.type;switch(s){case"text":i=new t.Input({placeholder:n.placeholder,onchange:function(){r.set(e,i.value())}});break;case"select":i=new t.Select.View({data:n.data,onchange:function(){r.set(e,i.value())}});break;case"slider":i=new t.Input({placeholder:n.placeholder,onchange:function(){r.set(e,i.value())}});break;case"separator":i=$("<div/>");break;default:console.log("ui-table-form:_add","Unknown setting type ("+n.type+")");return}if(s!="separator"){r.get(e)||r.set(e,n.init),i.value(r.get(e));var o=$("<div/>");o.append(i.$el),o.append('<div class="toolParamHelp" style="font-size: 0.9em;">'+n.info+"</div>"),this.table.add('<span style="white-space: nowrap;">'+n.title+"</span>","25%"),this.table.add(o)}else this.table.add('<h6 style="white-space: nowrap;">'+n.title+":<h6/>"),this.table.add($("<div/>"));this.table.append(e)}});return{View:r}}),define("plugin/views/settings",["plugin/library/ui","plugin/library/ui-table-form","utils/utils"],function(e,t,n){return Backbone.View.extend({initialize:function(e,n){this.app=e;var r=this;this.chart=this.app.chart,this.form=new t.View({title:"Chart options:",content:"This chart type does not provide any options."}),this.setElement(this.form.$el);var r=this;this.chart.on("change",function(){r._refreshTable()})},_refreshTable:function(){var e=this.chart.get("type");if(!e)return;var t=this.app.types.get(e);this.form.title(t.title+":"),this.form.update(t.settings,this.chart.settings)}})}),define("plugin/views/editor",["mvc/ui/ui-tabs","plugin/library/ui-table","plugin/library/ui","mvc/ui/ui-portlet","utils/utils","plugin/models/chart","plugin/models/group","plugin/views/group","plugin/views/settings"],function(e,t,n,r,i,s,o,u,a){return Backbone.View.extend({optionsDefault:{header:!0,content:"No content available."},initialize:function(s,o){this.app=s,this.chart=this.app.chart,this.options=i.merge(o,this.optionsDefault),this.portlet=new r.View({icon:"fa-bar-chart-o",title:"Editor",operations:{save:new n.ButtonIcon({icon:"fa-save",tooltip:"Draw Chart",title:"Draw",onclick:function(){u.app.go("viewer"),u._saveChart()}}),back:new n.ButtonIcon({icon:"fa-caret-left",tooltip:"Return to Viewer",title:"Return",onclick:function(){u.app.go("viewer"),u.app.storage.load()}})}});var u=this;this.table=new t.View({header:!1,onconfirm:function(e){u.chart.groups.length>0?u.app.modal.show({title:"Switching to another chart type?",body:"If you continue your settings and selections will be cleared.",buttons:{Cancel:function(){u.app.modal.hide()},Continue:function(){u.app.modal.hide(),u.table.value(e)}}}):u.table.value(e)},onchange:function(e){u.chart.groups.reset(),u.chart.settings.clear(),u.chart.set({type:e})},ondblclick:function(e){u.tabs.show("settings")},content:"No chart types available"});var f=0,l=s.types.attributes;for(var c in l){var h=l[c];this.table.add(++f+"."),h.execute?this.table.add(h.title+" (requires processing)"):this.table.add(h.title),this.table.append(c)}this.tabs=new e.View({title_new:"Add Data",onnew:function(){var e=u._addGroupModel();u.tabs.show(e.id)}}),this.title=new n.Input({placeholder:"Chart title",onchange:function(){u.app.config.set("title",u.title.value())}});var p=$("<div/>");p.append(i.wrap((new n.Label({title:"Provide a chart title:"})).$el)),p.append(i.wrap(this.title.$el)),p.append(i.wrap((new n.Label({title:"Select a chart type:"})).$el)),p.append(i.wrap(this.table.$el)),this.tabs.add({id:"main",title:"Start",$el:p}),this.settings=new a(this.app),this.tabs.add({id:"settings",title:"Configuration",$el:this.settings.$el}),this.portlet.append(this.tabs.$el),this.setElement(this.portlet.$el),this.tabs.hideOperation("back");var u=this;this.chart.on("change:title",function(e){u._refreshTitle()}),this.chart.on("change:type",function(e){u.table.value(e.get("type"))}),this.chart.on("reset",function(e){u._resetChart()}),this.app.chart.on("redraw",function(e){u.portlet.showOperation("back")}),this.app.chart.groups.on("add",function(e){u._addGroup(e)}),this.app.chart.groups.on("remove",function(e){u._removeGroup(e)}),this.app.chart.groups.on("reset",function(e){u._removeAllGroups()}),this.app.chart.groups.on("change:key",function(e){u._refreshGroupKey()}),this._resetChart()},show:function(){this.$el.show()},hide:function(){this.$el.hide()},_refreshTitle:function(){var e=this.chart.get("title");e&&(e=" - "+e),this.portlet.title("Charts"+e)},_refreshGroupKey:function(){var e=this,t=0;this.chart.groups.each(function(n){var r=n.get("key","");r==""&&(r="Chart data"),e.tabs.title(n.id,++t+": "+r)})},_addGroupModel:function(){var e=new o({id:i.uuid()});return this.chart.groups.add(e),e},_addGroup:function(e){var t=this,n=new u(this.app,{group:e}),r=t.chart.groups.length;this.tabs.add({id:e.id,$el:n.$el,ondel:function(){t.chart.groups.remove(e.id)}}),this._refreshGroupKey()},_removeGroup:function(e){this.tabs.del(e.id),this._refreshGroupKey()},_removeAllGroups:function(e){this.tabs.delRemovable()},_resetChart:function(){this.chart.set("id",i.uuid()),this.chart.set("type","bardiagram"),this.chart.set("dataset_id",this.app.options.config.dataset_id),this.chart.set("title","New Chart"),this.portlet.hideOperation("back")},_saveChart:function(){this.chart.set({type:this.table.value(),title:this.title.value(),date:i.time()}),this.chart.groups.length==0&&this._addGroupModel(),this.chart.trigger("redraw"),this.app.storage.save()}})}),define("plugin/models/config",[],function(){return Backbone.Model.extend({defaults:{query_limit:1e3,query_timeout:500}})}),define("plugin/charts/_nvd3/config",[],function(){return{title:"",columns:{y:{title:"Values for y-axis"}},settings:{separator_label:{title:"X axis",type:"separator"},x_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"X-axis",placeholder:"Axis label"},x_axis_type:{title:"Axis value type",info:"Select the value type of the axis.",type:"select",init:"f",data:[{label:"-- Do not show values --",value:"hide"},{label:"Float",value:"f"},{label:"Exponent",value:"e"},{label:"Integer",value:"d"},{label:"Percentage",value:"p"},{label:"Rounded",value:"r"},{label:"SI-prefix",value:"s"}]},x_axis_tick:{title:"Axis tick format",info:"Select the tick format for the axis.",type:"select",init:".1",data:[{label:"0.00001",value:".5"},{label:"0.0001",value:".4"},{label:"0.001",value:".3"},{label:"0.01",value:".2"},{label:"0.1",value:".1"},{label:"1",value:"1"}]},separator_tick:{title:"Y axis",type:"separator"},y_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"Y-axis",placeholder:"Axis label"},y_axis_type:{title:"Axis value type",info:"Select the value type of the axis.",type:"select",init:"f",data:[{label:"-- Do not show values --",value:"hide"},{label:"Float",value:"f"},{label:"Exponent",value:"e"},{label:"Integer",value:"d"},{label:"Percentage",value:"p"},{label:"Rounded",value:"r"},{label:"SI-prefix",value:"s"}]},y_axis_tick:{title:"Axis tick format",info:"Select the tick format for the axis.",type:"select",init:".1",data:[{label:"0.00001",value:".5"},{label:"0.0001",value:".4"},{label:"0.001",value:".3"},{label:"0.01",value:".2"},{label:"0.1",value:".1"},{label:"1",value:"1"}]},separator_legend:{title:"Others",type:"separator"},show_legend:{title:"Show legend",info:"Would you like to add a legend?",type:"select",init:"true",data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]}}}}),define("plugin/charts/bardiagram/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Bar diagram"})}),define("plugin/charts/histogram/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Histogram",execute:!0,columns:{y:{title:"Observations"}},settings:{x_axis_label:{init:"Breaks"},y_axis_label:{init:"Density"},y_axis_tick:{init:".3"}}})}),define("plugin/charts/horizontal/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Bar diagram (horizontal)",settings:{x_axis_type:{init:"hide"}}})}),define("plugin/charts/line/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line chart"})}),define("plugin/charts/linewithfocus/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line with focus"})}),define("plugin/charts/piechart/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Pie chart"})}),define("plugin/charts/scatterplot/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Scatter plot",columns:{x:{title:"Values for x-axis"}}})}),define("plugin/charts/stackedarea/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stacked area"})}),define("plugin/charts/types",["plugin/charts/bardiagram/config","plugin/charts/histogram/config","plugin/charts/horizontal/config","plugin/charts/line/config","plugin/charts/linewithfocus/config","plugin/charts/piechart/config","plugin/charts/scatterplot/config","plugin/charts/stackedarea/config"],function(e,t,n,r,i,s,o,u){return Backbone.Model.extend({defaults:{bardiagram:e,horizontal:n,histogram:t,line:r,linewithfocus:i,piechart:s,scatterplot:o,stackedarea:u}})}),define("plugin/app",["mvc/ui/ui-modal","mvc/ui/ui-portlet","plugin/library/ui","utils/utils","plugin/library/jobs","plugin/library/datasets","plugin/library/storage","plugin/views/viewer","plugin/views/editor","plugin/models/config","plugin/models/chart","plugin/charts/types"],function(e,t,n,r,i,s,o,u,a,f,l,c){return Backbone.View.extend({initialize:function(t){this.options=t,Galaxy&&Galaxy.modal?this.modal=Galaxy.modal:this.modal=new e.View,this.config=new f,this.types=new c,this.chart=new l,this.jobs=new i(this),this.datasets=new s(this),this.storage=new o(this),this.viewer_view=new u(this),this.editor_view=new a(this),this.$el.append(this.viewer_view.$el),this.$el.append(this.editor_view.$el),this.storage.load()?(this.go("viewer"),this.chart.trigger("redraw")):this.go("editor")},go:function(e){$(".tooltip").hide();switch(e){case"editor":this.editor_view.show(),this.viewer_view.hide();break;case"viewer":this.editor_view.hide(),this.viewer_view.show()}},execute:function(e){},onunload:function(){},log:function(e,t){console.log(e+" "+t)}})});
\ No newline at end of file
+(function(){var e=this,t=e._,n={},r=Array.prototype,i=Object.prototype,s=Function.prototype,o=r.push,u=r.slice,a=r.concat,f=i.toString,l=i.hasOwnProperty,c=r.forEach,h=r.map,p=r.reduce,d=r.reduceRight,v=r.filter,m=r.every,g=r.some,y=r.indexOf,b=r.lastIndexOf,w=Array.isArray,E=Object.keys,S=s.bind,x=function(e){if(e instanceof x)return e;if(!(this instanceof x))return new x(e);this._wrapped=e};typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(exports=module.exports=x),exports._=x):e._=x,x.VERSION="1.4.4";var T=x.each=x.forEach=function(e,t,r){if(e==null)return;if(c&&e.forEach===c)e.forEach(t,r);else if(e.length===+e.length){for(var i=0,s=e.length;i<s;i++)if(t.call(r,e[i],i,e)===n)return}else for(var o in e)if(x.has(e,o)&&t.call(r,e[o],o,e)===n)return};x.map=x.collect=function(e,t,n){var r=[];return e==null?r:h&&e.map===h?e.map(t,n):(T(e,function(e,i,s){r.push(t.call(n,e,i,s))}),r)};var N="Reduce of empty array with no initial value";x.reduce=x.foldl=x.inject=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(p&&e.reduce===p)return r&&(t=x.bind(t,r)),i?e.reduce(t,n):e.reduce(t);T(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError(N);return n},x.reduceRight=x.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(d&&e.reduceRight===d)return r&&(t=x.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=x.keys(e);s=o.length}T(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)});if(!i)throw new TypeError(N);return n},x.find=x.detect=function(e,t,n){var r;return C(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},x.filter=x.select=function(e,t,n){var r=[];return e==null?r:v&&e.filter===v?e.filter(t,n):(T(e,function(e,i,s){t.call(n,e,i,s)&&r.push(e)}),r)},x.reject=function(e,t,n){return x.filter(e,function(e,r,i){return!t.call(n,e,r,i)},n)},x.every=x.all=function(e,t,r){t||(t=x.identity);var i=!0;return e==null?i:m&&e.every===m?e.every(t,r):(T(e,function(e,s,o){if(!(i=i&&t.call(r,e,s,o)))return n}),!!i)};var C=x.some=x.any=function(e,t,r){t||(t=x.identity);var i=!1;return e==null?i:g&&e.some===g?e.some(t,r):(T(e,function(e,s,o){if(i||(i=t.call(r,e,s,o)))return n}),!!i)};x.contains=x.include=function(e,t){return e==null?!1:y&&e.indexOf===y?e.indexOf(t)!=-1:C(e,function(e){return e===t})},x.invoke=function(e,t){var n=u.call(arguments,2),r=x.isFunction(t);return x.map(e,function(e){return(r?t:e[t]).apply(e,n)})},x.pluck=function(e,t){return x.map(e,function(e){return e[t]})},x.where=function(e,t,n){return x.isEmpty(t)?n?void 0:[]:x[n?"find":"filter"](e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},x.findWhere=function(e,t){return x.where(e,t,!0)},x.max=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.max.apply(Math,e);if(!t&&x.isEmpty(e))return-Infinity;var r={computed:-Infinity,value:-Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},x.min=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.min.apply(Math,e);if(!t&&x.isEmpty(e))return Infinity;var r={computed:Infinity,value:Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o<r.computed&&(r={value:e,computed:o})}),r.value},x.shuffle=function(e){var t,n=0,r=[];return T(e,function(e){t=x.random(n++),r[n-1]=r[t],r[t]=e}),r};var k=function(e){return x.isFunction(e)?e:function(t){return t[e]}};x.sortBy=function(e,t,n){var r=k(t);return x.pluck(x.map(e,function(e,t,i){return{value:e,index:t,criteria:r.call(n,e,t,i)}}).sort(function(e,t){var n=e.criteria,r=t.criteria;if(n!==r){if(n>r||n===void 0)return 1;if(n<r||r===void 0)return-1}return e.index<t.index?-1:1}),"value")};var L=function(e,t,n,r){var i={},s=k(t==null?x.identity:t);return T(e,function(t,o){var u=s.call(n,t,o,e);r(i,u,t)}),i};x.groupBy=function(e,t,n){return L(e,t,n,function(e,t,n){(x.has(e,t)?e[t]:e[t]=[]).push(n)})},x.countBy=function(e,t,n){return L(e,t,n,function(e,t){x.has(e,t)||(e[t]=0),e[t]++})},x.sortedIndex=function(e,t,n,r){n=n==null?x.identity:k(n);var i=n.call(r,t),s=0,o=e.length;while(s<o){var u=s+o>>>1;n.call(r,e[u])<i?s=u+1:o=u}return s},x.toArray=function(e){return e?x.isArray(e)?u.call(e):e.length===+e.length?x.map(e,x.identity):x.values(e):[]},x.size=function(e){return e==null?0:e.length===+e.length?e.length:x.keys(e).length},x.first=x.head=x.take=function(e,t,n){return e==null?void 0:t!=null&&!n?u.call(e,0,t):e[0]},x.initial=function(e,t,n){return u.call(e,0,e.length-(t==null||n?1:t))},x.last=function(e,t,n){return e==null?void 0:t!=null&&!n?u.call(e,Math.max(e.length-t,0)):e[e.length-1]},x.rest=x.tail=x.drop=function(e,t,n){return u.call(e,t==null||n?1:t)},x.compact=function(e){return x.filter(e,x.identity)};var A=function(e,t,n){return T(e,function(e){x.isArray(e)?t?o.apply(n,e):A(e,t,n):n.push(e)}),n};x.flatten=function(e,t){return A(e,t,[])},x.without=function(e){return x.difference(e,u.call(arguments,1))},x.uniq=x.unique=function(e,t,n,r){x.isFunction(t)&&(r=n,n=t,t=!1);var i=n?x.map(e,n,r):e,s=[],o=[];return T(i,function(n,r){if(t?!r||o[o.length-1]!==n:!x.contains(o,n))o.push(n),s.push(e[r])}),s},x.union=function(){return x.uniq(a.apply(r,arguments))},x.intersection=function(e){var t=u.call(arguments,1);return x.filter(x.uniq(e),function(e){return x.every(t,function(t){return x.indexOf(t,e)>=0})})},x.difference=function(e){var t=a.apply(r,u.call(arguments,1));return x.filter(e,function(e){return!x.contains(t,e)})},x.zip=function(){var e=u.call(arguments),t=x.max(x.pluck(e,"length")),n=new Array(t);for(var r=0;r<t;r++)n[r]=x.pluck(e,""+r);return n},x.unzip=function(e){var t=[];return x.each(e,function(e,n){x.each(e,function(e,r){t.length<=r&&(t[r]=[]),t[r][n]=e})}),t},x.object=function(e,t){if(e==null)return{};var n={};for(var r=0,i=e.length;r<i;r++)t?n[e[r]]=t[r]:n[e[r][0]]=e[r][1];return n},x.indexOf=function(e,t,n){if(e==null)return-1;var r=0,i=e.length;if(n){if(typeof n!="number")return r=x.sortedIndex(e,t),e[r]===t?r:-1;r=n<0?Math.max(0,i+n):n}if(y&&e.indexOf===y)return e.indexOf(t,n);for(;r<i;r++)if(e[r]===t)return r;return-1},x.lastIndexOf=function(e,t,n){if(e==null)return-1;var r=n!=null;if(b&&e.lastIndexOf===b)return r?e.lastIndexOf(t,n):e.lastIndexOf(t);var i=r?n:e.length;while(i--)if(e[i]===t)return i;return-1},x.range=function(e,t,n){arguments.length<=1&&(t=e||0,e=0),n=arguments[2]||1;var r=Math.max(Math.ceil((t-e)/n),0),i=0,s=new Array(r);while(i<r)s[i++]=e,e+=n;return s};var O=function(){};x.bind=function(e,t){var n,r;if(e.bind===S&&S)return S.apply(e,u.call(arguments,1));if(!x.isFunction(e))throw new TypeError;return n=u.call(arguments,2),r=function(){if(this instanceof r){O.prototype=e.prototype;var i=new O;O.prototype=null;var s=e.apply(i,n.concat(u.call(arguments)));return Object(s)===s?s:i}return e.apply(t,n.concat(u.call(arguments)))}},x.partial=function(e){var t=u.call(arguments,1);return function(){return e.apply(this,t.concat(u.call(arguments)))}},x.bindAll=function(e){var t=u.call(arguments,1);if(t.length===0)throw new Error("bindAll must be passed function names");return T(t,function(t){e[t]=x.bind(e[t],e)}),e},x.memoize=function(e,t){var n={};return t||(t=x.identity),function(){var r=t.apply(this,arguments);return x.has(n,r)?n[r]:n[r]=e.apply(this,arguments)}},x.delay=function(e,t){var n=u.call(arguments,2);return setTimeout(function(){return e.apply(null,n)},t)},x.defer=function(e){return x.delay.apply(x,[e,1].concat(u.call(arguments,1)))},x.throttle=function(e,t,n){var r,i,s,o,u=0,a=function(){u=new Date,s=null,o=e.apply(r,i)};return function(){var f=new Date;!u&&n===!1&&(u=f);var l=t-(f-u);return r=this,i=arguments,l<=0?(clearTimeout(s),s=null,u=f,o=e.apply(r,i)):s||(s=setTimeout(a,l)),o}},x.debounce=function(e,t,n){var r,i;return function(){var s=this,o=arguments,u=function(){r=null,n||(i=e.apply(s,o))},a=n&&!r;return clearTimeout(r),r=setTimeout(u,t),a&&(i=e.apply(s,o)),i}},x.once=function(e){var t=!1,n;return function(){return t?n:(t=!0,n=e.apply(this,arguments),e=null,n)}},x.wrap=function(e,t){return function(){var n=[e];return o.apply(n,arguments),t.apply(this,n)}},x.compose=function(){var e=arguments;return function(){var t=arguments;for(var n=e.length-1;n>=0;n--)t=[e[n].apply(this,t)];return t[0]}},x.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},x.keys=E||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)x.has(e,n)&&t.push(n);return t},x.values=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push(e[n]);return t},x.pairs=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push([n,e[n]]);return t},x.invert=function(e){var t={};for(var n in e)x.has(e,n)&&(t[e[n]]=n);return t},x.functions=x.methods=function(e){var t=[];for(var n in e)x.isFunction(e[n])&&t.push(n);return t.sort()},x.extend=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},x.pick=function(e){var t={},n=a.apply(r,u.call(arguments,1));return T(n,function(n){n in e&&(t[n]=e[n])}),t},x.omit=function(e){var t={},n=a.apply(r,u.call(arguments,1));for(var i in e)x.contains(n,i)||(t[i]=e[i]);return t},x.defaults=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]===void 0&&(e[n]=t[n])}),e},x.clone=function(e){return x.isObject(e)?x.isArray(e)?e.slice():x.extend({},e):e},x.tap=function(e,t){return t(e),e};var M=function(e,t,n,r){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e instanceof x&&(e=e._wrapped),t instanceof x&&(t=t._wrapped);var i=f.call(e);if(i!=f.call(t))return!1;switch(i){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var s=n.length;while(s--)if(n[s]==e)return r[s]==t;n.push(e),r.push(t);var o=0,u=!0;if(i=="[object Array]"){o=e.length,u=o==t.length;if(u)while(o--)if(!(u=M(e[o],t[o],n,r)))break}else{var a=e.constructor,l=t.constructor;if(a!==l&&!(x.isFunction(a)&&a instanceof a&&x.isFunction(l)&&l instanceof l))return!1;for(var c in e)if(x.has(e,c)){o++;if(!(u=x.has(t,c)&&M(e[c],t[c],n,r)))break}if(u){for(c in t)if(x.has(t,c)&&!(o--))break;u=!o}}return n.pop(),r.pop(),u};x.isEqual=function(e,t){return M(e,t,[],[])},x.isEmpty=function(e){if(e==null)return!0;if(x.isArray(e)||x.isString(e))return e.length===0;for(var t in e)if(x.has(e,t))return!1;return!0},x.isElement=function(e){return!!e&&e.nodeType===1},x.isArray=w||function(e){return f.call(e)=="[object Array]"},x.isObject=function(e){return e===Object(e)},T(["Arguments","Function","String","Number","Date","RegExp"],function(e){x["is"+e]=function(t){return f.call(t)=="[object "+e+"]"}}),x.isArguments(arguments)||(x.isArguments=function(e){return!!e&&!!x.has(e,"callee")}),typeof /./!="function"&&(x.isFunction=function(e){return typeof e=="function"}),x.isFinite=function(e){return isFinite(e)&&!isNaN(parseFloat(e))},x.isNaN=function(e){return x.isNumber(e)&&e!=+e},x.isBoolean=function(e){return e===!0||e===!1||f.call(e)=="[object Boolean]"},x.isNull=function(e){return e===null},x.isUndefined=function(e){return e===void 0},x.has=function(e,t){return l.call(e,t)},x.noConflict=function(){return e._=t,this},x.identity=function(e){return e},x.times=function(e,t,n){var r=Array(e);for(var i=0;i<e;i++)r[i]=t.call(n,i);return r},x.random=function(e,t){return t==null&&(t=e,e=0),e+Math.floor(Math.random()*(t-e+1))};var _={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};_.unescape=x.invert(_.escape);var D={escape:new RegExp("["+x.keys(_.escape).join("")+"]","g"),unescape:new RegExp("("+x.keys(_.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(e){x[e]=function(t){return t==null?"":(""+t).replace(D[e],function(t){return _[e][t]})}}),x.result=function(e,t){if(e==null)return void 0;var n=e[t];return x.isFunction(n)?n.call(e):n},x.mixin=function(e){T(x.functions(e),function(t){var n=x[t]=e[t];x.prototype[t]=function(){var e=[this._wrapped];return o.apply(e,arguments),F.call(this,n.apply(x,e))}})};var P=0;x.uniqueId=function(e){var t=++P+"";return e?e+t:t},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var H=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},j=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(e,t,n){var r;n=x.defaults({},n,x.templateSettings);var i=new RegExp([(n.escape||H).source,(n.interpolate||H).source,(n.evaluate||H).source].join("|")+"|$","g"),s=0,o="__p+='";e.replace(i,function(t,n,r,i,u){return o+=e.slice(s,u).replace(j,function(e){return"\\"+B[e]}),n&&(o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),r&&(o+="'+\n((__t=("+r+"))==null?'':__t)+\n'"),i&&(o+="';\n"+i+"\n__p+='"),s=u+t.length,t}),o+="';\n",n.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{r=new Function(n.variable||"obj","_",o)}catch(u){throw u.source=o,u}if(t)return r(t,x);var a=function(e){return r.call(this,e,x)};return a.source="function("+(n.variable||"obj")+"){\n"+o+"}",a},x.chain=function(e){return x(e).chain()};var F=function(e){return this._chain?x(e).chain():e};x.mixin(x),T(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];x.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),(e=="shift"||e=="splice")&&n.length===0&&delete n[0],F.call(this,n)}}),T(["concat","join","slice"],function(e){var t=r[e];x.prototype[e]=function(){return F.call(this,t.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this),define("libs/underscore",function(e){return function(){var t,n;return t||e._}}(this)),define("utils/utils",["libs/underscore"],function(e){function t(e,t,r){n("GET",e,{},t,r)}function n(e,t,n,r,i){if(e=="GET"||e=="DELETE")t.indexOf("?")==-1?t+="?":t+="&",t+=$.param(n);var s=new XMLHttpRequest;s.open(e,t,!0),s.setRequestHeader("Accept","application/json"),s.setRequestHeader("Cache-Control","no-cache"),s.setRequestHeader("X-Requested-With","XMLHttpRequest"),s.setRequestHeader("Content-Type","application/json"),s.onloadend=function(){var e=s.status;try{response=jQuery.parseJSON(s.responseText)}catch(t){response=s.responseText}e==200?r&&r(response):i&&i(response)},e=="GET"||e=="DELETE"?s.send():s.send(JSON.stringify(n))}function r(e,t){var n=$('<div class="'+e+'"></div>');n.appendTo(":eq(0)");var r=n.css(t);return n.remove(),r}function i(e){$('link[href^="'+e+'"]').length||$('<link href="'+galaxy_config.root+e+'" rel="stylesheet">').appendTo("head")}function s(t,n){return t?e.defaults(t,n):n}function o(e,t){var n="";if(e>=1e11)e/=1e11,n="TB";else if(e>=1e8)e/=1e8,n="GB";else if(e>=1e5)e/=1e5,n="MB";else if(e>=100)e/=100,n="KB";else{if(!(e>0))return"<strong>-</strong>";e*=10,n="b"}var r=Math.round(e)/10;return t?r+" "+n:"<strong>"+r+"</strong> "+n}function u(){return(new Date).getTime().toString(36)}function a(e){var t=$("<p></p>");return t.append(e),t}function f(){var e=new Date,t=(e.getHours()<10?"0":"")+e.getHours(),n=(e.getMinutes()<10?"0":"")+e.getMinutes(),r=e.getDate()+"/"+(e.getMonth()+1)+"/"+e.getFullYear()+", "+t+":"+n;return r}return{cssLoadFile:i,cssGetAttribute:r,get:t,merge:s,bytesToString:o,uuid:u,time:f,wrap:a,request:n}}),define("mvc/ui/ui-modal",["utils/utils"],function(e){var t=Backbone.View.extend({elMain:"body",optionsDefault:{title:"ui-modal",body:"",backdrop:!0,height:null,width:null,closing_events:!1},buttonList:{},initialize:function(e){e&&this._create(e)},show:function(e){this.initialize(e),this.options.height?(this.$body.css("height",this.options.height),this.$body.css("overflow","hidden")):this.$body.css("max-height",$(window).height()/2),this.options.width&&this.$dialog.css("width",this.options.width),this.visible?this.$el.show():this.$el.fadeIn("fast"),this.visible=!0},hide:function(){this.visible=!1,this.$el.fadeOut("fast")},enableButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).prop("disabled",!1)},disableButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).prop("disabled",!0)},showButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).show()},hideButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).hide()},getButton:function(e){var t=this.buttonList[e];return this.$buttons.find("#"+t)},scrollTop:function(){return this.$body.scrollTop()},_create:function(e){var t=this;this.options=_.defaults(e,this.optionsDefault),this.options.body=="progress"&&(this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')),this.$el&&(this.$el.remove(),$(document).off("keyup")),this.setElement(this._template(this.options.title)),this.$dialog=this.$el.find(".modal-dialog"),this.$body=this.$el.find(".modal-body"),this.$footer=this.$el.find(".modal-footer"),this.$buttons=this.$el.find(".buttons"),this.$backdrop=this.$el.find(".modal-backdrop"),this.$body.html(this.options.body),this.options.backdrop||this.$backdrop.removeClass("in");if(this.options.buttons){this.buttonList={};var n=0;$.each(this.options.buttons,function(e,r){var i="button-"+n++;t.$buttons.append($('<button id="'+i+'"></button>').text(e).click(r)).append(" "),t.buttonList[e]=i})}else this.$footer.hide();$(this.elMain).append($(this.el)),this.options.closing_events&&(this.options.buttons.Pause||$(document).on("keyup",function(e){e.keyCode==27&&t.hide()}),this.$el.find(".modal-backdrop").on("click",function(){t.hide()}))},_template:function(e){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+e+"</h4>"+"</div>"+'<div class="modal-body" style="position: static;"></div>'+'<div class="modal-footer">'+'<div class="buttons" style="float: right;"></div>'+"</div>"+"</div"+"</div>"+"</div>"}});return{View:t}}),define("mvc/ui/ui-portlet",["utils/utils"],function(e){var t=Backbone.View.extend({visible:!1,optionsDefault:{title:"",icon:"",buttons:null,body:null,height:null,operations:null,placement:"bottom",overflow:"auto"},$title:null,$content:null,$buttons:null,$operations:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.$content=this.$el.find("#content"),this.$title=this.$el.find("#title-text"),this.options.height&&(this.$el.find("#body").css("height",this.options.height),this.$el.find("#content").css("overflow",this.options.overflow)),this.$buttons=$(this.el).find("#buttons");if(this.options.buttons){var n=this;$.each(this.options.buttons,function(e,t){t.$el.prop("id",e),n.$buttons.append(t.$el)})}else this.$buttons.remove();this.$operations=$(this.el).find("#operations");if(this.options.operations){var n=this;$.each(this.options.operations,function(e,t){t.$el.prop("id",e),n.$operations.append(t.$el)})}this.options.body&&this.append(this.options.body)},append:function(t){this.$content.append(e.wrap(t))},content:function(){return this.$content},show:function(){this.$el.fadeIn("fast"),this.visible=!0},hide:function(){this.$el.fadeOut("fast"),this.visible=!1},enableButton:function(e){this.$buttons.find("#"+e).prop("disabled",!1)},disableButton:function(e){this.$buttons.find("#"+e).prop("disabled",!0)},hideOperation:function(e){this.$operations.find("#"+e).hide()},showOperation:function(e){this.$operations.find("#"+e).show()},setOperation:function(e,t){var n=this.$operations.find("#"+e);n.off("click"),n.on("click",t)},title:function(e){var t=this.$title;return e&&t.html(e),t.html()},_template:function(e){var t='<div class="toolForm portlet-view no-highlight">';if(e.title||e.icon)t+='<div id="title" class="toolFormTitle portlet-title"><div id="operations" class="portlet-operations"/><div style="overflow: hidden;">',e.icon&&(t+='<div class="portlet-title-icon fa '+e.icon+'"> </div>'),t+='<div id="title-text" class="portlet-title-text">'+e.title+"</div>",t+="</div></div>";return t+='<div id="body" class="toolFormBody portlet-body">',e.placement=="top"&&(t+='<div id="buttons" class="portlet-buttons"/>'),t+='<div id="content" class="portlet-content"/>',e.placement=="bottom"&&(t+='<div id="buttons" class="portlet-buttons"/>'),t+="</div></div>",t}});return{View:t}}),define("plugin/library/ui-select",["utils/utils"],function(e){var t=Backbone.View.extend({optionsDefault:{id:"",cls:"",empty:"No data available",visible:!0,wait:!1},selected:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.$select=this.$el.find("#select"),this.$icon=this.$el.find("#icon"),this.selected=this.options.value;var n=this;this.options.onchange&&this.$select.on("change",function(){n.value(n.$select.val())}),this._refresh(),this.options.visible||this.hide(),this.options.wait?this.wait():this.show()},value:function(e){var t=this.selected;e!==undefined&&(this.selected=e,this.$select.val(e));var n=this.selected;return n&&n!=t&&this.options.onchange&&this.options.onchange(n),n},text:function(){return this.$select.find("option:selected").text()},show:function(){this.$icon.removeClass(),this.$icon.addClass("fa fa-caret-down"),this.$select.show(),this.$el.show()},hide:function(){this.$el.hide()},wait:function(){this.$icon.removeClass(),this.$icon.addClass("fa fa-spinner fa-spin"),this.$select.hide()},disabled:function(){return this.$select.is(":disabled")},enable:function(){this.$select.prop("disabled",!1)},disable:function(){this.$select.prop("disabled",!0)},add:function(e){this.$select.append(this._templateOption(e)),this._refresh()},del:function(e){this.$select.find("option[value="+e+"]").remove(),this.$select.trigger("change"),this._refresh()},update:function(e){this.$select.find("option").remove();for(var t in e)this.$select.append(this._templateOption(e[t]));!this.selected&&e.length>0&&this.value(e[0].value),this._refresh()},_refresh:function(){this.$select.find("option[value=null]").remove();var e=this.$select.find("option").length;e==0?(this.$select.append(this._templateOption({value:"null",label:this.options.empty})),this.disable()):(this.enable(),this.selected&&this.$select.val(this.selected))},_exists:function(e){return 0!=this.$select.find("option[value="+e+"]").length},_templateOption:function(e){return'<option value="'+e.value+'">'+e.label+"</option>"},_template:function(e){var t='<div id="'+e.id+'" class="styled-select">'+'<div class="button">'+'<i id="icon"/>'+"</div>"+'<select id="select" class="select '+e.cls+" "+e.id+'">';for(key in e.data){var n=e.data[key],r="";if(n.value==e.value||n.value=="")r="selected";t+='<option value="'+n.value+'" '+r+">"+n.label+"</option>"}return t+="</select></div>",t}});return{View:t}}),define("plugin/library/ui",["utils/utils","plugin/library/ui-select"],function(e,t){var n=Backbone.View.extend({optionsDefault:{title:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options))},title:function(e){this.$el.find("b").html(e)},_template:function(e){return"<label><b>"+e.title+"</b></label>"},value:function(){return options.title}}),r=Backbone.View.extend({optionsDefault:{id:null,title:"","float":"right",cls:"btn-default",icon:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){var t='<button id="'+e.id+'" type="submit" style="margin-right: 5px; float: '+e.float+';" type="button" class="btn '+e.cls+'">';return e.icon&&(t+='<i class="icon fa '+e.icon+'"></i> '),t+=e.title+"</button>",t}}),i=Backbone.View.extend({optionsDefault:{"float":"right",icon:"",tooltip:"",placement:"bottom",title:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){return'<div><span class="fa '+e.icon+'" style="font-size: 1.2em;"/> '+e.title+"</div>"}}),s=Backbone.View.extend({optionsDefault:{title:"",id:null,"float":"right",cls:"icon-btn",icon:"",tooltip:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){var t="";e.title&&(t="width: auto;");var n='<div id="'+e.id+'" style="margin-right: 5px; float: '+e.float+"; "+t+'" class="'+e.cls+'">';return e.title?n+='<div style="margin-right: 5px; margin-left: 5px;"><i class="icon fa '+e.icon+'"/> '+'<span style="position: relative; font-size: 0.8em; font-weight: normal; top: -1px;">'+e.title+"</span>"+"</div>":n+='<i class="icon fa '+e.icon+'"/>',n+="</div>",n}}),o=Backbone.View.extend({optionsDefault:{title:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick)},_template:function(e){return'<div><a href="javascript:void(0)">'+e.title+"</a></div>"}}),u=Backbone.View.extend({optionsDefault:{message:"",status:"info",persistent:!1},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement("<div></div>")},update:function(t){this.options=e.merge(t,this.optionsDefault);if(t.message!=""){this.$el.html(this._template(this.options)),this.$el.fadeIn();if(!t.persistent){var n=this;window.setTimeout(function(){n.$el.is(":visible")?n.$el.fadeOut():n.$el.hide()},3e3)}}else this.$el.fadeOut()},_template:function(e){return'<div class="alert alert-'+e.status+'" style="padding: 2px 2px 2px 10px;">'+e.message+"</div>"}}),a=Backbone.View.extend({optionsDefault:{onclick:null,searchword:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options));var n=this;this.options.onclick&&this.$el.on("submit",function(e){var t=n.$el.find("#search");n.options.onclick(t.val())})},_template:function(e){return'<div class="search"><form onsubmit="return false;"><input id="search" class="form-control input-sm" type="text" name="search" placeholder="Search..." value="'+e.searchword+'">'+'<button type="submit" class="btn search-btn">'+'<i class="fa fa-search"></i>'+"</button>"+"</form>"+"</div>"}}),f=Backbone.View.extend({optionsDefault:{title:"Unlabeled",body:null},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.options.body&&this.$el.find(".body").append(this.options.body)},_template:function(e){return'<div id="title" class="title">'+e.title+":"+"</div>"}}),l=Backbone.View.extend({optionsDefault:{id:"",title:"",target:"",href:"",onunload:null,onclick:null,visible:!0,icon:null,tag:""},$menu:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement($(this._template(this.options)));var n=$(this.el).find(".root"),r=this;n.on("click",function(e){e.preventDefault(),r.options.onclick&&r.options.onclick()}),this.options.visible||this.hide()},show:function(){$(this.el).show()},hide:function(){$(this.el).hide()},addMenu:function(t){var n={title:"",target:"",href:"",onclick:null,divider:!1,icon:null};n=e.merge(t,n),this.$menu||($(this.el).append(this._templateMenu()),this.$menu=$(this.el).find(".menu"));var r=$(this._templateMenuItem(n));r.on("click",function(e){e.preventDefault(),n.onclick&&n.onclick()}),this.$menu.append(r),n.divider&&this.$menu.append($(this._templateDivider()))},_templateMenuItem:function(e){var t='<li><a href="'+e.href+'" target="'+e.target+'">';return e.icon&&(t+='<i class="fa '+e.icon+'"></i>'),t+=" "+e.title+"</a>"+"</li>",t},_templateMenu:function(){return'<ul class="menu dropdown-menu pull-right" role="menu"></ul>'},_templateDivider:function(){return'<li class="divider"></li>'},_template:function(e){var t='<div id="'+e.id+'" class="button-menu btn-group">'+'<button type="button" class="root btn btn-default dropdown-toggle" data-toggle="dropdown">';return e.icon&&(t+='<i class="fa '+e.icon+'"></i>'),"</button></div>",t}}),c=Backbone.View.extend({optionsDefault:{value:"",type:"text",placeholder:"",disabled:!1,visible:!0},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.options.disabled&&this.$el.prop("disabled",!0),this.options.visible||this.$el.hide();var n=this;this.options.onchange&&this.$el.on("input",function(){n.options.onchange()})},value:function(e){return e!==undefined&&this.$el.val(e),this.$el.val()},_template:function(e){return'<input id="'+e.id+'" type="'+e.type+'" value="'+e.value+'" placeholder="'+e.placeholder+'" class="form-control">'}});return{Label:n,Button:r,Icon:i,ButtonIcon:s,Input:c,Anchor:o,Message:u,Searchbox:a,Title:f,Select:t,ButtonMenu:l}}),define("plugin/library/jobs",["utils/utils"],function(e){return Backbone.Model.extend({initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault)},cleanup:function(t){var n=t.get("dataset_id_job");if(n!=""){var r=this;e.request("PUT",config.root+"api/histories/none/contents/"+n,{deleted:!0},function(){r._refreshHdas()}),t.set("dataset_id_job","")}},submit:function(t,n,r,i){var s=this,o=t.id,u=t.get("type"),a=this.app.types.get(u);data={tool_id:"rkit",inputs:{input:{id:t.get("dataset_id"),src:"hda"},module:u,columns:r,settings:n}},t.state("submit","Sending job request..."),e.request("POST",config.root+"api/tools",data,function(e){if(!e.outputs||e.outputs.length==0)t.state("failed","Job submission failed. No response.");else{s._refreshHdas();var n=e.outputs[0];t.state("queued","Job has been queued..."),t.set("dataset_id_job",n.id),s._loop(n.id,function(e){switch(e.state){case"ok":return t.state("success","Job completed successfully..."),i(e),!0;case"error":return t.state("failed","Job has failed. Please check the history for details."),!0;case"running":return t.state("running","Job is running..."),!1}})}},function(e){var n="";e&&e.message&&e.message.data&&e.message.data.input&&(n=e.message.data.input+"."),t.state("failed","This visualization requires the R-kit. Please make sure it is installed. "+n)})},_loop:function(t,n){var r=this;e.request("GET",config.root+"api/jobs/"+t,{},function(e){n(e)||setTimeout(function(){r._loop(t,n)},r.app.config.get("query_timeout"))})},_refreshHdas:function(){Galaxy&&Galaxy.currHistoryPanel&&Galaxy.currHistoryPanel.refreshHdas()}})}),define("plugin/library/datasets",["utils/utils"],function(e){return Backbone.Collection.extend({list:{},initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault)},request:function(t,n,r){var i=this;if(t.groups)this._fetch(t,n);else{var s=this.list[t.id];if(s){n(s);return}e.request("GET",config.root+"api/datasets/"+t.id,{},function(e){switch(e.state){case"error":r&&r(e);break;default:i.list[t.id]=e,n(e)}})}},_fetch:function(t,n){var r=t.start?t.start:0,i=Math.abs(t.end-t.start),s=this.app.config.get("query_limit");if(!i||i>s)i=s;var o="",u={},a=0;for(var f in t.groups){var l=t.groups[f];for(var c in l.columns){var h=l.columns[c];o+=h+",",u[h]=a,a++}}if(a==0){n({});return}o=o.substring(0,o.length-1);var p=t.groups.slice(0);for(var f in p)p[f].values=[];var d=this;e.request("GET",config.root+"api/datasets/"+t.id,{data_type:"raw_data",provider:"dataset-column",limit:i,offset:r,indeces:o},function(e){for(var i in e.data){var s=e.data[i];for(var o in t.groups){var a=t.groups[o],f={x:parseInt(i)+r};for(var l in a.columns){var c=a.columns[l],h=u[c],d=s[h];if(isNaN(d)||!d)d=0;f[l]=d}p[o].values.push(f)}}n(p)})}})}),define("plugin/models/group",[],function(){return Backbone.Model.extend({defaults:{key:"Data label",date:""},reset:function(){this.clear({silent:!0}).set(this.defaults),this.trigger("reset",this)}})}),define("plugin/models/groups",["plugin/models/group"],function(e){return Backbone.Collection.extend({model:e})});var Visualization=Backbone.Model.extend({defaults:{config:{}},urlRoot:function(){var e="/api/visualizations";return window.galaxy_config&&galaxy_config.root?galaxy_config.root+e:e},initialize:function(e){_.isObject(e.config)&&_.isObject(this.defaults.config)&&_.defaults(e.config,this.defaults.config),this._setUpListeners()},_setUpListeners:function(){},set:function(e,t){if(e==="config"){var n=this.get("config");_.isObject(n)&&(t=_.extend(_.clone(n),t))}return Backbone.Model.prototype.set.call(this,e,t),this},toString:function(){var e=this.get("id")||"";return this.get("title")&&(e+=":"+this.get("title")),"Visualization("+e+")"}}),VisualizationCollection=Backbone.Collection.extend({model:Visualization,url:function(){return galaxy_config.root+"api/visualizations"},initialize:function(e,t){t=t||{}},set:function(e,t){var n=this;e=_.map(e,function(e){var t=n.get(e.id);if(!t)return e;var r=t.toJSON();return _.extend(r,e),r}),Backbone.Collection.prototype.set.call(this,e,t)},toString:function(){return["VisualizationCollection(",[this.historyId,this.length].join(),")"].join("")}});define("mvc/visualization/visualization-model",function(){}),define("plugin/models/chart",["plugin/models/groups","mvc/visualization/visualization-model"],function(e){return Backbone.Model.extend({defaults:{id:null,title:"",type:"",date:null,state:"",state_info:"",modified:!1,dataset_id:"",dataset_id_job:""},initialize:function(t){this.groups=new e,this.settings=new Backbone.Model},reset:function(){this.clear({silent:!0}).set(this.defaults),this.groups.reset(),this.settings.clear(),this.trigger("reset",this)},copy:function(e){var t=this;t.clear({silent:!0}).set(this.defaults),t.set(e.attributes),t.settings=e.settings.clone(),t.groups.reset(),e.groups.each(function(e){t.groups.add(e.clone())}),t.trigger("change",t)},state:function(e,t){this.set("state_info",t),this.set("state",e)},drawable:function(){return this.get("state")=="ok"||this.get("state")=="failed"||this.get("state")=="initialized"},editable:function(){return this.get("state")=="ok"||this.get("state")=="failed"}})}),define("plugin/library/storage",["utils/utils","plugin/models/chart","plugin/models/group"],function(e,t,n){return Backbone.Model.extend({vis:null,initialize:function(e){this.app=e,this.chart=this.app.chart,this.options=this.app.options,this.id=this.options.id,this.vis=new Visualization({type:"charts",config:{dataset_id:this.options.config.dataset_id,chart_dict:{}}}),this.id&&(this.vis.id=this.id);var t=this.options.config.chart_dict;t&&(this.vis.get("config").chart_dict=t)},save:function(){var e=this.app.chart;this.vis.get("config").chart_dict={};var t=e.get("title");t!=""&&this.vis.set("title",t);var n={attributes:e.attributes,settings:e.settings.attributes,groups:[]};e.groups.each(function(e){n.groups.push(e.attributes)}),this.vis.get("config").chart_dict=n;var r=this;this.vis.save().fail(function(e,t,n){console.error(e,t,n)}).then(function(e){e&&e.id&&(r.id=e.id)})},load:function(){var e=this.vis.get("config").chart_dict;if(!e.attributes)return!1;this.chart.set(e.attributes),this.chart.settings.set(e.settings);for(var t in e.groups)this.chart.groups.add(new n(e.groups[t]));return this.chart.state("ok","Loaded previously saved visualization."),this.chart.set("modified",!1),!0}})}),define("plugin/views/viewport",["mvc/ui/ui-portlet","plugin/library/ui","utils/utils"],function(e,t,n){return Backbone.View.extend({initialize:function(e,t){this.app=e,this.chart=this.app.chart,this.options=n.merge(t,this.optionsDefault),this.setElement($(this._template()));var r=this;this.chart.on("redraw",function(){r._create_svg(),r._draw(r.chart)}),this.chart.on("change:state",function(){var e=r.$el.find("#info"),t=e.find("#icon");t.removeClass(),e.show(),e.find("#text").html(r.chart.get("state_info"));var n=r.chart.get("state");switch(n){case"ok":r.chart.trigger("drawable"),e.hide();break;case"failed":r.chart.trigger("drawable"),t.addClass("fa fa-warning");break;case"initialized":r.chart.trigger("drawable"),t.addClass("fa fa-warning");break;default:t.addClass("fa fa-spinner fa-spin")}})},show:function(){this.$el.show()},hide:function(){this.$el.hide()},_create_svg:function(){this.svg&&this.svg.remove(),this.$el.append($(this._template_svg())),this.svg=d3.select(this.$el.find("svg")[0])},_draw:function(e){var t=this;if(!e.drawable()){e.off("drawable"),e.on("drawable",function(){t.chart.off("drawable"),e.drawable()&&t._draw(e)});return}e.state("wait","Please wait...");var n=e.get("type"),r=this.app.types.get(n);if(!r.execute||r.execute&&e.get("modified"))this.app.jobs.cleanup(e),e.set("modified",!1);var t=this;require(["plugin/charts/"+n+"/"+n],function(n){var i=new n(t.app,{svg:t.svg});r.execute?e.get("dataset_id_job")==""?t.app.jobs.submit(e,t._defaultSettingsString(e),t._defaultRequestString(e),function(){this.app.storage.save(),i.draw(e,t._defaultRequestDictionary(e))}):i.draw(e,t._defaultRequestDictionary(e)):i.draw(e,t._defaultRequestDictionary(e))})},_defaultRequestString:function(e){var t=this.app.types.get(e.get("type")),n="",r=0;return e.groups.each(function(e){for(var i in t.columns)n+=i+"_"+ ++r+":"+(parseInt(e.get(i))+1)+", "}),n.substring(0,n.length-2)},_defaultSettingsString:function(e){var t="";for(key in e.settings.attributes)t+=key+":"+e.settings.get(key)+", ";return t.substring(0,t.length-2)},_defaultRequestDictionary:function(e){var t=this.app.types.get(e.get("type")),n={groups:[]};t.execute?n.id=e.get("dataset_id_job"):n.id=e.get("dataset_id");var r=0;return e.groups.each(function(e){var i={};for(var s in t.columns)i[s]=e.get(s);n.groups.push({key:++r+":"+e.get("key"),columns:i})}),n},_template:function(){return'<div style="height: 100%; min-height: 50px;"><div id="info" style="position: absolute; margin-left: 10px; margin-top: 10px; margin-bottom: 50px;"><span id="icon" style="font-size: 1.2em; display: inline-block;"/><span id="text" style="position: relative; margin-left: 5px; top: -1px; font-size: 1.0em;"/></div></div>'},_template_svg:function(){return'<svg style="height: calc(100% - 80px)"/>'}})}),define("plugin/views/viewer",["utils/utils","plugin/library/ui","mvc/ui/ui-portlet","plugin/models/group","plugin/views/viewport"],function(e,t,n,r,i){return Backbone.View.extend({initialize:function(e,r){this.app=e,this.chart=this.app.chart,this.viewport_view=new i(e);var s=this;this.portlet=new n.View({icon:"fa-bar-chart-o",title:"Viewport",operations:{edit_button:new t.ButtonIcon({icon:"fa-gear",tooltip:"Customize Chart",title:"Customize",onclick:function(){s._wait(s.chart,function(){s.app.go("editor")})}})}}),this.portlet.append(this.viewport_view.$el),this.setElement(this.portlet.$el);var s=this;this.chart.on("change:title",function(){s._refreshTitle()})},show:function(){this.$el.show(),$(window).trigger("resize")},hide:function(){this.$el.hide()},_refreshTitle:function(){var e=this.chart.get("title");this.portlet.title(e)},_wait:function(e,t){if(e.editable())t();else{var n=this;this.app.modal.show({title:"Please wait!",body:"Your chart is currently being processed. Please wait and try again.",buttons:{Close:function(){n.app.modal.hide()},Retry:function(){n.app.modal.hide(),setTimeout(function(){n._wait(e,t)},n.app.config.get("query_timeout"))}}})}}})}),define("mvc/ui/ui-tabs",["utils/utils"],function(e){var t=Backbone.View.extend({visible:!1,list:{},$nav:null,$content:null,first_tab:null,optionsDefault:{title_new:"",operations:null,onnew:null},initialize:function(t){this.options=e.merge(t,this.optionsDefault);var n=$(this._template(this.options));this.$nav=n.find(".tab-navigation"),this.$content=n.find(".tab-content"),this.setElement(n),this.list={};var r=this;this.options.operations&&$.each(this.options.operations,function(e,t){t.$el.prop("id",e),r.$nav.find(".operations").append(t.$el)});if(this.options.onnew){var i=$(this._template_tab_new(this.options));this.$nav.append(i),i.tooltip({title:"Add a new tab",placement:"bottom",container:r.$el}),i.on("click",function(e){i.tooltip("hide"),r.options.onnew()})}},add:function(e){var t=e.id,n={$title:$(this._template_tab(e)),$content:$(this._template_tab_content(e)),removable:e.ondel?!0:!1};this.list[t]=n,this.options.onnew?this.$nav.find("#new-tab").before(n.$title):this.$nav.append(n.$title),n.$content.append(e.$el),this.$content.append(n.$content),_.size(this.list)==1&&(n.$title.addClass("active"),n.$content.addClass("active"),this.first_tab=t);if(e.ondel){var r=this,i=n.$title.find("#delete");i.tooltip({title:"Delete this tab",placement:"bottom",container:r.$el}),i.on("click",function(){return i.tooltip("destroy"),r.$el.find(".tooltip").remove(),e.ondel(),!1})}e.onclick&&n.$title.on("click",function(){e.onclick()})},del:function(e){var t=this.list[e];t.$title.remove(),t.$content.remove(),delete t,this.first_tab==e&&(this.first_tab=null),this.first_tab!=null&&this.show(this.first_tab)},delRemovable:function(){for(var e in this.list){var t=this.list[e];t.removable&&this.del(e)}},show:function(e){this.$el.fadeIn("fast"),this.visible=!0,e&&this.list[e].$title.find("a").tab("show")},hide:function(){this.$el.fadeOut("fast"),this.visible=!1},hideOperation:function(e){this.$nav.find("#"+e).hide()},showOperation:function(e){this.$nav.find("#"+e).show()},setOperation:function(e,t){var n=this.$nav.find("#"+e);n.off("click"),n.on("click",t)},title:function(e,t){var n=this.list[e].$title.find("#text");return t&&n.html(t),n.html()},_template:function(e){return'<div class="tabbable tabs-left"><ul class="tab-navigation nav nav-tabs"><div class="operations" style="float: right; margin-bottom: 4px;"></div></ul><div class="tab-content"/></div>'},_template_tab_new:function(e){return'<li id="new-tab"><a href="javascript:void(0);"><i style="font-size: 0.8em; margin-right: 5px;" class="fa fa-plus-circle"/>'+e.title_new+"</a>"+"</li>"},_template_tab:function(e){var t='<li id="title-'+e.id+'">'+'<a title="" href="#tab-'+e.id+'" data-toggle="tab" data-original-title="">'+'<span id="text">'+e.title+"</span>";return e.ondel&&(t+='<i id="delete" style="font-size: 0.8em; margin-left: 5px; cursor: pointer;" class="fa fa-minus-circle"/>'),t+="</a></li>",t},_template_tab_content:function(e){return'<div id="tab-'+e.id+'" class="tab-pane"/>'}});return{View:t}}),define("plugin/library/ui-table",["utils/utils"],function(e){var t=Backbone.View.extend({row:null,row_count:0,optionsDefault:{content:"No content available.",onchange:null,ondblclick:null,onconfirm:null},events:{click:"_onclick",dblclick:"_ondblclick"},first:!0,initialize:function(t){this.options=e.merge(t,this.optionsDefault);var n=$(this._template(t));this.$thead=n.find("thead"),this.$tbody=n.find("tbody"),this.$tmessage=n.find("tmessage"),this.setElement(n),this.row=$("<tr></tr>")},addHeader:function(e){var t=$("<th></th>");t.append(e),this.row.append(t)},appendHeader:function(){this.$thead.append(this.row),this.row=$("<tr></tr>")},add:function(e,t){var n=$("<td></td>");t&&n.css("width",t),n.append(e),this.row.append(n)},append:function(e){this._commit(e)},prepend:function(e){this._commit(e,!0)},remove:function(e){var t=this.$tbody.find("#"+e);t.length>0&&(t.remove(),this.row_count--,this._refresh())},removeAll:function(){this.$tbody.html(""),this.row_count=0,this._refresh()},value:function(e){this.before=this.$tbody.find(".current").attr("id"),e!==undefined&&(this.$tbody.find("tr").removeClass("current"),e&&this.$tbody.find("#"+e).addClass("current"));var t=this.$tbody.find(".current").attr("id");return t===undefined?null:(t!=this.before&&this.options.onchange&&this.options.onchange(e),t)},size:function(){return this.$tbody.find("tr").length},_commit:function(e,t){this.remove(e),this.row.attr("id",e),t?this.$tbody.prepend(this.row):this.$tbody.append(this.row),this.row=$("<tr></tr>"),this.row_count++,this._refresh()},_onclick:function(e){var t=this.value(),n=$(e.target).closest("tr").attr("id");n&&t!=n&&(this.options.onconfirm?this.options.onconfirm(n):this.value(n))},_ondblclick:function(e){var t=this.value();t&&this.options.ondblclick&&this.options.ondblclick(t)},_refresh:function(){this.row_count==0?this.$tmessage.show():this.$tmessage.hide()},_template:function(e){return'<div><table class="grid"><thead></thead><tbody style="cursor: pointer;"></tbody></table><tmessage>'+e.content+"</tmessage>"+"<div>"}});return{View:t}}),define("plugin/views/group",["plugin/library/ui-table","plugin/library/ui","utils/utils"],function(e,t,n){return Backbone.View.extend({columns:[],initialize:function(r,i){this.app=r;var s=this;this.chart=this.app.chart,this.group=i.group,this.group_key=new t.Input({placeholder:"Data label",onchange:function(){s.group.set("key",s.group_key.value())}}),this.table=new e.View({content:"No data column."});var o=$("<div/>");o.append(n.wrap((new t.Label({title:"Provide a label:"})).$el)),o.append(n.wrap(this.group_key.$el)),o.append(n.wrap((new t.Label({title:"Select columns:"})).$el)),o.append(n.wrap(this.table.$el)),this.setElement(o);var s=this;this.chart.on("change:dataset_id",function(){s._refreshTable()}),this.chart.on("change:type",function(){s._refreshTable()}),this.group.on("change:key",function(){s._refreshGroupKey()}),this.group.on("change",function(){s._refreshGroup()}),this._refreshTable(),this._refreshGroupKey(),this._refreshGroup()},_refreshTable:function(){var e=this.chart.get("dataset_id"),n=this.chart.get("type");if(!e||!n)return;var r=this,i=this.app.types.get(n);this.table.removeAll();var s={};for(var o in i.columns){var u=this.group.get(o);u||this.group.set(o,0);var a=i.columns[o],f=new t.Select.View({id:"select_"+o,gid:o,onchange:function(e){r.group.set(this.gid,e),r.chart.set("modified",!0)},value:u,wait:!0});this.table.add(a.title,"25%"),this.table.add(f.$el),this.table.append(o),s[o]=f}this.chart.state("wait","Loading metadata..."),this.app.datasets.request({id:e},function(e){r.columns=[];var t=e.metadata_column_types;for(var n in t)(t[n]=="int"||t[n]=="float")&&r.columns.push({label:"Column: "+(parseInt(n)+1)+" ["+t[n]+"]",value:n});for(var n in s)s[n].update(r.columns),s[n].show();r.chart.state("initialized","Metadata initialized...")})},_refreshGroup:function(){this.group.set("date",n.time())},_refreshGroupKey:function(){var e=this.group.get("key");e===undefined&&(e=""),this.group_key.value(e)}})}),define("plugin/library/ui-table-form",["plugin/library/ui-table","plugin/library/ui","utils/utils"],function(e,t,n){var r=Backbone.View.extend({initialize:function(r){this.table_title=new t.Label({title:r.title}),this.table=new e.View({content:r.content});var i=$("<div/>");i.append(n.wrap(this.table_title.$el)),i.append(n.wrap(this.table.$el)),this.setElement(i)},title:function(e){this.table_title.title(e)},update:function(e,t){this.table.removeAll();for(var n in e)this._add(n,e[n],t)},_add:function(e,n,r){var i=null,s=n.type;switch(s){case"text":i=new t.Input({placeholder:n.placeholder,onchange:function(){r.set(e,i.value())}});break;case"select":i=new t.Select.View({data:n.data,onchange:function(){r.set(e,i.value())}});break;case"slider":i=new t.Input({placeholder:n.placeholder,onchange:function(){r.set(e,i.value())}});break;case"separator":i=$("<div/>");break;default:console.log("ui-table-form:_add","Unknown setting type ("+n.type+")");return}if(s!="separator"){r.get(e)||r.set(e,n.init),i.value(r.get(e));var o=$("<div/>");o.append(i.$el),o.append('<div class="toolParamHelp" style="font-size: 0.9em;">'+n.info+"</div>"),this.table.add('<span style="white-space: nowrap;">'+n.title+"</span>","25%"),this.table.add(o)}else this.table.add('<h6 style="white-space: nowrap;">'+n.title+":<h6/>"),this.table.add($("<div/>"));this.table.append(e)}});return{View:r}}),define("plugin/views/settings",["plugin/library/ui","plugin/library/ui-table-form","utils/utils"],function(e,t,n){return Backbone.View.extend({initialize:function(e,n){this.app=e;var r=this;this.chart=this.app.chart,this.form=new t.View({title:"Chart options:",content:"This chart type does not provide any options."}),this.setElement(this.form.$el);var r=this;this.chart.on("change",function(){r._refreshTable()})},_refreshTable:function(){var e=this.chart.get("type");if(!e)return;var t=this.app.types.get(e);this.form.title(t.title+":"),this.form.update(t.settings,this.chart.settings)}})}),define("plugin/views/editor",["mvc/ui/ui-tabs","plugin/library/ui-table","plugin/library/ui","mvc/ui/ui-portlet","utils/utils","plugin/models/chart","plugin/models/group","plugin/views/group","plugin/views/settings"],function(e,t,n,r,i,s,o,u,a){return Backbone.View.extend({optionsDefault:{header:!0,content:"No content available."},initialize:function(s,o){this.app=s,this.chart=this.app.chart,this.options=i.merge(o,this.optionsDefault),this.portlet=new r.View({icon:"fa-bar-chart-o",title:"Editor",operations:{save:new n.ButtonIcon({icon:"fa-save",tooltip:"Draw Chart",title:"Draw",onclick:function(){u.app.go("viewer"),u._saveChart()}}),back:new n.ButtonIcon({icon:"fa-caret-left",tooltip:"Return to Viewer",title:"Return",onclick:function(){u.app.go("viewer"),u.app.storage.load()}})}});var u=this;this.table=new t.View({header:!1,onconfirm:function(e){u.chart.groups.length>0?u.app.modal.show({title:"Switching to another chart type?",body:"If you continue your settings and selections will be cleared.",buttons:{Cancel:function(){u.app.modal.hide()},Continue:function(){u.app.modal.hide(),u.table.value(e)}}}):u.table.value(e)},onchange:function(e){u.chart.groups.reset(),u.chart.settings.clear(),u.chart.set({type:e})},ondblclick:function(e){u.tabs.show("settings")},content:"No chart types available"});var f=0,l=s.types.attributes;for(var c in l){var h=l[c];this.table.add(++f+"."),h.execute?this.table.add(h.title+" (requires processing)"):this.table.add(h.title),this.table.append(c)}this.tabs=new e.View({title_new:"Add Data",onnew:function(){var e=u._addGroupModel();u.tabs.show(e.id)}}),this.title=new n.Input({placeholder:"Chart title",onchange:function(){u.app.config.set("title",u.title.value())}});var p=$("<div/>");p.append(i.wrap((new n.Label({title:"Provide a chart title:"})).$el)),p.append(i.wrap(this.title.$el)),p.append(i.wrap((new n.Label({title:"Select a chart type:"})).$el)),p.append(i.wrap(this.table.$el)),this.tabs.add({id:"main",title:"Start",$el:p}),this.settings=new a(this.app),this.tabs.add({id:"settings",title:"Configuration",$el:this.settings.$el}),this.portlet.append(this.tabs.$el),this.setElement(this.portlet.$el),this.tabs.hideOperation("back");var u=this;this.chart.on("change:title",function(e){u._refreshTitle()}),this.chart.on("change:type",function(e){u.table.value(e.get("type"))}),this.chart.on("reset",function(e){u._resetChart()}),this.app.chart.on("redraw",function(e){u.portlet.showOperation("back")}),this.app.chart.groups.on("add",function(e){u._addGroup(e)}),this.app.chart.groups.on("remove",function(e){u._removeGroup(e)}),this.app.chart.groups.on("reset",function(e){u._removeAllGroups()}),this.app.chart.groups.on("change:key",function(e){u._refreshGroupKey()}),this._resetChart()},show:function(){this.$el.show()},hide:function(){this.$el.hide()},_refreshTitle:function(){var e=this.chart.get("title");this.portlet.title(e)},_refreshGroupKey:function(){var e=this,t=0;this.chart.groups.each(function(n){var r=n.get("key","");r==""&&(r="Chart data"),e.tabs.title(n.id,++t+": "+r)})},_addGroupModel:function(){var e=new o({id:i.uuid()});return this.chart.groups.add(e),e},_addGroup:function(e){var t=this,n=new u(this.app,{group:e}),r=t.chart.groups.length;this.tabs.add({id:e.id,$el:n.$el,ondel:function(){t.chart.groups.remove(e.id)}}),this._refreshGroupKey()},_removeGroup:function(e){this.tabs.del(e.id),this._refreshGroupKey(),this.chart.set("modified",!0)},_removeAllGroups:function(e){this.tabs.delRemovable()},_resetChart:function(){this.chart.set("id",i.uuid()),this.chart.set("type","bardiagram"),this.chart.set("dataset_id",this.app.options.config.dataset_id),this.chart.set("title","New Chart"),this.portlet.hideOperation("back")},_saveChart:function(){this.chart.set({type:this.table.value(),title:this.title.value(),date:i.time()}),this.chart.groups.length==0&&this._addGroupModel(),this.chart.trigger("redraw"),this.app.storage.save()}})}),define("plugin/models/config",[],function(){return Backbone.Model.extend({defaults:{query_limit:1e3,query_timeout:500}})}),define("plugin/charts/_nvd3/config",[],function(){return{title:"",columns:{y:{title:"Values for y-axis"}},settings:{separator_label:{title:"X axis",type:"separator"},x_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"X-axis",placeholder:"Axis label"},x_axis_type:{title:"Axis value type",info:"Select the value type of the axis.",type:"select",init:"f",data:[{label:"-- Do not show values --",value:"hide"},{label:"Float",value:"f"},{label:"Exponent",value:"e"},{label:"Integer",value:"d"},{label:"Percentage",value:"p"},{label:"Rounded",value:"r"},{label:"SI-prefix",value:"s"}]},x_axis_tick:{title:"Axis tick format",info:"Select the tick format for the axis.",type:"select",init:".1",data:[{label:"0.00001",value:".5"},{label:"0.0001",value:".4"},{label:"0.001",value:".3"},{label:"0.01",value:".2"},{label:"0.1",value:".1"},{label:"1",value:"1"}]},separator_tick:{title:"Y axis",type:"separator"},y_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"Y-axis",placeholder:"Axis label"},y_axis_type:{title:"Axis value type",info:"Select the value type of the axis.",type:"select",init:"f",data:[{label:"-- Do not show values --",value:"hide"},{label:"Float",value:"f"},{label:"Exponent",value:"e"},{label:"Integer",value:"d"},{label:"Percentage",value:"p"},{label:"Rounded",value:"r"},{label:"SI-prefix",value:"s"}]},y_axis_tick:{title:"Axis tick format",info:"Select the tick format for the axis.",type:"select",init:".1",data:[{label:"0.00001",value:".5"},{label:"0.0001",value:".4"},{label:"0.001",value:".3"},{label:"0.01",value:".2"},{label:"0.1",value:".1"},{label:"1",value:"1"}]},separator_legend:{title:"Others",type:"separator"},show_legend:{title:"Show legend",info:"Would you like to add a legend?",type:"select",init:"true",data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]}}}}),define("plugin/charts/bardiagram/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Bar diagram"})}),define("plugin/charts/histogram/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Histogram",execute:!0,columns:{y:{title:"Observations"}},settings:{x_axis_label:{init:"Breaks"},y_axis_label:{init:"Density"},y_axis_tick:{init:".3"}}})}),define("plugin/charts/horizontal/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Bar diagram (horizontal)",settings:{x_axis_type:{init:"hide"}}})}),define("plugin/charts/line/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line chart"})}),define("plugin/charts/linewithfocus/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line with focus"})}),define("plugin/charts/piechart/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Pie chart"})}),define("plugin/charts/scatterplot/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Scatter plot",columns:{x:{title:"Values for x-axis"}}})}),define("plugin/charts/stackedarea/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stacked area"})}),define("plugin/charts/types",["plugin/charts/bardiagram/config","plugin/charts/histogram/config","plugin/charts/horizontal/config","plugin/charts/line/config","plugin/charts/linewithfocus/config","plugin/charts/piechart/config","plugin/charts/scatterplot/config","plugin/charts/stackedarea/config"],function(e,t,n,r,i,s,o,u){return Backbone.Model.extend({defaults:{bardiagram:e,horizontal:n,histogram:t,line:r,linewithfocus:i,piechart:s,scatterplot:o,stackedarea:u}})}),define("plugin/app",["mvc/ui/ui-modal","mvc/ui/ui-portlet","plugin/library/ui","utils/utils","plugin/library/jobs","plugin/library/datasets","plugin/library/storage","plugin/views/viewer","plugin/views/editor","plugin/models/config","plugin/models/chart","plugin/charts/types"],function(e,t,n,r,i,s,o,u,a,f,l,c){return Backbone.View.extend({initialize:function(t){this.options=t,Galaxy&&Galaxy.modal?this.modal=Galaxy.modal:this.modal=new e.View,this.config=new f,this.types=new c,this.chart=new l,this.jobs=new i(this),this.datasets=new s(this),this.storage=new o(this),this.viewer_view=new u(this),this.editor_view=new a(this),this.$el.append(this.viewer_view.$el),this.$el.append(this.editor_view.$el),this.storage.load()?(this.go("viewer"),this.chart.trigger("redraw")):this.go("editor")},go:function(e){$(".tooltip").hide();switch(e){case"editor":this.editor_view.show(),this.viewer_view.hide();break;case"viewer":this.editor_view.hide(),this.viewer_view.show()}},execute:function(e){},onunload:function(){},log:function(e,t){console.log(e+" "+t)}})});
\ No newline at end of file
diff -r ec715604b476c47703b90385062a6ec482cfd302 -r cab5878ce755b1736d09a9a74e419eddb12c0f2d config/plugins/visualizations/charts/static/charts/histogram/histogram.js
--- a/config/plugins/visualizations/charts/static/charts/histogram/histogram.js
+++ b/config/plugins/visualizations/charts/static/charts/histogram/histogram.js
@@ -13,9 +13,6 @@
// render
draw : function(chart, request_dictionary)
{
- // update request dataset id
- request_dictionary.id = chart.get('dataset_id_job');
-
// configure request
var index = 0;
for (var i in request_dictionary.groups) {
diff -r ec715604b476c47703b90385062a6ec482cfd302 -r cab5878ce755b1736d09a9a74e419eddb12c0f2d config/plugins/visualizations/charts/static/library/jobs.js
--- a/config/plugins/visualizations/charts/static/library/jobs.js
+++ b/config/plugins/visualizations/charts/static/library/jobs.js
@@ -17,12 +17,15 @@
cleanup: function(chart) {
// cleanup previous dataset file
var previous = chart.get('dataset_id_job');
- if (previous) {
+ if (previous != '') {
var self = this;
Utils.request('PUT', config.root + 'api/histories/none/contents/' + previous, { deleted: true }, function() {
// update galaxy history
self._refreshHdas();
});
+
+ // reset id
+ chart.set('dataset_id_job', '');
}
},
@@ -53,9 +56,6 @@
}
}
- // cleanup
- self.cleanup(chart);
-
// set chart state
chart.state('submit', 'Sending job request...');
diff -r ec715604b476c47703b90385062a6ec482cfd302 -r cab5878ce755b1736d09a9a74e419eddb12c0f2d config/plugins/visualizations/charts/static/library/storage.js
--- a/config/plugins/visualizations/charts/static/library/storage.js
+++ b/config/plugins/visualizations/charts/static/library/storage.js
@@ -77,7 +77,6 @@
this.vis.save()
.fail(function(xhr, status, message) {
console.error(xhr, status, message);
- alert( 'Error loading data:\n' + xhr.responseText );
})
.then(function(response) {
if (response && response.id) {
@@ -110,6 +109,9 @@
// reset status
this.chart.state('ok', 'Loaded previously saved visualization.');
+ // reset modified flag
+ this.chart.set('modified', false);
+
// return
return true;
}
diff -r ec715604b476c47703b90385062a6ec482cfd302 -r cab5878ce755b1736d09a9a74e419eddb12c0f2d config/plugins/visualizations/charts/static/models/chart.js
--- a/config/plugins/visualizations/charts/static/models/chart.js
+++ b/config/plugins/visualizations/charts/static/models/chart.js
@@ -7,12 +7,15 @@
{
// defaults
defaults : {
- id : null,
- title : '',
- type : '',
- date : null,
- state : 'ok',
- state_info : ''
+ id : null,
+ title : '',
+ type : '',
+ date : null,
+ state : '',
+ state_info : '',
+ modified : false,
+ dataset_id : '',
+ dataset_id_job : ''
},
// initialize
@@ -56,7 +59,11 @@
this.set('state', value);
},
- ready: function() {
+ drawable: function() {
+ return (this.get('state') == 'ok') || (this.get('state') == 'failed') || (this.get('state') == 'initialized');
+ },
+
+ editable: function() {
return (this.get('state') == 'ok') || (this.get('state') == 'failed');
}
});
diff -r ec715604b476c47703b90385062a6ec482cfd302 -r cab5878ce755b1736d09a9a74e419eddb12c0f2d config/plugins/visualizations/charts/static/views/editor.js
--- a/config/plugins/visualizations/charts/static/views/editor.js
+++ b/config/plugins/visualizations/charts/static/views/editor.js
@@ -222,10 +222,7 @@
// refresh title
_refreshTitle: function() {
var title = this.chart.get('title');
- if (title) {
- title = ' - ' + title;
- }
- this.portlet.title('Charts' + title);
+ this.portlet.title(title);
},
// update
@@ -280,6 +277,9 @@
// update titles
this._refreshGroupKey();
+
+ // reset
+ this.chart.set('modified', true);
},
// remove group
diff -r ec715604b476c47703b90385062a6ec482cfd302 -r cab5878ce755b1736d09a9a74e419eddb12c0f2d config/plugins/visualizations/charts/static/views/group.js
--- a/config/plugins/visualizations/charts/static/views/group.js
+++ b/config/plugins/visualizations/charts/static/views/group.js
@@ -98,6 +98,7 @@
gid : id,
onchange : function(value) {
self.group.set(this.gid, value);
+ self.chart.set('modified', true);
},
value : value,
wait : true
@@ -112,6 +113,9 @@
list[id] = select;
}
+ // loading
+ this.chart.state('wait', 'Loading metadata...');
+
// get dataset
this.app.datasets.request({id : dataset_id}, function(dataset) {
// configure columns
@@ -133,6 +137,9 @@
list[key].update(self.columns);
list[key].show();
}
+
+ // loading
+ self.chart.state('initialized', 'Metadata initialized...');
});
},
diff -r ec715604b476c47703b90385062a6ec482cfd302 -r cab5878ce755b1736d09a9a74e419eddb12c0f2d config/plugins/visualizations/charts/static/views/viewer.js
--- a/config/plugins/visualizations/charts/static/views/viewer.js
+++ b/config/plugins/visualizations/charts/static/views/viewer.js
@@ -32,7 +32,7 @@
title : 'Customize',
onclick : function() {
// attempt to load chart editor
- self._wait (self.app.chart, function() {
+ self._wait (self.chart, function() {
self.app.go('editor');
});
}
@@ -41,7 +41,7 @@
}
});
- // append view port
+ // append portlet
this.portlet.append(this.viewport_view.$el);
// set element
@@ -71,23 +71,20 @@
// refresh title
_refreshTitle: function() {
var title = this.chart.get('title');
- if (title) {
- title = ' - ' + title;
- }
- this.portlet.title('Charts' + title);
+ this.portlet.title(title);
},
// wait for chart to be ready
_wait: function(chart, callback) {
// get chart
- if (chart.ready()) {
+ if (chart.editable()) {
callback();
} else {
// show modal
var self = this;
this.app.modal.show({
title : 'Please wait!',
- body : 'Your chart is currently being processed. Please wait...',
+ body : 'Your chart is currently being processed. Please wait and try again.',
buttons : {
'Close' : function() {self.app.modal.hide();},
'Retry' : function() {
diff -r ec715604b476c47703b90385062a6ec482cfd302 -r cab5878ce755b1736d09a9a74e419eddb12c0f2d config/plugins/visualizations/charts/static/views/viewport.js
--- a/config/plugins/visualizations/charts/static/views/viewport.js
+++ b/config/plugins/visualizations/charts/static/views/viewport.js
@@ -5,9 +5,6 @@
// widget
return Backbone.View.extend(
{
- // height
- height : 300,
-
// initialize
initialize: function(app, options)
{
@@ -26,8 +23,48 @@
// events
var self = this;
this.chart.on('redraw', function() {
+ // create svg element
+ self._create_svg();
+
+ // initialize
self._draw(self.chart);
});
+
+ // link status handler
+ this.chart.on('change:state', function() {
+ // get info element
+ var $info = self.$el.find('#info');
+
+ // get icon
+ var $icon = $info.find('#icon');
+
+ // remove icon
+ $icon.removeClass();
+
+ // show info
+ $info.show();
+ $info.find('#text').html(self.chart.get('state_info'));
+
+ // check status
+ var state = self.chart.get('state');
+ switch (state) {
+ case 'ok':
+ self.chart.trigger('drawable');
+ $info.hide();
+ break;
+ case 'failed':
+ self.chart.trigger('drawable');
+ $icon.addClass('fa fa-warning');
+ break;
+ case 'initialized':
+ self.chart.trigger('drawable');
+ $icon.addClass('fa fa-warning');
+ break;
+ default:
+ $icon.addClass('fa fa-spinner fa-spin');
+ break;
+ }
+ });
},
// show
@@ -40,62 +77,36 @@
this.$el.hide();
},
- // add
- _draw: function(chart) {
- // link this
- var self = this;
-
- // check
- if (!chart.ready()) {
- this.app.log('viewport:_drawChart()', 'Invalid attempt to draw chart before completion.');
- return;
- }
-
+ // create
+ _create_svg: function() {
// clear svg
if (this.svg) {
this.svg.remove();
}
// create
- this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- this.svg.setAttribute('height', this.height);
- this.$el.append(this.svg);
+ this.$el.append($(this._template_svg()));
// find svg element
this.svg = d3.select(this.$el.find('svg')[0]);
-
- // clear all previous handlers (including redraw listeners)
- chart.off('change:state');
+ },
+
+ // add
+ _draw: function(chart) {
+ // link this
+ var self = this;
- // link status handler
- chart.on('change:state', function() {
- // get info element
- var $info = self.$el.find('#info');
-
- // get icon
- var $icon = $info.find('#icon');
-
- // remove icon
- $icon.removeClass();
-
- // show info
- $info.show();
- $info.find('#text').html(chart.get('state_info'));
-
- // check status
- var state = chart.get('state');
- switch (state) {
- case 'ok':
- $info.hide();
- break;
- case 'failed':
- $icon.addClass('fa fa-warning');
- break;
- default:
- $icon.addClass('fa fa-spinner fa-spin');
- break;
- }
- });
+ // proceed once chart is drawable
+ if (!chart.drawable()) {
+ chart.off('drawable');
+ chart.on('drawable', function() {
+ self.chart.off('drawable');
+ if (chart.drawable()) {
+ self._draw(chart);
+ }
+ });
+ return;
+ }
// set chart state
chart.state('wait', 'Please wait...');
@@ -104,6 +115,16 @@
var chart_type = chart.get('type');
var chart_settings = this.app.types.get(chart_type);
+ // clean up data if there is any from previous jobs
+ if (!chart_settings.execute ||
+ (chart_settings.execute && chart.get('modified'))) {
+ // reset jobs
+ this.app.jobs.cleanup(chart);
+
+ // reset modified flag
+ chart.set('modified', false);
+ }
+
// create chart view
var self = this;
require(['plugin/charts/' + chart_type + '/' + chart_type], function(ChartView) {
@@ -112,25 +133,26 @@
// request data
if (chart_settings.execute) {
- self.app.jobs.submit(chart, self._defaultSettingsString(chart), self._defaultRequestString(chart), function() {
+ if (chart.get('dataset_id_job') == '') {
+ // submit job
+ self.app.jobs.submit(chart, self._defaultSettingsString(chart), self._defaultRequestString(chart), function() {
+ // save
+ this.app.storage.save();
+
+ // draw
+ view.draw(chart, self._defaultRequestDictionary(chart));
+ });
+ } else {
+ // load data into view
view.draw(chart, self._defaultRequestDictionary(chart));
- });
+ }
} else {
+ // load data into view
view.draw(chart, self._defaultRequestDictionary(chart));
}
});
},
- // template
- _template: function() {
- return '<div>' +
- '<div id="info" style="text-align: center; margin-top: 20px;">' +
- '<span id="icon" style="font-size: 1.2em; display: inline-block;"/>' +
- '<span id="text" style="position: relative; margin-left: 5px; top: -1px; font-size: 1.0em;"/>' +
- '</div>' +
- '</div>';
- },
-
// create default chart request
_defaultRequestString: function(chart) {
@@ -175,10 +197,16 @@
// configure request
var request_dictionary = {
- id : chart.get('dataset_id'),
- groups : []
+ groups : []
};
+ // update request dataset id
+ if (chart_settings.execute) {
+ request_dictionary.id = chart.get('dataset_id_job');
+ } else {
+ request_dictionary.id = chart.get('dataset_id');
+ }
+
// add groups to data request
var group_index = 0;
chart.groups.each(function(group) {
@@ -198,7 +226,23 @@
// return
return request_dictionary;
- }
+ },
+
+ // template
+ _template: function() {
+ return '<div style="height: 100%; min-height: 50px;">' +
+ '<div id="info" style="position: absolute; margin-left: 10px; margin-top: 10px; margin-bottom: 50px;">' +
+ '<span id="icon" style="font-size: 1.2em; display: inline-block;"/>' +
+ '<span id="text" style="position: relative; margin-left: 5px; top: -1px; font-size: 1.0em;"/>' +
+ '</div>' +
+ '</div>';
+ },
+
+ // template svg element
+ _template_svg: function() {
+ return '<svg style="height: calc(100% - 80px)"/>';
+ },
+
});
});
\ No newline at end of file
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.
1
0
commit/galaxy-central: dan: Refactor tool error / bug reporting. Allow reporting errors via biostar in addition to existing email functionality.
by commits-noreply@bitbucket.org 24 Mar '14
by commits-noreply@bitbucket.org 24 Mar '14
24 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ec715604b476/
Changeset: ec715604b476
User: dan
Date: 2014-03-24 21:19:35
Summary: Refactor tool error / bug reporting. Allow reporting errors via biostar in addition to existing email functionality.
Affected #: 5 files
diff -r 6e9354d1fbdfd92ff316c52302945c02bbf13a0c -r ec715604b476c47703b90385062a6ec482cfd302 lib/galaxy/tools/errors.py
--- /dev/null
+++ b/lib/galaxy/tools/errors.py
@@ -0,0 +1,121 @@
+"""
+Functionality for dealing with tool errors.
+"""
+import string
+from galaxy import model, util, web
+
+error_report_template = """
+GALAXY TOOL ERROR REPORT
+------------------------
+
+This error report was sent from the Galaxy instance hosted on the server
+"${host}"
+-----------------------------------------------------------------------------
+This is in reference to dataset id ${dataset_id} from history id ${history_id}
+-----------------------------------------------------------------------------
+You should be able to view the history containing the related history item
+
+${hid}: ${history_item_name}
+
+by logging in as a Galaxy admin user to the Galaxy instance referenced above
+and pointing your browser to the following link.
+
+${history_view_link}
+-----------------------------------------------------------------------------
+The user '${email}' provided the following information:
+
+${message}
+-----------------------------------------------------------------------------
+job id: ${job_id}
+tool id: ${job_tool_id}
+job pid or drm id: ${job_runner_external_id}
+-----------------------------------------------------------------------------
+job command line:
+${job_command_line}
+-----------------------------------------------------------------------------
+job stderr:
+${job_stderr}
+-----------------------------------------------------------------------------
+job stdout:
+${job_stdout}
+-----------------------------------------------------------------------------
+job info:
+${job_info}
+-----------------------------------------------------------------------------
+job traceback:
+${job_traceback}
+-----------------------------------------------------------------------------
+(This is an automated message).
+"""
+
+class ErrorReporter( object ):
+ def __init__( self, hda, app ):
+ # Get the dataset
+ sa_session = app.model.context
+ if not isinstance( hda, model.HistoryDatasetAssociation ):
+ try:
+ hda = sa_session.query( model.HistoryDatasetAssociation ).get( hda )
+ except:
+ hda = sa_session.query( model.HistoryDatasetAssociation ).get( app.security.decode_id( hda ) )
+ assert isinstance( hda, model.HistoryDatasetAssociation ), ValueError( "Bad value provided for HDA (%s)." % ( hda ) )
+ self.hda = hda
+ # Get the associated job
+ self.job = hda.creating_job
+ self.app = app
+ self.tool_id = self.job.tool_id
+ self.report = None
+ def _can_access_dataset( self, user ):
+ if user:
+ roles = user.all_roles()
+ else:
+ roles = []
+ return self.app.security_agent.can_access_dataset( roles, self.hda.dataset )
+ def create_report( self, email='', message='', **kwd ):
+ hda = self.hda
+ job = self.job
+ host = web.url_for( '/', qualified=True )
+ history_view_link = web.url_for( controller="history", action="view", id=self.app.security.encode_id( hda.history_id ), qualified=True )
+ # Build the email message
+ self.report = string.Template( error_report_template ) \
+ .safe_substitute( host=host,
+ dataset_id=hda.dataset_id,
+ history_id=hda.history_id,
+ hid=hda.hid,
+ history_item_name=hda.get_display_name(),
+ history_view_link=history_view_link,
+ job_id=job.id,
+ job_tool_id=job.tool_id,
+ job_runner_external_id=job.job_runner_external_id,
+ job_command_line=job.command_line,
+ job_stderr=util.unicodify( job.stderr ),
+ job_stdout=util.unicodify( job.stdout ),
+ job_info=util.unicodify( job.info ),
+ job_traceback=util.unicodify( job.traceback ),
+ email=email,
+ message=util.unicodify( message ) )
+ def _send_report( self, user, email=None, message=None, **kwd ):
+ return self.report
+ def send_report( self, user, email=None, message=None, **kwd ):
+ if self.report is None:
+ self.create_report( email=email, message=message, **kwd )
+ return self._send_report( user, email=email, message=message, **kwd )
+
+class EmailErrorReporter( ErrorReporter ):
+ def _send_report( self, user, email=None, message=None, **kwd ):
+ smtp_server = self.app.config.smtp_server
+ assert smtp_server, ValueError( "Mail is not configured for this galaxy instance" )
+ to_address = self.app.config.error_email_to
+ assert to_address, ValueError( "Error reporting has been disabled for this galaxy instance" )
+
+ frm = to_address
+ # Check email a bit
+ email = email or ''
+ email = email.strip()
+ parts = email.split()
+ if len( parts ) == 1 and len( email ) > 0 and self._can_access_dataset( user ):
+ to = to_address + ", " + email
+ else:
+ to = to_address
+ subject = "Galaxy tool error report from %s" % email
+ # Send it
+ return util.send_mail( frm, to, subject, self.report, self.app.config )
diff -r 6e9354d1fbdfd92ff316c52302945c02bbf13a0c -r ec715604b476c47703b90385062a6ec482cfd302 lib/galaxy/util/biostar.py
--- a/lib/galaxy/util/biostar.py
+++ b/lib/galaxy/util/biostar.py
@@ -7,6 +7,7 @@
import re
from unicodedata import normalize
from galaxy.web.base.controller import url_for
+from galaxy.tools.errors import ErrorReporter
_punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?(a)\[\\\]^_`{|},.]+')
@@ -27,6 +28,8 @@
# Slugifying from Armin Ronacher (http://flask.pocoo.org/snippets/5/)
def slugify(text, delim=u'-'):
"""Generates an slightly worse ASCII-only slug."""
+ if not isinstance( text, unicode ):
+ text = unicode( text )
result = []
for word in _punct_re.split(text.lower()):
word = normalize('NFKD', word).encode('ascii', 'ignore')
@@ -63,7 +66,7 @@
Generate a reasonable biostar tag for a tool.
"""
#Biostar can now handle tags with spaces, do we want to generate tags differently now?
- return slugify( unicode( tool.name ), delim='-' )
+ return slugify( tool.name, delim='-' )
def populate_tag_payload( payload=None, tool=None ):
if payload is None:
@@ -119,3 +122,18 @@
delete_cookie( trans, trans.app.config.biostar_key_name )
return get_biostar_url( trans.app, biostar_action='log_out' )
return None
+
+class BiostarErrorReporter( ErrorReporter ):
+ def _send_report( self, user, email=None, message=None, **kwd ):
+ assert biostar_enabled( self.app ), ValueError( "Biostar is not configured for this galaxy instance" )
+ assert self._can_access_dataset( user ), Exception( "You are not allowed to access this dataset." )
+ tool_version_select_field, tools, tool = \
+ self.app.toolbox.get_tool_components( self.tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=True )
+ payload = { 'title': 'Bug report on "%s" tool' % ( tool.name ), 'content': self.report.replace( '\n', '<br />' ), 'tag_val':slugify( 'bug report' ) }
+ #Get footer for email from here
+ payload2 = populate_tool_payload( tool=tool )
+ if 'content' in payload2:
+ payload[ 'content' ] = "%s<br />%s" % ( payload['content'], payload2['content'] )
+ if 'tag_val' in payload2:
+ payload[ 'tag_val' ] = ','.join( [ payload2[ 'tag_val' ], payload[ 'tag_val' ] ] )
+ return payload
diff -r 6e9354d1fbdfd92ff316c52302945c02bbf13a0c -r ec715604b476c47703b90385062a6ec482cfd302 lib/galaxy/webapps/galaxy/controllers/biostar.py
--- a/lib/galaxy/webapps/galaxy/controllers/biostar.py
+++ b/lib/galaxy/webapps/galaxy/controllers/biostar.py
@@ -75,6 +75,19 @@
return self.biostar_question_redirect( trans, payload )
@web.expose
+ def biostar_tool_bug_report( self, trans, hda=None, email=None, message=None ):
+ """
+ Generate a redirect to a Biostar site using external authentication to
+ pass Galaxy user information and information about a specific tool error.
+ """
+ try:
+ error_reporter = biostar.BiostarErrorReporter( hda, trans.app )
+ payload = error_reporter.send_report( trans.user, email=email, message=message )
+ except Exception, e:
+ return error( str( e ) )
+ return self.biostar_question_redirect( trans, payload=payload )
+
+ @web.expose
def biostar_logout( self, trans ):
"""
Log out of biostar
diff -r 6e9354d1fbdfd92ff316c52302945c02bbf13a0c -r ec715604b476c47703b90385062a6ec482cfd302 lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -1,6 +1,5 @@
import logging
import os
-import string
import tempfile
import urllib
import zipfile
@@ -14,6 +13,7 @@
from galaxy.web.base.controller import BaseUIController, ERROR, SUCCESS, url_for, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, UsesExtendedMetadataMixin
from galaxy.web.framework.helpers import grids, iff, time_ago
from galaxy.web.framework.helpers import to_unicode
+from galaxy.tools.errors import EmailErrorReporter
eggs.require( "Paste" )
import paste.httpexceptions
@@ -27,51 +27,6 @@
except ImportError:
pass
-
-error_report_template = """
-GALAXY TOOL ERROR REPORT
-------------------------
-
-This error report was sent from the Galaxy instance hosted on the server
-"${host}"
------------------------------------------------------------------------------
-This is in reference to dataset id ${dataset_id} from history id ${history_id}
------------------------------------------------------------------------------
-You should be able to view the history containing the related history item
-
-${hid}: ${history_item_name}
-
-by logging in as a Galaxy admin user to the Galaxy instance referenced above
-and pointing your browser to the following link.
-
-${history_view_link}
------------------------------------------------------------------------------
-The user '${email}' provided the following information:
-
-${message}
------------------------------------------------------------------------------
-job id: ${job_id}
-tool id: ${job_tool_id}
-job pid or drm id: ${job_runner_external_id}
------------------------------------------------------------------------------
-job command line:
-${job_command_line}
------------------------------------------------------------------------------
-job stderr:
-${job_stderr}
------------------------------------------------------------------------------
-job stdout:
-${job_stdout}
------------------------------------------------------------------------------
-job info:
-${job_info}
------------------------------------------------------------------------------
-job traceback:
-${job_traceback}
------------------------------------------------------------------------------
-(This is an automated message).
-"""
-
class HistoryDatasetAssociationListGrid( grids.Grid ):
# Custom columns for grid.
class HistoryColumn( grids.GridColumn ):
@@ -197,50 +152,15 @@
except:
exit_code = "Invalid dataset ID or you are not allowed to access this dataset"
return exit_code
+
@web.expose
def report_error( self, trans, id, email='', message="", **kwd ):
- smtp_server = trans.app.config.smtp_server
- if smtp_server is None:
- return trans.show_error_message( "Mail is not configured for this galaxy instance" )
- to_address = trans.app.config.error_email_to
- if to_address is None:
- return trans.show_error_message( "Error reporting has been disabled for this galaxy instance" )
- # Get the dataset and associated job
- hda = trans.sa_session.query( model.HistoryDatasetAssociation ).get( id )
- job = hda.creating_job
- # Get the name of the server hosting the Galaxy instance from which this report originated
- host = trans.request.host
- history_view_link = "%s/history/view?id=%s" % ( str( host ), trans.security.encode_id( hda.history_id ) )
- # Build the email message
- body = string.Template( error_report_template ) \
- .safe_substitute( host=host,
- dataset_id=hda.dataset_id,
- history_id=hda.history_id,
- hid=hda.hid,
- history_item_name=hda.get_display_name(),
- history_view_link=history_view_link,
- job_id=job.id,
- job_tool_id=job.tool_id,
- job_runner_external_id=job.job_runner_external_id,
- job_command_line=job.command_line,
- job_stderr=util.unicodify( job.stderr ),
- job_stdout=util.unicodify( job.stdout ),
- job_info=util.unicodify( job.info ),
- job_traceback=util.unicodify( job.traceback ),
- email=email,
- message=util.unicodify( message ) )
- frm = to_address
- # Check email a bit
- email = email.strip()
- parts = email.split()
- if len( parts ) == 1 and len( email ) > 0 and self._can_access_dataset( trans, hda ):
- to = to_address + ", " + email
- else:
- to = to_address
- subject = "Galaxy tool error report from %s" % email
- # Send it
+ biostar_report = 'biostar' in str( kwd.get( 'submit_error_report') ).lower()
+ if biostar_report:
+ return trans.response.send_redirect( url_for( controller='biostar', action='biostar_tool_bug_report', hda=id, email=email, message=message ) )
try:
- util.send_mail( frm, to, subject, body, trans.app.config )
+ error_reporter = EmailErrorReporter( id, trans.app )
+ error_reporter.send_report( user=trans.user, email=email, message=message )
return trans.show_ok_message( "Your error report has been sent" )
except Exception, e:
return trans.show_error_message( "An error occurred sending the report by email: %s" % str( e ) )
diff -r 6e9354d1fbdfd92ff316c52302945c02bbf13a0c -r ec715604b476c47703b90385062a6ec482cfd302 templates/webapps/galaxy/dataset/errors.mako
--- a/templates/webapps/galaxy/dataset/errors.mako
+++ b/templates/webapps/galaxy/dataset/errors.mako
@@ -71,7 +71,10 @@
<textarea name="message" rows="10" cols="40"></textarea></div><div class="form-row">
- <input type="submit" name="submit_error_report" value="Report"/>
+ <input type="submit" name="submit_error_report" value="Report" onclick="form.setAttribute('target', '_self');"/>
+ %if trans.app.config.biostar_url:
+ <input type="submit" name="submit_error_report" value="Post on Biostar" onclick="form.setAttribute('target', '_blank');"/>
+ %endif
</div></form></div>
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.
1
0
commit/galaxy-central: martenson: correct for random error that happens during grunt build (this css modification is just randomly modified for some builds)
by commits-noreply@bitbucket.org 24 Mar '14
by commits-noreply@bitbucket.org 24 Mar '14
24 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6e9354d1fbdf/
Changeset: 6e9354d1fbdf
User: martenson
Date: 2014-03-24 20:15:10
Summary: correct for random error that happens during grunt build (this css modification is just randomly modified for some builds)
Affected #: 1 file
diff -r 68d35caf407f367c3447a1a6675d6705c8daacf4 -r 6e9354d1fbdfd92ff316c52302945c02bbf13a0c static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -41,7 +41,7 @@
body{font-family:"Lucida Grande",verdana,arial,helvetica,sans-serif;font-size:12px;line-height:1.428571429;color:#333;background-color:#fff}
input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}
button,input,select[multiple],textarea{background-image:none}
-a{color:#5f6990;text-decoration:none}a:hover,a:focus{color:#404862;text-decoration:underline}
+a{color:#303030;text-decoration:none}a:hover,a:focus{color:#0a0a0a;text-decoration:underline}
a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}
img{vertical-align:middle}
.img-responsive{display:block;max-width:100%;height:auto}
@@ -114,13 +114,13 @@
.col-xs-12{width:100%}
@media (min-width:768px){.container{max-width:750px} .col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11{float:left} .col-sm-1{width:8.333333333333332%} .col-sm-2{width:16.666666666666664%} .col-sm-3{width:25%} .col-sm-4{width:33.33333333333333%} .col-sm-5{width:41.66666666666667%} .col-sm-6{width:50%} .col-sm-7{width:58.333333333333336%} .col-sm-8{width:66.66666666666666%} .col-sm-9{width:75%} .col-sm-10{width:83.33333333333334%} .col-sm-11{width:91.66666666666666%} .col-sm-12{width:100%} .col-sm-push-1{left:8.333333333333332%} .col-sm-push-2{left:16.666666666666664%} .col-sm-push-3{left:25%} .col-sm-push-4{left:33.33333333333333%} .col-sm-push-5{left:41.66666666666667%} .col-sm-push-6{left:50%} .col-sm-push-7{left:58.333333333333336%} .col-sm-push-8{left:66.66666666666666%} .col-sm-push-9{left:75%} .col-sm-push-10{left:83.33333333333334%} .col-sm-push-11{left:91.66666666666666%} .col-sm-pull-1{right:8.333333333333332%} .col-sm-pull-2{right:16.666666666666664%} .col-sm-pull-3{right:25%} .col-sm-pull-4{right:33.33333333333333%} .col-sm-pull-5{right:41.66666666666667%} .col-sm-pull-6{right:50%} .col-sm-pull-7{right:58.333333333333336%} .col-sm-pull-8{right:66.66666666666666%} .col-sm-pull-9{right:75%} .col-sm-pull-10{right:83.33333333333334%} .col-sm-pull-11{right:91.66666666666666%} .col-sm-offset-1{margin-left:8.333333333333332%} .col-sm-offset-2{margin-left:16.666666666666664%} .col-sm-offset-3{margin-left:25%} .col-sm-offset-4{margin-left:33.33333333333333%} .col-sm-offset-5{margin-left:41.66666666666667%} .col-sm-offset-6{margin-left:50%} .col-sm-offset-7{margin-left:58.333333333333336%} .col-sm-offset-8{margin-left:66.66666666666666%} .col-sm-offset-9{margin-left:75%} .col-sm-offset-10{margin-left:83.33333333333334%} .col-sm-offset-11{margin-left:91.66666666666666%}}@media (min-width:992px){.container{max-width:970px} .col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11{float:left} .col-md-1{width:8.333333333333332%} .col-md-2{width:16.666666666666664%} .col-md-3{width:25%} .col-md-4{width:33.33333333333333%} .col-md-5{width:41.66666666666667%} .col-md-6{width:50%} .col-md-7{width:58.333333333333336%} .col-md-8{width:66.66666666666666%} .col-md-9{width:75%} .col-md-10{width:83.33333333333334%} .col-md-11{width:91.66666666666666%} .col-md-12{width:100%} .col-md-push-0{left:auto} .col-md-push-1{left:8.333333333333332%} .col-md-push-2{left:16.666666666666664%} .col-md-push-3{left:25%} .col-md-push-4{left:33.33333333333333%} .col-md-push-5{left:41.66666666666667%} .col-md-push-6{left:50%} .col-md-push-7{left:58.333333333333336%} .col-md-push-8{left:66.66666666666666%} .col-md-push-9{left:75%} .col-md-push-10{left:83.33333333333334%} .col-md-push-11{left:91.66666666666666%} .col-md-pull-0{right:auto} .col-md-pull-1{right:8.333333333333332%} .col-md-pull-2{right:16.666666666666664%} .col-md-pull-3{right:25%} .col-md-pull-4{right:33.33333333333333%} .col-md-pull-5{right:41.66666666666667%} .col-md-pull-6{right:50%} .col-md-pull-7{right:58.333333333333336%} .col-md-pull-8{right:66.66666666666666%} .col-md-pull-9{right:75%} .col-md-pull-10{right:83.33333333333334%} .col-md-pull-11{right:91.66666666666666%} .col-md-offset-0{margin-left:0} .col-md-offset-1{margin-left:8.333333333333332%} .col-md-offset-2{margin-left:16.666666666666664%} .col-md-offset-3{margin-left:25%} .col-md-offset-4{margin-left:33.33333333333333%} .col-md-offset-5{margin-left:41.66666666666667%} .col-md-offset-6{margin-left:50%} .col-md-offset-7{margin-left:58.333333333333336%} .col-md-offset-8{margin-left:66.66666666666666%} .col-md-offset-9{margin-left:75%} .col-md-offset-10{margin-left:83.33333333333334%} .col-md-offset-11{margin-left:91.66666666666666%}}@media (min-width:1200px){.container{max-width:1170px} .col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11{float:left} .col-lg-1{width:8.333333333333332%} .col-lg-2{width:16.666666666666664%} .col-lg-3{width:25%} .col-lg-4{width:33.33333333333333%} .col-lg-5{width:41.66666666666667%} .col-lg-6{width:50%} .col-lg-7{width:58.333333333333336%} .col-lg-8{width:66.66666666666666%} .col-lg-9{width:75%} .col-lg-10{width:83.33333333333334%} .col-lg-11{width:91.66666666666666%} .col-lg-12{width:100%} .col-lg-push-0{left:auto} .col-lg-push-1{left:8.333333333333332%} .col-lg-push-2{left:16.666666666666664%} .col-lg-push-3{left:25%} .col-lg-push-4{left:33.33333333333333%} .col-lg-push-5{left:41.66666666666667%} .col-lg-push-6{left:50%} .col-lg-push-7{left:58.333333333333336%} .col-lg-push-8{left:66.66666666666666%} .col-lg-push-9{left:75%} .col-lg-push-10{left:83.33333333333334%} .col-lg-push-11{left:91.66666666666666%} .col-lg-pull-0{right:auto} .col-lg-pull-1{right:8.333333333333332%} .col-lg-pull-2{right:16.666666666666664%} .col-lg-pull-3{right:25%} .col-lg-pull-4{right:33.33333333333333%} .col-lg-pull-5{right:41.66666666666667%} .col-lg-pull-6{right:50%} .col-lg-pull-7{right:58.333333333333336%} .col-lg-pull-8{right:66.66666666666666%} .col-lg-pull-9{right:75%} .col-lg-pull-10{right:83.33333333333334%} .col-lg-pull-11{right:91.66666666666666%} .col-lg-offset-0{margin-left:0} .col-lg-offset-1{margin-left:8.333333333333332%} .col-lg-offset-2{margin-left:16.666666666666664%} .col-lg-offset-3{margin-left:25%} .col-lg-offset-4{margin-left:33.33333333333333%} .col-lg-offset-5{margin-left:41.66666666666667%} .col-lg-offset-6{margin-left:50%} .col-lg-offset-7{margin-left:58.333333333333336%} .col-lg-offset-8{margin-left:66.66666666666666%} .col-lg-offset-9{margin-left:75%} .col-lg-offset-10{margin-left:83.33333333333334%} .col-lg-offset-11{margin-left:91.66666666666666%}}table{max-width:100%;background-color:transparent}
th{text-align:left}
-.table{width:100%;margin-bottom:17px}.table thead>tr>th,.table tbody>tr>th,.table tfoot>tr>th,.table thead>tr>td,.table tbody>tr>td,.table tfoot>tr>td{padding:8px;line-height:1.428571429;vertical-align:top;border-top:1px solid #bfbfbf}
-.table thead>tr>th{vertical-align:bottom;border-bottom:2px solid #bfbfbf}
+.table{width:100%;margin-bottom:17px}.table thead>tr>th,.table tbody>tr>th,.table tfoot>tr>th,.table thead>tr>td,.table tbody>tr>td,.table tfoot>tr>td{padding:8px;line-height:1.428571429;vertical-align:top;border-top:1px solid #d6b161}
+.table thead>tr>th{vertical-align:bottom;border-bottom:2px solid #d6b161}
.table caption+thead tr:first-child th,.table colgroup+thead tr:first-child th,.table thead:first-child tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child td{border-top:0}
-.table tbody+tbody{border-top:2px solid #bfbfbf}
+.table tbody+tbody{border-top:2px solid #d6b161}
.table .table{background-color:#fff}
.table-condensed thead>tr>th,.table-condensed tbody>tr>th,.table-condensed tfoot>tr>th,.table-condensed thead>tr>td,.table-condensed tbody>tr>td,.table-condensed tfoot>tr>td{padding:5px}
-.table-bordered{border:1px solid #bfbfbf}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #bfbfbf}
+.table-bordered{border:1px solid #d6b161}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #d6b161}
.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}
.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}
.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}
@@ -133,7 +133,7 @@
.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#f7b0ae;border-color:#c61913}
.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fce1ba;border-color:#e28709}
.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#fbd6a2;border-color:#c97908}
-@media (max-width:768px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #bfbfbf;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0;background-color:#fff}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap} .table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0} .table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0} .table-responsive>.table-bordered>thead>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>thead>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0}
+@media (max-width:768px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #d6b161;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0;background-color:#fff}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap} .table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0} .table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0} .table-responsive>.table-bordered>thead>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>thead>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0}
legend{display:block;width:100%;padding:0;margin-bottom:17px;font-size:18px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #bfbfbf}
label{display:inline-block;margin-bottom:5px;font-weight:bold}
input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}
@@ -148,7 +148,7 @@
.form-control::-moz-placeholder{color:#999}
.form-control:-ms-input-placeholder{color:#999}
.form-control::-webkit-input-placeholder{color:#999}
-.form-control{display:block;width:100%;height:27px;padding:4px 10px;font-size:12px;line-height:1.428571429;color:#555;vertical-align:middle;background-color:#fff;border:1px solid #bfbfbf;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6)}
+.form-control{display:block;width:100%;height:27px;padding:4px 10px;font-size:12px;line-height:1.428571429;color:#555;vertical-align:middle;background-color:#fff;border:1px solid #aaa;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6)}
.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee}
textarea.form-control{height:auto}
.form-group{margin-bottom:15px}
@@ -202,9 +202,9 @@
.btn-info{color:#fff;background-color:#3186a0;border-color:#010304}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#276c81;border-color:#000}
.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}
.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#3186a0;border-color:#010304}
-.btn-link{color:#5f6990;font-weight:normal;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}
+.btn-link{color:#303030;font-weight:normal;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}
.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}
-.btn-link:hover,.btn-link:focus{color:#404862;text-decoration:underline;background-color:transparent}
+.btn-link:hover,.btn-link:focus{color:#0a0a0a;text-decoration:underline;background-color:transparent}
.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}
.btn-lg{padding:8px 14px;font-size:15px;line-height:1.33;border-radius:5px}
.btn-sm,.btn-xs{padding:3px 8px;font-size:11px;line-height:1.5;border-radius:2px}
@@ -486,7 +486,7 @@
textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}
.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}
.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}
-.input-group-addon{padding:4px 10px;font-size:12px;font-weight:normal;line-height:1;text-align:center;background-color:#eee;border:1px solid #bfbfbf;border-radius:3px}.input-group-addon.input-sm{padding:3px 8px;font-size:11px;border-radius:2px}
+.input-group-addon{padding:4px 10px;font-size:12px;font-weight:normal;line-height:1;text-align:center;background-color:#eee;border:1px solid #aaa;border-radius:3px}.input-group-addon.input-sm{padding:3px 8px;font-size:11px;border-radius:2px}
.input-group-addon.input-lg{padding:8px 14px;font-size:15px;border-radius:5px}
.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}
.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}
@@ -502,7 +502,7 @@
.nav:after{clear:both}
.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:4px 10px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}
.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;cursor:not-allowed}
-.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#5f6990}
+.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#303030}
.nav .nav-divider{height:1px;margin:7.5px 0;overflow:hidden;background-color:#e5e5e5}
.nav>li>a>img{max-width:none}
.nav-tabs{border-bottom:1px solid #bfbfbf}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.428571429;border:1px solid transparent;border-radius:3px 3px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #bfbfbf}
@@ -526,8 +526,8 @@
.tabbable:after{clear:both}
.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}
.tab-content>.active,.pill-content>.active{display:block}
-.nav .caret{border-top-color:#5f6990;border-bottom-color:#5f6990}
-.nav a:hover .caret{border-top-color:#404862;border-bottom-color:#404862}
+.nav .caret{border-top-color:#303030;border-bottom-color:#303030}
+.nav a:hover .caret{border-top-color:#0a0a0a;border-bottom-color:#0a0a0a}
.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}
.navbar{position:relative;z-index:1000;min-height:33px;margin-bottom:17px;border:1px solid transparent}.navbar:before,.navbar:after{content:" ";display:table;}
.navbar:after{clear:both}
@@ -617,7 +617,7 @@
.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:11px;font-weight:bold;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#999;border-radius:10px}.badge:empty{display:none}
a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}
.btn .badge{position:relative;top:-1px}
-a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#5f6990;background-color:#fff}
+a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#303030;background-color:#fff}
.nav-pills>li>a>.badge{margin-left:3px}
.jumbotron{padding:30px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:2.1428571435;color:inherit;background-color:#eee}.jumbotron h1{line-height:1;color:inherit}
.jumbotron p{line-height:1.4}
@@ -672,7 +672,7 @@
.panel>.list-group .list-group-item:last-child{border-bottom:0}
.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}
.panel>.table{margin-bottom:0}
-.panel>.panel-body+.table{border-top:1px solid #bfbfbf}
+.panel>.panel-body+.table{border-top:1px solid #d6b161}
.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:2px;border-top-left-radius:2px}
.panel-title{margin-top:0;margin-bottom:0;font-size:14px}.panel-title>a{color:inherit}
.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #bfbfbf;border-bottom-right-radius:2px;border-bottom-left-radius:2px}
@@ -1454,7 +1454,7 @@
select{padding:2px;font-size:12px;line-height:1.428571429}
select,input,textarea{font:inherit}
.form-row select,.form-row textarea,.form-row input[type="text"],.form-row input[type="file"],.form-row input[type="password"]{max-width:90%}
-textarea,input[type="text"],input[type="password"]{font-size:12px;line-height:1.428571429;border:1px solid #bfbfbf;padding:3px}
+textarea,input[type="text"],input[type="password"]{font-size:12px;line-height:1.428571429;border:1px solid #aaa;padding:3px}
input[type="submit"],button{display:inline-block;padding:4px 10px;margin-bottom:0;font-size:12px;font-weight:normal;line-height:1.428571429;text-align:center;vertical-align:middle;cursor:pointer;border:1px solid transparent;border-radius:3px;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;white-space:normal;color:#333;background-color:#f2f2f2;border-color:#bfbfbf}input[type="submit"]:focus,button:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}
input[type="submit"]:hover,button:hover,input[type="submit"]:focus,button:focus{color:#333;text-decoration:none}
input[type="submit"]:active,button:active,input[type="submit"].active,button.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}
@@ -1465,7 +1465,7 @@
input[type="submit"].btn-primary,button.btn-primary{color:#fff;background-color:#5f6990;border-color:#0e0f15}input[type="submit"].btn-primary:hover,button.btn-primary:hover,input[type="submit"].btn-primary:focus,button.btn-primary:focus,input[type="submit"].btn-primary:active,button.btn-primary:active,input[type="submit"].btn-primary.active,button.btn-primary.active,.open .dropdown-toggleinput[type="submit"].btn-primary,.open .dropdown-togglebutton.btn-primary{color:#fff;background-color:#4e5777;border-color:#000}
input[type="submit"].btn-primary:active,button.btn-primary:active,input[type="submit"].btn-primary.active,button.btn-primary.active,.open .dropdown-toggleinput[type="submit"].btn-primary,.open .dropdown-togglebutton.btn-primary{background-image:none}
input[type="submit"].btn-primary.disabled,button.btn-primary.disabled,input[type="submit"].btn-primary[disabled],button.btn-primary[disabled],fieldset[disabled] input[type="submit"].btn-primary,fieldset[disabled] button.btn-primary,input[type="submit"].btn-primary.disabled:hover,button.btn-primary.disabled:hover,input[type="submit"].btn-primary[disabled]:hover,button.btn-primary[disabled]:hover,fieldset[disabled] input[type="submit"].btn-primary:hover,fieldset[disabled] button.btn-primary:hover,input[type="submit"].btn-primary.disabled:focus,button.btn-primary.disabled:focus,input[type="submit"].btn-primary[disabled]:focus,button.btn-primary[disabled]:focus,fieldset[disabled] input[type="submit"].btn-primary:focus,fieldset[disabled] button.btn-primary:focus,input[type="submit"].btn-primary.disabled:active,button.btn-primary.disabled:active,input[type="submit"].btn-primary[disabled]:active,button.btn-primary[disabled]:active,fieldset[disabled] input[type="submit"].btn-primary:active,fieldset[disabled] button.btn-primary:active,input[type="submit"].btn-primary.disabled.active,button.btn-primary.disabled.active,input[type="submit"].btn-primary[disabled].active,button.btn-primary[disabled].active,fieldset[disabled] input[type="submit"].btn-primary.active,fieldset[disabled] button.btn-primary.active{background-color:#5f6990;border-color:#0e0f15}
-.search-query{display:inline-block;padding:4px;font-size:12px;line-height:1.428571429;color:#555;border:1px solid #bfbfbf;padding-left:14px !important;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;max-width:auto}
+.search-query{display:inline-block;padding:4px;font-size:12px;line-height:1.428571429;color:#555;border:1px solid #aaa;padding-left:14px !important;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;max-width:auto}
.search-query:focus{border-color:rgba(24,132,218,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);outline:0;outline:thin dotted \9;}
.search-spinner{position:absolute;display:none;right:6px;top:9px}
#search-clear-btn{position:absolute;right:6px;top:5px;display:block;font-size:1.4em;text-decoration:none;color:#888;font-family:FontAwesome;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#search-clear-btn:before{content:"\f057"}
@@ -1506,9 +1506,9 @@
table.tabletip th{white-space:nowrap;border-bottom:1px solid #444;padding-right:3px}
table.tabletip td{border-bottom:1px solid #ddd}
table.tabletip tbody tr:hover td{background-color:#eee}
-table.colored{border-top:solid #bfbfbf 1px;border-bottom:solid #bfbfbf 1px}
+table.colored{border-top:solid #d6b161 1px;border-bottom:solid #d6b161 1px}
table.colored td,table.colored th{text-align:left;padding:5px;line-height:1.428571429}
-table.colored tr.header{background:#ebd9b2;color:#000;background-repeat:repeat-x;background-position:top;border-bottom:solid #bfbfbf 1px;font-weight:bold}
+table.colored tr.header{background:#ebd9b2;color:#000;background-repeat:repeat-x;background-position:top;border-bottom:solid #d6b161 1px;font-weight:bold}
table.colored tr{background:#fff}
table.colored tr.odd_row{background:#f9f9f9}
div.debug{margin:10px;padding:5px;background:#FFFF99;border:solid #FFFF33 1px;color:black}
@@ -1519,7 +1519,7 @@
.grid tbody td{line-height:1.428571429;border-top:solid #DDDDDD 1px;border-bottom:solid #DDDDDD 1px;padding:5px}
.grid tbody td:empty{padding:0}
.grid thead tr{height:2em}
-.grid thead th{line-height:1.428571429;background:#ebd9b2;color:#000;border-top:solid #bfbfbf 1px;border-bottom:solid #bfbfbf 1px;padding:5px;text-align:left;white-space:nowrap}
+.grid thead th{line-height:1.428571429;background:#ebd9b2;color:#000;border-top:solid #d6b161 1px;border-bottom:solid #d6b161 1px;padding:5px;text-align:left;white-space:nowrap}
.grid tfoot td{background-color:#F8F8F8;border-top:solid #DDDDDD 1px;border-bottom:solid #DDDDDD 1px;padding:5px}
.grid .current{background-color:#EEEEFF}
.count-box{min-width:1.1em;padding:5px;border-width:1px;border-style:solid;text-align:center;display:inline-block}
@@ -1618,7 +1618,7 @@
.icon-btn-group .icon-btn:only-child{margin:0px;border-radius:3px}
.search-input .search-query{width:100%;padding-right:24px}
.search-input .search-clear,.search-input .search-loading{position:relative;display:inline-block;float:right;margin-top:-23px;margin-right:4px;font-size:1.4em;line-height:23px;color:grey}
-.search-input .search-clear:hover{color:#5f6990}
+.search-input .search-clear:hover{color:#303030}
#history-refresh-button,#history-options-button{display:inline-block;height:22px;width:22px;text-align:center;line-height:19px;font-size:1.2em;padding:0px}
.history-panel .dataset{border:1px solid #bfbfbf}.history-panel .dataset .vertical-spacing{margin-bottom:8px}
.history-panel .dataset .info-section{border-radius:3px;border:1px solid rgba(153,153,153,0.30000000000000004);padding:4px}
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.
1
0