galaxy-commits
Threads by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 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
- 15302 discussions
commit/galaxy-central: carlfeberhard: HDA & History Managers: move serialization logic for bootstrapping into view, display_by_username_and_slug, and root/history into Managers in order to display dataset collections
by commits-noreply@bitbucket.org 10 Jul '14
by commits-noreply@bitbucket.org 10 Jul '14
10 Jul '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/65ec063664eb/
Changeset: 65ec063664eb
User: carlfeberhard
Date: 2014-07-10 15:56:17
Summary: HDA & History Managers: move serialization logic for bootstrapping into view, display_by_username_and_slug, and root/history into Managers in order to display dataset collections
Affected #: 7 files
diff -r 1861cbcc610da6e519c7760e1c829ec3b66887f4 -r 65ec063664eb0ce100d76d985fba93f200ac5178 lib/galaxy/managers/__init__.py
--- a/lib/galaxy/managers/__init__.py
+++ b/lib/galaxy/managers/__init__.py
@@ -1,4 +1,33 @@
-""" 'Business logic' independent of web transactions/user context (trans)
-should be pushed into models - but logic that requires the context trans
-should be placed under this module.
"""
+Classes that manage resources (models, tools, etc.) by using the current
+Transaction.
+
+Encapsulates the intersection of trans (or trans.sa_session), models,
+and Controllers.
+
+Responsibilities:
+ model operations that involve the trans/sa_session (CRUD)
+ security:
+ ownership, accessibility
+ common aspect-oriented operations via new mixins:
+ sharable, annotatable, tagable, ratable
+
+Not responsible for:
+ encoding/decoding ids
+ any http gobblygook
+ formatting of returned data (always python structures)
+ formatting of raised errors
+
+The goal is to have Controllers only handle:
+ query-string/payload parsing and encoding/decoding ids
+ http
+ return formatting
+
+and:
+ control, improve namespacing in Controllers
+ DRY for Controller ops (define here - use in both UI/API Controllers)
+
+In other words, 'Business logic' independent of web transactions/user context
+(trans) should be pushed into models - but logic that requires the context
+trans should be placed under this module.
+"""
diff -r 1861cbcc610da6e519c7760e1c829ec3b66887f4 -r 65ec063664eb0ce100d76d985fba93f200ac5178 lib/galaxy/managers/base.py
--- /dev/null
+++ b/lib/galaxy/managers/base.py
@@ -0,0 +1,7 @@
+
+
+class ModelManager( object ):
+ pass
+
+class ModelSerializer( object ):
+ pass
diff -r 1861cbcc610da6e519c7760e1c829ec3b66887f4 -r 65ec063664eb0ce100d76d985fba93f200ac5178 lib/galaxy/managers/hdas.py
--- a/lib/galaxy/managers/hdas.py
+++ b/lib/galaxy/managers/hdas.py
@@ -1,16 +1,35 @@
+"""
+Manager and Serializer for HDAs.
+
+HistoryDatasetAssociations (HDAs) are datasets contained or created in a
+history.
+"""
+
from galaxy import exceptions
-from ..managers import histories
+from galaxy.managers import base as manager_base
+from galaxy.managers import histories as history_manager
-class HDAManager( object ):
+import galaxy.web
+import galaxy.datatypes.metadata
+from galaxy import objectstore
+
+class HDAManager( manager_base.ModelManager ):
+ """
+ Interface/service object for interacting with HDAs.
+ """
def __init__( self ):
- self.histories_mgr = histories.HistoryManager()
+ """
+ Set up and initialize other managers needed by hdas.
+ """
+ self.histories_mgr = history_manager.HistoryManager()
def get( self, trans, unencoded_id, check_ownership=True, check_accessible=True ):
"""
+ Get an HDA by its unencoded db id, checking ownership (via its history)
+ or accessibility (via dataset shares/permissions).
"""
- # this is a replacement for UsesHistoryDatasetAssociationMixin because mixins are a bad soln/structure
hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( unencoded_id )
if hda is None:
raise exceptions.ObjectNotFound()
@@ -19,7 +38,8 @@
def secure( self, trans, hda, check_ownership=True, check_accessible=True ):
"""
- checks if (a) user owns item or (b) item is accessible to user.
+ check ownership (via its history) or accessibility (via dataset
+ shares/permissions).
"""
# all items are accessible to an admin
if trans.user and trans.user_is_admin():
@@ -31,12 +51,18 @@
return hda
def can_access_dataset( self, trans, hda ):
+ """
+ Use security agent to see if current user has access to dataset.
+ """
current_user_roles = trans.get_current_user_roles()
return trans.app.security_agent.can_access_dataset( current_user_roles, hda.dataset )
#TODO: is_owner, is_accessible
def check_ownership( self, trans, hda ):
+ """
+ Use history to see if current user owns HDA.
+ """
if not trans.user:
#if hda.history == trans.history:
# return hda
@@ -51,6 +77,9 @@
"HistoryDatasetAssociation is not owned by the current user", type='error' )
def check_accessible( self, trans, hda ):
+ """
+ Raise error if HDA is not accessible.
+ """
if trans.user and trans.user_is_admin():
return hda
# check for access of the containing history...
@@ -62,6 +91,151 @@
"HistoryDatasetAssociation is not accessible to the current user", type='error' )
def err_if_uploading( self, trans, hda ):
+ """
+ Raise error if HDA is still uploading.
+ """
if hda.state == trans.model.Dataset.states.UPLOAD:
raise exceptions.Conflict( "Please wait until this dataset finishes uploading" )
return hda
+
+ def get_hda_dict( self, trans, hda ):
+ """
+ Return full details of this HDA in dictionary form.
+ """
+ #precondition: the user's access to this hda has already been checked
+ #TODO:?? postcondition: all ids are encoded (is this really what we want at this level?)
+ expose_dataset_path = trans.user_is_admin() or trans.app.config.expose_dataset_path
+ hda_dict = hda.to_dict( view='element', expose_dataset_path=expose_dataset_path )
+ hda_dict[ 'api_type' ] = "file"
+
+ # Add additional attributes that depend on trans must be added here rather than at the model level.
+ can_access_hda = trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), hda.dataset )
+ can_access_hda = ( trans.user_is_admin() or can_access_hda )
+ if not can_access_hda:
+ return self.get_inaccessible_hda_dict( trans, hda )
+ hda_dict[ 'accessible' ] = True
+
+ #TODO: I'm unclear as to which access pattern is right
+ hda_dict[ 'annotation' ] = hda.get_item_annotation_str( trans.sa_session, hda.history.user, hda )
+ #annotation = getattr( hda, 'annotation', hda.get_item_annotation_str( trans.sa_session, trans.user, hda ) )
+
+ # ---- return here if deleted AND purged OR can't access
+ purged = ( hda.purged or hda.dataset.purged )
+ if ( hda.deleted and purged ):
+ #TODO: to_dict should really go AFTER this - only summary data
+ return trans.security.encode_dict_ids( hda_dict )
+
+ if expose_dataset_path:
+ try:
+ hda_dict[ 'file_name' ] = hda.file_name
+ except objectstore.ObjectNotFound:
+ log.exception( 'objectstore.ObjectNotFound, HDA %s.', hda.id )
+
+ hda_dict[ 'download_url' ] = galaxy.web.url_for( 'history_contents_display',
+ history_id = trans.security.encode_id( hda.history.id ),
+ history_content_id = trans.security.encode_id( hda.id ) )
+
+ # indeces, assoc. metadata files, etc.
+ meta_files = []
+ for meta_type in hda.metadata.spec.keys():
+ if isinstance( hda.metadata.spec[ meta_type ].param, galaxy.datatypes.metadata.FileParameter ):
+ meta_files.append( dict( file_type=meta_type ) )
+ if meta_files:
+ hda_dict[ 'meta_files' ] = meta_files
+
+ # currently, the viz reg is optional - handle on/off
+ if trans.app.visualizations_registry:
+ hda_dict[ 'visualizations' ] = trans.app.visualizations_registry.get_visualizations( trans, hda )
+ else:
+ hda_dict[ 'visualizations' ] = hda.get_visualizations()
+ #TODO: it may also be wiser to remove from here and add as API call that loads the visualizations
+ # when the visualizations button is clicked (instead of preloading/pre-checking)
+
+ # ---- return here if deleted
+ if hda.deleted and not purged:
+ return trans.security.encode_dict_ids( hda_dict )
+
+ return trans.security.encode_dict_ids( hda_dict )
+
+ def get_inaccessible_hda_dict( self, trans, hda ):
+ """
+ Return truncated serialization of HDA when inaccessible to user.
+ """
+ return trans.security.encode_dict_ids({
+ 'id' : hda.id,
+ 'history_id': hda.history.id,
+ 'hid' : hda.hid,
+ 'name' : hda.name,
+ 'state' : hda.state,
+ 'deleted' : hda.deleted,
+ 'visible' : hda.visible,
+ 'accessible': False
+ })
+
+ def get_hda_dict_with_error( self, trans, hda=None, history_id=None, id=None, error_msg='Error' ):
+ """
+ Return truncated serialization of HDA when error raised getting
+ details.
+ """
+ return trans.security.encode_dict_ids({
+ 'id' : hda.id if hda else id,
+ 'history_id': hda.history.id if hda else history_id,
+ 'hid' : hda.hid if hda else '(unknown)',
+ 'name' : hda.name if hda else '(unknown)',
+ 'error' : error_msg,
+ 'state' : trans.model.Dataset.states.NEW
+ })
+
+ def get_display_apps( self, trans, hda ):
+ """
+ Return dictionary containing new-style display app urls.
+ """
+ display_apps = []
+ for display_app in hda.get_display_applications( trans ).itervalues():
+
+ app_links = []
+ for link_app in display_app.links.itervalues():
+ app_links.append({
+ 'target': link_app.url.get( 'target_frame', '_blank' ),
+ 'href' : link_app.get_display_url( hda, trans ),
+ 'text' : gettext( link_app.name )
+ })
+ if app_links:
+ display_apps.append( dict( label=display_app.name, links=app_links ) )
+
+ return display_apps
+
+ def get_old_display_applications( self, trans, hda ):
+ """
+ Return dictionary containing old-style display app urls.
+ """
+ display_apps = []
+ if not trans.app.config.enable_old_display_applications:
+ return display_apps
+
+ for display_app in hda.datatype.get_display_types():
+ target_frame, display_links = hda.datatype.get_display_links( hda,
+ display_app, trans.app, trans.request.base )
+
+ if len( display_links ) > 0:
+ display_label = hda.datatype.get_display_label( display_app )
+
+ app_links = []
+ for display_name, display_link in display_links:
+ app_links.append({
+ 'target': target_frame,
+ 'href' : display_link,
+ 'text' : gettext( display_name )
+ })
+ if app_links:
+ display_apps.append( dict( label=display_label, links=app_links ) )
+
+ return display_apps
+
+
+# =============================================================================
+class HistorySerializer( manager_base.ModelSerializer ):
+ """
+ Interface/service object for serializing HDAs into dictionaries.
+ """
+ pass
diff -r 1861cbcc610da6e519c7760e1c829ec3b66887f4 -r 65ec063664eb0ce100d76d985fba93f200ac5178 lib/galaxy/managers/histories.py
--- a/lib/galaxy/managers/histories.py
+++ b/lib/galaxy/managers/histories.py
@@ -1,9 +1,32 @@
+"""
+Manager and Serializer for histories.
+
+Histories are containers for datasets or dataset collections
+created (or copied) by users over the course of an analysis.
+"""
+
from galaxy import exceptions
from galaxy.model import orm
+from galaxy.managers import base as manager_base
+import galaxy.managers.hdas
-class HistoryManager( object ):
+import galaxy.web
+import galaxy.dataset_collections.util
+
+import logging
+log = logging.getLogger( __name__ )
+
+
+# =============================================================================
+class HistoryManager( manager_base.ModelManager ):
+ """
+ Interface/service object for interacting with HDAs.
+ """
+
#TODO: all the following would be more useful if passed the user instead of defaulting to trans.user
+ def __init__( self, *args, **kwargs ):
+ super( HistoryManager, self ).__init__( *args, **kwargs )
def get( self, trans, unencoded_id, check_ownership=True, check_accessible=True, deleted=None ):
"""
@@ -44,7 +67,7 @@
def secure( self, trans, history, check_ownership=True, check_accessible=True ):
"""
- checks if (a) user owns item or (b) item is accessible to user.
+ Checks if (a) user owns item or (b) item is accessible to user.
"""
# all items are accessible to an admin
if trans.user and trans.user_is_admin():
@@ -56,15 +79,28 @@
return history
def is_current( self, trans, history ):
+ """
+ True if the given history is the user's current history.
+
+ Returns False if the session has no current history.
+ """
+ if trans.history is None:
+ return False
return trans.history == history
def is_owner( self, trans, history ):
+ """
+ True if the current user is the owner of the given history.
+ """
# anon users are only allowed to view their current history
if not trans.user:
return self.is_current( trans, history )
return trans.user == history.user
def check_ownership( self, trans, history ):
+ """
+ Raises error if the current user is not the owner of the history.
+ """
if trans.user and trans.user_is_admin():
return history
if not trans.user and not self.is_current( trans, history ):
@@ -74,6 +110,9 @@
raise exceptions.ItemOwnershipException( "History is not owned by the current user", type='error' )
def is_accessible( self, trans, history ):
+ """
+ True if the user can access (read) the current history.
+ """
# admin always have access
if trans.user and trans.user_is_admin():
return True
@@ -88,6 +127,103 @@
return False
def check_accessible( self, trans, history ):
+ """
+ Raises error if the current user can't access the history.
+ """
if self.is_accessible( trans, history ):
return history
raise exceptions.ItemAccessibilityException( "History is not accessible to the current user", type='error' )
+
+ #TODO: bleh...
+ def _get_history_data( self, trans, history ):
+ """
+ Returns a dictionary containing ``history`` and ``contents``, serialized
+ history and an array of serialized history contents respectively.
+ """
+ hda_mgr = galaxy.managers.hdas.HDAManager()
+ collection_dictifier = galaxy.dataset_collections.util.dictify_dataset_collection_instance
+
+ history_dictionary = {}
+ contents_dictionaries = []
+ try:
+ #for content in history.contents_iter( **contents_kwds ):
+ for content in history.contents_iter( types=[ 'dataset', 'dataset_collection' ] ):
+ hda_dict = {}
+
+ if isinstance( content, trans.app.model.HistoryDatasetAssociation ):
+ try:
+ hda_dict = hda_mgr.get_hda_dict( trans, content )
+ except Exception, exc:
+ # don't fail entire list if hda err's, record and move on
+ log.exception( 'Error bootstrapping hda: %s', exc )
+ hda_dict = hda_mgr.get_hda_dict_with_error( trans, content, str( exc ) )
+
+ elif isinstance( content, trans.app.model.HistoryDatasetCollectionAssociation ):
+ try:
+ service = trans.app.dataset_collections_service
+ dataset_collection_instance = service.get_dataset_collection_instance(
+ trans=trans,
+ instance_type='history',
+ id=trans.security.encode_id( content.id ),
+ )
+ hda_dict = collection_dictifier( dataset_collection_instance,
+ security=trans.security, parent=dataset_collection_instance.history, view="element" )
+
+ except Exception, exc:
+ log.exception( "Error in history API at listing dataset collection: %s", exc )
+ #TODO: return some dict with the error
+
+ contents_dictionaries.append( hda_dict )
+
+ # re-use the hdas above to get the history data...
+ history_dictionary = self.get_history_dict( trans, history, contents_dictionaries=contents_dictionaries )
+
+ except Exception, exc:
+ user_id = str( trans.user.id ) if trans.user else '(anonymous)'
+ log.exception( 'Error bootstrapping history for user %s: %s', user_id, str( exc ) )
+ message = ( 'An error occurred getting the history data from the server. '
+ + 'Please contact a Galaxy administrator if the problem persists.' )
+ history_dictionary[ 'error' ] = message
+
+ return {
+ 'history' : history_dictionary,
+ 'contents' : contents_dictionaries
+ }
+
+ def get_history_dict( self, trans, history, contents_dictionaries=None ):
+ """
+ Returns history data in the form of a dictionary.
+ """
+ #TODO: to serializer
+ history_dict = history.to_dict( view='element', value_mapper={ 'id':trans.security.encode_id })
+ history_dict[ 'user_id' ] = None
+ if history.user_id:
+ history_dict[ 'user_id' ] = trans.security.encode_id( history.user_id )
+
+ history_dict[ 'nice_size' ] = history.get_disk_size( nice_size=True )
+ history_dict[ 'annotation' ] = history.get_item_annotation_str( trans.sa_session, history.user, history )
+ if not history_dict[ 'annotation' ]:
+ history_dict[ 'annotation' ] = ''
+
+ #TODO: item_slug url
+ if history_dict[ 'importable' ] and history_dict[ 'slug' ]:
+ username_and_slug = ( '/' ).join(( 'u', history.user.username, 'h', history_dict[ 'slug' ] ))
+ history_dict[ 'username_and_slug' ] = username_and_slug
+
+#TODO: re-add
+ #hda_summaries = hda_dictionaries if hda_dictionaries else self.get_hda_summary_dicts( trans, history )
+ ##TODO remove the following in v2
+ #( state_counts, state_ids ) = self._get_hda_state_summaries( trans, hda_summaries )
+ #history_dict[ 'state_details' ] = state_counts
+ #history_dict[ 'state_ids' ] = state_ids
+ #history_dict[ 'state' ] = self._get_history_state_from_hdas( trans, history, state_counts )
+
+ return history_dict
+
+
+# =============================================================================
+class HistorySerializer( manager_base.ModelSerializer ):
+ """
+ Interface/service object for serializing histories into dictionaries.
+ """
+ pass
diff -r 1861cbcc610da6e519c7760e1c829ec3b66887f4 -r 65ec063664eb0ce100d76d985fba93f200ac5178 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
@@ -92,7 +92,7 @@
else:
types = [ 'dataset', "dataset_collection" ]
- contents_kwds = {'types': types}
+ contents_kwds = { 'types': types }
if ids:
ids = map( lambda id: trans.security.decode_id( id ), ids.split( ',' ) )
contents_kwds[ 'ids' ] = ids
@@ -110,14 +110,14 @@
details = util.listify( details )
for content in history.contents_iter( **contents_kwds ):
- if isinstance(content, trans.app.model.HistoryDatasetAssociation):
+ if isinstance( content, trans.app.model.HistoryDatasetAssociation ):
encoded_content_id = trans.security.encode_id( content.id )
detailed = details == 'all' or ( encoded_content_id in details )
if detailed:
rval.append( self._detailed_hda_dict( trans, content ) )
else:
rval.append( self._summary_hda_dict( trans, history_id, content ) )
- elif isinstance(content, trans.app.model.HistoryDatasetCollectionAssociation):
+ elif isinstance( content, trans.app.model.HistoryDatasetCollectionAssociation ):
rval.append( self.__collection_dict( trans, content ) )
return rval
@@ -149,7 +149,8 @@
}
def __collection_dict( self, trans, dataset_collection_instance, view="collection" ):
- return dictify_dataset_collection_instance( dataset_collection_instance, security=trans.security, parent=dataset_collection_instance.history, view=view )
+ return dictify_dataset_collection_instance( dataset_collection_instance,
+ security=trans.security, parent=dataset_collection_instance.history, view=view )
def _detailed_hda_dict( self, trans, hda ):
"""
@@ -201,8 +202,7 @@
)
return self.__collection_dict( trans, dataset_collection_instance, view="element" )
except Exception, e:
- msg = "Error in history API at listing dataset collection: %s" % ( str(e) )
- log.error( msg, exc_info=True )
+ log.exception( "Error in history API at listing dataset collection: %s", e )
trans.response.status = 500
return msg
diff -r 1861cbcc610da6e519c7760e1c829ec3b66887f4 -r 65ec063664eb0ce100d76d985fba93f200ac5178 lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -4,6 +4,7 @@
import galaxy.util
from galaxy import model
from galaxy import web
+from galaxy import managers
from galaxy.datatypes.data import nice_size
from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings
from galaxy.model.orm import and_, eagerload_all, func
@@ -194,6 +195,12 @@
class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesItemRatings,
UsesHistoryMixin, UsesHistoryDatasetAssociationMixin, ExportsHistoryMixin,
ImportsHistoryMixin ):
+ def __init__( self, app ):
+ super( HistoryController, self ).__init__( app )
+ self.mgrs = util.bunch.Bunch(
+ histories=managers.histories.HistoryManager()
+ )
+
@web.expose
def index( self, trans ):
return ""
@@ -204,6 +211,7 @@
trans.response.set_content_type( 'text/xml' )
return trans.fill_template( "/history/list_as_xml.mako" )
+ # ......................................................................... lists
stored_list_grid = HistoryListGrid()
shared_list_grid = SharedHistoryListGrid()
published_list_grid = HistoryAllPublishedGrid()
@@ -437,6 +445,7 @@
# Render the list view
return self.shared_list_grid( trans, status=status, message=message, **kwargs )
+ # ......................................................................... html
@web.expose
def display_structured( self, trans, id=None ):
"""
@@ -512,6 +521,435 @@
return trans.fill_template( "history/display_structured.mako", items=items, history=history )
@web.expose
+ def view( self, trans, id=None, show_deleted=False, show_hidden=False, use_panels=True ):
+ """
+ View a history. If a history is importable, then it is viewable by any user.
+ """
+ # Get history to view.
+ if not id:
+ return trans.show_error_message( "You must specify a history you want to view." )
+
+ show_deleted = galaxy.util.string_as_bool( show_deleted )
+ show_hidden = galaxy.util.string_as_bool( show_hidden )
+ use_panels = galaxy.util.string_as_bool( use_panels )
+
+ history_dictionary = {}
+ hda_dictionaries = []
+ user_is_owner = False
+ try:
+ history_to_view = self.get_history( trans, id, check_ownership=False, check_accessible=False )
+ if not history_to_view:
+ return trans.show_error_message( "The specified history does not exist." )
+ if history_to_view.user == trans.user:
+ user_is_owner = True
+
+ if( ( history_to_view.user != trans.user )
+ # Admin users can view any history
+ and ( not trans.user_is_admin() )
+ and ( not history_to_view.importable )
+ and ( trans.user not in history_to_view.users_shared_with_dot_users ) ):
+ return trans.show_error_message( "Either you are not allowed to view this history"
+ + " or the owner of this history has not made it accessible." )
+
+ # include all datasets: hidden, deleted, and purged
+ history_data = self.mgrs.histories._get_history_data( trans, history_to_view )
+ history_dictionary = history_data[ 'history' ]
+ hda_dictionaries = history_data[ 'contents' ]
+
+ except Exception, exc:
+ user_id = str( trans.user.id ) if trans.user else '(anonymous)'
+ log.exception( 'Error bootstrapping history for user %s: %s', user_id, str( exc ) )
+ history_dictionary[ 'error' ] = ( 'An error occurred getting the history data from the server. '
+ + 'Please contact a Galaxy administrator if the problem persists.' )
+
+ return trans.fill_template_mako( "history/view.mako",
+ history=history_dictionary, hdas=hda_dictionaries, user_is_owner=user_is_owner,
+ show_deleted=show_deleted, show_hidden=show_hidden, use_panels=use_panels )
+
+ @web.expose
+ def display_by_username_and_slug( self, trans, username, slug ):
+ """
+ Display history based on a username and slug.
+ """
+ # Get history.
+ session = trans.sa_session
+ user = session.query( model.User ).filter_by( username=username ).first()
+ history = trans.sa_session.query( model.History ).filter_by( user=user, slug=slug, deleted=False ).first()
+ if history is None:
+ raise web.httpexceptions.HTTPNotFound()
+ # Security check raises error if user cannot access history.
+ self.security_check( trans, history, False, True)
+
+ # Get rating data.
+ user_item_rating = 0
+ if trans.get_user():
+ user_item_rating = self.get_user_item_rating( trans.sa_session, trans.get_user(), history )
+ if user_item_rating:
+ user_item_rating = user_item_rating.rating
+ else:
+ user_item_rating = 0
+ ave_item_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, history )
+
+ # create ownership flag for template, dictify models
+ user_is_owner = trans.user == history.user
+ history_data = self.mgrs.histories._get_history_data( trans, history )
+ history_dict = history_data[ 'history' ]
+ hda_dicts = history_data[ 'contents' ]
+
+ history_dict[ 'annotation' ] = self.get_item_annotation_str( trans.sa_session, history.user, history )
+ # note: adding original annotation since this is published - get_dict returns user-based annos
+ #for hda_dict in hda_dicts:
+ # hda_dict[ 'annotation' ] = hda.annotation
+ # dataset.annotation = self.get_item_annotation_str( trans.sa_session, history.user, dataset )
+
+ return trans.stream_template_mako( "history/display.mako", item=history, item_data=[],
+ user_is_owner=user_is_owner, history_dict=history_dict, hda_dicts=hda_dicts,
+ user_item_rating = user_item_rating, ave_item_rating=ave_item_rating, num_ratings=num_ratings )
+
+ # ......................................................................... sharing & publishing
+ @web.expose
+ @web.require_login( "share Galaxy histories" )
+ def sharing( self, trans, id=None, histories=[], **kwargs ):
+ """ Handle history sharing. """
+
+ # Get session and histories.
+ session = trans.sa_session
+ # Id values take precedence over histories passed in; last resort is current history.
+ if id:
+ ids = galaxy.util.listify( id )
+ if ids:
+ histories = [ self.get_history( trans, history_id ) for history_id in ids ]
+ elif not histories:
+ histories = [ trans.history ]
+
+ # Do operation on histories.
+ for history in histories:
+ if 'make_accessible_via_link' in kwargs:
+ self._make_item_accessible( trans.sa_session, history )
+ elif 'make_accessible_and_publish' in kwargs:
+ self._make_item_accessible( trans.sa_session, history )
+ history.published = True
+ elif 'publish' in kwargs:
+ if history.importable:
+ history.published = True
+ else:
+ # TODO: report error here.
+ pass
+ elif 'disable_link_access' in kwargs:
+ history.importable = False
+ elif 'unpublish' in kwargs:
+ history.published = False
+ elif 'disable_link_access_and_unpublish' in kwargs:
+ history.importable = history.published = False
+ elif 'unshare_user' in kwargs:
+ user = trans.sa_session.query( trans.app.model.User ).get( trans.security.decode_id( kwargs[ 'unshare_user' ] ) )
+ # Look for and delete sharing relation for history-user.
+ deleted_sharing_relation = False
+ husas = trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ).filter_by( user=user, history=history ).all()
+ if husas:
+ deleted_sharing_relation = True
+ for husa in husas:
+ trans.sa_session.delete( husa )
+ if not deleted_sharing_relation:
+ message = "History '%s' does not seem to be shared with user '%s'" % ( history.name, user.email )
+ return trans.fill_template( '/sharing_base.mako', item=history,
+ message=message, status='error' )
+
+
+ # Legacy issue: histories made accessible before recent updates may not have a slug. Create slug for any histories that need them.
+ for history in histories:
+ if history.importable and not history.slug:
+ self._make_item_accessible( trans.sa_session, history )
+
+ session.flush()
+
+ return trans.fill_template( "/sharing_base.mako", item=history )
+
+ @web.expose
+ @web.require_login( "share histories with other users" )
+ def share( self, trans, id=None, email="", **kwd ):
+ # If a history contains both datasets that can be shared and others that cannot be shared with the desired user,
+ # then the entire history is shared, and the protected datasets will be visible, but inaccessible ( greyed out )
+ # in the copyd history
+ params = Params( kwd )
+ user = trans.get_user()
+ # TODO: we have too many error messages floating around in here - we need
+ # to incorporate the messaging system used by the libraries that will display
+ # a message on any page.
+ err_msg = galaxy.util.restore_text( params.get( 'err_msg', '' ) )
+ if not email:
+ if not id:
+ # Default to the current history
+ id = trans.security.encode_id( trans.history.id )
+ id = galaxy.util.listify( id )
+ send_to_err = err_msg
+ histories = []
+ for history_id in id:
+ histories.append( self.get_history( trans, history_id ) )
+ return trans.fill_template( "/history/share.mako",
+ histories=histories,
+ email=email,
+ send_to_err=send_to_err )
+ histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email )
+ if not send_to_users:
+ if not send_to_err:
+ send_to_err += "%s is not a valid Galaxy user. %s" % ( email, err_msg )
+ return trans.fill_template( "/history/share.mako",
+ histories=histories,
+ email=email,
+ send_to_err=send_to_err )
+ if params.get( 'share_button', False ):
+ # The user has not yet made a choice about how to share, so dictionaries will be built for display
+ can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err = \
+ self._populate_restricted( trans, user, histories, send_to_users, None, send_to_err, unique=True )
+ send_to_err += err_msg
+ if cannot_change and not no_change_needed and not can_change:
+ send_to_err = "The histories you are sharing do not contain any datasets that can be accessed by the users with which you are sharing."
+ return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
+ if can_change or cannot_change:
+ return trans.fill_template( "/history/share.mako",
+ histories=histories,
+ email=email,
+ send_to_err=send_to_err,
+ can_change=can_change,
+ cannot_change=cannot_change,
+ no_change_needed=unique_no_change_needed )
+ if no_change_needed:
+ return self._share_histories( trans, user, send_to_err, histories=no_change_needed )
+ elif not send_to_err:
+ # User seems to be sharing an empty history
+ send_to_err = "You cannot share an empty history. "
+ return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
+
+ @web.expose
+ @web.require_login( "share restricted histories with other users" )
+ def share_restricted( self, trans, id=None, email="", **kwd ):
+ if 'action' in kwd:
+ action = kwd[ 'action' ]
+ else:
+ err_msg = "Select an action. "
+ return trans.response.send_redirect( url_for( controller='history',
+ action='share',
+ id=id,
+ email=email,
+ err_msg=err_msg,
+ share_button=True ) )
+ user = trans.get_user()
+ user_roles = user.all_roles()
+ histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email )
+ send_to_err = ''
+ # The user has made a choice, so dictionaries will be built for sharing
+ can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err = \
+ self._populate_restricted( trans, user, histories, send_to_users, action, send_to_err )
+ # Now that we've populated the can_change, cannot_change, and no_change_needed dictionaries,
+ # we'll populate the histories_for_sharing dictionary from each of them.
+ histories_for_sharing = {}
+ if no_change_needed:
+ # Don't need to change anything in cannot_change, so populate as is
+ histories_for_sharing, send_to_err = \
+ self._populate( trans, histories_for_sharing, no_change_needed, send_to_err )
+ if cannot_change:
+ # Can't change anything in cannot_change, so populate as is
+ histories_for_sharing, send_to_err = \
+ self._populate( trans, histories_for_sharing, cannot_change, send_to_err )
+ # The action here is either 'public' or 'private', so we'll continue to populate the
+ # histories_for_sharing dictionary from the can_change dictionary.
+ for send_to_user, history_dict in can_change.items():
+ for history in history_dict:
+ # Make sure the current history has not already been shared with the current send_to_user
+ if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \
+ .filter( and_( trans.app.model.HistoryUserShareAssociation.table.c.user_id == send_to_user.id,
+ trans.app.model.HistoryUserShareAssociation.table.c.history_id == history.id ) ) \
+ .count() > 0:
+ send_to_err += "History (%s) already shared with user (%s)" % ( history.name, send_to_user.email )
+ else:
+ # Only deal with datasets that have not been purged
+ for hda in history.activatable_datasets:
+ # If the current dataset is not public, we may need to perform an action on it to
+ # make it accessible by the other user.
+ if not trans.app.security_agent.can_access_dataset( send_to_user.all_roles(), hda.dataset ):
+ # The user with which we are sharing the history does not have access permission on the current dataset
+ if trans.app.security_agent.can_manage_dataset( user_roles, hda.dataset ) and not hda.dataset.library_associations:
+ # The current user has authority to change permissions on the current dataset because
+ # they have permission to manage permissions on the dataset and the dataset is not associated
+ # with a library.
+ if action == "private":
+ trans.app.security_agent.privately_share_dataset( hda.dataset, users=[ user, send_to_user ] )
+ elif action == "public":
+ trans.app.security_agent.make_dataset_public( hda.dataset )
+ # Populate histories_for_sharing with the history after performing any requested actions on
+ # its datasets to make them accessible by the other user.
+ if send_to_user not in histories_for_sharing:
+ histories_for_sharing[ send_to_user ] = [ history ]
+ elif history not in histories_for_sharing[ send_to_user ]:
+ histories_for_sharing[ send_to_user ].append( history )
+ return self._share_histories( trans, user, send_to_err, histories=histories_for_sharing )
+
+ def _get_histories_and_users( self, trans, user, id, email ):
+ if not id:
+ # Default to the current history
+ id = trans.security.encode_id( trans.history.id )
+ id = galaxy.util.listify( id )
+ send_to_err = ""
+ histories = []
+ for history_id in id:
+ histories.append( self.get_history( trans, history_id ) )
+ send_to_users = []
+ for email_address in galaxy.util.listify( email ):
+ email_address = email_address.strip()
+ if email_address:
+ if email_address == user.email:
+ send_to_err += "You cannot send histories to yourself. "
+ else:
+ send_to_user = trans.sa_session.query( trans.app.model.User ) \
+ .filter( and_( trans.app.model.User.table.c.email==email_address,
+ trans.app.model.User.table.c.deleted==False ) ) \
+ .first()
+ if send_to_user:
+ send_to_users.append( send_to_user )
+ else:
+ send_to_err += "%s is not a valid Galaxy user. " % email_address
+ return histories, send_to_users, send_to_err
+
+ def _populate( self, trans, histories_for_sharing, other, send_to_err ):
+ # This method will populate the histories_for_sharing dictionary with the users and
+ # histories in other, eliminating histories that have already been shared with the
+ # associated user. No security checking on datasets is performed.
+ # If not empty, the histories_for_sharing dictionary looks like:
+ # { userA: [ historyX, historyY ], userB: [ historyY ] }
+ # other looks like:
+ # { userA: {historyX : [hda, hda], historyY : [hda]}, userB: {historyY : [hda]} }
+ for send_to_user, history_dict in other.items():
+ for history in history_dict:
+ # Make sure the current history has not already been shared with the current send_to_user
+ if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \
+ .filter( and_( trans.app.model.HistoryUserShareAssociation.table.c.user_id == send_to_user.id,
+ trans.app.model.HistoryUserShareAssociation.table.c.history_id == history.id ) ) \
+ .count() > 0:
+ send_to_err += "History (%s) already shared with user (%s)" % ( history.name, send_to_user.email )
+ else:
+ # Build the dict that will be used for sharing
+ if send_to_user not in histories_for_sharing:
+ histories_for_sharing[ send_to_user ] = [ history ]
+ elif history not in histories_for_sharing[ send_to_user ]:
+ histories_for_sharing[ send_to_user ].append( history )
+ return histories_for_sharing, send_to_err
+
+ def _populate_restricted( self, trans, user, histories, send_to_users, action, send_to_err, unique=False ):
+ # The user may be attempting to share histories whose datasets cannot all be accessed by other users.
+ # If this is the case, the user sharing the histories can:
+ # 1) action=='public': choose to make the datasets public if he is permitted to do so
+ # 2) action=='private': automatically create a new "sharing role" allowing protected
+ # datasets to be accessed only by the desired users
+ # This method will populate the can_change, cannot_change and no_change_needed dictionaries, which
+ # are used for either displaying to the user, letting them make 1 of the choices above, or sharing
+ # after the user has made a choice. They will be used for display if 'unique' is True, and will look
+ # like: {historyX : [hda, hda], historyY : [hda] }
+ # For sharing, they will look like:
+ # { userA: {historyX : [hda, hda], historyY : [hda]}, userB: {historyY : [hda]} }
+ can_change = {}
+ cannot_change = {}
+ no_change_needed = {}
+ unique_no_change_needed = {}
+ user_roles = user.all_roles()
+ for history in histories:
+ for send_to_user in send_to_users:
+ # Make sure the current history has not already been shared with the current send_to_user
+ if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \
+ .filter( and_( trans.app.model.HistoryUserShareAssociation.table.c.user_id == send_to_user.id,
+ trans.app.model.HistoryUserShareAssociation.table.c.history_id == history.id ) ) \
+ .count() > 0:
+ send_to_err += "History (%s) already shared with user (%s)" % ( history.name, send_to_user.email )
+ else:
+ # Only deal with datasets that have not been purged
+ for hda in history.activatable_datasets:
+ if trans.app.security_agent.can_access_dataset( send_to_user.all_roles(), hda.dataset ):
+ # The no_change_needed dictionary is a special case. If both of can_change
+ # and cannot_change are empty, no_change_needed will used for sharing. Otherwise
+ # unique_no_change_needed will be used for displaying, so we need to populate both.
+ # Build the dictionaries for display, containing unique histories only
+ if history not in unique_no_change_needed:
+ unique_no_change_needed[ history ] = [ hda ]
+ else:
+ unique_no_change_needed[ history ].append( hda )
+ # Build the dictionaries for sharing
+ if send_to_user not in no_change_needed:
+ no_change_needed[ send_to_user ] = {}
+ if history not in no_change_needed[ send_to_user ]:
+ no_change_needed[ send_to_user ][ history ] = [ hda ]
+ else:
+ no_change_needed[ send_to_user ][ history ].append( hda )
+ else:
+ # The user with which we are sharing the history does not have access permission on the current dataset
+ if trans.app.security_agent.can_manage_dataset( user_roles, hda.dataset ):
+ # The current user has authority to change permissions on the current dataset because
+ # they have permission to manage permissions on the dataset.
+ # NOTE: ( gvk )There may be problems if the dataset also has an ldda, but I don't think so
+ # because the user with which we are sharing will not have the "manage permission" permission
+ # on the dataset in their history. Keep an eye on this though...
+ if unique:
+ # Build the dictionaries for display, containing unique histories only
+ if history not in can_change:
+ can_change[ history ] = [ hda ]
+ else:
+ can_change[ history ].append( hda )
+ else:
+ # Build the dictionaries for sharing
+ if send_to_user not in can_change:
+ can_change[ send_to_user ] = {}
+ if history not in can_change[ send_to_user ]:
+ can_change[ send_to_user ][ history ] = [ hda ]
+ else:
+ can_change[ send_to_user ][ history ].append( hda )
+ else:
+ if action in [ "private", "public" ]:
+ # The user has made a choice, so 'unique' doesn't apply. Don't change stuff
+ # that the user doesn't have permission to change
+ continue
+ if unique:
+ # Build the dictionaries for display, containing unique histories only
+ if history not in cannot_change:
+ cannot_change[ history ] = [ hda ]
+ else:
+ cannot_change[ history ].append( hda )
+ else:
+ # Build the dictionaries for sharing
+ if send_to_user not in cannot_change:
+ cannot_change[ send_to_user ] = {}
+ if history not in cannot_change[ send_to_user ]:
+ cannot_change[ send_to_user ][ history ] = [ hda ]
+ else:
+ cannot_change[ send_to_user ][ history ].append( hda )
+ return can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err
+
+ def _share_histories( self, trans, user, send_to_err, histories=None ):
+ # histories looks like: { userA: [ historyX, historyY ], userB: [ historyY ] }
+ histories = histories or {}
+ msg = ""
+ sent_to_emails = []
+ for send_to_user in histories.keys():
+ sent_to_emails.append( send_to_user.email )
+ emails = ",".join( e for e in sent_to_emails )
+ if not histories:
+ send_to_err += "No users have been specified or no histories can be sent without changing permissions or associating a sharing role. "
+ else:
+ for send_to_user, send_to_user_histories in histories.items():
+ shared_histories = []
+ for history in send_to_user_histories:
+ share = trans.app.model.HistoryUserShareAssociation()
+ share.history = history
+ share.user = send_to_user
+ trans.sa_session.add( share )
+ self.create_item_slug( trans.sa_session, history )
+ trans.sa_session.flush()
+ if history not in shared_histories:
+ shared_histories.append( history )
+ if send_to_err:
+ msg += send_to_err
+ return self.sharing( trans, histories=shared_histories, msg=msg )
+
+ # ......................................................................... actions/orig. async
+ @web.expose
def delete_hidden_datasets( self, trans ):
"""
This method deletes all hidden datasets in the current history.
@@ -839,451 +1277,6 @@
#TODO: used in history/view, display, embed
@web.expose
- def view( self, trans, id=None, show_deleted=False, show_hidden=False, use_panels=True ):
- """
- View a history. If a history is importable, then it is viewable by any user.
- """
- # Get history to view.
- if not id:
- return trans.show_error_message( "You must specify a history you want to view." )
-
- show_deleted = galaxy.util.string_as_bool( show_deleted )
- show_hidden = galaxy.util.string_as_bool( show_hidden )
- use_panels = galaxy.util.string_as_bool( use_panels )
-
- history_dictionary = {}
- hda_dictionaries = []
- user_is_owner = False
- try:
- history_to_view = self.get_history( trans, id, check_ownership=False, check_accessible=False )
- if not history_to_view:
- return trans.show_error_message( "The specified history does not exist." )
- if history_to_view.user == trans.user:
- user_is_owner = True
-
- if( ( history_to_view.user != trans.user )
- # Admin users can view any history
- and ( not trans.user_is_admin() )
- and ( not history_to_view.importable )
- and ( trans.user not in history_to_view.users_shared_with_dot_users ) ):
- return trans.show_error_message( "Either you are not allowed to view this history"
- + " or the owner of this history has not made it accessible." )
-
- # include all datasets: hidden, deleted, and purged
- hdas = self.get_history_datasets( trans, history_to_view,
- show_deleted=True, show_hidden=True, show_purged=True )
- for hda in hdas:
- hda_dict = {}
- try:
- hda_dict = self.get_hda_dict( trans, hda )
-
- except Exception, exc:
- # don't fail entire list if hda err's, record and move on
- log.error( 'Error bootstrapping hda %d: %s', hda.id, str( exc ), exc_info=True )
- hda_dict = self.get_hda_dict_with_error( trans, hda, str( exc ) )
-
- hda_dictionaries.append( hda_dict )
-
- # re-use the hdas above to get the history data...
- history_dictionary = self.get_history_dict( trans, history_to_view, hda_dictionaries=hda_dictionaries )
-
- except Exception, exc:
- user_id = str( trans.user.id ) if trans.user else '(anonymous)'
- log.exception( 'Error bootstrapping history for user %s: %s', user_id, str( exc ) )
- history_dictionary[ 'error' ] = ( 'An error occurred getting the history data from the server. '
- + 'Please contact a Galaxy administrator if the problem persists.' )
-
- return trans.fill_template_mako( "history/view.mako",
- history=history_dictionary, hdas=hda_dictionaries, user_is_owner=user_is_owner,
- show_deleted=show_deleted, show_hidden=show_hidden, use_panels=use_panels )
-
- @web.expose
- def display_by_username_and_slug( self, trans, username, slug ):
- """
- Display history based on a username and slug.
- """
- # Get history.
- session = trans.sa_session
- user = session.query( model.User ).filter_by( username=username ).first()
- history = trans.sa_session.query( model.History ).filter_by( user=user, slug=slug, deleted=False ).first()
- if history is None:
- raise web.httpexceptions.HTTPNotFound()
- # Security check raises error if user cannot access history.
- self.security_check( trans, history, False, True)
-
- # Get datasets and annotations
- datasets = self.get_history_datasets( trans, history )
- history.annotation = self.get_item_annotation_str( trans.sa_session, history.user, history )
- for dataset in datasets:
- dataset.annotation = self.get_item_annotation_str( trans.sa_session, history.user, dataset )
-
- # Get rating data.
- user_item_rating = 0
- if trans.get_user():
- user_item_rating = self.get_user_item_rating( trans.sa_session, trans.get_user(), history )
- if user_item_rating:
- user_item_rating = user_item_rating.rating
- else:
- user_item_rating = 0
- ave_item_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, history )
-
- # create ownership flag for template, dictify models
- # note: adding original annotation since this is published - get_dict returns user-based annos
- user_is_owner = trans.user == history.user
- hda_dicts = []
- for hda in datasets:
- hda_dict = self.get_hda_dict( trans, hda )
- hda_dict[ 'annotation' ] = hda.annotation
- hda_dicts.append( hda_dict )
- history_dict = self.get_history_dict( trans, history, hda_dictionaries=hda_dicts )
- history_dict[ 'annotation' ] = history.annotation
-
- return trans.stream_template_mako( "history/display.mako", item=history, item_data=datasets,
- user_is_owner=user_is_owner, history_dict=history_dict, hda_dicts=hda_dicts,
- user_item_rating = user_item_rating, ave_item_rating=ave_item_rating, num_ratings=num_ratings )
-
- @web.expose
- @web.require_login( "share Galaxy histories" )
- def sharing( self, trans, id=None, histories=[], **kwargs ):
- """ Handle history sharing. """
-
- # Get session and histories.
- session = trans.sa_session
- # Id values take precedence over histories passed in; last resort is current history.
- if id:
- ids = galaxy.util.listify( id )
- if ids:
- histories = [ self.get_history( trans, history_id ) for history_id in ids ]
- elif not histories:
- histories = [ trans.history ]
-
- # Do operation on histories.
- for history in histories:
- if 'make_accessible_via_link' in kwargs:
- self._make_item_accessible( trans.sa_session, history )
- elif 'make_accessible_and_publish' in kwargs:
- self._make_item_accessible( trans.sa_session, history )
- history.published = True
- elif 'publish' in kwargs:
- if history.importable:
- history.published = True
- else:
- # TODO: report error here.
- pass
- elif 'disable_link_access' in kwargs:
- history.importable = False
- elif 'unpublish' in kwargs:
- history.published = False
- elif 'disable_link_access_and_unpublish' in kwargs:
- history.importable = history.published = False
- elif 'unshare_user' in kwargs:
- user = trans.sa_session.query( trans.app.model.User ).get( trans.security.decode_id( kwargs[ 'unshare_user' ] ) )
- # Look for and delete sharing relation for history-user.
- deleted_sharing_relation = False
- husas = trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ).filter_by( user=user, history=history ).all()
- if husas:
- deleted_sharing_relation = True
- for husa in husas:
- trans.sa_session.delete( husa )
- if not deleted_sharing_relation:
- message = "History '%s' does not seem to be shared with user '%s'" % ( history.name, user.email )
- return trans.fill_template( '/sharing_base.mako', item=history,
- message=message, status='error' )
-
-
- # Legacy issue: histories made accessible before recent updates may not have a slug. Create slug for any histories that need them.
- for history in histories:
- if history.importable and not history.slug:
- self._make_item_accessible( trans.sa_session, history )
-
- session.flush()
-
- return trans.fill_template( "/sharing_base.mako", item=history )
-
- @web.expose
- @web.require_login( "share histories with other users" )
- def share( self, trans, id=None, email="", **kwd ):
- # If a history contains both datasets that can be shared and others that cannot be shared with the desired user,
- # then the entire history is shared, and the protected datasets will be visible, but inaccessible ( greyed out )
- # in the copyd history
- params = Params( kwd )
- user = trans.get_user()
- # TODO: we have too many error messages floating around in here - we need
- # to incorporate the messaging system used by the libraries that will display
- # a message on any page.
- err_msg = galaxy.util.restore_text( params.get( 'err_msg', '' ) )
- if not email:
- if not id:
- # Default to the current history
- id = trans.security.encode_id( trans.history.id )
- id = galaxy.util.listify( id )
- send_to_err = err_msg
- histories = []
- for history_id in id:
- histories.append( self.get_history( trans, history_id ) )
- return trans.fill_template( "/history/share.mako",
- histories=histories,
- email=email,
- send_to_err=send_to_err )
- histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email )
- if not send_to_users:
- if not send_to_err:
- send_to_err += "%s is not a valid Galaxy user. %s" % ( email, err_msg )
- return trans.fill_template( "/history/share.mako",
- histories=histories,
- email=email,
- send_to_err=send_to_err )
- if params.get( 'share_button', False ):
- # The user has not yet made a choice about how to share, so dictionaries will be built for display
- can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err = \
- self._populate_restricted( trans, user, histories, send_to_users, None, send_to_err, unique=True )
- send_to_err += err_msg
- if cannot_change and not no_change_needed and not can_change:
- send_to_err = "The histories you are sharing do not contain any datasets that can be accessed by the users with which you are sharing."
- return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
- if can_change or cannot_change:
- return trans.fill_template( "/history/share.mako",
- histories=histories,
- email=email,
- send_to_err=send_to_err,
- can_change=can_change,
- cannot_change=cannot_change,
- no_change_needed=unique_no_change_needed )
- if no_change_needed:
- return self._share_histories( trans, user, send_to_err, histories=no_change_needed )
- elif not send_to_err:
- # User seems to be sharing an empty history
- send_to_err = "You cannot share an empty history. "
- return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
-
- @web.expose
- @web.require_login( "share restricted histories with other users" )
- def share_restricted( self, trans, id=None, email="", **kwd ):
- if 'action' in kwd:
- action = kwd[ 'action' ]
- else:
- err_msg = "Select an action. "
- return trans.response.send_redirect( url_for( controller='history',
- action='share',
- id=id,
- email=email,
- err_msg=err_msg,
- share_button=True ) )
- user = trans.get_user()
- user_roles = user.all_roles()
- histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email )
- send_to_err = ''
- # The user has made a choice, so dictionaries will be built for sharing
- can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err = \
- self._populate_restricted( trans, user, histories, send_to_users, action, send_to_err )
- # Now that we've populated the can_change, cannot_change, and no_change_needed dictionaries,
- # we'll populate the histories_for_sharing dictionary from each of them.
- histories_for_sharing = {}
- if no_change_needed:
- # Don't need to change anything in cannot_change, so populate as is
- histories_for_sharing, send_to_err = \
- self._populate( trans, histories_for_sharing, no_change_needed, send_to_err )
- if cannot_change:
- # Can't change anything in cannot_change, so populate as is
- histories_for_sharing, send_to_err = \
- self._populate( trans, histories_for_sharing, cannot_change, send_to_err )
- # The action here is either 'public' or 'private', so we'll continue to populate the
- # histories_for_sharing dictionary from the can_change dictionary.
- for send_to_user, history_dict in can_change.items():
- for history in history_dict:
- # Make sure the current history has not already been shared with the current send_to_user
- if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \
- .filter( and_( trans.app.model.HistoryUserShareAssociation.table.c.user_id == send_to_user.id,
- trans.app.model.HistoryUserShareAssociation.table.c.history_id == history.id ) ) \
- .count() > 0:
- send_to_err += "History (%s) already shared with user (%s)" % ( history.name, send_to_user.email )
- else:
- # Only deal with datasets that have not been purged
- for hda in history.activatable_datasets:
- # If the current dataset is not public, we may need to perform an action on it to
- # make it accessible by the other user.
- if not trans.app.security_agent.can_access_dataset( send_to_user.all_roles(), hda.dataset ):
- # The user with which we are sharing the history does not have access permission on the current dataset
- if trans.app.security_agent.can_manage_dataset( user_roles, hda.dataset ) and not hda.dataset.library_associations:
- # The current user has authority to change permissions on the current dataset because
- # they have permission to manage permissions on the dataset and the dataset is not associated
- # with a library.
- if action == "private":
- trans.app.security_agent.privately_share_dataset( hda.dataset, users=[ user, send_to_user ] )
- elif action == "public":
- trans.app.security_agent.make_dataset_public( hda.dataset )
- # Populate histories_for_sharing with the history after performing any requested actions on
- # its datasets to make them accessible by the other user.
- if send_to_user not in histories_for_sharing:
- histories_for_sharing[ send_to_user ] = [ history ]
- elif history not in histories_for_sharing[ send_to_user ]:
- histories_for_sharing[ send_to_user ].append( history )
- return self._share_histories( trans, user, send_to_err, histories=histories_for_sharing )
-
- def _get_histories_and_users( self, trans, user, id, email ):
- if not id:
- # Default to the current history
- id = trans.security.encode_id( trans.history.id )
- id = galaxy.util.listify( id )
- send_to_err = ""
- histories = []
- for history_id in id:
- histories.append( self.get_history( trans, history_id ) )
- send_to_users = []
- for email_address in galaxy.util.listify( email ):
- email_address = email_address.strip()
- if email_address:
- if email_address == user.email:
- send_to_err += "You cannot send histories to yourself. "
- else:
- send_to_user = trans.sa_session.query( trans.app.model.User ) \
- .filter( and_( trans.app.model.User.table.c.email==email_address,
- trans.app.model.User.table.c.deleted==False ) ) \
- .first()
- if send_to_user:
- send_to_users.append( send_to_user )
- else:
- send_to_err += "%s is not a valid Galaxy user. " % email_address
- return histories, send_to_users, send_to_err
-
- def _populate( self, trans, histories_for_sharing, other, send_to_err ):
- # This method will populate the histories_for_sharing dictionary with the users and
- # histories in other, eliminating histories that have already been shared with the
- # associated user. No security checking on datasets is performed.
- # If not empty, the histories_for_sharing dictionary looks like:
- # { userA: [ historyX, historyY ], userB: [ historyY ] }
- # other looks like:
- # { userA: {historyX : [hda, hda], historyY : [hda]}, userB: {historyY : [hda]} }
- for send_to_user, history_dict in other.items():
- for history in history_dict:
- # Make sure the current history has not already been shared with the current send_to_user
- if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \
- .filter( and_( trans.app.model.HistoryUserShareAssociation.table.c.user_id == send_to_user.id,
- trans.app.model.HistoryUserShareAssociation.table.c.history_id == history.id ) ) \
- .count() > 0:
- send_to_err += "History (%s) already shared with user (%s)" % ( history.name, send_to_user.email )
- else:
- # Build the dict that will be used for sharing
- if send_to_user not in histories_for_sharing:
- histories_for_sharing[ send_to_user ] = [ history ]
- elif history not in histories_for_sharing[ send_to_user ]:
- histories_for_sharing[ send_to_user ].append( history )
- return histories_for_sharing, send_to_err
-
- def _populate_restricted( self, trans, user, histories, send_to_users, action, send_to_err, unique=False ):
- # The user may be attempting to share histories whose datasets cannot all be accessed by other users.
- # If this is the case, the user sharing the histories can:
- # 1) action=='public': choose to make the datasets public if he is permitted to do so
- # 2) action=='private': automatically create a new "sharing role" allowing protected
- # datasets to be accessed only by the desired users
- # This method will populate the can_change, cannot_change and no_change_needed dictionaries, which
- # are used for either displaying to the user, letting them make 1 of the choices above, or sharing
- # after the user has made a choice. They will be used for display if 'unique' is True, and will look
- # like: {historyX : [hda, hda], historyY : [hda] }
- # For sharing, they will look like:
- # { userA: {historyX : [hda, hda], historyY : [hda]}, userB: {historyY : [hda]} }
- can_change = {}
- cannot_change = {}
- no_change_needed = {}
- unique_no_change_needed = {}
- user_roles = user.all_roles()
- for history in histories:
- for send_to_user in send_to_users:
- # Make sure the current history has not already been shared with the current send_to_user
- if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \
- .filter( and_( trans.app.model.HistoryUserShareAssociation.table.c.user_id == send_to_user.id,
- trans.app.model.HistoryUserShareAssociation.table.c.history_id == history.id ) ) \
- .count() > 0:
- send_to_err += "History (%s) already shared with user (%s)" % ( history.name, send_to_user.email )
- else:
- # Only deal with datasets that have not been purged
- for hda in history.activatable_datasets:
- if trans.app.security_agent.can_access_dataset( send_to_user.all_roles(), hda.dataset ):
- # The no_change_needed dictionary is a special case. If both of can_change
- # and cannot_change are empty, no_change_needed will used for sharing. Otherwise
- # unique_no_change_needed will be used for displaying, so we need to populate both.
- # Build the dictionaries for display, containing unique histories only
- if history not in unique_no_change_needed:
- unique_no_change_needed[ history ] = [ hda ]
- else:
- unique_no_change_needed[ history ].append( hda )
- # Build the dictionaries for sharing
- if send_to_user not in no_change_needed:
- no_change_needed[ send_to_user ] = {}
- if history not in no_change_needed[ send_to_user ]:
- no_change_needed[ send_to_user ][ history ] = [ hda ]
- else:
- no_change_needed[ send_to_user ][ history ].append( hda )
- else:
- # The user with which we are sharing the history does not have access permission on the current dataset
- if trans.app.security_agent.can_manage_dataset( user_roles, hda.dataset ):
- # The current user has authority to change permissions on the current dataset because
- # they have permission to manage permissions on the dataset.
- # NOTE: ( gvk )There may be problems if the dataset also has an ldda, but I don't think so
- # because the user with which we are sharing will not have the "manage permission" permission
- # on the dataset in their history. Keep an eye on this though...
- if unique:
- # Build the dictionaries for display, containing unique histories only
- if history not in can_change:
- can_change[ history ] = [ hda ]
- else:
- can_change[ history ].append( hda )
- else:
- # Build the dictionaries for sharing
- if send_to_user not in can_change:
- can_change[ send_to_user ] = {}
- if history not in can_change[ send_to_user ]:
- can_change[ send_to_user ][ history ] = [ hda ]
- else:
- can_change[ send_to_user ][ history ].append( hda )
- else:
- if action in [ "private", "public" ]:
- # The user has made a choice, so 'unique' doesn't apply. Don't change stuff
- # that the user doesn't have permission to change
- continue
- if unique:
- # Build the dictionaries for display, containing unique histories only
- if history not in cannot_change:
- cannot_change[ history ] = [ hda ]
- else:
- cannot_change[ history ].append( hda )
- else:
- # Build the dictionaries for sharing
- if send_to_user not in cannot_change:
- cannot_change[ send_to_user ] = {}
- if history not in cannot_change[ send_to_user ]:
- cannot_change[ send_to_user ][ history ] = [ hda ]
- else:
- cannot_change[ send_to_user ][ history ].append( hda )
- return can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err
-
- def _share_histories( self, trans, user, send_to_err, histories=None ):
- # histories looks like: { userA: [ historyX, historyY ], userB: [ historyY ] }
- histories = histories or {}
- msg = ""
- sent_to_emails = []
- for send_to_user in histories.keys():
- sent_to_emails.append( send_to_user.email )
- emails = ",".join( e for e in sent_to_emails )
- if not histories:
- send_to_err += "No users have been specified or no histories can be sent without changing permissions or associating a sharing role. "
- else:
- for send_to_user, send_to_user_histories in histories.items():
- shared_histories = []
- for history in send_to_user_histories:
- share = trans.app.model.HistoryUserShareAssociation()
- share.history = history
- share.user = send_to_user
- trans.sa_session.add( share )
- self.create_item_slug( trans.sa_session, history )
- trans.sa_session.flush()
- if history not in shared_histories:
- shared_histories.append( history )
- if send_to_err:
- msg += send_to_err
- return self.sharing( trans, histories=shared_histories, msg=msg )
-
- @web.expose
@web.require_login( "rename histories" )
def rename( self, trans, id=None, name=None, **kwd ):
user = trans.get_user()
@@ -1361,7 +1354,8 @@
name += " (active items only)"
new_history = history.copy( name=name, target_user=user )
if len( histories ) == 1:
- msg = 'New history "<a href="%s" target="_top">%s</a>" has been created.' % ( url_for( controller="history", action="switch_to_history", hist_id=trans.security.encode_id( new_history.id ) ) , new_history.name )
+ switch_url = url_for( controller="history", action="switch_to_history", hist_id=trans.security.encode_id( new_history.id ) )
+ msg = 'New history "<a href="%s" target="_top">%s</a>" has been created.' % ( switch_url, new_history.name )
else:
msg = 'Copied and created %d new histories.' % len( histories )
return trans.show_ok_message( msg )
diff -r 1861cbcc610da6e519c7760e1c829ec3b66887f4 -r 65ec063664eb0ce100d76d985fba93f200ac5178 lib/galaxy/webapps/galaxy/controllers/root.py
--- a/lib/galaxy/webapps/galaxy/controllers/root.py
+++ b/lib/galaxy/webapps/galaxy/controllers/root.py
@@ -7,20 +7,28 @@
from paste.httpexceptions import HTTPNotFound, HTTPBadGateway
+from galaxy.web.base.controller import BaseUIController, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin
+from galaxy import managers
+
from galaxy import web
from galaxy.web import url_for
from galaxy.model.item_attrs import UsesAnnotations
+from galaxy import util
from galaxy.util import listify, Params, string_as_bool, string_as_bool_or_none
-from galaxy.web.base.controller import BaseUIController, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin
from galaxy.util.json import to_json_string
-from galaxy.util.debugging import SimpleProfiler
-
import logging
log = logging.getLogger( __name__ )
class RootController( BaseUIController, UsesHistoryMixin, UsesHistoryDatasetAssociationMixin, UsesAnnotations ):
- """Controller class that maps to the url root of Galaxy (i.e. '/')."""
+ """
+ Controller class that maps to the url root of Galaxy (i.e. '/').
+ """
+ def __init__( self, app ):
+ super( RootController, self ).__init__( app )
+ self.mgrs = util.bunch.Bunch(
+ histories=managers.histories.HistoryManager()
+ )
@web.expose
def default(self, trans, target1=None, target2=None, **kwd):
@@ -100,42 +108,6 @@
show_deleted=string_as_bool( show_deleted ),
show_hidden=string_as_bool( show_hidden ) )
- def _get_current_history_data( self, trans ):
- history_dictionary = {}
- hda_dictionaries = []
-
- try:
- history = trans.get_history( create=True )
- hdas = self.get_history_datasets( trans, history,
- show_deleted=True, show_hidden=True, show_purged=True )
-
- for hda in hdas:
- hda_dict = {}
- try:
- hda_dict = self.get_hda_dict( trans, hda )
-
- except Exception, exc:
- # don't fail entire list if hda err's, record and move on
- log.error( 'Error bootstrapping hda %d: %s', hda.id, str( exc ), exc_info=True )
- hda_dict = self.get_hda_dict_with_error( trans, hda, str( exc ) )
-
- hda_dictionaries.append( hda_dict )
-
- # re-use the hdas above to get the history data...
- history_dictionary = self.get_history_dict( trans, history, hda_dictionaries=hda_dictionaries )
-
- except Exception, exc:
- user_id = str( trans.user.id ) if trans.user else '(anonymous)'
- log.exception( 'Error bootstrapping history for user %s: %s', user_id, str( exc ) )
- message = ( 'An error occurred getting the history data from the server. '
- + 'Please contact a Galaxy administrator if the problem persists.' )
- history_dictionary[ 'error' ] = message
-
- return {
- 'history' : history_dictionary,
- 'hdas' : hda_dictionaries
- }
-
@web.expose
def history( self, trans, as_xml=False, show_deleted=None, show_hidden=None, **kwd ):
"""
@@ -154,11 +126,11 @@
show_hidden = string_as_bool_or_none( show_hidden )
history_dictionary = {}
- hda_dictionaries = []
+ hda_dictionaries = []
try:
- history_data = self._get_current_history_data( trans )
+ history_data = self.mgrs.histories._get_history_data( trans, trans.get_history( create=True ) )
history_dictionary = history_data[ 'history' ]
- hda_dictionaries = history_data[ 'hdas' ]
+ hda_dictionaries = history_data[ 'contents' ]
except Exception, exc:
user_id = str( trans.user.id ) if trans.user else '(anonymous)'
@@ -170,6 +142,7 @@
history = history_dictionary, hdas = hda_dictionaries,
show_deleted=show_deleted, show_hidden=show_hidden )
+
## ---- Dataset display / editing ----------------------------------------
@web.expose
def display( self, trans, id=None, hid=None, tofile=None, toext=".txt", encoded_id=None, **kwd ):
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: jmchilton: Fix bug related to undefined attribute error in workflow editor.
by commits-noreply@bitbucket.org 09 Jul '14
by commits-noreply@bitbucket.org 09 Jul '14
09 Jul '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/1861cbcc610d/
Changeset: 1861cbcc610d
User: jmchilton
Date: 2014-07-10 00:39:00
Summary: Fix bug related to undefined attribute error in workflow editor.
Affected #: 1 file
diff -r 5e74f91d068bedde58cf4ec9d8299bd05fdff697 -r 1861cbcc610da6e519c7760e1c829ec3b66887f4 static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -306,7 +306,7 @@
inputFilled = false;
} else {
var firstOutput = this.connectors[ 0 ].handle1;
- if( firstOutput === null ){
+ if( ! firstOutput ){
inputFilled = false;
} else {
if( firstOutput.isDataCollectionInput || firstOutput.isMappedOver() || firstOutput.datatypes.indexOf( "input_collection" ) > 0 ) {
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: jgoecks: Remove debugging statement.
by commits-noreply@bitbucket.org 09 Jul '14
by commits-noreply@bitbucket.org 09 Jul '14
09 Jul '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/5e74f91d068b/
Changeset: 5e74f91d068b
User: jgoecks
Date: 2014-07-09 22:36:47
Summary: Remove debugging statement.
Affected #: 1 file
diff -r 64d51b16a55173bfec78dc22ee802d3e085dd4fc -r 5e74f91d068bedde58cf4ec9d8299bd05fdff697 static/scripts/mvc/ui/ui-frames.js
--- a/static/scripts/mvc/ui/ui-frames.js
+++ b/static/scripts/mvc/ui/ui-frames.js
@@ -196,7 +196,6 @@
// Load content into frame.
var content_elt = $frame_el.find('.f-content');
- console.log(content_elt);
if (_.isFunction(options.content)) {
options.content(content_elt);
}
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 test_history_functions test 80, copying history items.
by commits-noreply@bitbucket.org 09 Jul '14
by commits-noreply@bitbucket.org 09 Jul '14
09 Jul '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/64d51b16a551/
Changeset: 64d51b16a551
User: davebgx
Date: 2014-07-09 22:11:50
Summary: Fix test_history_functions test 80, copying history items.
Affected #: 2 files
diff -r 50a273de6cd76b770eb78861fee2b0a97621fa3b -r 64d51b16a55173bfec78dc22ee802d3e085dd4fc test/base/twilltestcase.py
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -506,13 +506,13 @@
self.check_page_for_string( 'The file conversion of Convert BED to GFF on data' )
self.wait() # wait for the format convert tool to finish before returning
- def copy_history_item( self, source_dataset_id=None, target_history_id=None, all_target_history_ids=[],
+ def copy_history_item( self, source_history_id=None, source_dataset_id=None, target_history_id=None, all_target_history_ids=[],
deleted_history_ids=[] ):
"""
Copy 1 history_dataset_association to 1 history (Limited by twill since it doesn't support multiple
field names, such as checkboxes
"""
- self.visit_url( "%s/dataset/copy_datasets?source_dataset_ids=%s" % ( self.url, source_dataset_id ) )
+ self.visit_url( "/dataset/copy_datasets" )
self.check_page_for_string( 'Source History:' )
# Make sure all of users active histories are displayed
for id in all_target_history_ids:
@@ -524,9 +524,11 @@
raise AssertionError( "deleted history id %d displayed in list of target histories" % id )
except:
pass
-
- tc.fv( '1', 'target_history_id', target_history_id )
- tc.submit( 'do_copy' )
+ form_values = [ ( 'source_history', source_history_id ),
+ ( 'target_history_id', target_history_id ),
+ ( 'source_content_ids', 'dataset|%s' % source_dataset_id ),
+ ( 'do_copy', True ) ]
+ self.visit_url( "/dataset/copy_datasets", params=form_values )
check_str = '1 dataset copied to 1 history'
self.check_page_for_string( check_str )
diff -r 50a273de6cd76b770eb78861fee2b0a97621fa3b -r 64d51b16a55173bfec78dc22ee802d3e085dd4fc test/functional/test_history_functions.py
--- a/test/functional/test_history_functions.py
+++ b/test/functional/test_history_functions.py
@@ -831,7 +831,8 @@
deleted_history_ids = [ self.security.encode_id( history1.id ), self.security.encode_id( history2.id ) ]
# Test copying to the current history
target_history_id = self.security.encode_id( history6.id )
- self.copy_history_item( source_dataset_id=source_dataset_ids,
+ self.copy_history_item( source_history_id=self.security.encode_id( history6.id ),
+ source_dataset_id=source_dataset_ids,
target_history_id=target_history_id,
all_target_history_ids=all_target_history_ids,
deleted_history_ids=deleted_history_ids )
@@ -852,7 +853,8 @@
all_target_history_ids = [ self.security.encode_id( hda.id ) for hda in admin_user.active_histories ]
# Test copying to the a history that is not the current history
self.security.encode_id( history7.id )
- self.copy_history_item( source_dataset_id=source_dataset_ids,
+ self.copy_history_item( source_history_id=self.security.encode_id( history6.id ),
+ source_dataset_id=source_dataset_ids,
target_history_id=target_history_id,
all_target_history_ids=all_target_history_ids,
deleted_history_ids=deleted_history_ids )
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: carlfeberhard: History panel: refactoring for dataset collections
by commits-noreply@bitbucket.org 09 Jul '14
by commits-noreply@bitbucket.org 09 Jul '14
09 Jul '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/50a273de6cd7/
Changeset: 50a273de6cd7
User: carlfeberhard
Date: 2014-07-09 19:40:43
Summary: History panel: refactoring for dataset collections
Affected #: 17 files
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/mvc/collection/hdca-model.js
--- /dev/null
+++ b/static/scripts/mvc/collection/hdca-model.js
@@ -0,0 +1,69 @@
+define([
+ "mvc/history/history-content-base",
+ "utils/localization"
+], function( historyContent, _l ){
+//==============================================================================
+var HistoryDatasetCollectionAssociation = historyContent.HistoryContent.extend(
+/** @lends HistoryDatasetCollectionAssociation.prototype */{
+ /** default attributes for a model */
+ defaults : {
+ // parent (containing) history
+ history_id : null,
+ // often used with tagging
+ model_class : 'HistoryDatasetCollectionAssociation',
+ history_content_type : 'dataset_collection',
+ hid : 0,
+
+ id : null,
+ name : '(unnamed dataset collection)',
+ // one of HistoryDatasetAssociation.STATES, calling them all 'ok' for now.
+ state : 'ok',
+
+ accessible : true,
+ deleted : false,
+ visible : true,
+
+ purged : false, // Purged doesn't make sense for collections - at least right now.
+
+ tags : [],
+ annotation : ''
+ },
+ urls : function(){
+ },
+
+ inReadyState : function(){
+ return true; // TODO
+ },
+
+ // ........................................................................ search
+ /** what attributes of an collection will be used in a text search */
+ searchAttributes : [
+ 'name'
+ ],
+
+ /** our attr keys don't often match the labels we display to the user - so, when using
+ * attribute specifiers ('name="bler"') in a term, allow passing in aliases for the
+ * following attr keys.
+ */
+ searchAliases : {
+ title : 'name'
+ // TODO: Add tag...
+ },
+
+ // ........................................................................ misc
+ /** String representation */
+ toString : function(){
+ var nameAndId = this.get( 'id' ) || '';
+ if( this.get( 'name' ) ){
+ nameAndId = this.get( 'hid' ) + ' :"' + this.get( 'name' ) + '",' + nameAndId;
+ }
+ return 'HDCA-' + this.get( 'collection_type' ) + '(' + nameAndId + ')';
+ }
+});
+
+
+//==============================================================================
+ return {
+ HistoryDatasetCollectionAssociation : HistoryDatasetCollectionAssociation
+ };
+});
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/mvc/dataset/hda-model.js
--- a/static/scripts/mvc/dataset/hda-model.js
+++ b/static/scripts/mvc/dataset/hda-model.js
@@ -1,169 +1,7 @@
define([
- "mvc/base-mvc",
+ "mvc/history/history-content-base",
"utils/localization"
-], function( baseMVC, _l ){
-//==============================================================================
-/** @class model for contents related to a history.
- * @name HistoryContent
- *
- * @augments Backbone.Model
- * @borrows LoggableMixin#logger as #logger
- * @borrows LoggableMixin#log as #log
- * @constructs
- */
-var HistoryContent = Backbone.Model.extend( baseMVC.LoggableMixin ).extend( {
- idAttribute : 'type_id',
-
- /** fetch location of this HDA's history in the api */
- urlRoot: galaxy_config.root + 'api/histories/',
-
- constructor : function( attrs, options ){
- attrs.type_id = HistoryContent.typeIdStr( attrs.history_content_type, attrs.id );
- Backbone.Model.apply( this, arguments );
- },
-
- initialize : function( attrs, options ){
- // assumes type won't change
- this.on( 'change:id', this._createTypeId );
- //TODO: not sure this covers all the bases...
- },
-
- /** full url spec. for this HDA */
- url : function(){
- return this.urlRoot + this.get( 'history_id' ) + '/contents/' + this.get('history_content_type') + 's/' + this.get( 'id' );
- },
-
- /** the more common alias of visible */
- hidden : function(){
- return !this.get( 'visible' );
- },
-
- // ........................................................................ ajax
-
- /** save this HDA, _Mark_ing it as deleted (just a flag) */
- 'delete' : function _delete( options ){
- if( this.get( 'deleted' ) ){ return jQuery.when(); }
- return this.save( { deleted: true }, options );
- },
- /** save this HDA, _Mark_ing it as undeleted */
- undelete : function _undelete( options ){
- if( !this.get( 'deleted' ) || this.get( 'purged' ) ){ return jQuery.when(); }
- return this.save( { deleted: false }, options );
- },
-
- /** save this HDA as not visible */
- hide : function _hide( options ){
- if( !this.get( 'visible' ) ){ return jQuery.when(); }
- return this.save( { visible: false }, options );
- },
- /** save this HDA as visible */
- unhide : function _uhide( options ){
- if( this.get( 'visible' ) ){ return jQuery.when(); }
- return this.save( { visible: true }, options );
- },
-
- /** based on show_deleted, show_hidden (gen. from the container control),
- * would this ds show in the list of ds's?
- * @param {Boolean} show_deleted are we showing deleted hdas?
- * @param {Boolean} show_hidden are we showing hidden hdas?
- */
- isVisible : function( show_deleted, show_hidden ){
- var isVisible = true;
- if( ( !show_deleted )
- && ( this.get( 'deleted' ) || this.get( 'purged' ) ) ){
- isVisible = false;
- }
- if( ( !show_hidden )
- && ( !this.get( 'visible' ) ) ){
- isVisible = false;
- }
- return isVisible;
- },
-
- /** search the attribute with key attrKey for the string searchFor; T/F if found */
- searchAttribute : function( attrKey, searchFor ){
- var attrVal = this.get( attrKey );
- //console.debug( 'searchAttribute', attrKey, attrVal, searchFor );
- // bail if empty searchFor or unsearchable values
- if( !searchFor
- || ( attrVal === undefined || attrVal === null ) ){
- return false;
- }
- // pass to sep. fn for deep search of array attributes
- if( _.isArray( attrVal ) ){ return this._searchArrayAttribute( attrVal, searchFor ); }
- return ( attrVal.toString().toLowerCase().indexOf( searchFor.toLowerCase() ) !== -1 );
- },
-
- /** deep(er) search for array attributes; T/F if found */
- _searchArrayAttribute : function( array, searchFor ){
- //console.debug( '_searchArrayAttribute', array, searchFor );
- searchFor = searchFor.toLowerCase();
- //precondition: searchFor has already been validated as non-empty string
- //precondition: assumes only 1 level array
- //TODO: could possibly break up searchFor more (CSV...)
- return _.any( array, function( elem ){
- return ( elem.toString().toLowerCase().indexOf( searchFor.toLowerCase() ) !== -1 );
- });
- },
-
- /** search all searchAttributes for the string searchFor,
- * returning a list of keys of attributes that contain searchFor
- */
- search : function( searchFor ){
- var model = this;
- return _.filter( this.searchAttributes, function( key ){
- return model.searchAttribute( key, searchFor );
- });
- },
-
- /** alias of search, but returns a boolean; accepts attribute specifiers where
- * the attributes searched can be narrowed to a single attribute using
- * the form: matches( 'genome_build=hg19' )
- * (the attribute keys allowed can also be aliases to the true attribute key;
- * see searchAliases above)
- * @param {String} term plain text or ATTR_SPECIFIER sep. key=val pair
- * @returns {Boolean} was term found in (any) attribute(s)
- */
- matches : function( term ){
- var ATTR_SPECIFIER = '=',
- split = term.split( ATTR_SPECIFIER );
- // attribute is specified - search only that
- if( split.length >= 2 ){
- var attrKey = split[0];
- attrKey = this.searchAliases[ attrKey ] || attrKey;
- return this.searchAttribute( attrKey, split[1] );
- }
- // no attribute is specified - search all attributes in searchAttributes
- return !!this.search( term ).length;
- },
-
- /** an implicit AND search for all terms; IOW, an hda must match all terms given
- * where terms is a whitespace separated value string.
- * e.g. given terms of: 'blah bler database=hg19'
- * an HDA would have to have attributes containing blah AND bler AND a genome_build == hg19
- * To include whitespace in terms: wrap the term in double quotations.
- */
- matchesAll : function( terms ){
- var model = this;
- // break the terms up by whitespace and filter out the empty strings
- terms = terms.match( /(".*"|\w*=".*"|\S*)/g ).filter( function( s ){ return !!s; });
- return _.all( terms, function( term ){
- term = term.replace( /"/g, '' );
- return model.matches( term );
- });
- },
-
- _createTypeId : function(){
- this.set( 'type_id', TypeIdModel.typeIdStr( this.get( 'history_content_type' ), this.get( 'id' ) ) );
- },
-
-} );
-
-/** create a type + id string for use in mixed collections */
-HistoryContent.typeIdStr = function _typeId( type, id ){
- return [ type, id ].join( '-' );
-};
-
+], function( historyContent, _l ){
//==============================================================================
/** @class (HDA) model for a Galaxy dataset
* related to a history.
@@ -174,7 +12,7 @@
* @borrows LoggableMixin#log as #log
* @constructs
*/
-var HistoryDatasetAssociation = HistoryContent.extend(
+var HistoryDatasetAssociation = historyContent.HistoryContent.extend(
/** @lends HistoryDatasetAssociation.prototype */{
///** logger used to record this.log messages, commonly set to console */
@@ -456,326 +294,9 @@
HistoryDatasetAssociation.STATES.NEW
];
-//==============================================================================
-/** @class Backbone collection of (HDA) models
- * TODO: Rename HistoryContentCollection
- *
- * @borrows LoggableMixin#logger as #logger
- * @borrows LoggableMixin#log as #log
- * @constructs
- */
-var HDACollection = Backbone.Collection.extend( baseMVC.LoggableMixin ).extend(
-/** @lends HDACollection.prototype */{
- model : function( attrs, options ) {
- if( attrs.history_content_type == "dataset" ) {
- return new HistoryDatasetAssociation( attrs, options );
- } else if( attrs.history_content_type == "dataset_collection" ) {
- return new HistoryDatasetCollectionAssociation( attrs, options );
- } else {
- // TODO: Handle unknown history_content_type...
- }
- },
-
- ///** logger used to record this.log messages, commonly set to console */
- //// comment this out to suppress log output
- //logger : console,
-
- /** root api url */
- urlRoot : galaxy_config.root + 'api/histories',
- /** complete api url */
- url : function(){
- return this.urlRoot + '/' + this.historyId + '/contents';
- },
-
- /** Set up.
- * @see Backbone.Collection#initialize
- */
- initialize : function( models, options ){
- options = options || {};
- this.historyId = options.historyId;
- //this._setUpListeners();
- },
-
- //_setUpListeners : function(){
- //},
-
- // ........................................................................ common queries
- /** Get the ids of every hda in this collection
- * @returns array of encoded ids
- */
- ids : function(){
- return this.map( function( hda ){ return hda.get('id'); });
- },
-
- /** Get hdas that are not ready
- * @returns array of HDAs
- */
- notReady : function(){
- return this.filter( function( hda ){
- return !hda.inReadyState();
- });
- },
-
- /** Get the id of every hda in this collection not in a 'ready' state (running).
- * @returns an array of hda ids
- * @see HistoryDatasetAssociation#inReadyState
- */
- running : function(){
- var idList = [];
- this.each( function( item ){
- if( !item.inReadyState() ){
- idList.push( item.get( 'id' ) );
- }
- });
- return idList;
- },
-
- /** Get the hda with the given hid
- * @param {Int} hid the hid to search for
- * @returns {HistoryDatasetAssociation} the hda with the given hid or undefined if not found
- */
- getByHid : function( hid ){
- return _.first( this.filter( function( hda ){ return hda.get( 'hid' ) === hid; }) );
- },
-
- /** Get every 'shown' hda in this collection based on show_deleted/hidden
- * @param {Boolean} show_deleted are we showing deleted hdas?
- * @param {Boolean} show_hidden are we showing hidden hdas?
- * @returns array of hda models
- * @see HistoryDatasetAssociation#isVisible
- */
- getVisible : function( show_deleted, show_hidden, filters ){
- filters = filters || [];
- //console.debug( 'filters:', filters );
- //TODO:?? why doesn't this return a collection?
- // always filter by show deleted/hidden first
- var filteredHdas = new HDACollection( this.filter( function( item ){
- return item.isVisible( show_deleted, show_hidden );
- }));
-
- _.each( filters, function( filter_fn ){
- if( !_.isFunction( filter_fn ) ){ return; }
- filteredHdas = new HDACollection( filteredHdas.filter( filter_fn ) );
- });
- //if( filteredHdas.length ){
- // console.debug( 'filteredHdas:' );
- // filteredHdas.each( function( hda ){
- // console.debug( '\t', hda );
- // });
- //} else {
- // console.warn( 'no visible hdas' );
- //}
- return filteredHdas;
- },
-
- /** return true if any hdas don't have details */
- haveDetails : function(){
- return this.all( function( hda ){ return hda.hasDetails(); });
- },
-
- // ........................................................................ ajax
- /** fetch detailed model data for all HDAs in this collection */
- fetchAllDetails : function( options ){
- options = options || {};
- var detailsFlag = { details: 'all' };
- options.data = ( options.data )?( _.extend( options.data, detailsFlag ) ):( detailsFlag );
- return this.fetch( options );
- },
-
- /** using a queue, perform hdaModelAjaxFn on each of the hdas in this collection */
- ajaxQueue : function( hdaAjaxFn, options ){
- var deferred = jQuery.Deferred(),
- startingLength = this.length,
- responses = [];
-
- if( !startingLength ){
- deferred.resolve([]);
- return deferred;
- }
-
- // use reverse order (stylistic choice)
- var ajaxFns = this.chain().reverse().map( function( hda, i ){
- return function(){
- var xhr = hdaAjaxFn.call( hda, options );
- // if successful, notify using the deferred to allow tracking progress
- xhr.done( function( response ){
- deferred.notify({ curr: i, total: startingLength, response: response, model: hda });
- });
- // (regardless of previous error or success) if not last ajax call, shift and call the next
- // if last fn, resolve deferred
- xhr.always( function( response ){
- responses.push( response );
- if( ajaxFns.length ){
- ajaxFns.shift()();
- } else {
- deferred.resolve( responses );
- }
- });
- };
- }).value();
- // start the queue
- ajaxFns.shift()();
-
- return deferred;
- },
-
- // ........................................................................ sorting/filtering
- /** return a new collection of HDAs whose attributes contain the substring matchesWhat */
- matches : function( matchesWhat ){
- return this.filter( function( hda ){
- return hda.matches( matchesWhat );
- });
- },
-
- // ........................................................................ misc
- set : function( models, options ){
- // arrrrrrrrrrrrrrrrrg...
- // override to get a correct/smarter merge when incoming data is partial (e.g. stupid backbone)
- // w/o this partial models from the server will fill in missing data with model defaults
- // and overwrite existing data on the client
- // see Backbone.Collection.set and _prepareModel
- var collection = this;
- models = _.map( models, function( model ){
- var attrs = model.attributes || model; // Handle raw json or Backbone model.
- var typeId = HistoryContent.typeIdStr( attrs.history_content_type, attrs.id );
- var existing = collection.get( typeId );
- if( !existing ){ return model; }
-
- // merge the models _BEFORE_ calling the superclass version
- var merged = existing.toJSON();
- _.extend( merged, model );
- return merged;
- });
- // now call superclass when the data is filled
- Backbone.Collection.prototype.set.call( this, models, options );
- },
-
- /** Convert this ad-hoc collection of HDAs to a formal collection tracked
- by the server.
- **/
- promoteToHistoryDatasetCollection : function _promote( history, collection_type, options ){
- options = options || {};
- options.url = this.url();
- options.type = "POST";
- var full_collection_type = collection_type;
- var element_identifiers = [],
- name = null;
-
- // This mechanism is rough - no error handling, allows invalid selections, no way
- // for user to pick/override element identifiers. This is only really meant
- if( collection_type == "list" ) {
- this.chain().each( function( hda ) {
- // TODO: Handle duplicate names.
- var name = hda.attributes.name;
- var id = hda.get('id');
- var content_type = hda.attributes.history_content_type;
- if( content_type == "dataset" ) {
- if( full_collection_type != "list" ) {
- console.log( "Invalid collection type" );
- }
- element_identifiers.push( { name: name, src: "hda", id: id } );
- } else {
- if( full_collection_type == "list" ) {
- full_collection_type = "list:" + hda.attributes.collection_type;
- } else {
- if( full_collection_type != "list:" + hda.attributes.collection_type ) {
- console.log( "Invalid collection type" );
- }
- }
- element_identifiers.push( { name: name, src: "hdca", id: id } );
- }
- });
- name = "New Dataset List";
- } else if( collection_type == "paired" ) {
- var ids = this.ids();
- if( ids.length != 2 ){
- // TODO: Do something...
- }
- element_identifiers.push( { name: "forward", src: "hda", id: ids[ 0 ] } );
- element_identifiers.push( { name: "reverse", src: "hda", id: ids[ 1 ] } );
- name = "New Dataset Pair";
- }
- options.data = {type: "dataset_collection",
- name: name,
- collection_type: full_collection_type,
- element_identifiers: JSON.stringify(element_identifiers),
- };
-
- var xhr = jQuery.ajax( options );
- xhr.done( function( message, status, responseObj ){
- history.refresh( );
- });
- xhr.fail( function( xhr, status, message ){
- if( xhr.responseJSON && xhr.responseJSON.error ){
- error = xhr.responseJSON.error;
- } else {
- error = xhr.responseJSON;
- }
- xhr.responseText = error;
- // Do something?
- });
- return xhr;
- },
-
- /** String representation. */
- toString : function(){
- return ([ 'HDACollection(', [ this.historyId, this.length ].join(), ')' ].join( '' ));
- }
-});
-
-var HistoryDatasetCollectionAssociation = HistoryContent.extend(
-/** @lends HistoryDatasetCollectionAssociation.prototype */{
- /** default attributes for a model */
- defaults : {
- // parent (containing) history
- history_id : null,
- // often used with tagging
- model_class : 'HistoryDatasetCollectionAssociation',
- history_content_type : 'dataset_collection',
- hid : 0,
-
- id : null,
- name : '(unnamed dataset collection)',
- // one of HistoryDatasetAssociation.STATES, calling them all 'ok' for now.
- state : 'ok',
-
- accessible : true,
- deleted : false,
- visible : true,
-
- purged : false, // Purged doesn't make sense for collections - at least right now.
-
- tags : [],
- annotation : ''
- },
- urls : function(){
- },
-
- inReadyState : function(){
- return true; // TODO
- },
-
- // ........................................................................ search
- /** what attributes of an collection will be used in a text search */
- searchAttributes : [
- 'name'
- ],
-
- /** our attr keys don't often match the labels we display to the user - so, when using
- * attribute specifiers ('name="bler"') in a term, allow passing in aliases for the
- * following attr keys.
- */
- searchAliases : {
- title : 'name'
- // TODO: Add tag...
- },
-
-});
-
-
//==============================================================================
-return {
- HistoryDatasetAssociation : HistoryDatasetAssociation,
- HDACollection : HDACollection
-};});
+ return {
+ HistoryDatasetAssociation : HistoryDatasetAssociation
+ };
+});
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b 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
@@ -53,7 +53,7 @@
noneFoundMsg : _l( "No matching datasets found" ),
// ......................................................................... SET UP
- /** Set up the view, set up storage, bind listeners to HDACollection events
+ /** Set up the view, set up storage, bind listeners to HistoryContents events
* @param {Object} attributes
*/
initialize : function( attributes ){
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/mvc/history/history-content-base.js
--- /dev/null
+++ b/static/scripts/mvc/history/history-content-base.js
@@ -0,0 +1,181 @@
+define([
+ "mvc/base-mvc",
+ "utils/localization"
+], function( baseMVC, _l ){
+//==============================================================================
+/** @class base model for content items contained in a history or hdca.
+ * @name HistoryContent
+ *
+ * @augments Backbone.Model
+ * @borrows LoggableMixin#logger as #logger
+ * @borrows LoggableMixin#log as #log
+ * @constructs
+ */
+var HistoryContent = Backbone.Model.extend( baseMVC.LoggableMixin ).extend( {
+ idAttribute : 'type_id',
+
+ constructor : function( attrs, options ){
+ attrs.type_id = HistoryContent.typeIdStr( attrs.history_content_type, attrs.id );
+ Backbone.Model.apply( this, arguments );
+ },
+
+ initialize : function( attrs, options ){
+ // assumes type won't change
+ this.on( 'change:id', this._createTypeId );
+ //TODO: not sure this covers all the bases...
+ },
+ _createTypeId : function(){
+ this.set( 'type_id', TypeIdModel.typeIdStr( this.get( 'history_content_type' ), this.get( 'id' ) ) );
+ },
+
+ // ........................................................................ common queries
+ /** the more common alias of visible */
+ hidden : function(){
+ return !this.get( 'visible' );
+ },
+
+ /** based on show_deleted, show_hidden (gen. from the container control),
+ * would this ds show in the list of ds's?
+ * @param {Boolean} show_deleted are we showing deleted hdas?
+ * @param {Boolean} show_hidden are we showing hidden hdas?
+ */
+ isVisible : function( show_deleted, show_hidden ){
+ var isVisible = true;
+ if( ( !show_deleted )
+ && ( this.get( 'deleted' ) || this.get( 'purged' ) ) ){
+ isVisible = false;
+ }
+ if( ( !show_hidden )
+ && ( !this.get( 'visible' ) ) ){
+ isVisible = false;
+ }
+ return isVisible;
+ },
+
+ // ........................................................................ ajax
+ /** */
+ urlRoot: galaxy_config.root + 'api/histories/',
+
+ /** full url spec. for this HDA */
+ url : function(){
+ return this.urlRoot + this.get( 'history_id' ) + '/contents/'
+ + this.get('history_content_type') + 's/' + this.get( 'id' );
+ },
+
+ /** save this HDA, _Mark_ing it as deleted (just a flag) */
+ 'delete' : function _delete( options ){
+ if( this.get( 'deleted' ) ){ return jQuery.when(); }
+ return this.save( { deleted: true }, options );
+ },
+ /** save this HDA, _Mark_ing it as undeleted */
+ undelete : function _undelete( options ){
+ if( !this.get( 'deleted' ) || this.get( 'purged' ) ){ return jQuery.when(); }
+ return this.save( { deleted: false }, options );
+ },
+
+ /** save this HDA as not visible */
+ hide : function _hide( options ){
+ if( !this.get( 'visible' ) ){ return jQuery.when(); }
+ return this.save( { visible: false }, options );
+ },
+ /** save this HDA as visible */
+ unhide : function _uhide( options ){
+ if( this.get( 'visible' ) ){ return jQuery.when(); }
+ return this.save( { visible: true }, options );
+ },
+
+ // ........................................................................ searching
+ /** search the attribute with key attrKey for the string searchFor; T/F if found */
+ searchAttribute : function( attrKey, searchFor ){
+ var attrVal = this.get( attrKey );
+ //console.debug( 'searchAttribute', attrKey, attrVal, searchFor );
+ // bail if empty searchFor or unsearchable values
+ if( !searchFor
+ || ( attrVal === undefined || attrVal === null ) ){
+ return false;
+ }
+ // pass to sep. fn for deep search of array attributes
+ if( _.isArray( attrVal ) ){ return this._searchArrayAttribute( attrVal, searchFor ); }
+ return ( attrVal.toString().toLowerCase().indexOf( searchFor.toLowerCase() ) !== -1 );
+ },
+
+ /** deep(er) search for array attributes; T/F if found */
+ _searchArrayAttribute : function( array, searchFor ){
+ //console.debug( '_searchArrayAttribute', array, searchFor );
+ searchFor = searchFor.toLowerCase();
+ //precondition: searchFor has already been validated as non-empty string
+ //precondition: assumes only 1 level array
+ //TODO: could possibly break up searchFor more (CSV...)
+ return _.any( array, function( elem ){
+ return ( elem.toString().toLowerCase().indexOf( searchFor.toLowerCase() ) !== -1 );
+ });
+ },
+
+ /** search all searchAttributes for the string searchFor,
+ * returning a list of keys of attributes that contain searchFor
+ */
+ search : function( searchFor ){
+ var model = this;
+ return _.filter( this.searchAttributes, function( key ){
+ return model.searchAttribute( key, searchFor );
+ });
+ },
+
+ /** alias of search, but returns a boolean; accepts attribute specifiers where
+ * the attributes searched can be narrowed to a single attribute using
+ * the form: matches( 'genome_build=hg19' )
+ * (the attribute keys allowed can also be aliases to the true attribute key;
+ * see searchAliases above)
+ * @param {String} term plain text or ATTR_SPECIFIER sep. key=val pair
+ * @returns {Boolean} was term found in (any) attribute(s)
+ */
+ matches : function( term ){
+ var ATTR_SPECIFIER = '=',
+ split = term.split( ATTR_SPECIFIER );
+ // attribute is specified - search only that
+ if( split.length >= 2 ){
+ var attrKey = split[0];
+ attrKey = this.searchAliases[ attrKey ] || attrKey;
+ return this.searchAttribute( attrKey, split[1] );
+ }
+ // no attribute is specified - search all attributes in searchAttributes
+ return !!this.search( term ).length;
+ },
+
+ /** an implicit AND search for all terms; IOW, an hda must match all terms given
+ * where terms is a whitespace separated value string.
+ * e.g. given terms of: 'blah bler database=hg19'
+ * an HDA would have to have attributes containing blah AND bler AND a genome_build == hg19
+ * To include whitespace in terms: wrap the term in double quotations.
+ */
+ matchesAll : function( terms ){
+ var model = this;
+ // break the terms up by whitespace and filter out the empty strings
+ terms = terms.match( /(".*"|\w*=".*"|\S*)/g ).filter( function( s ){ return !!s; });
+ return _.all( terms, function( term ){
+ term = term.replace( /"/g, '' );
+ return model.matches( term );
+ });
+ },
+
+ // ........................................................................ misc
+ /** String representation */
+ toString : function(){
+ var nameAndId = this.get( 'id' ) || '';
+ if( this.get( 'name' ) ){
+ nameAndId = this.get( 'hid' ) + ' :"' + this.get( 'name' ) + '",' + nameAndId;
+ }
+ return 'HistoryContent(' + nameAndId + ')';
+ }
+});
+
+/** create a type + id string for use in mixed collections */
+HistoryContent.typeIdStr = function _typeId( type, id ){
+ return [ type, id ].join( '-' );
+};
+
+
+//==============================================================================
+return {
+ HistoryContent : HistoryContent
+};});
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/mvc/history/history-contents.js
--- /dev/null
+++ b/static/scripts/mvc/history/history-contents.js
@@ -0,0 +1,277 @@
+define([
+ "mvc/history/history-content-base",
+ "mvc/dataset/hda-model",
+ "mvc/collection/hdca-model",
+ "mvc/base-mvc",
+ "utils/localization"
+], function( historyContent, hdaModel, hdcaModel, baseMVC, _l ){
+//==============================================================================
+/** @class Backbone collection for history content.
+ * NOTE: history content seems like a dataset collection, but differs in that it is mixed:
+ * each element can be either an HDA (dataset) or a DatasetCollection and co-exist on
+ * the same level.
+ * Dataset collections on the other hand are not mixed and (so far) can only contain either
+ * HDAs or child dataset collections on one level.
+ * This is why this does not inherit from any of the DatasetCollections (currently).
+ *
+ * @borrows LoggableMixin#logger as #logger
+ * @borrows LoggableMixin#log as #log
+ * @constructs
+ */
+var HistoryContents = Backbone.Collection.extend( baseMVC.LoggableMixin ).extend(
+/** @lends HistoryContents.prototype */{
+
+ ///** logger used to record this.log messages, commonly set to console */
+ //// comment this out to suppress log output
+ //logger : console,
+
+ /** since history content is a mix, override model fn into a factory, creating based on history_content_type */
+ model : function( attrs, options ) {
+ if( attrs.history_content_type === "dataset" ) {
+ return new hdaModel.HistoryDatasetAssociation( attrs, options );
+ } else if( attrs.history_content_type === "dataset_collection" ) {
+ return new hdcaModel.HistoryDatasetCollectionAssociation( attrs, options );
+ }
+ // TODO: Handle unknown history_content_type...
+ throw new TypeError( 'Unknown history_content_type:' + attrs.history_content_type );
+ },
+
+ /** Set up.
+ * @see Backbone.Collection#initialize
+ */
+ initialize : function( models, options ){
+ options = options || {};
+ this.historyId = options.historyId;
+ //this._setUpListeners();
+ },
+
+ /** root api url */
+ urlRoot : galaxy_config.root + 'api/histories',
+ /** complete api url */
+ url : function(){
+ return this.urlRoot + '/' + this.historyId + '/contents';
+ },
+
+ // ........................................................................ common queries
+ /** Get the ids of every hda in this collection
+ * @returns array of encoded ids
+ */
+ ids : function(){
+ return this.map( function( hda ){ return hda.get('id'); });
+ },
+
+ /** Get hdas that are not ready
+ * @returns array of HDAs
+ */
+ notReady : function(){
+ return this.filter( function( hda ){
+ return !hda.inReadyState();
+ });
+ },
+
+ /** Get the id of every hda in this collection not in a 'ready' state (running).
+ * @returns an array of hda ids
+ * @see HistoryDatasetAssociation#inReadyState
+ */
+ running : function(){
+ var idList = [];
+ this.each( function( item ){
+ if( !item.inReadyState() ){
+ idList.push( item.get( 'id' ) );
+ }
+ });
+ return idList;
+ },
+
+ /** Get the hda with the given hid
+ * @param {Int} hid the hid to search for
+ * @returns {HistoryDatasetAssociation} the hda with the given hid or undefined if not found
+ */
+ getByHid : function( hid ){
+ return _.first( this.filter( function( hda ){ return hda.get( 'hid' ) === hid; }) );
+ },
+
+ /** Get every 'shown' hda in this collection based on show_deleted/hidden
+ * @param {Boolean} show_deleted are we showing deleted hdas?
+ * @param {Boolean} show_hidden are we showing hidden hdas?
+ * @returns array of hda models
+ * @see HistoryDatasetAssociation#isVisible
+ */
+ getVisible : function( show_deleted, show_hidden, filters ){
+ filters = filters || [];
+ //console.debug( 'filters:', filters );
+ //TODO:?? why doesn't this return a collection?
+ // always filter by show deleted/hidden first
+ var filteredHdas = new HistoryContents( this.filter( function( item ){
+ return item.isVisible( show_deleted, show_hidden );
+ }));
+
+ _.each( filters, function( filter_fn ){
+ if( !_.isFunction( filter_fn ) ){ return; }
+ filteredHdas = new HistoryContents( filteredHdas.filter( filter_fn ) );
+ });
+ return filteredHdas;
+ },
+
+ /** return true if any hdas don't have details */
+ haveDetails : function(){
+ return this.all( function( hda ){ return hda.hasDetails(); });
+ },
+
+ // ........................................................................ ajax
+ /** fetch detailed model data for all HDAs in this collection */
+ fetchAllDetails : function( options ){
+ options = options || {};
+ var detailsFlag = { details: 'all' };
+ options.data = ( options.data )?( _.extend( options.data, detailsFlag ) ):( detailsFlag );
+ return this.fetch( options );
+ },
+
+ /** using a queue, perform hdaModelAjaxFn on each of the hdas in this collection */
+ ajaxQueue : function( hdaAjaxFn, options ){
+ var deferred = jQuery.Deferred(),
+ startingLength = this.length,
+ responses = [];
+
+ if( !startingLength ){
+ deferred.resolve([]);
+ return deferred;
+ }
+
+ // use reverse order (stylistic choice)
+ var ajaxFns = this.chain().reverse().map( function( hda, i ){
+ return function(){
+ var xhr = hdaAjaxFn.call( hda, options );
+ // if successful, notify using the deferred to allow tracking progress
+ xhr.done( function( response ){
+ deferred.notify({ curr: i, total: startingLength, response: response, model: hda });
+ });
+ // (regardless of previous error or success) if not last ajax call, shift and call the next
+ // if last fn, resolve deferred
+ xhr.always( function( response ){
+ responses.push( response );
+ if( ajaxFns.length ){
+ ajaxFns.shift()();
+ } else {
+ deferred.resolve( responses );
+ }
+ });
+ };
+ }).value();
+ // start the queue
+ ajaxFns.shift()();
+
+ return deferred;
+ },
+
+ // ........................................................................ sorting/filtering
+ /** return a new collection of HDAs whose attributes contain the substring matchesWhat */
+ matches : function( matchesWhat ){
+ return this.filter( function( hda ){
+ return hda.matches( matchesWhat );
+ });
+ },
+
+ // ........................................................................ misc
+ set : function( models, options ){
+ // arrrrrrrrrrrrrrrrrg...
+ // override to get a correct/smarter merge when incoming data is partial (e.g. stupid backbone)
+ // w/o this partial models from the server will fill in missing data with model defaults
+ // and overwrite existing data on the client
+ // see Backbone.Collection.set and _prepareModel
+ var collection = this;
+ models = _.map( models, function( model ){
+ var attrs = model.attributes || model; // Handle raw json or Backbone model.
+ var typeId = historyContent.HistoryContent.typeIdStr( attrs.history_content_type, attrs.id );
+ var existing = collection.get( typeId );
+ if( !existing ){ return model; }
+
+ // merge the models _BEFORE_ calling the superclass version
+ var merged = existing.toJSON();
+ _.extend( merged, model );
+ return merged;
+ });
+ // now call superclass when the data is filled
+ Backbone.Collection.prototype.set.call( this, models, options );
+ },
+
+ /** Convert this ad-hoc collection of HDAs to a formal collection tracked
+ by the server.
+ **/
+ promoteToHistoryDatasetCollection : function _promote( history, collection_type, options ){
+ options = options || {};
+ options.url = this.url();
+ options.type = "POST";
+ var full_collection_type = collection_type;
+ var element_identifiers = [],
+ name = null;
+
+ // This mechanism is rough - no error handling, allows invalid selections, no way
+ // for user to pick/override element identifiers. This is only really meant
+ if( collection_type === "list" ) {
+ this.chain().each( function( hda ) {
+ // TODO: Handle duplicate names.
+ var name = hda.attributes.name;
+ var id = hda.get('id');
+ var content_type = hda.attributes.history_content_type;
+ if( content_type === "dataset" ) {
+ if( full_collection_type !== "list" ) {
+ console.log( "Invalid collection type" );
+ }
+ element_identifiers.push( { name: name, src: "hda", id: id } );
+ } else {
+ if( full_collection_type === "list" ) {
+ full_collection_type = "list:" + hda.attributes.collection_type;
+ } else {
+ if( full_collection_type !== "list:" + hda.attributes.collection_type ) {
+ console.log( "Invalid collection type" );
+ }
+ }
+ element_identifiers.push( { name: name, src: "hdca", id: id } );
+ }
+ });
+ name = "New Dataset List";
+ } else if( collection_type === "paired" ) {
+ var ids = this.ids();
+ if( ids.length !== 2 ){
+ // TODO: Do something...
+ }
+ element_identifiers.push( { name: "forward", src: "hda", id: ids[ 0 ] } );
+ element_identifiers.push( { name: "reverse", src: "hda", id: ids[ 1 ] } );
+ name = "New Dataset Pair";
+ }
+ options.data = {
+ type: "dataset_collection",
+ name: name,
+ collection_type: full_collection_type,
+ element_identifiers: JSON.stringify( element_identifiers )
+ };
+
+ var xhr = jQuery.ajax( options );
+ xhr.done( function( message, status, responseObj ){
+ history.refresh( );
+ });
+ xhr.fail( function( xhr, status, message ){
+ if( xhr.responseJSON && xhr.responseJSON.error ){
+ error = xhr.responseJSON.error;
+ } else {
+ error = xhr.responseJSON;
+ }
+ xhr.responseText = error;
+ // Do something?
+ });
+ return xhr;
+ },
+
+ /** String representation. */
+ toString : function(){
+ return ([ 'HistoryContents(', [ this.parentId, this.length ].join(), ')' ].join( '' ));
+ }
+});
+
+
+//==============================================================================
+ return {
+ HistoryContents : HistoryContents
+ };
+});
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/mvc/history/history-model.js
--- a/static/scripts/mvc/history/history-model.js
+++ b/static/scripts/mvc/history/history-model.js
@@ -1,8 +1,8 @@
define([
- "mvc/dataset/hda-model",
+ "mvc/history/history-contents",
"mvc/base-mvc",
"utils/localization"
-], function( hdaModel, baseMVC, _l ){
+], function( historyContents, baseMVC, _l ){
//==============================================================================
/** @class Model for a Galaxy history resource - both a record of user
* tool use and a collection of the datasets those tools produced.
@@ -45,8 +45,8 @@
this.logger = options.logger || null;
this.log( this + ".initialize:", historyJSON, hdaJSON, options );
- /** HDACollection of the HDAs contained in this history. */
- this.hdas = new hdaModel.HDACollection( hdaJSON || [], { historyId: this.get( 'id' )});
+ /** HistoryContents collection of the HDAs contained in this history. */
+ this.hdas = new historyContents.HistoryContents( hdaJSON || [], { historyId: this.get( 'id' )});
// if we've got hdas passed in the constructor, load them
if( hdaJSON && _.isArray( hdaJSON ) ){
this.hdas.reset( hdaJSON );
@@ -230,12 +230,12 @@
}
var data = {};
if( hdaDetailIds.length ) {
- data[ "dataset_details" ] = hdaDetailIds.join( ',' );
+ data.dataset_details = hdaDetailIds.join( ',' );
}
if( hdcaDetailIds.length ) {
// for symmetry, not actually used by backend of consumed
// by frontend.
- data[ "dataset_collection_details" ] = hdcaDetailIds.join( ',' );
+ data.dataset_collection_details = hdcaDetailIds.join( ',' );
}
return jQuery.ajax( galaxy_config.root + 'api/histories/' + historyData.id + '/contents', { data: data });
}
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/mvc/history/history-panel.js
--- a/static/scripts/mvc/history/history-panel.js
+++ b/static/scripts/mvc/history/history-panel.js
@@ -2,11 +2,12 @@
"mvc/dataset/hda-model",
"mvc/dataset/hda-edit",
"mvc/collection/dataset-collection-edit",
+ "mvc/history/history-contents",
"mvc/history/readonly-history-panel",
"mvc/tags",
"mvc/annotations",
"utils/localization"
-], function( hdaModel, hdaEdit, datasetCollectionEdit, readonlyPanel, tagsMod, annotationsMod, _l ){
+], function( hdaModel, hdaEdit, datasetCollectionEdit, historyContents, readonlyPanel, tagsMod, annotationsMod, _l ){
/* =============================================================================
TODO:
@@ -36,7 +37,7 @@
HDAViewClass : hdaEdit.HDAEditView,
// ......................................................................... SET UP
- /** Set up the view, set up storage, bind listeners to HDACollection events
+ /** Set up the view, set up storage, bind listeners to HistoryContents events
* @param {Object} attributes
*/
initialize : function( attributes ){
@@ -493,7 +494,7 @@
/** return an HdaCollection of the models of all currenly selected hdas */
getSelectedHdaCollection : function(){
- return new hdaModel.HDACollection( _.map( this.getSelectedHdaViews(), function( hdaView ){
+ return new historyContents.HistoryContents( _.map( this.getSelectedHdaViews(), function( hdaView ){
return hdaView.model;
}), { historyId: this.model.id });
},
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/mvc/history/readonly-history-panel.js
--- a/static/scripts/mvc/history/readonly-history-panel.js
+++ b/static/scripts/mvc/history/readonly-history-panel.js
@@ -97,7 +97,7 @@
noneFoundMsg : _l( 'No matching datasets found' ),
// ......................................................................... SET UP
- /** Set up the view, set up storage, bind listeners to HDACollection events
+ /** Set up the view, set up storage, bind listeners to HistoryContents events
* @param {Object} attributes optional settings for the panel
*/
initialize : function( attributes ){
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/packed/mvc/collection/hdca-model.js
--- /dev/null
+++ b/static/scripts/packed/mvc/collection/hdca-model.js
@@ -0,0 +1,1 @@
+define(["mvc/history/history-content-base","utils/localization"],function(b,c){var a=b.HistoryContent.extend({defaults:{history_id:null,model_class:"HistoryDatasetCollectionAssociation",history_content_type:"dataset_collection",hid:0,id:null,name:"(unnamed dataset collection)",state:"ok",accessible:true,deleted:false,visible:true,purged:false,tags:[],annotation:""},urls:function(){},inReadyState:function(){return true},searchAttributes:["name"],searchAliases:{title:"name"},toString:function(){var d=this.get("id")||"";if(this.get("name")){d=this.get("hid")+' :"'+this.get("name")+'",'+d}return"HDCA-"+this.get("collection_type")+"("+d+")"}});return{HistoryDatasetCollectionAssociation:a}});
\ No newline at end of file
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/packed/mvc/dataset/hda-model.js
--- a/static/scripts/packed/mvc/dataset/hda-model.js
+++ b/static/scripts/packed/mvc/dataset/hda-model.js
@@ -1,1 +1,1 @@
-define(["mvc/base-mvc","utils/localization"],function(e,c){var a=Backbone.Model.extend(e.LoggableMixin).extend({idAttribute:"type_id",urlRoot:galaxy_config.root+"api/histories/",constructor:function(o,n){o.type_id=a.typeIdStr(o.history_content_type,o.id);Backbone.Model.apply(this,arguments)},initialize:function(o,n){this.on("change:id",this._createTypeId)},url:function(){return this.urlRoot+this.get("history_id")+"/contents/"+this.get("history_content_type")+"s/"+this.get("id")},hidden:function(){return !this.get("visible")},"delete":function f(n){if(this.get("deleted")){return jQuery.when()}return this.save({deleted:true},n)},undelete:function j(n){if(!this.get("deleted")||this.get("purged")){return jQuery.when()}return this.save({deleted:false},n)},hide:function d(n){if(!this.get("visible")){return jQuery.when()}return this.save({visible:false},n)},unhide:function i(n){if(this.get("visible")){return jQuery.when()}return this.save({visible:true},n)},isVisible:function(o,p){var n=true;if((!o)&&(this.get("deleted")||this.get("purged"))){n=false}if((!p)&&(!this.get("visible"))){n=false}return n},searchAttribute:function(p,n){var o=this.get(p);if(!n||(o===undefined||o===null)){return false}if(_.isArray(o)){return this._searchArrayAttribute(o,n)}return(o.toString().toLowerCase().indexOf(n.toLowerCase())!==-1)},_searchArrayAttribute:function(o,n){n=n.toLowerCase();return _.any(o,function(p){return(p.toString().toLowerCase().indexOf(n.toLowerCase())!==-1)})},search:function(n){var o=this;return _.filter(this.searchAttributes,function(p){return o.searchAttribute(p,n)})},matches:function(o){var q="=",n=o.split(q);if(n.length>=2){var p=n[0];p=this.searchAliases[p]||p;return this.searchAttribute(p,n[1])}return !!this.search(o).length},matchesAll:function(o){var n=this;o=o.match(/(".*"|\w*=".*"|\S*)/g).filter(function(p){return !!p});return _.all(o,function(p){p=p.replace(/"/g,"");return n.matches(p)})},_createTypeId:function(){this.set("type_id",TypeIdModel.typeIdStr(this.get("history_content_type"),this.get("id")))},});a.typeIdStr=function h(n,o){return[n,o].join("-")};var m=a.extend({defaults:{history_id:null,model_class:"HistoryDatasetAssociation",history_content_type:"dataset",hid:0,id:null,name:"(unnamed dataset)",state:"new",deleted:false,visible:true,accessible:true,purged:false,data_type:"",file_size:0,file_ext:"",meta_files:[],misc_blurb:"",misc_info:"",tags:[],annotation:""},urls:function(){var o=this.get("id");if(!o){return{}}var n={purge:galaxy_config.root+"datasets/"+o+"/purge_async",display:galaxy_config.root+"datasets/"+o+"/display/?preview=True",edit:galaxy_config.root+"datasets/"+o+"/edit",download:galaxy_config.root+"datasets/"+o+"/display?to_ext="+this.get("file_ext"),report_error:galaxy_config.root+"dataset/errors?id="+o,rerun:galaxy_config.root+"tool_runner/rerun?id="+o,show_params:galaxy_config.root+"datasets/"+o+"/show_params",visualization:galaxy_config.root+"visualization",annotation:{get:galaxy_config.root+"dataset/get_annotation_async?id="+o,set:galaxy_config.root+"dataset/annotate_async?id="+o},meta_download:galaxy_config.root+"dataset/get_metadata_file?hda_id="+o+"&metadata_name="};return n},initialize:function(n){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"));if(!this.get("accessible")){this.set("state",m.STATES.NOT_VIEWABLE)}this._setUpListeners()},_setUpListeners:function(){this.on("change:state",function(o,n){this.log(this+" has changed state:",o,n);if(this.inReadyState()){this.trigger("state:ready",o,n,this.previous("state"))}})},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},inReadyState:function(){var n=_.contains(m.READY_STATES,this.get("state"));return(this.isDeletedOrPurged()||n)},hasDetails:function(){return _.has(this.attributes,"genome_build")},hasData:function(){return(this.get("file_size")>0)},"delete":function f(n){if(this.get("deleted")){return jQuery.when()}return this.save({deleted:true},n)},undelete:function j(n){if(!this.get("deleted")||this.get("purged")){return jQuery.when()}return this.save({deleted:false},n)},hide:function d(n){if(!this.get("visible")){return jQuery.when()}return this.save({visible:false},n)},unhide:function i(n){if(this.get("visible")){return jQuery.when()}return this.save({visible:true},n)},purge:function g(n){if(this.get("purged")){return jQuery.when()}n=n||{};n.url=galaxy_config.root+"datasets/"+this.get("id")+"/purge_async";var o=this,p=jQuery.ajax(n);p.done(function(s,q,r){o.set({deleted:true,purged:true})});p.fail(function(u,q,t){var r=c("Unable to purge dataset");var s=("Removal of datasets by users is not allowed in this Galaxy instance");if(u.responseJSON&&u.responseJSON.error){r=u.responseJSON.error}else{if(u.responseText.indexOf(s)!==-1){r=s}}u.responseText=r;o.trigger("error",o,u,n,c(r),{error:r})});return p},searchAttributes:["name","file_ext","genome_build","misc_blurb","misc_info","annotation","tags"],searchAliases:{title:"name",format:"file_ext",database:"genome_build",blurb:"misc_blurb",description:"misc_blurb",info:"misc_info",tag:"tags"},toString:function(){var n=this.get("id")||"";if(this.get("name")){n=this.get("hid")+' :"'+this.get("name")+'",'+n}return"HDA("+n+")"}});m.STATES={UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",SETTING_METADATA:"setting_metadata",NEW:"new",EMPTY:"empty",OK:"ok",PAUSED:"paused",FAILED_METADATA:"failed_metadata",NOT_VIEWABLE:"noPermission",DISCARDED:"discarded",ERROR:"error"};m.READY_STATES=[m.STATES.OK,m.STATES.EMPTY,m.STATES.PAUSED,m.STATES.FAILED_METADATA,m.STATES.NOT_VIEWABLE,m.STATES.DISCARDED,m.STATES.ERROR];m.NOT_READY_STATES=[m.STATES.UPLOAD,m.STATES.QUEUED,m.STATES.RUNNING,m.STATES.SETTING_METADATA,m.STATES.NEW];var b=Backbone.Collection.extend(e.LoggableMixin).extend({model:function(o,n){if(o.history_content_type=="dataset"){return new m(o,n)}else{if(o.history_content_type=="dataset_collection"){return new k(o,n)}else{}}},urlRoot:galaxy_config.root+"api/histories",url:function(){return this.urlRoot+"/"+this.historyId+"/contents"},initialize:function(o,n){n=n||{};this.historyId=n.historyId},ids:function(){return this.map(function(n){return n.get("id")})},notReady:function(){return this.filter(function(n){return !n.inReadyState()})},running:function(){var n=[];this.each(function(o){if(!o.inReadyState()){n.push(o.get("id"))}});return n},getByHid:function(n){return _.first(this.filter(function(o){return o.get("hid")===n}))},getVisible:function(n,q,p){p=p||[];var o=new b(this.filter(function(r){return r.isVisible(n,q)}));_.each(p,function(r){if(!_.isFunction(r)){return}o=new b(o.filter(r))});return o},haveDetails:function(){return this.all(function(n){return n.hasDetails()})},fetchAllDetails:function(o){o=o||{};var n={details:"all"};o.data=(o.data)?(_.extend(o.data,n)):(n);return this.fetch(o)},ajaxQueue:function(q,p){var o=jQuery.Deferred(),n=this.length,s=[];if(!n){o.resolve([]);return o}var r=this.chain().reverse().map(function(u,t){return function(){var v=q.call(u,p);v.done(function(w){o.notify({curr:t,total:n,response:w,model:u})});v.always(function(w){s.push(w);if(r.length){r.shift()()}else{o.resolve(s)}})}}).value();r.shift()();return o},matches:function(n){return this.filter(function(o){return o.matches(n)})},set:function(p,n){var o=this;p=_.map(p,function(s){var r=s.attributes||s;var t=a.typeIdStr(r.history_content_type,r.id);var u=o.get(t);if(!u){return s}var q=u.toJSON();_.extend(q,s);return q});Backbone.Collection.prototype.set.call(this,p,n)},promoteToHistoryDatasetCollection:function l(s,q,o){o=o||{};o.url=this.url();o.type="POST";var u=q;var r=[],n=null;if(q=="list"){this.chain().each(function(x){var v=x.attributes.name;var y=x.get("id");var w=x.attributes.history_content_type;if(w=="dataset"){if(u!="list"){console.log("Invalid collection type")}r.push({name:v,src:"hda",id:y})}else{if(u=="list"){u="list:"+x.attributes.collection_type}else{if(u!="list:"+x.attributes.collection_type){console.log("Invalid collection type")}}r.push({name:v,src:"hdca",id:y})}});n="New Dataset List"}else{if(q=="paired"){var p=this.ids();if(p.length!=2){}r.push({name:"forward",src:"hda",id:p[0]});r.push({name:"reverse",src:"hda",id:p[1]});n="New Dataset Pair"}}o.data={type:"dataset_collection",name:n,collection_type:u,element_identifiers:JSON.stringify(r),};var t=jQuery.ajax(o);t.done(function(x,v,w){s.refresh()});t.fail(function(x,v,w){if(x.responseJSON&&x.responseJSON.error){error=x.responseJSON.error}else{error=x.responseJSON}x.responseText=error});return t},toString:function(){return(["HDACollection(",[this.historyId,this.length].join(),")"].join(""))}});var k=a.extend({defaults:{history_id:null,model_class:"HistoryDatasetCollectionAssociation",history_content_type:"dataset_collection",hid:0,id:null,name:"(unnamed dataset collection)",state:"ok",accessible:true,deleted:false,visible:true,purged:false,tags:[],annotation:""},urls:function(){},inReadyState:function(){return true},searchAttributes:["name"],searchAliases:{title:"name"},});return{HistoryDatasetAssociation:m,HDACollection:b}});
\ No newline at end of file
+define(["mvc/history/history-content-base","utils/localization"],function(e,f){var d=e.HistoryContent.extend({defaults:{history_id:null,model_class:"HistoryDatasetAssociation",history_content_type:"dataset",hid:0,id:null,name:"(unnamed dataset)",state:"new",deleted:false,visible:true,accessible:true,purged:false,data_type:"",file_size:0,file_ext:"",meta_files:[],misc_blurb:"",misc_info:"",tags:[],annotation:""},urls:function(){var j=this.get("id");if(!j){return{}}var i={purge:galaxy_config.root+"datasets/"+j+"/purge_async",display:galaxy_config.root+"datasets/"+j+"/display/?preview=True",edit:galaxy_config.root+"datasets/"+j+"/edit",download:galaxy_config.root+"datasets/"+j+"/display?to_ext="+this.get("file_ext"),report_error:galaxy_config.root+"dataset/errors?id="+j,rerun:galaxy_config.root+"tool_runner/rerun?id="+j,show_params:galaxy_config.root+"datasets/"+j+"/show_params",visualization:galaxy_config.root+"visualization",annotation:{get:galaxy_config.root+"dataset/get_annotation_async?id="+j,set:galaxy_config.root+"dataset/annotate_async?id="+j},meta_download:galaxy_config.root+"dataset/get_metadata_file?hda_id="+j+"&metadata_name="};return i},initialize:function(i){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"));if(!this.get("accessible")){this.set("state",d.STATES.NOT_VIEWABLE)}this._setUpListeners()},_setUpListeners:function(){this.on("change:state",function(j,i){this.log(this+" has changed state:",j,i);if(this.inReadyState()){this.trigger("state:ready",j,i,this.previous("state"))}})},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},inReadyState:function(){var i=_.contains(d.READY_STATES,this.get("state"));return(this.isDeletedOrPurged()||i)},hasDetails:function(){return _.has(this.attributes,"genome_build")},hasData:function(){return(this.get("file_size")>0)},"delete":function c(i){if(this.get("deleted")){return jQuery.when()}return this.save({deleted:true},i)},undelete:function a(i){if(!this.get("deleted")||this.get("purged")){return jQuery.when()}return this.save({deleted:false},i)},hide:function b(i){if(!this.get("visible")){return jQuery.when()}return this.save({visible:false},i)},unhide:function h(i){if(this.get("visible")){return jQuery.when()}return this.save({visible:true},i)},purge:function g(i){if(this.get("purged")){return jQuery.when()}i=i||{};i.url=galaxy_config.root+"datasets/"+this.get("id")+"/purge_async";var j=this,k=jQuery.ajax(i);k.done(function(n,l,m){j.set({deleted:true,purged:true})});k.fail(function(p,l,o){var m=f("Unable to purge dataset");var n=("Removal of datasets by users is not allowed in this Galaxy instance");if(p.responseJSON&&p.responseJSON.error){m=p.responseJSON.error}else{if(p.responseText.indexOf(n)!==-1){m=n}}p.responseText=m;j.trigger("error",j,p,i,f(m),{error:m})});return k},searchAttributes:["name","file_ext","genome_build","misc_blurb","misc_info","annotation","tags"],searchAliases:{title:"name",format:"file_ext",database:"genome_build",blurb:"misc_blurb",description:"misc_blurb",info:"misc_info",tag:"tags"},toString:function(){var i=this.get("id")||"";if(this.get("name")){i=this.get("hid")+' :"'+this.get("name")+'",'+i}return"HDA("+i+")"}});d.STATES={UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",SETTING_METADATA:"setting_metadata",NEW:"new",EMPTY:"empty",OK:"ok",PAUSED:"paused",FAILED_METADATA:"failed_metadata",NOT_VIEWABLE:"noPermission",DISCARDED:"discarded",ERROR:"error"};d.READY_STATES=[d.STATES.OK,d.STATES.EMPTY,d.STATES.PAUSED,d.STATES.FAILED_METADATA,d.STATES.NOT_VIEWABLE,d.STATES.DISCARDED,d.STATES.ERROR];d.NOT_READY_STATES=[d.STATES.UPLOAD,d.STATES.QUEUED,d.STATES.RUNNING,d.STATES.SETTING_METADATA,d.STATES.NEW];return{HistoryDatasetAssociation:d}});
\ No newline at end of file
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/packed/mvc/history/history-content-base.js
--- /dev/null
+++ b/static/scripts/packed/mvc/history/history-content-base.js
@@ -0,0 +1,1 @@
+define(["mvc/base-mvc","utils/localization"],function(b,e){var f=Backbone.Model.extend(b.LoggableMixin).extend({idAttribute:"type_id",constructor:function(j,i){j.type_id=f.typeIdStr(j.history_content_type,j.id);Backbone.Model.apply(this,arguments)},initialize:function(j,i){this.on("change:id",this._createTypeId)},_createTypeId:function(){this.set("type_id",TypeIdModel.typeIdStr(this.get("history_content_type"),this.get("id")))},hidden:function(){return !this.get("visible")},isVisible:function(j,k){var i=true;if((!j)&&(this.get("deleted")||this.get("purged"))){i=false}if((!k)&&(!this.get("visible"))){i=false}return i},urlRoot:galaxy_config.root+"api/histories/",url:function(){return this.urlRoot+this.get("history_id")+"/contents/"+this.get("history_content_type")+"s/"+this.get("id")},"delete":function d(i){if(this.get("deleted")){return jQuery.when()}return this.save({deleted:true},i)},undelete:function a(i){if(!this.get("deleted")||this.get("purged")){return jQuery.when()}return this.save({deleted:false},i)},hide:function c(i){if(!this.get("visible")){return jQuery.when()}return this.save({visible:false},i)},unhide:function g(i){if(this.get("visible")){return jQuery.when()}return this.save({visible:true},i)},searchAttribute:function(k,i){var j=this.get(k);if(!i||(j===undefined||j===null)){return false}if(_.isArray(j)){return this._searchArrayAttribute(j,i)}return(j.toString().toLowerCase().indexOf(i.toLowerCase())!==-1)},_searchArrayAttribute:function(j,i){i=i.toLowerCase();return _.any(j,function(k){return(k.toString().toLowerCase().indexOf(i.toLowerCase())!==-1)})},search:function(i){var j=this;return _.filter(this.searchAttributes,function(k){return j.searchAttribute(k,i)})},matches:function(j){var l="=",i=j.split(l);if(i.length>=2){var k=i[0];k=this.searchAliases[k]||k;return this.searchAttribute(k,i[1])}return !!this.search(j).length},matchesAll:function(j){var i=this;j=j.match(/(".*"|\w*=".*"|\S*)/g).filter(function(k){return !!k});return _.all(j,function(k){k=k.replace(/"/g,"");return i.matches(k)})},toString:function(){var i=this.get("id")||"";if(this.get("name")){i=this.get("hid")+' :"'+this.get("name")+'",'+i}return"HistoryContent("+i+")"}});f.typeIdStr=function h(i,j){return[i,j].join("-")};return{HistoryContent:f}});
\ No newline at end of file
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/packed/mvc/history/history-contents.js
--- /dev/null
+++ b/static/scripts/packed/mvc/history/history-contents.js
@@ -0,0 +1,1 @@
+define(["mvc/history/history-content-base","mvc/dataset/hda-model","mvc/collection/hdca-model","mvc/base-mvc","utils/localization"],function(b,d,f,a,c){var g=Backbone.Collection.extend(a.LoggableMixin).extend({model:function(i,h){console.debug("HistoryContents.model:",i);if(i.history_content_type==="dataset"){return new d.HistoryDatasetAssociation(i,h)}else{if(i.history_content_type==="dataset_collection"){return new f.HistoryDatasetCollectionAssociation(i,h)}}throw new TypeError("Unknown history_content_type:"+i.history_content_type)},initialize:function(i,h){h=h||{};console.debug("new HistoryContents",i,h);this.historyId=h.historyId},urlRoot:galaxy_config.root+"api/histories",url:function(){return this.urlRoot+"/"+this.historyId+"/contents"},ids:function(){return this.map(function(h){return h.get("id")})},notReady:function(){return this.filter(function(h){return !h.inReadyState()})},running:function(){var h=[];this.each(function(i){if(!i.inReadyState()){h.push(i.get("id"))}});return h},getByHid:function(h){return _.first(this.filter(function(i){return i.get("hid")===h}))},getVisible:function(h,k,j){j=j||[];var i=new g(this.filter(function(l){return l.isVisible(h,k)}));_.each(j,function(l){if(!_.isFunction(l)){return}i=new g(i.filter(l))});return i},haveDetails:function(){return this.all(function(h){return h.hasDetails()})},fetchAllDetails:function(i){i=i||{};var h={details:"all"};i.data=(i.data)?(_.extend(i.data,h)):(h);return this.fetch(i)},ajaxQueue:function(k,j){var i=jQuery.Deferred(),h=this.length,m=[];if(!h){i.resolve([]);return i}var l=this.chain().reverse().map(function(o,n){return function(){var p=k.call(o,j);p.done(function(q){i.notify({curr:n,total:h,response:q,model:o})});p.always(function(q){m.push(q);if(l.length){l.shift()()}else{i.resolve(m)}})}}).value();l.shift()();return i},matches:function(h){return this.filter(function(i){return i.matches(h)})},set:function(j,h){var i=this;j=_.map(j,function(m){var l=m.attributes||m;var n=b.HistoryContent.typeIdStr(l.history_content_type,l.id);var o=i.get(n);if(!o){return m}var k=o.toJSON();_.extend(k,m);return k});Backbone.Collection.prototype.set.call(this,j,h)},promoteToHistoryDatasetCollection:function e(m,k,i){i=i||{};i.url=this.url();i.type="POST";var o=k;var l=[],h=null;if(k==="list"){this.chain().each(function(r){var p=r.attributes.name;var s=r.get("id");var q=r.attributes.history_content_type;if(q==="dataset"){if(o!=="list"){console.log("Invalid collection type")}l.push({name:p,src:"hda",id:s})}else{if(o==="list"){o="list:"+r.attributes.collection_type}else{if(o!=="list:"+r.attributes.collection_type){console.log("Invalid collection type")}}l.push({name:p,src:"hdca",id:s})}});h="New Dataset List"}else{if(k==="paired"){var j=this.ids();if(j.length!==2){}l.push({name:"forward",src:"hda",id:j[0]});l.push({name:"reverse",src:"hda",id:j[1]});h="New Dataset Pair"}}i.data={type:"dataset_collection",name:h,collection_type:o,element_identifiers:JSON.stringify(l)};var n=jQuery.ajax(i);n.done(function(r,p,q){m.refresh()});n.fail(function(r,p,q){if(r.responseJSON&&r.responseJSON.error){error=r.responseJSON.error}else{error=r.responseJSON}r.responseText=error});return n},toString:function(){return(["HistoryContents(",[this.parentId,this.length].join(),")"].join(""))}});return{HistoryContents:g}});
\ No newline at end of file
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/packed/mvc/history/history-model.js
--- a/static/scripts/packed/mvc/history/history-model.js
+++ b/static/scripts/packed/mvc/history/history-model.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model","mvc/base-mvc","utils/localization"],function(c,a,b){var e=Backbone.Model.extend(a.LoggableMixin).extend({defaults:{model_class:"History",id:null,name:"Unnamed History",state:"new",diskSize:0,deleted:false},urlRoot:galaxy_config.root+"api/histories",initialize:function(h,i,g){g=g||{};this.logger=g.logger||null;this.log(this+".initialize:",h,i,g);this.hdas=new c.HDACollection(i||[],{historyId:this.get("id")});if(i&&_.isArray(i)){this.hdas.reset(i)}this._setUpListeners();this.updateTimeoutId=null},_setUpListeners:function(){this.on("error",function(h,k,g,j,i){this.errorHandler(h,k,g,j,i)});if(this.hdas){this.listenTo(this.hdas,"error",function(){this.trigger.apply(this,["error:hdas"].concat(jQuery.makeArray(arguments)))})}this.on("change:id",function(h,g){if(this.hdas){this.hdas.historyId=g}},this)},errorHandler:function(h,k,g,j,i){this.clearUpdateTimeout()},ownedByCurrUser:function(){if(!Galaxy||!Galaxy.currUser){return false}if(Galaxy.currUser.isAnonymous()||Galaxy.currUser.id!==this.get("user_id")){return false}return true},hdaCount:function(){return _.reduce(_.values(this.get("state_details")),function(g,h){return g+h},0)},checkForUpdates:function(g){if(this.hdas.running().length){this.setUpdateTimeout()}else{this.trigger("ready");if(_.isFunction(g)){g.call(this)}}return this},setUpdateTimeout:function(g){g=g||e.UPDATE_DELAY;var h=this;this.clearUpdateTimeout();this.updateTimeoutId=setTimeout(function(){h.refresh()},g);return this.updateTimeoutId},clearUpdateTimeout:function(){if(this.updateTimeoutId){clearTimeout(this.updateTimeoutId);this.updateTimeoutId=null}},refresh:function(h,g){h=h||[];g=g||{};var i=this;g.data=g.data||{};if(h.length){g.data.details=h.join(",")}var j=this.hdas.fetch(g);j.done(function(k){i.checkForUpdates(function(){this.fetch()})});return j},toString:function(){return"History("+this.get("id")+","+this.get("name")+")"}});e.UPDATE_DELAY=4000;e.getHistoryData=function f(h,s){s=s||{};var m=s.hdaDetailIds||[];var j=s.hdcaDetailIds||[];var o=jQuery.Deferred(),n=null;function i(t){return jQuery.ajax(galaxy_config.root+"api/histories/"+h)}function g(t){if(!t||!t.state_ids){return 0}return _.reduce(t.state_ids,function(u,w,v){return u+w.length},0)}function r(u){if(!g(u)){return[]}if(_.isFunction(m)){m=m(u)}if(_.isFunction(j)){j=j(u)}var t={};if(m.length){t.dataset_details=m.join(",")}if(j.length){t.dataset_collection_details=j.join(",")}return jQuery.ajax(galaxy_config.root+"api/histories/"+u.id+"/contents",{data:t})}var q=s.historyFn||i,p=s.hdaFn||r;var l=q(h);l.done(function(t){n=t;o.notify({status:"history data retrieved",historyJSON:n})});l.fail(function(v,t,u){o.reject(v,"loading the history")});var k=l.then(p);k.then(function(t){o.notify({status:"dataset data retrieved",historyJSON:n,hdaJSON:t});o.resolve(n,t)});k.fail(function(v,t,u){o.reject(v,"loading the datasets",{history:n})});return o};var d=Backbone.Collection.extend(a.LoggableMixin).extend({model:e,urlRoot:galaxy_config.root+"api/histories"});return{History:e,HistoryCollection:d}});
\ No newline at end of file
+define(["mvc/history/history-contents","mvc/base-mvc","utils/localization"],function(e,a,b){var d=Backbone.Model.extend(a.LoggableMixin).extend({defaults:{model_class:"History",id:null,name:"Unnamed History",state:"new",diskSize:0,deleted:false},urlRoot:galaxy_config.root+"api/histories",initialize:function(h,i,g){g=g||{};this.logger=g.logger||null;this.log(this+".initialize:",h,i,g);this.hdas=new e.HistoryContents(i||[],{historyId:this.get("id")});if(i&&_.isArray(i)){this.hdas.reset(i)}this._setUpListeners();this.updateTimeoutId=null},_setUpListeners:function(){this.on("error",function(h,k,g,j,i){this.errorHandler(h,k,g,j,i)});if(this.hdas){this.listenTo(this.hdas,"error",function(){this.trigger.apply(this,["error:hdas"].concat(jQuery.makeArray(arguments)))})}this.on("change:id",function(h,g){if(this.hdas){this.hdas.historyId=g}},this)},errorHandler:function(h,k,g,j,i){this.clearUpdateTimeout()},ownedByCurrUser:function(){if(!Galaxy||!Galaxy.currUser){return false}if(Galaxy.currUser.isAnonymous()||Galaxy.currUser.id!==this.get("user_id")){return false}return true},hdaCount:function(){return _.reduce(_.values(this.get("state_details")),function(g,h){return g+h},0)},checkForUpdates:function(g){if(this.hdas.running().length){this.setUpdateTimeout()}else{this.trigger("ready");if(_.isFunction(g)){g.call(this)}}return this},setUpdateTimeout:function(g){g=g||d.UPDATE_DELAY;var h=this;this.clearUpdateTimeout();this.updateTimeoutId=setTimeout(function(){h.refresh()},g);return this.updateTimeoutId},clearUpdateTimeout:function(){if(this.updateTimeoutId){clearTimeout(this.updateTimeoutId);this.updateTimeoutId=null}},refresh:function(h,g){h=h||[];g=g||{};var i=this;g.data=g.data||{};if(h.length){g.data.details=h.join(",")}var j=this.hdas.fetch(g);j.done(function(k){i.checkForUpdates(function(){this.fetch()})});return j},toString:function(){return"History("+this.get("id")+","+this.get("name")+")"}});d.UPDATE_DELAY=4000;d.getHistoryData=function f(h,s){s=s||{};var m=s.hdaDetailIds||[];var j=s.hdcaDetailIds||[];var o=jQuery.Deferred(),n=null;function i(t){return jQuery.ajax(galaxy_config.root+"api/histories/"+h)}function g(t){if(!t||!t.state_ids){return 0}return _.reduce(t.state_ids,function(u,w,v){return u+w.length},0)}function r(u){if(!g(u)){return[]}if(_.isFunction(m)){m=m(u)}if(_.isFunction(j)){j=j(u)}var t={};if(m.length){t.dataset_details=m.join(",")}if(j.length){t.dataset_collection_details=j.join(",")}return jQuery.ajax(galaxy_config.root+"api/histories/"+u.id+"/contents",{data:t})}var q=s.historyFn||i,p=s.hdaFn||r;var l=q(h);l.done(function(t){n=t;o.notify({status:"history data retrieved",historyJSON:n})});l.fail(function(v,t,u){o.reject(v,"loading the history")});var k=l.then(p);k.then(function(t){o.notify({status:"dataset data retrieved",historyJSON:n,hdaJSON:t});o.resolve(n,t)});k.fail(function(v,t,u){o.reject(v,"loading the datasets",{history:n})});return o};var c=Backbone.Collection.extend(a.LoggableMixin).extend({model:d,urlRoot:galaxy_config.root+"api/histories"});return{History:d,HistoryCollection:c}});
\ No newline at end of file
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/packed/mvc/history/history-panel.js
--- a/static/scripts/packed/mvc/history/history-panel.js
+++ b/static/scripts/packed/mvc/history/history-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model","mvc/dataset/hda-edit","mvc/collection/dataset-collection-edit","mvc/history/readonly-history-panel","mvc/tags","mvc/annotations","utils/localization"],function(f,b,h,d,a,c,e){var g=d.ReadOnlyHistoryPanel.extend({HDAViewClass:b.HDAEditView,initialize:function(i){i=i||{};this.selectedHdaIds=[];this.lastSelectedViewId=null;this.tagsEditor=null;this.annotationEditor=null;this.purgeAllowed=i.purgeAllowed||false;this.selecting=i.selecting||false;this.annotationEditorShown=i.annotationEditorShown||false;this.tagsEditorShown=i.tagsEditorShown||false;d.ReadOnlyHistoryPanel.prototype.initialize.call(this,i)},_setUpModelEventHandlers:function(){d.ReadOnlyHistoryPanel.prototype._setUpModelEventHandlers.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);this.model.hdas.on("change:deleted",this._handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this._handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(i){this.model.fetch()},this)},renderModel:function(){var i=$("<div/>");i.append(g.templates.historyPanel(this.model.toJSON()));this.$emptyMessage(i).text(this.emptyMsg);if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(i);this._renderAnnotation(i)}i.find(".history-secondary-actions").prepend(this._renderSelectButton());i.find(".history-dataset-actions").toggle(this.selecting);i.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(i);this.renderHdas(i);return i},_renderTags:function(i){var j=this;this.tagsEditor=new a.TagsEditor({model:this.model,el:i.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){j.toggleHDATagEditors(true,j.fxSpeed)},onhide:function(){j.toggleHDATagEditors(false,j.fxSpeed)},$activator:faIconButton({title:e("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(i.find(".history-secondary-actions"))})},_renderAnnotation:function(i){var j=this;this.annotationEditor=new c.AnnotationEditor({model:this.model,el:i.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){j.toggleHDAAnnotationEditors(true,j.fxSpeed)},onhide:function(){j.toggleHDAAnnotationEditors(false,j.fxSpeed)},$activator:faIconButton({title:e("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(i.find(".history-secondary-actions"))})},_renderSelectButton:function(i){return faIconButton({title:e("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(i){i=i||this.$el;d.ReadOnlyHistoryPanel.prototype._setUpBehaviours.call(this,i);if(!this.model){return}this._setUpDatasetActionsPopup(i);if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var j=this;i.find(".history-name").attr("title",e("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(k){var l=j.model.get("name");if(k&&k!==l){j.$el.find(".history-name").text(k);j.model.save({name:k}).fail(function(){j.$el.find(".history-name").text(j.model.previous("name"))})}else{j.$el.find(".history-name").text(l)}}})},_setUpDatasetActionsPopup:function(i){var j=this,k=[{html:e("Hide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.hide;j.getSelectedHdaCollection().ajaxQueue(l)}},{html:e("Unhide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.unhide;j.getSelectedHdaCollection().ajaxQueue(l)}},{html:e("Delete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype["delete"];j.getSelectedHdaCollection().ajaxQueue(l)}},{html:e("Undelete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.undelete;j.getSelectedHdaCollection().ajaxQueue(l)}}];if(j.purgeAllowed){k.push({html:e("Permanently delete datasets"),func:function(){if(confirm(e("This will permanently remove the data in your datasets. Are you sure?"))){var l=f.HistoryDatasetAssociation.prototype.purge;j.getSelectedHdaCollection().ajaxQueue(l)}}})}k.push({html:e("Build Dataset List (Experimental)"),func:function(){j.getSelectedHdaCollection().promoteToHistoryDatasetCollection(j.model,"list")}});k.push({html:e("Build Dataset Pair (Experimental)"),func:function(){j.getSelectedHdaCollection().promoteToHistoryDatasetCollection(j.model,"paired")}});k.push({html:e("Build List of Dataset Pairs (Experimental)"),func:_.bind(j._showPairedCollectionModal,j)});return new PopupMenu(i.find(".history-dataset-action-popup-btn"),k)},_showPairedCollectionModal:function(){var i=this,j=i.getSelectedHdaCollection().toJSON().filter(function(k){return k.history_content_type==="dataset"&&k.state===f.HistoryDatasetAssociation.STATES.OK});if(j.length){require(["mvc/collection/paired-collection-creator"],function(k){window.creator=k.pairedCollectionCreatorModal(j,{historyId:i.model.id})})}else{}},_handleHdaDeletionChange:function(i){if(i.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[i.id])}},_handleHdaVisibleChange:function(i){if(i.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[i.id])}},_createContentView:function(j){var i=j.id,l=j.get("history_content_type"),k=null;if(l==="dataset"){k=new this.HDAViewClass({model:j,linkTarget:this.linkTarget,expanded:this.storage.get("expandedHdas")[i],selectable:this.selecting,purgeAllowed:this.purgeAllowed,hasUser:this.model.ownedByCurrUser(),logger:this.logger,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)})}else{if(l==="dataset_collection"){k=new h.DatasetCollectionEditView({model:j,linkTarget:this.linkTarget,expanded:this.storage.get("expandedHdas")[i],hasUser:this.model.ownedByCurrUser(),logger:this.logger})}}this._setUpHdaListeners(k);return k},_setUpHdaListeners:function(j){var i=this;d.ReadOnlyHistoryPanel.prototype._setUpHdaListeners.call(this,j);j.on("selected",function(m,l){if(!l){return}var k=[];if((l.shiftKey)&&(i.lastSelectedViewId&&_.has(i.hdaViews,i.lastSelectedViewId))){var o=i.hdaViews[i.lastSelectedViewId];k=i.selectDatasetRange(m,o).map(function(p){return p.model.id})}else{var n=m.model.id;i.lastSelectedViewId=n;k=[n]}i.selectedHdaIds=_.union(i.selectedHdaIds,k)});j.on("de-selected",function(l,k){var m=l.model.id;i.selectedHdaIds=_.without(i.selectedHdaIds,m)})},toggleHDATagEditors:function(i){var j=arguments;_.each(this.hdaViews,function(k){if(k.tagsEditor){k.tagsEditor.toggle.apply(k.tagsEditor,j)}})},toggleHDAAnnotationEditors:function(i){var j=arguments;_.each(this.hdaViews,function(k){if(k.annotationEditor){k.annotationEditor.toggle.apply(k.annotationEditor,j)}})},removeHdaView:function(j){if(!j){return}var i=this;j.$el.fadeOut(i.fxSpeed,function(){j.off();j.remove();delete i.hdaViews[j.model.id];if(_.isEmpty(i.hdaViews)){i.trigger("empty-history",i);i._renderEmptyMsg()}})},events:_.extend(_.clone(d.ReadOnlyHistoryPanel.prototype.events),{"click .history-select-btn":"toggleSelectors","click .history-select-all-datasets-btn":"selectAllDatasets","click .history-deselect-all-datasets-btn":"deselectAllDatasets"}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},showSelectors:function(i){i=(i!==undefined)?(i):(this.fxSpeed);this.selecting=true;this.$(".history-dataset-actions").slideDown(i);_.each(this.hdaViews,function(j){j.showSelector()});this.selectedHdaIds=[];this.lastSelectedViewId=null},hideSelectors:function(i){i=(i!==undefined)?(i):(this.fxSpeed);this.selecting=false;this.$(".history-dataset-actions").slideUp(i);_.each(this.hdaViews,function(j){j.hideSelector()});this.selectedHdaIds=[];this.lastSelectedViewId=null},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAllDatasets:function(i){_.each(this.hdaViews,function(j){j.select(i)})},deselectAllDatasets:function(i){this.lastSelectedViewId=null;_.each(this.hdaViews,function(j){j.deselect(i)})},selectDatasetRange:function(k,j){var i=this.hdaViewRange(k,j);_.each(i,function(l){l.select()});return i},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(i){return i.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(i){return i.model}),{historyId:this.model.id})},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanel:g}});
\ No newline at end of file
+define(["mvc/dataset/hda-model","mvc/dataset/hda-edit","mvc/collection/dataset-collection-edit","mvc/history/history-contents","mvc/history/readonly-history-panel","mvc/tags","mvc/annotations","utils/localization"],function(f,h,b,g,c,d,i,a){var e=c.ReadOnlyHistoryPanel.extend({HDAViewClass:h.HDAEditView,initialize:function(j){j=j||{};this.selectedHdaIds=[];this.lastSelectedViewId=null;this.tagsEditor=null;this.annotationEditor=null;this.purgeAllowed=j.purgeAllowed||false;this.selecting=j.selecting||false;this.annotationEditorShown=j.annotationEditorShown||false;this.tagsEditorShown=j.tagsEditorShown||false;c.ReadOnlyHistoryPanel.prototype.initialize.call(this,j)},_setUpModelEventHandlers:function(){c.ReadOnlyHistoryPanel.prototype._setUpModelEventHandlers.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);this.model.hdas.on("change:deleted",this._handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this._handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(j){this.model.fetch()},this)},renderModel:function(){var j=$("<div/>");j.append(e.templates.historyPanel(this.model.toJSON()));this.$emptyMessage(j).text(this.emptyMsg);if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(j);this._renderAnnotation(j)}j.find(".history-secondary-actions").prepend(this._renderSelectButton());j.find(".history-dataset-actions").toggle(this.selecting);j.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(j);this.renderHdas(j);return j},_renderTags:function(j){var k=this;this.tagsEditor=new d.TagsEditor({model:this.model,el:j.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.toggleHDATagEditors(true,k.fxSpeed)},onhide:function(){k.toggleHDATagEditors(false,k.fxSpeed)},$activator:faIconButton({title:a("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(j.find(".history-secondary-actions"))})},_renderAnnotation:function(j){var k=this;this.annotationEditor=new i.AnnotationEditor({model:this.model,el:j.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.toggleHDAAnnotationEditors(true,k.fxSpeed)},onhide:function(){k.toggleHDAAnnotationEditors(false,k.fxSpeed)},$activator:faIconButton({title:a("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(j.find(".history-secondary-actions"))})},_renderSelectButton:function(j){return faIconButton({title:a("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(j){j=j||this.$el;c.ReadOnlyHistoryPanel.prototype._setUpBehaviours.call(this,j);if(!this.model){return}this._setUpDatasetActionsPopup(j);if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var k=this;j.find(".history-name").attr("title",a("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(l){var m=k.model.get("name");if(l&&l!==m){k.$el.find(".history-name").text(l);k.model.save({name:l}).fail(function(){k.$el.find(".history-name").text(k.model.previous("name"))})}else{k.$el.find(".history-name").text(m)}}})},_setUpDatasetActionsPopup:function(j){var k=this,l=[{html:a("Hide datasets"),func:function(){var m=f.HistoryDatasetAssociation.prototype.hide;k.getSelectedHdaCollection().ajaxQueue(m)}},{html:a("Unhide datasets"),func:function(){var m=f.HistoryDatasetAssociation.prototype.unhide;k.getSelectedHdaCollection().ajaxQueue(m)}},{html:a("Delete datasets"),func:function(){var m=f.HistoryDatasetAssociation.prototype["delete"];k.getSelectedHdaCollection().ajaxQueue(m)}},{html:a("Undelete datasets"),func:function(){var m=f.HistoryDatasetAssociation.prototype.undelete;k.getSelectedHdaCollection().ajaxQueue(m)}}];if(k.purgeAllowed){l.push({html:a("Permanently delete datasets"),func:function(){if(confirm(a("This will permanently remove the data in your datasets. Are you sure?"))){var m=f.HistoryDatasetAssociation.prototype.purge;k.getSelectedHdaCollection().ajaxQueue(m)}}})}l.push({html:a("Build Dataset List (Experimental)"),func:function(){k.getSelectedHdaCollection().promoteToHistoryDatasetCollection(k.model,"list")}});l.push({html:a("Build Dataset Pair (Experimental)"),func:function(){k.getSelectedHdaCollection().promoteToHistoryDatasetCollection(k.model,"paired")}});l.push({html:a("Build List of Dataset Pairs (Experimental)"),func:_.bind(k._showPairedCollectionModal,k)});return new PopupMenu(j.find(".history-dataset-action-popup-btn"),l)},_showPairedCollectionModal:function(){var j=this,k=j.getSelectedHdaCollection().toJSON().filter(function(l){return l.history_content_type==="dataset"&&l.state===f.HistoryDatasetAssociation.STATES.OK});if(k.length){require(["mvc/collection/paired-collection-creator"],function(l){window.creator=l.pairedCollectionCreatorModal(k,{historyId:j.model.id})})}else{}},_handleHdaDeletionChange:function(j){if(j.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[j.id])}},_handleHdaVisibleChange:function(j){if(j.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[j.id])}},_createContentView:function(k){var j=k.id,m=k.get("history_content_type"),l=null;if(m==="dataset"){l=new this.HDAViewClass({model:k,linkTarget:this.linkTarget,expanded:this.storage.get("expandedHdas")[j],selectable:this.selecting,purgeAllowed:this.purgeAllowed,hasUser:this.model.ownedByCurrUser(),logger:this.logger,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)})}else{if(m==="dataset_collection"){l=new b.DatasetCollectionEditView({model:k,linkTarget:this.linkTarget,expanded:this.storage.get("expandedHdas")[j],hasUser:this.model.ownedByCurrUser(),logger:this.logger})}}this._setUpHdaListeners(l);return l},_setUpHdaListeners:function(k){var j=this;c.ReadOnlyHistoryPanel.prototype._setUpHdaListeners.call(this,k);k.on("selected",function(n,m){if(!m){return}var l=[];if((m.shiftKey)&&(j.lastSelectedViewId&&_.has(j.hdaViews,j.lastSelectedViewId))){var p=j.hdaViews[j.lastSelectedViewId];l=j.selectDatasetRange(n,p).map(function(q){return q.model.id})}else{var o=n.model.id;j.lastSelectedViewId=o;l=[o]}j.selectedHdaIds=_.union(j.selectedHdaIds,l)});k.on("de-selected",function(m,l){var n=m.model.id;j.selectedHdaIds=_.without(j.selectedHdaIds,n)})},toggleHDATagEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.tagsEditor){l.tagsEditor.toggle.apply(l.tagsEditor,k)}})},toggleHDAAnnotationEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.annotationEditor){l.annotationEditor.toggle.apply(l.annotationEditor,k)}})},removeHdaView:function(k){if(!k){return}var j=this;k.$el.fadeOut(j.fxSpeed,function(){k.off();k.remove();delete j.hdaViews[k.model.id];if(_.isEmpty(j.hdaViews)){j.trigger("empty-history",j);j._renderEmptyMsg()}})},events:_.extend(_.clone(c.ReadOnlyHistoryPanel.prototype.events),{"click .history-select-btn":"toggleSelectors","click .history-select-all-datasets-btn":"selectAllDatasets","click .history-deselect-all-datasets-btn":"deselectAllDatasets"}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},showSelectors:function(j){j=(j!==undefined)?(j):(this.fxSpeed);this.selecting=true;this.$(".history-dataset-actions").slideDown(j);_.each(this.hdaViews,function(k){k.showSelector()});this.selectedHdaIds=[];this.lastSelectedViewId=null},hideSelectors:function(j){j=(j!==undefined)?(j):(this.fxSpeed);this.selecting=false;this.$(".history-dataset-actions").slideUp(j);_.each(this.hdaViews,function(k){k.hideSelector()});this.selectedHdaIds=[];this.lastSelectedViewId=null},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAllDatasets:function(j){_.each(this.hdaViews,function(k){k.select(j)})},deselectAllDatasets:function(j){this.lastSelectedViewId=null;_.each(this.hdaViews,function(k){k.deselect(j)})},selectDatasetRange:function(l,k){var j=this.hdaViewRange(l,k);_.each(j,function(m){m.select()});return j},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(j){return j.selected})},getSelectedHdaCollection:function(){return new g.HistoryContents(_.map(this.getSelectedHdaViews(),function(j){return j.model}),{historyId:this.model.id})},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanel:e}});
\ No newline at end of file
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/packed/viz/trackster/tracks.js
--- a/static/scripts/packed/viz/trackster/tracks.js
+++ b/static/scripts/packed/viz/trackster/tracks.js
@@ -1,1 +1,1 @@
-define(["libs/underscore","viz/visualization","viz/trackster/util","viz/trackster/slotting","viz/trackster/painters","viz/trackster/filters","mvc/data","mvc/tools","utils/config"],function(aa,w,j,s,J,h,W,P,R){var o=aa.extend;var l={};var i=function(ab,ac){l[ab.attr("id")]=ac};var k=function(ab,ac,ae,ad){ae=".group";l[ab.attr("id")]=ad;ab.bind("drag",{handle:"."+ac,relative:true},function(am,an){var al=$(this),aq=$(this).parent(),ai=aq.children(".track,.group"),ak=l[$(this).attr("id")],ah,ag,ao,af,aj;ag=$(this).parents(ae);if(ag.length!==0){ao=ag.position().top;af=ao+ag.outerHeight();var ap=l[ag.attr("id")];if(an.offsetY<ao){$(this).insertBefore(ag);ap.remove_drawable(ak);ap.container.add_drawable_before(ak,ap);return}else{if(an.offsetY>af){$(this).insertAfter(ag);ap.remove_drawable(ak);ap.container.add_drawable(ak);return}}}ag=null;for(aj=0;aj<ai.length;aj++){ah=$(ai.get(aj));ao=ah.position().top;af=ao+ah.outerHeight();if(ah.is(ae)&&this!==ah.get(0)&&an.offsetY>=ao&&an.offsetY<=af){if(an.offsetY-ao<af-an.offsetY){ah.find(".content-div").prepend(this)}else{ah.find(".content-div").append(this)}if(ak.container){ak.container.remove_drawable(ak)}l[ah.attr("id")].add_drawable(ak);return}}for(aj=0;aj<ai.length;aj++){ah=$(ai.get(aj));if(an.offsetY<ah.position().top&&!(ah.hasClass("reference-track")||ah.hasClass("intro"))){break}}if(aj===ai.length){if(this!==ai.get(aj-1)){aq.append(this);l[aq.attr("id")].move_drawable(ak,aj)}}else{if(this!==ai.get(aj)){$(this).insertBefore(ai.get(aj));l[aq.attr("id")].move_drawable(ak,(an.deltaY>0?aj-1:aj))}}}).bind("dragstart",function(){$(this).addClass("dragging")}).bind("dragend",function(){$(this).removeClass("dragging")})};var D=20,z=100,G=12000,Q=400,I=5000,v=100,m="Cannot display dataset due to an error. ",H="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",E="No data for this chrom/contig.",u="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",x="Tool cannot be rerun: ",a="Loading data...",T="Ready for display",O=10,F=20,A=["Histogram","Line","Filled","Intensity"];function U(ac,ab){if(!ab){ab=0}var ad=Math.pow(10,ab);return Math.round(ac*ad)/ad}var p=function(ac,ab,ae){if(!p.id_counter){p.id_counter=0}this.id=p.id_counter++;this.view=ac;this.container=ab;this.drag_handle_class=ae.drag_handle_class;this.is_overview=false;this.action_icons={};this.config=R.ConfigSettingCollection.from_models_and_saved_values(this.config_params,ae.prefs);if(!this.config.get_value("name")){this.config.set_value("name",ae.name)}if(this.config_onchange){this.config.on("change",this.config_onchange,this)}this.container_div=this.build_container_div();this.header_div=this.build_header_div();if(this.header_div){this.container_div.append(this.header_div);this.icons_div=$("<div/>").addClass("track-icons").hide().appendTo(this.header_div);this.build_action_icons(this.action_icons_def);this.header_div.append($("<div style='clear: both'/>"));this.header_div.dblclick(function(af){af.stopPropagation()});var ad=this;this.container_div.hover(function(){ad.icons_div.show()},function(){ad.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};p.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(ab){if(ab.config.get_value("content_visible")){ab.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");ab.hide_contents();ab.config.set_value("content_visible",false)}else{ab.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");ab.config.set_value("content_visible",true);ab.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"gear",on_click_fn:function(ac){var ab=new R.ConfigSettingCollectionView({collection:ac.config});ab.render_in_modal("Configure Track")}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(ab){$(".tooltip").remove();ab.remove()}}];o(p.prototype,{config_params:[{key:"name",label:"Name",type:"text",default_value:""},{key:"content_visible",type:"bool",default_value:true,hidden:true}],config_onchange:function(){},init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.config.get_value("content_visible")){return true}return false},request_draw:function(){},_draw:function(ab){},to_dict:function(){},set_name:function(ab){this.old_name=this.config.get_value("name");this.config.set_value("name",ab);this.name_div.text(ab)},revert_name:function(){if(this.old_name){this.config.set_value("name",this.old_name);this.name_div.text(this.old_name)}},remove:function(){this.changed();this.container.remove_drawable(this);var ab=this.view;this.container_div.hide(0,function(){$(this).remove();ab.update_intro_div()})},build_container_div:function(){},build_header_div:function(){},add_action_icon:function(ac,ah,ag,af,ab,ae){var ad=this;this.action_icons[ac]=$("<a/>").attr("title",ah).addClass("icon-button").addClass(ag).tooltip().click(function(){af(ad)}).appendTo(this.icons_div);if(ae){this.action_icons[ac].hide()}},build_action_icons:function(ab){var ad;for(var ac=0;ac<ab.length;ac++){ad=ab[ac];this.add_action_icon(ad.name,ad.title,ad.css_class,ad.on_click_fn,ad.prepend,ad.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){},get_drawables:function(){}});var y=function(ac,ab,ad){p.call(this,ac,ab,ad);this.obj_type=ad.obj_type;this.drawables=[]};o(y.prototype,p.prototype,{unpack_drawables:function(ad){this.drawables=[];var ac;for(var ab=0;ab<ad.length;ab++){ac=n(ad[ab],this.view,this);this.add_drawable(ac)}},init:function(){for(var ab=0;ab<this.drawables.length;ab++){this.drawables[ab].init()}},_draw:function(ab){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac]._draw(ab)}},to_dict:function(){var ac=[];for(var ab=0;ab<this.drawables.length;ab++){ac.push(this.drawables[ab].to_dict())}return{prefs:this.config.to_key_value_dict(),obj_type:this.obj_type,drawables:ac}},add_drawable:function(ab){this.drawables.push(ab);ab.container=this;this.changed()},add_drawable_before:function(ad,ab){this.changed();var ac=this.drawables.indexOf(ab);if(ac!==-1){this.drawables.splice(ac,0,ad);return true}return false},replace_drawable:function(ad,ab,ac){var ae=this.drawables.indexOf(ad);if(ae!==-1){this.drawables[ae]=ab;if(ac){ad.container_div.replaceWith(ab.container_div)}this.changed()}return ae},remove_drawable:function(ac){var ab=this.drawables.indexOf(ac);if(ab!==-1){this.drawables.splice(ab,1);ac.container=null;this.changed();return true}return false},move_drawable:function(ac,ad){var ab=this.drawables.indexOf(ac);if(ab!==-1){this.drawables.splice(ab,1);this.drawables.splice(ad,0,ac);this.changed();return true}return false},get_drawables:function(){return this.drawables},get_tracks:function(ae){var ab=this.drawables.slice(0),ac=[],ad;while(ab.length!==0){ad=ab.shift();if(ad instanceof ae){ac.push(ad)}else{if(ad.drawables){ab=ab.concat(ad.drawables)}}}return ac}});var N=function(ac,ab,ae){o(ae,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});y.call(this,ac,ab,ae);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);i(this.container_div,this);i(this.content_div,this);k(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new h.FiltersManager(this);this.header_div.after(this.filters_manager.parent_div);this.saved_filters_managers=[];if("drawables" in ae){this.unpack_drawables(ae.drawables)}if("filters" in ae){var ad=this.filters_manager;this.filters_manager=new h.FiltersManager(this,ae.filters);ad.parent_div.replaceWith(this.filters_manager.parent_div);if(ae.filters.visible){this.setup_multitrack_filtering()}}};o(N.prototype,p.prototype,y.prototype,{action_icons_def:[p.prototype.action_icons_def[0],p.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(ab){$(".tooltip").remove();ab.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(ab){if(ab.filters_manager.visible()){ab.filters_manager.clear_filters();ab._restore_filter_managers()}else{ab.setup_multitrack_filtering();ab.request_draw({clear_tile_cache:true})}ab.filters_manager.toggle()}},p.prototype.action_icons_def[2]],build_container_div:function(){var ab=$("<div/>").addClass("group").attr("id","group_"+this.id);if(this.container){this.container.content_div.append(ab)}return ab},build_header_div:function(){var ab=$("<div/>").addClass("track-header");ab.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.config.get_value("name")).appendTo(ab);return ab},hide_contents:function(){this.tiles_div.hide()},show_contents:function(){this.tiles_div.show();this.request_draw()},update_icons:function(){var ad=this.drawables.length;if(ad===0){this.action_icons.composite_icon.hide();this.action_icons.filters_icon.hide()}else{if(ad===1){if(this.drawables[0] instanceof e){this.action_icons.composite_icon.show()}this.action_icons.filters_icon.hide()}else{var ak,aj,ah,an=true,af=this.drawables[0].get_type(),ab=0;for(ak=0;ak<ad;ak++){ah=this.drawables[ak];if(ah.get_type()!==af){can_composite=false;break}if(ah instanceof c){ab++}}if(an&&this.drawables[0] instanceof g){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".tooltip").remove()}if(ab>1&&ab===this.drawables.length){var ao={},ac;ah=this.drawables[0];for(aj=0;aj<ah.filters_manager.filters.length;aj++){ac=ah.filters_manager.filters[aj];ao[ac.name]=[ac]}for(ak=1;ak<this.drawables.length;ak++){ah=this.drawables[ak];for(aj=0;aj<ah.filters_manager.filters.length;aj++){ac=ah.filters_manager.filters[aj];if(ac.name in ao){ao[ac.name].push(ac)}}}this.filters_manager.remove_all();var ae,ag,ai,al;for(var am in ao){ae=ao[am];if(ae.length===ab){ag=new h.NumberFilter({name:ae[0].name,index:ae[0].index});this.filters_manager.add_filter(ag)}}if(this.filters_manager.filters.length>0){this.action_icons.filters_icon.show()}else{this.action_icons.filters_icon.hide()}}else{this.action_icons.filters_icon.hide()}}}},_restore_filter_managers:function(){for(var ab=0;ab<this.drawables.length;ab++){this.drawables[ab].filters_manager=this.saved_filters_managers[ab]}this.saved_filters_managers=[]},setup_multitrack_filtering:function(){if(this.filters_manager.filters.length>0){this.saved_filters_managers=[];for(var ab=0;ab<this.drawables.length;ab++){drawable=this.drawables[ab];this.saved_filters_managers.push(drawable.filters_manager);drawable.filters_manager=this.filters_manager}}this.filters_manager.init_filters()},show_composite_track:function(){var ac=new e(this.view,this.view,{name:this.config.get_value("name"),drawables:this.drawables});var ab=this.container.replace_drawable(this,ac,true);ac.request_draw()},add_drawable:function(ab){y.prototype.add_drawable.call(this,ab);this.update_icons()},remove_drawable:function(ab){y.prototype.remove_drawable.call(this,ab);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var ab=o(y.prototype.to_dict.call(this),{filters:this.filters_manager.to_dict()});if(this.filters_manager.visible()){this.setup_multitrack_filtering()}return ab},request_draw:function(ab){aa.each(this.drawables,function(ac){ac.request_draw(ab)})}});var X=Backbone.View.extend({initialize:function(ab){o(ab,{obj_type:"View"});y.call(this,"View",ab.container,ab);this.chrom=null;this.vis_id=ab.vis_id;this.dbkey=ab.dbkey;this.label_tracks=[];this.tracks_to_be_redrawn=[];this.max_low=0;this.max_high=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.load_chroms_deferred=null;this.render();this.canvas_manager=new w.CanvasManager(this.container.get(0).ownerDocument);this.reset();this.config=R.ConfigSettingCollection.from_models_and_saved_values([{key:"name",label:"Name",type:"text",default_value:""},{key:"a_color",label:"A Color",type:"color",default_value:"#FF0000"},{key:"c_color",label:"C Color",type:"color",default_value:"#00FF00"},{key:"g_color",label:"G Color",type:"color",default_value:"#0000FF"},{key:"t_color",label:"T Color",type:"color",default_value:"#FF00FF"},{key:"n_color",label:"N Color",type:"color",default_value:"#AAAAAA"}],{name:ab.name})},render:function(){this.requested_redraw=false;var ad=this.container,ab=this;this.top_container=$("<div/>").addClass("top-container").appendTo(ad);this.browser_content_div=$("<div/>").addClass("content").appendTo(ad);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(ad);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").attr("id","viewport-container").appendTo(this.browser_content_div);this.content_div=this.viewport_container;i(this.viewport_container,ab);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var ae=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){w.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":ab.dbkey},function(af){aa.each(af,function(ag){ab.add_drawable(n(ag,ab,ab))})})});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("trackster-nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("trackster-nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a/>").attr("title","Close overview").addClass("icon-button overview-close tooltip").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div/>").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_select=$("<select/>").attr({name:"chrom"}).addClass("chrom-nav").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ac=function(af){if(af.type==="focusout"||(af.keyCode||af.which)===13||(af.keyCode||af.which)===27){if((af.keyCode||af.which)!==27){ab.go_to($(this).val())}$(this).hide();$(this).val("");ab.location_span.show();ab.chrom_select.show()}af.stopPropagation()};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ac).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("title","Click to change location").tooltip({placement:"bottom"}).appendTo(this.nav_controls);this.location_span.click(function(){ab.location_span.hide();ab.chrom_select.hide();ab.nav_input.val(ab.chrom+":"+ab.low+"-"+ab.high);ab.nav_input.css("display","inline-block");ab.nav_input.select();ab.nav_input.focus();ab.nav_input.autocomplete({source:function(ah,af){var ai=[],ag=$.map(ab.get_tracks(c),function(aj){return aj.data_manager.search_features(ah.term).success(function(ak){ai=ai.concat(ak)})});$.when.apply($,ag).done(function(){af($.map(ai,function(aj){return{label:aj[0],value:aj[1]}}))})},minLength:2})});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.nav_controls)}this.zo_link=$("<a/>").attr("id","zoom-out").attr("title","Zoom out").tooltip({placement:"bottom"}).click(function(){ab.zoom_out()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tooltip({placement:"bottom"}).click(function(){ab.zoom_in()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){ab.change_chrom(ab.chrom_select.val())});this.browser_content_div.click(function(af){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(af){ab.zoom_in(af.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(af,ag){this.current_x=ag.offsetX}).bind("drag",function(af,ah){var ai=ah.offsetX-this.current_x;this.current_x=ah.offsetX;var ag=Math.round(ai/ab.viewport_container.width()*(ab.max_high-ab.max_low));ab.move_delta(-ag)});this.overview_close.click(function(){ab.reset_overview()});this.viewport_container.bind("draginit",function(af,ag){if(af.clientX>ab.viewport_container.width()-16){return false}}).bind("dragstart",function(af,ag){ag.original_low=ab.low;ag.current_height=af.clientY;ag.current_x=ag.offsetX}).bind("drag",function(ah,aj){var af=$(this);var ak=aj.offsetX-aj.current_x;var ag=af.scrollTop()-(ah.clientY-aj.current_height);af.scrollTop(ag);aj.current_height=ah.clientY;aj.current_x=aj.offsetX;var ai=Math.round(ak/ab.viewport_container.width()*(ab.high-ab.low));ab.move_delta(ai)});this.top_labeltrack.bind("dragstart",function(af,ag){return $("<div/>").addClass("zoom-area").css("height",ab.browser_content_div.height()+ab.top_labeltrack.height()+ab.nav_labeltrack.height()+1).appendTo($(this))}).bind("drag",function(aj,ak){$(ak.proxy).css({left:Math.min(aj.pageX,ak.startX)-ab.container.offset().left,width:Math.abs(aj.pageX-ak.startX)});var ag=Math.min(aj.pageX,ak.startX)-ab.container.offset().left,af=Math.max(aj.pageX,ak.startX)-ab.container.offset().left,ai=(ab.high-ab.low),ah=ab.viewport_container.width();ab.update_location(Math.round(ag/ah*ai)+ab.low,Math.round(af/ah*ai)+ab.low)}).bind("dragend",function(ak,al){var ag=Math.min(ak.pageX,al.startX),af=Math.max(ak.pageX,al.startX),ai=(ab.high-ab.low),ah=ab.viewport_container.width(),aj=ab.low;ab.low=Math.round(ag/ah*ai)+aj;ab.high=Math.round(af/ah*ai)+aj;$(al.proxy).remove();ab.request_redraw()});this.add_label_track(new V(this,{content_div:this.top_labeltrack}));this.add_label_track(new V(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){if(this.resize_timer){clearTimeout(this.resize_timer)}this.resize_timer=setTimeout(function(){ab.resize_window()},500)});$(document).bind("redraw",function(){ab.redraw()});this.reset();$(window).trigger("resize")},get_base_color:function(ab){return this.config.get_value(ab.toLowerCase()+"_color")||this.config.get_value("n_color")}});o(X.prototype,y.prototype,{changed:function(){this.has_changes=true},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},trigger_navigate:function(ac,ae,ab,af){if(this.timer){clearTimeout(this.timer)}if(af){var ad=this;this.timer=setTimeout(function(){ad.trigger("navigate",ac+":"+ae+"-"+ab)},500)}else{view.trigger("navigate",ac+":"+ae+"-"+ab)}},update_location:function(ab,ad){this.location_span.text(commatize(ab)+" - "+commatize(ad));this.nav_input.val(this.chrom+":"+commatize(ab)+"-"+commatize(ad));var ac=view.chrom_select.val();if(ac!==""){this.trigger_navigate(ac,view.low,view.high,true)}},load_chroms:function(ad){ad.num=v;var ab=this,ac=$.Deferred();$.ajax({url:galaxy_config.root+"api/genomes/"+this.dbkey,data:ad,dataType:"json",success:function(af){if(af.chrom_info.length===0){return}if(af.reference){var ag=new B(ab);ab.add_label_track(ag);ab.reference_track=ag}ab.chrom_data=af.chrom_info;var aj='<option value="">Select Chrom/Contig</option>';for(var ai=0,ae=ab.chrom_data.length;ai<ae;ai++){var ah=ab.chrom_data[ai].chrom;aj+='<option value="'+ah+'">'+ah+"</option>"}if(af.prev_chroms){aj+='<option value="previous">Previous '+v+"</option>"}if(af.next_chroms){aj+='<option value="next">Next '+v+"</option>"}ab.chrom_select.html(aj);ab.chrom_start_index=af.start_index;ac.resolve(af.chrom_info)},error:function(){alert("Could not load chroms for this dbkey: "+ab.dbkey)}});return ac},change_chrom:function(ag,ac,ai){var ad=this;if(!ad.chrom_data){ad.load_chroms_deferred.then(function(){ad.change_chrom(ag,ac,ai)});return}if(!ag||ag==="None"){return}if(ag==="previous"){ad.load_chroms({low:this.chrom_start_index-v});return}if(ag==="next"){ad.load_chroms({low:this.chrom_start_index+v});return}var ah=$.grep(ad.chrom_data,function(aj,ak){return aj.chrom===ag})[0];if(ah===undefined){ad.load_chroms({chrom:ag},function(){ad.change_chrom(ag,ac,ai)});return}else{if(ag!==ad.chrom){ad.chrom=ag;ad.chrom_select.val(ad.chrom);ad.max_high=ah.len-1;ad.reset();for(var af=0,ab=ad.drawables.length;af<ab;af++){var ae=ad.drawables[af];if(ae.init){ae.init()}}if(ad.reference_track){ad.reference_track.init()}}if(ac===undefined&&ai===undefined){ad.low=0;ad.high=ad.max_high}else{ad.low=(ac!==undefined?Math.max(ac,0):0);if(ai===undefined){ad.low=Math.max(ad.low-15,0);ad.high=ad.low+30}else{ad.high=Math.min(ai,ad.max_high)}}ad.reset_overview();ad.request_redraw()}},go_to:function(af){af=af.replace(/,/g,"");af=af.replace(/:|\-/g," ");var ac=af.split(/\s+/),ae=ac[0],ad=(ac[1]?parseInt(ac[1],10):undefined),ab=(ac[2]?parseInt(ac[2],10):undefined);this.change_chrom(ae,ad,ab)},move_fraction:function(ad){var ab=this;var ac=ab.high-ab.low;this.move_delta(ad*ac)},move_delta:function(ae){var ab=this;var ad=ab.high-ab.low;if(ab.low-ae<ab.max_low){ab.low=ab.max_low;ab.high=ab.max_low+ad}else{if(ab.high-ae>ab.max_high){ab.high=ab.max_high;ab.low=ab.max_high-ad}else{ab.high-=ae;ab.low-=ae}}ab.request_redraw({data_fetch:false});if(this.redraw_on_move_fn){clearTimeout(this.redraw_on_move_fn)}this.redraw_on_move_fn=setTimeout(function(){ab.request_redraw()},200);var ac=ab.chrom_select.val();this.trigger_navigate(ac,ab.low,ab.high,true)},add_drawable:function(ab){y.prototype.add_drawable.call(this,ab);ab.init();this.changed();this.update_intro_div()},add_label_track:function(ab){ab.view=this;ab.init();this.label_tracks.push(ab)},remove_drawable:function(ad,ac){y.prototype.remove_drawable.call(this,ad);if(ac){var ab=this;ad.container_div.hide(0,function(){$(this).remove();ab.update_intro_div()})}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(ac,ad){var ab=this,ae=(ad?[ad]:ab.drawables);aa.each(ae,function(af){var ag=aa.find(ab.tracks_to_be_redrawn,function(ah){return ah[0]===af});if(ag){ag[1]=ac}else{ab.tracks_to_be_redrawn.push([af,ac])}});if(!this.requested_redraw){requestAnimationFrame(function(){ab._redraw()});this.requested_redraw=true}},_redraw:function(){this.requested_redraw=false;var ab=this.low,af=this.high;if(ab<this.max_low){ab=this.max_low}if(af>this.max_high){af=this.max_high}var ac=this.high-this.low;if(this.high!==0&&ac<this.min_separation){af=ab+this.min_separation}this.low=Math.floor(ab);this.high=Math.ceil(af);this.update_location(this.low,this.high);this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var ae=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ag=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ad=13;this.overview_box.css({left:ae,width:Math.max(ad,ag)}).show();if(ag<ad){this.overview_box.css("left",ae-(ad-ag)/2)}if(this.overview_highlight){this.overview_highlight.css({left:ae,width:ag})}aa.each(this.tracks_to_be_redrawn,function(aj){var ah=aj[0],ai=aj[1];if(ah){ah._draw(ai)}});this.tracks_to_be_redrawn=[];aa.each(this.label_tracks,function(ah){ah._draw()})},zoom_in:function(ac,ad){if(this.max_high===0||this.high-this.low<=this.min_separation){return}var ae=this.high-this.low,af=ae/2+this.low,ab=(ae/this.zoom_factor)/2;if(ac){af=ac/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(af-ab);this.high=Math.round(af+ab);this.changed();this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ac=this.high-this.low,ad=ac/2+this.low,ab=(ac*this.zoom_factor)/2;this.low=Math.round(ad-ab);this.high=Math.round(ad+ab);this.changed();this.request_redraw()},resize_viewport:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height())},resize_window:function(){this.resize_viewport();this.request_redraw()},set_overview:function(ad){if(this.overview_drawable){if(this.overview_drawable.dataset.id===ad.dataset.id){return}this.overview_viewport.find(".track").remove()}var ac=ad.copy({content_div:this.overview_viewport}),ab=this;ac.header_div.hide();ac.is_overview=true;ab.overview_drawable=ac;this.overview_drawable.postdraw_actions=function(){ab.overview_highlight.show().height(ab.overview_drawable.content_div.height());ab.overview_viewport.height(ab.overview_drawable.content_div.height()+ab.overview_box.outerHeight());ab.overview_close.show();ab.resize_window()};ab.overview_drawable.request_draw();this.changed()},reset_overview:function(){$(".tooltip").remove();this.overview_viewport.find(".track-tile").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide();view.resize_window();view.overview_drawable=null}});var q=P.Tool.extend({defaults:{track:null},initialize:function(ab){P.Tool.prototype.initialize.call(this,ab);var ac=true;if(ab.tool_state!==undefined&&ab.tool_state.hidden!==undefined){ac=ab.tool_state.hidden}this.set("hidden",ac);this.remove_inputs(["data","hidden_data","conditional"])},state_dict:function(ab){return aa.extend(this.get_inputs_dict(),{hidden:!this.is_visible()})}});var t=Backbone.View.extend({events:{"change :input":"update_value"},render:function(){var ad=this.$el.addClass("param-row"),ae=this.model;var ab=$("<div>").addClass("param-label").text(ae.get("label")).appendTo(ad);var ac=$("<div/>").addClass("param-input").html(ae.get("html")).appendTo(ad);ac.find(":input").val(ae.get("value"));$("<div style='clear: both;'/>").appendTo(ad)},update_value:function(ab){this.model.set_value($(ab.target).val())}});var Z=Backbone.View.extend({initialize:function(ab){this.model.on("change:hidden",this.set_visible,this)},render:function(){var ac=this;tool=this.model,parent_div=this.$el.addClass("dynamic-tool").hide();parent_div.bind("drag",function(ag){ag.stopPropagation()}).click(function(ag){ag.stopPropagation()}).bind("dblclick",function(ag){ag.stopPropagation()}).keydown(function(ag){ag.stopPropagation()});var ad=$("<div class='tool-name'>").appendTo(parent_div).text(tool.get("name"));tool.get("inputs").each(function(ah){var ag=new t({model:ah});ag.render();parent_div.append(ag.$el)});parent_div.find("input").click(function(){$(this).select()});var ae=$("<div>").addClass("param-row").appendTo(parent_div);var af=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(ae);var ab=$("<input type='submit'>").attr("value","Run on visible region").appendTo(ae);ab.click(function(){ac.run_on_region()});af.click(function(){ac.run_on_dataset()});if(tool.is_visible()){this.$el.show()}},set_visible:function(){if(this.model.is_visible()){this.$el.show()}else{this.$el.hide()}},update_params:function(){for(var ab=0;ab<this.params.length;ab++){this.params[ab].update_value()}},run_on_dataset:function(){var ab=this.model;this.run({target_dataset_id:this.model.get("track").dataset.id,action:"rerun",tool_id:ab.id},null,function(ac){Galaxy.modal.show({title:ab.get("name")+" is Running",body:ab.get("name")+" is running on the complete dataset. Tool outputs are in dataset's history.",buttons:{Close:function(){Galaxy.modal.hide()}}})})},run_on_region:function(){var ac=this.model.get("track"),af=this.model,ah=new w.GenomeRegion({chrom:ac.view.chrom,start:ac.view.low,end:ac.view.high}),ai={target_dataset_id:ac.dataset.id,action:"rerun",tool_id:af.id,regions:[ah.toJSON()]},ag=ac,ak=af.get("name")+ag.tool_region_and_parameters_str(ah),ab;if(ag.container===view){var aj=new N(view,view,{name:ac.config.get_value("name")});var ae=ag.container.replace_drawable(ag,aj,false);aj.container_div.insertBefore(ag.view.content_div.children()[ae]);aj.add_drawable(ag);ag.container_div.appendTo(aj.content_div);ab=aj}else{ab=ag.container}var ad=new ag.constructor(view,ab,{name:ak,hda_ldda:"hda"});ad.init_for_tool_data();ad.change_mode(ag.mode);ad.set_filters_manager(ag.filters_manager.copy(ad));ad.update_icons();ab.add_drawable(ad);ad.tiles_div.text("Starting job.");this.run(ai,ad,function(al){ad.set_dataset(new W.Dataset(al));ad.tiles_div.text("Running job.");ad.init()})},run:function(ab,ad,ae){ab.inputs=this.model.get_inputs_dict();var ac=new j.ServerStateDeferred({ajax_settings:{url:galaxy_config.root+"api/tools",data:JSON.stringify(ab),dataType:"json",contentType:"application/json",type:"POST"},interval:2000,success_fn:function(af){return af!=="pending"}});$.when(ac.go()).then(function(af){if(af==="no converter"){ad.container_div.addClass("error");ad.content_div.text(H)}else{if(af.error){ad.container_div.addClass("error");ad.content_div.text(x+af.message)}else{ae(af)}}})}});var C=function(ab,ac){J.Scaler.call(this,ac);this.filter=ab};C.prototype.gen_val=function(ab){if(this.filter.high===Number.MAX_VALUE||this.filter.low===-Number.MAX_VALUE||this.filter.low===this.filter.high){return this.default_val}return((parseFloat(ab[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var b=function(ab,ae,af,ac,ad){this.track=ab;this.region=ae;this.low=ae.get("start");this.high=ae.get("end");this.w_scale=af;this.canvas=ac;this.html_elt=$("<div class='track-tile'/>").append(ac);this.data=ad;this.stale=false};b.prototype.predisplay_actions=function(){};var K=function(ab,ae,af,ac,ad){b.call(this,ab,ae,af,ac,ad)};K.prototype.predisplay_actions=function(){};var M=function(ad,ai,ak,ac,af,ag,al,ab,ah,ae,aj){b.call(this,ad,ai,ak,ac,af);this.mode=ag;this.all_slotted=ab;this.feature_mapper=ah;this.has_icons=false;this.incomplete_features=ae;this.seq_data=aj};o(M.prototype,b.prototype);M.prototype.predisplay_actions=function(){var ac=this,ab={};if(ac.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()}).mousemove(function(an){if(!this.hovered){return}var ai=$(this).offset(),am=an.pageX-ai.left,al=an.pageY-ai.top,ar=ac.feature_mapper.get_feature_data(am,al),aj=(ar?ar[0]:null);$(this).parents(".track-content").children(".overlay").children(".feature-popup").each(function(){if(!aj||$(this).attr("id")!==aj.toString()){$(this).remove()}});if(ar){var ae=ab[aj];if(!ae){var ao={name:ar[3],start:ar[1],end:ar[2],strand:ar[4]},ah=ac.track.filters_manager.filters,ag;for(var ak=0;ak<ah.length;ak++){ag=ah[ak];ao[ag.name]=ar[ag.index]}ae=$("<div/>").attr("id",aj).addClass("feature-popup");var at=$("<table/>"),aq,ap,au;for(aq in ao){ap=ao[aq];au=$("<tr/>").appendTo(at);$("<th/>").appendTo(au).text(aq);$("<td/>").attr("align","left").appendTo(au).text(typeof(ap)==="number"?U(ap,2):ap)}ae.append($("<div class='feature-popup-inner'>").append(at));ab[aj]=ae}ae.appendTo($(this).parents(".track-content").children(".overlay"));var af=am+parseInt(ac.html_elt.css("left"),10)-ae.width()/2,ad=al+parseInt(ac.html_elt.css("top"),10)+7;ae.css("left",af+"px").css("top",ad+"px")}else{if(!an.isPropagationStopped()){an.stopPropagation();$(this).siblings().each(function(){$(this).trigger(an)})}}}).mouseleave(function(){$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()})};var f=function(ac,ab,ad){o(ad,{drag_handle_class:"draghandle"});p.call(this,ac,ab,ad);this.dataset=null;if(ad.dataset){this.dataset=(ad.dataset instanceof Backbone.Model?ad.dataset:new W.Dataset(ad.dataset))}this.dataset_check_type="converted_datasets_state";this.data_url_extra_params={};this.data_query_wait=("data_query_wait" in ad?ad.data_query_wait:I);this.data_manager=("data_manager" in ad?ad.data_manager:new w.GenomeDataManager({dataset:this.dataset,genome:new w.Genome({key:ac.dbkey,chroms_info:{chrom_info:ac.chrom_data}}),data_mode_compatible:this.data_and_mode_compatible,can_subset:this.can_subset}));this.min_height_px=16;this.max_height_px=800;this.visible_height_px=this.config.get_value("height");this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div);if(!("resize" in ad)||ad.resize){this.add_resize_handle()}}};o(f.prototype,p.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},p.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"application-dock-270",on_click_fn:function(ab){ab.view.set_overview(ab)}},p.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"ui-slider-050",on_click_fn:function(ab){if(ab.filters_manager.visible()){ab.filters_manager.clear_filters()}else{ab.filters_manager.init_filters()}ab.filters_manager.toggle()}},{name:"tools_icon",title:"Tool",css_class:"hammer",on_click_fn:function(ab){ab.tool.toggle();if(ab.tool.is_visible()){ab.set_name(ab.config.get_value("name")+ab.tool_region_and_parameters_str())}else{ab.revert_name()}$(".tooltip").remove()}},{name:"param_space_viz_icon",title:"Tool parameter space visualization",css_class:"arrow-split",on_click_fn:function(ab){var ae='<strong>Tool</strong>: <%= track.tool.get("name") %><br/><strong>Dataset</strong>: <%= track.config.get_value("name") %><br/><strong>Region(s)</strong>: <select name="regions"><option value="cur">current viewing area</option><option value="bookmarks">bookmarks</option><option value="both">current viewing area and bookmarks</option></select>',ad=aa.template(ae,{track:ab});var ag=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},ac=function(){var ai=$('select[name="regions"] option:selected').val(),ak,ah=new w.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),aj=aa.map($(".bookmark"),function(al){return new w.GenomeRegion({from_str:$(al).children(".position").text()})});if(ai==="cur"){ak=[ah]}else{if(ai==="bookmarks"){ak=aj}else{ak=[ah].concat(aj)}}Galaxy.modal.hide();window.location.href=galaxy_config.root+"visualization/sweepster?"+$.param({dataset_id:ab.dataset.id,hda_ldda:ab.dataset.get("hda_ldda"),regions:JSON.stringify(new Backbone.Collection(ak).toJSON())})},af=function(ah){if((ah.keyCode||ah.which)===27){ag()}else{if((ah.keyCode||ah.which)===13){ac()}}};Galaxy.modal.show({title:"Visualize tool parameter space and output from different parameter settings?",body:ad,buttons:{No:ag,Yes:ac}})}},p.prototype.action_icons_def[2]],can_draw:function(){return this.dataset&&p.prototype.can_draw.call(this)},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id)},build_header_div:function(){var ab=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(ab)}this.name_div=$("<div/>").addClass("track-name").appendTo(ab).text(this.config.get_value("name")).attr("id",this.config.get_value("name").replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return ab},set_dataset:function(ab){this.dataset=ab;this.data_manager.set("dataset",ab)},on_resize:function(){this.request_draw({clear_tile_cache:true})},add_resize_handle:function(){var ab=this;var ae=false;var ad=false;var ac=$("<div class='track-resize'>");$(ab.container_div).hover(function(){if(ab.config.get_value("content_visible")){ae=true;ac.show()}},function(){ae=false;if(!ad){ac.hide()}});ac.hide().bind("dragstart",function(af,ag){ad=true;ag.original_height=$(ab.content_div).height()}).bind("drag",function(ag,ah){var af=Math.min(Math.max(ah.original_height+ah.deltaY,ab.min_height_px),ab.max_height_px);$(ab.tiles_div).css("height",af);ab.visible_height_px=(ab.max_height_px===af?0:af);ab.on_resize()}).bind("dragend",function(af,ag){ab.tile_cache.clear();ad=false;if(!ae){ac.hide()}ab.config.set_value("height",ab.visible_height_px);ab.changed()}).appendTo(ab.container_div)},set_display_modes:function(ae,ah){this.display_modes=ae;this.mode=(ah?ah:(this.config&&this.config.get_value("mode")?this.config.get_value("mode"):this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var ac=this,af={};for(var ad=0,ab=ac.display_modes.length;ad<ab;ad++){var ag=ac.display_modes[ad];af[ag]=function(ai){return function(){ac.change_mode(ai);ac.icons_div.show();ac.container_div.mouseleave(function(){ac.icons_div.hide()})}}(ag)}make_popupmenu(this.action_icons.mode_icon,af)},build_action_icons:function(){p.prototype.build_action_icons.call(this,this.action_icons_def);if(this.display_modes!==undefined){this.set_display_modes(this.display_modes)}},hide_contents:function(){this.tiles_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.tiles_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof V){return"LabelTrack"}else{if(this instanceof B){return"ReferenceTrack"}else{if(this instanceof g){return"LineTrack"}else{if(this instanceof S){return"ReadTrack"}else{if(this instanceof Y){return"VariantTrack"}else{if(this instanceof e){return"CompositeTrack"}else{if(this instanceof c){return"FeatureTrack"}}}}}}}return""},show_message:function(ab){this.tiles_div.remove();return $("<span/>").addClass("message").html(ab).appendTo(this.content_div)},init:function(ad){var ac=this;ac.enabled=false;ac.tile_cache.clear();ac.data_manager.clear();ac.content_div.children().remove();ac.container_div.removeClass("nodata error pending");ac.tiles_div=$("<div/>").addClass("tiles").appendTo(ac.content_div);if(!ac.dataset.id){return}var ab=$.Deferred(),ae={hda_ldda:ac.dataset.get("hda_ldda"),data_type:this.dataset_check_type,chrom:ac.view.chrom,retry:ad};$.getJSON(this.dataset.url(),ae,function(af){if(!af||af==="error"||af.kind==="error"){ac.container_div.addClass("error");var ag=ac.show_message(m);if(af.message){ag.append($("<a href='javascript:void(0);'></a>").text("View error").click(function(){Galaxy.modal.show({title:"Trackster Error",body:"<pre>"+af.message+"</pre>",buttons:{Close:function(){Galaxy.modal.hide()}}})}));ag.append($("<span/>").text(" "));ag.append($("<a href='javascript:void(0);'></a>").text("Try again").click(function(){ac.init(true)}))}}else{if(af==="no converter"){ac.container_div.addClass("error");ac.show_message(H)}else{if(af==="no data"||(af.data!==undefined&&(af.data===null||af.data.length===0))){ac.container_div.addClass("nodata");ac.show_message(E)}else{if(af==="pending"){ac.container_div.addClass("pending");ac.show_message(u);setTimeout(function(){ac.init()},ac.data_query_wait)}else{if(af==="data"||af.status==="data"){if(af.valid_chroms){ac.valid_chroms=af.valid_chroms;ac.update_icons()}ac.tiles_div.text(T);if(ac.view.chrom){ac.tiles_div.text("");ac.tiles_div.css("height",ac.visible_height_px+"px");ac.enabled=true;$.when.apply($,ac.predraw_init()).done(function(){ab.resolve();ac.container_div.removeClass("nodata error pending");ac.request_draw()})}else{ab.resolve()}}}}}}});this.update_icons();return ab},predraw_init:function(){var ab=this;return $.getJSON(ab.dataset.url(),{data_type:"data",stats:true,chrom:ab.view.chrom,low:0,high:ab.view.max_high,hda_ldda:ab.dataset.get("hda_ldda")},function(ac){var ae=ac.data;if(ae&&ae.min&&ae.max){var ad=ae.min,af=ae.max;ad=Math.floor(Math.min(0,Math.max(ad,ae.mean-2*ae.sd)));af=Math.ceil(Math.max(0,Math.min(af,ae.mean+2*ae.sd)));ab.config.set_default_value("min_value",ad);ab.config.set_default_value("max_value",af);ab.config.set_value("min_value",ad);ab.config.set_value("max_value",af)}})},get_drawables:function(){return this}});var L=function(ad,ac,af){f.call(this,ad,ac,af);var ab=this;k(ab.container_div,ab.drag_handle_class,".group",ab);this.filters_manager=new h.FiltersManager(this,("filters" in af?af.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=(af.tool?new q(aa.extend(af.tool,{track:this,tool_state:af.tool_state})):null);this.tile_cache=new w.Cache(O);this.left_offset=0;if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){var ae=new Z({model:this.tool});ae.render();this.dynamic_tool_div=ae.$el;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);if(!this.config.get_value("content_visible")){this.tiles_div.hide()}this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(af.mode){this.change_mode(af.mode)}};o(L.prototype,p.prototype,f.prototype,{action_icons_def:f.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(ab){$(".tooltip").remove();ab.slotters[ab.view.resolution_px_b].max_rows*=2;ab.request_draw({clear_tile_cache:true})},hide:true}]),copy:function(ab){var ac=this.to_dict();o(ac,{data_manager:this.data_manager});var ad=new this.constructor(this.view,ab,ac);ad.change_mode(this.mode);ad.enabled=this.enabled;return ad},set_filters_manager:function(ab){this.filters_manager=ab;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),dataset:{id:this.dataset.id,hda_ldda:this.dataset.get("hda_ldda")},prefs:this.config.to_key_value_dict(),mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},set_min_max:function(){var ab=this;return $.getJSON(ab.dataset.url(),{data_type:"data",stats:true,chrom:ab.view.chrom,low:0,high:ab.view.max_high,hda_ldda:ab.dataset.get("hda_ldda")},function(ac){var ae=ac.data;if(isNaN(parseFloat(ab.config.get_value("min_value")))||isNaN(parseFloat(ab.config.get_value("max_value")))){var ad=ae.min,af=ae.max;ad=Math.floor(Math.min(0,Math.max(ad,ae.mean-2*ae.sd)));af=Math.ceil(Math.max(0,Math.min(af,ae.mean+2*ae.sd)));ab.config.set_value("min_value",ad);ab.config.set_value("max_value",af)}})},change_mode:function(ac){var ab=this;ab.mode=ac;ab.config.set_value("mode",ac);if(ac==="Auto"){this.data_manager.clear()}ab.request_draw({clear_tile_cache:true});this.action_icons.mode_icon.attr("title","Set display mode (now: "+ab.mode+")");return ab},update_icons:function(){var ab=this;if(ab.filters_available){ab.action_icons.filters_icon.show()}else{ab.action_icons.filters_icon.hide()}if(ab.tool){ab.action_icons.tools_icon.show();ab.action_icons.param_space_viz_icon.show()}else{ab.action_icons.tools_icon.hide();ab.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(ac,ab){return ac+"_"+ab},request_draw:function(ab){if(ab&&ab.clear_tile_cache){this.tile_cache.clear()}this.view.request_redraw(ab,this)},before_draw:function(){this.max_height_px=0},_draw:function(ap){if(!this.can_draw()){return}var am=ap&&ap.clear_after,ak=this.view.low,af=this.view.high,ai=af-ak,ac=this.view.container.width(),ao=this.view.resolution_px_b,ae=1/ao;if(this.is_overview){ak=this.view.max_low;af=this.view.max_high;ao=ac/(view.max_high-view.max_low);ae=1/ao}this.before_draw();this.tiles_div.children().addClass("remove");var ab=Math.floor(ak/(ae*Q)),aj,al,ag,ah=[],an=[];while((ab*Q*ae)<af){aj=Math.floor(ab*Q*ae);al=new w.GenomeRegion({chrom:this.view.chrom,start:aj,end:Math.min(aj+Math.ceil(Q*ae),this.view.max_high)});ag=this.draw_helper(al,ao,ap);ah.push(ag);$.when(ag).then(function(aq){an.push(aq)});ab+=1}if(!am){this.tiles_div.children(".remove").removeClass("remove").remove()}var ad=this;$.when.apply($,ah).then(function(){ad.tiles_div.children(".remove").remove();an=aa.filter(an,function(aq){return aq!==null});if(an.length!==0){ad.postdraw_actions(an,ac,ao,am)}})},_add_yaxis_label:function(ae,ag){var ac=this,af=(ae==="max"?"top":"bottom"),ah=(ae==="max"?"max":"min"),ab=(ae==="max"?"max_value":"min_value"),ad=this.container_div.find(".yaxislabel."+af);ag=ag||function(){ac.request_draw({clear_tile_cache:true})};if(ad.length!==0){ad.text(ac.config.get_value(ab))}else{ad=$("<div/>").text(ac.config.get_value(ab)).make_text_editable({num_cols:12,on_finish:function(ai){$(".tooltip").remove();ac.config.set_value(ab,ai);ag()},help_text:"Set "+ah+" value"}).addClass("yaxislabel "+af).css("color",this.config.get_value("label_color"));this.container_div.prepend(ad)}},postdraw_actions:function(ae,af,ah,ab){if(this.view.reference_track.tiles_div.is(":visible")){this.view.resize_viewport()}var ad=aa.filter(ae,function(ai){return(ai instanceof K)});if(ad.length>0){this.max_height_px=0;var ac=this;aa.each(ae,function(ai){if(!(ai instanceof K)){ai.html_elt.remove();ac.draw_helper(ai.region,ah,{force:true,mode:"Coverage"})}});ac._add_yaxis_label("max")}else{this.container_div.find(".yaxislabel").remove();var ag=aa.find(ae,function(ai){return ai.has_icons});if(ag){aa.each(ae,function(ai){if(!ai.has_icons){ai.html_elt.css("padding-top",D)}})}}},get_mode:function(ab){return this.mode},update_auto_mode:function(ab){},_get_drawables:function(){return[this]},draw_helper:function(ak,am,an){if(!an){an={}}var ac=an.force,ah=an.mode||this.mode,ae=1/am,ad=this,af=this._get_drawables(),al=this._gen_tile_cache_key(am,ak),ag=function(ao){return(ao&&"track" in ao)};var ai=(ac?undefined:ad.tile_cache.get_elt(al));if(ai){if(ag(ai)){ad.show_tile(ai,am)}return ai}if(an.data_fetch===false){return null}var aj=function(){var ao=(aa.find(A,function(aq){return aq===ah})?"Coverage":ah);var ap=aa.map(af,function(aq){return aq.data_manager.get_data(ak,ao,ae,ad.data_url_extra_params)});if(view.reference_track){ap.push(view.reference_track.data_manager.get_data(ak,ah,ae,view.reference_track.data_url_extra_params))}return ap};var ab=$.Deferred();ad.tile_cache.set_elt(al,ab);$.when.apply($,aj()).then(function(){var ao=aj(),au=ao,aA;if(aa.find(ao,function(aC){return j.is_deferred(aC)})){ad.tile_cache.set_elt(al,undefined);$.when(ad.draw_helper(ak,am,an)).then(function(aC){ab.resolve(aC)});return}if(view.reference_track){aA=view.reference_track.data_manager.subset_entry(ao.pop(),ak)}var av=[],ar=[];aa.each(af,function(aF,aC){var aE=aF.mode,aD=au[aC];if(aE==="Auto"){aE=aF.get_mode(aD);aF.update_auto_mode(aE)}av.push(aE);ar.push(aF.get_canvas_height(aD,aE,am,ap))});var at=ad.view.canvas_manager.new_canvas(),aw=ak.get("start"),aB=ak.get("end"),aq=0,ap=Math.ceil((aB-aw)*am)+ad.left_offset,ay=aa.max(ar),ax;at.width=ap;at.height=(an.height||ay);var az=at.getContext("2d");az.translate(ad.left_offset,0);if(af.length>1){az.globalAlpha=0.5;az.globalCompositeOperation="source-over"}aa.each(af,function(aD,aC){ax=aD.draw_tile(au[aC],az,av[aC],ak,am,aA)});if(ax!==undefined){ad.tile_cache.set_elt(al,ax);ad.show_tile(ax,am)}ab.resolve(ax)});return ab},get_canvas_height:function(ab,ad,ae,ac){return this.visible_height_px},_draw_line_track_tile:function(ab,ad,ag,af,ah){var ae=ad.canvas,ac=new J.LinePainter(ab.data,af.get("start"),af.get("end"),this.config.to_key_value_dict(),ag);ac.draw(ad,ae.width,ae.height,ah);return new K(this,af,ah,ae,ab.data)},draw_tile:function(ab,ac,af,ae,ag,ad){},show_tile:function(ad,ag){var ac=this,ab=ad.html_elt;ad.predisplay_actions();var af=(ad.low-(this.is_overview?this.view.max_low:this.view.low))*ag;if(this.left_offset){af-=this.left_offset}ab.css("left",af);if(ab.hasClass("remove")){ab.removeClass("remove")}else{this.tiles_div.append(ab)}ab.css("height","auto");this.max_height_px=Math.max(this.max_height_px,ab.height()-2);ab.parent().children().css("height",this.max_height_px+"px");var ae=this.max_height_px;if(this.visible_height_px!==0){ae=Math.min(this.max_height_px,this.visible_height_px)}this.tiles_div.css("height",ae+"px")},tool_region_and_parameters_str:function(ae){var ab=this,ad=(ae!==undefined?ae.toString():"all"),ac=aa.values(ab.tool.get_inputs_dict()).join(", ");return" - region=["+ad+"], parameters=["+ac+"]"},data_and_mode_compatible:function(ab,ac){if(ac==="Auto"){return true}else{if(ac==="Coverage"){return ab.dataset_type==="bigwig"}else{if(ab.dataset_type==="bigwig"||ab.extra_info==="no_detail"){return false}else{return true}}}},can_subset:function(ab){if(ab.message||ab.extra_info==="no_detail"){return false}else{if(ab.dataset_type==="bigwig"){return(ab.data[1][0]-ab.data[0][0]===1)}}return true},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data");this.data_query_wait=1000;this.dataset_check_type="state"}});var V=function(ac,ab){var ad={resize:false};f.call(this,ac,ab,ad);this.container_div.addClass("label-track")};o(V.prototype,f.prototype,{build_header_div:function(){},init:function(){this.enabled=true},predraw_init:function(){},_draw:function(af){var ad=this.view,ae=ad.high-ad.low,ai=Math.floor(Math.pow(10,Math.floor(Math.log(ae)/Math.log(10)))),ab=Math.floor(ad.low/ai)*ai,ag=this.view.container.width(),ac=$("<div/>").addClass("label-container");while(ab<ad.high){var ah=(ab-ad.low)/ae*ag;ac.append($("<div/>").addClass("label").text(commatize(ab)).css({left:ah}));ab+=ai}this.content_div.children(":first").remove();this.content_div.append(ac)}});var e=function(ac,ab,af){L.call(this,ac,ab,af);this.drawables=[];if("drawables" in af){var ae;for(var ad=0;ad<af.drawables.length;ad++){ae=af.drawables[ad];this.drawables[ad]=n(ae,ac,null);if(ae.left_offset>this.left_offset){this.left_offset=ae.left_offset}}this.enabled=true}aa.each(this.drawables,function(ag){if(ag instanceof c||ag instanceof S){ag.change_mode("Coverage")}});this.update_icons();this.obj_type="CompositeTrack"};o(e.prototype,L.prototype,{display_modes:A,config_params:aa.union(p.prototype.config_params,[{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:30,hidden:true}]),action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(ab){$(".tooltip").remove();ab.show_group()}}].concat(L.prototype.action_icons_def),to_dict:y.prototype.to_dict,add_drawable:y.prototype.add_drawable,unpack_drawables:y.prototype.unpack_drawables,config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},on_resize:function(){var ab=this.visible_height_px;aa.each(this.drawables,function(ac){ac.visible_height_px=ab});f.prototype.on_resize.call(this)},change_mode:function(ab){L.prototype.change_mode.call(this,ab);for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac].change_mode(ab)}},init:function(){var ad=[];for(var ac=0;ac<this.drawables.length;ac++){ad.push(this.drawables[ac].init())}var ab=this;$.when.apply($,ad).then(function(){ab.enabled=true;ab.request_draw()})},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide();this.action_icons.param_space_viz_icon.hide()},can_draw:p.prototype.can_draw,_get_drawables:function(){return this.drawables},show_group:function(){var ae=new N(this.view,this.container,{name:this.config.get_value("name")}),ab;for(var ad=0;ad<this.drawables.length;ad++){ab=this.drawables[ad];ab.update_icons();ae.add_drawable(ab);ab.container=ae;ae.content_div.append(ab.container_div)}var ac=this.container.replace_drawable(this,ae,true);ae.request_draw({clear_tile_cache:true})},before_draw:function(){var ac=aa.min(aa.map(this.drawables,function(ad){return ad.config.get_value("min_value")})),ab=aa.max(aa.map(this.drawables,function(ad){return ad.config.get_value("max_value")}));this.config.set_value("min_value",ac);this.config.set_value("max_value",ab);aa.each(this.drawables,function(ad){ad.config.set_value("min_value",ac);ad.config.set_value("max_value",ab)})},update_all_min_max:function(){var ac=this,ab=this.config.get_value("min_value"),ad=this.config.get_value("max_value");aa.each(this.drawables,function(ae){ae.config.set_value("min_value",ab);ae.config.set_value("max_value",ad)});this.request_draw({clear_tile_cache:true})},postdraw_actions:function(ah,ab,ak,ag){var af=-1,ad;for(ad=0;ad<ah.length;ad++){var ai=ah[ad].html_elt.find("canvas").height();if(ai>af){af=ai}}for(ad=0;ad<ah.length;ad++){var ae=ah[ad];if(ae.html_elt.find("canvas").height()!==af){this.draw_helper(ae.region,ak,{force:true,height:af});ae.html_elt.remove()}}var ac=this,aj=function(){ac.update_all_min_max()};this._add_yaxis_label("min",aj);this._add_yaxis_label("max",aj)}});var B=function(ab){L.call(this,ab,{content_div:ab.top_labeltrack},{resize:false});this.left_offset=ab.canvas_manager.char_width_px;this.container_div.addClass("reference-track");this.data_url=galaxy_config.root+"api/genomes/"+this.view.dbkey;this.data_url_extra_params={reference:true};this.data_manager=new w.GenomeReferenceDataManager({data_url:this.data_url,can_subset:this.can_subset});this.hide_contents()};o(B.prototype,p.prototype,L.prototype,{config_params:aa.union(p.prototype.config_params,[{key:"height",type:"int",default_value:13,hidden:true}]),build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},predraw_init:function(){},can_draw:p.prototype.can_draw,draw_helper:function(ac,ad,ab){if(ad>this.view.canvas_manager.char_width_px){this.tiles_div.show();return L.prototype.draw_helper.call(this,ac,ad,ab)}else{this.tiles_div.hide();return null}},can_subset:function(ab){return true},draw_tile:function(ad,aj,ae,ag,ak){var ac=this.data_manager.subset_entry(ad,ag),ai=ac.data;var ab=aj.canvas;aj.font=aj.canvas.manager.default_font;aj.textAlign="center";for(var af=0,ah=ai.length;af<ah;af++){aj.fillStyle=this.view.get_base_color(ai[af]);aj.fillText(ai[af],Math.floor(af*ak),10)}return new b(this,ag,ak,ab,ac)}});var g=function(ac,ab,ad){this.mode="Histogram";L.call(this,ac,ab,ad)};o(g.prototype,p.prototype,L.prototype,{display_modes:A,config_params:aa.union(p.prototype.config_params,[{key:"color",label:"Color",type:"color"},{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:30,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},before_draw:function(){},draw_tile:function(ab,ac,ae,ad,af){return this._draw_line_track_tile(ab,ac,ae,ad,af)},can_subset:function(ab){return(ab.data[1][0]-ab.data[0][0]===1)},postdraw_actions:function(ac,ad,ae,ab){this._add_yaxis_label("max");this._add_yaxis_label("min")}});var r=function(ac,ab,ad){this.mode="Heatmap";L.call(this,ac,ab,ad)};o(r.prototype,p.prototype,L.prototype,{display_modes:["Heatmap"],config_params:aa.union(p.prototype.config_params,[{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"min_value",label:"Min Value",type:"float",default_value:-1},{key:"max_value",label:"Max Value",type:"float",default_value:1},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:500,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},draw_tile:function(ab,ad,ag,af,ah){var ae=ad.canvas,ac=new J.DiagonalHeatmapPainter(ab.data,af.get("start"),af.get("end"),this.config.to_key_value_dict(),ag);ac.draw(ad,ae.width,ae.height,ah);return new b(this,af,ah,ae,ab.data)}});var c=function(ac,ab,ad){L.call(this,ac,ab,ad);this.container_div.addClass("feature-track");this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};o(c.prototype,p.prototype,L.prototype,{display_modes:["Auto","Coverage","Dense","Squish","Pack"],config_params:aa.union(p.prototype.config_params,[{key:"block_color",label:"Block color",type:"color"},{key:"reverse_strand_color",label:"Antisense strand color",type:"color"},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"connector_style",label:"Connector style",type:"select",default_value:"fishbones",options:[{label:"Line with arrows",value:"fishbone"},{label:"Arcs",value:"arcs"}]},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.set_painter_from_config();this.request_draw({clear_tile_cache:true})},set_painter_from_config:function(){if(this.config.get_value("connector_style")==="arcs"){this.painter=J.ArcLinkedFeaturePainter}else{this.painter=J.LinkedFeaturePainter}},postdraw_actions:function(an,ae,ap,am){L.prototype.postdraw_actions.call(this,an,ae,ap,am);var ag=this,ah,al=aa.filter(an,function(aq){return(aq instanceof K)});if(al.length===0){var ab={};aa.each(aa.pluck(an,"incomplete_features"),function(aq){aa.each(aq,function(ar){ab[ar[0]]=ar})});var ao=this;aa.each(an,function(aq){ao.draw_tile({data:aa.values(ab)},aq.canvas.getContext("2d"),aq.mode,aq.region,ap,aq.seq_data,true)})}if(ag.filters_manager){var af=ag.filters_manager.filters,aj;for(aj=0;aj<af.length;aj++){af[aj].update_ui_elt()}var ai=false,ak,ad;for(ah=0;ah<an.length;ah++){if(an[ah].data.length){ak=an[ah].data[0];for(aj=0;aj<af.length;aj++){ad=af[aj];if(ad.applies_to(ak)&&ad.min!==ad.max){ai=true;break}}}}if(ag.filters_available!==ai){ag.filters_available=ai;if(!ag.filters_available){ag.filters_manager.hide()}ag.update_icons()}}if(an[0] instanceof M){var ac=true;for(ah=0;ah<an.length;ah++){if(!an[ah].all_slotted){ac=false;break}}if(!ac){this.action_icons.show_more_rows_icon.show()}else{this.action_icons.show_more_rows_icon.hide()}}else{this.action_icons.show_more_rows_icon.hide()}},update_auto_mode:function(ab){if(this.mode==="Auto"){if(ab==="no_detail"){ab="feature spans"}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+ab+")")}},incremental_slots:function(af,ab,ae){var ac=this.view.canvas_manager.dummy_context,ad=this.slotters[af];if(!ad||(ad.mode!==ae)){ad=new (s.FeatureSlotter)(af,ae,z,function(ag){return ac.measureText(ag)});this.slotters[af]=ad}return ad.slot_features(ab)},get_mode:function(ab){if(ab.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>G){mode="Squish"}else{mode="Pack"}}return mode},get_canvas_height:function(ab,af,ag,ac){if(af==="Coverage"||ab.dataset_type==="bigwig"){return this.summary_draw_height}else{var ae=this.incremental_slots(ag,ab.data,af);var ad=new (this.painter)(null,null,null,this.config.to_key_value_dict(),af);return Math.max(this.min_height_px,ad.get_required_height(ae,ac))}},draw_tile:function(am,ar,ap,af,aj,ae,an){var aq=this,ad=ar.canvas,ay=af.get("start"),ac=af.get("end"),ag=this.left_offset;if(am.dataset_type==="bigwig"){return this._draw_line_track_tile(am,ar,ap,af,aj)}var ai=[],ao=this.slotters[aj].slots;all_slotted=true;if(am.data){var ak=this.filters_manager.filters;for(var at=0,av=am.data.length;at<av;at++){var ah=am.data[at];var au=false;var al;for(var ax=0,aC=ak.length;ax<aC;ax++){al=ak[ax];al.update_attrs(ah);if(!al.keep(ah)){au=true;break}}if(!au){ai.push(ah);if(!(ah[0] in ao)){all_slotted=false}}}}var aB=(this.filters_manager.alpha_filter?new C(this.filters_manager.alpha_filter):null),az=(this.filters_manager.height_filter?new C(this.filters_manager.height_filter):null),aA=new (this.painter)(ai,ay,ac,this.config.to_key_value_dict(),ap,aB,az,ae,function(aD){return aq.view.get_base_color(aD)});var aw=null;ar.fillStyle=this.config.get_value("block_color");ar.font=ar.canvas.manager.default_font;ar.textAlign="right";if(am.data){var ab=aA.draw(ar,ad.width,ad.height,aj,ao);aw=ab.feature_mapper;incomplete_features=ab.incomplete_features;aw.translation=-ag}if(!an){return new M(aq,af,aj,ad,am.data,ap,am.message,all_slotted,aw,incomplete_features,ae)}}});var Y=function(ac,ab,ad){L.call(this,ac,ab,ad);this.painter=J.VariantPainter;this.summary_draw_height=30;this.left_offset=30};o(Y.prototype,p.prototype,L.prototype,{display_modes:["Auto","Coverage","Dense","Squish","Pack"],config_params:aa.union(p.prototype.config_params,[{key:"color",label:"Histogram color",type:"color"},{key:"show_sample_data",label:"Show sample data",type:"bool",default_value:true},{key:"show_labels",label:"Show summary and sample labels",type:"bool",default_value:true},{key:"summary_height",label:"Locus summary height",type:"float",default_value:20},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},draw_tile:function(ab,ae,ag,af,ah){if(ab.dataset_type==="bigwig"){return this._draw_line_track_tile(ab,ae,"Histogram",af,ah)}else{var ad=this.view,ac=new (this.painter)(ab.data,af.get("start"),af.get("end"),this.config.to_key_value_dict(),ag,function(ai){return ad.get_base_color(ai)});ac.draw(ae,ae.canvas.width,ae.canvas.height,ah);return new b(this,af,ah,ae.canvas,ab.data)}},get_canvas_height:function(ab,af,ag,ad){if(ab.dataset_type==="bigwig"){return this.summary_draw_height}else{var ac=(this.dataset.get_metadata("sample_names")?this.dataset.get_metadata("sample_names").length:0);if(ac===0&&ab.data.length!==0){ac=ab.data[0][7].match(/,/g);if(ac===null){ac=1}else{ac=ac.length+1}}var ae=new (this.painter)(null,null,null,this.config.to_key_value_dict(),af);return ae.get_required_height(ac)}},predraw_init:function(){var ab=[f.prototype.predraw_init.call(this)];if(!this.dataset.get_metadata("sample_names")){ab.push(this.dataset.fetch())}return ab},postdraw_actions:function(af,ag,ai,ac){L.prototype.postdraw_actions.call(this,af,ag,ai,ac);var ae=aa.filter(af,function(aj){return(aj instanceof K)});var ad=this.dataset.get_metadata("sample_names");if(ae.length===0&&this.config.get_value("show_labels")&&ad&&ad.length>1){var ab;if(this.container_div.find(".yaxislabel.variant").length===0){ab=this.config.get_value("summary_height")/2;this.tiles_div.prepend($("<div/>").text("Summary").addClass("yaxislabel variant top").css({"font-size":ab+"px",top:(this.config.get_value("summary_height")-ab)/2+"px"}));if(this.config.get_value("show_sample_data")){var ah=ad.join("<br/>");this.tiles_div.prepend($("<div/>").html(ah).addClass("yaxislabel variant top sample").css({top:this.config.get_value("summary_height")}))}}ab=(this.mode==="Squish"?5:10)+"px";$(this.tiles_div).find(".sample").css({"font-size":ab,"line-height":ab});$(this.tiles_div).find(".yaxislabel").css("color",this.config.get_value("label_color"))}else{this.container_div.find(".yaxislabel.variant").remove()}}});var S=function(ac,ab,ad){c.call(this,ac,ab,ad);this.painter=J.ReadPainter;this.update_icons()};o(S.prototype,p.prototype,L.prototype,c.prototype,{config_params:aa.union(p.prototype.config_params,[{key:"block_color",label:"Histogram color",type:"color"},{key:"detail_block_color",label:"Sense strand block color",type:"color",default_value:"#AAAAAA"},{key:"reverse_strand_color",label:"Antisense strand block color",type:"color",default_value:"#DDDDDD"},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})}});var d={CompositeTrack:e,DrawableGroup:N,DiagonalHeatmapTrack:r,FeatureTrack:c,LineTrack:g,ReadTrack:S,VariantTrack:Y,VcfTrack:Y};var n=function(ad,ac,ab){if("copy" in ad){return ad.copy(ab)}else{var ae=ad.obj_type;if(!ae){ae=ad.track_type}return new d[ae](ac,ab,ad)}};return{TracksterView:X,DrawableGroup:N,LineTrack:g,FeatureTrack:c,DiagonalHeatmapTrack:r,ReadTrack:S,VariantTrack:Y,CompositeTrack:e,object_from_template:n}});
\ No newline at end of file
+define(["libs/underscore","viz/visualization","viz/viz_views","viz/trackster/util","viz/trackster/slotting","viz/trackster/painters","viz/trackster/filters","mvc/data","mvc/tools","utils/config"],function(ab,w,V,j,s,J,h,X,P,R){var o=ab.extend;var l={};var i=function(ac,ad){l[ac.attr("id")]=ad};var k=function(ac,ad,af,ae){af=".group";l[ac.attr("id")]=ae;ac.bind("drag",{handle:"."+ad,relative:true},function(an,ao){var am=$(this),ar=$(this).parent(),aj=ar.children(".track,.group"),al=l[$(this).attr("id")],ai,ah,ap,ag,ak;ah=$(this).parents(af);if(ah.length!==0){ap=ah.position().top;ag=ap+ah.outerHeight();var aq=l[ah.attr("id")];if(ao.offsetY<ap){$(this).insertBefore(ah);aq.remove_drawable(al);aq.container.add_drawable_before(al,aq);return}else{if(ao.offsetY>ag){$(this).insertAfter(ah);aq.remove_drawable(al);aq.container.add_drawable(al);return}}}ah=null;for(ak=0;ak<aj.length;ak++){ai=$(aj.get(ak));ap=ai.position().top;ag=ap+ai.outerHeight();if(ai.is(af)&&this!==ai.get(0)&&ao.offsetY>=ap&&ao.offsetY<=ag){if(ao.offsetY-ap<ag-ao.offsetY){ai.find(".content-div").prepend(this)}else{ai.find(".content-div").append(this)}if(al.container){al.container.remove_drawable(al)}l[ai.attr("id")].add_drawable(al);return}}for(ak=0;ak<aj.length;ak++){ai=$(aj.get(ak));if(ao.offsetY<ai.position().top&&!(ai.hasClass("reference-track")||ai.hasClass("intro"))){break}}if(ak===aj.length){if(this!==aj.get(ak-1)){ar.append(this);l[ar.attr("id")].move_drawable(al,ak)}}else{if(this!==aj.get(ak)){$(this).insertBefore(aj.get(ak));l[ar.attr("id")].move_drawable(al,(ao.deltaY>0?ak-1:ak))}}}).bind("dragstart",function(){$(this).addClass("dragging")}).bind("dragend",function(){$(this).removeClass("dragging")})};var D=20,z=100,G=12000,Q=400,I=5000,v=100,m="Cannot display dataset due to an error. ",H="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",E="No data for this chrom/contig.",u="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",x="Tool cannot be rerun: ",a="Loading data...",T="Ready for display",O=10,F=20,A=["Histogram","Line","Filled","Intensity"];function U(ad,ac){if(!ac){ac=0}var ae=Math.pow(10,ac);return Math.round(ad*ae)/ae}var p=function(ad,ac,ag){if(!p.id_counter){p.id_counter=0}this.id=p.id_counter++;this.view=ad;this.container=ac;this.drag_handle_class=ag.drag_handle_class;this.is_overview=false;this.action_icons={};this.config=R.ConfigSettingCollection.from_models_and_saved_values(this.config_params,ag.prefs);if(!this.config.get_value("name")){this.config.set_value("name",ag.name)}if(this.config_onchange){this.config.on("change",this.config_onchange,this)}this.container_div=this.build_container_div();this.header_div=null;if(ag.header!==false){var ae=new V.TrackHeaderView({model:this,id:this.id});this.header_div=ae.$el;this.container_div.append(this.header_div);var af=ae.icons_div;this.action_icons=ae.action_icons;this.container_div.hover(function(){af.show()},function(){af.hide()})}};p.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(ac){if(ac.config.get_value("content_visible")){ac.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");ac.hide_contents();ac.config.set_value("content_visible",false)}else{ac.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");ac.config.set_value("content_visible",true);ac.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"gear",on_click_fn:function(ad){var ac=new R.ConfigSettingCollectionView({collection:ad.config});ac.render_in_modal("Configure Track")}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(ac){$(".tooltip").remove();ac.remove()}}];o(p.prototype,{config_params:[{key:"name",label:"Name",type:"text",default_value:""},{key:"content_visible",type:"bool",default_value:true,hidden:true}],config_onchange:function(){},init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.config.get_value("content_visible")){return true}return false},request_draw:function(){},_draw:function(ac){},to_dict:function(){},set_name:function(ac){this.old_name=this.config.get_value("name");this.config.set_value("name",ac)},revert_name:function(){if(this.old_name){this.config.set_value("name",this.old_name)}},remove:function(){this.changed();this.container.remove_drawable(this);var ac=this.view;this.container_div.hide(0,function(){$(this).remove();ac.update_intro_div()})},build_container_div:function(){},update_icons:function(){},hide_contents:function(){},show_contents:function(){},get_drawables:function(){}});var y=function(ad,ac,ae){p.call(this,ad,ac,ae);this.obj_type=ae.obj_type;this.drawables=[]};o(y.prototype,p.prototype,{unpack_drawables:function(ae){this.drawables=[];var ad;for(var ac=0;ac<ae.length;ac++){ad=n(ae[ac],this.view,this);this.add_drawable(ad)}},init:function(){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac].init()}},_draw:function(ac){for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad]._draw(ac)}},to_dict:function(){var ad=[];for(var ac=0;ac<this.drawables.length;ac++){ad.push(this.drawables[ac].to_dict())}return{prefs:this.config.to_key_value_dict(),obj_type:this.obj_type,drawables:ad}},add_drawable:function(ac){this.drawables.push(ac);ac.container=this;this.changed()},add_drawable_before:function(ae,ac){this.changed();var ad=this.drawables.indexOf(ac);if(ad!==-1){this.drawables.splice(ad,0,ae);return true}return false},replace_drawable:function(ae,ac,ad){var af=this.drawables.indexOf(ae);if(af!==-1){this.drawables[af]=ac;if(ad){ae.container_div.replaceWith(ac.container_div)}this.changed()}return af},remove_drawable:function(ad){var ac=this.drawables.indexOf(ad);if(ac!==-1){this.drawables.splice(ac,1);ad.container=null;this.changed();return true}return false},move_drawable:function(ad,ae){var ac=this.drawables.indexOf(ad);if(ac!==-1){this.drawables.splice(ac,1);this.drawables.splice(ae,0,ad);this.changed();return true}return false},get_drawables:function(){return this.drawables},get_tracks:function(af){var ac=this.drawables.slice(0),ad=[],ae;while(ac.length!==0){ae=ac.shift();if(ae instanceof af){ad.push(ae)}else{if(ae.drawables){ac=ac.concat(ae.drawables)}}}return ad}});var N=function(ad,ac,af){o(af,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});y.call(this,ad,ac,af);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);i(this.container_div,this);i(this.content_div,this);k(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new h.FiltersManager(this);this.header_div.after(this.filters_manager.parent_div);this.filters_manager.parent_div.after($("<div style='clear: both'/>"));this.saved_filters_managers=[];if("drawables" in af){this.unpack_drawables(af.drawables)}if("filters" in af){var ae=this.filters_manager;this.filters_manager=new h.FiltersManager(this,af.filters);ae.parent_div.replaceWith(this.filters_manager.parent_div);if(af.filters.visible){this.setup_multitrack_filtering()}}};o(N.prototype,p.prototype,y.prototype,{action_icons_def:[p.prototype.action_icons_def[0],p.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(ac){$(".tooltip").remove();ac.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"ui-slider-050",on_click_fn:function(ac){if(ac.filters_manager.visible()){ac.filters_manager.clear_filters();ac._restore_filter_managers()}else{ac.setup_multitrack_filtering();ac.request_draw({clear_tile_cache:true})}ac.filters_manager.toggle()}},p.prototype.action_icons_def[2]],build_container_div:function(){var ac=$("<div/>").addClass("group").attr("id","group_"+this.id);if(this.container){this.container.content_div.append(ac)}return ac},hide_contents:function(){this.tiles_div.hide()},show_contents:function(){this.tiles_div.show();this.request_draw()},update_icons:function(){var ae=this.drawables.length;if(ae===0){this.action_icons.composite_icon.hide();this.action_icons.filters_icon.hide()}else{if(ae===1){this.action_icons.composite_icon.toggle(this.drawables[0] instanceof e);this.action_icons.filters_icon.hide()}else{var al,ak,ai,ao=true,ag=this.drawables[0].get_type(),ac=0;for(al=0;al<ae;al++){ai=this.drawables[al];if(ai.get_type()!==ag){can_composite=false;break}if(ai instanceof c){ac++}}if(ao&&this.drawables[0] instanceof g){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".tooltip").remove()}if(ac>1&&ac===this.drawables.length){var ap={},ad;ai=this.drawables[0];for(ak=0;ak<ai.filters_manager.filters.length;ak++){ad=ai.filters_manager.filters[ak];ap[ad.name]=[ad]}for(al=1;al<this.drawables.length;al++){ai=this.drawables[al];for(ak=0;ak<ai.filters_manager.filters.length;ak++){ad=ai.filters_manager.filters[ak];if(ad.name in ap){ap[ad.name].push(ad)}}}this.filters_manager.remove_all();var af,ah,aj,am;for(var an in ap){af=ap[an];if(af.length===ac){ah=new h.NumberFilter({name:af[0].name,index:af[0].index});this.filters_manager.add_filter(ah)}}this.action_icons.filters_icon.toggle(this.filters_manager.filters.length>0)}else{this.action_icons.filters_icon.hide()}}}},_restore_filter_managers:function(){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac].filters_manager=this.saved_filters_managers[ac]}this.saved_filters_managers=[]},setup_multitrack_filtering:function(){if(this.filters_manager.filters.length>0){this.saved_filters_managers=[];for(var ac=0;ac<this.drawables.length;ac++){drawable=this.drawables[ac];this.saved_filters_managers.push(drawable.filters_manager);drawable.filters_manager=this.filters_manager}}this.filters_manager.init_filters()},show_composite_track:function(){var ad=new e(this.view,this.view,{name:this.config.get_value("name"),drawables:this.drawables});var ac=this.container.replace_drawable(this,ad,true);ad.request_draw()},add_drawable:function(ac){y.prototype.add_drawable.call(this,ac);this.update_icons()},remove_drawable:function(ac){y.prototype.remove_drawable.call(this,ac);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var ac=o(y.prototype.to_dict.call(this),{filters:this.filters_manager.to_dict()});if(this.filters_manager.visible()){this.setup_multitrack_filtering()}return ac},request_draw:function(ac){ab.each(this.drawables,function(ad){ad.request_draw(ac)})}});var Y=Backbone.View.extend({initialize:function(ac){o(ac,{obj_type:"View"});y.call(this,"View",ac.container,ac);this.chrom=null;this.vis_id=ac.vis_id;this.dbkey=ac.dbkey;this.label_tracks=[];this.tracks_to_be_redrawn=[];this.max_low=0;this.max_high=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.load_chroms_deferred=null;this.render();this.canvas_manager=new w.CanvasManager(this.container.get(0).ownerDocument);this.reset();this.config=R.ConfigSettingCollection.from_models_and_saved_values([{key:"name",label:"Name",type:"text",default_value:""},{key:"a_color",label:"A Color",type:"color",default_value:"#FF0000"},{key:"c_color",label:"C Color",type:"color",default_value:"#00FF00"},{key:"g_color",label:"G Color",type:"color",default_value:"#0000FF"},{key:"t_color",label:"T Color",type:"color",default_value:"#FF00FF"},{key:"n_color",label:"N Color",type:"color",default_value:"#AAAAAA"}],{name:ac.name})},render:function(){this.requested_redraw=false;var ae=this.container,ac=this;this.top_container=$("<div/>").addClass("top-container").appendTo(ae);this.browser_content_div=$("<div/>").addClass("content").appendTo(ae);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(ae);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").attr("id","viewport-container").appendTo(this.browser_content_div);this.content_div=this.viewport_container;i(this.viewport_container,ac);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var af=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){w.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":ac.dbkey},function(ag){ab.each(ag,function(ah){ac.add_drawable(n(ah,ac,ac))})})});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("trackster-nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("trackster-nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a/>").attr("title","Close overview").addClass("icon-button overview-close tooltip").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div/>").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_select=$("<select/>").attr({name:"chrom"}).addClass("chrom-nav").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ad=function(ag){if(ag.type==="focusout"||(ag.keyCode||ag.which)===13||(ag.keyCode||ag.which)===27){if((ag.keyCode||ag.which)!==27){ac.go_to($(this).val())}$(this).hide();$(this).val("");ac.location_span.show();ac.chrom_select.show()}ag.stopPropagation()};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ad).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("title","Click to change location").tooltip({placement:"bottom"}).appendTo(this.nav_controls);this.location_span.click(function(){ac.location_span.hide();ac.chrom_select.hide();ac.nav_input.val(ac.chrom+":"+ac.low+"-"+ac.high);ac.nav_input.css("display","inline-block");ac.nav_input.select();ac.nav_input.focus();ac.nav_input.autocomplete({source:function(ai,ag){var aj=[],ah=$.map(ac.get_tracks(c),function(ak){return ak.data_manager.search_features(ai.term).success(function(al){aj=aj.concat(al)})});$.when.apply($,ah).done(function(){ag($.map(aj,function(ak){return{label:ak[0],value:ak[1]}}))})},minLength:2})});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.nav_controls)}this.zo_link=$("<a/>").attr("id","zoom-out").attr("title","Zoom out").tooltip({placement:"bottom"}).click(function(){ac.zoom_out()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tooltip({placement:"bottom"}).click(function(){ac.zoom_in()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){ac.change_chrom(ac.chrom_select.val())});this.browser_content_div.click(function(ag){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(ag){ac.zoom_in(ag.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(ag,ah){this.current_x=ah.offsetX}).bind("drag",function(ag,ai){var aj=ai.offsetX-this.current_x;this.current_x=ai.offsetX;var ah=Math.round(aj/ac.viewport_container.width()*(ac.max_high-ac.max_low));ac.move_delta(-ah)});this.overview_close.click(function(){ac.reset_overview()});this.viewport_container.bind("draginit",function(ag,ah){if(ag.clientX>ac.viewport_container.width()-16){return false}}).bind("dragstart",function(ag,ah){ah.original_low=ac.low;ah.current_height=ag.clientY;ah.current_x=ah.offsetX}).bind("drag",function(ai,ak){var ag=$(this);var al=ak.offsetX-ak.current_x;var ah=ag.scrollTop()-(ai.clientY-ak.current_height);ag.scrollTop(ah);ak.current_height=ai.clientY;ak.current_x=ak.offsetX;var aj=Math.round(al/ac.viewport_container.width()*(ac.high-ac.low));ac.move_delta(aj)});this.top_labeltrack.bind("dragstart",function(ag,ah){return $("<div/>").addClass("zoom-area").css("height",ac.browser_content_div.height()+ac.top_labeltrack.height()+ac.nav_labeltrack.height()+1).appendTo($(this))}).bind("drag",function(ak,al){$(al.proxy).css({left:Math.min(ak.pageX,al.startX)-ac.container.offset().left,width:Math.abs(ak.pageX-al.startX)});var ah=Math.min(ak.pageX,al.startX)-ac.container.offset().left,ag=Math.max(ak.pageX,al.startX)-ac.container.offset().left,aj=(ac.high-ac.low),ai=ac.viewport_container.width();ac.update_location(Math.round(ah/ai*aj)+ac.low,Math.round(ag/ai*aj)+ac.low)}).bind("dragend",function(al,am){var ah=Math.min(al.pageX,am.startX),ag=Math.max(al.pageX,am.startX),aj=(ac.high-ac.low),ai=ac.viewport_container.width(),ak=ac.low;ac.low=Math.round(ah/ai*aj)+ak;ac.high=Math.round(ag/ai*aj)+ak;$(am.proxy).remove();ac.request_redraw()});this.add_label_track(new W(this,{content_div:this.top_labeltrack}));this.add_label_track(new W(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){if(this.resize_timer){clearTimeout(this.resize_timer)}this.resize_timer=setTimeout(function(){ac.resize_window()},500)});$(document).bind("redraw",function(){ac.redraw()});this.reset();$(window).trigger("resize")},get_base_color:function(ac){return this.config.get_value(ac.toLowerCase()+"_color")||this.config.get_value("n_color")}});o(Y.prototype,y.prototype,{changed:function(){this.has_changes=true},update_intro_div:function(){this.intro_div.toggle(this.drawables.length===0)},trigger_navigate:function(ad,af,ac,ag){if(this.timer){clearTimeout(this.timer)}if(ag){var ae=this;this.timer=setTimeout(function(){ae.trigger("navigate",ad+":"+af+"-"+ac)},500)}else{view.trigger("navigate",ad+":"+af+"-"+ac)}},update_location:function(ac,ae){this.location_span.text(commatize(ac)+" - "+commatize(ae));this.nav_input.val(this.chrom+":"+commatize(ac)+"-"+commatize(ae));var ad=view.chrom_select.val();if(ad!==""){this.trigger_navigate(ad,view.low,view.high,true)}},load_chroms:function(ae){ae.num=v;var ac=this,ad=$.Deferred();$.ajax({url:galaxy_config.root+"api/genomes/"+this.dbkey,data:ae,dataType:"json",success:function(ag){if(ag.chrom_info.length===0){return}if(ag.reference){var ah=new B(ac);ac.add_label_track(ah);ac.reference_track=ah}ac.chrom_data=ag.chrom_info;var ak='<option value="">Select Chrom/Contig</option>';for(var aj=0,af=ac.chrom_data.length;aj<af;aj++){var ai=ac.chrom_data[aj].chrom;ak+='<option value="'+ai+'">'+ai+"</option>"}if(ag.prev_chroms){ak+='<option value="previous">Previous '+v+"</option>"}if(ag.next_chroms){ak+='<option value="next">Next '+v+"</option>"}ac.chrom_select.html(ak);ac.chrom_start_index=ag.start_index;ad.resolve(ag.chrom_info)},error:function(){alert("Could not load chroms for this dbkey: "+ac.dbkey)}});return ad},change_chrom:function(ah,ad,aj){var ae=this;if(!ae.chrom_data){ae.load_chroms_deferred.then(function(){ae.change_chrom(ah,ad,aj)});return}if(!ah||ah==="None"){return}if(ah==="previous"){ae.load_chroms({low:this.chrom_start_index-v});return}if(ah==="next"){ae.load_chroms({low:this.chrom_start_index+v});return}var ai=$.grep(ae.chrom_data,function(ak,al){return ak.chrom===ah})[0];if(ai===undefined){ae.load_chroms({chrom:ah},function(){ae.change_chrom(ah,ad,aj)});return}else{if(ah!==ae.chrom){ae.chrom=ah;ae.chrom_select.val(ae.chrom);ae.max_high=ai.len-1;ae.reset();for(var ag=0,ac=ae.drawables.length;ag<ac;ag++){var af=ae.drawables[ag];if(af.init){af.init()}}if(ae.reference_track){ae.reference_track.init()}}if(ad===undefined&&aj===undefined){ae.low=0;ae.high=ae.max_high}else{ae.low=(ad!==undefined?Math.max(ad,0):0);if(aj===undefined){ae.low=Math.max(ae.low-15,0);ae.high=ae.low+30}else{ae.high=Math.min(aj,ae.max_high)}}ae.reset_overview();ae.request_redraw()}},go_to:function(ag){ag=ag.replace(/,/g,"");ag=ag.replace(/:|\-/g," ");var ad=ag.split(/\s+/),af=ad[0],ae=(ad[1]?parseInt(ad[1],10):undefined),ac=(ad[2]?parseInt(ad[2],10):undefined);this.change_chrom(af,ae,ac)},move_fraction:function(ae){var ac=this;var ad=ac.high-ac.low;this.move_delta(ae*ad)},move_delta:function(af){var ac=this;var ae=ac.high-ac.low;if(ac.low-af<ac.max_low){ac.low=ac.max_low;ac.high=ac.max_low+ae}else{if(ac.high-af>ac.max_high){ac.high=ac.max_high;ac.low=ac.max_high-ae}else{ac.high-=af;ac.low-=af}}ac.request_redraw({data_fetch:false});if(this.redraw_on_move_fn){clearTimeout(this.redraw_on_move_fn)}this.redraw_on_move_fn=setTimeout(function(){ac.request_redraw()},200);var ad=ac.chrom_select.val();this.trigger_navigate(ad,ac.low,ac.high,true)},add_drawable:function(ac){y.prototype.add_drawable.call(this,ac);ac.init();this.changed();this.update_intro_div()},add_label_track:function(ac){ac.view=this;ac.init();this.label_tracks.push(ac)},remove_drawable:function(ae,ad){y.prototype.remove_drawable.call(this,ae);if(ad){var ac=this;ae.container_div.hide(0,function(){$(this).remove();ac.update_intro_div()})}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(ad,ae){var ac=this,af=(ae?[ae]:ac.drawables);ab.each(af,function(ag){var ah=ab.find(ac.tracks_to_be_redrawn,function(ai){return ai[0]===ag});if(ah){ah[1]=ad}else{ac.tracks_to_be_redrawn.push([ag,ad])}});if(!this.requested_redraw){requestAnimationFrame(function(){ac._redraw()});this.requested_redraw=true}},_redraw:function(){this.requested_redraw=false;var ac=this.low,ag=this.high;if(ac<this.max_low){ac=this.max_low}if(ag>this.max_high){ag=this.max_high}var ad=this.high-this.low;if(this.high!==0&&ad<this.min_separation){ag=ac+this.min_separation}this.low=Math.floor(ac);this.high=Math.ceil(ag);this.update_location(this.low,this.high);this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var af=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ah=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ae=13;this.overview_box.css({left:af,width:Math.max(ae,ah)}).show();if(ah<ae){this.overview_box.css("left",af-(ae-ah)/2)}if(this.overview_highlight){this.overview_highlight.css({left:af,width:ah})}ab.each(this.tracks_to_be_redrawn,function(ak){var ai=ak[0],aj=ak[1];if(ai){ai._draw(aj)}});this.tracks_to_be_redrawn=[];ab.each(this.label_tracks,function(ai){ai._draw()})},zoom_in:function(ad,ae){if(this.max_high===0||this.high-this.low<=this.min_separation){return}var af=this.high-this.low,ag=af/2+this.low,ac=(af/this.zoom_factor)/2;if(ad){ag=ad/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(ag-ac);this.high=Math.round(ag+ac);this.changed();this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ad=this.high-this.low,ae=ad/2+this.low,ac=(ad*this.zoom_factor)/2;this.low=Math.round(ae-ac);this.high=Math.round(ae+ac);this.changed();this.request_redraw()},resize_viewport:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height())},resize_window:function(){this.resize_viewport();this.request_redraw()},set_overview:function(ae){if(this.overview_drawable){if(this.overview_drawable.dataset.id===ae.dataset.id){return}this.overview_viewport.find(".track").remove()}var ad=ae.copy({content_div:this.overview_viewport}),ac=this;ad.header_div.hide();ad.is_overview=true;ac.overview_drawable=ad;this.overview_drawable.postdraw_actions=function(){ac.overview_highlight.show().height(ac.overview_drawable.content_div.height());ac.overview_viewport.height(ac.overview_drawable.content_div.height()+ac.overview_box.outerHeight());ac.overview_close.show();ac.resize_window()};ac.overview_drawable.request_draw();this.changed()},reset_overview:function(){$(".tooltip").remove();this.overview_viewport.find(".track-tile").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide();view.resize_window();view.overview_drawable=null}});var q=P.Tool.extend({defaults:{track:null},initialize:function(ac){P.Tool.prototype.initialize.call(this,ac);var ad=true;if(ac.tool_state!==undefined&&ac.tool_state.hidden!==undefined){ad=ac.tool_state.hidden}this.set("hidden",ad);this.remove_inputs(["data","hidden_data","conditional"])},state_dict:function(ac){return ab.extend(this.get_inputs_dict(),{hidden:!this.is_visible()})}});var t=Backbone.View.extend({events:{"change :input":"update_value"},render:function(){var ae=this.$el.addClass("param-row"),af=this.model;var ac=$("<div>").addClass("param-label").text(af.get("label")).appendTo(ae);var ad=$("<div/>").addClass("param-input").html(af.get("html")).appendTo(ae);ad.find(":input").val(af.get("value"));$("<div style='clear: both;'/>").appendTo(ae)},update_value:function(ac){this.model.set_value($(ac.target).val())}});var aa=Backbone.View.extend({initialize:function(ac){this.model.on("change:hidden",this.set_visible,this)},render:function(){var ad=this;tool=this.model,parent_div=this.$el.addClass("dynamic-tool").hide();parent_div.bind("drag",function(ah){ah.stopPropagation()}).click(function(ah){ah.stopPropagation()}).bind("dblclick",function(ah){ah.stopPropagation()}).keydown(function(ah){ah.stopPropagation()});var ae=$("<div class='tool-name'>").appendTo(parent_div).text(tool.get("name"));tool.get("inputs").each(function(ai){var ah=new t({model:ai});ah.render();parent_div.append(ah.$el)});parent_div.find("input").click(function(){$(this).select()});var af=$("<div>").addClass("param-row").appendTo(parent_div);var ag=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(af);var ac=$("<input type='submit'>").attr("value","Run on visible region").appendTo(af);ac.click(function(){ad.run_on_region()});ag.click(function(){ad.run_on_dataset()});if(tool.is_visible()){this.$el.show()}},set_visible:function(){this.$el.toggle(this.model.is_visible())},update_params:function(){for(var ac=0;ac<this.params.length;ac++){this.params[ac].update_value()}},run_on_dataset:function(){var ac=this.model;this.run({target_dataset_id:this.model.get("track").dataset.id,action:"rerun",tool_id:ac.id},null,function(ad){Galaxy.modal.show({title:ac.get("name")+" is Running",body:ac.get("name")+" is running on the complete dataset. Tool outputs are in dataset's history.",buttons:{Close:function(){Galaxy.modal.hide()}}})})},run_on_region:function(){var ad=this.model.get("track"),ag=this.model,ai=new w.GenomeRegion({chrom:ad.view.chrom,start:ad.view.low,end:ad.view.high}),aj={target_dataset_id:ad.dataset.id,action:"rerun",tool_id:ag.id,regions:[ai.toJSON()]},ah=ad,al=ag.get("name")+ah.tool_region_and_parameters_str(ai),ac;if(ah.container===view){var ak=new N(view,view,{name:ad.config.get_value("name")});var af=ah.container.replace_drawable(ah,ak,false);ak.container_div.insertBefore(ah.view.content_div.children()[af]);ak.add_drawable(ah);ah.container_div.appendTo(ak.content_div);ac=ak}else{ac=ah.container}var ae=new ah.constructor(view,ac,{name:al,hda_ldda:"hda"});ae.init_for_tool_data();ae.change_mode(ah.mode);ae.set_filters_manager(ah.filters_manager.copy(ae));ae.update_icons();ac.add_drawable(ae);ae.tiles_div.text("Starting job.");this.run(aj,ae,function(am){ae.set_dataset(new X.Dataset(am));ae.tiles_div.text("Running job.");ae.init()})},run:function(ac,ae,af){ac.inputs=this.model.get_inputs_dict();var ad=new j.ServerStateDeferred({ajax_settings:{url:galaxy_config.root+"api/tools",data:JSON.stringify(ac),dataType:"json",contentType:"application/json",type:"POST"},interval:2000,success_fn:function(ag){return ag!=="pending"}});$.when(ad.go()).then(function(ag){if(ag==="no converter"){ae.container_div.addClass("error");ae.content_div.text(H)}else{if(ag.error){ae.container_div.addClass("error");ae.content_div.text(x+ag.message)}else{af(ag)}}})}});var C=function(ac,ad){J.Scaler.call(this,ad);this.filter=ac};C.prototype.gen_val=function(ac){if(this.filter.high===Number.MAX_VALUE||this.filter.low===-Number.MAX_VALUE||this.filter.low===this.filter.high){return this.default_val}return((parseFloat(ac[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var b=function(ac,af,ag,ad,ae){this.track=ac;this.region=af;this.low=af.get("start");this.high=af.get("end");this.w_scale=ag;this.canvas=ad;this.html_elt=$("<div class='track-tile'/>").append(ad);this.data=ae;this.stale=false};b.prototype.predisplay_actions=function(){};var K=function(ac,af,ag,ad,ae){b.call(this,ac,af,ag,ad,ae)};K.prototype.predisplay_actions=function(){};var M=function(ae,aj,al,ad,ag,ah,am,ac,ai,af,ak){b.call(this,ae,aj,al,ad,ag);this.mode=ah;this.all_slotted=ac;this.feature_mapper=ai;this.has_icons=false;this.incomplete_features=af;this.seq_data=ak};o(M.prototype,b.prototype);M.prototype.predisplay_actions=function(){var ad=this,ac={};if(ad.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()}).mousemove(function(ao){if(!this.hovered){return}var aj=$(this).offset(),an=ao.pageX-aj.left,am=ao.pageY-aj.top,at=ad.feature_mapper.get_feature_data(an,am),ak=(at?at[0]:null);$(this).parents(".track-content").children(".overlay").children(".feature-popup").each(function(){if(!ak||$(this).attr("id")!==ak.toString()){$(this).remove()}});if(at){var af=ac[ak];if(!af){var ap={name:at[3],start:at[1],end:at[2],strand:at[4]},ai=ad.track.filters_manager.filters,ah;for(var al=0;al<ai.length;al++){ah=ai[al];ap[ah.name]=at[ah.index]}af=$("<div/>").attr("id",ak).addClass("feature-popup");var au=$("<table/>"),ar,aq,av;for(ar in ap){aq=ap[ar];av=$("<tr/>").appendTo(au);$("<th/>").appendTo(av).text(ar);$("<td/>").attr("align","left").appendTo(av).text(typeof(aq)==="number"?U(aq,2):aq)}af.append($("<div class='feature-popup-inner'>").append(au));ac[ak]=af}af.appendTo($(this).parents(".track-content").children(".overlay"));var ag=an+parseInt(ad.html_elt.css("left"),10)-af.width()/2,ae=am+parseInt(ad.html_elt.css("top"),10)+7;af.css("left",ag+"px").css("top",ae+"px")}else{if(!ao.isPropagationStopped()){ao.stopPropagation();$(this).siblings().each(function(){$(this).trigger(ao)})}}}).mouseleave(function(){$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()})};var f=function(ad,ac,ae){o(ae,{drag_handle_class:"draghandle"});p.call(this,ad,ac,ae);this.dataset=null;if(ae.dataset){this.dataset=(ae.dataset instanceof Backbone.Model?ae.dataset:new X.Dataset(ae.dataset))}this.dataset_check_type="converted_datasets_state";this.data_url_extra_params={};this.data_query_wait=("data_query_wait" in ae?ae.data_query_wait:I);this.data_manager=("data_manager" in ae?ae.data_manager:new w.GenomeDataManager({dataset:this.dataset,genome:new w.Genome({key:ad.dbkey,chroms_info:{chrom_info:ad.chrom_data}}),data_mode_compatible:this.data_and_mode_compatible,can_subset:this.can_subset}));this.min_height_px=16;this.max_height_px=800;this.visible_height_px=this.config.get_value("height");this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div);if(!("resize" in ae)||ae.resize){this.add_resize_handle()}}};o(f.prototype,p.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},p.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"application-dock-270",on_click_fn:function(ac){ac.view.set_overview(ac)}},p.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"ui-slider-050",on_click_fn:function(ac){if(ac.filters_manager.visible()){ac.filters_manager.clear_filters()}else{ac.filters_manager.init_filters()}ac.filters_manager.toggle()}},{name:"tools_icon",title:"Tool",css_class:"hammer",on_click_fn:function(ac){ac.tool.toggle();if(ac.tool.is_visible()){ac.set_name(ac.config.get_value("name")+ac.tool_region_and_parameters_str())}else{ac.revert_name()}$(".tooltip").remove()}},{name:"param_space_viz_icon",title:"Tool parameter space visualization",css_class:"arrow-split",on_click_fn:function(ac){var af='<strong>Tool</strong>: <%= track.tool.get("name") %><br/><strong>Dataset</strong>: <%= track.config.get_value("name") %><br/><strong>Region(s)</strong>: <select name="regions"><option value="cur">current viewing area</option><option value="bookmarks">bookmarks</option><option value="both">current viewing area and bookmarks</option></select>',ae=ab.template(af,{track:ac});var ah=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},ad=function(){var aj=$('select[name="regions"] option:selected').val(),al,ai=new w.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),ak=ab.map($(".bookmark"),function(am){return new w.GenomeRegion({from_str:$(am).children(".position").text()})});if(aj==="cur"){al=[ai]}else{if(aj==="bookmarks"){al=ak}else{al=[ai].concat(ak)}}Galaxy.modal.hide();window.location.href=galaxy_config.root+"visualization/sweepster?"+$.param({dataset_id:ac.dataset.id,hda_ldda:ac.dataset.get("hda_ldda"),regions:JSON.stringify(new Backbone.Collection(al).toJSON())})},ag=function(ai){if((ai.keyCode||ai.which)===27){ah()}else{if((ai.keyCode||ai.which)===13){ad()}}};Galaxy.modal.show({title:"Visualize tool parameter space and output from different parameter settings?",body:ae,buttons:{No:ah,Yes:ad}})}},p.prototype.action_icons_def[2]],can_draw:function(){return this.dataset&&p.prototype.can_draw.call(this)},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id)},set_dataset:function(ac){this.dataset=ac;this.data_manager.set("dataset",ac)},on_resize:function(){this.request_draw({clear_tile_cache:true})},add_resize_handle:function(){var ac=this;var af=false;var ae=false;var ad=$("<div class='track-resize'>");$(ac.container_div).hover(function(){if(ac.config.get_value("content_visible")){af=true;ad.show()}},function(){af=false;if(!ae){ad.hide()}});ad.hide().bind("dragstart",function(ag,ah){ae=true;ah.original_height=$(ac.content_div).height()}).bind("drag",function(ah,ai){var ag=Math.min(Math.max(ai.original_height+ai.deltaY,ac.min_height_px),ac.max_height_px);$(ac.tiles_div).css("height",ag);ac.visible_height_px=(ac.max_height_px===ag?0:ag);ac.on_resize()}).bind("dragend",function(ag,ah){ac.tile_cache.clear();ae=false;if(!af){ad.hide()}ac.config.set_value("height",ac.visible_height_px);ac.changed()}).appendTo(ac.container_div)},hide_contents:function(){this.tiles_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.tiles_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof W){return"LabelTrack"}else{if(this instanceof B){return"ReferenceTrack"}else{if(this instanceof g){return"LineTrack"}else{if(this instanceof S){return"ReadTrack"}else{if(this instanceof Z){return"VariantTrack"}else{if(this instanceof e){return"CompositeTrack"}else{if(this instanceof c){return"FeatureTrack"}}}}}}}return""},show_message:function(ac){this.tiles_div.remove();return $("<span/>").addClass("message").html(ac).appendTo(this.content_div)},init:function(ae){var ad=this;ad.enabled=false;ad.tile_cache.clear();ad.data_manager.clear();ad.content_div.children().remove();ad.container_div.removeClass("nodata error pending");ad.tiles_div=$("<div/>").addClass("tiles").appendTo(ad.content_div);if(!ad.dataset.id){return}var ac=$.Deferred(),af={hda_ldda:ad.dataset.get("hda_ldda"),data_type:this.dataset_check_type,chrom:ad.view.chrom,retry:ae};$.getJSON(this.dataset.url(),af,function(ag){if(!ag||ag==="error"||ag.kind==="error"){ad.container_div.addClass("error");var ah=ad.show_message(m);if(ag.message){ah.append($("<a href='javascript:void(0);'></a>").text("View error").click(function(){Galaxy.modal.show({title:"Trackster Error",body:"<pre>"+ag.message+"</pre>",buttons:{Close:function(){Galaxy.modal.hide()}}})}));ah.append($("<span/>").text(" "));ah.append($("<a href='javascript:void(0);'></a>").text("Try again").click(function(){ad.init(true)}))}}else{if(ag==="no converter"){ad.container_div.addClass("error");ad.show_message(H)}else{if(ag==="no data"||(ag.data!==undefined&&(ag.data===null||ag.data.length===0))){ad.container_div.addClass("nodata");ad.show_message(E)}else{if(ag==="pending"){ad.container_div.addClass("pending");ad.show_message(u);setTimeout(function(){ad.init()},ad.data_query_wait)}else{if(ag==="data"||ag.status==="data"){if(ag.valid_chroms){ad.valid_chroms=ag.valid_chroms;ad.update_icons()}ad.tiles_div.text(T);if(ad.view.chrom){ad.tiles_div.text("");ad.tiles_div.css("height",ad.visible_height_px+"px");ad.enabled=true;$.when.apply($,ad.predraw_init()).done(function(){ac.resolve();ad.container_div.removeClass("nodata error pending");ad.request_draw()})}else{ac.resolve()}}}}}}});this.update_icons();return ac},predraw_init:function(){var ac=this;return $.getJSON(ac.dataset.url(),{data_type:"data",stats:true,chrom:ac.view.chrom,low:0,high:ac.view.max_high,hda_ldda:ac.dataset.get("hda_ldda")},function(ad){var af=ad.data;if(af&&af.min&&af.max){var ae=af.min,ag=af.max;ae=Math.floor(Math.min(0,Math.max(ae,af.mean-2*af.sd)));ag=Math.ceil(Math.max(0,Math.min(ag,af.mean+2*af.sd)));ac.config.set_default_value("min_value",ae);ac.config.set_default_value("max_value",ag);ac.config.set_value("min_value",ae);ac.config.set_value("max_value",ag)}})},get_drawables:function(){return this}});var L=function(ae,ad,ag){f.call(this,ae,ad,ag);var ac=this;k(ac.container_div,ac.drag_handle_class,".group",ac);this.filters_manager=new h.FiltersManager(this,("filters" in ag?ag.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=(ag.tool?new q(ab.extend(ag.tool,{track:this,tool_state:ag.tool_state})):null);this.tile_cache=new w.Cache(O);this.left_offset=0;if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){var af=new aa({model:this.tool});af.render();this.dynamic_tool_div=af.$el;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);if(!this.config.get_value("content_visible")){this.tiles_div.hide()}this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(ag.mode){this.change_mode(ag.mode)}};o(L.prototype,p.prototype,f.prototype,{action_icons_def:f.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(ac){$(".tooltip").remove();ac.slotters[ac.view.resolution_px_b].max_rows*=2;ac.request_draw({clear_tile_cache:true})},hide:true}]),copy:function(ac){var ad=this.to_dict();o(ad,{data_manager:this.data_manager});var ae=new this.constructor(this.view,ac,ad);ae.change_mode(this.mode);ae.enabled=this.enabled;return ae},set_filters_manager:function(ac){this.filters_manager=ac;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),dataset:{id:this.dataset.id,hda_ldda:this.dataset.get("hda_ldda")},prefs:this.config.to_key_value_dict(),mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},set_min_max:function(){var ac=this;return $.getJSON(ac.dataset.url(),{data_type:"data",stats:true,chrom:ac.view.chrom,low:0,high:ac.view.max_high,hda_ldda:ac.dataset.get("hda_ldda")},function(ad){var af=ad.data;if(isNaN(parseFloat(ac.config.get_value("min_value")))||isNaN(parseFloat(ac.config.get_value("max_value")))){var ae=af.min,ag=af.max;ae=Math.floor(Math.min(0,Math.max(ae,af.mean-2*af.sd)));ag=Math.ceil(Math.max(0,Math.min(ag,af.mean+2*af.sd)));ac.config.set_value("min_value",ae);ac.config.set_value("max_value",ag)}})},change_mode:function(ad){var ac=this;ac.mode=ad;ac.config.set_value("mode",ad);if(ad==="Auto"){this.data_manager.clear()}ac.request_draw({clear_tile_cache:true});this.action_icons.mode_icon.attr("title","Set display mode (now: "+ac.mode+")");return ac},update_icons:function(){var ac=this;ac.action_icons.filters_icon.toggle(ac.filters_available);ac.action_icons.tools_icon.toggle(ac.tool!==null);ac.action_icons.param_space_viz_icon.toggle(ac.tool!==null)},_gen_tile_cache_key:function(ad,ac){return ad+"_"+ac},request_draw:function(ac){if(ac&&ac.clear_tile_cache){this.tile_cache.clear()}this.view.request_redraw(ac,this)},before_draw:function(){this.max_height_px=0},_draw:function(aq){if(!this.can_draw()){return}var an=aq&&aq.clear_after,al=this.view.low,ag=this.view.high,aj=ag-al,ad=this.view.container.width(),ap=this.view.resolution_px_b,af=1/ap;if(this.is_overview){al=this.view.max_low;ag=this.view.max_high;ap=ad/(view.max_high-view.max_low);af=1/ap}this.before_draw();this.tiles_div.children().addClass("remove");var ac=Math.floor(al/(af*Q)),ak,am,ah,ai=[],ao=[];while((ac*Q*af)<ag){ak=Math.floor(ac*Q*af);am=new w.GenomeRegion({chrom:this.view.chrom,start:ak,end:Math.min(ak+Math.ceil(Q*af),this.view.max_high)});ah=this.draw_helper(am,ap,aq);ai.push(ah);$.when(ah).then(function(ar){ao.push(ar)});ac+=1}if(!an){this.tiles_div.children(".remove").removeClass("remove").remove()}var ae=this;$.when.apply($,ai).then(function(){ae.tiles_div.children(".remove").remove();ao=ab.filter(ao,function(ar){return ar!==null});if(ao.length!==0){ae.postdraw_actions(ao,ad,ap,an)}})},_add_yaxis_label:function(af,ah){var ad=this,ag=(af==="max"?"top":"bottom"),ai=(af==="max"?"max":"min"),ac=(af==="max"?"max_value":"min_value"),ae=this.container_div.find(".yaxislabel."+ag);ah=ah||function(){ad.request_draw({clear_tile_cache:true})};if(ae.length!==0){ae.text(ad.config.get_value(ac))}else{ae=$("<div/>").text(ad.config.get_value(ac)).make_text_editable({num_cols:12,on_finish:function(aj){$(".tooltip").remove();ad.config.set_value(ac,aj);ah()},help_text:"Set "+ai+" value"}).addClass("yaxislabel "+ag).css("color",this.config.get_value("label_color"));this.container_div.prepend(ae)}},postdraw_actions:function(af,ag,ai,ac){var ae=ab.filter(af,function(aj){return(aj instanceof K)});if(ae.length>0){this.max_height_px=0;var ad=this;ab.each(af,function(aj){if(!(aj instanceof K)){aj.html_elt.remove();ad.draw_helper(aj.region,ai,{force:true,mode:"Coverage"})}});ad._add_yaxis_label("max")}else{this.container_div.find(".yaxislabel").remove();var ah=ab.find(af,function(aj){return aj.has_icons});if(ah){ab.each(af,function(aj){if(!aj.has_icons){aj.html_elt.css("padding-top",D)}})}}},get_mode:function(ac){return this.mode},update_auto_mode:function(ac){},_get_drawables:function(){return[this]},draw_helper:function(al,an,ao){if(!ao){ao={}}var ad=ao.force,ai=ao.mode||this.mode,af=1/an,ae=this,ag=this._get_drawables(),am=this._gen_tile_cache_key(an,al),ah=function(ap){return(ap&&"track" in ap)};var aj=(ad?undefined:ae.tile_cache.get_elt(am));if(aj){if(ah(aj)){ae.show_tile(aj,an)}return aj}if(ao.data_fetch===false){return null}var ak=function(){var ap=(ab.find(A,function(ar){return ar===ai})?"Coverage":ai);var aq=ab.map(ag,function(ar){return ar.data_manager.get_data(al,ap,af,ae.data_url_extra_params)});if(view.reference_track){aq.push(view.reference_track.data_manager.get_data(al,ai,af,view.reference_track.data_url_extra_params))}return aq};var ac=$.Deferred();ae.tile_cache.set_elt(am,ac);$.when.apply($,ak()).then(function(){var ap=ak(),av=ap,aB;if(ab.find(ap,function(aD){return j.is_deferred(aD)})){ae.tile_cache.set_elt(am,undefined);$.when(ae.draw_helper(al,an,ao)).then(function(aD){ac.resolve(aD)});return}if(view.reference_track){aB=view.reference_track.data_manager.subset_entry(ap.pop(),al)}var aw=[],at=[];ab.each(ag,function(aG,aD){var aF=aG.mode,aE=av[aD];if(aF==="Auto"){aF=aG.get_mode(aE);aG.update_auto_mode(aF)}aw.push(aF);at.push(aG.get_canvas_height(aE,aF,an,aq))});var au=ae.view.canvas_manager.new_canvas(),ax=al.get("start"),aC=al.get("end"),ar=0,aq=Math.ceil((aC-ax)*an)+ae.left_offset,az=ab.max(at),ay;au.width=aq;au.height=(ao.height||az);var aA=au.getContext("2d");aA.translate(ae.left_offset,0);if(ag.length>1){aA.globalAlpha=0.5;aA.globalCompositeOperation="source-over"}ab.each(ag,function(aE,aD){ay=aE.draw_tile(av[aD],aA,aw[aD],al,an,aB)});if(ay!==undefined){ae.tile_cache.set_elt(am,ay);ae.show_tile(ay,an)}ac.resolve(ay)});return ac},get_canvas_height:function(ac,ae,af,ad){return this.visible_height_px},_draw_line_track_tile:function(ac,ae,ah,ag,ai){var af=ae.canvas,ad=new J.LinePainter(ac.data,ag.get("start"),ag.get("end"),this.config.to_key_value_dict(),ah);ad.draw(ae,af.width,af.height,ai);return new K(this,ag,ai,af,ac.data)},draw_tile:function(ac,ad,ag,af,ah,ae){},show_tile:function(ae,ah){var ad=this,ac=ae.html_elt;ae.predisplay_actions();var ag=(ae.low-(this.is_overview?this.view.max_low:this.view.low))*ah;if(this.left_offset){ag-=this.left_offset}ac.css("left",ag);if(ac.hasClass("remove")){ac.removeClass("remove")}else{this.tiles_div.append(ac)}ac.css("height","auto");this.max_height_px=Math.max(this.max_height_px,ac.height()-2);ac.parent().children().css("height",this.max_height_px+"px");var af=this.max_height_px;if(this.visible_height_px!==0){af=Math.min(this.max_height_px,this.visible_height_px)}this.tiles_div.css("height",af+"px")},tool_region_and_parameters_str:function(af){var ac=this,ae=(af!==undefined?af.toString():"all"),ad=ab.values(ac.tool.get_inputs_dict()).join(", ");return" - region=["+ae+"], parameters=["+ad+"]"},data_and_mode_compatible:function(ac,ad){if(ad==="Auto"){return true}else{if(ad==="Coverage"){return ac.dataset_type==="bigwig"}else{if(ac.dataset_type==="bigwig"||ac.extra_info==="no_detail"){return false}else{return true}}}},can_subset:function(ac){if(ac.message||ac.extra_info==="no_detail"){return false}else{if(ac.dataset_type==="bigwig"){return(ac.data[1][0]-ac.data[0][0]===1)}}return true},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data");this.data_query_wait=1000;this.dataset_check_type="state"}});var W=function(ad,ac){f.call(this,ad,ac,{resize:false,header:false});this.container_div.addClass("label-track")};o(W.prototype,f.prototype,{init:function(){this.enabled=true},predraw_init:function(){},_draw:function(ag){var ae=this.view,af=ae.high-ae.low,aj=Math.floor(Math.pow(10,Math.floor(Math.log(af)/Math.log(10)))),ac=Math.floor(ae.low/aj)*aj,ah=this.view.container.width(),ad=$("<div/>").addClass("label-container");while(ac<ae.high){var ai=(ac-ae.low)/af*ah;ad.append($("<div/>").addClass("label").text(commatize(ac)).css({left:ai}));ac+=aj}this.content_div.children(":first").remove();this.content_div.append(ad)}});var e=function(ad,ac,ag){L.call(this,ad,ac,ag);this.drawables=[];if("drawables" in ag){var af;for(var ae=0;ae<ag.drawables.length;ae++){af=ag.drawables[ae];this.drawables[ae]=n(af,ad,null);if(af.left_offset>this.left_offset){this.left_offset=af.left_offset}}this.enabled=true}ab.each(this.drawables,function(ah){if(ah instanceof c||ah instanceof S){ah.change_mode("Coverage")}});this.update_icons();this.obj_type="CompositeTrack"};o(e.prototype,L.prototype,{display_modes:A,config_params:ab.union(p.prototype.config_params,[{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:30,hidden:true}]),action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(ac){$(".tooltip").remove();ac.show_group()}}].concat(L.prototype.action_icons_def),to_dict:y.prototype.to_dict,add_drawable:y.prototype.add_drawable,unpack_drawables:y.prototype.unpack_drawables,config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},on_resize:function(){var ac=this.visible_height_px;ab.each(this.drawables,function(ad){ad.visible_height_px=ac});f.prototype.on_resize.call(this)},change_mode:function(ac){L.prototype.change_mode.call(this,ac);for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad].change_mode(ac)}},init:function(){var ae=[];for(var ad=0;ad<this.drawables.length;ad++){ae.push(this.drawables[ad].init())}var ac=this;$.when.apply($,ae).then(function(){ac.enabled=true;ac.request_draw()})},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide();this.action_icons.param_space_viz_icon.hide()},can_draw:p.prototype.can_draw,_get_drawables:function(){return this.drawables},show_group:function(){var af=new N(this.view,this.container,{name:this.config.get_value("name")}),ac;for(var ae=0;ae<this.drawables.length;ae++){ac=this.drawables[ae];ac.update_icons();af.add_drawable(ac);ac.container=af;af.content_div.append(ac.container_div)}var ad=this.container.replace_drawable(this,af,true);af.request_draw({clear_tile_cache:true})},before_draw:function(){var ad=ab.min(ab.map(this.drawables,function(ae){return ae.config.get_value("min_value")})),ac=ab.max(ab.map(this.drawables,function(ae){return ae.config.get_value("max_value")}));this.config.set_value("min_value",ad);this.config.set_value("max_value",ac);ab.each(this.drawables,function(ae){ae.config.set_value("min_value",ad);ae.config.set_value("max_value",ac)})},update_all_min_max:function(){var ad=this,ac=this.config.get_value("min_value"),ae=this.config.get_value("max_value");ab.each(this.drawables,function(af){af.config.set_value("min_value",ac);af.config.set_value("max_value",ae)});this.request_draw({clear_tile_cache:true})},postdraw_actions:function(ai,ac,al,ah){var ag=-1,ae;for(ae=0;ae<ai.length;ae++){var aj=ai[ae].html_elt.find("canvas").height();if(aj>ag){ag=aj}}for(ae=0;ae<ai.length;ae++){var af=ai[ae];if(af.html_elt.find("canvas").height()!==ag){this.draw_helper(af.region,al,{force:true,height:ag});af.html_elt.remove()}}var ad=this,ak=function(){ad.update_all_min_max()};this._add_yaxis_label("min",ak);this._add_yaxis_label("max",ak)}});var B=function(ac){L.call(this,ac,{content_div:ac.top_labeltrack},{resize:false,header:false});this.left_offset=ac.canvas_manager.char_width_px;this.container_div.addClass("reference-track");this.data_url=galaxy_config.root+"api/genomes/"+this.view.dbkey;this.data_url_extra_params={reference:true};this.data_manager=new w.GenomeReferenceDataManager({data_url:this.data_url,can_subset:this.can_subset});this.hide_contents()};o(B.prototype,p.prototype,L.prototype,{config_params:ab.union(p.prototype.config_params,[{key:"height",type:"int",default_value:13,hidden:true}]),init:function(){this.data_manager.clear();this.enabled=true},predraw_init:function(){},can_draw:p.prototype.can_draw,draw_helper:function(af,ah,ad){var ac=this.tiles_div.is(":visible"),ag,ae=null;if(ah>this.view.canvas_manager.char_width_px){this.tiles_div.show();ag=true;ae=L.prototype.draw_helper.call(this,af,ah,ad)}else{ag=false;this.tiles_div.hide()}if(ac!==ag){this.view.resize_viewport()}return ae},can_subset:function(ac){return true},draw_tile:function(ae,ak,af,ah,al){var ad=this.data_manager.subset_entry(ae,ah),aj=ad.data;var ac=ak.canvas;ak.font=ak.canvas.manager.default_font;ak.textAlign="center";for(var ag=0,ai=aj.length;ag<ai;ag++){ak.fillStyle=this.view.get_base_color(aj[ag]);ak.fillText(aj[ag],Math.floor(ag*al),10)}return new b(this,ah,al,ac,ad)}});var g=function(ad,ac,ae){this.mode="Histogram";L.call(this,ad,ac,ae)};o(g.prototype,p.prototype,L.prototype,{display_modes:A,config_params:ab.union(p.prototype.config_params,[{key:"color",label:"Color",type:"color"},{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:30,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},before_draw:function(){},draw_tile:function(ac,ad,af,ae,ag){return this._draw_line_track_tile(ac,ad,af,ae,ag)},can_subset:function(ac){return(ac.data[1][0]-ac.data[0][0]===1)},postdraw_actions:function(ad,ae,af,ac){this._add_yaxis_label("max");this._add_yaxis_label("min")}});var r=function(ad,ac,ae){this.mode="Heatmap";L.call(this,ad,ac,ae)};o(r.prototype,p.prototype,L.prototype,{display_modes:["Heatmap"],config_params:ab.union(p.prototype.config_params,[{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"min_value",label:"Min Value",type:"float",default_value:-1},{key:"max_value",label:"Max Value",type:"float",default_value:1},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:500,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},draw_tile:function(ac,ae,ah,ag,ai){var af=ae.canvas,ad=new J.DiagonalHeatmapPainter(ac.data,ag.get("start"),ag.get("end"),this.config.to_key_value_dict(),ah);ad.draw(ae,af.width,af.height,ai);return new b(this,ag,ai,af,ac.data)}});var c=function(ad,ac,ae){L.call(this,ad,ac,ae);this.container_div.addClass("feature-track");this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};o(c.prototype,p.prototype,L.prototype,{display_modes:["Auto","Coverage","Dense","Squish","Pack"],config_params:ab.union(p.prototype.config_params,[{key:"block_color",label:"Block color",type:"color"},{key:"reverse_strand_color",label:"Antisense strand color",type:"color"},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"connector_style",label:"Connector style",type:"select",default_value:"fishbones",options:[{label:"Line with arrows",value:"fishbone"},{label:"Arcs",value:"arcs"}]},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.set_painter_from_config();this.request_draw({clear_tile_cache:true})},set_painter_from_config:function(){if(this.config.get_value("connector_style")==="arcs"){this.painter=J.ArcLinkedFeaturePainter}else{this.painter=J.LinkedFeaturePainter}},postdraw_actions:function(ao,af,aq,an){L.prototype.postdraw_actions.call(this,ao,af,aq,an);var ah=this,ai,am=ab.filter(ao,function(ar){return(ar instanceof K)});if(am.length===0){var ac={};ab.each(ab.pluck(ao,"incomplete_features"),function(ar){ab.each(ar,function(at){ac[at[0]]=at})});var ap=this;ab.each(ao,function(ar){ap.draw_tile({data:ab.values(ac)},ar.canvas.getContext("2d"),ar.mode,ar.region,aq,ar.seq_data,true)})}if(ah.filters_manager){var ag=ah.filters_manager.filters,ak;for(ak=0;ak<ag.length;ak++){ag[ak].update_ui_elt()}var aj=false,al,ae;for(ai=0;ai<ao.length;ai++){if(ao[ai].data.length){al=ao[ai].data[0];for(ak=0;ak<ag.length;ak++){ae=ag[ak];if(ae.applies_to(al)&&ae.min!==ae.max){aj=true;break}}}}if(ah.filters_available!==aj){ah.filters_available=aj;if(!ah.filters_available){ah.filters_manager.hide()}ah.update_icons()}}if(ao[0] instanceof M){var ad=true;for(ai=0;ai<ao.length;ai++){if(!ao[ai].all_slotted){ad=false;break}}this.action_icons.show_more_rows_icon.toggle(!ad)}else{this.action_icons.show_more_rows_icon.hide()}},update_auto_mode:function(ac){if(this.mode==="Auto"){if(ac==="no_detail"){ac="feature spans"}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+ac+")")}},incremental_slots:function(ag,ac,af){var ad=this.view.canvas_manager.dummy_context,ae=this.slotters[ag];if(!ae||(ae.mode!==af)){ae=new (s.FeatureSlotter)(ag,af,z,function(ah){return ad.measureText(ah)});this.slotters[ag]=ae}return ae.slot_features(ac)},get_mode:function(ac){if(ac.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>G){mode="Squish"}else{mode="Pack"}}return mode},get_canvas_height:function(ac,ag,ah,ad){if(ag==="Coverage"||ac.dataset_type==="bigwig"){return this.summary_draw_height}else{var af=this.incremental_slots(ah,ac.data,ag);var ae=new (this.painter)(null,null,null,this.config.to_key_value_dict(),ag);return Math.max(this.min_height_px,ae.get_required_height(af,ad))}},draw_tile:function(an,at,aq,ag,ak,af,ao){var ar=this,ae=at.canvas,az=ag.get("start"),ad=ag.get("end"),ah=this.left_offset;if(an.dataset_type==="bigwig"){return this._draw_line_track_tile(an,at,aq,ag,ak)}var aj=[],ap=this.slotters[ak].slots;all_slotted=true;if(an.data){var al=this.filters_manager.filters;for(var au=0,aw=an.data.length;au<aw;au++){var ai=an.data[au];var av=false;var am;for(var ay=0,aD=al.length;ay<aD;ay++){am=al[ay];am.update_attrs(ai);if(!am.keep(ai)){av=true;break}}if(!av){aj.push(ai);if(!(ai[0] in ap)){all_slotted=false}}}}var aC=(this.filters_manager.alpha_filter?new C(this.filters_manager.alpha_filter):null),aA=(this.filters_manager.height_filter?new C(this.filters_manager.height_filter):null),aB=new (this.painter)(aj,az,ad,this.config.to_key_value_dict(),aq,aC,aA,af,function(aE){return ar.view.get_base_color(aE)});var ax=null;at.fillStyle=this.config.get_value("block_color");at.font=at.canvas.manager.default_font;at.textAlign="right";if(an.data){var ac=aB.draw(at,ae.width,ae.height,ak,ap);ax=ac.feature_mapper;incomplete_features=ac.incomplete_features;ax.translation=-ah}if(!ao){return new M(ar,ag,ak,ae,an.data,aq,an.message,all_slotted,ax,incomplete_features,af)}}});var Z=function(ad,ac,ae){L.call(this,ad,ac,ae);this.painter=J.VariantPainter;this.summary_draw_height=30;this.left_offset=30};o(Z.prototype,p.prototype,L.prototype,{display_modes:["Auto","Coverage","Dense","Squish","Pack"],config_params:ab.union(p.prototype.config_params,[{key:"color",label:"Histogram color",type:"color"},{key:"show_sample_data",label:"Show sample data",type:"bool",default_value:true},{key:"show_labels",label:"Show summary and sample labels",type:"bool",default_value:true},{key:"summary_height",label:"Locus summary height",type:"float",default_value:20},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},draw_tile:function(ac,af,ah,ag,ai){if(ac.dataset_type==="bigwig"){return this._draw_line_track_tile(ac,af,"Histogram",ag,ai)}else{var ae=this.view,ad=new (this.painter)(ac.data,ag.get("start"),ag.get("end"),this.config.to_key_value_dict(),ah,function(aj){return ae.get_base_color(aj)});ad.draw(af,af.canvas.width,af.canvas.height,ai);return new b(this,ag,ai,af.canvas,ac.data)}},get_canvas_height:function(ac,ag,ah,ae){if(ac.dataset_type==="bigwig"){return this.summary_draw_height}else{var ad=(this.dataset.get_metadata("sample_names")?this.dataset.get_metadata("sample_names").length:0);if(ad===0&&ac.data.length!==0){ad=ac.data[0][7].match(/,/g);if(ad===null){ad=1}else{ad=ad.length+1}}var af=new (this.painter)(null,null,null,this.config.to_key_value_dict(),ag);return af.get_required_height(ad)}},predraw_init:function(){var ac=[f.prototype.predraw_init.call(this)];if(!this.dataset.get_metadata("sample_names")){ac.push(this.dataset.fetch())}return ac},postdraw_actions:function(ag,ah,aj,ad){L.prototype.postdraw_actions.call(this,ag,ah,aj,ad);var af=ab.filter(ag,function(ak){return(ak instanceof K)});var ae=this.dataset.get_metadata("sample_names");if(af.length===0&&this.config.get_value("show_labels")&&ae&&ae.length>1){var ac;if(this.container_div.find(".yaxislabel.variant").length===0){ac=this.config.get_value("summary_height")/2;this.tiles_div.prepend($("<div/>").text("Summary").addClass("yaxislabel variant top").css({"font-size":ac+"px",top:(this.config.get_value("summary_height")-ac)/2+"px"}));if(this.config.get_value("show_sample_data")){var ai=ae.join("<br/>");this.tiles_div.prepend($("<div/>").html(ai).addClass("yaxislabel variant top sample").css({top:this.config.get_value("summary_height")}))}}ac=(this.mode==="Squish"?5:10)+"px";$(this.tiles_div).find(".sample").css({"font-size":ac,"line-height":ac});$(this.tiles_div).find(".yaxislabel").css("color",this.config.get_value("label_color"))}else{this.container_div.find(".yaxislabel.variant").remove()}}});var S=function(ad,ac,ae){c.call(this,ad,ac,ae);this.painter=J.ReadPainter;this.update_icons()};o(S.prototype,p.prototype,L.prototype,c.prototype,{config_params:ab.union(p.prototype.config_params,[{key:"block_color",label:"Histogram color",type:"color"},{key:"detail_block_color",label:"Sense strand block color",type:"color",default_value:"#AAAAAA"},{key:"reverse_strand_color",label:"Antisense strand block color",type:"color",default_value:"#DDDDDD"},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})}});var d={CompositeTrack:e,DrawableGroup:N,DiagonalHeatmapTrack:r,FeatureTrack:c,LineTrack:g,ReadTrack:S,VariantTrack:Z,VcfTrack:Z};var n=function(ae,ad,ac){if("copy" in ae){return ae.copy(ac)}else{var af=ae.obj_type;if(!af){af=ae.track_type}return new d[af](ad,ac,ae)}};return{TracksterView:Y,DrawableGroup:N,LineTrack:g,FeatureTrack:c,DiagonalHeatmapTrack:r,ReadTrack:S,VariantTrack:Z,CompositeTrack:e,object_from_template:n}});
\ No newline at end of file
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/packed/viz/trackster_ui.js
--- a/static/scripts/packed/viz/trackster_ui.js
+++ b/static/scripts/packed/viz/trackster_ui.js
@@ -1,1 +1,1 @@
-define(["base","libs/underscore","viz/trackster/slotting","viz/trackster/painters","viz/trackster/tracks","viz/visualization"],function(a,f,e,c,d,h){var j=d.object_from_template;var b=function(l,k){if(!k){k={}}var m=new IconButtonCollection(f.map(l,function(n){return new IconButton(f.extend(n,k))}));return new IconButtonMenuView({collection:m})};var g=a.Base.extend({initialize:function(k){this.baseURL=k},createButtonMenu:function(){var k=this,l=b([{icon_class:"plus-button",title:"Add tracks",on_click:function(){h.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":view.dbkey},function(m){f.each(m,function(n){view.add_drawable(j(n,view,view))})})}},{icon_class:"block--plus",title:"Add group",on_click:function(){view.add_drawable(new d.DrawableGroup(view,view,{name:"New Group"}))}},{icon_class:"bookmarks",title:"Bookmarks",on_click:function(){force_right_panel(($("div#right").css("right")=="0px"?"hide":"show"))}},{icon_class:"globe",title:"Circster",on_click:function(){window.location=k.baseURL+"visualization/circster?id="+view.vis_id}},{icon_class:"disk--arrow",title:"Save",on_click:function(){Galaxy.modal.show({title:"Saving...",body:"progress"});var m=[];$(".bookmark").each(function(){m.push({position:$(this).children(".position").text(),annotation:$(this).children(".annotation").text()})});var n=(view.overview_drawable?view.overview_drawable.config.get_value("name"):null),o={view:view.to_dict(),viewport:{chrom:view.chrom,start:view.low,end:view.high,overview:n},bookmarks:m};$.ajax({url:galaxy_config.root+"visualization/save",type:"POST",dataType:"json",data:{id:view.vis_id,title:view.config.get_value("name"),dbkey:view.dbkey,type:"trackster",vis_json:JSON.stringify(o)}}).success(function(p){Galaxy.modal.hide();view.vis_id=p.vis_id;view.has_changes=false;window.history.pushState({},"",p.url+window.location.hash)}).error(function(){Galaxy.modal.show({title:"Could Not Save",body:"Could not save visualization. Please try again later.",buttons:{Cancel:function(){Galaxy.modal.hide()}}})})}},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location=galaxy_config.root+"visualization"}}],{tooltip_config:{placement:"bottom"}});this.buttonMenu=l;return l},add_bookmarks:function(){var k=this,l=this.baseURL;Galaxy.modal.show({title:"Select dataset for new bookmarks",body:"progress"});$.ajax({url:this.baseURL+"/visualization/list_histories",data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(m){Galaxy.modal.show({title:"Select dataset for new bookmarks",body:m,buttons:{Cancel:function(){Galaxy.modal.hide()},Insert:function(){$("input[name=id]:checked,input[name=ldda_ids]:checked").first().each(function(){var n,o=$(this).val();if($(this).attr("name")==="id"){n={hda_id:o}}else{n={ldda_id:o}}$.ajax({url:this.baseURL+"/visualization/bookmarks_from_dataset",data:n,dataType:"json"}).then(function(p){for(i=0;i<p.data.length;i++){var q=p.data[i];k.add_bookmark(q[0],q[1])}})});Galaxy.modal.hide()}}})}})},add_bookmark:function(o,m,k){var q=$("#right .unified-panel-body"),s=$("<div/>").addClass("bookmark").appendTo(q);var t=$("<div/>").addClass("position").appendTo(s),p=$("<a href=''/>").text(o).appendTo(t).click(function(){view.go_to(o);return false}),n=$("<div/>").text(m).appendTo(s);if(k){var r=$("<div/>").addClass("delete-icon-container").prependTo(s).click(function(){s.slideUp("fast");s.remove();view.has_changes=true;return false}),l=$("<a href=''/>").addClass("icon-button delete").appendTo(r);n.make_text_editable({num_rows:3,use_textarea:true,help_text:"Edit bookmark note"}).addClass("annotation")}view.has_changes=true;return s},create_visualization:function(p,k,o,q,n){var m=this,l=new d.TracksterView(p);l.editor=true;$.when(l.load_chroms_deferred).then(function(B){if(k){var z=k.chrom,r=k.start,w=k.end,t=k.overview;if(z&&(r!==undefined)&&w){l.change_chrom(z,r,w)}else{l.change_chrom(B[0].chrom)}}else{l.change_chrom(B[0].chrom)}if(o){var u,s,v;for(var x=0;x<o.length;x++){l.add_drawable(j(o[x],l,l))}}l.update_intro_div();var A;for(var x=0;x<l.drawables.length;x++){if(l.drawables[x].config.get_value("name")===t){l.set_overview(l.drawables[x]);break}}if(q){var y;for(var x=0;x<q.length;x++){y=q[x];m.add_bookmark(y.position,y.annotation,n)}}l.has_changes=false});this.set_up_router({view:l});return l},set_up_router:function(k){new h.TrackBrowserRouter(k);Backbone.history.start()},init_keyboard_nav:function(k){$(document).keyup(function(l){if($(l.srcElement).is(":input")){return}switch(l.which){case 37:k.move_fraction(0.25);break;case 38:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()-20);break;case 39:k.move_fraction(-0.25);break;case 40:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()+20);break}})}});return{object_from_template:j,TracksterUI:g}});
\ No newline at end of file
+define(["base","libs/underscore","viz/trackster/slotting","viz/trackster/painters","viz/trackster/tracks","viz/visualization"],function(a,f,e,c,d,h){var j=d.object_from_template;var b=function(l,k){if(!k){k={}}var m=new IconButtonCollection(f.map(l,function(n){return new IconButton(f.extend(n,k))}));return new IconButtonMenuView({collection:m})};var g=a.Base.extend({initialize:function(k){this.baseURL=k},createButtonMenu:function(){var k=this,l=b([{icon_class:"plus-button",title:"Add tracks",on_click:function(){h.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":view.dbkey},function(m){f.each(m,function(n){view.add_drawable(j(n,view,view))})})}},{icon_class:"block--plus",title:"Add group",on_click:function(){view.add_drawable(new d.DrawableGroup(view,view,{name:"New Group"}))}},{icon_class:"bookmarks",title:"Bookmarks",on_click:function(){force_right_panel(($("div#right").css("right")=="0px"?"hide":"show"))}},{icon_class:"globe",title:"Circster",on_click:function(){window.location=k.baseURL+"visualization/circster?id="+view.vis_id}},{icon_class:"disk--arrow",title:"Save",on_click:function(){Galaxy.modal.show({title:"Saving...",body:"progress"});var m=[];$(".bookmark").each(function(){m.push({position:$(this).children(".position").text(),annotation:$(this).children(".annotation").text()})});var n=(view.overview_drawable?view.overview_drawable.config.get_value("name"):null),o={view:view.to_dict(),viewport:{chrom:view.chrom,start:view.low,end:view.high,overview:n},bookmarks:m};$.ajax({url:galaxy_config.root+"visualization/save",type:"POST",dataType:"json",data:{id:view.vis_id,title:view.config.get_value("name"),dbkey:view.dbkey,type:"trackster",vis_json:JSON.stringify(o)}}).success(function(p){Galaxy.modal.hide();view.vis_id=p.vis_id;view.has_changes=false;window.history.pushState({},"",p.url+window.location.hash)}).error(function(){Galaxy.modal.show({title:"Could Not Save",body:"Could not save visualization. Please try again later.",buttons:{Cancel:function(){Galaxy.modal.hide()}}})})}},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location=galaxy_config.root+"visualization"}}],{tooltip_config:{placement:"bottom"}});this.buttonMenu=l;return l},add_bookmarks:function(){var k=this,l=this.baseURL;Galaxy.modal.show({title:"Select dataset for new bookmarks",body:"progress"});$.ajax({url:this.baseURL+"/visualization/list_histories",data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(m){Galaxy.modal.show({title:"Select dataset for new bookmarks",body:m,buttons:{Cancel:function(){Galaxy.modal.hide()},Insert:function(){$("input[name=id]:checked,input[name=ldda_ids]:checked").first().each(function(){var n,o=$(this).val();if($(this).attr("name")==="id"){n={hda_id:o}}else{n={ldda_id:o}}$.ajax({url:this.baseURL+"/visualization/bookmarks_from_dataset",data:n,dataType:"json"}).then(function(p){for(i=0;i<p.data.length;i++){var q=p.data[i];k.add_bookmark(q[0],q[1])}})});Galaxy.modal.hide()}}})}})},add_bookmark:function(o,m,k){var q=$("#right .unified-panel-body"),s=$("<div/>").addClass("bookmark").appendTo(q);var t=$("<div/>").addClass("position").appendTo(s),p=$("<a href=''/>").text(o).appendTo(t).click(function(){view.go_to(o);return false}),n=$("<div/>").text(m).appendTo(s);if(k){var r=$("<div/>").addClass("delete-icon-container").prependTo(s).click(function(){s.slideUp("fast");s.remove();view.has_changes=true;return false}),l=$("<a href=''/>").addClass("icon-button delete").appendTo(r);n.make_text_editable({num_rows:3,use_textarea:true,help_text:"Edit bookmark note"}).addClass("annotation")}view.has_changes=true;return s},create_visualization:function(p,k,o,q,n){var m=this,l=new d.TracksterView(f.extend(p,{header:false}));l.editor=true;$.when(l.load_chroms_deferred).then(function(B){if(k){var z=k.chrom,r=k.start,w=k.end,t=k.overview;if(z&&(r!==undefined)&&w){l.change_chrom(z,r,w)}else{l.change_chrom(B[0].chrom)}}else{l.change_chrom(B[0].chrom)}if(o){var u,s,v;for(var x=0;x<o.length;x++){l.add_drawable(j(o[x],l,l))}}l.update_intro_div();var A;for(var x=0;x<l.drawables.length;x++){if(l.drawables[x].config.get_value("name")===t){l.set_overview(l.drawables[x]);break}}if(q){var y;for(var x=0;x<q.length;x++){y=q[x];m.add_bookmark(y.position,y.annotation,n)}}l.has_changes=false});this.set_up_router({view:l});return l},set_up_router:function(k){new h.TrackBrowserRouter(k);Backbone.history.start()},init_keyboard_nav:function(k){$(document).keyup(function(l){if($(l.srcElement).is(":input")){return}switch(l.which){case 37:k.move_fraction(0.25);break;case 38:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()-20);break;case 39:k.move_fraction(-0.25);break;case 40:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()+20);break}})}});return{object_from_template:j,TracksterUI:g}});
\ No newline at end of file
diff -r 83a3792548db71c8bf69e278bf745bc5df123db7 -r 50a273de6cd76b770eb78861fee2b0a97621fa3b static/scripts/packed/viz/viz_views.js
--- /dev/null
+++ b/static/scripts/packed/viz/viz_views.js
@@ -0,0 +1,1 @@
+define(["libs/underscore"],function(a){var b=Backbone.View.extend({className:"track-header",initialize:function(){this.model.config.get("name").on("change:value",this.update_name,this);this.render()},render:function(){this.$el.append($("<div/>").addClass(this.model.drag_handle_class));this.$el.append($("<div/>").addClass("track-name").text(this.model.config.get_value("name")));this.action_icons={};this.render_action_icons();this.$el.dblclick(function(c){c.stopPropagation()});this.$el.append($("<div style='clear: both'/>"))},update_name:function(){this.$el.find(".track-name").text(this.model.config.get_value("name"))},render_action_icons:function(){var c=this;this.icons_div=$("<div/>").addClass("track-icons").hide().appendTo(this.$el);a.each(this.model.action_icons_def,function(d){c.add_action_icon(d.name,d.title,d.css_class,d.on_click_fn,d.prepend,d.hide)});this.set_display_modes(this.model.display_modes)},add_action_icon:function(e,i,h,g,d,f){var c=this;this.action_icons[e]=$("<a/>").attr("title",i).addClass("icon-button").addClass(h).tooltip().click(function(){g(c.model)}).appendTo(this.icons_div);if(f){this.action_icons[e].hide()}},set_display_modes:function(g,k){if(!g){return}this.model.display_modes=g;this.model.mode=(k||this.model.config.get_value("mode")||this.model.display_modes[0]);this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var e=this,d=this.model,h={};for(var f=0,c=d.display_modes.length;f<c;f++){var j=d.display_modes[f];h[j]=function(i){return function(){d.change_mode(i)}}(j)}make_popupmenu(this.action_icons.mode_icon,h)}});return{TrackHeaderView: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: natefoo: In the dynamic job function mapper, get the user directly from the job instead of from the history.
by commits-noreply@bitbucket.org 09 Jul '14
by commits-noreply@bitbucket.org 09 Jul '14
09 Jul '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/83a3792548db/
Changeset: 83a3792548db
User: natefoo
Date: 2014-07-09 19:10:43
Summary: In the dynamic job function mapper, get the user directly from the job instead of from the history.
Affected #: 1 file
diff -r cbe3fe407093b0161469e1de662623d1d13bf532 -r 83a3792548db71c8bf69e278bf745bc5df123db7 lib/galaxy/jobs/mapper.py
--- a/lib/galaxy/jobs/mapper.py
+++ b/lib/galaxy/jobs/mapper.py
@@ -83,7 +83,7 @@
if "job" in function_arg_names or "user" in function_arg_names or "user_email" in function_arg_names or "resource_params" in function_arg_names:
job = self.job_wrapper.get_job()
history = job.history
- user = history and history.user
+ user = job.user
user_email = user and str(user.email)
if "job" in function_arg_names:
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/5a5f22a64812/
Changeset: 5a5f22a64812
User: jgoecks
Date: 2014-07-09 17:46:31
Summary: Trackster: truncate long track names.
Affected #: 2 files
diff -r d1d5fcd5bec6d4d48908f6da58a31ca9242e694e -r 5a5f22a6481261d44ac2b4c308dbbfa87c93a457 static/style/blue/trackster.css
--- a/static/style/blue/trackster.css
+++ b/static/style/blue/trackster.css
@@ -22,6 +22,7 @@
.group-handle{cursor:move;float:left;background:#eee url('../images/tracks/block.png');width:12px;height:12px}
.group{min-height:20px;border-top:1px solid #888;border-bottom:1px solid #888}.group>.track-header{position:relative;float:left}
.track-header{height:16px;position:absolute;z-index:2;background-color:rgba(1,1,1,0.1);border-radius:5px;padding:0px 2px;text-align:left;margin:2px;}.track-header:hover{background-color:#DDDDDD}
+.track-header .track-name{max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.track-header .menubutton{margin-left:0px}
.track-name{float:left;margin-top:2px}
.tiles{background:url('../images/tracks/diag_bg.gif');position:relative;overflow:hidden}
diff -r d1d5fcd5bec6d4d48908f6da58a31ca9242e694e -r 5a5f22a6481261d44ac2b4c308dbbfa87c93a457 static/style/src/less/trackster.less
--- a/static/style/src/less/trackster.less
+++ b/static/style/src/less/trackster.less
@@ -200,6 +200,14 @@
background-color: #DDDDDD;
}
+ // Truncate long track names.
+ .track-name {
+ max-width: 200px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
.menubutton {
margin-left: 0px;
}
https://bitbucket.org/galaxy/galaxy-central/commits/cbe3fe407093/
Changeset: cbe3fe407093
User: jgoecks
Date: 2014-07-09 17:46:51
Summary: Automated merge
Affected #: 4 files
diff -r 5a5f22a6481261d44ac2b4c308dbbfa87c93a457 -r cbe3fe407093b0161469e1de662623d1d13bf532 lib/tool_shed/galaxy_install/install_manager.py
--- a/lib/tool_shed/galaxy_install/install_manager.py
+++ b/lib/tool_shed/galaxy_install/install_manager.py
@@ -44,7 +44,8 @@
def __init__( self, app ):
self.app = app
- self.INSTALL_ACTIONS = [ 'download_binary', 'download_by_url', 'download_file', 'setup_perl_environment',
+ self.INSTALL_ACTIONS = [ 'download_binary', 'download_by_url', 'download_file',
+ 'setup_perl_environment', 'setup_python_environment',
'setup_r_environment', 'setup_ruby_environment', 'shell_command' ]
def format_traceback( self ):
diff -r 5a5f22a6481261d44ac2b4c308dbbfa87c93a457 -r cbe3fe407093b0161469e1de662623d1d13bf532 lib/tool_shed/galaxy_install/tool_dependencies/env_manager.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/env_manager.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/env_manager.py
@@ -124,7 +124,7 @@
Parse an XML tag set to discover all child repository dependency tags and define the path to an env.sh file associated
with the repository (this requires the repository dependency to be in an installed state). The received action_dict
will be updated with these discovered paths and returned to the caller. This method handles tool dependency definition
- tag sets <setup_r_environment>, <setup_ruby_environment> and <setup_perl_environment>.
+ tag sets <setup_r_environment>, <setup_ruby_environment>, <setup_python_environment> and <setup_perl_environment>.
"""
# An example elem is:
# <action type="setup_perl_environment">
diff -r 5a5f22a6481261d44ac2b4c308dbbfa87c93a457 -r cbe3fe407093b0161469e1de662623d1d13bf532 lib/tool_shed/galaxy_install/tool_dependencies/recipe/recipe_manager.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/recipe_manager.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/recipe_manager.py
@@ -54,6 +54,7 @@
setup_perl_environment=step_handler.SetupPerlEnvironment( self.app ),
setup_r_environment=step_handler.SetupREnvironment( self.app ),
setup_ruby_environment=step_handler.SetupRubyEnvironment( self.app ),
+ setup_python_environment=step_handler.SetupPythonEnvironment( self.app ),
setup_virtualenv=step_handler.SetupVirtualEnv( self.app ),
shell_command=step_handler.ShellCommand( self.app ),
template_command=step_handler.TemplateCommand( self.app ) )
diff -r 5a5f22a6481261d44ac2b4c308dbbfa87c93a457 -r cbe3fe407093b0161469e1de662623d1d13bf532 lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
@@ -8,6 +8,7 @@
import time
import urllib2
import zipfile
+import hashlib
from galaxy.util import asbool
from galaxy.util.template import fill_template
@@ -139,10 +140,29 @@
class Download( object ):
def url_download( self, install_dir, downloaded_file_name, download_url, extract=True ):
+ """
+ The given download_url can have an extension like #md5# or #sha256#.
+ This indicates a checksum which will be chekced after download.
+ If the checksum does not match an exception is thrown.
+
+ https://pypi.python.org/packages/source/k/khmer/khmer-1.0.tar.gz#md5#b60639…
+ """
+
file_path = os.path.join( install_dir, downloaded_file_name )
src = None
dst = None
+ checksum = None
+ sha256 = False
+ md5 = False
# Set a timer so we don't sit here forever.
+
+ if '#md5#' in download_url:
+ md5 = True
+ download_url, checksum = download_url.split('#md5#')
+ elif '#sha256#' in download_url:
+ sha256 = True
+ download_url, checksum = download_url.split('#sha256#')
+
start_time = time.time()
try:
src = urllib2.urlopen( download_url )
@@ -166,6 +186,18 @@
src.close()
if dst:
dst.close()
+
+ try:
+ if sha256:
+ downloaded_checksum = hashlib.sha256(open(file_path, 'rb').read()).hexdigest()
+ elif md5:
+ downloaded_checksum = hashlib.md5(open(file_path, 'rb').read()).hexdigest()
+
+ if checksum and downloaded_checksum != checksum:
+ raise Exception( 'Given checksum does not match with the one from the downloaded file (%s).' % (downloaded_checksum) )
+ except Exception, e:
+ raise
+
if extract:
if tarfile.is_tarfile( file_path ) or ( zipfile.is_zipfile( file_path ) and not file_path.endswith( '.jar' ) ):
archive = CompressedFile( file_path )
@@ -174,6 +206,7 @@
extraction_path = os.path.abspath( install_dir )
else:
extraction_path = os.path.abspath( install_dir )
+
return extraction_path
@@ -1390,6 +1423,139 @@
return action_dict
+class SetupPythonEnvironment( Download, RecipeStep ):
+
+ def __init__( self, app ):
+ self.app = app
+ self.type = 'setup_python_environment'
+
+ def execute_step( self, tool_dependency, package_name, actions, action_dict, filtered_actions, env_file_builder,
+ install_environment, work_dir, current_dir=None, initial_download=False ):
+ """
+ Initialize the environment for installing Python packages. The class is called during the initial
+ download stage when installing packages, so the value of initial_download will generally be True.
+ However, the parameter value allows this class to also be used in the second stage of the installation,
+ although it may never be necessary. If initial_download is True, the recipe steps will be filtered
+ and returned and the installation directory (i.e., dir) will be defined and returned. If we're not
+ in the initial download stage, these actions will not occur, and None values will be returned for them.
+
+ Warning: easy_install is configured that it will not be install any dependency, the tool developer needs
+ to specify every dependency explicitly
+ """
+ # <action type="setup_python_environment">
+ # <repository name="package_python_2_7" owner="bgruening">
+ # <package name="python" version="2.7" />
+ # </repository>
+ # <!-- allow downloading and installing a Python package from https://pypi.python.org/ -->
+ # <package>pysam.tar.gz</package>
+ # <package>http://url-to-some-python-package.de/pysam.tar.gz</package>
+ # </action>
+ dir = None
+ if initial_download:
+ filtered_actions = actions[ 1: ]
+ env_shell_file_paths = action_dict.get( 'env_shell_file_paths', None )
+ if env_shell_file_paths is None:
+ log.debug( 'Missing Python environment, make sure your specified Python installation exists.' )
+ if initial_download:
+ return tool_dependency, filtered_actions, dir
+ return tool_dependency, None, None
+ else:
+ install_environment.add_env_shell_file_paths( env_shell_file_paths )
+ log.debug( 'Handling setup_python_environment for tool dependency %s with install_environment.env_shell_file_paths:\n%s' % \
+ ( str( tool_dependency.name ), str( install_environment.env_shell_file_paths ) ) )
+ dir = os.path.curdir
+ current_dir = os.path.abspath( os.path.join( work_dir, dir ) )
+ with lcd( current_dir ):
+ with settings( warn_only=True ):
+ python_package_tups = action_dict.get( 'python_package_tups', [] )
+ for python_package_tup in python_package_tups:
+ package, package_version = python_package_tup
+ package_path = os.path.join( install_environment.tool_shed_repository_install_dir, package )
+ if os.path.isfile( package_path ):
+ # we assume a local shipped python package
+
+ cmd = r'''PATH=$PATH:$PYTHONHOME/bin; export PATH;
+ export PYTHONPATH=$PYTHONPATH:$INSTALL_DIR;
+ easy_install --no-deps --install-dir $INSTALL_DIR --script-dir $INSTALL_DIR/bin %s
+ ''' % ( package_path )
+ elif package.find( '://' ) != -1:
+ # We assume a URL to a python package.
+ url = package
+ package_name = url.split( '/' )[ -1 ]
+ self.url_download( work_dir, package_name, url, extract=False )
+
+ cmd = r'''PATH=$PATH:$PYTHONHOME/bin; export PATH;
+ export PYTHONPATH=$PYTHONPATH:$INSTALL_DIR;
+ easy_install --no-deps --install-dir $INSTALL_DIR --script-dir $INSTALL_DIR/bin %s
+ ''' % ( package_name )
+ else:
+ pass
+ # pypi can be implemented or for > python3.4 we can use the build-in system
+ cmd = install_environment.build_command( basic_util.evaluate_template( cmd, install_environment ) )
+ return_code = install_environment.handle_command( tool_dependency=tool_dependency,
+ cmd=cmd,
+ return_output=False )
+ if return_code:
+ if initial_download:
+ return tool_dependency, filtered_actions, dir
+ return tool_dependency, None, None
+ # Pull in python dependencies (runtime).
+ env_file_builder.handle_action_shell_file_paths( action_dict )
+ env_file_builder.append_line( name="PYTHONPATH",
+ action="prepend_to",
+ value= os.path.join( install_environment.install_dir, 'lib', 'python') )
+ env_file_builder.append_line( name="PATH",
+ action="prepend_to",
+ value=os.path.join( install_environment.install_dir, 'bin' ) )
+ return_code = env_file_builder.return_code
+ if return_code:
+ if initial_download:
+ return tool_dependency, filtered_actions, dir
+ return tool_dependency, None, None
+ if initial_download:
+ return tool_dependency, filtered_actions, dir
+ return tool_dependency, None, None
+
+ def prepare_step( self, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
+ # setup a Python environment.
+ # <action type="setup_python_environment">
+ # <repository name="package_python_2_7" owner="bgruening">
+ # <package name="python" version="2.7" />
+ # </repository>
+ # <!-- allow downloading and installing an Python package from https://pypi.org/ -->
+ # <package>pysam.tar.gz</package>
+ # <package>http://url-to-some-python-package.de/pysam.tar.gz</package>
+ # </action>
+ # Discover all child repository dependency tags and define the path to an env.sh file
+ # associated with each repository. This will potentially update the value of the
+ # 'env_shell_file_paths' entry in action_dict.
+ all_env_shell_file_paths = []
+ env_manager = EnvManager( self.app )
+ action_dict = env_manager.get_env_shell_file_paths_from_setup_environment_elem( all_env_shell_file_paths,
+ action_elem,
+ action_dict )
+ python_package_tups = []
+ for env_elem in action_elem:
+ if env_elem.tag == 'package':
+ #A valid package definitions can be:
+ # pysam.tar.gz -> locally shipped tarball
+ # ftp://ftp.gruening.de/pysam.tar.gz -> online tarball
+ python_token = env_elem.text.strip().split( '=' )
+ if len( python_token ) == 2:
+ # version string
+ package_name = python_token[ 0 ]
+ package_version = python_token[ 1 ]
+ python_package_tups.append( ( package_name, package_version ) )
+ else:
+ # package name for pypi.org without version number
+ package = env_elem.text.strip()
+ python_package_tups.append( ( package, None ) )
+ if python_package_tups:
+ action_dict[ 'python_package_tups' ] = python_package_tups
+ return action_dict
+
+
+
class SetupVirtualEnv( Download, RecipeStep ):
def __init__( self, app ):
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: dannon: Close branch tool_shed_checksum
by commits-noreply@bitbucket.org 09 Jul '14
by commits-noreply@bitbucket.org 09 Jul '14
09 Jul '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ed443abea720/
Changeset: ed443abea720
Branch: tool_shed_checksum
User: dannon
Date: 2014-07-09 17:39:45
Summary: Close branch tool_shed_checksum
Affected #: 0 files
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/d1572a4a0f52/
Changeset: d1572a4a0f52
Branch: tool_shed_checksum
User: Bjoern Gruening
Date: 2014-06-29 21:35:02
Summary: With that PR the Tool Shed will be able to get checksums from the Tool Developer.
These checksums will be verified after download and in case of an mismatch an error is thrown.
Currently, md5 and sha256 checksums are supported.
Example:
<package>https://pypi.python.org/packages/source/k/khmer/khmer-1.0.tar.gz#md5=b60639…</package>
Affected #: 1 file
diff -r 7b424e9c2e57ec12eca26f35c8f4259bbcc55cdb -r d1572a4a0f529fb20f2c7036eb7dfed19563ac89 lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
@@ -8,6 +8,7 @@
import time
import urllib2
import zipfile
+import hashlib
from galaxy.util import asbool
from galaxy.util.template import fill_template
@@ -139,10 +140,29 @@
class Download( object ):
def url_download( self, install_dir, downloaded_file_name, download_url, extract=True ):
+ """
+ The given download_url can have an extension like #md5# or #sha256#.
+ This indicates a checksum which will be chekced after download.
+ If the checksum does not match an exception is thrown.
+
+ https://pypi.python.org/packages/source/k/khmer/khmer-1.0.tar.gz#md5#b60639…
+ """
+
file_path = os.path.join( install_dir, downloaded_file_name )
src = None
dst = None
+ checksum = None
+ sha256 = False
+ md5 = False
# Set a timer so we don't sit here forever.
+
+ if '#md5#' in download_url:
+ md5 = True
+ dowonload_url, checksum = download_url.split('#md5#')
+ elif '#sha256#' in download_url:
+ sha256 = True
+ download_url, checksum = download_url.split('#sha256#')
+
start_time = time.time()
try:
src = urllib2.urlopen( download_url )
@@ -166,6 +186,18 @@
src.close()
if dst:
dst.close()
+
+ try:
+ if sha256:
+ downloaded_checksum = hashlib.sha256(open(file_path, 'rb').read()).hexdigest()
+ elif md5:
+ downloaded_checksum = hashlib.md5(open(file_path, 'rb').read()).hexdigest()
+
+ if checksum and downloaded_checksum != checksum:
+ raise Exception( 'Given checksum does not match with the one from the downloaded file (%s).' % (downloaded_checksum) )
+ except Exception, e:
+ raise
+
if extract:
if tarfile.is_tarfile( file_path ) or ( zipfile.is_zipfile( file_path ) and not file_path.endswith( '.jar' ) ):
archive = CompressedFile( file_path )
@@ -174,6 +206,7 @@
extraction_path = os.path.abspath( install_dir )
else:
extraction_path = os.path.abspath( install_dir )
+
return extraction_path
@@ -1421,7 +1454,6 @@
if initial_download:
filtered_actions = actions[ 1: ]
env_shell_file_paths = action_dict.get( 'env_shell_file_paths', None )
- log.debug( '\n%s\n' % env_shell_file_paths )
if env_shell_file_paths is None:
log.debug( 'Missing Python environment, make sure your specified Python installation exists.' )
if initial_download:
https://bitbucket.org/galaxy/galaxy-central/commits/848a727a1141/
Changeset: 848a727a1141
Branch: tool_shed_checksum
User: BjoernGruening
Date: 2014-06-29 22:34:56
Summary: spelling fix
Affected #: 1 file
diff -r d1572a4a0f529fb20f2c7036eb7dfed19563ac89 -r 848a727a1141aa65a0cce41f9c046ff3296bb504 lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
@@ -158,7 +158,7 @@
if '#md5#' in download_url:
md5 = True
- dowonload_url, checksum = download_url.split('#md5#')
+ download_url, checksum = download_url.split('#md5#')
elif '#sha256#' in download_url:
sha256 = True
download_url, checksum = download_url.split('#sha256#')
https://bitbucket.org/galaxy/galaxy-central/commits/b86bab7dad7d/
Changeset: b86bab7dad7d
User: dannon
Date: 2014-07-09 17:39:45
Summary: Merged in BjoernGruening/galaxy-central-bgruening/tool_shed_checksum (pull request #432)
Implement Tool Shed checksums
Affected #: 1 file
diff -r 4411a021a9fad918cef26e00adf6f49e247fb0bd -r b86bab7dad7d744c57f6eebe9f5b390ad083a8f6 lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
@@ -8,6 +8,7 @@
import time
import urllib2
import zipfile
+import hashlib
from galaxy.util import asbool
from galaxy.util.template import fill_template
@@ -139,10 +140,29 @@
class Download( object ):
def url_download( self, install_dir, downloaded_file_name, download_url, extract=True ):
+ """
+ The given download_url can have an extension like #md5# or #sha256#.
+ This indicates a checksum which will be chekced after download.
+ If the checksum does not match an exception is thrown.
+
+ https://pypi.python.org/packages/source/k/khmer/khmer-1.0.tar.gz#md5#b60639…
+ """
+
file_path = os.path.join( install_dir, downloaded_file_name )
src = None
dst = None
+ checksum = None
+ sha256 = False
+ md5 = False
# Set a timer so we don't sit here forever.
+
+ if '#md5#' in download_url:
+ md5 = True
+ download_url, checksum = download_url.split('#md5#')
+ elif '#sha256#' in download_url:
+ sha256 = True
+ download_url, checksum = download_url.split('#sha256#')
+
start_time = time.time()
try:
src = urllib2.urlopen( download_url )
@@ -166,6 +186,18 @@
src.close()
if dst:
dst.close()
+
+ try:
+ if sha256:
+ downloaded_checksum = hashlib.sha256(open(file_path, 'rb').read()).hexdigest()
+ elif md5:
+ downloaded_checksum = hashlib.md5(open(file_path, 'rb').read()).hexdigest()
+
+ if checksum and downloaded_checksum != checksum:
+ raise Exception( 'Given checksum does not match with the one from the downloaded file (%s).' % (downloaded_checksum) )
+ except Exception, e:
+ raise
+
if extract:
if tarfile.is_tarfile( file_path ) or ( zipfile.is_zipfile( file_path ) and not file_path.endswith( '.jar' ) ):
archive = CompressedFile( file_path )
@@ -174,6 +206,7 @@
extraction_path = os.path.abspath( install_dir )
else:
extraction_path = os.path.abspath( install_dir )
+
return extraction_path
@@ -1421,7 +1454,6 @@
if initial_download:
filtered_actions = actions[ 1: ]
env_shell_file_paths = action_dict.get( 'env_shell_file_paths', None )
- log.debug( '\n%s\n' % env_shell_file_paths )
if env_shell_file_paths is None:
log.debug( 'Missing Python environment, make sure your specified Python installation exists.' )
if initial_download:
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: dannon: Merged in BjoernGruening/galaxy-central-bgruening/tool_shed_checksum (pull request #432)
by commits-noreply@bitbucket.org 09 Jul '14
by commits-noreply@bitbucket.org 09 Jul '14
09 Jul '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/b86bab7dad7d/
Changeset: b86bab7dad7d
User: dannon
Date: 2014-07-09 17:39:45
Summary: Merged in BjoernGruening/galaxy-central-bgruening/tool_shed_checksum (pull request #432)
Implement Tool Shed checksums
Affected #: 1 file
diff -r 4411a021a9fad918cef26e00adf6f49e247fb0bd -r b86bab7dad7d744c57f6eebe9f5b390ad083a8f6 lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
@@ -8,6 +8,7 @@
import time
import urllib2
import zipfile
+import hashlib
from galaxy.util import asbool
from galaxy.util.template import fill_template
@@ -139,10 +140,29 @@
class Download( object ):
def url_download( self, install_dir, downloaded_file_name, download_url, extract=True ):
+ """
+ The given download_url can have an extension like #md5# or #sha256#.
+ This indicates a checksum which will be chekced after download.
+ If the checksum does not match an exception is thrown.
+
+ https://pypi.python.org/packages/source/k/khmer/khmer-1.0.tar.gz#md5#b60639…
+ """
+
file_path = os.path.join( install_dir, downloaded_file_name )
src = None
dst = None
+ checksum = None
+ sha256 = False
+ md5 = False
# Set a timer so we don't sit here forever.
+
+ if '#md5#' in download_url:
+ md5 = True
+ download_url, checksum = download_url.split('#md5#')
+ elif '#sha256#' in download_url:
+ sha256 = True
+ download_url, checksum = download_url.split('#sha256#')
+
start_time = time.time()
try:
src = urllib2.urlopen( download_url )
@@ -166,6 +186,18 @@
src.close()
if dst:
dst.close()
+
+ try:
+ if sha256:
+ downloaded_checksum = hashlib.sha256(open(file_path, 'rb').read()).hexdigest()
+ elif md5:
+ downloaded_checksum = hashlib.md5(open(file_path, 'rb').read()).hexdigest()
+
+ if checksum and downloaded_checksum != checksum:
+ raise Exception( 'Given checksum does not match with the one from the downloaded file (%s).' % (downloaded_checksum) )
+ except Exception, e:
+ raise
+
if extract:
if tarfile.is_tarfile( file_path ) or ( zipfile.is_zipfile( file_path ) and not file_path.endswith( '.jar' ) ):
archive = CompressedFile( file_path )
@@ -174,6 +206,7 @@
extraction_path = os.path.abspath( install_dir )
else:
extraction_path = os.path.abspath( install_dir )
+
return extraction_path
@@ -1421,7 +1454,6 @@
if initial_download:
filtered_actions = actions[ 1: ]
env_shell_file_paths = action_dict.get( 'env_shell_file_paths', None )
- log.debug( '\n%s\n' % env_shell_file_paths )
if env_shell_file_paths is None:
log.debug( 'Missing Python environment, make sure your specified Python installation exists.' )
if initial_download:
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