galaxy-commits
Threads by month
- ----- 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
5 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/1b7f0f272c1a/
changeset: 1b7f0f272c1a
user: dannon
date: 2012-11-07 16:51:50
summary: Security: Sanitize grid filters.
affected #: 1 file
diff -r 6971bbc8bd8418baea51345b891e1aa647d14a88 -r 1b7f0f272c1a3f6b8d55969fbf73122884d23070 lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py
+++ b/lib/galaxy/web/framework/helpers/grids.py
@@ -2,6 +2,7 @@
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import iff
from galaxy.web import url_for
+from galaxy.util import sanitize_text
from galaxy.util.json import from_json_string, to_json_string
from galaxy.util.odict import odict
from galaxy.web.framework.helpers import to_unicode
@@ -136,7 +137,8 @@
# Update query.
query = column.filter( trans, trans.user, query, column_filter )
# Upate current filter dict.
- cur_filter_dict[ column.key ] = column_filter
+ #Column filters are rendered in various places, sanitize them all here.
+ cur_filter_dict[ column.key ] = sanitize_text(column_filter)
# Carry filter along to newly generated urls; make sure filter is a string so
# that we can encode to UTF-8 and thus handle user input to filters.
if isinstance( column_filter, list ):
https://bitbucket.org/galaxy/galaxy-central/changeset/643931af6baf/
changeset: 643931af6baf
user: dannon
date: 2012-11-07 16:54:57
summary: Whitespace cleanup.
affected #: 1 file
diff -r 1b7f0f272c1a3f6b8d55969fbf73122884d23070 -r 643931af6baf5cfdd772dab1342f47149a23620c lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py
+++ b/lib/galaxy/web/framework/helpers/grids.py
@@ -36,7 +36,7 @@
num_page_links = 10
# Set preference names.
cur_filter_pref_name = ".filter"
- cur_sort_key_pref_name = ".sort_key"
+ cur_sort_key_pref_name = ".sort_key"
pass_through_operations = {}
def __init__( self ):
# Determine if any multiple row operations are defined
@@ -45,13 +45,13 @@
if operation.allow_multiple:
self.has_multiple_item_operations = True
break
-
- # If a column does not have a model class, set the column's model class
+
+ # If a column does not have a model class, set the column's model class
# to be the grid's model class.
for column in self.columns:
if not column.model_class:
column.model_class = self.model_class
-
+
def __call__( self, trans, **kwargs ):
# Get basics.
# FIXME: pretty sure this is only here to pass along, can likely be eliminated
@@ -88,7 +88,7 @@
cur_filter_dict = {}
for column in self.columns:
if column.key:
- # Get the filter criterion for the column. Precedence is (a) if using default filter, only look there; otherwise,
+ # Get the filter criterion for the column. Precedence is (a) if using default filter, only look there; otherwise,
# (b) look in kwargs; and (c) look in base filter.
column_filter = None
if use_default_filter:
@@ -98,7 +98,7 @@
# Queries that include table joins cannot guarantee unique column names. This problem is
# handled by setting the column_filter value to <TableName>.<ColumnName>.
column_filter = kwargs.get( "f-" + column.model_class.__name__ + ".%s" % column.key )
- elif "f-" + column.key in kwargs:
+ elif "f-" + column.key in kwargs:
column_filter = kwargs.get( "f-" + column.key )
elif column.key in base_filter:
column_filter = base_filter.get( column.key )
@@ -108,7 +108,7 @@
if isinstance( item, basestring):
try:
# Not clear what we're decoding, so recurse to ensure that we catch everything.
- decoded_item = from_json_string( item )
+ decoded_item = from_json_string( item )
if isinstance( decoded_item, list):
decoded_list = from_json_string_recurse( decoded_item )
else:
@@ -146,7 +146,7 @@
for filter in column_filter:
if not isinstance( filter, basestring ):
filter = unicode( filter ).encode("utf-8")
- extra_url_args[ "f-" + column.key ] = to_json_string( column_filter )
+ extra_url_args[ "f-" + column.key ] = to_json_string( column_filter )
else:
# Process singleton filter.
if not isinstance( column_filter, basestring ):
@@ -190,19 +190,19 @@
if self.use_paging:
if 'page' in kwargs:
if kwargs['page'] == 'all':
- page_num = 0
+ page_num = 0
else:
page_num = int( kwargs['page'] )
else:
page_num = 1
-
+
if page_num == 0:
# Show all rows in page.
total_num_rows = query.count()
page_num = 1
num_pages = 1
else:
- # Show a limited number of rows. Before modifying query, get the total number of rows that query
+ # Show a limited number of rows. Before modifying query, get the total number of rows that query
# returns so that the total number of pages can be computed.
total_num_rows = query.count()
query = query.limit( self.num_rows_per_page ).offset( ( page_num-1 ) * self.num_rows_per_page )
@@ -211,11 +211,11 @@
# Defaults.
page_num = 1
num_pages = 1
-
- # There are some places in grid templates where it's useful for a grid
+
+ # There are some places in grid templates where it's useful for a grid
# to have its current filter.
self.cur_filter_dict = cur_filter_dict
-
+
# Preserve grid state: save current filter and sort key.
if self.preserve_state:
pref_name = unicode( self.__class__.__name__ + self.cur_filter_pref_name )
@@ -253,10 +253,10 @@
return url_for( **new_kwargs )
self.use_panels = ( kwargs.get( 'use_panels', False ) in [ True, 'True', 'true' ] )
async_request = ( ( self.use_async ) and ( kwargs.get( 'async', False ) in [ True, 'True', 'true'] ) )
- # Currently, filling the template returns a str object; this requires decoding the string into a
- # unicode object within mako templates. What probably should be done is to return the template as
+ # Currently, filling the template returns a str object; this requires decoding the string into a
+ # unicode object within mako templates. What probably should be done is to return the template as
# utf-8 unicode; however, this would require encoding the object as utf-8 before returning the grid
- # results via a controller method, which is require substantial changes. Hence, for now, return grid
+ # results via a controller method, which is require substantial changes. Hence, for now, return grid
# as str.
return trans.fill_template( iff( async_request, self.async_template, self.template ),
grid=self,
@@ -303,7 +303,7 @@
# (gvk) Is this method necessary? Why not simply build the entire query,
# including applying filters in the build_initial_query() method?
return query
-
+
class GridColumn( object ):
def __init__( self, label, key=None, model_class=None, method=None, format=None, \
link=None, attach_popup=False, visible=True, ncells=1, nowrap=False, \
@@ -360,16 +360,16 @@
if column_name is None:
column_name = self.key
if ascending:
- query = query.order_by( self.model_class.table.c.get( column_name ).asc() )
+ query = query.order_by( self.model_class.table.c.get( column_name ).asc() )
else:
query = query.order_by( self.model_class.table.c.get( column_name ).desc() )
return query
-
+
class ReverseSortColumn( GridColumn ):
""" Column that reverses sorting; this is useful when the natural sort is descending. """
def sort( self, trans, query, ascending, column_name=None ):
return GridColumn.sort( self, trans, query, (not ascending), column_name=column_name )
-
+
class TextColumn( GridColumn ):
""" Generic column that employs freetext and, hence, supports freetext, case-independent filtering. """
def filter( self, trans, user, query, column_filter ):
@@ -380,7 +380,7 @@
query = query.filter( self.get_filter( trans, user, column_filter ) )
return query
def get_filter( self, trans, user, column_filter ):
- """ Returns a SQLAlchemy criterion derived from column_filter. """
+ """ Returns a SQLAlchemy criterion derived from column_filter. """
if isinstance( column_filter, basestring ):
return self.get_single_filter( user, column_filter )
elif isinstance( column_filter, list ):
@@ -406,7 +406,7 @@
if column_name is None:
column_name = self.key
if ascending:
- query = query.order_by( func.lower( self.model_class.table.c.get( column_name ) ).asc() )
+ query = query.order_by( func.lower( self.model_class.table.c.get( column_name ) ).asc() )
else:
query = query.order_by( func.lower( self.model_class.table.c.get( column_name ) ).desc() )
return query
@@ -430,9 +430,9 @@
class IntegerColumn( TextColumn ):
"""
- Integer column that employs freetext, but checks that the text is an integer,
+ Integer column that employs freetext, but checks that the text is an integer,
so support filtering on integer values.
-
+
IMPORTANT NOTE: grids that use this column type should not include the column
in the cols_to_filter list of MulticolFilterColumn ( i.e., searching on this
column type should not be performed in the grid's standard search - it won't
@@ -440,8 +440,8 @@
that search on this column should use 'filterable="advanced"' so that searching
is only performed in the advanced search component, restricting the search to
the specific column.
-
- This is useful for searching on object ids or other integer columns. See the
+
+ This is useful for searching on object ids or other integer columns. See the
JobIdColumn column in the SpecifiedDateListGrid class in the jobs controller of
the reports webapp for an example.
"""
@@ -452,14 +452,14 @@
def sort( self, trans, query, ascending, column_name=None ):
"""Sort query using this column."""
return GridColumn.sort( self, trans, query, ascending, column_name=column_name )
-
+
class CommunityRatingColumn( GridColumn, UsesItemRatings ):
""" Column that displays community ratings for an item. """
def get_value( self, trans, grid, item ):
ave_item_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, item, webapp_model=trans.model )
- return trans.fill_template( "community_rating.mako",
- ave_item_rating=ave_item_rating,
- num_ratings=num_ratings,
+ return trans.fill_template( "community_rating.mako",
+ ave_item_rating=ave_item_rating,
+ num_ratings=num_ratings,
item_id=trans.security.encode_id( item.id ) )
def sort( self, trans, query, ascending, column_name=None ):
def get_foreign_key( source_class, target_class ):
@@ -511,12 +511,12 @@
return ann_snippet
def get_single_filter( self, user, a_filter ):
""" Filter by annotation and annotation owner. """
- return self.model_class.annotations.any(
- and_( func.lower( self.model_annotation_association_class.annotation ).like( "%" + a_filter.lower() + "%" ),
- # TODO: not sure why, to filter by owner's annotations, we have to do this rather than
+ return self.model_class.annotations.any(
+ and_( func.lower( self.model_annotation_association_class.annotation ).like( "%" + a_filter.lower() + "%" ),
+ # TODO: not sure why, to filter by owner's annotations, we have to do this rather than
# 'self.model_class.user==self.model_annotation_association_class.user'
- self.model_annotation_association_class.table.c.user_id==self.model_class.table.c.user_id ) )
-
+ self.model_annotation_association_class.table.c.user_id==self.model_class.table.c.user_id ) )
+
class CommunityTagsColumn( TextColumn ):
""" Column that supports community tags. """
def __init__( self, col_name, key, model_class=None, model_tag_association_class=None, filterable=None, grid_name=None ):
@@ -549,7 +549,7 @@
# Filter by all values.
clause_list.append( self.model_class.tags.any( func.lower( self.model_tag_association_class.user_value ).like( "%" + value.lower() + "%" ) ) )
return and_( *clause_list )
-
+
class IndividualTagsColumn( CommunityTagsColumn ):
""" Column that supports individual tags. """
def get_value( self, trans, grid, item ):
@@ -577,7 +577,7 @@
# Filter by individual's tag values.
clause_list.append( self.model_class.tags.any( and_( func.lower( self.model_tag_association_class.user_value ).like( "%" + value.lower() + "%" ), self.model_tag_association_class.user == user ) ) )
return and_( *clause_list )
-
+
class MulticolFilterColumn( TextColumn ):
""" Column that performs multicolumn filtering. """
def __init__( self, col_name, cols_to_filter, key, visible, filterable="default" ):
@@ -601,7 +601,7 @@
clause_list.append( column.get_filter( trans, user, column_filter ) )
complete_filter = or_( *clause_list )
return query.filter( complete_filter )
-
+
class OwnerColumn( TextColumn ):
""" Column that lists item's owner. """
def get_value( self, trans, grid, item ):
@@ -609,7 +609,7 @@
def sort( self, trans, query, ascending, column_name=None ):
""" Sort column using case-insensitive alphabetical sorting on item's username. """
if ascending:
- query = query.order_by( func.lower ( self.model_class.username ).asc() )
+ query = query.order_by( func.lower ( self.model_class.username ).asc() )
else:
query = query.order_by( func.lower( self.model_class.username ).desc() )
return query
@@ -751,17 +751,17 @@
return self.condition( item )
else:
return True
-
+
class DisplayByUsernameAndSlugGridOperation( GridOperation ):
""" Operation to display an item by username and slug. """
def get_url_args( self, item ):
return { 'action' : 'display_by_username_and_slug', 'username' : item.user.username, 'slug' : item.slug }
-
+
class GridAction( object ):
def __init__( self, label=None, url_args=None ):
self.label = label
self.url_args = url_args
-
+
class GridColumnFilter( object ):
def __init__( self, label, args=None ):
self.label = label
https://bitbucket.org/galaxy/galaxy-central/changeset/36ecf36af10d/
changeset: 36ecf36af10d
user: dannon
date: 2012-11-07 16:56:26
summary: Fix incorrect new_args vs new_kwargs. Remove dead code.
affected #: 1 file
diff -r 643931af6baf5cfdd772dab1342f47149a23620c -r 36ecf36af10d88e6cc35666352a69f35c9313439 lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py
+++ b/lib/galaxy/web/framework/helpers/grids.py
@@ -116,7 +116,6 @@
except ValueError:
decoded_list = [ unicode ( item ) ]
elif isinstance( item, list):
- return_val = []
for element in item:
a_list = from_json_string_recurse( element )
decoded_list = decoded_list + a_list
@@ -247,7 +246,7 @@
if 'id' in new_kwargs:
id = new_kwargs[ 'id' ]
if isinstance( id, list ):
- new_args[ 'id' ] = [ trans.security.encode_id( i ) for i in id ]
+ new_kwargs[ 'id' ] = [ trans.security.encode_id( i ) for i in id ]
else:
new_kwargs[ 'id' ] = trans.security.encode_id( id )
return url_for( **new_kwargs )
https://bitbucket.org/galaxy/galaxy-central/changeset/4379576e8199/
changeset: 4379576e8199
user: dannon
date: 2012-11-07 16:57:54
summary: Sphinx: Add doc/build to .hgignore.
affected #: 1 file
diff -r 36ecf36af10d88e6cc35666352a69f35c9313439 -r 4379576e8199c18b11ec448f090e8300bc0f372d .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -41,6 +41,8 @@
static/welcome.html.*
static/welcome.html
+doc/build
+
# Tool data.
tool-data/annotation_profiler_options.xml
tool-data/annotation_profiler_valid_builds.txt
https://bitbucket.org/galaxy/galaxy-central/changeset/0a20563f9d46/
changeset: 0a20563f9d46
user: dannon
date: 2012-11-07 17:00:23
summary: Merge.
affected #: 4 files
diff -r 4379576e8199c18b11ec448f090e8300bc0f372d -r 0a20563f9d46749d041c079907492bf9d4866628 lib/galaxy/tool_shed/common_util.py
--- /dev/null
+++ b/lib/galaxy/tool_shed/common_util.py
@@ -0,0 +1,93 @@
+import os, urllib2
+from galaxy import util
+from galaxy.util.odict import odict
+from galaxy.tool_shed.encoding_util import *
+
+REPOSITORY_OWNER = 'devteam'
+
+def check_for_missing_tools( app, tool_panel_configs, latest_tool_migration_script_number ):
+ # Get the 000x_tools.xml file associated with the current migrate_tools version number.
+ tools_xml_file_path = os.path.abspath( os.path.join( 'scripts', 'migrate_tools', '%04d_tools.xml' % latest_tool_migration_script_number ) )
+ # Parse the XML and load the file attributes for later checking against the proprietary tool_panel_config.
+ migrated_tool_configs_dict = odict()
+ tree = util.parse_xml( tools_xml_file_path )
+ root = tree.getroot()
+ tool_shed = root.get( 'name' )
+ tool_shed_url = get_tool_shed_url_from_tools_xml_file_path( app, tool_shed )
+ # The default behavior is that the tool shed is down.
+ tool_shed_accessible = False
+ if tool_shed_url:
+ for elem in root:
+ if elem.tag == 'repository':
+ tool_dependencies = []
+ tool_dependencies_dict = {}
+ repository_name = elem.get( 'name' )
+ changeset_revision = elem.get( 'changeset_revision' )
+ url = '%s/repository/get_tool_dependencies?name=%s&owner=%s&changeset_revision=%s&from_install_manager=True' % \
+ ( tool_shed_url, repository_name, REPOSITORY_OWNER, changeset_revision )
+ try:
+ response = urllib2.urlopen( url )
+ text = response.read()
+ response.close()
+ tool_shed_accessible = True
+ except Exception, e:
+ # Tool shed may be unavailable - we have to set tool_shed_accessible since we're looping.
+ tool_shed_accessible = False
+ print "The URL\n%s\nraised the exception:\n%s\n" % ( url, str( e ) )
+ if tool_shed_accessible:
+ if text:
+ tool_dependencies_dict = tool_shed_decode( text )
+ for dependency_key, requirements_dict in tool_dependencies_dict.items():
+ tool_dependency_name = requirements_dict[ 'name' ]
+ tool_dependency_version = requirements_dict[ 'version' ]
+ tool_dependency_type = requirements_dict[ 'type' ]
+ tool_dependency_readme = requirements_dict.get( 'readme', '' )
+ tool_dependencies.append( ( tool_dependency_name, tool_dependency_version, tool_dependency_type, tool_dependency_readme ) )
+ for tool_elem in elem.findall( 'tool' ):
+ migrated_tool_configs_dict[ tool_elem.get( 'file' ) ] = tool_dependencies
+ if tool_shed_accessible:
+ # Parse the proprietary tool_panel_configs (the default is tool_conf.xml) and generate the list of missing tool config file names.
+ missing_tool_configs_dict = odict()
+ for tool_panel_config in tool_panel_configs:
+ tree = util.parse_xml( tool_panel_config )
+ root = tree.getroot()
+ for elem in root:
+ if elem.tag == 'tool':
+ missing_tool_configs_dict = check_tool_tag_set( elem, migrated_tool_configs_dict, missing_tool_configs_dict )
+ elif elem.tag == 'section':
+ for section_elem in elem:
+ if section_elem.tag == 'tool':
+ missing_tool_configs_dict = check_tool_tag_set( section_elem, migrated_tool_configs_dict, missing_tool_configs_dict )
+ else:
+ exception_msg = '\n\nThe entry for the main Galaxy tool shed at %s is missing from the %s file. ' % ( tool_shed, app.config.tool_sheds_config )
+ exception_msg += 'The entry for this tool shed must always be available in this file, so re-add it before attempting to start your Galaxy server.\n'
+ raise Exception( exception_msg )
+ return tool_shed_accessible, missing_tool_configs_dict
+def check_tool_tag_set( elem, migrated_tool_configs_dict, missing_tool_configs_dict ):
+ file_path = elem.get( 'file', None )
+ if file_path:
+ path, name = os.path.split( file_path )
+ if name in migrated_tool_configs_dict:
+ tool_dependencies = migrated_tool_configs_dict[ name ]
+ missing_tool_configs_dict[ name ] = tool_dependencies
+ return missing_tool_configs_dict
+def get_non_shed_tool_panel_configs( app ):
+ # Get the non-shed related tool panel configs - there can be more than one, and the default is tool_conf.xml.
+ config_filenames = []
+ for config_filename in app.config.tool_configs:
+ # Any config file that includes a tool_path attribute in the root tag set like the following is shed-related.
+ # <toolbox tool_path="../shed_tools">
+ tree = util.parse_xml( config_filename )
+ root = tree.getroot()
+ tool_path = root.get( 'tool_path', None )
+ if tool_path is None:
+ config_filenames.append( config_filename )
+ return config_filenames
+def get_tool_shed_url_from_tools_xml_file_path( app, tool_shed ):
+ search_str = '://%s' % tool_shed
+ for shed_name, shed_url in app.tool_shed_registry.tool_sheds.items():
+ if shed_url.find( search_str ) >= 0:
+ if shed_url.endswith( '/' ):
+ shed_url = shed_url.rstrip( '/' )
+ return shed_url
+ return None
diff -r 4379576e8199c18b11ec448f090e8300bc0f372d -r 0a20563f9d46749d041c079907492bf9d4866628 lib/galaxy/tool_shed/install_manager.py
--- a/lib/galaxy/tool_shed/install_manager.py
+++ b/lib/galaxy/tool_shed/install_manager.py
@@ -7,8 +7,7 @@
from galaxy.util.json import from_json_string, to_json_string
from galaxy.util.shed_util import *
from galaxy.util.odict import odict
-
-REPOSITORY_OWNER = 'devteam'
+from galaxy.tool_shed.common_util import *
class InstallManager( object ):
def __init__( self, app, latest_migration_script_number, tool_shed_install_config, migrated_tools_config, install_dependencies ):
@@ -19,10 +18,11 @@
self.app = app
self.toolbox = self.app.toolbox
self.migrated_tools_config = migrated_tools_config
- # If install_dependencies is True, but tool_dependency_dir is not set,
- # do not attempt to install, but print informative error message
+ # If install_dependencies is True but tool_dependency_dir is not set, do not attempt to install but print informative error message.
if install_dependencies and app.config.tool_dependency_dir is None:
- raise Exception( 'You are attempting to install tool dependencies, but do not have a value for "tool_dependency_dir" set in your ini file. Please set this to the path where you would like to install dependencies and rerun the migration script.' )
+ message = 'You are attempting to install tool dependencies but do not have a value for "tool_dependency_dir" set in your universe_wsgi.ini '
+ message += 'file. Set this location value to the path where you want tool dependencies installed and rerun the migration script.'
+ raise Exception( message )
# Get the local non-shed related tool panel configs (there can be more than one, and the default name is tool_conf.xml).
self.proprietary_tool_confs = self.non_shed_tool_panel_configs
self.proprietary_tool_panel_elems = self.get_proprietary_tool_panel_elems( latest_migration_script_number )
@@ -38,8 +38,39 @@
self.tool_shed = clean_tool_shed_url( root.get( 'name' ) )
self.repository_owner = REPOSITORY_OWNER
index, self.shed_config_dict = get_shed_tool_conf_dict( app, self.migrated_tools_config )
- for repository_elem in root:
- self.install_repository( repository_elem, install_dependencies )
+ # Since tool migration scripts can be executed any number of times, we need to make sure the appropriate tools are defined in
+ # tool_conf.xml. If no tools associated with the migration stage are defined, no repositories will be installed on disk.
+ # The default behavior is that the tool shed is down.
+ tool_shed_accessible = False
+ tool_panel_configs = get_non_shed_tool_panel_configs( app )
+ if tool_panel_configs:
+ # The missing_tool_configs_dict contents are something like:
+ # {'emboss_antigenic.xml': [('emboss', '5.0.0', 'package', '\nreadme blah blah blah\n')]}
+ tool_shed_accessible, missing_tool_configs_dict = check_for_missing_tools( app, tool_panel_configs, latest_migration_script_number )
+ else:
+ # It doesn't matter if the tool shed is accessible since there are no migrated tools defined in the local Galaxy instance, but
+ # we have to set the value of tool_shed_accessible to True so that the value of migrate_tools.version can be correctly set in
+ # the database.
+ tool_shed_accessible = True
+ missing_tool_configs_dict = odict()
+ if tool_shed_accessible:
+ if len( self.proprietary_tool_confs ) == 1:
+ plural = ''
+ file_names = self.proprietary_tool_confs[ 0 ]
+ else:
+ plural = 's'
+ file_names = ', '.join( self.proprietary_tool_confs )
+ if missing_tool_configs_dict:
+ for repository_elem in root:
+ self.install_repository( repository_elem, install_dependencies )
+ else:
+ message = "\nNo tools associated with migration stage %s are defined in your " % str( latest_migration_script_number )
+ message += "file%s named %s,\nso no repositories will be installed on disk.\n" % ( plural, file_names )
+ print message
+ else:
+ message = "\nThe main Galaxy tool shed is not currently available, so skipped migration stage %s.\n" % str( latest_migration_script_number )
+ message += "Try again later.\n"
+ print message
def get_guid( self, repository_clone_url, relative_install_dir, tool_config ):
if self.shed_config_dict.get( 'tool_path' ):
relative_install_dir = os.path.join( self.shed_config_dict['tool_path'], relative_install_dir )
@@ -144,7 +175,8 @@
for k, v in tool_panel_dict_for_tool_config.items():
tool_panel_dict_for_display[ k ] = v
else:
- print 'The tool "%s" (%s) has not been enabled because it is not defined in a proprietary tool config (%s).' % ( guid, tool_config, ", ".join( self.proprietary_tool_confs or [] ) )
+ print 'The tool "%s" (%s) has not been enabled because it is not defined in a proprietary tool config (%s).' \
+ % ( guid, tool_config, ", ".join( self.proprietary_tool_confs or [] ) )
metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=self.app,
repository=tool_shed_repository,
repository_clone_url=repository_clone_url,
@@ -315,20 +347,9 @@
update_tool_shed_repository_status( self.app, tool_shed_repository, self.app.model.ToolShedRepository.installation_status.INSTALLED )
@property
def non_shed_tool_panel_configs( self ):
- # Get the non-shed related tool panel config file names from the Galaxy config - the default is tool_conf.xml.
- config_filenames = []
- for config_filename in self.app.config.tool_configs:
- # Any config file that includes a tool_path attribute in the root tag set like the following is shed-related.
- # <toolbox tool_path="../shed_tools">
- tree = util.parse_xml( config_filename )
- root = tree.getroot()
- tool_path = root.get( 'tool_path', None )
- if tool_path is None:
- config_filenames.append( config_filename )
- return config_filenames
+ return get_non_shed_tool_panel_configs( self.app )
def __get_url_from_tool_shed( self, tool_shed ):
- # The value of tool_shed is something like: toolshed.g2.bx.psu.edu
- # We need the URL to this tool shed, which is something like:
+ # The value of tool_shed is something like: toolshed.g2.bx.psu.edu. We need the URL to this tool shed, which is something like:
# http://toolshed.g2.bx.psu.edu/
for shed_name, shed_url in self.app.tool_shed_registry.tool_sheds.items():
if shed_url.find( tool_shed ) >= 0:
diff -r 4379576e8199c18b11ec448f090e8300bc0f372d -r 0a20563f9d46749d041c079907492bf9d4866628 lib/galaxy/tool_shed/migrate/common.py
--- a/lib/galaxy/tool_shed/migrate/common.py
+++ b/lib/galaxy/tool_shed/migrate/common.py
@@ -1,103 +1,13 @@
-import sys, os, ConfigParser, urllib2
+import sys, os, ConfigParser
import galaxy.config
import galaxy.datatypes.registry
-from galaxy import util, tools
+from galaxy import tools
import galaxy.model.mapping
import galaxy.tools.search
from galaxy.objectstore import build_object_store_from_config
+from galaxy.tool_shed.common_util import *
import galaxy.tool_shed.tool_shed_registry
from galaxy.tool_shed import install_manager
-from galaxy.tool_shed.encoding_util import *
-from galaxy.util.odict import odict
-
-REPOSITORY_OWNER = 'devteam'
-
-def check_for_missing_tools( app, tool_panel_configs, latest_tool_migration_script_number ):
- # Get the 000x_tools.xml file associated with the current migrate_tools version number.
- tools_xml_file_path = os.path.abspath( os.path.join( 'scripts', 'migrate_tools', '%04d_tools.xml' % latest_tool_migration_script_number ) )
- # Parse the XML and load the file attributes for later checking against the proprietary tool_panel_config.
- migrated_tool_configs_dict = odict()
- tree = util.parse_xml( tools_xml_file_path )
- root = tree.getroot()
- tool_shed = root.get( 'name' )
- tool_shed_url = get_tool_shed_url_from_tools_xml_file_path( app, tool_shed )
- # The default behavior is that the tool shed is down.
- tool_shed_accessible = False
- if tool_shed_url:
- for elem in root:
- if elem.tag == 'repository':
- tool_dependencies = []
- tool_dependencies_dict = {}
- repository_name = elem.get( 'name' )
- changeset_revision = elem.get( 'changeset_revision' )
- url = '%s/repository/get_tool_dependencies?name=%s&owner=%s&changeset_revision=%s&from_install_manager=True' % \
- ( tool_shed_url, repository_name, REPOSITORY_OWNER, changeset_revision )
- try:
- response = urllib2.urlopen( url )
- text = response.read()
- response.close()
- tool_shed_accessible = True
- except Exception, e:
- # Tool shed may be unavailable - we have to set tool_shed_accessible since we're looping.
- tool_shed_accessible = False
- print "The URL\n%s\nraised the exception:\n%s\n" % ( url, str( e ) )
- if tool_shed_accessible:
- if text:
- tool_dependencies_dict = tool_shed_decode( text )
- for dependency_key, requirements_dict in tool_dependencies_dict.items():
- tool_dependency_name = requirements_dict[ 'name' ]
- tool_dependency_version = requirements_dict[ 'version' ]
- tool_dependency_type = requirements_dict[ 'type' ]
- tool_dependency_readme = requirements_dict.get( 'readme', '' )
- tool_dependencies.append( ( tool_dependency_name, tool_dependency_version, tool_dependency_type, tool_dependency_readme ) )
- for tool_elem in elem.findall( 'tool' ):
- migrated_tool_configs_dict[ tool_elem.get( 'file' ) ] = tool_dependencies
- if tool_shed_accessible:
- # Parse the proprietary tool_panel_configs (the default is tool_conf.xml) and generate the list of missing tool config file names.
- missing_tool_configs_dict = odict()
- for tool_panel_config in tool_panel_configs:
- tree = util.parse_xml( tool_panel_config )
- root = tree.getroot()
- for elem in root:
- if elem.tag == 'tool':
- missing_tool_configs_dict = check_tool_tag_set( elem, migrated_tool_configs_dict, missing_tool_configs_dict )
- elif elem.tag == 'section':
- for section_elem in elem:
- if section_elem.tag == 'tool':
- missing_tool_configs_dict = check_tool_tag_set( section_elem, migrated_tool_configs_dict, missing_tool_configs_dict )
- else:
- exception_msg = '\n\nThe entry for the main Galaxy tool shed at %s is missing from the %s file. ' % ( tool_shed, app.config.tool_sheds_config )
- exception_msg += 'The entry for this tool shed must always be available in this file, so re-add it before attempting to start your Galaxy server.\n'
- raise Exception( exception_msg )
- return tool_shed_accessible, missing_tool_configs_dict
-def check_tool_tag_set( elem, migrated_tool_configs_dict, missing_tool_configs_dict ):
- file_path = elem.get( 'file', None )
- if file_path:
- path, name = os.path.split( file_path )
- if name in migrated_tool_configs_dict:
- tool_dependencies = migrated_tool_configs_dict[ name ]
- missing_tool_configs_dict[ name ] = tool_dependencies
- return missing_tool_configs_dict
-def get_non_shed_tool_panel_configs( app ):
- # Get the non-shed related tool panel configs - there can be more than one, and the default is tool_conf.xml.
- config_filenames = []
- for config_filename in app.config.tool_configs:
- # Any config file that includes a tool_path attribute in the root tag set like the following is shed-related.
- # <toolbox tool_path="../shed_tools">
- tree = util.parse_xml( config_filename )
- root = tree.getroot()
- tool_path = root.get( 'tool_path', None )
- if tool_path is None:
- config_filenames.append( config_filename )
- return config_filenames
-def get_tool_shed_url_from_tools_xml_file_path( app, tool_shed ):
- search_str = '://%s' % tool_shed
- for shed_name, shed_url in app.tool_shed_registry.tool_sheds.items():
- if shed_url.find( search_str ) >= 0:
- if shed_url.endswith( '/' ):
- shed_url = shed_url.rstrip( '/' )
- return shed_url
- return None
class MigrateToolsApplication( object ):
"""Encapsulates the state of a basic Galaxy Universe application in order to initiate the Install Manager"""
diff -r 4379576e8199c18b11ec448f090e8300bc0f372d -r 0a20563f9d46749d041c079907492bf9d4866628 scripts/migrate_tools/migrate_tools.py
--- a/scripts/migrate_tools/migrate_tools.py
+++ b/scripts/migrate_tools/migrate_tools.py
@@ -26,8 +26,8 @@
else:
plural = 's'
file_names = ', '.join( non_shed_tool_confs )
-msg = "\nThe installation process is finished. You should now remove entries for the installed tools from your file%s named\n" % plural
-msg += "%s and start your Galaxy server.\n" % file_names
+msg = "\nThe installation process is finished. If any tools associated with this migration were defined in your file%s named\n" % plural
+msg += "%s, then you should remove entries for them and start your Galaxy server.\n" % file_names
print msg
app.shutdown()
sys.exit( 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
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/d10de4954af2/
changeset: d10de4954af2
user: greg
date: 2012-11-07 16:51:35
summary: Since tool migration scripts can be executed any number of times, make sure that no repositories are installed if not tools associated with the migration are defined in the tool_conf.xml file (or equivalent). This fix is associated only with the recently introduced Galaxy admin UI feature displaying the list of migration stages currently available. This is the way the migration process at server startup has always worked so no changes are needed in that scenario.
affected #: 4 files
diff -r 6971bbc8bd8418baea51345b891e1aa647d14a88 -r d10de4954af2e94cb7c517ae6398d0247843168e lib/galaxy/tool_shed/common_util.py
--- /dev/null
+++ b/lib/galaxy/tool_shed/common_util.py
@@ -0,0 +1,93 @@
+import os, urllib2
+from galaxy import util
+from galaxy.util.odict import odict
+from galaxy.tool_shed.encoding_util import *
+
+REPOSITORY_OWNER = 'devteam'
+
+def check_for_missing_tools( app, tool_panel_configs, latest_tool_migration_script_number ):
+ # Get the 000x_tools.xml file associated with the current migrate_tools version number.
+ tools_xml_file_path = os.path.abspath( os.path.join( 'scripts', 'migrate_tools', '%04d_tools.xml' % latest_tool_migration_script_number ) )
+ # Parse the XML and load the file attributes for later checking against the proprietary tool_panel_config.
+ migrated_tool_configs_dict = odict()
+ tree = util.parse_xml( tools_xml_file_path )
+ root = tree.getroot()
+ tool_shed = root.get( 'name' )
+ tool_shed_url = get_tool_shed_url_from_tools_xml_file_path( app, tool_shed )
+ # The default behavior is that the tool shed is down.
+ tool_shed_accessible = False
+ if tool_shed_url:
+ for elem in root:
+ if elem.tag == 'repository':
+ tool_dependencies = []
+ tool_dependencies_dict = {}
+ repository_name = elem.get( 'name' )
+ changeset_revision = elem.get( 'changeset_revision' )
+ url = '%s/repository/get_tool_dependencies?name=%s&owner=%s&changeset_revision=%s&from_install_manager=True' % \
+ ( tool_shed_url, repository_name, REPOSITORY_OWNER, changeset_revision )
+ try:
+ response = urllib2.urlopen( url )
+ text = response.read()
+ response.close()
+ tool_shed_accessible = True
+ except Exception, e:
+ # Tool shed may be unavailable - we have to set tool_shed_accessible since we're looping.
+ tool_shed_accessible = False
+ print "The URL\n%s\nraised the exception:\n%s\n" % ( url, str( e ) )
+ if tool_shed_accessible:
+ if text:
+ tool_dependencies_dict = tool_shed_decode( text )
+ for dependency_key, requirements_dict in tool_dependencies_dict.items():
+ tool_dependency_name = requirements_dict[ 'name' ]
+ tool_dependency_version = requirements_dict[ 'version' ]
+ tool_dependency_type = requirements_dict[ 'type' ]
+ tool_dependency_readme = requirements_dict.get( 'readme', '' )
+ tool_dependencies.append( ( tool_dependency_name, tool_dependency_version, tool_dependency_type, tool_dependency_readme ) )
+ for tool_elem in elem.findall( 'tool' ):
+ migrated_tool_configs_dict[ tool_elem.get( 'file' ) ] = tool_dependencies
+ if tool_shed_accessible:
+ # Parse the proprietary tool_panel_configs (the default is tool_conf.xml) and generate the list of missing tool config file names.
+ missing_tool_configs_dict = odict()
+ for tool_panel_config in tool_panel_configs:
+ tree = util.parse_xml( tool_panel_config )
+ root = tree.getroot()
+ for elem in root:
+ if elem.tag == 'tool':
+ missing_tool_configs_dict = check_tool_tag_set( elem, migrated_tool_configs_dict, missing_tool_configs_dict )
+ elif elem.tag == 'section':
+ for section_elem in elem:
+ if section_elem.tag == 'tool':
+ missing_tool_configs_dict = check_tool_tag_set( section_elem, migrated_tool_configs_dict, missing_tool_configs_dict )
+ else:
+ exception_msg = '\n\nThe entry for the main Galaxy tool shed at %s is missing from the %s file. ' % ( tool_shed, app.config.tool_sheds_config )
+ exception_msg += 'The entry for this tool shed must always be available in this file, so re-add it before attempting to start your Galaxy server.\n'
+ raise Exception( exception_msg )
+ return tool_shed_accessible, missing_tool_configs_dict
+def check_tool_tag_set( elem, migrated_tool_configs_dict, missing_tool_configs_dict ):
+ file_path = elem.get( 'file', None )
+ if file_path:
+ path, name = os.path.split( file_path )
+ if name in migrated_tool_configs_dict:
+ tool_dependencies = migrated_tool_configs_dict[ name ]
+ missing_tool_configs_dict[ name ] = tool_dependencies
+ return missing_tool_configs_dict
+def get_non_shed_tool_panel_configs( app ):
+ # Get the non-shed related tool panel configs - there can be more than one, and the default is tool_conf.xml.
+ config_filenames = []
+ for config_filename in app.config.tool_configs:
+ # Any config file that includes a tool_path attribute in the root tag set like the following is shed-related.
+ # <toolbox tool_path="../shed_tools">
+ tree = util.parse_xml( config_filename )
+ root = tree.getroot()
+ tool_path = root.get( 'tool_path', None )
+ if tool_path is None:
+ config_filenames.append( config_filename )
+ return config_filenames
+def get_tool_shed_url_from_tools_xml_file_path( app, tool_shed ):
+ search_str = '://%s' % tool_shed
+ for shed_name, shed_url in app.tool_shed_registry.tool_sheds.items():
+ if shed_url.find( search_str ) >= 0:
+ if shed_url.endswith( '/' ):
+ shed_url = shed_url.rstrip( '/' )
+ return shed_url
+ return None
diff -r 6971bbc8bd8418baea51345b891e1aa647d14a88 -r d10de4954af2e94cb7c517ae6398d0247843168e lib/galaxy/tool_shed/install_manager.py
--- a/lib/galaxy/tool_shed/install_manager.py
+++ b/lib/galaxy/tool_shed/install_manager.py
@@ -7,8 +7,7 @@
from galaxy.util.json import from_json_string, to_json_string
from galaxy.util.shed_util import *
from galaxy.util.odict import odict
-
-REPOSITORY_OWNER = 'devteam'
+from galaxy.tool_shed.common_util import *
class InstallManager( object ):
def __init__( self, app, latest_migration_script_number, tool_shed_install_config, migrated_tools_config, install_dependencies ):
@@ -19,10 +18,11 @@
self.app = app
self.toolbox = self.app.toolbox
self.migrated_tools_config = migrated_tools_config
- # If install_dependencies is True, but tool_dependency_dir is not set,
- # do not attempt to install, but print informative error message
+ # If install_dependencies is True but tool_dependency_dir is not set, do not attempt to install but print informative error message.
if install_dependencies and app.config.tool_dependency_dir is None:
- raise Exception( 'You are attempting to install tool dependencies, but do not have a value for "tool_dependency_dir" set in your ini file. Please set this to the path where you would like to install dependencies and rerun the migration script.' )
+ message = 'You are attempting to install tool dependencies but do not have a value for "tool_dependency_dir" set in your universe_wsgi.ini '
+ message += 'file. Set this location value to the path where you want tool dependencies installed and rerun the migration script.'
+ raise Exception( message )
# Get the local non-shed related tool panel configs (there can be more than one, and the default name is tool_conf.xml).
self.proprietary_tool_confs = self.non_shed_tool_panel_configs
self.proprietary_tool_panel_elems = self.get_proprietary_tool_panel_elems( latest_migration_script_number )
@@ -38,8 +38,39 @@
self.tool_shed = clean_tool_shed_url( root.get( 'name' ) )
self.repository_owner = REPOSITORY_OWNER
index, self.shed_config_dict = get_shed_tool_conf_dict( app, self.migrated_tools_config )
- for repository_elem in root:
- self.install_repository( repository_elem, install_dependencies )
+ # Since tool migration scripts can be executed any number of times, we need to make sure the appropriate tools are defined in
+ # tool_conf.xml. If no tools associated with the migration stage are defined, no repositories will be installed on disk.
+ # The default behavior is that the tool shed is down.
+ tool_shed_accessible = False
+ tool_panel_configs = get_non_shed_tool_panel_configs( app )
+ if tool_panel_configs:
+ # The missing_tool_configs_dict contents are something like:
+ # {'emboss_antigenic.xml': [('emboss', '5.0.0', 'package', '\nreadme blah blah blah\n')]}
+ tool_shed_accessible, missing_tool_configs_dict = check_for_missing_tools( app, tool_panel_configs, latest_migration_script_number )
+ else:
+ # It doesn't matter if the tool shed is accessible since there are no migrated tools defined in the local Galaxy instance, but
+ # we have to set the value of tool_shed_accessible to True so that the value of migrate_tools.version can be correctly set in
+ # the database.
+ tool_shed_accessible = True
+ missing_tool_configs_dict = odict()
+ if tool_shed_accessible:
+ if len( self.proprietary_tool_confs ) == 1:
+ plural = ''
+ file_names = self.proprietary_tool_confs[ 0 ]
+ else:
+ plural = 's'
+ file_names = ', '.join( self.proprietary_tool_confs )
+ if missing_tool_configs_dict:
+ for repository_elem in root:
+ self.install_repository( repository_elem, install_dependencies )
+ else:
+ message = "\nNo tools associated with migration stage %s are defined in your " % str( latest_migration_script_number )
+ message += "file%s named %s,\nso no repositories will be installed on disk.\n" % ( plural, file_names )
+ print message
+ else:
+ message = "\nThe main Galaxy tool shed is not currently available, so skipped migration stage %s.\n" % str( latest_migration_script_number )
+ message += "Try again later.\n"
+ print message
def get_guid( self, repository_clone_url, relative_install_dir, tool_config ):
if self.shed_config_dict.get( 'tool_path' ):
relative_install_dir = os.path.join( self.shed_config_dict['tool_path'], relative_install_dir )
@@ -144,7 +175,8 @@
for k, v in tool_panel_dict_for_tool_config.items():
tool_panel_dict_for_display[ k ] = v
else:
- print 'The tool "%s" (%s) has not been enabled because it is not defined in a proprietary tool config (%s).' % ( guid, tool_config, ", ".join( self.proprietary_tool_confs or [] ) )
+ print 'The tool "%s" (%s) has not been enabled because it is not defined in a proprietary tool config (%s).' \
+ % ( guid, tool_config, ", ".join( self.proprietary_tool_confs or [] ) )
metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=self.app,
repository=tool_shed_repository,
repository_clone_url=repository_clone_url,
@@ -315,20 +347,9 @@
update_tool_shed_repository_status( self.app, tool_shed_repository, self.app.model.ToolShedRepository.installation_status.INSTALLED )
@property
def non_shed_tool_panel_configs( self ):
- # Get the non-shed related tool panel config file names from the Galaxy config - the default is tool_conf.xml.
- config_filenames = []
- for config_filename in self.app.config.tool_configs:
- # Any config file that includes a tool_path attribute in the root tag set like the following is shed-related.
- # <toolbox tool_path="../shed_tools">
- tree = util.parse_xml( config_filename )
- root = tree.getroot()
- tool_path = root.get( 'tool_path', None )
- if tool_path is None:
- config_filenames.append( config_filename )
- return config_filenames
+ return get_non_shed_tool_panel_configs( self.app )
def __get_url_from_tool_shed( self, tool_shed ):
- # The value of tool_shed is something like: toolshed.g2.bx.psu.edu
- # We need the URL to this tool shed, which is something like:
+ # The value of tool_shed is something like: toolshed.g2.bx.psu.edu. We need the URL to this tool shed, which is something like:
# http://toolshed.g2.bx.psu.edu/
for shed_name, shed_url in self.app.tool_shed_registry.tool_sheds.items():
if shed_url.find( tool_shed ) >= 0:
diff -r 6971bbc8bd8418baea51345b891e1aa647d14a88 -r d10de4954af2e94cb7c517ae6398d0247843168e lib/galaxy/tool_shed/migrate/common.py
--- a/lib/galaxy/tool_shed/migrate/common.py
+++ b/lib/galaxy/tool_shed/migrate/common.py
@@ -1,103 +1,13 @@
-import sys, os, ConfigParser, urllib2
+import sys, os, ConfigParser
import galaxy.config
import galaxy.datatypes.registry
-from galaxy import util, tools
+from galaxy import tools
import galaxy.model.mapping
import galaxy.tools.search
from galaxy.objectstore import build_object_store_from_config
+from galaxy.tool_shed.common_util import *
import galaxy.tool_shed.tool_shed_registry
from galaxy.tool_shed import install_manager
-from galaxy.tool_shed.encoding_util import *
-from galaxy.util.odict import odict
-
-REPOSITORY_OWNER = 'devteam'
-
-def check_for_missing_tools( app, tool_panel_configs, latest_tool_migration_script_number ):
- # Get the 000x_tools.xml file associated with the current migrate_tools version number.
- tools_xml_file_path = os.path.abspath( os.path.join( 'scripts', 'migrate_tools', '%04d_tools.xml' % latest_tool_migration_script_number ) )
- # Parse the XML and load the file attributes for later checking against the proprietary tool_panel_config.
- migrated_tool_configs_dict = odict()
- tree = util.parse_xml( tools_xml_file_path )
- root = tree.getroot()
- tool_shed = root.get( 'name' )
- tool_shed_url = get_tool_shed_url_from_tools_xml_file_path( app, tool_shed )
- # The default behavior is that the tool shed is down.
- tool_shed_accessible = False
- if tool_shed_url:
- for elem in root:
- if elem.tag == 'repository':
- tool_dependencies = []
- tool_dependencies_dict = {}
- repository_name = elem.get( 'name' )
- changeset_revision = elem.get( 'changeset_revision' )
- url = '%s/repository/get_tool_dependencies?name=%s&owner=%s&changeset_revision=%s&from_install_manager=True' % \
- ( tool_shed_url, repository_name, REPOSITORY_OWNER, changeset_revision )
- try:
- response = urllib2.urlopen( url )
- text = response.read()
- response.close()
- tool_shed_accessible = True
- except Exception, e:
- # Tool shed may be unavailable - we have to set tool_shed_accessible since we're looping.
- tool_shed_accessible = False
- print "The URL\n%s\nraised the exception:\n%s\n" % ( url, str( e ) )
- if tool_shed_accessible:
- if text:
- tool_dependencies_dict = tool_shed_decode( text )
- for dependency_key, requirements_dict in tool_dependencies_dict.items():
- tool_dependency_name = requirements_dict[ 'name' ]
- tool_dependency_version = requirements_dict[ 'version' ]
- tool_dependency_type = requirements_dict[ 'type' ]
- tool_dependency_readme = requirements_dict.get( 'readme', '' )
- tool_dependencies.append( ( tool_dependency_name, tool_dependency_version, tool_dependency_type, tool_dependency_readme ) )
- for tool_elem in elem.findall( 'tool' ):
- migrated_tool_configs_dict[ tool_elem.get( 'file' ) ] = tool_dependencies
- if tool_shed_accessible:
- # Parse the proprietary tool_panel_configs (the default is tool_conf.xml) and generate the list of missing tool config file names.
- missing_tool_configs_dict = odict()
- for tool_panel_config in tool_panel_configs:
- tree = util.parse_xml( tool_panel_config )
- root = tree.getroot()
- for elem in root:
- if elem.tag == 'tool':
- missing_tool_configs_dict = check_tool_tag_set( elem, migrated_tool_configs_dict, missing_tool_configs_dict )
- elif elem.tag == 'section':
- for section_elem in elem:
- if section_elem.tag == 'tool':
- missing_tool_configs_dict = check_tool_tag_set( section_elem, migrated_tool_configs_dict, missing_tool_configs_dict )
- else:
- exception_msg = '\n\nThe entry for the main Galaxy tool shed at %s is missing from the %s file. ' % ( tool_shed, app.config.tool_sheds_config )
- exception_msg += 'The entry for this tool shed must always be available in this file, so re-add it before attempting to start your Galaxy server.\n'
- raise Exception( exception_msg )
- return tool_shed_accessible, missing_tool_configs_dict
-def check_tool_tag_set( elem, migrated_tool_configs_dict, missing_tool_configs_dict ):
- file_path = elem.get( 'file', None )
- if file_path:
- path, name = os.path.split( file_path )
- if name in migrated_tool_configs_dict:
- tool_dependencies = migrated_tool_configs_dict[ name ]
- missing_tool_configs_dict[ name ] = tool_dependencies
- return missing_tool_configs_dict
-def get_non_shed_tool_panel_configs( app ):
- # Get the non-shed related tool panel configs - there can be more than one, and the default is tool_conf.xml.
- config_filenames = []
- for config_filename in app.config.tool_configs:
- # Any config file that includes a tool_path attribute in the root tag set like the following is shed-related.
- # <toolbox tool_path="../shed_tools">
- tree = util.parse_xml( config_filename )
- root = tree.getroot()
- tool_path = root.get( 'tool_path', None )
- if tool_path is None:
- config_filenames.append( config_filename )
- return config_filenames
-def get_tool_shed_url_from_tools_xml_file_path( app, tool_shed ):
- search_str = '://%s' % tool_shed
- for shed_name, shed_url in app.tool_shed_registry.tool_sheds.items():
- if shed_url.find( search_str ) >= 0:
- if shed_url.endswith( '/' ):
- shed_url = shed_url.rstrip( '/' )
- return shed_url
- return None
class MigrateToolsApplication( object ):
"""Encapsulates the state of a basic Galaxy Universe application in order to initiate the Install Manager"""
diff -r 6971bbc8bd8418baea51345b891e1aa647d14a88 -r d10de4954af2e94cb7c517ae6398d0247843168e scripts/migrate_tools/migrate_tools.py
--- a/scripts/migrate_tools/migrate_tools.py
+++ b/scripts/migrate_tools/migrate_tools.py
@@ -26,8 +26,8 @@
else:
plural = 's'
file_names = ', '.join( non_shed_tool_confs )
-msg = "\nThe installation process is finished. You should now remove entries for the installed tools from your file%s named\n" % plural
-msg += "%s and start your Galaxy server.\n" % file_names
+msg = "\nThe installation process is finished. If any tools associated with this migration were defined in your file%s named\n" % plural
+msg += "%s, then you should remove entries for them and start your Galaxy server.\n" % file_names
print msg
app.shutdown()
sys.exit( 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: dan: Change indent from 3 spaces to 4 for 8171:4b62ed46e5b6.
by Bitbucket 07 Nov '12
by Bitbucket 07 Nov '12
07 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/6971bbc8bd84/
changeset: 6971bbc8bd84
user: dan
date: 2012-11-07 16:45:10
summary: Change indent from 3 spaces to 4 for 8171:4b62ed46e5b6.
affected #: 1 file
diff -r 4b62ed46e5b6f5448ec262e445eefd0b17ef4898 -r 6971bbc8bd8418baea51345b891e1aa647d14a88 scripts/set_metadata.py
--- a/scripts/set_metadata.py
+++ b/scripts/set_metadata.py
@@ -14,9 +14,9 @@
# ensure supported version
from check_python import check_python
try:
- check_python()
+ check_python()
except:
- sys.exit(1)
+ sys.exit(1)
new_path = [ os.path.join( os.getcwd(), "lib" ) ]
new_path.extend( sys.path[1:] ) # remove scripts/ from the path
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dan: Use check_python() to verify python version in set_metadata.py instead of hardcoded minimum version check.
by Bitbucket 07 Nov '12
by Bitbucket 07 Nov '12
07 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/4b62ed46e5b6/
changeset: 4b62ed46e5b6
user: dan
date: 2012-11-07 16:35:56
summary: Use check_python() to verify python version in set_metadata.py instead of hardcoded minimum version check.
affected #: 1 file
diff -r 4ce18911037cc4297a84dfcfe81f36ccac98d425 -r 4b62ed46e5b6f5448ec262e445eefd0b17ef4898 scripts/set_metadata.py
--- a/scripts/set_metadata.py
+++ b/scripts/set_metadata.py
@@ -11,7 +11,12 @@
log = logging.getLogger( __name__ )
import os, sys, cPickle
-assert sys.version_info[:2] >= ( 2, 4 )
+# ensure supported version
+from check_python import check_python
+try:
+ check_python()
+except:
+ sys.exit(1)
new_path = [ os.path.join( os.getcwd(), "lib" ) ]
new_path.extend( sys.path[1:] ) # remove scripts/ from the path
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dan: When extracting a workflow from a history, provide a warning message if the tool version for the job does not match the tool version of the currently loaded tool.
by Bitbucket 07 Nov '12
by Bitbucket 07 Nov '12
07 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/4ce18911037c/
changeset: 4ce18911037c
user: dan
date: 2012-11-07 15:52:43
summary: When extracting a workflow from a history, provide a warning message if the tool version for the job does not match the tool version of the currently loaded tool.
affected #: 1 file
diff -r 6ff28399645ae158a03cec4b32c15a78da9b8018 -r 4ce18911037cc4297a84dfcfe81f36ccac98d425 templates/workflow/build_from_current_history.mako
--- a/templates/workflow/build_from_current_history.mako
+++ b/templates/workflow/build_from_current_history.mako
@@ -1,4 +1,5 @@
<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" /><% _=n_ %>
@@ -111,6 +112,10 @@
disabled = True
else:
disabled = False
+ if tool and tool.version != job.tool_version:
+ tool_version_warning = 'Dataset was created with tool version "%s", but workflow extraction will use version "%s".' % ( job.tool_version, tool.version )
+ else:
+ tool_version_warning = ''
%><tr>
@@ -123,6 +128,9 @@
<div style="font-style: italic; color: gray">This tool cannot be used in workflows</div>
%else:
<div><input type="checkbox" name="job_ids" value="${job.id}" checked="true" />Include "${tool_name}" in workflow</div>
+ %if tool_version_warning:
+ ${ render_msg( tool_version_warning, status="warning" ) }
+ %endif
%endif
</div></div>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dan: When getting job parameters for extracting a workflow from a history set ignore_errors to True. Prevents traceback when e.g. a tool was updated and had a text value changed to an integer.
by Bitbucket 07 Nov '12
by Bitbucket 07 Nov '12
07 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/6ff28399645a/
changeset: 6ff28399645a
user: dan
date: 2012-11-07 15:30:46
summary: When getting job parameters for extracting a workflow from a history set ignore_errors to True. Prevents traceback when e.g. a tool was updated and had a text value changed to an integer.
affected #: 1 file
diff -r af1d4e5a8b6325ea311513ffe9d671fe2b87764e -r 6ff28399645ae158a03cec4b32c15a78da9b8018 lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -1260,7 +1260,7 @@
assert job_id in jobs_by_id, "Attempt to create workflow with job not connected to current history"
job = jobs_by_id[ job_id ]
tool = trans.app.toolbox.get_tool( job.tool_id )
- param_values = job.get_param_values( trans.app )
+ param_values = job.get_param_values( trans.app, ignore_errors=True ) #If a tool was updated and e.g. had a text value changed to an integer, we don't want a traceback here
associations = cleanup_param_values( tool.inputs, param_values )
step = model.WorkflowStep()
step.type = 'tool'
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: clements: More work on docstrings to get them to be Sphinx compatible. Added dependency to Sphinx Makefile to get rid of errors when build files weren't there.
by Bitbucket 07 Nov '12
by Bitbucket 07 Nov '12
07 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/af1d4e5a8b63/
changeset: af1d4e5a8b63
user: clements
date: 2012-11-01 09:23:56
summary: More work on docstrings to get them to be Sphinx compatible. Added dependency to Sphinx Makefile to get rid of errors when build files weren't there.
affected #: 5 files
diff -r b0c9163490705bd872c2877070196056bc4e872e -r af1d4e5a8b6325ea311513ffe9d671fe2b87764e doc/Makefile
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -14,8 +14,22 @@
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+# Galaxy Local variables
+
+TOOLDATASHAREDDIR = ../tool-data/shared
+TOOLDATABUILDFILES = $(TOOLDATASHAREDDIR)/ensembl/builds.txt \
+ $(TOOLDATASHAREDDIR)/ncbi/builds.txt \
+ $(TOOLDATASHAREDDIR)/ucsc/publicbuilds.txt
+
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+# Sphinx wants the build files to be there; Copy the sample files into
+# place if we don't already have the build files.
+$(TOOLDATABUILDFILES) :
+ /bin/cp $@.sample $@
+
+
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@@ -41,7 +55,7 @@
clean:
-rm -rf $(BUILDDIR)/*
-html:
+html: $(TOOLDATABUILDFILES)
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
diff -r b0c9163490705bd872c2877070196056bc4e872e -r af1d4e5a8b6325ea311513ffe9d671fe2b87764e lib/galaxy/datatypes/assembly.py
--- a/lib/galaxy/datatypes/assembly.py
+++ b/lib/galaxy/datatypes/assembly.py
@@ -25,25 +25,26 @@
# It should call get_headers() like other sniff methods.
"""
Determines whether the file is an amos assembly file format
- Example:
- {CTG
- iid:1
- eid:1
- seq:
- CCTCTCCTGTAGAGTTCAACCGA-GCCGGTAGAGTTTTATCA
- .
- qlt:
- DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
- .
- {TLE
- src:1027
- off:0
- clr:618,0
- gap:
- 250 612
- .
- }
- }
+ Example::
+
+ {CTG
+ iid:1
+ eid:1
+ seq:
+ CCTCTCCTGTAGAGTTCAACCGA-GCCGGTAGAGTTTTATCA
+ .
+ qlt:
+ DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+ .
+ {TLE
+ src:1027
+ off:0
+ clr:618,0
+ gap:
+ 250 612
+ .
+ }
+ }
"""
isAmos = False
try:
@@ -68,7 +69,8 @@
def sniff( self, filename ):
"""
Determines whether the file is a velveth produced fasta format
- The id line has 3 fields separated by tabs: sequence_name sequence_index cataegory
+ The id line has 3 fields separated by tabs: sequence_name sequence_index cataegory::
+
>SEQUENCE_0_length_35 1 1
GGATATAGGGCCAACCCAACTCAACGGCCTGTCTT
>SEQUENCE_1_length_35 2 1
@@ -103,7 +105,7 @@
def sniff( self, filename ):
"""
- Determines whether the file is a velveth produced RoadMap
+ Determines whether the file is a velveth produced RoadMap::
142858 21 1
ROADMAP 1
ROADMAP 2
diff -r b0c9163490705bd872c2877070196056bc4e872e -r af1d4e5a8b6325ea311513ffe9d671fe2b87764e lib/galaxy/datatypes/data.py
--- a/lib/galaxy/datatypes/data.py
+++ b/lib/galaxy/datatypes/data.py
@@ -797,13 +797,14 @@
path, name = os.path.split(__file__)
full_path = os.path.join( path, 'test', fname )
return full_path
+
def get_file_peek( file_name, is_multi_byte=False, WIDTH=256, LINE_COUNT=5, skipchars=[] ):
"""
- Returns the first LINE_COUNT lines wrapped to WIDTH
+ Returns the first LINE_COUNT lines wrapped to WIDTH::
- ## >>> fname = get_test_fname('4.bed')
- ## >>> get_file_peek(fname)
- ## 'chr22 30128507 31828507 uc003bnx.1_cds_2_0_chr22_29227_f 0 +\n'
+ ## >>> fname = get_test_fname('4.bed')
+ ## >>> get_file_peek(fname)
+ ## 'chr22 30128507 31828507 uc003bnx.1_cds_2_0_chr22_29227_f 0 +\n'
"""
# Set size for file.readline() to a negative number to force it to
# read until either a newline or EOF. Needed for datasets with very
diff -r b0c9163490705bd872c2877070196056bc4e872e -r af1d4e5a8b6325ea311513ffe9d671fe2b87764e lib/galaxy/datatypes/genetics.py
--- a/lib/galaxy/datatypes/genetics.py
+++ b/lib/galaxy/datatypes/genetics.py
@@ -67,14 +67,18 @@
"""
from the ever-helpful angie hinrichs angie(a)soe.ucsc.edu
a genome graphs call looks like this
+
http://genome.ucsc.edu/cgi-bin/hgGenome?clade=mammal&org=Human&db=hg18&hgGe…
- &hgGenome_dataSetDescription=test&hgGenome_formatType=best%20guess&hgGenome_markerType=best%20guess
+ &hgGenome_dataSetDescription=test&hgGenome_formatType=best%20guess&hgGenome_markerType=best%20guess
&hgGenome_columnLabels=best%20guess&hgGenome_maxVal=&hgGenome_labelVals=
&hgGenome_maxGapToFill=25000000&hgGenome_uploadFile=http://galaxy.esphealth.org/datasets/333/display/index
&hgGenome_doSubmitUpload=submit
- Galaxy gives this for an interval file
+
+ Galaxy gives this for an interval file
+
http://genome.ucsc.edu/cgi-bin/hgTracks?db=hg18&position=chr1:1-1000&hgt.cu…
http%3A%2F%2Fgalaxy.esphealth.org%2Fdisplay_as%3Fid%3D339%26display_app%3Ducsc
+
"""
ret_val = []
ggtail = 'hgGenome_doSubmitUpload=submit'
diff -r b0c9163490705bd872c2877070196056bc4e872e -r af1d4e5a8b6325ea311513ffe9d671fe2b87764e lib/galaxy/datatypes/sequence.py
--- a/lib/galaxy/datatypes/sequence.py
+++ b/lib/galaxy/datatypes/sequence.py
@@ -25,11 +25,14 @@
"""
Class storing information about a sequence file composed of multiple gzip files concatenated as
one OR an uncompressed file. In the GZIP case, each sub-file's location is stored in start and end.
- The format of the file is JSON:
- { "sections" : [
- { "start" : "x", "end" : "y", "sequences" : "z" },
- ...
- ]}
+
+ The format of the file is JSON::
+
+ { "sections" : [
+ { "start" : "x", "end" : "y", "sequences" : "z" },
+ ...
+ ]}
+
"""
def set_peek( self, dataset, is_multi_byte=False ):
if not dataset.dataset.purged:
@@ -224,12 +227,19 @@
For complete details see http://www.ncbi.nlm.nih.gov/blast/fasta.shtml
Rules for sniffing as True:
+
We don't care about line length (other than empty lines).
+
The first non-empty line must start with '>' and the Very Next line.strip() must have sequence data and not be a header.
+
'sequence data' here is loosely defined as non-empty lines which do not start with '>'
+
This will cause Color Space FASTA (csfasta) to be detected as True (they are, after all, still FASTA files - they have a header line followed by sequence data)
+
Previously this method did some checking to determine if the sequence data had integers (presumably to differentiate between fasta and csfasta)
+
This should be done through sniff order, where csfasta (currently has a null sniff function) is detected for first (stricter definition) followed sometime after by fasta
+
We will only check that the first purported sequence is correctly formatted.
>>> fname = get_test_fname( 'sequence.maf' )
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: clements: Update docstrings so they no longer generate warnings in Sphix.
by Bitbucket 06 Nov '12
by Bitbucket 06 Nov '12
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/b0c916349070/
changeset: b0c916349070
user: clements
date: 2012-11-01 07:08:09
summary: Update docstrings so they no longer generate warnings in Sphix.
affected #: 2 files
diff -r f2abe519d05e6f4e01fcf9983ab3fb72f5009272 -r b0c9163490705bd872c2877070196056bc4e872e lib/galaxy/datatypes/util/gff_util.py
--- a/lib/galaxy/datatypes/util/gff_util.py
+++ b/lib/galaxy/datatypes/util/gff_util.py
@@ -119,12 +119,13 @@
Reader wrapper for GFF files.
Wrapper has two major functions:
- (1) group entries for GFF file (via group column), GFF3 (via id attribute),
- or GTF (via gene_id/transcript id);
- (2) convert coordinates from GFF format--starting and ending coordinates
- are 1-based, closed--to the 'traditional'/BED interval format--0 based,
- half-open. This is useful when using GFF files as inputs to tools that
- expect traditional interval format.
+
+ 1. group entries for GFF file (via group column), GFF3 (via id attribute),
+ or GTF (via gene_id/transcript id);
+ 2. convert coordinates from GFF format--starting and ending coordinates
+ are 1-based, closed--to the 'traditional'/BED interval format--0 based,
+ half-open. This is useful when using GFF files as inputs to tools that
+ expect traditional interval format.
"""
def __init__( self, reader, chrom_col=0, feature_col=2, start_col=3, \
@@ -303,9 +304,13 @@
"""
Parses a GFF/GTF attribute string and returns a dictionary of name-value
pairs. The general format for a GFF3 attributes string is
+
name1=value1;name2=value2
+
The general format for a GTF attribute string is
+
name1 "value1" ; name2 "value2"
+
The general format for a GFF attribute string is a single string that
denotes the interval's group; in this case, method returns a dictionary
with a single key-value pair, and key name is 'group'
@@ -412,4 +417,4 @@
for chrom_features in chroms_features_sorted:
for feature in chrom_features:
yield feature
-
\ No newline at end of file
+
diff -r f2abe519d05e6f4e01fcf9983ab3fb72f5009272 -r b0c9163490705bd872c2877070196056bc4e872e lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -7,6 +7,7 @@
"""
Compose the sequence of commands necessary to execute a job. This will
currently include:
+
- environment settings corresponding to any requirement tags
- preparing input files
- command line taken from job wrapper
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: clements: Change docstring so it no longer gerneates a warning in Sphinx.
by Bitbucket 06 Nov '12
by Bitbucket 06 Nov '12
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/f2abe519d05e/
changeset: f2abe519d05e
user: clements
date: 2012-11-01 06:50:03
summary: Change docstring so it no longer gerneates a warning in Sphinx.
affected #: 1 file
diff -r 563d279f69bcec68c76a5a3c7c4144b34b614011 -r f2abe519d05e6f4e01fcf9983ab3fb72f5009272 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -340,7 +340,7 @@
def get_database_engine_options( kwargs ):
"""
Allow options for the SQLAlchemy database engine to be passed by using
- the prefix "database_engine_option_".
+ the prefix "database_engine_option".
"""
conversions = {
'convert_unicode': string_as_bool,
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: greg: Add the ability to view the current data tables registry.
by Bitbucket 06 Nov '12
by Bitbucket 06 Nov '12
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/563d279f69bc/
changeset: 563d279f69bc
user: greg
date: 2012-11-06 22:11:13
summary: Add the ability to view the current data tables registry.
affected #: 4 files
diff -r 1911265573315cf71962ac1ae5ca6ba513d051b2 -r 563d279f69bcec68c76a5a3c7c4144b34b614011 lib/galaxy/webapps/galaxy/controllers/admin.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin.py
@@ -763,3 +763,9 @@
message = util.restore_text( kwd.get( 'message', '' ) )
status = util.restore_text( kwd.get( 'status', 'done' ) )
return trans.fill_template( 'admin/view_datatypes_registry.mako', message=message, status=status )
+ @web.expose
+ @web.require_admin
+ def view_tool_data_tables( self, trans, **kwd ):
+ message = util.restore_text( kwd.get( 'message', '' ) )
+ status = util.restore_text( kwd.get( 'status', 'done' ) )
+ return trans.fill_template( 'admin/view_data_tables_registry.mako', message=message, status=status )
diff -r 1911265573315cf71962ac1ae5ca6ba513d051b2 -r 563d279f69bcec68c76a5a3c7c4144b34b614011 templates/admin/view_data_tables_registry.mako
--- /dev/null
+++ b/templates/admin/view_data_tables_registry.mako
@@ -0,0 +1,42 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<%
+ ctr = 0
+ data_tables = trans.app.tool_data_tables
+ sorted_data_table_elem_names = sorted( trans.app.tool_data_tables.data_table_elem_names )
+%>
+
+<div class="toolForm">
+ <div class="toolFormTitle">Current data table registry contains ${len( sorted_data_table_elem_names )} data tables</div>
+ <div class="toolFormBody">
+ <table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <tr>
+ <th bgcolor="#D8D8D8">Name</th>
+ <th bgcolor="#D8D8D8">Tool data path</th>
+ <th bgcolor="#D8D8D8">Missing index file</th>
+ </tr>
+ %for data_table_elem_name in sorted_data_table_elem_names:
+ <% data_table = data_tables[ data_table_elem_name ] %>
+ %if ctr % 2 == 1:
+ <tr class="odd_row">
+ %else:
+ <tr class="tr">
+ %endif
+ <td>${data_table.name}</td>
+ <td>${data_table.tool_data_path}</td>
+ <td>
+ %if data_table.missing_index_file:
+ ${data_table.missing_index_file}
+ %endif
+ </td>
+ </tr>
+ <% ctr += 1 %>
+ %endfor
+ </table>
+ </div>
+</div>
diff -r 1911265573315cf71962ac1ae5ca6ba513d051b2 -r 563d279f69bcec68c76a5a3c7c4144b34b614011 templates/admin/view_datatypes_registry.mako
--- a/templates/admin/view_datatypes_registry.mako
+++ b/templates/admin/view_datatypes_registry.mako
@@ -22,14 +22,14 @@
%><div class="toolForm">
- <div class="toolFormTitle">Current datatypes registry contains ${len( sorted_datatypes )} datatypes</div>
+ <div class="toolFormTitle">Current data types registry contains ${len( sorted_datatypes )} data types</div><div class="toolFormBody"><table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%"><tr>
- <th>Extension</th>
- <th>Type</th>
- <th>Mimetype</th>
- <th>Display in upload</th>
+ <th bgcolor="#D8D8D8">Extension</th>
+ <th bgcolor="#D8D8D8">Type</th>
+ <th bgcolor="#D8D8D8">Mimetype</th>
+ <th bgcolor="#D8D8D8">Display in upload</th></tr>
%for datatype in sorted_datatypes:
%if ctr % 2 == 1:
diff -r 1911265573315cf71962ac1ae5ca6ba513d051b2 -r 563d279f69bcec68c76a5a3c7c4144b34b614011 templates/webapps/galaxy/admin/index.mako
--- a/templates/webapps/galaxy/admin/index.mako
+++ b/templates/webapps/galaxy/admin/index.mako
@@ -65,7 +65,8 @@
<div class="toolSectionTitle">Server</div><div class="toolSectionBody"><div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='view_datatypes_registry' )}" target="galaxy_main">View datatypes registry</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='view_datatypes_registry' )}" target="galaxy_main">View data types registry</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='view_tool_data_tables' )}" target="galaxy_main">View data tables registry</a></div><div class="toolTitle"><a href="${h.url_for( controller='admin', action='tool_versions' )}" target="galaxy_main">View tool lineage</a></div><div class="toolTitle"><a href="${h.url_for( controller='admin', action='reload_tool' )}" target="galaxy_main">Reload a tool's configuration</a></div><div class="toolTitle"><a href="${h.url_for( controller='admin', action='memdump' )}" target="galaxy_main">Profile memory usage</a></div>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/fdea658292c9/
changeset: fdea658292c9
user: carlfeberhard
date: 2012-11-06 21:54:47
summary: (alt)history: split files up, rework names, add global Galaxy namespace
affected #: 6 files
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 static/scripts/mvc/dataset/hda-edit.js
--- /dev/null
+++ b/static/scripts/mvc/dataset/hda-edit.js
@@ -0,0 +1,862 @@
+//define([
+// "../mvc/base-mvc"
+//], function(){
+//==============================================================================
+/** View for editing (working with - as opposed to viewing/read-only) an hda
+ *
+ */
+var HDAView = BaseView.extend( LoggableMixin ).extend({
+ //??TODO: add alias in initialize this.hda = this.model?
+ // view for HistoryDatasetAssociation model above
+
+ // uncomment this out see log messages
+ //logger : console,
+
+ tagName : "div",
+ className : "historyItemContainer",
+
+ // ................................................................................ SET UP
+ initialize : function( attributes ){
+ this.log( this + '.initialize:', attributes );
+
+ // render urlTemplates (gen. provided by GalaxyPaths) to urls
+ if( !attributes.urlTemplates ){ throw( 'HDAView needs urlTemplates on initialize' ); }
+ this.urls = this.renderUrls( attributes.urlTemplates, this.model.toJSON() );
+
+ // whether the body of this hda is expanded (shown)
+ this.expanded = attributes.expanded || false;
+
+ // re-render the entire view on any model change
+ this.model.bind( 'change', this.render, this );
+ //this.bind( 'all', function( event ){
+ // this.log( event );
+ //}, this );
+ },
+
+ // urlTemplates is a map (or nested map) of underscore templates (currently, anyhoo)
+ // use the templates to create the apropo urls for each action this ds could use
+ renderUrls : function( urlTemplates, modelJson ){
+ var hdaView = this,
+ urls = {};
+ _.each( urlTemplates, function( urlTemplateOrObj, urlKey ){
+ // object == nested templates: recurse
+ if( _.isObject( urlTemplateOrObj ) ){
+ urls[ urlKey ] = hdaView.renderUrls( urlTemplateOrObj, modelJson );
+
+ // string == template:
+ } else {
+ // meta_down load is a special case (see renderMetaDownloadUrls)
+ //TODO: should be a better (gen.) way to handle this case
+ if( urlKey === 'meta_download' ){
+ urls[ urlKey ] = hdaView.renderMetaDownloadUrls( urlTemplateOrObj, modelJson );
+ } else {
+ urls[ urlKey ] = _.template( urlTemplateOrObj, modelJson );
+ }
+ }
+ });
+ return urls;
+ },
+
+ // there can be more than one meta_file to download, so return a list of url and file_type for each
+ renderMetaDownloadUrls : function( urlTemplate, modelJson ){
+ return _.map( modelJson.meta_files, function( meta_file ){
+ return {
+ url : _.template( urlTemplate, { id: modelJson.id, file_type: meta_file.file_type }),
+ file_type : meta_file.file_type
+ };
+ });
+ },
+
+ // ................................................................................ RENDER MAIN
+ render : function(){
+ var view = this,
+ id = this.model.get( 'id' ),
+ state = this.model.get( 'state' ),
+ itemWrapper = $( '<div/>' ).attr( 'id', 'historyItem-' + id ),
+ initialRender = ( this.$el.children().size() === 0 );
+
+ //console.debug( this + '.render, initial?:', initialRender );
+
+ this._clearReferences();
+ this.$el.attr( 'id', 'historyItemContainer-' + id );
+
+ itemWrapper
+ .addClass( 'historyItemWrapper' ).addClass( 'historyItem' )
+ .addClass( 'historyItem-' + state );
+
+ itemWrapper.append( this._render_warnings() );
+ itemWrapper.append( this._render_titleBar() );
+ this.body = $( this._render_body() );
+ itemWrapper.append( this.body );
+
+ //TODO: move to own function: setUpBehaviours
+ // we can potentially skip this step and call popupmenu directly on the download button
+ make_popup_menus( itemWrapper );
+
+ // set up canned behavior on children (bootstrap, popupmenus, editable_text, etc.)
+ itemWrapper.find( '.tooltip' ).tooltip({ placement : 'bottom' });
+
+ // transition...
+ this.$el.fadeOut( 'fast', function(){
+ view.$el.children().remove();
+ view.$el.append( itemWrapper ).fadeIn( 'fast', function(){
+ view.log( view + ' rendered:', view.$el );
+ var renderedEventName = 'rendered';
+
+ if( initialRender ){
+ renderedEventName += ':initial';
+ } else if( view.model.inReadyState() ){
+ renderedEventName += ':ready';
+ }
+ view.trigger( renderedEventName );
+ });
+ });
+ return this;
+ },
+
+ //NOTE: button renderers have the side effect of caching their IconButtonViews to this view
+ // clear out cached sub-views, dom refs, etc. from prev. render
+ _clearReferences : function(){
+ //??TODO: we should reset these in the button logic checks (i.e. if not needed this.button = null; return null)
+ //?? do we really need these - not so far
+ //TODO: at least move these to a map
+ this.displayButton = null;
+ this.editButton = null;
+ this.deleteButton = null;
+ this.errButton = null;
+ this.showParamsButton = null;
+ this.rerunButton = null;
+ this.visualizationsButton = null;
+ this.tagButton = null;
+ this.annotateButton = null;
+ },
+
+ // ................................................................................ RENDER WARNINGS
+ // hda warnings including: is deleted, is purged, is hidden (including links to further actions (undelete, etc.))
+ _render_warnings : function(){
+ // jQ errs on building dom with whitespace - if there are no messages, trim -> ''
+ return $( jQuery.trim( HDAView.templates.messages(
+ _.extend( this.model.toJSON(), { urls: this.urls } )
+ )));
+ },
+
+ // ................................................................................ RENDER TITLEBAR
+ // the part of an hda always shown (whether the body is expanded or not): title link, title buttons
+ _render_titleBar : function(){
+ var titleBar = $( '<div class="historyItemTitleBar" style="overflow: hidden"></div>' );
+ titleBar.append( this._render_titleButtons() );
+ titleBar.append( '<span class="state-icon"></span>' );
+ titleBar.append( this._render_titleLink() );
+ return titleBar;
+ },
+
+ // ................................................................................ display, edit attr, delete
+ // icon-button group for the common, most easily accessed actions
+ //NOTE: these are generally displayed for almost every hda state (tho poss. disabled)
+ _render_titleButtons : function(){
+ // render the display, edit attr and delete icon-buttons
+ var buttonDiv = $( '<div class="historyItemButtons"></div>' );
+ buttonDiv.append( this._render_displayButton() );
+ buttonDiv.append( this._render_editButton() );
+ buttonDiv.append( this._render_deleteButton() );
+ return buttonDiv;
+ },
+
+ // icon-button to display this hda in the galaxy main iframe
+ _render_displayButton : function(){
+ // don't show display if not in ready state, error'd, or not accessible
+ if( ( !this.model.inReadyState() )
+ || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
+ || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
+ || ( !this.model.get( 'accessible' ) ) ){
+ return null;
+ }
+
+ var displayBtnData = {
+ icon_class : 'display'
+ };
+
+ // show a disabled display if the data's been purged
+ if( this.model.get( 'purged' ) ){
+ displayBtnData.enabled = false;
+ displayBtnData.title = 'Cannot display datasets removed from disk';
+
+ } else {
+ displayBtnData.title = 'Display data in browser';
+ displayBtnData.href = this.urls.display;
+ }
+
+ if( this.model.get( 'for_editing' ) ){
+ displayBtnData.target = 'galaxy_main';
+ }
+
+ this.displayButton = new IconButtonView({ model : new IconButton( displayBtnData ) });
+ return this.displayButton.render().$el;
+ },
+
+ // icon-button to edit the attributes (format, permissions, etc.) this hda
+ _render_editButton : function(){
+ // don't show edit while uploading, or if editable
+ if( ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.UPLOAD )
+ || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
+ || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
+ || ( !this.model.get( 'accessible' ) )
+ || ( !this.model.get( 'for_editing' ) ) ){
+ return null;
+ }
+
+ var purged = this.model.get( 'purged' ),
+ deleted = this.model.get( 'deleted' ),
+ editBtnData = {
+ title : 'Edit attributes',
+ href : this.urls.edit,
+ target : 'galaxy_main',
+ icon_class : 'edit'
+ };
+
+ // disable if purged or deleted and explain why in the tooltip
+ //TODO: if for_editing
+ if( deleted || purged ){
+ editBtnData.enabled = false;
+ if( purged ){
+ editBtnData.title = 'Cannot edit attributes of datasets removed from disk';
+ } else if( deleted ){
+ editBtnData.title = 'Undelete dataset to edit attributes';
+ }
+ }
+
+ this.editButton = new IconButtonView({ model : new IconButton( editBtnData ) });
+ return this.editButton.render().$el;
+ },
+
+ // icon-button to delete this hda
+ _render_deleteButton : function(){
+ // don't show delete if...
+ if( ( !this.model.get( 'for_editing' ) )
+ || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
+ || ( !this.model.get( 'accessible' ) ) ){
+ return null;
+ }
+
+ var deleteBtnData = {
+ title : 'Delete',
+ href : this.urls[ 'delete' ],
+ id : 'historyItemDeleter-' + this.model.get( 'id' ),
+ icon_class : 'delete'
+ };
+ if( this.model.get( 'deleted' ) || this.model.get( 'purged' ) ){
+ deleteBtnData = {
+ title : 'Dataset is already deleted',
+ icon_class : 'delete',
+ enabled : false
+ };
+ }
+ this.deleteButton = new IconButtonView({ model : new IconButton( deleteBtnData ) });
+ return this.deleteButton.render().$el;
+ },
+
+ // ................................................................................ titleLink
+ // render the hid and hda.name as a link (that will expand the body)
+ _render_titleLink : function(){
+ return $( jQuery.trim( HDAView.templates.titleLink(
+ _.extend( this.model.toJSON(), { urls: this.urls } )
+ )));
+ },
+
+ // ................................................................................ RENDER BODY
+ // render the data/metadata summary (format, size, misc info, etc.)
+ _render_hdaSummary : function(){
+ var modelData = _.extend( this.model.toJSON(), { urls: this.urls } );
+ // if there's no dbkey and it's editable : pass a flag to the template to render a link to editing in the '?'
+ if( this.model.get( 'metadata_dbkey' ) === '?'
+ && !this.model.isDeletedOrPurged() ){
+ _.extend( modelData, { dbkey_unknown_and_editable : true });
+ }
+ return HDAView.templates.hdaSummary( modelData );
+ },
+
+ // ................................................................................ primary actions
+ // render the icon-buttons gen. placed underneath the hda summary
+ _render_primaryActionButtons : function( buttonRenderingFuncs ){
+ var primaryActionButtons = $( '<div/>' ).attr( 'id', 'primary-actions-' + this.model.get( 'id' ) ),
+ view = this;
+ _.each( buttonRenderingFuncs, function( fn ){
+ primaryActionButtons.append( fn.call( view ) );
+ });
+ return primaryActionButtons;
+ },
+
+ // icon-button/popupmenu to down the data (and/or the associated meta files (bai, etc.)) for this hda
+ _render_downloadButton : function(){
+ // don't show anything if the data's been purged
+ if( this.model.get( 'purged' ) ){ return null; }
+
+ // return either: a single download icon-button (if there are no meta files)
+ // or a popupmenu with links to download assoc. meta files (if there are meta files)
+ var downloadLinkHTML = HDAView.templates.downloadLinks(
+ _.extend( this.model.toJSON(), { urls: this.urls } )
+ );
+ //this.log( this + '_render_downloadButton, downloadLinkHTML:', downloadLinkHTML );
+ return $( downloadLinkHTML );
+ },
+
+ // icon-button to show the input and output (stdout/err) for the job that created this hda
+ _render_errButton : function(){
+ if( ( this.model.get( 'state' ) !== HistoryDatasetAssociation.STATES.ERROR )
+ || ( !this.model.get( 'for_editing' ) ) ){ return null; }
+
+ this.errButton = new IconButtonView({ model : new IconButton({
+ title : 'View or report this error',
+ href : this.urls.report_error,
+ target : 'galaxy_main',
+ icon_class : 'bug'
+ })});
+ return this.errButton.render().$el;
+ },
+
+ // icon-button to show the input and output (stdout/err) for the job that created this hda
+ _render_showParamsButton : function(){
+ // gen. safe to show in all cases
+ this.showParamsButton = new IconButtonView({ model : new IconButton({
+ title : 'View details',
+ href : this.urls.show_params,
+ target : 'galaxy_main',
+ icon_class : 'information'
+ }) });
+ return this.showParamsButton.render().$el;
+ },
+
+ // icon-button to re run the job that created this hda
+ _render_rerunButton : function(){
+ if( !this.model.get( 'for_editing' ) ){ return null; }
+ this.rerunButton = new IconButtonView({ model : new IconButton({
+ title : 'Run this job again',
+ href : this.urls.rerun,
+ target : 'galaxy_main',
+ icon_class : 'arrow-circle'
+ }) });
+ return this.rerunButton.render().$el;
+ },
+
+ // build an icon-button or popupmenu based on the number of applicable visualizations
+ // also map button/popup clicks to viz setup functions
+ _render_visualizationsButton : function(){
+ var dbkey = this.model.get( 'dbkey' ),
+ visualizations = this.model.get( 'visualizations' ),
+ visualization_url = this.urls.visualization,
+ popup_menu_dict = {},
+ params = {
+ dataset_id: this.model.get( 'id' ),
+ hda_ldda: 'hda'
+ };
+
+ if( !( this.model.hasData() )
+ || !( this.model.get( 'for_editing' ) )
+ || !( visualizations && visualizations.length )
+ || !( visualization_url ) ){
+ //console.warn( 'NOT rendering visualization icon' )
+ return null;
+ }
+
+ // render the icon from template
+ this.visualizationsButton = new IconButtonView({ model : new IconButton({
+ title : 'Visualize',
+ href : visualization_url,
+ icon_class : 'chart_curve'
+ })});
+ var $icon = this.visualizationsButton.render().$el;
+ $icon.addClass( 'visualize-icon' ); // needed?
+
+ //TODO: make this more concise
+ // map a function to each visualization in the icon's attributes
+ // create a popupmenu from that map
+ // Add dbkey to params if it exists.
+ if( dbkey ){ params.dbkey = dbkey; }
+
+ function create_viz_action( visualization ) {
+ switch( visualization ){
+ case 'trackster':
+ return create_trackster_action_fn( visualization_url, params, dbkey );
+ case 'scatterplot':
+ return create_scatterplot_action_fn( visualization_url, params );
+ default:
+ return function(){
+ window.parent.location = visualization_url + '/' + visualization + '?' + $.param( params ); };
+ }
+ }
+
+ // No need for popup menu because there's a single visualization.
+ if ( visualizations.length === 1 ) {
+ $icon.attr( 'title', visualizations[0] );
+ $icon.click( create_viz_action( visualizations[0] ) );
+
+ // >1: Populate menu dict with visualization fns, make the popupmenu
+ } else {
+ _.each( visualizations, function( visualization ) {
+ //TODO: move to utils
+ var titleCaseVisualization = visualization.charAt( 0 ).toUpperCase() + visualization.slice( 1 );
+ popup_menu_dict[ titleCaseVisualization ] = create_viz_action( visualization );
+ });
+ make_popupmenu( $icon, popup_menu_dict );
+ }
+ return $icon;
+ },
+
+ // ................................................................................ secondary actions
+ // secondary actions: currently tagging and annotation (if user is allowed)
+ _render_secondaryActionButtons : function( buttonRenderingFuncs ){
+ // move to the right (same level as primary)
+ var secondaryActionButtons = $( '<div/>' ),
+ view = this;
+ secondaryActionButtons
+ .attr( 'style', 'float: right;' )
+ .attr( 'id', 'secondary-actions-' + this.model.get( 'id' ) );
+
+ _.each( buttonRenderingFuncs, function( fn ){
+ secondaryActionButtons.append( fn.call( view ) );
+ });
+ return secondaryActionButtons;
+ },
+
+ // icon-button to load and display tagging html
+ //TODO: these should be a sub-MV
+ _render_tagButton : function(){
+ if( !( this.model.hasData() )
+ || !( this.model.get( 'for_editing' ) )
+ || ( !this.urls.tags.get ) ){ return null; }
+
+ this.tagButton = new IconButtonView({ model : new IconButton({
+ title : 'Edit dataset tags',
+ target : 'galaxy_main',
+ href : this.urls.tags.get,
+ icon_class : 'tags'
+ })});
+ return this.tagButton.render().$el;
+ },
+
+ // icon-button to load and display annotation html
+ //TODO: these should be a sub-MV
+ _render_annotateButton : function(){
+ if( !( this.model.hasData() )
+ || !( this.model.get( 'for_editing' ) )
+ || ( !this.urls.annotation.get ) ){ return null; }
+
+ this.annotateButton = new IconButtonView({ model : new IconButton({
+ title : 'Edit dataset annotation',
+ target : 'galaxy_main',
+ icon_class : 'annotate'
+ })});
+ return this.annotateButton.render().$el;
+ },
+
+ // ................................................................................ other elements
+ // render links to external genome display applications (igb, gbrowse, etc.)
+ //TODO: not a fan of the style on these
+ _render_displayApps : function(){
+ if( !this.model.hasData() ){ return null; }
+
+ var displayAppsDiv = $( '<div/>' ).addClass( 'display-apps' );
+ if( !_.isEmpty( this.model.get( 'display_types' ) ) ){
+ //this.log( this + 'display_types:', this.model.get( 'urls' ).display_types );
+ //TODO:?? does this ever get used?
+ displayAppsDiv.append(
+ HDAView.templates.displayApps({ displayApps : this.model.get( 'display_types' ) })
+ );
+ }
+ if( !_.isEmpty( this.model.get( 'display_apps' ) ) ){
+ //this.log( this + 'display_apps:', this.model.get( 'urls' ).display_apps );
+ displayAppsDiv.append(
+ HDAView.templates.displayApps({ displayApps : this.model.get( 'display_apps' ) })
+ );
+ }
+ return displayAppsDiv;
+ },
+
+ //TODO: into sub-MV
+ // render the area used to load tag display
+ _render_tagArea : function(){
+ if( !this.urls.tags.set ){ return null; }
+ //TODO: move to mvc/tags.js
+ return $( HDAView.templates.tagArea(
+ _.extend( this.model.toJSON(), { urls: this.urls } )
+ ));
+ },
+
+ //TODO: into sub-MV
+ // render the area used to load annotation display
+ _render_annotationArea : function(){
+ if( !this.urls.annotation.get ){ return null; }
+ //TODO: move to mvc/annotations.js
+ return $( HDAView.templates.annotationArea(
+ _.extend( this.model.toJSON(), { urls: this.urls } )
+ ));
+ },
+
+ // render the data peek
+ //TODO: curr. pre-formatted into table on the server side - may not be ideal/flexible
+ _render_peek : function(){
+ if( !this.model.get( 'peek' ) ){ return null; }
+ return $( '<div/>' ).append(
+ $( '<pre/>' )
+ .attr( 'id', 'peek' + this.model.get( 'id' ) )
+ .addClass( 'peek' )
+ .append( this.model.get( 'peek' ) )
+ );
+ },
+
+ // ................................................................................ state body renderers
+ // _render_body fns for the various states
+ //TODO: only render these on expansion (or already expanded)
+ _render_body_not_viewable : function( parent ){
+ //TODO: revisit - still showing display, edit, delete (as common) - that CAN'T be right
+ parent.append( $( '<div>You do not have permission to view dataset.</div>' ) );
+ },
+
+ _render_body_uploading : function( parent ){
+ parent.append( $( '<div>Dataset is uploading</div>' ) );
+ },
+
+ _render_body_queued : function( parent ){
+ parent.append( $( '<div>Job is waiting to run.</div>' ) );
+ parent.append( this._render_primaryActionButtons([
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ },
+
+ _render_body_running : function( parent ){
+ parent.append( '<div>Job is currently running.</div>' );
+ parent.append( this._render_primaryActionButtons([
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ },
+
+ _render_body_error : function( parent ){
+ if( !this.model.get( 'purged' ) ){
+ parent.append( $( '<div>' + this.model.get( 'misc_blurb' ) + '</div>' ) );
+ }
+ parent.append( ( 'An error occurred running this job: '
+ + '<i>' + $.trim( this.model.get( 'misc_info' ) ) + '</i>' ) );
+ parent.append( this._render_primaryActionButtons([
+ this._render_downloadButton,
+ this._render_errButton,
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ },
+
+ _render_body_discarded : function( parent ){
+ parent.append( '<div>The job creating this dataset was cancelled before completion.</div>' );
+ parent.append( this._render_primaryActionButtons([
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ },
+
+ _render_body_setting_metadata : function( parent ){
+ parent.append( $( '<div>Metadata is being auto-detected.</div>' ) );
+ },
+
+ _render_body_empty : function( parent ){
+ //TODO: replace i with dataset-misc-info class
+ //?? why are we showing the file size when we know it's zero??
+ parent.append( $( '<div>No data: <i>' + this.model.get( 'misc_blurb' ) + '</i></div>' ) );
+ parent.append( this._render_primaryActionButtons([
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ },
+
+ _render_body_failed_metadata : function( parent ){
+ //TODO: the css for this box is broken (unlike the others)
+ // add a message box about the failure at the top of the body...
+ parent.append( $( HDAView.templates.failedMetadata( this.model.toJSON() ) ) );
+ //...then render the remaining body as STATES.OK (only diff between these states is the box above)
+ this._render_body_ok( parent );
+ },
+
+ _render_body_ok : function( parent ){
+ // most common state renderer and the most complicated
+ parent.append( this._render_hdaSummary() );
+
+ // return shortened form if del'd
+ //TODO: is this correct? maybe only on purged
+ if( this.model.isDeletedOrPurged() ){
+ parent.append( this._render_primaryActionButtons([
+ this._render_downloadButton,
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ return;
+ }
+
+ //NOTE: change the order here
+ parent.append( this._render_primaryActionButtons([
+ this._render_downloadButton,
+ this._render_errButton,
+ this._render_showParamsButton,
+ this._render_rerunButton,
+ this._render_visualizationsButton
+ ]));
+ parent.append( this._render_secondaryActionButtons([
+ this._render_tagButton,
+ this._render_annotateButton
+ ]));
+ parent.append( '<div class="clear"/>' );
+
+ parent.append( this._render_tagArea() );
+ parent.append( this._render_annotationArea() );
+
+ parent.append( this._render_displayApps() );
+ parent.append( this._render_peek() );
+
+ //TODO??: still needed?
+ //// If Mozilla, hide scrollbars in hidden items since they cause animation bugs
+ //if ( $.browser.mozilla ) {
+ // $( "div.historyItemBody" ).each( function() {
+ // if ( !$(this).is(":visible") ) { $(this).find( "pre.peek" ).css( "overflow", "hidden" ); }
+ // });
+ //}
+ },
+
+ _render_body : function(){
+ //this.log( this + '_render_body' );
+ //this.log( 'state:', state, 'for_editing', for_editing );
+
+ //TODO: incorrect id (encoded - use hid?)
+ var body = $( '<div/>' )
+ .attr( 'id', 'info-' + this.model.get( 'id' ) )
+ .addClass( 'historyItemBody' )
+ .attr( 'style', 'display: block' );
+
+ //TODO: not a fan of this dispatch
+ switch( this.model.get( 'state' ) ){
+ case HistoryDatasetAssociation.STATES.NOT_VIEWABLE :
+ this._render_body_not_viewable( body );
+ break;
+ case HistoryDatasetAssociation.STATES.UPLOAD :
+ this._render_body_uploading( body );
+ break;
+ case HistoryDatasetAssociation.STATES.QUEUED :
+ this._render_body_queued( body );
+ break;
+ case HistoryDatasetAssociation.STATES.RUNNING :
+ this._render_body_running( body );
+ break;
+ case HistoryDatasetAssociation.STATES.ERROR :
+ this._render_body_error( body );
+ break;
+ case HistoryDatasetAssociation.STATES.DISCARDED :
+ this._render_body_discarded( body );
+ break;
+ case HistoryDatasetAssociation.STATES.SETTING_METADATA :
+ this._render_body_setting_metadata( body );
+ break;
+ case HistoryDatasetAssociation.STATES.EMPTY :
+ this._render_body_empty( body );
+ break;
+ case HistoryDatasetAssociation.STATES.FAILED_METADATA :
+ this._render_body_failed_metadata( body );
+ break;
+ case HistoryDatasetAssociation.STATES.OK :
+ this._render_body_ok( body );
+ break;
+ default:
+ //??: no body?
+ body.append( $( '<div>Error: unknown dataset state "' + state + '".</div>' ) );
+ }
+ body.append( '<div style="clear: both"></div>' );
+
+ if( this.expanded ){
+ body.show();
+ } else {
+ body.hide();
+ }
+ return body;
+ },
+
+ // ................................................................................ EVENTS
+ events : {
+ 'click .historyItemTitle' : 'toggleBodyVisibility',
+ 'click a.icon-button.tags' : 'loadAndDisplayTags',
+ 'click a.icon-button.annotate' : 'loadAndDisplayAnnotation'
+ },
+
+ // ................................................................................ STATE CHANGES / MANIPULATION
+ // find the tag area and, if initial: (via ajax) load the html for displaying them; otherwise, unhide/hide
+ //TODO: into sub-MV
+ loadAndDisplayTags : function( event ){
+ //BUG: broken with latest
+ //TODO: this is a drop in from history.mako - should use MV as well
+ this.log( this + '.loadAndDisplayTags', event );
+ var tagArea = this.$el.find( '.tag-area' ),
+ tagElt = tagArea.find( '.tag-elt' );
+
+ // Show or hide tag area; if showing tag area and it's empty, fill it.
+ if( tagArea.is( ":hidden" ) ){
+ if( !jQuery.trim( tagElt.html() ) ){
+ // Need to fill tag element.
+ $.ajax({
+ //TODO: the html from this breaks a couple of times
+ url: this.urls.tags.get,
+ error: function() { alert( "Tagging failed" ); },
+ success: function(tag_elt_html) {
+ tagElt.html(tag_elt_html);
+ tagElt.find(".tooltip").tooltip();
+ tagArea.slideDown("fast");
+ }
+ });
+ } else {
+ // Tag element is filled; show.
+ tagArea.slideDown("fast");
+ }
+ } else {
+ // Hide.
+ tagArea.slideUp("fast");
+ }
+ return false;
+ },
+
+ // find the annotation area and, if initial: (via ajax) load the html for displaying it; otherwise, unhide/hide
+ //TODO: into sub-MV
+ loadAndDisplayAnnotation : function( event ){
+ //TODO: this is a drop in from history.mako - should use MV as well
+ this.log( this + '.loadAndDisplayAnnotation', event );
+ var annotationArea = this.$el.find( '.annotation-area' ),
+ annotationElem = annotationArea.find( '.annotation-elt' ),
+ setAnnotationUrl = this.urls.annotation.set;
+
+ // Show or hide annotation area; if showing annotation area and it's empty, fill it.
+ if ( annotationArea.is( ":hidden" ) ){
+ if( !jQuery.trim( annotationElem.html() ) ){
+ // Need to fill annotation element.
+ $.ajax({
+ url: this.urls.annotation.get,
+ error: function(){ alert( "Annotations failed" ); },
+ success: function( htmlFromAjax ){
+ if( htmlFromAjax === "" ){
+ htmlFromAjax = "<em>Describe or add notes to dataset</em>";
+ }
+ annotationElem.html( htmlFromAjax );
+ annotationArea.find(".tooltip").tooltip();
+
+ async_save_text(
+ annotationElem.attr("id"), annotationElem.attr("id"),
+ setAnnotationUrl,
+ "new_annotation", 18, true, 4
+ );
+ annotationArea.slideDown("fast");
+ }
+ });
+ } else {
+ annotationArea.slideDown("fast");
+ }
+
+ } else {
+ // Hide.
+ annotationArea.slideUp("fast");
+ }
+ return false;
+ },
+
+ // expand/collapse body
+ //side effect: trigger event
+ toggleBodyVisibility : function( event, expanded ){
+ var $body = this.$el.find( '.historyItemBody' );
+ expanded = ( expanded === undefined )?( !$body.is( ':visible' ) ):( expanded );
+ //this.log( 'toggleBodyVisibility, expanded:', expanded, '$body:', $body );
+
+ if( expanded ){
+ $body.slideDown( 'fast' );
+ } else {
+ $body.slideUp( 'fast' );
+ }
+ this.trigger( 'toggleBodyVisibility', this.model.get( 'id' ), expanded );
+ },
+
+ // ................................................................................ UTILTIY
+ toString : function(){
+ var modelString = ( this.model )?( this.model + '' ):( '(no model)' );
+ return 'HDAView(' + modelString + ')';
+ }
+});
+
+//------------------------------------------------------------------------------
+HDAView.templates = {
+ warningMsg : Handlebars.templates[ 'template-warningmessagesmall' ],
+
+ messages : Handlebars.templates[ 'template-history-warning-messages' ],
+ titleLink : Handlebars.templates[ 'template-history-titleLink' ],
+ hdaSummary : Handlebars.templates[ 'template-history-hdaSummary' ],
+ downloadLinks : Handlebars.templates[ 'template-history-downloadLinks' ],
+ failedMetadata : Handlebars.templates[ 'template-history-failedMetaData' ],
+ tagArea : Handlebars.templates[ 'template-history-tagArea' ],
+ annotationArea : Handlebars.templates[ 'template-history-annotationArea' ],
+ displayApps : Handlebars.templates[ 'template-history-displayApps' ]
+};
+
+//==============================================================================
+//TODO: these belong somewhere else
+
+//TODO: should be imported from scatterplot.js
+//TODO: OR abstracted to 'load this in the galaxy_main frame'
+function create_scatterplot_action_fn( url, params ){
+ action = function() {
+ var galaxy_main = $( window.parent.document ).find( 'iframe#galaxy_main' ),
+ final_url = url + '/scatterplot?' + $.param(params);
+ galaxy_main.attr( 'src', final_url );
+ //TODO: this needs to go away
+ $( 'div.popmenu-wrapper' ).remove();
+ return false;
+ };
+ return action;
+}
+
+// -----------------------------------------------------------------------------
+// Create trackster action function.
+//TODO: should be imported from trackster.js
+function create_trackster_action_fn(vis_url, dataset_params, dbkey) {
+ return function() {
+ var params = {};
+ if (dbkey) { params.dbkey = dbkey; }
+ $.ajax({
+ url: vis_url + '/list_tracks?f-' + $.param(params),
+ dataType: "html",
+ error: function() { alert( "Could not add this dataset to browser." ); },
+ success: function(table_html) {
+ var parent = window.parent;
+
+ parent.show_modal("View Data in a New or Saved Visualization", "", {
+ "Cancel": function() {
+ parent.hide_modal();
+ },
+ "View in saved visualization": function() {
+ // Show new modal with saved visualizations.
+ parent.show_modal("Add Data to Saved Visualization", table_html, {
+ "Cancel": function() {
+ parent.hide_modal();
+ },
+ "Add to visualization": function() {
+ $(parent.document).find('input[name=id]:checked').each(function() {
+ var vis_id = $(this).val();
+ dataset_params.id = vis_id;
+ parent.location = vis_url + "/trackster?" + $.param(dataset_params);
+ });
+ }
+ });
+ },
+ "View in new visualization": function() {
+ parent.location = vis_url + "/trackster?" + $.param(dataset_params);
+ }
+ });
+ }
+ });
+ return false;
+ };
+}
+
+//==============================================================================
+//return {
+// HDAView : HDAView,
+//};});
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 static/scripts/mvc/dataset/hda-model.js
--- /dev/null
+++ b/static/scripts/mvc/dataset/hda-model.js
@@ -0,0 +1,223 @@
+//define([
+// "../mvc/base-mvc"
+//], function(){
+//==============================================================================
+/**
+ *
+ */
+var HistoryDatasetAssociation = BaseModel.extend( LoggableMixin ).extend({
+ // a single HDA model
+
+ // uncomment this out see log messages
+ //logger : console,
+
+ defaults : {
+ // ---these are part of an HDA proper:
+
+ // parent (containing) history
+ history_id : null,
+ // often used with tagging
+ model_class : 'HistoryDatasetAssociation',
+ // index within history (??)
+ hid : 0,
+
+ // ---whereas these are Dataset related/inherited
+
+ id : null,
+ name : '',
+ // one of HistoryDatasetAssociation.STATES
+ state : '',
+ // sniffed datatype (sam, tabular, bed, etc.)
+ data_type : null,
+ // size in bytes
+ file_size : 0,
+
+ // array of associated file types (eg. [ 'bam_index', ... ])
+ meta_files : [],
+
+ misc_blurb : '',
+ misc_info : '',
+
+ deleted : false,
+ purged : false,
+ // aka. !hidden
+ visible : false,
+ // based on trans.user (is_admin or security_agent.can_access_dataset( <user_roles>, hda.dataset ))
+ accessible : false,
+
+ //TODO: this needs to be removed (it is a function of the view type (e.g. HDAForEditingView))
+ for_editing : true
+ },
+
+ // fetch location of this history in the api
+ url : function(){
+ //TODO: get this via url router
+ return 'api/histories/' + this.get( 'history_id' ) + '/contents/' + this.get( 'id' );
+ },
+
+ // (curr) only handles changing state of non-accessible hdas to STATES.NOT_VIEWABLE
+ //TODO:? use initialize (or validate) to check purged AND deleted -> purged XOR deleted
+ initialize : function(){
+ this.log( this + '.initialize', this.attributes );
+ this.log( '\tparent history_id: ' + this.get( 'history_id' ) );
+
+ //!! this state is not in trans.app.model.Dataset.states - set it here -
+ //TODO: change to server side.
+ if( !this.get( 'accessible' ) ){
+ this.set( 'state', HistoryDatasetAssociation.STATES.NOT_VIEWABLE );
+ }
+
+ // if the state has changed and the new state is a ready state, fire an event
+ this.on( 'change:state', function( currModel, newState ){
+ this.log( this + ' has changed state:', currModel, newState );
+ if( this.inReadyState() ){
+ this.trigger( 'state:ready', this.get( 'id' ), newState, this.previous( 'state' ), currModel );
+ }
+ });
+
+ // debug on change events
+ //this.on( 'change', function( currModel, changedList ){
+ // this.log( this + ' has changed:', currModel, changedList );
+ //});
+ //this.bind( 'all', function( event ){
+ // this.log( this + '', arguments );
+ //});
+ },
+
+ isDeletedOrPurged : function(){
+ return ( this.get( 'deleted' ) || this.get( 'purged' ) );
+ },
+
+ // based on show_deleted, show_hidden (gen. from the container control), would this ds show in the list of ds's?
+ //TODO: too many visibles
+ 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;
+ },
+
+ // 'ready' states are states where no processing (for the ds) is left to do on the server
+ inReadyState : function(){
+ var state = this.get( 'state' );
+ return (
+ ( state === HistoryDatasetAssociation.STATES.NEW )
+ || ( state === HistoryDatasetAssociation.STATES.OK )
+ || ( state === HistoryDatasetAssociation.STATES.EMPTY )
+ || ( state === HistoryDatasetAssociation.STATES.FAILED_METADATA )
+ || ( state === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
+ || ( state === HistoryDatasetAssociation.STATES.DISCARDED )
+ || ( state === HistoryDatasetAssociation.STATES.ERROR )
+ );
+ },
+
+ // convenience fn to match hda.has_data
+ hasData : function(){
+ //TODO:?? is this equivalent to all possible hda.has_data calls?
+ return ( this.get( 'file_size' ) > 0 );
+ },
+
+ toString : function(){
+ var nameAndId = this.get( 'id' ) || '';
+ if( this.get( 'name' ) ){
+ nameAndId += ':"' + this.get( 'name' ) + '"';
+ }
+ return 'HistoryDatasetAssociation(' + nameAndId + ')';
+ }
+});
+
+//------------------------------------------------------------------------------
+HistoryDatasetAssociation.STATES = {
+ UPLOAD : 'upload',
+ QUEUED : 'queued',
+ RUNNING : 'running',
+ SETTING_METADATA : 'setting_metadata',
+
+ NEW : 'new',
+ OK : 'ok',
+ EMPTY : 'empty',
+
+ FAILED_METADATA : 'failed_metadata',
+ NOT_VIEWABLE : 'noPermission', // not in trans.app.model.Dataset.states
+ DISCARDED : 'discarded',
+ ERROR : 'error'
+};
+
+//==============================================================================
+/**
+ *
+ */
+var HDACollection = Backbone.Collection.extend( LoggableMixin ).extend({
+ model : HistoryDatasetAssociation,
+
+ //logger : console,
+
+ initialize : function(){
+ //this.bind( 'all', function( event ){
+ // this.log( this + '', arguments );
+ //});
+ },
+
+ // return the ids of every hda in this collection
+ ids : function(){
+ return this.map( function( item ){ return item.id; });
+ },
+
+ // return an HDA collection containing every 'shown' hda based on show_deleted/hidden
+ getVisible : function( show_deleted, show_hidden ){
+ return this.filter( function( item ){ return item.isVisible( show_deleted, show_hidden ); });
+ },
+
+ // get a map where <possible hda state> : [ <list of hda ids in that state> ]
+ getStateLists : function(){
+ var stateLists = {};
+ _.each( _.values( HistoryDatasetAssociation.STATES ), function( state ){
+ stateLists[ state ] = [];
+ });
+ //NOTE: will err on unknown state
+ this.each( function( item ){
+ stateLists[ item.get( 'state' ) ].push( item.get( 'id' ) );
+ });
+ return stateLists;
+ },
+
+ // returns the id of every hda still running (not in a ready state)
+ running : function(){
+ var idList = [];
+ this.each( function( item ){
+ if( !item.inReadyState() ){
+ idList.push( item.get( 'id' ) );
+ }
+ });
+ return idList;
+ },
+
+ // update (fetch -> render) the hdas with the ids given
+ update : function( ids ){
+ this.log( this + 'update:', ids );
+
+ if( !( ids && ids.length ) ){ return; }
+
+ var collection = this;
+ _.each( ids, function( id, index ){
+ var historyItem = collection.get( id );
+ historyItem.fetch();
+ });
+ },
+
+ toString : function(){
+ return ( 'HDACollection(' + this.ids().join(',') + ')' );
+ }
+});
+
+//==============================================================================
+//return {
+// HistoryDatasetAssociation : HistoryDatasetAssociation,
+// HDACollection : HDACollection,
+//};});
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 static/scripts/mvc/history.js
--- a/static/scripts/mvc/history.js
+++ /dev/null
@@ -1,1611 +0,0 @@
-//define([
-// "../mvc/base-mvc"
-//
-//], function(){
-/* =============================================================================
-Backbone.js implementation of history panel
-
-TODO:
- bug:
- anon, mako:
- tooltips not rendered
- anno, tags rendered
- title editable
- bug:
- when over quota history is re-rendered, over quota msg is not displayed
- bc the quota:over event isn't fired
- bc the user state hasn't changed
-
- anon user, mako template init:
- bug: rename url seems to be wrong url
-
- currently, adding a dataset (via tool execute, etc.) creates a new dataset and refreshes the page
- logged in, mako template:
- BUG: am able to start upload even if over quota - 'runs' forever
-
- BUG: from above sit, delete uploading hda - now in state 'discarded'! ...new state to handle
- bug: quotaMeter bar rendering square in chrome
- BUG: quotaMsg not showing when 100% (on load)
- BUG: upload, history size, doesn't change
- TODO: on hdas state:ready, update ONLY the size...from what? histories.py? in js?
- BUG: imported, shared history with unaccessible dataset errs in historycontents when getting history
- (entire history is inaccessible)
- ??: still happening?
-
- from loadFromApi:
- BUG: not showing previous annotations
-
- fixed:
- BUG: historyItem, error'd ds show display, download?
- FIXED: removed
- bug: loading hdas (alt_hist)
- FIXED: added anon user api request ( trans.user == None and trans.history.id == requested id )
- bug: quota meter not updating on upload/tool run
- FIXED: quotaMeter now listens for 'state:ready' from glx_history in alternate_history.mako
- bug: use of new HDACollection with event listener in init doesn't die...keeps reporting
- FIXED: change getVisible to return an array
- BUG: history, broken intial hist state (running, updater, etc.)
- ??: doesn't seem to happen anymore
- BUG: collapse all should remove all expanded from storage
- FIXED: hideAllItemBodies now resets storage.expandedItems
- BUG: historyItem, shouldn't allow tag, annotate, peek on purged datasets
- FIXED: ok state now shows only: info, rerun
- BUG: history?, some ids aren't returning encoded...
- FIXED:???
- BUG: history, showing deleted ds
- FIXED
- UGH: historyItems have to be decorated with history_ids (api/histories/:history_id/contents/:id)
- FIXED by adding history_id to history_contents.show
- BUG: history, if hist has err'd ds, hist has perm state 'error', updater on following ds's doesn't run
- FIXED by reordering history state from ds' states here and histories
- BUG: history, broken annotation on reload (can't get thru api (sets fine, tho))
- FIXED: get thru api for now
-
- to relational model?
- HDACollection, meta_files, display_apps, etc.
-
-
- quota mgr
- show_deleted/hidden:
- use storage
- on/off ui
- move histview fadein/out in render to app?
- don't draw body until it's first expand event
- localize all
- break this file up
- ?: render url templates on init or render?
- ?: history, annotation won't accept unicode
-
- hierarchy:
- dataset -> hda
- history -> historyForEditing, historyForViewing
- display_structured?
-
- btw: get an error'd ds by running fastqc on fastq (when you don't have it installed)
-
- meta:
- css/html class/id 'item' -> hda
- add classes, ids on empty divs
- events (local/ui and otherwise)
- list in docs as well
- require.js
- convert function comments to jsDoc style, complete comments
- move inline styles into base.less
- watch the magic strings
- watch your globals
-
- features:
- lineage
- hide button
- show permissions in info
- show shared/sharing status on ds, history
- maintain scroll position on refresh (storage?)
- selection, multi-select (and actions common to selected (ugh))
- searching
- sorting, re-shuffling
-
-============================================================================= */
-var HistoryDatasetAssociation = BaseModel.extend( LoggableMixin ).extend({
- // a single HDA model
-
- // uncomment this out see log messages
- //logger : console,
-
- defaults : {
- // ---these are part of an HDA proper:
-
- // parent (containing) history
- history_id : null,
- // often used with tagging
- model_class : 'HistoryDatasetAssociation',
- // index within history (??)
- hid : 0,
-
- // ---whereas these are Dataset related/inherited
-
- id : null,
- name : '',
- // one of HistoryDatasetAssociation.STATES
- state : '',
- // sniffed datatype (sam, tabular, bed, etc.)
- data_type : null,
- // size in bytes
- file_size : 0,
-
- // array of associated file types (eg. [ 'bam_index', ... ])
- meta_files : [],
-
- misc_blurb : '',
- misc_info : '',
-
- deleted : false,
- purged : false,
- // aka. !hidden
- visible : false,
- // based on trans.user (is_admin or security_agent.can_access_dataset( <user_roles>, hda.dataset ))
- accessible : false,
-
- //TODO: this needs to be removed (it is a function of the view type (e.g. HDAForEditingView))
- for_editing : true
- },
-
- // fetch location of this history in the api
- url : function(){
- //TODO: get this via url router
- return 'api/histories/' + this.get( 'history_id' ) + '/contents/' + this.get( 'id' );
- },
-
- // (curr) only handles changing state of non-accessible hdas to STATES.NOT_VIEWABLE
- //TODO:? use initialize (or validate) to check purged AND deleted -> purged XOR deleted
- initialize : function(){
- this.log( this + '.initialize', this.attributes );
- this.log( '\tparent history_id: ' + this.get( 'history_id' ) );
-
- //!! this state is not in trans.app.model.Dataset.states - set it here -
- //TODO: change to server side.
- if( !this.get( 'accessible' ) ){
- this.set( 'state', HistoryDatasetAssociation.STATES.NOT_VIEWABLE );
- }
-
- // if the state has changed and the new state is a ready state, fire an event
- this.on( 'change:state', function( currModel, newState ){
- this.log( this + ' has changed state:', currModel, newState );
- if( this.inReadyState() ){
- this.trigger( 'state:ready', this.get( 'id' ), newState, this.previous( 'state' ), currModel );
- }
- });
-
- // debug on change events
- //this.on( 'change', function( currModel, changedList ){
- // this.log( this + ' has changed:', currModel, changedList );
- //});
- //this.bind( 'all', function( event ){
- // this.log( this + '', arguments );
- //});
- },
-
- isDeletedOrPurged : function(){
- return ( this.get( 'deleted' ) || this.get( 'purged' ) );
- },
-
- // based on show_deleted, show_hidden (gen. from the container control), would this ds show in the list of ds's?
- //TODO: too many visibles
- 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;
- },
-
- // 'ready' states are states where no processing (for the ds) is left to do on the server
- inReadyState : function(){
- var state = this.get( 'state' );
- return (
- ( state === HistoryDatasetAssociation.STATES.NEW )
- || ( state === HistoryDatasetAssociation.STATES.OK )
- || ( state === HistoryDatasetAssociation.STATES.EMPTY )
- || ( state === HistoryDatasetAssociation.STATES.FAILED_METADATA )
- || ( state === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
- || ( state === HistoryDatasetAssociation.STATES.DISCARDED )
- || ( state === HistoryDatasetAssociation.STATES.ERROR )
- );
- },
-
- // convenience fn to match hda.has_data
- hasData : function(){
- //TODO:?? is this equivalent to all possible hda.has_data calls?
- return ( this.get( 'file_size' ) > 0 );
- },
-
- toString : function(){
- var nameAndId = this.get( 'id' ) || '';
- if( this.get( 'name' ) ){
- nameAndId += ':"' + this.get( 'name' ) + '"';
- }
- return 'HistoryDatasetAssociation(' + nameAndId + ')';
- }
-});
-
-//------------------------------------------------------------------------------
-HistoryDatasetAssociation.STATES = {
- UPLOAD : 'upload',
- QUEUED : 'queued',
- RUNNING : 'running',
- SETTING_METADATA : 'setting_metadata',
-
- NEW : 'new',
- OK : 'ok',
- EMPTY : 'empty',
-
- FAILED_METADATA : 'failed_metadata',
- NOT_VIEWABLE : 'noPermission', // not in trans.app.model.Dataset.states
- DISCARDED : 'discarded',
- ERROR : 'error'
-};
-
-//==============================================================================
-var HDACollection = Backbone.Collection.extend( LoggableMixin ).extend({
- model : HistoryDatasetAssociation,
-
- //logger : console,
-
- initialize : function(){
- //this.bind( 'all', function( event ){
- // this.log( this + '', arguments );
- //});
- },
-
- // return the ids of every hda in this collection
- ids : function(){
- return this.map( function( item ){ return item.id; });
- },
-
- // return an HDA collection containing every 'shown' hda based on show_deleted/hidden
- getVisible : function( show_deleted, show_hidden ){
- return this.filter( function( item ){ return item.isVisible( show_deleted, show_hidden ); });
- },
-
- // get a map where <possible hda state> : [ <list of hda ids in that state> ]
- getStateLists : function(){
- var stateLists = {};
- _.each( _.values( HistoryDatasetAssociation.STATES ), function( state ){
- stateLists[ state ] = [];
- });
- //NOTE: will err on unknown state
- this.each( function( item ){
- stateLists[ item.get( 'state' ) ].push( item.get( 'id' ) );
- });
- return stateLists;
- },
-
- // returns the id of every hda still running (not in a ready state)
- running : function(){
- var idList = [];
- this.each( function( item ){
- if( !item.inReadyState() ){
- idList.push( item.get( 'id' ) );
- }
- });
- return idList;
- },
-
- // update (fetch -> render) the hdas with the ids given
- update : function( ids ){
- this.log( this + 'update:', ids );
-
- if( !( ids && ids.length ) ){ return; }
-
- var collection = this;
- _.each( ids, function( id, index ){
- var historyItem = collection.get( id );
- historyItem.fetch();
- });
- },
-
- toString : function(){
- return ( 'HDACollection(' + this.ids().join(',') + ')' );
- }
-});
-
-
-//==============================================================================
-var History = BaseModel.extend( LoggableMixin ).extend({
- //TODO: bind change events from items and collection to this (itemLengths, states)
-
- // uncomment this out see log messages
- //logger : console,
-
- // values from api (may need more)
- defaults : {
- id : '',
- name : '',
- state : '',
-
- //TODO: wire these to items (or this)
- show_deleted : false,
- show_hidden : false,
-
- diskSize : 0,
- deleted : false,
-
- tags : [],
- annotation : null,
-
- //TODO: quota msg and message? how to get those over the api?
- message : null,
- quotaMsg : false
- },
-
- url : function(){
- // api location of history resource
- //TODO: hardcoded
- return 'api/histories/' + this.get( 'id' );
- },
-
- initialize : function( initialSettings, initialHdas ){
- this.log( this + ".initialize:", initialSettings, initialHdas );
-
- this.hdas = new HDACollection();
-
- // if we've got hdas passed in the constructor, load them and set up updates if needed
- if( initialHdas && initialHdas.length ){
- this.hdas.reset( initialHdas );
- this.checkForUpdates();
- }
-
- //this.on( 'change', function( currModel, changedList ){
- // this.log( this + ' has changed:', currModel, changedList );
- //});
- //this.bind( 'all', function( event ){
- // this.log( this + '', arguments );
- //});
- },
-
- // get data via the api (alternative to sending options,hdas to initialize)
- loadFromApi : function( historyId, callback ){
- var history = this;
-
- // fetch the history AND the user (mainly to see if they're logged in at this point)
- history.attributes.id = historyId;
- //TODO:?? really? fetch user here?
- jQuery.when( jQuery.ajax( 'api/users/current' ), history.fetch()
-
- ).then( function( userResponse, historyResponse ){
- //console.warn( 'fetched user, history: ', userResponse, historyResponse );
- history.attributes.user = userResponse[0]; //? meh.
- history.log( history );
-
- }).then( function(){
- // ...then the hdas (using contents?ids=...)
- jQuery.ajax( history.url() + '/contents?' + jQuery.param({
- ids : history.itemIdsFromStateIds().join( ',' )
-
- // reset the collection to the hdas returned
- })).success( function( hdas ){
- //console.warn( 'fetched hdas' );
- history.hdas.reset( hdas );
- history.checkForUpdates();
- callback();
- });
- });
- },
-
- // reduce the state_ids map of hda id lists -> a single list of ids
- //...ugh - seems roundabout; necessary because the history doesn't have a straightforward list of ids
- // (and history_contents/index curr returns a summary only)
- hdaIdsFromStateIds : function(){
- return _.reduce( _.values( this.get( 'state_ids' ) ), function( reduction, currIdList ){
- return reduction.concat( currIdList );
- });
- },
-
- // get the history's state from it's cummulative ds states, delay + update if needed
- checkForUpdates : function( datasets ){
- // get overall History state from collection, run updater if History has running/queued hdas
- // boiling it down on the client to running/not
- if( this.hdas.running().length ){
- this.stateUpdater();
- }
- return this;
- },
-
- // update this history, find any hda's running/queued, update ONLY those that have changed states,
- // set up to run this again in some interval of time
- stateUpdater : function(){
- var history = this,
- oldState = this.get( 'state' ),
- // state ids is a map of every possible hda state, each containing a list of ids for hdas in that state
- oldStateIds = this.get( 'state_ids' );
-
- // pull from the history api
- //TODO: fetch?
- jQuery.ajax( 'api/histories/' + this.get( 'id' )
-
- ).success( function( response ){
- //this.log( 'historyApiRequest, response:', response );
- history.set( response );
- history.log( 'current history state:', history.get( 'state' ),
- '(was)', oldState,
- 'new size:', history.get( 'nice_size' ) );
-
- //TODO: revisit this - seems too elaborate, need something straightforward
- // for each state, check for the difference between old dataset states and new
- // the goal here is to check ONLY those datasets that have changed states (not all datasets)
- var changedIds = [];
- _.each( _.keys( response.state_ids ), function( state ){
- var diffIds = _.difference( response.state_ids[ state ], oldStateIds[ state ] );
- // aggregate those changed ids
- changedIds = changedIds.concat( diffIds );
- });
-
- // send the changed ids (if any) to dataset collection to have them fetch their own model changes
- if( changedIds.length ){
- history.hdas.update( changedIds );
- }
-
- // set up to keep pulling if this history in run/queue state
- //TODO: magic number here
- if( ( history.get( 'state' ) === HistoryDatasetAssociation.STATES.RUNNING )
- || ( history.get( 'state' ) === HistoryDatasetAssociation.STATES.QUEUED ) ){
- setTimeout( function(){
- history.stateUpdater();
- }, 4000 );
- }
-
- }).error( function( xhr, status, error ){
- if( console && console.warn ){
- console.warn( 'Error getting history updates from the server:', xhr, status, error );
- }
- alert( 'Error getting history updates from the server.\n' + error );
- });
- },
-
- toString : function(){
- var nameString = ( this.get( 'name' ) )?
- ( ',' + this.get( 'name' ) ) : ( '' );
- return 'History(' + this.get( 'id' ) + nameString + ')';
- }
-});
-
-//==============================================================================
-var HDAView = BaseView.extend( LoggableMixin ).extend({
- //??TODO: add alias in initialize this.hda = this.model?
- // view for HistoryDatasetAssociation model above
-
- // uncomment this out see log messages
- //logger : console,
-
- tagName : "div",
- className : "historyItemContainer",
-
- // ................................................................................ SET UP
- initialize : function( attributes ){
- this.log( this + '.initialize:', attributes );
-
- // render urlTemplates (gen. provided by GalaxyPaths) to urls
- if( !attributes.urlTemplates ){ throw( 'HDAView needs urlTemplates on initialize' ); }
- this.urls = this.renderUrls( attributes.urlTemplates, this.model.toJSON() );
-
- // whether the body of this hda is expanded (shown)
- this.expanded = attributes.expanded || false;
-
- // re-render the entire view on any model change
- this.model.bind( 'change', this.render, this );
- //this.bind( 'all', function( event ){
- // this.log( event );
- //}, this );
- },
-
- // urlTemplates is a map (or nested map) of underscore templates (currently, anyhoo)
- // use the templates to create the apropo urls for each action this ds could use
- renderUrls : function( urlTemplates, modelJson ){
- var hdaView = this,
- urls = {};
- _.each( urlTemplates, function( urlTemplateOrObj, urlKey ){
- // object == nested templates: recurse
- if( _.isObject( urlTemplateOrObj ) ){
- urls[ urlKey ] = hdaView.renderUrls( urlTemplateOrObj, modelJson );
-
- // string == template:
- } else {
- // meta_down load is a special case (see renderMetaDownloadUrls)
- //TODO: should be a better (gen.) way to handle this case
- if( urlKey === 'meta_download' ){
- urls[ urlKey ] = hdaView.renderMetaDownloadUrls( urlTemplateOrObj, modelJson );
- } else {
- urls[ urlKey ] = _.template( urlTemplateOrObj, modelJson );
- }
- }
- });
- return urls;
- },
-
- // there can be more than one meta_file to download, so return a list of url and file_type for each
- renderMetaDownloadUrls : function( urlTemplate, modelJson ){
- return _.map( modelJson.meta_files, function( meta_file ){
- return {
- url : _.template( urlTemplate, { id: modelJson.id, file_type: meta_file.file_type }),
- file_type : meta_file.file_type
- };
- });
- },
-
- // ................................................................................ RENDER MAIN
- render : function(){
- var view = this,
- id = this.model.get( 'id' ),
- state = this.model.get( 'state' ),
- itemWrapper = $( '<div/>' ).attr( 'id', 'historyItem-' + id ),
- initialRender = ( this.$el.children().size() === 0 );
-
- //console.debug( this + '.render, initial?:', initialRender );
-
- this._clearReferences();
- this.$el.attr( 'id', 'historyItemContainer-' + id );
-
- itemWrapper
- .addClass( 'historyItemWrapper' ).addClass( 'historyItem' )
- .addClass( 'historyItem-' + state );
-
- itemWrapper.append( this._render_warnings() );
- itemWrapper.append( this._render_titleBar() );
- this.body = $( this._render_body() );
- itemWrapper.append( this.body );
-
- //TODO: move to own function: setUpBehaviours
- // we can potentially skip this step and call popupmenu directly on the download button
- make_popup_menus( itemWrapper );
-
- // set up canned behavior on children (bootstrap, popupmenus, editable_text, etc.)
- itemWrapper.find( '.tooltip' ).tooltip({ placement : 'bottom' });
-
- // transition...
- this.$el.fadeOut( 'fast', function(){
- view.$el.children().remove();
- view.$el.append( itemWrapper ).fadeIn( 'fast', function(){
- view.log( view + ' rendered:', view.$el );
- var renderedEventName = 'rendered';
-
- if( initialRender ){
- renderedEventName += ':initial';
- } else if( view.model.inReadyState() ){
- renderedEventName += ':ready';
- }
- view.trigger( renderedEventName );
- });
- });
- return this;
- },
-
- //NOTE: button renderers have the side effect of caching their IconButtonViews to this view
- // clear out cached sub-views, dom refs, etc. from prev. render
- _clearReferences : function(){
- //??TODO: we should reset these in the button logic checks (i.e. if not needed this.button = null; return null)
- //?? do we really need these - not so far
- //TODO: at least move these to a map
- this.displayButton = null;
- this.editButton = null;
- this.deleteButton = null;
- this.errButton = null;
- this.showParamsButton = null;
- this.rerunButton = null;
- this.visualizationsButton = null;
- this.tagButton = null;
- this.annotateButton = null;
- },
-
- // ................................................................................ RENDER WARNINGS
- // hda warnings including: is deleted, is purged, is hidden (including links to further actions (undelete, etc.))
- _render_warnings : function(){
- // jQ errs on building dom with whitespace - if there are no messages, trim -> ''
- return $( jQuery.trim( HDAView.templates.messages(
- _.extend( this.model.toJSON(), { urls: this.urls } )
- )));
- },
-
- // ................................................................................ RENDER TITLEBAR
- // the part of an hda always shown (whether the body is expanded or not): title link, title buttons
- _render_titleBar : function(){
- var titleBar = $( '<div class="historyItemTitleBar" style="overflow: hidden"></div>' );
- titleBar.append( this._render_titleButtons() );
- titleBar.append( '<span class="state-icon"></span>' );
- titleBar.append( this._render_titleLink() );
- return titleBar;
- },
-
- // ................................................................................ display, edit attr, delete
- // icon-button group for the common, most easily accessed actions
- //NOTE: these are generally displayed for almost every hda state (tho poss. disabled)
- _render_titleButtons : function(){
- // render the display, edit attr and delete icon-buttons
- var buttonDiv = $( '<div class="historyItemButtons"></div>' );
- buttonDiv.append( this._render_displayButton() );
- buttonDiv.append( this._render_editButton() );
- buttonDiv.append( this._render_deleteButton() );
- return buttonDiv;
- },
-
- // icon-button to display this hda in the galaxy main iframe
- _render_displayButton : function(){
- // don't show display if not in ready state, error'd, or not accessible
- if( ( !this.model.inReadyState() )
- || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
- || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
- || ( !this.model.get( 'accessible' ) ) ){
- return null;
- }
-
- var displayBtnData = {
- icon_class : 'display'
- };
-
- // show a disabled display if the data's been purged
- if( this.model.get( 'purged' ) ){
- displayBtnData.enabled = false;
- displayBtnData.title = 'Cannot display datasets removed from disk';
-
- } else {
- displayBtnData.title = 'Display data in browser';
- displayBtnData.href = this.urls.display;
- }
-
- if( this.model.get( 'for_editing' ) ){
- displayBtnData.target = 'galaxy_main';
- }
-
- this.displayButton = new IconButtonView({ model : new IconButton( displayBtnData ) });
- return this.displayButton.render().$el;
- },
-
- // icon-button to edit the attributes (format, permissions, etc.) this hda
- _render_editButton : function(){
- // don't show edit while uploading, or if editable
- if( ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.UPLOAD )
- || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
- || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
- || ( !this.model.get( 'accessible' ) )
- || ( !this.model.get( 'for_editing' ) ) ){
- return null;
- }
-
- var purged = this.model.get( 'purged' ),
- deleted = this.model.get( 'deleted' ),
- editBtnData = {
- title : 'Edit attributes',
- href : this.urls.edit,
- target : 'galaxy_main',
- icon_class : 'edit'
- };
-
- // disable if purged or deleted and explain why in the tooltip
- //TODO: if for_editing
- if( deleted || purged ){
- editBtnData.enabled = false;
- if( purged ){
- editBtnData.title = 'Cannot edit attributes of datasets removed from disk';
- } else if( deleted ){
- editBtnData.title = 'Undelete dataset to edit attributes';
- }
- }
-
- this.editButton = new IconButtonView({ model : new IconButton( editBtnData ) });
- return this.editButton.render().$el;
- },
-
- // icon-button to delete this hda
- _render_deleteButton : function(){
- // don't show delete if...
- if( ( !this.model.get( 'for_editing' ) )
- || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
- || ( !this.model.get( 'accessible' ) ) ){
- return null;
- }
-
- var deleteBtnData = {
- title : 'Delete',
- href : this.urls[ 'delete' ],
- id : 'historyItemDeleter-' + this.model.get( 'id' ),
- icon_class : 'delete'
- };
- if( this.model.get( 'deleted' ) || this.model.get( 'purged' ) ){
- deleteBtnData = {
- title : 'Dataset is already deleted',
- icon_class : 'delete',
- enabled : false
- };
- }
- this.deleteButton = new IconButtonView({ model : new IconButton( deleteBtnData ) });
- return this.deleteButton.render().$el;
- },
-
- // ................................................................................ titleLink
- // render the hid and hda.name as a link (that will expand the body)
- _render_titleLink : function(){
- return $( jQuery.trim( HDAView.templates.titleLink(
- _.extend( this.model.toJSON(), { urls: this.urls } )
- )));
- },
-
- // ................................................................................ RENDER BODY
- // render the data/metadata summary (format, size, misc info, etc.)
- _render_hdaSummary : function(){
- var modelData = _.extend( this.model.toJSON(), { urls: this.urls } );
- // if there's no dbkey and it's editable : pass a flag to the template to render a link to editing in the '?'
- if( this.model.get( 'metadata_dbkey' ) === '?'
- && !this.model.isDeletedOrPurged() ){
- _.extend( modelData, { dbkey_unknown_and_editable : true });
- }
- return HDAView.templates.hdaSummary( modelData );
- },
-
- // ................................................................................ primary actions
- // render the icon-buttons gen. placed underneath the hda summary
- _render_primaryActionButtons : function( buttonRenderingFuncs ){
- var primaryActionButtons = $( '<div/>' ).attr( 'id', 'primary-actions-' + this.model.get( 'id' ) ),
- view = this;
- _.each( buttonRenderingFuncs, function( fn ){
- primaryActionButtons.append( fn.call( view ) );
- });
- return primaryActionButtons;
- },
-
- // icon-button/popupmenu to down the data (and/or the associated meta files (bai, etc.)) for this hda
- _render_downloadButton : function(){
- // don't show anything if the data's been purged
- if( this.model.get( 'purged' ) ){ return null; }
-
- // return either: a single download icon-button (if there are no meta files)
- // or a popupmenu with links to download assoc. meta files (if there are meta files)
- var downloadLinkHTML = HDAView.templates.downloadLinks(
- _.extend( this.model.toJSON(), { urls: this.urls } )
- );
- //this.log( this + '_render_downloadButton, downloadLinkHTML:', downloadLinkHTML );
- return $( downloadLinkHTML );
- },
-
- // icon-button to show the input and output (stdout/err) for the job that created this hda
- _render_errButton : function(){
- if( ( this.model.get( 'state' ) !== HistoryDatasetAssociation.STATES.ERROR )
- || ( !this.model.get( 'for_editing' ) ) ){ return null; }
-
- this.errButton = new IconButtonView({ model : new IconButton({
- title : 'View or report this error',
- href : this.urls.report_error,
- target : 'galaxy_main',
- icon_class : 'bug'
- })});
- return this.errButton.render().$el;
- },
-
- // icon-button to show the input and output (stdout/err) for the job that created this hda
- _render_showParamsButton : function(){
- // gen. safe to show in all cases
- this.showParamsButton = new IconButtonView({ model : new IconButton({
- title : 'View details',
- href : this.urls.show_params,
- target : 'galaxy_main',
- icon_class : 'information'
- }) });
- return this.showParamsButton.render().$el;
- },
-
- // icon-button to re run the job that created this hda
- _render_rerunButton : function(){
- if( !this.model.get( 'for_editing' ) ){ return null; }
- this.rerunButton = new IconButtonView({ model : new IconButton({
- title : 'Run this job again',
- href : this.urls.rerun,
- target : 'galaxy_main',
- icon_class : 'arrow-circle'
- }) });
- return this.rerunButton.render().$el;
- },
-
- // build an icon-button or popupmenu based on the number of applicable visualizations
- // also map button/popup clicks to viz setup functions
- _render_visualizationsButton : function(){
- var dbkey = this.model.get( 'dbkey' ),
- visualizations = this.model.get( 'visualizations' ),
- visualization_url = this.urls.visualization,
- popup_menu_dict = {},
- params = {
- dataset_id: this.model.get( 'id' ),
- hda_ldda: 'hda'
- };
-
- if( !( this.model.hasData() )
- || !( this.model.get( 'for_editing' ) )
- || !( visualizations && visualizations.length )
- || !( visualization_url ) ){
- //console.warn( 'NOT rendering visualization icon' )
- return null;
- }
-
- // render the icon from template
- this.visualizationsButton = new IconButtonView({ model : new IconButton({
- title : 'Visualize',
- href : visualization_url,
- icon_class : 'chart_curve'
- })});
- var $icon = this.visualizationsButton.render().$el;
- $icon.addClass( 'visualize-icon' ); // needed?
-
- //TODO: make this more concise
- // map a function to each visualization in the icon's attributes
- // create a popupmenu from that map
- // Add dbkey to params if it exists.
- if( dbkey ){ params.dbkey = dbkey; }
-
- function create_viz_action( visualization ) {
- switch( visualization ){
- case 'trackster':
- return create_trackster_action_fn( visualization_url, params, dbkey );
- case 'scatterplot':
- return create_scatterplot_action_fn( visualization_url, params );
- default:
- return function(){
- window.parent.location = visualization_url + '/' + visualization + '?' + $.param( params ); };
- }
- }
-
- // No need for popup menu because there's a single visualization.
- if ( visualizations.length === 1 ) {
- $icon.attr( 'title', visualizations[0] );
- $icon.click( create_viz_action( visualizations[0] ) );
-
- // >1: Populate menu dict with visualization fns, make the popupmenu
- } else {
- _.each( visualizations, function( visualization ) {
- //TODO: move to utils
- var titleCaseVisualization = visualization.charAt( 0 ).toUpperCase() + visualization.slice( 1 );
- popup_menu_dict[ titleCaseVisualization ] = create_viz_action( visualization );
- });
- make_popupmenu( $icon, popup_menu_dict );
- }
- return $icon;
- },
-
- // ................................................................................ secondary actions
- // secondary actions: currently tagging and annotation (if user is allowed)
- _render_secondaryActionButtons : function( buttonRenderingFuncs ){
- // move to the right (same level as primary)
- var secondaryActionButtons = $( '<div/>' ),
- view = this;
- secondaryActionButtons
- .attr( 'style', 'float: right;' )
- .attr( 'id', 'secondary-actions-' + this.model.get( 'id' ) );
-
- _.each( buttonRenderingFuncs, function( fn ){
- secondaryActionButtons.append( fn.call( view ) );
- });
- return secondaryActionButtons;
- },
-
- // icon-button to load and display tagging html
- //TODO: these should be a sub-MV
- _render_tagButton : function(){
- if( !( this.model.hasData() )
- || !( this.model.get( 'for_editing' ) )
- || ( !this.urls.tags.get ) ){ return null; }
-
- this.tagButton = new IconButtonView({ model : new IconButton({
- title : 'Edit dataset tags',
- target : 'galaxy_main',
- href : this.urls.tags.get,
- icon_class : 'tags'
- })});
- return this.tagButton.render().$el;
- },
-
- // icon-button to load and display annotation html
- //TODO: these should be a sub-MV
- _render_annotateButton : function(){
- if( !( this.model.hasData() )
- || !( this.model.get( 'for_editing' ) )
- || ( !this.urls.annotation.get ) ){ return null; }
-
- this.annotateButton = new IconButtonView({ model : new IconButton({
- title : 'Edit dataset annotation',
- target : 'galaxy_main',
- icon_class : 'annotate'
- })});
- return this.annotateButton.render().$el;
- },
-
- // ................................................................................ other elements
- // render links to external genome display applications (igb, gbrowse, etc.)
- //TODO: not a fan of the style on these
- _render_displayApps : function(){
- if( !this.model.hasData() ){ return null; }
-
- var displayAppsDiv = $( '<div/>' ).addClass( 'display-apps' );
- if( !_.isEmpty( this.model.get( 'display_types' ) ) ){
- //this.log( this + 'display_types:', this.model.get( 'urls' ).display_types );
- //TODO:?? does this ever get used?
- displayAppsDiv.append(
- HDAView.templates.displayApps({ displayApps : this.model.get( 'display_types' ) })
- );
- }
- if( !_.isEmpty( this.model.get( 'display_apps' ) ) ){
- //this.log( this + 'display_apps:', this.model.get( 'urls' ).display_apps );
- displayAppsDiv.append(
- HDAView.templates.displayApps({ displayApps : this.model.get( 'display_apps' ) })
- );
- }
- return displayAppsDiv;
- },
-
- //TODO: into sub-MV
- // render the area used to load tag display
- _render_tagArea : function(){
- if( !this.urls.tags.set ){ return null; }
- //TODO: move to mvc/tags.js
- return $( HDAView.templates.tagArea(
- _.extend( this.model.toJSON(), { urls: this.urls } )
- ));
- },
-
- //TODO: into sub-MV
- // render the area used to load annotation display
- _render_annotationArea : function(){
- if( !this.urls.annotation.get ){ return null; }
- //TODO: move to mvc/annotations.js
- return $( HDAView.templates.annotationArea(
- _.extend( this.model.toJSON(), { urls: this.urls } )
- ));
- },
-
- // render the data peek
- //TODO: curr. pre-formatted into table on the server side - may not be ideal/flexible
- _render_peek : function(){
- if( !this.model.get( 'peek' ) ){ return null; }
- return $( '<div/>' ).append(
- $( '<pre/>' )
- .attr( 'id', 'peek' + this.model.get( 'id' ) )
- .addClass( 'peek' )
- .append( this.model.get( 'peek' ) )
- );
- },
-
- // ................................................................................ state body renderers
- // _render_body fns for the various states
- //TODO: only render these on expansion (or already expanded)
- _render_body_not_viewable : function( parent ){
- //TODO: revisit - still showing display, edit, delete (as common) - that CAN'T be right
- parent.append( $( '<div>You do not have permission to view dataset.</div>' ) );
- },
-
- _render_body_uploading : function( parent ){
- parent.append( $( '<div>Dataset is uploading</div>' ) );
- },
-
- _render_body_queued : function( parent ){
- parent.append( $( '<div>Job is waiting to run.</div>' ) );
- parent.append( this._render_primaryActionButtons([
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- },
-
- _render_body_running : function( parent ){
- parent.append( '<div>Job is currently running.</div>' );
- parent.append( this._render_primaryActionButtons([
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- },
-
- _render_body_error : function( parent ){
- if( !this.model.get( 'purged' ) ){
- parent.append( $( '<div>' + this.model.get( 'misc_blurb' ) + '</div>' ) );
- }
- parent.append( ( 'An error occurred running this job: '
- + '<i>' + $.trim( this.model.get( 'misc_info' ) ) + '</i>' ) );
- parent.append( this._render_primaryActionButtons([
- this._render_downloadButton,
- this._render_errButton,
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- },
-
- _render_body_discarded : function( parent ){
- parent.append( '<div>The job creating this dataset was cancelled before completion.</div>' );
- parent.append( this._render_primaryActionButtons([
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- },
-
- _render_body_setting_metadata : function( parent ){
- parent.append( $( '<div>Metadata is being auto-detected.</div>' ) );
- },
-
- _render_body_empty : function( parent ){
- //TODO: replace i with dataset-misc-info class
- //?? why are we showing the file size when we know it's zero??
- parent.append( $( '<div>No data: <i>' + this.model.get( 'misc_blurb' ) + '</i></div>' ) );
- parent.append( this._render_primaryActionButtons([
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- },
-
- _render_body_failed_metadata : function( parent ){
- //TODO: the css for this box is broken (unlike the others)
- // add a message box about the failure at the top of the body...
- parent.append( $( HDAView.templates.failedMetadata( this.model.toJSON() ) ) );
- //...then render the remaining body as STATES.OK (only diff between these states is the box above)
- this._render_body_ok( parent );
- },
-
- _render_body_ok : function( parent ){
- // most common state renderer and the most complicated
- parent.append( this._render_hdaSummary() );
-
- // return shortened form if del'd
- //TODO: is this correct? maybe only on purged
- if( this.model.isDeletedOrPurged() ){
- parent.append( this._render_primaryActionButtons([
- this._render_downloadButton,
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- return;
- }
-
- //NOTE: change the order here
- parent.append( this._render_primaryActionButtons([
- this._render_downloadButton,
- this._render_errButton,
- this._render_showParamsButton,
- this._render_rerunButton,
- this._render_visualizationsButton
- ]));
- parent.append( this._render_secondaryActionButtons([
- this._render_tagButton,
- this._render_annotateButton
- ]));
- parent.append( '<div class="clear"/>' );
-
- parent.append( this._render_tagArea() );
- parent.append( this._render_annotationArea() );
-
- parent.append( this._render_displayApps() );
- parent.append( this._render_peek() );
-
- //TODO??: still needed?
- //// If Mozilla, hide scrollbars in hidden items since they cause animation bugs
- //if ( $.browser.mozilla ) {
- // $( "div.historyItemBody" ).each( function() {
- // if ( !$(this).is(":visible") ) { $(this).find( "pre.peek" ).css( "overflow", "hidden" ); }
- // });
- //}
- },
-
- _render_body : function(){
- //this.log( this + '_render_body' );
- //this.log( 'state:', state, 'for_editing', for_editing );
-
- //TODO: incorrect id (encoded - use hid?)
- var body = $( '<div/>' )
- .attr( 'id', 'info-' + this.model.get( 'id' ) )
- .addClass( 'historyItemBody' )
- .attr( 'style', 'display: block' );
-
- //TODO: not a fan of this dispatch
- switch( this.model.get( 'state' ) ){
- case HistoryDatasetAssociation.STATES.NOT_VIEWABLE :
- this._render_body_not_viewable( body );
- break;
- case HistoryDatasetAssociation.STATES.UPLOAD :
- this._render_body_uploading( body );
- break;
- case HistoryDatasetAssociation.STATES.QUEUED :
- this._render_body_queued( body );
- break;
- case HistoryDatasetAssociation.STATES.RUNNING :
- this._render_body_running( body );
- break;
- case HistoryDatasetAssociation.STATES.ERROR :
- this._render_body_error( body );
- break;
- case HistoryDatasetAssociation.STATES.DISCARDED :
- this._render_body_discarded( body );
- break;
- case HistoryDatasetAssociation.STATES.SETTING_METADATA :
- this._render_body_setting_metadata( body );
- break;
- case HistoryDatasetAssociation.STATES.EMPTY :
- this._render_body_empty( body );
- break;
- case HistoryDatasetAssociation.STATES.FAILED_METADATA :
- this._render_body_failed_metadata( body );
- break;
- case HistoryDatasetAssociation.STATES.OK :
- this._render_body_ok( body );
- break;
- default:
- //??: no body?
- body.append( $( '<div>Error: unknown dataset state "' + state + '".</div>' ) );
- }
- body.append( '<div style="clear: both"></div>' );
-
- if( this.expanded ){
- body.show();
- } else {
- body.hide();
- }
- return body;
- },
-
- // ................................................................................ EVENTS
- events : {
- 'click .historyItemTitle' : 'toggleBodyVisibility',
- 'click a.icon-button.tags' : 'loadAndDisplayTags',
- 'click a.icon-button.annotate' : 'loadAndDisplayAnnotation'
- },
-
- // ................................................................................ STATE CHANGES / MANIPULATION
- // find the tag area and, if initial: (via ajax) load the html for displaying them; otherwise, unhide/hide
- //TODO: into sub-MV
- loadAndDisplayTags : function( event ){
- //BUG: broken with latest
- //TODO: this is a drop in from history.mako - should use MV as well
- this.log( this + '.loadAndDisplayTags', event );
- var tagArea = this.$el.find( '.tag-area' ),
- tagElt = tagArea.find( '.tag-elt' );
-
- // Show or hide tag area; if showing tag area and it's empty, fill it.
- if( tagArea.is( ":hidden" ) ){
- if( !jQuery.trim( tagElt.html() ) ){
- // Need to fill tag element.
- $.ajax({
- //TODO: the html from this breaks a couple of times
- url: this.urls.tags.get,
- error: function() { alert( "Tagging failed" ); },
- success: function(tag_elt_html) {
- tagElt.html(tag_elt_html);
- tagElt.find(".tooltip").tooltip();
- tagArea.slideDown("fast");
- }
- });
- } else {
- // Tag element is filled; show.
- tagArea.slideDown("fast");
- }
- } else {
- // Hide.
- tagArea.slideUp("fast");
- }
- return false;
- },
-
- // find the annotation area and, if initial: (via ajax) load the html for displaying it; otherwise, unhide/hide
- //TODO: into sub-MV
- loadAndDisplayAnnotation : function( event ){
- //TODO: this is a drop in from history.mako - should use MV as well
- this.log( this + '.loadAndDisplayAnnotation', event );
- var annotationArea = this.$el.find( '.annotation-area' ),
- annotationElem = annotationArea.find( '.annotation-elt' ),
- setAnnotationUrl = this.urls.annotation.set;
-
- // Show or hide annotation area; if showing annotation area and it's empty, fill it.
- if ( annotationArea.is( ":hidden" ) ){
- if( !jQuery.trim( annotationElem.html() ) ){
- // Need to fill annotation element.
- $.ajax({
- url: this.urls.annotation.get,
- error: function(){ alert( "Annotations failed" ); },
- success: function( htmlFromAjax ){
- if( htmlFromAjax === "" ){
- htmlFromAjax = "<em>Describe or add notes to dataset</em>";
- }
- annotationElem.html( htmlFromAjax );
- annotationArea.find(".tooltip").tooltip();
-
- async_save_text(
- annotationElem.attr("id"), annotationElem.attr("id"),
- setAnnotationUrl,
- "new_annotation", 18, true, 4
- );
- annotationArea.slideDown("fast");
- }
- });
- } else {
- annotationArea.slideDown("fast");
- }
-
- } else {
- // Hide.
- annotationArea.slideUp("fast");
- }
- return false;
- },
-
- // expand/collapse body
- //side effect: trigger event
- toggleBodyVisibility : function( event, expanded ){
- var $body = this.$el.find( '.historyItemBody' );
- expanded = ( expanded === undefined )?( !$body.is( ':visible' ) ):( expanded );
- //this.log( 'toggleBodyVisibility, expanded:', expanded, '$body:', $body );
-
- if( expanded ){
- $body.slideDown( 'fast' );
- } else {
- $body.slideUp( 'fast' );
- }
- this.trigger( 'toggleBodyVisibility', this.model.get( 'id' ), expanded );
- },
-
- // ................................................................................ UTILTIY
- toString : function(){
- var modelString = ( this.model )?( this.model + '' ):( '(no model)' );
- return 'HDAView(' + modelString + ')';
- }
-});
-
-//------------------------------------------------------------------------------
-HDAView.templates = {
- warningMsg : Handlebars.templates[ 'template-warningmessagesmall' ],
-
- messages : Handlebars.templates[ 'template-history-warning-messages' ],
- titleLink : Handlebars.templates[ 'template-history-titleLink' ],
- hdaSummary : Handlebars.templates[ 'template-history-hdaSummary' ],
- downloadLinks : Handlebars.templates[ 'template-history-downloadLinks' ],
- failedMetadata : Handlebars.templates[ 'template-history-failedMetaData' ],
- tagArea : Handlebars.templates[ 'template-history-tagArea' ],
- annotationArea : Handlebars.templates[ 'template-history-annotationArea' ],
- displayApps : Handlebars.templates[ 'template-history-displayApps' ]
-};
-
-//==============================================================================
-//TODO: these belong somewhere else
-
-//TODO: should be imported from scatterplot.js
-//TODO: OR abstracted to 'load this in the galaxy_main frame'
-function create_scatterplot_action_fn( url, params ){
- action = function() {
- var galaxy_main = $( window.parent.document ).find( 'iframe#galaxy_main' ),
- final_url = url + '/scatterplot?' + $.param(params);
- galaxy_main.attr( 'src', final_url );
- //TODO: this needs to go away
- $( 'div.popmenu-wrapper' ).remove();
- return false;
- };
- return action;
-}
-
-// -----------------------------------------------------------------------------
-// Create trackster action function.
-//TODO: should be imported from trackster.js
-function create_trackster_action_fn(vis_url, dataset_params, dbkey) {
- return function() {
- var params = {};
- if (dbkey) { params.dbkey = dbkey; }
- $.ajax({
- url: vis_url + '/list_tracks?f-' + $.param(params),
- dataType: "html",
- error: function() { alert( "Could not add this dataset to browser." ); },
- success: function(table_html) {
- var parent = window.parent;
-
- parent.show_modal("View Data in a New or Saved Visualization", "", {
- "Cancel": function() {
- parent.hide_modal();
- },
- "View in saved visualization": function() {
- // Show new modal with saved visualizations.
- parent.show_modal("Add Data to Saved Visualization", table_html, {
- "Cancel": function() {
- parent.hide_modal();
- },
- "Add to visualization": function() {
- $(parent.document).find('input[name=id]:checked').each(function() {
- var vis_id = $(this).val();
- dataset_params.id = vis_id;
- parent.location = vis_url + "/trackster?" + $.param(dataset_params);
- });
- }
- });
- },
- "View in new visualization": function() {
- parent.location = vis_url + "/trackster?" + $.param(dataset_params);
- }
- });
- }
- });
- return false;
- };
-}
-
-
-//==============================================================================
-// view for the HDACollection (as per current right hand panel)
-var HistoryView = BaseView.extend( LoggableMixin ).extend({
-
- // uncomment this out see log messages
- //logger : console,
-
- // direct attachment to existing element
- el : 'body.historyPage',
-
- // init with the model, urlTemplates, set up storage, bind HDACollection events
- //NOTE: this will create or load PersistantStorage keyed under 'HistoryView.<id>'
- //pre: you'll need to pass in the urlTemplates (urlTemplates : { history : {...}, hda : {...} })
- initialize : function( attributes ){
- this.log( this + '.initialize:', attributes );
-
- // set up url templates
- //TODO: prob. better to put this in class scope (as the handlebars templates), but...
- // they're added to GalaxyPaths on page load (after this file is loaded)
- if( !attributes.urlTemplates ){ throw( 'HDAView needs urlTemplates on initialize' ); }
- if( !attributes.urlTemplates.history ){ throw( 'HDAView needs urlTemplates.history on initialize' ); }
- if( !attributes.urlTemplates.hda ){ throw( 'HDAView needs urlTemplates.hda on initialize' ); }
- this.urlTemplates = attributes.urlTemplates.history;
- this.hdaUrlTemplates = attributes.urlTemplates.hda;
-
- // data that needs to be persistant over page refreshes
- // (note the key function which uses the history id as well)
- this.storage = new PersistantStorage(
- 'HistoryView.' + this.model.get( 'id' ),
- { expandedHdas : {} }
- );
-
- // bind events from the model's hda collection
- //this.model.bind( 'change', this.render, this );
- this.model.bind( 'change:nice_size', this.updateHistoryDiskSize, this );
-
- this.model.hdas.bind( 'add', this.add, this );
- this.model.hdas.bind( 'reset', this.addAll, this );
- this.model.hdas.bind( 'all', this.all, this );
-
- //this.bind( 'all', function(){
- // this.log( arguments );
- //}, this );
-
- // set up instance vars
- this.hdaViews = {};
- this.urls = {};
- },
-
- add : function( hda ){
- //console.debug( 'add.' + this, hda );
- //TODO
- },
-
- addAll : function(){
- //console.debug( 'addAll.' + this );
- // re render when all hdas are reset
- this.render();
- },
-
- all : function( event ){
- //console.debug( 'allItemEvents.' + this, event );
- //...for which to do the debuggings
- },
-
- // render the urls for this view using urlTemplates and the model data
- renderUrls : function( modelJson ){
- var historyView = this;
-
- historyView.urls = {};
- _.each( this.urlTemplates, function( urlTemplate, urlKey ){
- historyView.urls[ urlKey ] = _.template( urlTemplate, modelJson );
- });
- return historyView.urls;
- },
-
- // render urls, historyView body, and hdas (if any are shown), fade out, swap, fade in, set up behaviours
- render : function(){
- var historyView = this,
- setUpQueueName = historyView.toString() + '.set-up',
- newRender = $( '<div/>' ),
- modelJson = this.model.toJSON(),
- initialRender = ( this.$el.children().size() === 0 );
-
- //console.debug( this + '.render, initialRender:', initialRender );
-
- // render the urls and add them to the model json
- modelJson.urls = this.renderUrls( modelJson );
-
- // render the main template, tooltips
- //NOTE: this is done before the items, since item views should handle theirs themselves
- newRender.append( HistoryView.templates.historyPanel( modelJson ) );
- newRender.find( '.tooltip' ).tooltip();
-
- // render hda views (if any and any shown (show_deleted/hidden)
- if( !this.model.hdas.length
- || !this.renderItems( newRender.find( '#' + this.model.get( 'id' ) + '-datasets' ) ) ){
- // if history is empty or no hdas would be rendered, show the empty message
- newRender.find( '#emptyHistoryMessage' ).show();
- }
-
- // fade out existing, swap with the new, fade in, set up behaviours
- $( historyView ).queue( setUpQueueName, function( next ){
- historyView.$el.fadeOut( 'fast', function(){ next(); });
- });
- $( historyView ).queue( setUpQueueName, function( next ){
- // swap over from temp div newRender
- historyView.$el.html( '' );
- historyView.$el.append( newRender.children() );
-
- historyView.$el.fadeIn( 'fast', function(){ next(); });
- });
- $( historyView ).queue( setUpQueueName, function( next ){
- this.log( historyView + ' rendered:', historyView.$el );
-
- //TODO: ideally, these would be set up before the fade in (can't because of async save text)
- historyView.setUpBehaviours();
-
- if( initialRender ){
- historyView.trigger( 'rendered:initial' );
-
- } else {
- historyView.trigger( 'rendered' );
- }
- next();
- });
- $( historyView ).dequeue( setUpQueueName );
- return this;
- },
-
- // set up a view for each item to be shown, init with model and listeners, cache to map ( model.id : view )
- renderItems : function( $whereTo ){
- this.hdaViews = {};
- var historyView = this,
- show_deleted = this.model.get( 'show_deleted' ),
- show_hidden = this.model.get( 'show_hidden' ),
- visibleHdas = this.model.hdas.getVisible( show_deleted, show_hidden );
-
- // only render the shown hdas
- _.each( visibleHdas, function( hda ){
- var hdaId = hda.get( 'id' ),
- expanded = historyView.storage.get( 'expandedHdas' ).get( hdaId );
- historyView.hdaViews[ hdaId ] = new HDAView({
- model : hda,
- expanded : expanded,
- urlTemplates : historyView.hdaUrlTemplates
- });
- historyView.setUpHdaListeners( historyView.hdaViews[ hdaId ] );
- // render it (NOTE: reverse order, newest on top (prepend))
- //TODO: by default send a reverse order list (although this may be more efficient - it's more confusing)
- $whereTo.prepend( historyView.hdaViews[ hdaId ].render().$el );
- });
- return visibleHdas.length;
- },
-
- // set up HistoryView->HDAView listeners
- setUpHdaListeners : function( hdaView ){
- var historyView = this;
-
- // use storage to maintain a list of hdas whose bodies are expanded
- hdaView.bind( 'toggleBodyVisibility', function( id, visible ){
- if( visible ){
- historyView.storage.get( 'expandedHdas' ).set( id, true );
- } else {
- historyView.storage.get( 'expandedHdas' ).deleteKey( id );
- }
- });
-
- // rendering listeners
- hdaView.bind( 'rendered:ready', function(){ historyView.trigger( 'hda:rendered:ready' ); });
- },
-
- // set up js/widget behaviours: tooltips,
- //TODO: these should be either sub-MVs, or handled by events
- setUpBehaviours : function(){
- // anon users shouldn't have access to any of these
- if( !( this.model.get( 'user' ) && this.model.get( 'user' ).email ) ){ return; }
-
- // annotation slide down
- var historyAnnotationArea = this.$( '#history-annotation-area' );
- this.$( '#history-annotate' ).click( function() {
- if ( historyAnnotationArea.is( ":hidden" ) ) {
- historyAnnotationArea.slideDown( "fast" );
- } else {
- historyAnnotationArea.slideUp( "fast" );
- }
- return false;
- });
-
- // title and annotation editable text
- //NOTE: these use page scoped selectors - so these need to be in the page DOM before they're applicable
- async_save_text( "history-name-container", "history-name",
- this.urls.rename, "new_name", 18 );
-
- async_save_text( "history-annotation-container", "history-annotation",
- this.urls.annotate, "new_annotation", 18, true, 4 );
- },
-
- // update the history size display (curr. upper right of panel)
- updateHistoryDiskSize : function(){
- this.$el.find( '#history-size' ).text( this.model.get( 'nice_size' ) );
- },
-
- //TODO: this seems more like a per user message than a history message; IOW, this doesn't belong here
- showQuotaMessage : function( userData ){
- var msg = this.$el.find( '#quota-message-container' );
- //this.log( this + ' showing quota message:', msg, userData );
- if( msg.is( ':hidden' ) ){ msg.slideDown( 'fast' ); }
- },
-
- //TODO: this seems more like a per user message than a history message
- hideQuotaMessage : function( userData ){
- var msg = this.$el.find( '#quota-message-container' );
- //this.log( this + ' hiding quota message:', msg, userData );
- if( !msg.is( ':hidden' ) ){ msg.slideUp( 'fast' ); }
- },
-
- events : {
- 'click #history-collapse-all' : 'hideAllHdaBodies',
- 'click #history-tag' : 'loadAndDisplayTags'
- },
-
- // collapse all hda bodies
- hideAllHdaBodies : function(){
- _.each( this.hdaViews, function( item ){
- item.toggleBodyVisibility( null, false );
- });
- this.storage.set( 'expandedHdas', {} );
- },
-
- // find the tag area and, if initial: (via ajax) load the html for displaying them; otherwise, unhide/hide
- //TODO: into sub-MV
- loadAndDisplayTags : function( event ){
- this.log( this + '.loadAndDisplayTags', event );
- var tagArea = this.$el.find( '#history-tag-area' ),
- tagElt = tagArea.find( '.tag-elt' );
- this.log( '\t tagArea', tagArea, ' tagElt', tagElt );
-
- // Show or hide tag area; if showing tag area and it's empty, fill it
- if( tagArea.is( ":hidden" ) ){
- if( !jQuery.trim( tagElt.html() ) ){
- var view = this;
- // Need to fill tag element.
- $.ajax({
- //TODO: the html from this breaks a couple of times
- url: view.urls.tag,
- error: function() { alert( "Tagging failed" ); },
- success: function(tag_elt_html) {
- //view.log( view + ' tag elt html (ajax)', tag_elt_html );
- tagElt.html(tag_elt_html);
- tagElt.find(".tooltip").tooltip();
- tagArea.slideDown("fast");
- }
- });
- } else {
- // Tag element already filled: show
- tagArea.slideDown("fast");
- }
-
- } else {
- // Currently shown: Hide
- tagArea.slideUp("fast");
- }
- return false;
- },
-
- toString : function(){
- var nameString = this.model.get( 'name' ) || '';
- return 'HistoryView(' + nameString + ')';
- }
-});
-HistoryView.templates = {
- historyPanel : Handlebars.templates[ 'template-history-historyPanel' ]
-};
-
-//==============================================================================
-//return {
-// HistoryItem : HistoryItem,
-// HDAView : HDAView,
-// HistoryCollection : HistoryCollection,
-// History : History,
-// HistoryView : HistoryView
-//};});
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 static/scripts/mvc/history/history-model.js
--- /dev/null
+++ b/static/scripts/mvc/history/history-model.js
@@ -0,0 +1,180 @@
+//define([
+// "../mvc/base-mvc"
+//], function(){
+//==============================================================================
+/**
+ *
+ */
+var History = BaseModel.extend( LoggableMixin ).extend({
+ //TODO: bind change events from items and collection to this (itemLengths, states)
+
+ // uncomment this out see log messages
+ //logger : console,
+
+ // values from api (may need more)
+ defaults : {
+ id : '',
+ name : '',
+ state : '',
+
+ //TODO: wire these to items (or this)
+ show_deleted : false,
+ show_hidden : false,
+
+ diskSize : 0,
+ deleted : false,
+
+ tags : [],
+ annotation : null,
+
+ //TODO: quota msg and message? how to get those over the api?
+ message : null,
+ quotaMsg : false
+ },
+
+ url : function(){
+ // api location of history resource
+ //TODO: hardcoded
+ return 'api/histories/' + this.get( 'id' );
+ },
+
+ initialize : function( initialSettings, initialHdas ){
+ this.log( this + ".initialize:", initialSettings, initialHdas );
+
+ this.hdas = new HDACollection();
+
+ // if we've got hdas passed in the constructor, load them and set up updates if needed
+ if( initialHdas && initialHdas.length ){
+ this.hdas.reset( initialHdas );
+ this.checkForUpdates();
+ }
+
+ //this.on( 'change', function( currModel, changedList ){
+ // this.log( this + ' has changed:', currModel, changedList );
+ //});
+ //this.bind( 'all', function( event ){
+ // this.log( this + '', arguments );
+ //});
+ },
+
+ // get data via the api (alternative to sending options,hdas to initialize)
+ loadFromApi : function( historyId, callback ){
+ var history = this;
+
+ // fetch the history AND the user (mainly to see if they're logged in at this point)
+ history.attributes.id = historyId;
+ //TODO:?? really? fetch user here?
+ jQuery.when( jQuery.ajax( 'api/users/current' ), history.fetch()
+
+ ).then( function( userResponse, historyResponse ){
+ //console.warn( 'fetched user, history: ', userResponse, historyResponse );
+ history.attributes.user = userResponse[0]; //? meh.
+ history.log( history );
+
+ }).then( function(){
+ // ...then the hdas (using contents?ids=...)
+ jQuery.ajax( history.url() + '/contents?' + jQuery.param({
+ ids : history.itemIdsFromStateIds().join( ',' )
+
+ // reset the collection to the hdas returned
+ })).success( function( hdas ){
+ //console.warn( 'fetched hdas' );
+ history.hdas.reset( hdas );
+ history.checkForUpdates();
+ callback();
+ });
+ });
+ },
+
+ // reduce the state_ids map of hda id lists -> a single list of ids
+ //...ugh - seems roundabout; necessary because the history doesn't have a straightforward list of ids
+ // (and history_contents/index curr returns a summary only)
+ hdaIdsFromStateIds : function(){
+ return _.reduce( _.values( this.get( 'state_ids' ) ), function( reduction, currIdList ){
+ return reduction.concat( currIdList );
+ });
+ },
+
+ // get the history's state from it's cummulative ds states, delay + update if needed
+ checkForUpdates : function( datasets ){
+ // get overall History state from collection, run updater if History has running/queued hdas
+ // boiling it down on the client to running/not
+ if( this.hdas.running().length ){
+ this.stateUpdater();
+ }
+ return this;
+ },
+
+ // update this history, find any hda's running/queued, update ONLY those that have changed states,
+ // set up to run this again in some interval of time
+ stateUpdater : function(){
+ var history = this,
+ oldState = this.get( 'state' ),
+ // state ids is a map of every possible hda state, each containing a list of ids for hdas in that state
+ oldStateIds = this.get( 'state_ids' );
+
+ // pull from the history api
+ //TODO: fetch?
+ jQuery.ajax( 'api/histories/' + this.get( 'id' )
+
+ ).success( function( response ){
+ //this.log( 'historyApiRequest, response:', response );
+ history.set( response );
+ history.log( 'current history state:', history.get( 'state' ),
+ '(was)', oldState,
+ 'new size:', history.get( 'nice_size' ) );
+
+ //TODO: revisit this - seems too elaborate, need something straightforward
+ // for each state, check for the difference between old dataset states and new
+ // the goal here is to check ONLY those datasets that have changed states (not all datasets)
+ var changedIds = [];
+ _.each( _.keys( response.state_ids ), function( state ){
+ var diffIds = _.difference( response.state_ids[ state ], oldStateIds[ state ] );
+ // aggregate those changed ids
+ changedIds = changedIds.concat( diffIds );
+ });
+
+ // send the changed ids (if any) to dataset collection to have them fetch their own model changes
+ if( changedIds.length ){
+ history.hdas.update( changedIds );
+ }
+
+ // set up to keep pulling if this history in run/queue state
+ //TODO: magic number here
+ if( ( history.get( 'state' ) === HistoryDatasetAssociation.STATES.RUNNING )
+ || ( history.get( 'state' ) === HistoryDatasetAssociation.STATES.QUEUED ) ){
+ setTimeout( function(){
+ history.stateUpdater();
+ }, 4000 );
+ }
+
+ }).error( function( xhr, status, error ){
+ if( console && console.warn ){
+ console.warn( 'Error getting history updates from the server:', xhr, status, error );
+ }
+ alert( 'Error getting history updates from the server.\n' + error );
+ });
+ },
+
+ toString : function(){
+ var nameString = ( this.get( 'name' ) )?
+ ( ',' + this.get( 'name' ) ) : ( '' );
+ return 'History(' + this.get( 'id' ) + nameString + ')';
+ }
+});
+
+//==============================================================================
+/** A collection of histories (per user or admin)
+ * (stub) currently unused
+ */
+var HistoryCollection = Backbone.Collection.extend( LoggableMixin ).extend({
+ model : History,
+ urlRoot : 'api/histories',
+ logger : console
+});
+
+//==============================================================================
+//return {
+// History : History,
+// HistoryCollection : HistoryCollection,
+//};});
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 static/scripts/mvc/history/history-panel.js
--- /dev/null
+++ b/static/scripts/mvc/history/history-panel.js
@@ -0,0 +1,380 @@
+//define([
+// "../mvc/base-mvc"
+//], function(){
+/* =============================================================================
+Backbone.js implementation of history panel
+
+TODO:
+ anon user, mako template init:
+ bug: rename url seems to be wrong url
+
+ logged in, mako template:
+ BUG: meter is not updating RELIABLY on change:nice_size
+ BUG: am able to start upload even if over quota - 'runs' forever
+ bug: quotaMeter bar rendering square in chrome
+ BUG: quotaMsg not showing when 100% (on load)
+ BUG: imported, shared history with unaccessible dataset errs in historycontents when getting history
+ (entire history is inaccessible)
+ ??: still happening?
+
+ from loadFromApi:
+ BUG: not showing previous annotations
+
+ fixed:
+ BUG: upload, history size, doesn't change
+ FIXED: using change:nice_size to trigger re-render of history size
+ BUG: delete uploading hda - now in state 'discarded'! ...new state to handle
+ FIXED: handled state
+ BUG: historyItem, error'd ds show display, download?
+ FIXED: removed
+ bug: loading hdas (alt_hist)
+ FIXED: added anon user api request ( trans.user == None and trans.history.id == requested id )
+ bug: quota meter not updating on upload/tool run
+ FIXED: quotaMeter now listens for 'state:ready' from glx_history in alternate_history.mako
+ bug: use of new HDACollection with event listener in init doesn't die...keeps reporting
+ FIXED: change getVisible to return an array
+ BUG: history, broken intial hist state (running, updater, etc.)
+ ??: doesn't seem to happen anymore
+ BUG: collapse all should remove all expanded from storage
+ FIXED: hideAllItemBodies now resets storage.expandedItems
+ BUG: historyItem, shouldn't allow tag, annotate, peek on purged datasets
+ FIXED: ok state now shows only: info, rerun
+ BUG: history?, some ids aren't returning encoded...
+ FIXED:???
+ BUG: history, showing deleted ds
+ FIXED
+ UGH: historyItems have to be decorated with history_ids (api/histories/:history_id/contents/:id)
+ FIXED by adding history_id to history_contents.show
+ BUG: history, if hist has err'd ds, hist has perm state 'error', updater on following ds's doesn't run
+ FIXED by reordering history state from ds' states here and histories
+ BUG: history, broken annotation on reload (can't get thru api (sets fine, tho))
+ FIXED: get thru api for now
+
+ replication:
+ show_deleted/hidden:
+ use storage
+ on/off ui
+ move histview fadein/out in render to app?
+ don't draw body until it's first expand event
+ localize all
+ ?: render url templates on init or render?
+ ?: history, annotation won't accept unicode
+
+ RESTful:
+ move over webui functions available in api
+ delete, undelete
+ update?
+ currently, adding a dataset (via tool execute, etc.) creates a new dataset and refreshes the page
+ provide a means to update the panel via js
+
+ hierarchy:
+ to relational model?
+ HDACollection, meta_files, display_apps, etc.
+ dataset -> hda
+ history -> historyForEditing, historyForViewing
+ display_structured?
+
+ meta:
+ css/html class/id 'item' -> hda
+ add classes, ids on empty divs
+ events (local/ui and otherwise)
+ list in docs as well
+ require.js
+ convert function comments to jsDoc style, complete comments
+ move inline styles into base.less
+ watch the magic strings
+ watch your globals
+
+ feature creep:
+ lineage
+ hide button
+ show permissions in info
+ show shared/sharing status on ds, history
+ maintain scroll position on refresh (storage?)
+ selection, multi-select (and actions common to selected (ugh))
+ searching
+ sorting, re-shuffling
+
+============================================================================= */
+/** view for the HDACollection (as per current right hand panel)
+ *
+ */
+var HistoryPanel = BaseView.extend( LoggableMixin ).extend({
+
+ // uncomment this out see log messages
+ //logger : console,
+
+ // direct attachment to existing element
+ el : 'body.historyPage',
+
+ // init with the model, urlTemplates, set up storage, bind HDACollection events
+ //NOTE: this will create or load PersistantStorage keyed under 'HistoryView.<id>'
+ //pre: you'll need to pass in the urlTemplates (urlTemplates : { history : {...}, hda : {...} })
+ initialize : function( attributes ){
+ this.log( this + '.initialize:', attributes );
+
+ // set up url templates
+ //TODO: prob. better to put this in class scope (as the handlebars templates), but...
+ // they're added to GalaxyPaths on page load (after this file is loaded)
+ if( !attributes.urlTemplates ){ throw( 'HDAView needs urlTemplates on initialize' ); }
+ if( !attributes.urlTemplates.history ){ throw( 'HDAView needs urlTemplates.history on initialize' ); }
+ if( !attributes.urlTemplates.hda ){ throw( 'HDAView needs urlTemplates.hda on initialize' ); }
+ this.urlTemplates = attributes.urlTemplates.history;
+ this.hdaUrlTemplates = attributes.urlTemplates.hda;
+
+ // data that needs to be persistant over page refreshes
+ // (note the key function which uses the history id as well)
+ this.storage = new PersistantStorage(
+ 'HistoryView.' + this.model.get( 'id' ),
+ { expandedHdas : {} }
+ );
+
+ // bind events from the model's hda collection
+ //this.model.bind( 'change', this.render, this );
+ this.model.bind( 'change:nice_size', this.updateHistoryDiskSize, this );
+
+ this.model.hdas.bind( 'add', this.add, this );
+ this.model.hdas.bind( 'reset', this.addAll, this );
+ this.model.hdas.bind( 'all', this.all, this );
+
+ //this.bind( 'all', function(){
+ // this.log( arguments );
+ //}, this );
+
+ // set up instance vars
+ this.hdaViews = {};
+ this.urls = {};
+ },
+
+ add : function( hda ){
+ //console.debug( 'add.' + this, hda );
+ //TODO
+ },
+
+ addAll : function(){
+ //console.debug( 'addAll.' + this );
+ // re render when all hdas are reset
+ this.render();
+ },
+
+ all : function( event ){
+ //console.debug( 'allItemEvents.' + this, event );
+ //...for which to do the debuggings
+ },
+
+ // render the urls for this view using urlTemplates and the model data
+ renderUrls : function( modelJson ){
+ var historyView = this;
+
+ historyView.urls = {};
+ _.each( this.urlTemplates, function( urlTemplate, urlKey ){
+ historyView.urls[ urlKey ] = _.template( urlTemplate, modelJson );
+ });
+ return historyView.urls;
+ },
+
+ // render urls, historyView body, and hdas (if any are shown), fade out, swap, fade in, set up behaviours
+ render : function(){
+ var historyView = this,
+ setUpQueueName = historyView.toString() + '.set-up',
+ newRender = $( '<div/>' ),
+ modelJson = this.model.toJSON(),
+ initialRender = ( this.$el.children().size() === 0 );
+
+ //console.debug( this + '.render, initialRender:', initialRender );
+
+ // render the urls and add them to the model json
+ modelJson.urls = this.renderUrls( modelJson );
+
+ // render the main template, tooltips
+ //NOTE: this is done before the items, since item views should handle theirs themselves
+ newRender.append( HistoryPanel.templates.historyPanel( modelJson ) );
+ newRender.find( '.tooltip' ).tooltip();
+
+ // render hda views (if any and any shown (show_deleted/hidden)
+ if( !this.model.hdas.length
+ || !this.renderItems( newRender.find( '#' + this.model.get( 'id' ) + '-datasets' ) ) ){
+ // if history is empty or no hdas would be rendered, show the empty message
+ newRender.find( '#emptyHistoryMessage' ).show();
+ }
+
+ // fade out existing, swap with the new, fade in, set up behaviours
+ $( historyView ).queue( setUpQueueName, function( next ){
+ historyView.$el.fadeOut( 'fast', function(){ next(); });
+ });
+ $( historyView ).queue( setUpQueueName, function( next ){
+ // swap over from temp div newRender
+ historyView.$el.html( '' );
+ historyView.$el.append( newRender.children() );
+
+ historyView.$el.fadeIn( 'fast', function(){ next(); });
+ });
+ $( historyView ).queue( setUpQueueName, function( next ){
+ this.log( historyView + ' rendered:', historyView.$el );
+
+ //TODO: ideally, these would be set up before the fade in (can't because of async save text)
+ historyView.setUpBehaviours();
+
+ if( initialRender ){
+ historyView.trigger( 'rendered:initial' );
+
+ } else {
+ historyView.trigger( 'rendered' );
+ }
+ next();
+ });
+ $( historyView ).dequeue( setUpQueueName );
+ return this;
+ },
+
+ // set up a view for each item to be shown, init with model and listeners, cache to map ( model.id : view )
+ renderItems : function( $whereTo ){
+ this.hdaViews = {};
+ var historyView = this,
+ show_deleted = this.model.get( 'show_deleted' ),
+ show_hidden = this.model.get( 'show_hidden' ),
+ visibleHdas = this.model.hdas.getVisible( show_deleted, show_hidden );
+
+ // only render the shown hdas
+ _.each( visibleHdas, function( hda ){
+ var hdaId = hda.get( 'id' ),
+ expanded = historyView.storage.get( 'expandedHdas' ).get( hdaId );
+ historyView.hdaViews[ hdaId ] = new HDAView({
+ model : hda,
+ expanded : expanded,
+ urlTemplates : historyView.hdaUrlTemplates
+ });
+ historyView.setUpHdaListeners( historyView.hdaViews[ hdaId ] );
+ // render it (NOTE: reverse order, newest on top (prepend))
+ //TODO: by default send a reverse order list (although this may be more efficient - it's more confusing)
+ $whereTo.prepend( historyView.hdaViews[ hdaId ].render().$el );
+ });
+ return visibleHdas.length;
+ },
+
+ // set up HistoryView->HDAView listeners
+ setUpHdaListeners : function( hdaView ){
+ var historyView = this;
+
+ // use storage to maintain a list of hdas whose bodies are expanded
+ hdaView.bind( 'toggleBodyVisibility', function( id, visible ){
+ if( visible ){
+ historyView.storage.get( 'expandedHdas' ).set( id, true );
+ } else {
+ historyView.storage.get( 'expandedHdas' ).deleteKey( id );
+ }
+ });
+
+ // rendering listeners
+ hdaView.bind( 'rendered:ready', function(){ historyView.trigger( 'hda:rendered:ready' ); });
+ },
+
+ // set up js/widget behaviours: tooltips,
+ //TODO: these should be either sub-MVs, or handled by events
+ setUpBehaviours : function(){
+ // anon users shouldn't have access to any of these
+ if( !( this.model.get( 'user' ) && this.model.get( 'user' ).email ) ){ return; }
+
+ // annotation slide down
+ var historyAnnotationArea = this.$( '#history-annotation-area' );
+ this.$( '#history-annotate' ).click( function() {
+ if ( historyAnnotationArea.is( ":hidden" ) ) {
+ historyAnnotationArea.slideDown( "fast" );
+ } else {
+ historyAnnotationArea.slideUp( "fast" );
+ }
+ return false;
+ });
+
+ // title and annotation editable text
+ //NOTE: these use page scoped selectors - so these need to be in the page DOM before they're applicable
+ async_save_text( "history-name-container", "history-name",
+ this.urls.rename, "new_name", 18 );
+
+ async_save_text( "history-annotation-container", "history-annotation",
+ this.urls.annotate, "new_annotation", 18, true, 4 );
+ },
+
+ // update the history size display (curr. upper right of panel)
+ updateHistoryDiskSize : function(){
+ this.$el.find( '#history-size' ).text( this.model.get( 'nice_size' ) );
+ },
+
+ //TODO: this seems more like a per user message than a history message; IOW, this doesn't belong here
+ showQuotaMessage : function( userData ){
+ var msg = this.$el.find( '#quota-message-container' );
+ //this.log( this + ' showing quota message:', msg, userData );
+ if( msg.is( ':hidden' ) ){ msg.slideDown( 'fast' ); }
+ },
+
+ //TODO: this seems more like a per user message than a history message
+ hideQuotaMessage : function( userData ){
+ var msg = this.$el.find( '#quota-message-container' );
+ //this.log( this + ' hiding quota message:', msg, userData );
+ if( !msg.is( ':hidden' ) ){ msg.slideUp( 'fast' ); }
+ },
+
+ events : {
+ 'click #history-collapse-all' : 'hideAllHdaBodies',
+ 'click #history-tag' : 'loadAndDisplayTags'
+ },
+
+ // collapse all hda bodies
+ hideAllHdaBodies : function(){
+ _.each( this.hdaViews, function( item ){
+ item.toggleBodyVisibility( null, false );
+ });
+ this.storage.set( 'expandedHdas', {} );
+ },
+
+ // find the tag area and, if initial: (via ajax) load the html for displaying them; otherwise, unhide/hide
+ //TODO: into sub-MV
+ loadAndDisplayTags : function( event ){
+ this.log( this + '.loadAndDisplayTags', event );
+ var tagArea = this.$el.find( '#history-tag-area' ),
+ tagElt = tagArea.find( '.tag-elt' );
+ this.log( '\t tagArea', tagArea, ' tagElt', tagElt );
+
+ // Show or hide tag area; if showing tag area and it's empty, fill it
+ if( tagArea.is( ":hidden" ) ){
+ if( !jQuery.trim( tagElt.html() ) ){
+ var view = this;
+ // Need to fill tag element.
+ $.ajax({
+ //TODO: the html from this breaks a couple of times
+ url: view.urls.tag,
+ error: function() { alert( "Tagging failed" ); },
+ success: function(tag_elt_html) {
+ //view.log( view + ' tag elt html (ajax)', tag_elt_html );
+ tagElt.html(tag_elt_html);
+ tagElt.find(".tooltip").tooltip();
+ tagArea.slideDown("fast");
+ }
+ });
+ } else {
+ // Tag element already filled: show
+ tagArea.slideDown("fast");
+ }
+
+ } else {
+ // Currently shown: Hide
+ tagArea.slideUp("fast");
+ }
+ return false;
+ },
+
+ toString : function(){
+ var nameString = this.model.get( 'name' ) || '';
+ return 'HistoryView(' + nameString + ')';
+ }
+});
+
+//------------------------------------------------------------------------------
+HistoryPanel.templates = {
+ historyPanel : Handlebars.templates[ 'template-history-historyPanel' ]
+};
+
+//==============================================================================
+//return {
+// HistoryPanel : HistoryPanel
+//};});
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 templates/root/alternate_history.mako
--- a/templates/root/alternate_history.mako
+++ b/templates/root/alternate_history.mako
@@ -222,97 +222,127 @@
"template-user-quotaMeter-usage"
)}
-##TODO: fix: curr hasta be _after_ h.templates - move somehow
+##TODO: fix: curr hasta be _after_ h.templates bc these use those templates - move somehow
${h.js(
- "mvc/history",
+ "mvc/dataset/hda-model", "mvc/dataset/hda-edit",
+ "mvc/history/history-model", "mvc/history/history-panel",
+
##"mvc/tags", "mvc/annotations",
"mvc/user/user-model", "mvc/user/user-quotameter"
)}
<script type="text/javascript">
+function galaxyPageSetUp(){
+ // moving global functions, objects into Galaxy namespace
+ top.Galaxy = top.Galaxy || {};
+
+ // bad idea from memleak standpoint?
+ top.Galaxy.mainWindow = top.Galaxy.mainWindow || top.frames.galaxy_main;
+ top.Galaxy.toolWindow = top.Galaxy.toolWindow || top.frames.galaxy_tools;
+ top.Galaxy.historyWindow = top.Galaxy.historyWindow || top.frames.galaxy_history;
+
+ top.Galaxy.$masthead = top.Galaxy.$masthead || $( top.document ).find( 'div#masthead' );
+ top.Galaxy.$messagebox = top.Galaxy.$messagebox || $( top.document ).find( 'div#messagebox' );
+ top.Galaxy.$leftPanel = top.Galaxy.$leftPanel || $( top.document ).find( 'div#left' );
+ top.Galaxy.$centerPanel = top.Galaxy.$centerPanel || $( top.document ).find( 'div#center' );
+ top.Galaxy.$rightPanel = top.Galaxy.$rightPanel || $( top.document ).find( 'div#right' );
+
+ //modals
+ // other base functions
+
+ // global backbone models
+ top.Galaxy.currUser = top.Galaxy.currUser;
+ top.Galaxy.currHistoryPanel = top.Galaxy.currHistoryPanel;
+ top.Galaxy.historyPanels = top.Galaxy.historyPanels || [];
+
+ top.Galaxy.paths = galaxy_paths;
+
+ window.Galaxy = top.Galaxy;
+}
+
// set js localizable strings
GalaxyLocalization.setLocalizedString( ${ create_localization_json( get_page_localized_strings() ) } );
// add needed controller urls to GalaxyPaths
galaxy_paths.set( 'hda', ${get_hda_url_templates()} );
galaxy_paths.set( 'history', ${get_history_url_templates()} );
+//console.debug( 'galaxy_paths:', galaxy_paths );
$(function(){
+ galaxyPageSetUp();
+ Galaxy.historyFrame = window;
// ostensibly, this is the App
if( console && console.debug ){
//if( console.clear ){ console.clear(); }
- console.debug( 'using backbone.js in history panel' );
console.pretty = function( o ){ $( '<pre/>' ).text( JSON.stringify( o, null, ' ' ) ).appendTo( 'body' ); }
+ top.storage = jQuery.jStorage
}
- // load initial data in this page - since we're already sending it...
+ // LOAD INITIAL DATA IN THIS PAGE - since we're already sending it...
+ // ...use mako to 'bootstrap' the models
var user = ${ get_current_user() },
history = ${ get_history( history.id ) },
hdas = ${ get_hdas( history.id, datasets ) };
//console.debug( 'user:', user );
//console.debug( 'history:', history );
//console.debug( 'hdas:', hdas );
+ var currUser = new User( user );
+ if( !Galaxy.currUser ){ Galaxy.currUser = currUser; }
- // i don't like this relationship, but user authentication changes views/behaviour
+ // add user data to history
+ // i don't like this history+user relationship, but user authentication changes views/behaviour
history.user = user;
+ // is page sending in show settings? if so override history's
+ //TODO: move into historyPanel
history.show_deleted = ${ 'true' if show_deleted else 'false' };
history.show_hidden = ${ 'true' if show_hidden else 'false' };
- //console.debug( 'galaxy_paths:', galaxy_paths );
- var glx_history = new History( history, hdas );
- glx_history.logger = console;
-
- var glx_history_view = new HistoryView({
- model: glx_history,
+ var historyPanel = new HistoryPanel({
+ model : new History( history, hdas ),
urlTemplates: galaxy_paths.attributes,
- logger: console
+ logger : console
});
- glx_history_view.render();
-
-
- // ...OR load from the api
- //var glx_history = new History().setPaths( galaxy_paths ),
- // glx_history_view = new HistoryView({ model: glx_history });
- //console.warn( 'fetching' );
- //glx_history.loadFromApi( pageData.history.id );
+ historyPanel.render();
+ if( !Galaxy.currHistoryPanel ){ Galaxy.currHistoryPanel = historyPanel; }
+ if( !( historyPanel in Galaxy.historyPanels ) ){ Galaxy.historyPanels.unshift( historyPanel ); }
- // quota meter is a cross-frame ui element (meter in masthead, over quota message in history)
+ // ...or LOAD FROM THE API
+ //historyPanel = new HistoryView({ model: new History().setPaths( galaxy_paths ) });
+ //historyPanel.loadFromApi( pageData.history.id );
+
+
+ // QUOTA METER is a cross-frame ui element (meter in masthead, over quota message in history)
// create it and join them here for now (via events)
- window.currUser = new User( user );
+ //TODO: this really belongs in the masthead
+
//window.currUser.logger = console;
- window.quotaMeter = new UserQuotaMeter({ model: currUser, el: $( top.document ).find( '.quota-meter-container' ) });
- window.quotaMeter.render();
- //window.quotaMeter.logger = console;
+ var quotaMeter = new UserQuotaMeter({
+ model : currUser,
+ el : $( top.document ).find( '.quota-meter-container' )
+ });
+ //quotaMeter.logger = console; window.quotaMeter = quotaMeter
+ quotaMeter.render();
// show/hide the 'over quota message' in the history when the meter tells it to
- quotaMeter.bind( 'quota:over', glx_history_view.showQuotaMessage, glx_history_view );
- quotaMeter.bind( 'quota:under', glx_history_view.hideQuotaMessage, glx_history_view );
+ quotaMeter.bind( 'quota:over', historyPanel.showQuotaMessage, historyPanel );
+ quotaMeter.bind( 'quota:under', historyPanel.hideQuotaMessage, historyPanel );
// having to add this to handle re-render of hview while overquota (the above do not fire)
- glx_history_view.on( 'rendered', function(){
- if( window.quotaMeter.isOverQuota() ){
- glx_history_view.showQuotaMessage();
+ historyPanel.on( 'rendered', function(){
+ if( quotaMeter.isOverQuota() ){
+ historyPanel.showQuotaMessage();
}
});
//TODO: this _is_ sent to the page (over_quota)...
// update the quota meter when current history changes size
- glx_history.bind( 'change:nice_size', function(){
- window.quotaMeter.update()
- }, window.quotaMeter );
+ historyPanel.model.bind( 'change:nice_size', function(){
+ quotaMeter.update()
+ }, quotaMeter );
- if( console && console.debug ){
- window.user = top.user = user;
- window._history = top._history = history;
- window.hdas = top.hdas = hdas;
- window.glx_history = top.glx_history = glx_history;
- window.glx_history_view = top.glx_history_view = glx_history_view;
- top.storage = jQuery.jStorage
- }
-
return;
});
</script>
https://bitbucket.org/galaxy/galaxy-central/changeset/191126557331/
changeset: 191126557331
user: carlfeberhard
date: 2012-11-06 21:58:25
summary: merge and pack scripts
affected #: 8 files
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -400,7 +400,6 @@
# the state or whether the tool used exit codes and regular
# expressions to do so. So we use
# job.state == job.states.ERROR to replace this same test.
- #elif not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) and not context['stderr']:
elif not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) and job.states.ERROR != job.state:
dataset._state = model.Dataset.states.FAILED_METADATA
else:
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/jobs/runners/local.py
--- a/lib/galaxy/jobs/runners/local.py
+++ b/lib/galaxy/jobs/runners/local.py
@@ -183,8 +183,9 @@
def stop_job( self, job ):
#if our local job has JobExternalOutputMetadata associated, then our primary job has to have already finished
- if job.get_external_output_metadata():
- pid = job.get_external_output_metadata()[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
+ job_ext_output_metadata = job.get_external_output_metadata()
+ if job_ext_output_metadata:
+ pid = job_ext_output_metadata[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
else:
pid = job.get_job_runner_external_id()
if pid in [ None, '' ]:
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -18,7 +18,7 @@
from galaxy.model.item_attrs import UsesAnnotations, APIItem
from sqlalchemy.orm import object_session
from sqlalchemy.sql.expression import func
-import sys, os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
+import os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
if sys.version_info[:2] < ( 2, 5 ):
from sets import Set as set
@@ -138,6 +138,12 @@
# TODO: Add accessors for members defined in SQL Alchemy for the Job table and
# for the mapper defined to the Job table.
+ def get_external_output_metadata( self ):
+ """
+ The external_output_metadata is currently a reference from Job to
+ JobExternalOutputMetadata. It exists for a job but not a task.
+ """
+ return self.external_output_metadata
def get_session_id( self ):
return self.session_id
def get_user_id( self ):
@@ -370,6 +376,13 @@
# (e.g., for a session) or never use the member (e.g., external output
# metdata). These can be filled in as needed.
def get_external_output_metadata( self ):
+ """
+ The external_output_metadata is currently a backref to
+ JobExternalOutputMetadata. It exists for a job but not a task,
+ and when a task is cancelled its corresponding parent Job will
+ be cancelled. So None is returned now, but that could be changed
+ to self.get_job().get_external_output_metadata().
+ """
return None
def get_job_runner_name( self ):
"""
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/tool_shed/tool_dependencies/common_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/common_util.py
+++ b/lib/galaxy/tool_shed/tool_dependencies/common_util.py
@@ -19,6 +19,14 @@
else:
env_var_text = elem.text.replace( '$INSTALL_DIR', tool_shed_repository_install_dir )
return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ if elem.text:
+ # Allow for environment variables that contain neither REPOSITORY_INSTALL_DIR nor INSTALL_DIR since there may be command line
+ # parameters that are tuned for a Galaxy instance. Allowing them to be set in one location rather than being hard coded into
+ # each tool config is the best approach. For example:
+ # <environment_variable name="GATK2_SITE_OPTIONS" action="set_to">
+ # "--num_threads 4 --num_cpu_threads_per_data_thread 3 --phone_home STANDARD"
+ # </environment_variable>
+ return dict( name=env_var_name, action=env_var_action, value=elem.text)
return None
def create_or_update_env_shell_file( install_dir, env_var_dict ):
env_var_name = env_var_dict[ 'name' ]
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -1731,7 +1731,7 @@
callback( "", input, value[input.name] )
else:
input.visit_inputs( "", value[input.name], callback )
- def handle_input( self, trans, incoming, history=None ):
+ def handle_input( self, trans, incoming, history=None, old_errors=None ):
"""
Process incoming parameters for this tool from the dict `incoming`,
update the tool state (or create if none existed), and either return
@@ -1766,7 +1766,7 @@
else:
# Update state for all inputs on the current page taking new
# values from `incoming`.
- errors = self.update_state( trans, self.inputs_by_page[state.page], state.inputs, incoming )
+ errors = self.update_state( trans, self.inputs_by_page[state.page], state.inputs, incoming, old_errors=old_errors or {} )
# If the tool provides a `validate_input` hook, call it.
validate_input = self.get_hook( 'validate_input' )
if validate_input:
@@ -1895,7 +1895,10 @@
any_group_errors = True
# Only need to find one that can't be removed due to size, since only
# one removal is processed at # a time anyway
- break
+ break
+ elif group_old_errors and group_old_errors[i]:
+ group_errors[i] = group_old_errors[i]
+ any_group_errors = True
# Update state
max_index = -1
for i, rep_state in enumerate( group_state ):
@@ -1978,6 +1981,8 @@
update_only=update_only,
old_errors=group_old_errors,
item_callback=item_callback )
+ if input.test_param.name in group_old_errors and not test_param_error:
+ test_param_error = group_old_errors[ input.test_param.name ]
if test_param_error:
group_errors[ input.test_param.name ] = test_param_error
if group_errors:
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/tools/parameters/__init__.py
--- a/lib/galaxy/tools/parameters/__init__.py
+++ b/lib/galaxy/tools/parameters/__init__.py
@@ -94,3 +94,26 @@
value = params[key].value_from_basic( value, app, ignore_errors )
rval[ key ] = value
return rval
+
+def params_to_incoming( incoming, inputs, input_values, app, name_prefix="" ):
+ """
+ Given a tool's parameter definition (`inputs`) and a specific set of
+ parameter `input_values` objects, populate `incoming` with the html values.
+
+ Useful for e.g. the rerun function.
+ """
+ for input in inputs.itervalues():
+ if isinstance( input, Repeat ) or isinstance( input, UploadDataset ):
+ for i, d in enumerate( input_values[ input.name ] ):
+ index = d['__index__']
+ new_name_prefix = name_prefix + "%s_%d|" % ( input.name, index )
+ params_to_incoming( incoming, input.inputs, d, app, new_name_prefix )
+ elif isinstance( input, Conditional ):
+ values = input_values[ input.name ]
+ current = values["__current_case__"]
+ new_name_prefix = name_prefix + input.name + "|"
+ incoming[ new_name_prefix + input.test_param.name ] = values[ input.test_param.name ]
+ params_to_incoming( incoming, input.cases[current].inputs, values, app, new_name_prefix )
+ else:
+ incoming[ name_prefix + input.name ] = input.to_string( input_values.get( input.name ), app )
+
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -1539,25 +1539,38 @@
if trans.workflow_building_mode:
return None
if not value:
- raise ValueError( "History does not include a dataset of the required format / build" )
+ raise ValueError( "History does not include a dataset of the required format / build" )
if value in [None, "None"]:
return None
if isinstance( value, list ):
- return [ trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( v ) for v in value ]
+ rval = [ trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( v ) for v in value ]
elif isinstance( value, trans.app.model.HistoryDatasetAssociation ):
- return value
+ rval = value
else:
- return trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( value )
+ rval = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( value )
+ if isinstance( rval, list ):
+ values = rval
+ else:
+ values = [ rval ]
+ for v in values:
+ if v:
+ if v.deleted:
+ raise ValueError( "The previously selected dataset has been previously deleted" )
+ if v.dataset.state in [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED ]:
+ raise ValueError( "The previously selected dataset has entered an unusable state" )
+ return rval
def to_string( self, value, app ):
- if value is None or isinstance( value, str ):
+ if value is None or isinstance( value, basestring ):
return value
+ elif isinstance( value, int ):
+ return str( value )
elif isinstance( value, DummyDataset ):
return None
elif isinstance( value, list) and len(value) > 0 and isinstance( value[0], DummyDataset):
return None
elif isinstance( value, list ):
- return ",".join( [ val if isinstance( val, str ) else str(val.id) for val in value] )
+ return ",".join( [ val if isinstance( val, basestring ) else str(val.id) for val in value] )
return value.id
def to_python( self, value, app ):
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -6,6 +6,7 @@
from galaxy.util.bunch import Bunch
from galaxy.tools import DefaultToolState
from galaxy.tools.parameters.basic import UnvalidatedValue
+from galaxy.tools.parameters import params_to_incoming
from galaxy.tools.actions import upload_common
import logging
@@ -192,25 +193,29 @@
if isinstance(value,list):
values = []
for val in value:
- if val not in history.datasets and val in hda_source_dict:
+ if val in history.datasets:
+ values.append( val )
+ elif val in hda_source_dict:
values.append( hda_source_dict[ val ])
return values
if value not in history.datasets and value in hda_source_dict:
return hda_source_dict[ value ]
visit_input_values( tool.inputs, params_objects, rerun_callback )
- # Create a fake tool_state for the tool, with the parameters values
+ # Create a fake tool_state for the tool, with the parameters values
state = tool.new_state( trans )
state.inputs = params_objects
- tool_state_string = util.object_to_string(state.encode(tool, trans.app))
- # Setup context for template
- vars = dict( tool_state=state, errors = upgrade_messages )
+ #create an incoming object from the original job's dataset-modified param objects
+ incoming = {}
+ params_to_incoming( incoming, tool.inputs, params_objects, trans.app )
+ incoming[ "tool_state" ] = util.object_to_string( state.encode( tool, trans.app ) )
+ template, vars = tool.handle_input( trans, incoming, old_errors=upgrade_messages ) #update new state with old parameters
# Is the "add frame" stuff neccesary here?
add_frame = AddFrameData()
add_frame.debug = trans.debug
if from_noframe is not None:
add_frame.wiki_url = trans.app.config.wiki_url
add_frame.from_noframe = True
- return trans.fill_template( "tool_form.mako",
+ return trans.fill_template( template,
history=history,
toolbox=self.get_toolbox(),
tool_version_select_field=tool_version_select_field,
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
4 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/67da1098be88/
changeset: 67da1098be88
user: dan
date: 2012-11-06 21:46:57
summary: Add a helper menthol "params_to_incoming" that takes a set of parameters and populates a dictionary as if it were an incoming html post.
affected #: 1 file
diff -r aba101adc4a7012f6c959c23ea5f1b8701667649 -r 67da1098be8881f223f469c53ef7dc3528409343 lib/galaxy/tools/parameters/__init__.py
--- a/lib/galaxy/tools/parameters/__init__.py
+++ b/lib/galaxy/tools/parameters/__init__.py
@@ -94,3 +94,26 @@
value = params[key].value_from_basic( value, app, ignore_errors )
rval[ key ] = value
return rval
+
+def params_to_incoming( incoming, inputs, input_values, app, name_prefix="" ):
+ """
+ Given a tool's parameter definition (`inputs`) and a specific set of
+ parameter `input_values` objects, populate `incoming` with the html values.
+
+ Useful for e.g. the rerun function.
+ """
+ for input in inputs.itervalues():
+ if isinstance( input, Repeat ) or isinstance( input, UploadDataset ):
+ for i, d in enumerate( input_values[ input.name ] ):
+ index = d['__index__']
+ new_name_prefix = name_prefix + "%s_%d|" % ( input.name, index )
+ params_to_incoming( incoming, input.inputs, d, app, new_name_prefix )
+ elif isinstance( input, Conditional ):
+ values = input_values[ input.name ]
+ current = values["__current_case__"]
+ new_name_prefix = name_prefix + input.name + "|"
+ incoming[ new_name_prefix + input.test_param.name ] = values[ input.test_param.name ]
+ params_to_incoming( incoming, input.cases[current].inputs, values, app, new_name_prefix )
+ else:
+ incoming[ name_prefix + input.name ] = input.to_string( input_values.get( input.name ), app )
+
https://bitbucket.org/galaxy/galaxy-central/changeset/50513229f6ef/
changeset: 50513229f6ef
user: dan
date: 2012-11-06 21:46:57
summary: Allow passing old_errors to handle_input. Fixes for handling old_errors in handle_input for grouping parameters.
affected #: 1 file
diff -r 67da1098be8881f223f469c53ef7dc3528409343 -r 50513229f6ef8338361d838a40c956108836465d lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -1731,7 +1731,7 @@
callback( "", input, value[input.name] )
else:
input.visit_inputs( "", value[input.name], callback )
- def handle_input( self, trans, incoming, history=None ):
+ def handle_input( self, trans, incoming, history=None, old_errors=None ):
"""
Process incoming parameters for this tool from the dict `incoming`,
update the tool state (or create if none existed), and either return
@@ -1766,7 +1766,7 @@
else:
# Update state for all inputs on the current page taking new
# values from `incoming`.
- errors = self.update_state( trans, self.inputs_by_page[state.page], state.inputs, incoming )
+ errors = self.update_state( trans, self.inputs_by_page[state.page], state.inputs, incoming, old_errors=old_errors or {} )
# If the tool provides a `validate_input` hook, call it.
validate_input = self.get_hook( 'validate_input' )
if validate_input:
@@ -1895,7 +1895,10 @@
any_group_errors = True
# Only need to find one that can't be removed due to size, since only
# one removal is processed at # a time anyway
- break
+ break
+ elif group_old_errors and group_old_errors[i]:
+ group_errors[i] = group_old_errors[i]
+ any_group_errors = True
# Update state
max_index = -1
for i, rep_state in enumerate( group_state ):
@@ -1978,6 +1981,8 @@
update_only=update_only,
old_errors=group_old_errors,
item_callback=item_callback )
+ if input.test_param.name in group_old_errors and not test_param_error:
+ test_param_error = group_old_errors[ input.test_param.name ]
if test_param_error:
group_errors[ input.test_param.name ] = test_param_error
if group_errors:
https://bitbucket.org/galaxy/galaxy-central/changeset/81a007dbc152/
changeset: 81a007dbc152
user: dan
date: 2012-11-06 21:46:57
summary: Rework rerun functionality to treat the previously set job parameters as though they are an incoming form post. This allows validation and subsequent display of errors between the original and current states.
affected #: 1 file
diff -r 50513229f6ef8338361d838a40c956108836465d -r 81a007dbc1528a6124dfdc9caf05579887fe0d4c lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -6,6 +6,7 @@
from galaxy.util.bunch import Bunch
from galaxy.tools import DefaultToolState
from galaxy.tools.parameters.basic import UnvalidatedValue
+from galaxy.tools.parameters import params_to_incoming
from galaxy.tools.actions import upload_common
import logging
@@ -192,25 +193,29 @@
if isinstance(value,list):
values = []
for val in value:
- if val not in history.datasets and val in hda_source_dict:
+ if val in history.datasets:
+ values.append( val )
+ elif val in hda_source_dict:
values.append( hda_source_dict[ val ])
return values
if value not in history.datasets and value in hda_source_dict:
return hda_source_dict[ value ]
visit_input_values( tool.inputs, params_objects, rerun_callback )
- # Create a fake tool_state for the tool, with the parameters values
+ # Create a fake tool_state for the tool, with the parameters values
state = tool.new_state( trans )
state.inputs = params_objects
- tool_state_string = util.object_to_string(state.encode(tool, trans.app))
- # Setup context for template
- vars = dict( tool_state=state, errors = upgrade_messages )
+ #create an incoming object from the original job's dataset-modified param objects
+ incoming = {}
+ params_to_incoming( incoming, tool.inputs, params_objects, trans.app )
+ incoming[ "tool_state" ] = util.object_to_string( state.encode( tool, trans.app ) )
+ template, vars = tool.handle_input( trans, incoming, old_errors=upgrade_messages ) #update new state with old parameters
# Is the "add frame" stuff neccesary here?
add_frame = AddFrameData()
add_frame.debug = trans.debug
if from_noframe is not None:
add_frame.wiki_url = trans.app.config.wiki_url
add_frame.from_noframe = True
- return trans.fill_template( "tool_form.mako",
+ return trans.fill_template( template,
history=history,
toolbox=self.get_toolbox(),
tool_version_select_field=tool_version_select_field,
https://bitbucket.org/galaxy/galaxy-central/changeset/907f364107c5/
changeset: 907f364107c5
user: dan
date: 2012-11-06 21:46:58
summary: Add error messages for a DataToolParameter when the provided value is no longer valid due to be deleted or being in an error state.
affected #: 1 file
diff -r 81a007dbc1528a6124dfdc9caf05579887fe0d4c -r 907f364107c534cd531b4d91fcda7fe3e59eb4b1 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -1539,25 +1539,38 @@
if trans.workflow_building_mode:
return None
if not value:
- raise ValueError( "History does not include a dataset of the required format / build" )
+ raise ValueError( "History does not include a dataset of the required format / build" )
if value in [None, "None"]:
return None
if isinstance( value, list ):
- return [ trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( v ) for v in value ]
+ rval = [ trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( v ) for v in value ]
elif isinstance( value, trans.app.model.HistoryDatasetAssociation ):
- return value
+ rval = value
else:
- return trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( value )
+ rval = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( value )
+ if isinstance( rval, list ):
+ values = rval
+ else:
+ values = [ rval ]
+ for v in values:
+ if v:
+ if v.deleted:
+ raise ValueError( "The previously selected dataset has been previously deleted" )
+ if v.dataset.state in [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED ]:
+ raise ValueError( "The previously selected dataset has entered an unusable state" )
+ return rval
def to_string( self, value, app ):
- if value is None or isinstance( value, str ):
+ if value is None or isinstance( value, basestring ):
return value
+ elif isinstance( value, int ):
+ return str( value )
elif isinstance( value, DummyDataset ):
return None
elif isinstance( value, list) and len(value) > 0 and isinstance( value[0], DummyDataset):
return None
elif isinstance( value, list ):
- return ",".join( [ val if isinstance( val, str ) else str(val.id) for val in value] )
+ return ",".join( [ val if isinstance( val, basestring ) else str(val.id) for val in value] )
return value.id
def to_python( self, value, 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
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/aba101adc4a7/
changeset: aba101adc4a7
user: smcmanus
date: 2012-11-06 21:18:42
summary: The job's external metadata is returned from the model. The Task class had included a get_external_output_metadata method that was missing from the Job class, and this was the result of trying to merge the two interfaces. This caused problems in cancelling jobs (i.e., the jobs would run to completion) when the jobs were scheduled for the local runner.
affected #: 3 files
diff -r 8fb2a905f2492a388799ac4d861e52bda9365300 -r aba101adc4a7012f6c959c23ea5f1b8701667649 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -400,7 +400,6 @@
# the state or whether the tool used exit codes and regular
# expressions to do so. So we use
# job.state == job.states.ERROR to replace this same test.
- #elif not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) and not context['stderr']:
elif not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) and job.states.ERROR != job.state:
dataset._state = model.Dataset.states.FAILED_METADATA
else:
diff -r 8fb2a905f2492a388799ac4d861e52bda9365300 -r aba101adc4a7012f6c959c23ea5f1b8701667649 lib/galaxy/jobs/runners/local.py
--- a/lib/galaxy/jobs/runners/local.py
+++ b/lib/galaxy/jobs/runners/local.py
@@ -183,8 +183,9 @@
def stop_job( self, job ):
#if our local job has JobExternalOutputMetadata associated, then our primary job has to have already finished
- if job.get_external_output_metadata():
- pid = job.get_external_output_metadata()[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
+ job_ext_output_metadata = job.get_external_output_metadata()
+ if job_ext_output_metadata:
+ pid = job_ext_output_metadata[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
else:
pid = job.get_job_runner_external_id()
if pid in [ None, '' ]:
diff -r 8fb2a905f2492a388799ac4d861e52bda9365300 -r aba101adc4a7012f6c959c23ea5f1b8701667649 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -18,7 +18,7 @@
from galaxy.model.item_attrs import UsesAnnotations, APIItem
from sqlalchemy.orm import object_session
from sqlalchemy.sql.expression import func
-import sys, os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
+import os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
if sys.version_info[:2] < ( 2, 5 ):
from sets import Set as set
@@ -138,6 +138,12 @@
# TODO: Add accessors for members defined in SQL Alchemy for the Job table and
# for the mapper defined to the Job table.
+ def get_external_output_metadata( self ):
+ """
+ The external_output_metadata is currently a reference from Job to
+ JobExternalOutputMetadata. It exists for a job but not a task.
+ """
+ return self.external_output_metadata
def get_session_id( self ):
return self.session_id
def get_user_id( self ):
@@ -370,6 +376,13 @@
# (e.g., for a session) or never use the member (e.g., external output
# metdata). These can be filled in as needed.
def get_external_output_metadata( self ):
+ """
+ The external_output_metadata is currently a backref to
+ JobExternalOutputMetadata. It exists for a job but not a task,
+ and when a task is cancelled its corresponding parent Job will
+ be cancelled. So None is returned now, but that could be changed
+ to self.get_job().get_external_output_metadata().
+ """
return None
def get_job_runner_name( self ):
"""
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
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8fb2a905f249/
changeset: 8fb2a905f249
user: greg
date: 2012-11-06 21:09:34
summary: Per James Johnson, allow for environment variables that contain neither REPOSITORY_INSTALL_DIR nor INSTALL_DIR when defining tool dependencies to be installed along with tool shed repositories.
affected #: 1 file
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r 8fb2a905f2492a388799ac4d861e52bda9365300 lib/galaxy/tool_shed/tool_dependencies/common_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/common_util.py
+++ b/lib/galaxy/tool_shed/tool_dependencies/common_util.py
@@ -19,6 +19,14 @@
else:
env_var_text = elem.text.replace( '$INSTALL_DIR', tool_shed_repository_install_dir )
return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ if elem.text:
+ # Allow for environment variables that contain neither REPOSITORY_INSTALL_DIR nor INSTALL_DIR since there may be command line
+ # parameters that are tuned for a Galaxy instance. Allowing them to be set in one location rather than being hard coded into
+ # each tool config is the best approach. For example:
+ # <environment_variable name="GATK2_SITE_OPTIONS" action="set_to">
+ # "--num_threads 4 --num_cpu_threads_per_data_thread 3 --phone_home STANDARD"
+ # </environment_variable>
+ return dict( name=env_var_name, action=env_var_action, value=elem.text)
return None
def create_or_update_env_shell_file( install_dir, env_var_dict ):
env_var_name = env_var_dict[ 'name' ]
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/79c884befb23/
changeset: 79c884befb23
user: carlfeberhard
date: 2012-11-06 20:07:21
summary: quota meter: fix multiple api calls on first history render; controllers/root.py: set up to move show_deleted/hidden into client
affected #: 5 files
diff -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b lib/galaxy/webapps/galaxy/controllers/root.py
--- a/lib/galaxy/webapps/galaxy/controllers/root.py
+++ b/lib/galaxy/webapps/galaxy/controllers/root.py
@@ -116,13 +116,23 @@
show_deleted=util.string_as_bool( show_deleted ),
show_hidden=util.string_as_bool( show_hidden ) )
else:
- show_deleted = show_purged = util.string_as_bool( show_deleted )
+ show_deleted = util.string_as_bool( show_deleted )
show_hidden = util.string_as_bool( show_hidden )
- datasets = self.get_history_datasets( trans, history, show_deleted, show_hidden, show_purged )
+ show_purged = util.string_as_bool( show_deleted )
+ datasets = []
history_panel_template = "root/history.mako"
- # history panel -> backbone
- #history_panel_template = "root/alternate_history.mako"
+
+ # history panel -> backbone (WIP - uncomment next to use)
+ #USE_ALTERNATE = True
+ if 'USE_ALTERNATE' in locals():
+ datasets = self.get_history_datasets( trans, history,
+ show_deleted=True, show_hidden=True, show_purged=True )
+ history_panel_template = "root/alternate_history.mako"
+
+ else:
+ datasets = self.get_history_datasets( trans, history, show_deleted, show_hidden, show_purged )
+
return trans.stream_template_mako( history_panel_template,
history = history,
annotation = self.get_item_annotation_str( trans.sa_session, trans.user, history ),
diff -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b static/scripts/mvc/history.js
--- a/static/scripts/mvc/history.js
+++ b/static/scripts/mvc/history.js
@@ -27,7 +27,7 @@
bug: quotaMeter bar rendering square in chrome
BUG: quotaMsg not showing when 100% (on load)
BUG: upload, history size, doesn't change
- TODO: on hdas state:final, update ONLY the size...from what? histories.py? in js?
+ TODO: on hdas state:ready, update ONLY the size...from what? histories.py? in js?
BUG: imported, shared history with unaccessible dataset errs in historycontents when getting history
(entire history is inaccessible)
??: still happening?
@@ -41,7 +41,7 @@
bug: loading hdas (alt_hist)
FIXED: added anon user api request ( trans.user == None and trans.history.id == requested id )
bug: quota meter not updating on upload/tool run
- FIXED: quotaMeter now listens for 'state:final' from glx_history in alternate_history.mako
+ FIXED: quotaMeter now listens for 'state:ready' from glx_history in alternate_history.mako
bug: use of new HDACollection with event listener in init doesn't die...keeps reporting
FIXED: change getVisible to return an array
BUG: history, broken intial hist state (running, updater, etc.)
@@ -134,6 +134,7 @@
// array of associated file types (eg. [ 'bam_index', ... ])
meta_files : [],
+
misc_blurb : '',
misc_info : '',
@@ -155,25 +156,32 @@
},
// (curr) only handles changing state of non-accessible hdas to STATES.NOT_VIEWABLE
- //TODO: use initialize (or validate) to check purged AND deleted -> purged XOR deleted
+ //TODO:? use initialize (or validate) to check purged AND deleted -> purged XOR deleted
initialize : function(){
this.log( this + '.initialize', this.attributes );
this.log( '\tparent history_id: ' + this.get( 'history_id' ) );
- //!! this state is not in trans.app.model.Dataset.states - set it here
+ //!! this state is not in trans.app.model.Dataset.states - set it here -
+ //TODO: change to server side.
if( !this.get( 'accessible' ) ){
this.set( 'state', HistoryDatasetAssociation.STATES.NOT_VIEWABLE );
}
+ // if the state has changed and the new state is a ready state, fire an event
+ this.on( 'change:state', function( currModel, newState ){
+ this.log( this + ' has changed state:', currModel, newState );
+ if( this.inReadyState() ){
+ this.trigger( 'state:ready', this.get( 'id' ), newState, this.previous( 'state' ), currModel );
+ }
+ });
+
+ // debug on change events
//this.on( 'change', function( currModel, changedList ){
// this.log( this + ' has changed:', currModel, changedList );
//});
- this.on( 'change:state', function( currModel, newState ){
- this.log( this + ' has changed state:', currModel, newState );
- if( this.inFinalState() ){
- this.trigger( 'state:final', currModel, newState, this.previous( 'state' ) );
- }
- });
+ //this.bind( 'all', function( event ){
+ // this.log( this + '', arguments );
+ //});
},
isDeletedOrPurged : function(){
@@ -195,8 +203,8 @@
return isVisible;
},
- // 'final' states are states where no processing (for the ds) is left to do on the server
- inFinalState : function(){
+ // 'ready' states are states where no processing (for the ds) is left to do on the server
+ inReadyState : function(){
var state = this.get( 'state' );
return (
( state === HistoryDatasetAssociation.STATES.NEW )
@@ -248,8 +256,8 @@
//logger : console,
initialize : function(){
- //this.bind( 'all', function( x, y, z ){
- // console.info( this + '', x, y, z );
+ //this.bind( 'all', function( event ){
+ // this.log( this + '', arguments );
//});
},
@@ -276,11 +284,11 @@
return stateLists;
},
- // returns the id of every hda still running (not in a final state)
+ // returns the id of every hda still running (not in a ready state)
running : function(){
var idList = [];
this.each( function( item ){
- if( !item.inFinalState() ){
+ if( !item.inReadyState() ){
idList.push( item.get( 'id' ) );
}
});
@@ -354,6 +362,9 @@
//this.on( 'change', function( currModel, changedList ){
// this.log( this + ' has changed:', currModel, changedList );
//});
+ //this.bind( 'all', function( event ){
+ // this.log( this + '', arguments );
+ //});
},
// get data via the api (alternative to sending options,hdas to initialize)
@@ -419,7 +430,9 @@
).success( function( response ){
//this.log( 'historyApiRequest, response:', response );
history.set( response );
- history.log( 'current history state:', history.get( 'state' ), '(was)', oldState );
+ history.log( 'current history state:', history.get( 'state' ),
+ '(was)', oldState,
+ 'new size:', history.get( 'nice_size' ) );
//TODO: revisit this - seems too elaborate, need something straightforward
// for each state, check for the difference between old dataset states and new
@@ -484,6 +497,9 @@
// re-render the entire view on any model change
this.model.bind( 'change', this.render, this );
+ //this.bind( 'all', function( event ){
+ // this.log( event );
+ //}, this );
},
// urlTemplates is a map (or nested map) of underscore templates (currently, anyhoo)
@@ -525,7 +541,10 @@
var view = this,
id = this.model.get( 'id' ),
state = this.model.get( 'state' ),
- itemWrapper = $( '<div/>' ).attr( 'id', 'historyItem-' + id );
+ itemWrapper = $( '<div/>' ).attr( 'id', 'historyItem-' + id ),
+ initialRender = ( this.$el.children().size() === 0 );
+
+ //console.debug( this + '.render, initial?:', initialRender );
this._clearReferences();
this.$el.attr( 'id', 'historyItemContainer-' + id );
@@ -551,10 +570,14 @@
view.$el.children().remove();
view.$el.append( itemWrapper ).fadeIn( 'fast', function(){
view.log( view + ' rendered:', view.$el );
- view.trigger( 'rendered' );
- if( view.model.inFinalState() ){
- view.trigger( 'rendered:final' );
+ var renderedEventName = 'rendered';
+
+ if( initialRender ){
+ renderedEventName += ':initial';
+ } else if( view.model.inReadyState() ){
+ renderedEventName += ':ready';
}
+ view.trigger( renderedEventName );
});
});
return this;
@@ -610,8 +633,8 @@
// icon-button to display this hda in the galaxy main iframe
_render_displayButton : function(){
- // don't show display if not in final state, error'd, or not accessible
- if( ( !this.model.inFinalState() )
+ // don't show display if not in ready state, error'd, or not accessible
+ if( ( !this.model.inReadyState() )
|| ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
|| ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
|| ( !this.model.get( 'accessible' ) ) ){
@@ -1335,13 +1358,18 @@
{ expandedHdas : {} }
);
+ // bind events from the model's hda collection
//this.model.bind( 'change', this.render, this );
+ this.model.bind( 'change:nice_size', this.updateHistoryDiskSize, this );
- // bind events from the model's hda collection
this.model.hdas.bind( 'add', this.add, this );
this.model.hdas.bind( 'reset', this.addAll, this );
this.model.hdas.bind( 'all', this.all, this );
+ //this.bind( 'all', function(){
+ // this.log( arguments );
+ //}, this );
+
// set up instance vars
this.hdaViews = {};
this.urls = {};
@@ -1379,7 +1407,10 @@
var historyView = this,
setUpQueueName = historyView.toString() + '.set-up',
newRender = $( '<div/>' ),
- modelJson = this.model.toJSON();
+ modelJson = this.model.toJSON(),
+ initialRender = ( this.$el.children().size() === 0 );
+
+ //console.debug( this + '.render, initialRender:', initialRender );
// render the urls and add them to the model json
modelJson.urls = this.renderUrls( modelJson );
@@ -1389,7 +1420,7 @@
newRender.append( HistoryView.templates.historyPanel( modelJson ) );
newRender.find( '.tooltip' ).tooltip();
- // render hda views (if any)
+ // render hda views (if any and any shown (show_deleted/hidden)
if( !this.model.hdas.length
|| !this.renderItems( newRender.find( '#' + this.model.get( 'id' ) + '-datasets' ) ) ){
// if history is empty or no hdas would be rendered, show the empty message
@@ -1413,7 +1444,12 @@
//TODO: ideally, these would be set up before the fade in (can't because of async save text)
historyView.setUpBehaviours();
- historyView.trigger( 'rendered' );
+ if( initialRender ){
+ historyView.trigger( 'rendered:initial' );
+
+ } else {
+ historyView.trigger( 'rendered' );
+ }
next();
});
$( historyView ).dequeue( setUpQueueName );
@@ -1459,8 +1495,7 @@
});
// rendering listeners
- //hdaView.bind( 'rendered', function(){});
- hdaView.bind( 'rendered:final', function(){ historyView.trigger( 'hda:rendered:final' ); });
+ hdaView.bind( 'rendered:ready', function(){ historyView.trigger( 'hda:rendered:ready' ); });
},
// set up js/widget behaviours: tooltips,
@@ -1488,6 +1523,11 @@
async_save_text( "history-annotation-container", "history-annotation",
this.urls.annotate, "new_annotation", 18, true, 4 );
},
+
+ // update the history size display (curr. upper right of panel)
+ updateHistoryDiskSize : function(){
+ this.$el.find( '#history-size' ).text( this.model.get( 'nice_size' ) );
+ },
//TODO: this seems more like a per user message than a history message; IOW, this doesn't belong here
showQuotaMessage : function( userData ){
diff -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b static/scripts/mvc/user/user-model.js
--- a/static/scripts/mvc/user/user-model.js
+++ b/static/scripts/mvc/user/user-model.js
@@ -22,9 +22,9 @@
options = options || {};
var model = this,
userFn = options.success;
- options.success = function( model, response ){
- model.trigger( 'loaded', model, response );
- if( userFn ){ userFn( model, response ); }
+ options.success = function( newModel, response ){
+ model.trigger( 'loaded', newModel, response );
+ if( userFn ){ userFn( newModel, response ); }
};
if( idOrCurrent === User.CURRENT_ID_STR ){
options.url = this.urlRoot + '/' + User.CURRENT_ID_STR;
@@ -41,14 +41,18 @@
return 'User(' + userInfo.join( ':' ) + ')';
}
});
+
+// string to send to tell server to return this transaction's user (see api/users.py)
User.CURRENT_ID_STR = 'current';
+// class method to load the current user via the api and return that model
User.getCurrentUserFromApi = function( options ){
var currentUser = new User();
currentUser.loadFromApi( User.CURRENT_ID_STR, options );
return currentUser;
};
+// (stub) collection for users (shouldn't be common unless admin UI)
var UserCollection = Backbone.Collection.extend( LoggableMixin ).extend({
model : User,
logger : console,
diff -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b static/scripts/mvc/user/user-quotameter.js
--- a/static/scripts/mvc/user/user-quotameter.js
+++ b/static/scripts/mvc/user/user-quotameter.js
@@ -5,7 +5,7 @@
// for now, keep the view in the history panel (where the message is), but render ALSO to the masthead
var UserQuotaMeter = BaseView.extend( LoggableMixin ).extend({
- logger : console,
+ //logger : console,
options : {
warnAtPercent : 85,
diff -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b templates/root/alternate_history.mako
--- a/templates/root/alternate_history.mako
+++ b/templates/root/alternate_history.mako
@@ -251,9 +251,9 @@
var user = ${ get_current_user() },
history = ${ get_history( history.id ) },
hdas = ${ get_hdas( history.id, datasets ) };
- console.debug( 'user:', user );
- console.debug( 'history:', history );
- console.debug( 'hdas:', hdas );
+ //console.debug( 'user:', user );
+ //console.debug( 'history:', history );
+ //console.debug( 'hdas:', hdas );
// i don't like this relationship, but user authentication changes views/behaviour
history.user = user;
@@ -296,10 +296,12 @@
glx_history_view.showQuotaMessage();
}
});
+ //TODO: this _is_ sent to the page (over_quota)...
- // update the quota meter when any hda reaches a 'final' state
- //NOTE: use an anon function or update will be passed the hda and think it's the options param
- glx_history_view.on( 'hda:rendered:final', function(){ window.quotaMeter.update({}) }, window.quotaMeter )
+ // update the quota meter when current history changes size
+ glx_history.bind( 'change:nice_size', function(){
+ window.quotaMeter.update()
+ }, window.quotaMeter );
if( console && console.debug ){
https://bitbucket.org/galaxy/galaxy-central/changeset/3d27b35e1c1f/
changeset: 3d27b35e1c1f
user: carlfeberhard
date: 2012-11-06 20:09:40
summary: pack scripts
affected #: 7 files
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/mvc/history.js
--- a/static/scripts/packed/mvc/history.js
+++ b/static/scripts/packed/mvc/history.js
@@ -1,1 +1,1 @@
-var HistoryDatasetAssociation=BaseModel.extend(LoggableMixin).extend({defaults:{history_id:null,model_class:"HistoryDatasetAssociation",hid:0,id:null,name:"",state:"",data_type:null,file_size:0,meta_files:[],misc_blurb:"",misc_info:"",deleted:false,purged:false,visible:false,accessible:false,for_editing:true},url:function(){return"api/histories/"+this.get("history_id")+"/contents/"+this.get("id")},initialize:function(){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"));if(!this.get("accessible")){this.set("state",HistoryDatasetAssociation.STATES.NOT_VIEWABLE)}this.on("change",function(b,a,d,c){this.log(this+" has changed:",b,a,d,c)})},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},isEditable:function(){return(!this.isDeletedOrPurged())},isVisible:function(b,c){var a=true;if((!b)&&(this.get("deleted")||this.get("purged"))){a=false}if((!c)&&(!this.get("visible"))){a=false}return a},inFinalState:function(){return((this.get("state")===HistoryDatasetAssociation.STATES.OK)||(this.get("state")===HistoryDatasetAssociation.STATES.FAILED_METADATA)||(this.get("state")===HistoryDatasetAssociation.STATES.EMPTY)||(this.get("state")===HistoryDatasetAssociation.STATES.ERROR))},hasData:function(){return(this.get("file_size")>0)},toString:function(){var a=this.get("id")||"";if(this.get("name")){a+=':"'+this.get("name")+'"'}return"HistoryDatasetAssociation("+a+")"}});HistoryDatasetAssociation.STATES={NOT_VIEWABLE:"noPermission",NEW:"new",UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",OK:"ok",EMPTY:"empty",ERROR:"error",DISCARDED:"discarded",SETTING_METADATA:"setting_metadata",FAILED_METADATA:"failed_metadata"};var HDACollection=Backbone.Collection.extend(LoggableMixin).extend({model:HistoryDatasetAssociation,logger:console,ids:function(){return this.map(function(a){return a.id})},getVisible:function(a,b){return new HDACollection(this.filter(function(c){return c.isVisible(a,b)}))},getStateLists:function(){var a={};_.each(_.values(HistoryDatasetAssociation.STATES),function(b){a[b]=[]});this.each(function(b){a[b.get("state")].push(b.get("id"))});return a},update:function(a){this.log(this+"update:",a);if(!(a&&a.length)){return}var b=this;_.each(a,function(e,c){var d=b.get(e);d.fetch()})},toString:function(){return("HDACollection("+this.ids().join(",")+")")}});var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",show_deleted:false,show_hidden:false,diskSize:0,deleted:false,tags:[],annotation:null,message:null,quotaMsg:false},url:function(){return"api/histories/"+this.get("id")},initialize:function(a,b){this.log(this+".initialize:",a,b);this.hdas=new HDACollection();if(b&&b.length){this.hdas.reset(b);this.checkForUpdates()}this.on("change",function(d,c,f,e){this.log(this+" has changed:",d,c,f,e)})},loadFromAPI:function(a,c){var b=this;b.attributes.id=a;jQuery.when(jQuery.ajax("api/users/current"),b.fetch()).then(function(e,d){b.attributes.user=e[0];b.log(b)}).then(function(){jQuery.ajax(b.url()+"/contents?"+jQuery.param({ids:b.itemIdsFromStateIds().join(",")})).success(function(d){b.hdas.reset(d);b.checkForUpdates();c()})})},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},checkForUpdates:function(a){this.stateFromStateIds();if((this.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(this.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){this.stateUpdater()}return this},stateFromStateIds:function(){var a=this.hdas.getStateLists();this.attributes.state_ids=a;if((a.running.length>0)||(a.upload.length>0)||(a.setting_metadata.length>0)){this.set("state",HistoryDatasetAssociation.STATES.RUNNING)}else{if(a.queued.length>0){this.set("state",HistoryDatasetAssociation.STATES.QUEUED)}else{if((a.error.length>0)||(a.failed_metadata.length>0)){this.set("state",HistoryDatasetAssociation.STATES.ERROR)}else{if(a.ok.length===this.hdas.length){this.set("state",HistoryDatasetAssociation.STATES.OK)}else{throw (this+".stateFromStateDetails: unable to determine history state from hda states: "+this.get("state_ids"))}}}}return this},stateUpdater:function(){var c=this,a=this.get("state"),b=this.get("state_ids");jQuery.ajax("api/histories/"+this.get("id")).success(function(d){c.set(d);c.log("current history state:",c.get("state"),"(was)",a);var e=[];_.each(_.keys(d.state_ids),function(g){var f=_.difference(d.state_ids[g],b[g]);e=e.concat(f)});if(e.length){c.hdas.update(e)}if((c.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(c.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){setTimeout(function(){c.stateUpdater()},4000)}}).error(function(f,d,e){if(console&&console.warn){console.warn("Error getting history updates from the server:",f,d,e)}alert("Error getting history updates from the server.\n"+e)})},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});var HDAView=BaseView.extend(LoggableMixin).extend({logger:console,tagName:"div",className:"historyItemContainer",initialize:function(a){this.log(this+".initialize:",a);if(!a.urlTemplates){throw ("HDAView needs urlTemplates on initialize")}this.urls=this.renderUrls(a.urlTemplates,this.model.toJSON());this.expanded=a.expanded||false;this.model.bind("change",this.render,this)},renderUrls:function(d,a){var b=this,c={};_.each(d,function(e,f){if(_.isObject(e)){c[f]=b.renderUrls(e,a)}else{if(f==="meta_download"){c[f]=b.renderMetaDownloadUrls(e,a)}else{c[f]=_.template(e,a)}}});return c},renderMetaDownloadUrls:function(b,a){return _.map(a.meta_files,function(c){return{url:_.template(b,{id:a.id,file_type:c.file_type}),file_type:c.file_type}})},render:function(){var b=this,d=this.model.get("id"),c=this.model.get("state"),a=$("<div/>").attr("id","historyItem-"+d);this._clearReferences();this.$el.attr("id","historyItemContainer-"+d);a.addClass("historyItemWrapper").addClass("historyItem").addClass("historyItem-"+c);a.append(this._render_warnings());a.append(this._render_titleBar());this.body=$(this._render_body());a.append(this.body);make_popup_menus(a);a.find(".tooltip").tooltip({placement:"bottom"});this.$el.fadeOut("fast",function(){b.$el.children().remove();b.$el.append(a).fadeIn("fast",function(){b.log(b+" rendered:",b.$el);b.trigger("rendered")})});return this},_clearReferences:function(){this.displayButton=null;this.editButton=null;this.deleteButton=null;this.errButton=null;this.showParamsButton=null;this.rerunButton=null;this.visualizationsButton=null;this.tagButton=null;this.annotateButton=null},_render_warnings:function(){return $(jQuery.trim(HDAView.templates.messages(_.extend(this.model.toJSON(),{urls:this.urls}))))},_render_titleBar:function(){var a=$('<div class="historyItemTitleBar" style="overflow: hidden"></div>');a.append(this._render_titleButtons());a.append('<span class="state-icon"></span>');a.append(this._render_titleLink());return a},_render_titleButtons:function(){var a=$('<div class="historyItemButtons"></div>');a.append(this._render_displayButton());a.append(this._render_editButton());a.append(this._render_deleteButton());return a},_render_displayButton:function(){if((!this.model.inFinalState())||(this.model.get("state")===HistoryDatasetAssociation.STATES.ERROR)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var a={icon_class:"display"};if(this.model.get("purged")){a.enabled=false;a.title="Cannot display datasets removed from disk"}else{a.title="Display data in browser";a.href=this.urls.display}if(this.model.get("for_editing")){a.target="galaxy_main"}this.displayButton=new IconButtonView({model:new IconButton(a)});return this.displayButton.render().$el},_render_editButton:function(){if((this.model.get("state")===HistoryDatasetAssociation.STATES.UPLOAD)||(this.model.get("state")===HistoryDatasetAssociation.STATES.ERROR)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))||(!this.model.get("for_editing"))){return null}var c=this.model.get("purged"),a=this.model.get("deleted"),b={title:"Edit attributes",href:this.urls.edit,target:"galaxy_main",icon_class:"edit"};if(a||c){b.enabled=false;if(c){b.title="Cannot edit attributes of datasets removed from disk"}else{if(a){b.title="Undelete dataset to edit attributes"}}}this.editButton=new IconButtonView({model:new IconButton(b)});return this.editButton.render().$el},_render_deleteButton:function(){if((!this.model.get("for_editing"))||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var a={title:"Delete",href:this.urls["delete"],id:"historyItemDeleter-"+this.model.get("id"),icon_class:"delete"};if(this.model.get("deleted")||this.model.get("purged")){a={title:"Dataset is already deleted",icon_class:"delete",enabled:false}}this.deleteButton=new IconButtonView({model:new IconButton(a)});return this.deleteButton.render().$el},_render_titleLink:function(){return $(jQuery.trim(HDAView.templates.titleLink(_.extend(this.model.toJSON(),{urls:this.urls}))))},_render_hdaSummary:function(){var a=_.extend(this.model.toJSON(),{urls:this.urls});if(this.model.get("metadata_dbkey")==="?"&&this.model.isEditable()){_.extend(a,{dbkey_unknown_and_editable:true})}return HDAView.templates.hdaSummary(a)},_render_primaryActionButtons:function(c){var b=$("<div/>").attr("id","primary-actions-"+this.model.get("id")),a=this;_.each(c,function(d){b.append(d.call(a))});return b},_render_downloadButton:function(){if(this.model.get("purged")){return null}var a=HDAView.templates.downloadLinks(_.extend(this.model.toJSON(),{urls:this.urls}));return $(a)},_render_errButton:function(){if((this.model.get("state")!==HistoryDatasetAssociation.STATES.ERROR)||(!this.model.get("for_editing"))){return null}this.errButton=new IconButtonView({model:new IconButton({title:"View or report this error",href:this.urls.report_error,target:"galaxy_main",icon_class:"bug"})});return this.errButton.render().$el},_render_showParamsButton:function(){this.showParamsButton=new IconButtonView({model:new IconButton({title:"View details",href:this.urls.show_params,target:"galaxy_main",icon_class:"information"})});return this.showParamsButton.render().$el},_render_rerunButton:function(){if(!this.model.get("for_editing")){return null}this.rerunButton=new IconButtonView({model:new IconButton({title:"Run this job again",href:this.urls.rerun,target:"galaxy_main",icon_class:"arrow-circle"})});return this.rerunButton.render().$el},_render_visualizationsButton:function(){var c=this.model.get("dbkey"),a=this.model.get("visualizations"),f=this.urls.visualization,d={},g={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(!(this.model.hasData())||!(this.model.get("for_editing"))||!(a&&a.length)||!(f)){return null}this.visualizationsButton=new IconButtonView({model:new IconButton({title:"Visualize",href:f,icon_class:"chart_curve"})});var b=this.visualizationsButton.render().$el;b.addClass("visualize-icon");if(c){g.dbkey=c}function e(h){switch(h){case"trackster":return create_trackster_action_fn(f,g,c);case"scatterplot":return create_scatterplot_action_fn(f,g);default:return function(){window.parent.location=f+"/"+h+"?"+$.param(g)}}}if(a.length===1){b.attr("title",a[0]);b.click(e(a[0]))}else{_.each(a,function(i){var h=i.charAt(0).toUpperCase()+i.slice(1);d[h]=e(i)});make_popupmenu(b,d)}return b},_render_secondaryActionButtons:function(b){var c=$("<div/>"),a=this;c.attr("style","float: right;").attr("id","secondary-actions-"+this.model.get("id"));_.each(b,function(d){c.append(d.call(a))});return c},_render_tagButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.urls.tags.get)){return null}this.tagButton=new IconButtonView({model:new IconButton({title:"Edit dataset tags",target:"galaxy_main",href:this.urls.tags.get,icon_class:"tags"})});return this.tagButton.render().$el},_render_annotateButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.urls.annotation.get)){return null}this.annotateButton=new IconButtonView({model:new IconButton({title:"Edit dataset annotation",target:"galaxy_main",icon_class:"annotate"})});return this.annotateButton.render().$el},_render_displayApps:function(){if(!this.model.hasData()){return null}var a=$("<div/>").addClass("display-apps");if(!_.isEmpty(this.model.get("display_types"))){a.append(HDAView.templates.displayApps({displayApps:this.model.get("display_types")}))}if(!_.isEmpty(this.model.get("display_apps"))){a.append(HDAView.templates.displayApps({displayApps:this.model.get("display_apps")}))}return a},_render_tagArea:function(){if(!this.urls.tags.set){return null}return $(HDAView.templates.tagArea(_.extend(this.model.toJSON(),{urls:this.urls})))},_render_annotationArea:function(){if(!this.urls.annotation.get){return null}return $(HDAView.templates.annotationArea(_.extend(this.model.toJSON(),{urls:this.urls})))},_render_peek:function(){if(!this.model.get("peek")){return null}return $("<div/>").append($("<pre/>").attr("id","peek"+this.model.get("id")).addClass("peek").append(this.model.get("peek")))},_render_body_not_viewable:function(a){a.append($("<div>You do not have permission to view dataset.</div>"))},_render_body_uploading:function(a){a.append($("<div>Dataset is uploading</div>"))},_render_body_queued:function(a){a.append($("<div>Job is waiting to run.</div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_running:function(a){a.append("<div>Job is currently running.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_error:function(a){if(!this.model.get("purged")){a.append($("<div>"+this.model.get("misc_blurb")+"</div>"))}a.append(("An error occurred running this job: <i>"+$.trim(this.model.get("misc_info"))+"</i>"));a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton]))},_render_body_discarded:function(a){a.append("<div>The job creating this dataset was cancelled before completion.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_setting_metadata:function(a){a.append($("<div>Metadata is being auto-detected.</div>"))},_render_body_empty:function(a){a.append($("<div>No data: <i>"+this.model.get("misc_blurb")+"</i></div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_failed_metadata:function(a){a.append($(HDAView.templates.failedMetadata(this.model.toJSON())));this._render_body_ok(a)},_render_body_ok:function(a){a.append(this._render_hdaSummary());if(this.model.isDeletedOrPurged()){a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_showParamsButton,this._render_rerunButton]));return}a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton,this._render_visualizationsButton]));a.append(this._render_secondaryActionButtons([this._render_tagButton,this._render_annotateButton]));a.append('<div class="clear"/>');a.append(this._render_tagArea());a.append(this._render_annotationArea());a.append(this._render_displayApps());a.append(this._render_peek())},_render_body:function(){var a=$("<div/>").attr("id","info-"+this.model.get("id")).addClass("historyItemBody").attr("style","display: block");switch(this.model.get("state")){case HistoryDatasetAssociation.STATES.NOT_VIEWABLE:this._render_body_not_viewable(a);break;case HistoryDatasetAssociation.STATES.UPLOAD:this._render_body_uploading(a);break;case HistoryDatasetAssociation.STATES.QUEUED:this._render_body_queued(a);break;case HistoryDatasetAssociation.STATES.RUNNING:this._render_body_running(a);break;case HistoryDatasetAssociation.STATES.ERROR:this._render_body_error(a);break;case HistoryDatasetAssociation.STATES.DISCARDED:this._render_body_discarded(a);break;case HistoryDatasetAssociation.STATES.SETTING_METADATA:this._render_body_setting_metadata(a);break;case HistoryDatasetAssociation.STATES.EMPTY:this._render_body_empty(a);break;case HistoryDatasetAssociation.STATES.FAILED_METADATA:this._render_body_failed_metadata(a);break;case HistoryDatasetAssociation.STATES.OK:this._render_body_ok(a);break;default:a.append($('<div>Error: unknown dataset state "'+state+'".</div>'))}a.append('<div style="clear: both"></div>');if(this.expanded){a.show()}else{a.hide()}return a},events:{"click .historyItemTitle":"toggleBodyVisibility","click a.icon-button.tags":"loadAndDisplayTags","click a.icon-button.annotate":"loadAndDisplayAnnotation"},loadAndDisplayTags:function(b){this.log(this+".loadAndDisplayTags",b);var c=this.$el.find(".tag-area"),a=c.find(".tag-elt");if(c.is(":hidden")){if(!jQuery.trim(a.html())){$.ajax({url:this.urls.tags.get,error:function(){alert("Tagging failed")},success:function(d){a.html(d);a.find(".tooltip").tooltip();c.slideDown("fast")}})}else{c.slideDown("fast")}}else{c.slideUp("fast")}return false},loadAndDisplayAnnotation:function(b){this.log(this+".loadAndDisplayAnnotation",b);var d=this.$el.find(".annotation-area"),c=d.find(".annotation-elt"),a=this.urls.annotation.set;if(d.is(":hidden")){if(!jQuery.trim(c.html())){$.ajax({url:this.urls.annotation.get,error:function(){alert("Annotations failed")},success:function(e){if(e===""){e="<em>Describe or add notes to dataset</em>"}c.html(e);d.find(".tooltip").tooltip();async_save_text(c.attr("id"),c.attr("id"),a,"new_annotation",18,true,4);d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toggleBodyVisibility:function(b,a){var c=this.$el.find(".historyItemBody");a=(a===undefined)?(!c.is(":visible")):(a);if(a){c.slideDown("fast")}else{c.slideUp("fast")}this.trigger("toggleBodyVisibility",this.model.get("id"),a)},toString:function(){var a=(this.model)?(this.model+""):("(no model)");return"HDAView("+a+")"}});HDAView.templates={warningMsg:Handlebars.templates["template-warningmessagesmall"],messages:Handlebars.templates["template-history-warning-messages"],titleLink:Handlebars.templates["template-history-titleLink"],hdaSummary:Handlebars.templates["template-history-hdaSummary"],downloadLinks:Handlebars.templates["template-history-downloadLinks"],failedMetadata:Handlebars.templates["template-history-failedMetaData"],tagArea:Handlebars.templates["template-history-tagArea"],annotationArea:Handlebars.templates["template-history-annotationArea"],displayApps:Handlebars.templates["template-history-displayApps"]};function create_scatterplot_action_fn(a,b){action=function(){var d=$(window.parent.document).find("iframe#galaxy_main"),c=a+"/scatterplot?"+$.param(b);d.attr("src",c);$("div.popmenu-wrapper").remove();return false};return action}function create_trackster_action_fn(a,c,b){return function(){var d={};if(b){d.dbkey=b}$.ajax({url:a+"/list_tracks?f-"+$.param(d),dataType:"html",error:function(){alert("Could not add this dataset to browser.")},success:function(e){var f=window.parent;f.show_modal("View Data in a New or Saved Visualization","",{Cancel:function(){f.hide_modal()},"View in saved visualization":function(){f.show_modal("Add Data to Saved Visualization",e,{Cancel:function(){f.hide_modal()},"Add to visualization":function(){$(f.document).find("input[name=id]:checked").each(function(){var g=$(this).val();c.id=g;f.location=a+"/trackster?"+$.param(c)})}})},"View in new visualization":function(){f.location=a+"/trackster?"+$.param(c)}})}});return false}}var HistoryView=BaseView.extend(LoggableMixin).extend({logger:console,el:"body.historyPage",initialize:function(a){this.log(this+".initialize:",a);if(!a.urlTemplates){throw ("HDAView needs urlTemplates on initialize")}if(!a.urlTemplates.history){throw ("HDAView needs urlTemplates.history on initialize")}if(!a.urlTemplates.hda){throw ("HDAView needs urlTemplates.hda on initialize")}this.urlTemplates=a.urlTemplates.history;this.hdaUrlTemplates=a.urlTemplates.hda;this.storage=new PersistantStorage("HistoryView."+this.model.get("id"),{expandedHdas:{}});this.model.hdas.bind("add",this.add,this);this.model.hdas.bind("reset",this.addAll,this);this.model.hdas.bind("all",this.all,this);this.hdaViews={};this.urls={}},add:function(a){},addAll:function(){this.render()},all:function(a){},renderUrls:function(a){var b=this;b.urls={};_.each(this.urlTemplates,function(d,c){b.urls[c]=_.template(d,a)});return b.urls},render:function(){var b=this,d=b.toString()+".set-up",c=$("<div/>"),a=this.model.toJSON();a.urls=this.renderUrls(a);c.append(HistoryView.templates.historyPanel(a));b.$el.find(".tooltip").tooltip();if(!this.model.hdas.length||!this.renderItems(c.find("#"+this.model.get("id")+"-datasets"))){c.find("#emptyHistoryMessage").show()}$(b).queue(d,function(e){b.$el.fadeOut("fast",function(){e()})});$(b).queue(d,function(e){b.$el.html("");b.$el.append(c.children());b.$el.fadeIn("fast",function(){e()})});$(b).queue(d,function(e){this.log(b+" rendered:",b.$el);b.setUpBehaviours();b.trigger("rendered");e()});$(b).dequeue(d);return this},renderItems:function(c){this.hdaViews={};var b=this,a=this.model.get("show_deleted"),e=this.model.get("show_hidden"),d=this.model.hdas.getVisible(a,e);d.each(function(h){var g=h.get("id"),f=b.storage.get("expandedHdas").get(g);b.hdaViews[g]=new HDAView({model:h,expanded:f,urlTemplates:b.hdaUrlTemplates});b.setUpHdaListeners(b.hdaViews[g]);c.prepend(b.hdaViews[g].render().$el)});return d.length},setUpHdaListeners:function(a){var b=this;a.bind("toggleBodyVisibility",function(d,c){if(c){b.storage.get("expandedHdas").set(d,true)}else{b.storage.get("expandedHdas").deleteKey(d)}})},setUpBehaviours:function(){var a=this.$("#history-annotation-area");this.$("#history-annotate").click(function(){if(a.is(":hidden")){a.slideDown("fast")}else{a.slideUp("fast")}return false});async_save_text("history-name-container","history-name",this.urls.rename,"new_name",18);async_save_text("history-annotation-container","history-annotation",this.urls.annotate,"new_annotation",18,true,4)},events:{"click #history-collapse-all":"hideAllHdaBodies","click #history-tag":"loadAndDisplayTags"},hideAllHdaBodies:function(){_.each(this.itemViews,function(a){a.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},loadAndDisplayTags:function(c){this.log(this+".loadAndDisplayTags",c);var d=this.$el.find("#history-tag-area"),b=d.find(".tag-elt");this.log("\t tagArea",d," tagElt",b);if(d.is(":hidden")){if(!jQuery.trim(b.html())){var a=this;$.ajax({url:a.urls.tag,error:function(){alert("Tagging failed")},success:function(e){b.html(e);b.find(".tooltip").tooltip();d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toString:function(){var a=this.model.get("name")||"";return"HistoryView("+a+")"}});HistoryView.templates={historyPanel:Handlebars.templates["template-history-historyPanel"]};
\ No newline at end of file
+var HistoryDatasetAssociation=BaseModel.extend(LoggableMixin).extend({defaults:{history_id:null,model_class:"HistoryDatasetAssociation",hid:0,id:null,name:"",state:"",data_type:null,file_size:0,meta_files:[],misc_blurb:"",misc_info:"",deleted:false,purged:false,visible:false,accessible:false,for_editing:true},url:function(){return"api/histories/"+this.get("history_id")+"/contents/"+this.get("id")},initialize:function(){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"));if(!this.get("accessible")){this.set("state",HistoryDatasetAssociation.STATES.NOT_VIEWABLE)}this.on("change:state",function(b,a){this.log(this+" has changed state:",b,a);if(this.inReadyState()){this.trigger("state:ready",this.get("id"),a,this.previous("state"),b)}})},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},isVisible:function(b,c){var a=true;if((!b)&&(this.get("deleted")||this.get("purged"))){a=false}if((!c)&&(!this.get("visible"))){a=false}return a},inReadyState:function(){var a=this.get("state");return((a===HistoryDatasetAssociation.STATES.NEW)||(a===HistoryDatasetAssociation.STATES.OK)||(a===HistoryDatasetAssociation.STATES.EMPTY)||(a===HistoryDatasetAssociation.STATES.FAILED_METADATA)||(a===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(a===HistoryDatasetAssociation.STATES.DISCARDED)||(a===HistoryDatasetAssociation.STATES.ERROR))},hasData:function(){return(this.get("file_size")>0)},toString:function(){var a=this.get("id")||"";if(this.get("name")){a+=':"'+this.get("name")+'"'}return"HistoryDatasetAssociation("+a+")"}});HistoryDatasetAssociation.STATES={UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",SETTING_METADATA:"setting_metadata",NEW:"new",OK:"ok",EMPTY:"empty",FAILED_METADATA:"failed_metadata",NOT_VIEWABLE:"noPermission",DISCARDED:"discarded",ERROR:"error"};var HDACollection=Backbone.Collection.extend(LoggableMixin).extend({model:HistoryDatasetAssociation,initialize:function(){},ids:function(){return this.map(function(a){return a.id})},getVisible:function(a,b){return this.filter(function(c){return c.isVisible(a,b)})},getStateLists:function(){var a={};_.each(_.values(HistoryDatasetAssociation.STATES),function(b){a[b]=[]});this.each(function(b){a[b.get("state")].push(b.get("id"))});return a},running:function(){var a=[];this.each(function(b){if(!b.inReadyState()){a.push(b.get("id"))}});return a},update:function(a){this.log(this+"update:",a);if(!(a&&a.length)){return}var b=this;_.each(a,function(e,c){var d=b.get(e);d.fetch()})},toString:function(){return("HDACollection("+this.ids().join(",")+")")}});var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",show_deleted:false,show_hidden:false,diskSize:0,deleted:false,tags:[],annotation:null,message:null,quotaMsg:false},url:function(){return"api/histories/"+this.get("id")},initialize:function(a,b){this.log(this+".initialize:",a,b);this.hdas=new HDACollection();if(b&&b.length){this.hdas.reset(b);this.checkForUpdates()}},loadFromApi:function(a,c){var b=this;b.attributes.id=a;jQuery.when(jQuery.ajax("api/users/current"),b.fetch()).then(function(e,d){b.attributes.user=e[0];b.log(b)}).then(function(){jQuery.ajax(b.url()+"/contents?"+jQuery.param({ids:b.itemIdsFromStateIds().join(",")})).success(function(d){b.hdas.reset(d);b.checkForUpdates();c()})})},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},checkForUpdates:function(a){if(this.hdas.running().length){this.stateUpdater()}return this},stateUpdater:function(){var c=this,a=this.get("state"),b=this.get("state_ids");jQuery.ajax("api/histories/"+this.get("id")).success(function(d){c.set(d);c.log("current history state:",c.get("state"),"(was)",a,"new size:",c.get("nice_size"));var e=[];_.each(_.keys(d.state_ids),function(g){var f=_.difference(d.state_ids[g],b[g]);e=e.concat(f)});if(e.length){c.hdas.update(e)}if((c.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(c.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){setTimeout(function(){c.stateUpdater()},4000)}}).error(function(f,d,e){if(console&&console.warn){console.warn("Error getting history updates from the server:",f,d,e)}alert("Error getting history updates from the server.\n"+e)})},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});var HDAView=BaseView.extend(LoggableMixin).extend({tagName:"div",className:"historyItemContainer",initialize:function(a){this.log(this+".initialize:",a);if(!a.urlTemplates){throw ("HDAView needs urlTemplates on initialize")}this.urls=this.renderUrls(a.urlTemplates,this.model.toJSON());this.expanded=a.expanded||false;this.model.bind("change",this.render,this)},renderUrls:function(d,a){var b=this,c={};_.each(d,function(e,f){if(_.isObject(e)){c[f]=b.renderUrls(e,a)}else{if(f==="meta_download"){c[f]=b.renderMetaDownloadUrls(e,a)}else{c[f]=_.template(e,a)}}});return c},renderMetaDownloadUrls:function(b,a){return _.map(a.meta_files,function(c){return{url:_.template(b,{id:a.id,file_type:c.file_type}),file_type:c.file_type}})},render:function(){var b=this,e=this.model.get("id"),c=this.model.get("state"),a=$("<div/>").attr("id","historyItem-"+e),d=(this.$el.children().size()===0);this._clearReferences();this.$el.attr("id","historyItemContainer-"+e);a.addClass("historyItemWrapper").addClass("historyItem").addClass("historyItem-"+c);a.append(this._render_warnings());a.append(this._render_titleBar());this.body=$(this._render_body());a.append(this.body);make_popup_menus(a);a.find(".tooltip").tooltip({placement:"bottom"});this.$el.fadeOut("fast",function(){b.$el.children().remove();b.$el.append(a).fadeIn("fast",function(){b.log(b+" rendered:",b.$el);var f="rendered";if(d){f+=":initial"}else{if(b.model.inReadyState()){f+=":ready"}}b.trigger(f)})});return this},_clearReferences:function(){this.displayButton=null;this.editButton=null;this.deleteButton=null;this.errButton=null;this.showParamsButton=null;this.rerunButton=null;this.visualizationsButton=null;this.tagButton=null;this.annotateButton=null},_render_warnings:function(){return $(jQuery.trim(HDAView.templates.messages(_.extend(this.model.toJSON(),{urls:this.urls}))))},_render_titleBar:function(){var a=$('<div class="historyItemTitleBar" style="overflow: hidden"></div>');a.append(this._render_titleButtons());a.append('<span class="state-icon"></span>');a.append(this._render_titleLink());return a},_render_titleButtons:function(){var a=$('<div class="historyItemButtons"></div>');a.append(this._render_displayButton());a.append(this._render_editButton());a.append(this._render_deleteButton());return a},_render_displayButton:function(){if((!this.model.inReadyState())||(this.model.get("state")===HistoryDatasetAssociation.STATES.ERROR)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var a={icon_class:"display"};if(this.model.get("purged")){a.enabled=false;a.title="Cannot display datasets removed from disk"}else{a.title="Display data in browser";a.href=this.urls.display}if(this.model.get("for_editing")){a.target="galaxy_main"}this.displayButton=new IconButtonView({model:new IconButton(a)});return this.displayButton.render().$el},_render_editButton:function(){if((this.model.get("state")===HistoryDatasetAssociation.STATES.UPLOAD)||(this.model.get("state")===HistoryDatasetAssociation.STATES.ERROR)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))||(!this.model.get("for_editing"))){return null}var c=this.model.get("purged"),a=this.model.get("deleted"),b={title:"Edit attributes",href:this.urls.edit,target:"galaxy_main",icon_class:"edit"};if(a||c){b.enabled=false;if(c){b.title="Cannot edit attributes of datasets removed from disk"}else{if(a){b.title="Undelete dataset to edit attributes"}}}this.editButton=new IconButtonView({model:new IconButton(b)});return this.editButton.render().$el},_render_deleteButton:function(){if((!this.model.get("for_editing"))||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var a={title:"Delete",href:this.urls["delete"],id:"historyItemDeleter-"+this.model.get("id"),icon_class:"delete"};if(this.model.get("deleted")||this.model.get("purged")){a={title:"Dataset is already deleted",icon_class:"delete",enabled:false}}this.deleteButton=new IconButtonView({model:new IconButton(a)});return this.deleteButton.render().$el},_render_titleLink:function(){return $(jQuery.trim(HDAView.templates.titleLink(_.extend(this.model.toJSON(),{urls:this.urls}))))},_render_hdaSummary:function(){var a=_.extend(this.model.toJSON(),{urls:this.urls});if(this.model.get("metadata_dbkey")==="?"&&!this.model.isDeletedOrPurged()){_.extend(a,{dbkey_unknown_and_editable:true})}return HDAView.templates.hdaSummary(a)},_render_primaryActionButtons:function(c){var b=$("<div/>").attr("id","primary-actions-"+this.model.get("id")),a=this;_.each(c,function(d){b.append(d.call(a))});return b},_render_downloadButton:function(){if(this.model.get("purged")){return null}var a=HDAView.templates.downloadLinks(_.extend(this.model.toJSON(),{urls:this.urls}));return $(a)},_render_errButton:function(){if((this.model.get("state")!==HistoryDatasetAssociation.STATES.ERROR)||(!this.model.get("for_editing"))){return null}this.errButton=new IconButtonView({model:new IconButton({title:"View or report this error",href:this.urls.report_error,target:"galaxy_main",icon_class:"bug"})});return this.errButton.render().$el},_render_showParamsButton:function(){this.showParamsButton=new IconButtonView({model:new IconButton({title:"View details",href:this.urls.show_params,target:"galaxy_main",icon_class:"information"})});return this.showParamsButton.render().$el},_render_rerunButton:function(){if(!this.model.get("for_editing")){return null}this.rerunButton=new IconButtonView({model:new IconButton({title:"Run this job again",href:this.urls.rerun,target:"galaxy_main",icon_class:"arrow-circle"})});return this.rerunButton.render().$el},_render_visualizationsButton:function(){var c=this.model.get("dbkey"),a=this.model.get("visualizations"),f=this.urls.visualization,d={},g={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(!(this.model.hasData())||!(this.model.get("for_editing"))||!(a&&a.length)||!(f)){return null}this.visualizationsButton=new IconButtonView({model:new IconButton({title:"Visualize",href:f,icon_class:"chart_curve"})});var b=this.visualizationsButton.render().$el;b.addClass("visualize-icon");if(c){g.dbkey=c}function e(h){switch(h){case"trackster":return create_trackster_action_fn(f,g,c);case"scatterplot":return create_scatterplot_action_fn(f,g);default:return function(){window.parent.location=f+"/"+h+"?"+$.param(g)}}}if(a.length===1){b.attr("title",a[0]);b.click(e(a[0]))}else{_.each(a,function(i){var h=i.charAt(0).toUpperCase()+i.slice(1);d[h]=e(i)});make_popupmenu(b,d)}return b},_render_secondaryActionButtons:function(b){var c=$("<div/>"),a=this;c.attr("style","float: right;").attr("id","secondary-actions-"+this.model.get("id"));_.each(b,function(d){c.append(d.call(a))});return c},_render_tagButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.urls.tags.get)){return null}this.tagButton=new IconButtonView({model:new IconButton({title:"Edit dataset tags",target:"galaxy_main",href:this.urls.tags.get,icon_class:"tags"})});return this.tagButton.render().$el},_render_annotateButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.urls.annotation.get)){return null}this.annotateButton=new IconButtonView({model:new IconButton({title:"Edit dataset annotation",target:"galaxy_main",icon_class:"annotate"})});return this.annotateButton.render().$el},_render_displayApps:function(){if(!this.model.hasData()){return null}var a=$("<div/>").addClass("display-apps");if(!_.isEmpty(this.model.get("display_types"))){a.append(HDAView.templates.displayApps({displayApps:this.model.get("display_types")}))}if(!_.isEmpty(this.model.get("display_apps"))){a.append(HDAView.templates.displayApps({displayApps:this.model.get("display_apps")}))}return a},_render_tagArea:function(){if(!this.urls.tags.set){return null}return $(HDAView.templates.tagArea(_.extend(this.model.toJSON(),{urls:this.urls})))},_render_annotationArea:function(){if(!this.urls.annotation.get){return null}return $(HDAView.templates.annotationArea(_.extend(this.model.toJSON(),{urls:this.urls})))},_render_peek:function(){if(!this.model.get("peek")){return null}return $("<div/>").append($("<pre/>").attr("id","peek"+this.model.get("id")).addClass("peek").append(this.model.get("peek")))},_render_body_not_viewable:function(a){a.append($("<div>You do not have permission to view dataset.</div>"))},_render_body_uploading:function(a){a.append($("<div>Dataset is uploading</div>"))},_render_body_queued:function(a){a.append($("<div>Job is waiting to run.</div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_running:function(a){a.append("<div>Job is currently running.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_error:function(a){if(!this.model.get("purged")){a.append($("<div>"+this.model.get("misc_blurb")+"</div>"))}a.append(("An error occurred running this job: <i>"+$.trim(this.model.get("misc_info"))+"</i>"));a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton]))},_render_body_discarded:function(a){a.append("<div>The job creating this dataset was cancelled before completion.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_setting_metadata:function(a){a.append($("<div>Metadata is being auto-detected.</div>"))},_render_body_empty:function(a){a.append($("<div>No data: <i>"+this.model.get("misc_blurb")+"</i></div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_failed_metadata:function(a){a.append($(HDAView.templates.failedMetadata(this.model.toJSON())));this._render_body_ok(a)},_render_body_ok:function(a){a.append(this._render_hdaSummary());if(this.model.isDeletedOrPurged()){a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_showParamsButton,this._render_rerunButton]));return}a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton,this._render_visualizationsButton]));a.append(this._render_secondaryActionButtons([this._render_tagButton,this._render_annotateButton]));a.append('<div class="clear"/>');a.append(this._render_tagArea());a.append(this._render_annotationArea());a.append(this._render_displayApps());a.append(this._render_peek())},_render_body:function(){var a=$("<div/>").attr("id","info-"+this.model.get("id")).addClass("historyItemBody").attr("style","display: block");switch(this.model.get("state")){case HistoryDatasetAssociation.STATES.NOT_VIEWABLE:this._render_body_not_viewable(a);break;case HistoryDatasetAssociation.STATES.UPLOAD:this._render_body_uploading(a);break;case HistoryDatasetAssociation.STATES.QUEUED:this._render_body_queued(a);break;case HistoryDatasetAssociation.STATES.RUNNING:this._render_body_running(a);break;case HistoryDatasetAssociation.STATES.ERROR:this._render_body_error(a);break;case HistoryDatasetAssociation.STATES.DISCARDED:this._render_body_discarded(a);break;case HistoryDatasetAssociation.STATES.SETTING_METADATA:this._render_body_setting_metadata(a);break;case HistoryDatasetAssociation.STATES.EMPTY:this._render_body_empty(a);break;case HistoryDatasetAssociation.STATES.FAILED_METADATA:this._render_body_failed_metadata(a);break;case HistoryDatasetAssociation.STATES.OK:this._render_body_ok(a);break;default:a.append($('<div>Error: unknown dataset state "'+state+'".</div>'))}a.append('<div style="clear: both"></div>');if(this.expanded){a.show()}else{a.hide()}return a},events:{"click .historyItemTitle":"toggleBodyVisibility","click a.icon-button.tags":"loadAndDisplayTags","click a.icon-button.annotate":"loadAndDisplayAnnotation"},loadAndDisplayTags:function(b){this.log(this+".loadAndDisplayTags",b);var c=this.$el.find(".tag-area"),a=c.find(".tag-elt");if(c.is(":hidden")){if(!jQuery.trim(a.html())){$.ajax({url:this.urls.tags.get,error:function(){alert("Tagging failed")},success:function(d){a.html(d);a.find(".tooltip").tooltip();c.slideDown("fast")}})}else{c.slideDown("fast")}}else{c.slideUp("fast")}return false},loadAndDisplayAnnotation:function(b){this.log(this+".loadAndDisplayAnnotation",b);var d=this.$el.find(".annotation-area"),c=d.find(".annotation-elt"),a=this.urls.annotation.set;if(d.is(":hidden")){if(!jQuery.trim(c.html())){$.ajax({url:this.urls.annotation.get,error:function(){alert("Annotations failed")},success:function(e){if(e===""){e="<em>Describe or add notes to dataset</em>"}c.html(e);d.find(".tooltip").tooltip();async_save_text(c.attr("id"),c.attr("id"),a,"new_annotation",18,true,4);d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toggleBodyVisibility:function(b,a){var c=this.$el.find(".historyItemBody");a=(a===undefined)?(!c.is(":visible")):(a);if(a){c.slideDown("fast")}else{c.slideUp("fast")}this.trigger("toggleBodyVisibility",this.model.get("id"),a)},toString:function(){var a=(this.model)?(this.model+""):("(no model)");return"HDAView("+a+")"}});HDAView.templates={warningMsg:Handlebars.templates["template-warningmessagesmall"],messages:Handlebars.templates["template-history-warning-messages"],titleLink:Handlebars.templates["template-history-titleLink"],hdaSummary:Handlebars.templates["template-history-hdaSummary"],downloadLinks:Handlebars.templates["template-history-downloadLinks"],failedMetadata:Handlebars.templates["template-history-failedMetaData"],tagArea:Handlebars.templates["template-history-tagArea"],annotationArea:Handlebars.templates["template-history-annotationArea"],displayApps:Handlebars.templates["template-history-displayApps"]};function create_scatterplot_action_fn(a,b){action=function(){var d=$(window.parent.document).find("iframe#galaxy_main"),c=a+"/scatterplot?"+$.param(b);d.attr("src",c);$("div.popmenu-wrapper").remove();return false};return action}function create_trackster_action_fn(a,c,b){return function(){var d={};if(b){d.dbkey=b}$.ajax({url:a+"/list_tracks?f-"+$.param(d),dataType:"html",error:function(){alert("Could not add this dataset to browser.")},success:function(e){var f=window.parent;f.show_modal("View Data in a New or Saved Visualization","",{Cancel:function(){f.hide_modal()},"View in saved visualization":function(){f.show_modal("Add Data to Saved Visualization",e,{Cancel:function(){f.hide_modal()},"Add to visualization":function(){$(f.document).find("input[name=id]:checked").each(function(){var g=$(this).val();c.id=g;f.location=a+"/trackster?"+$.param(c)})}})},"View in new visualization":function(){f.location=a+"/trackster?"+$.param(c)}})}});return false}}var HistoryView=BaseView.extend(LoggableMixin).extend({el:"body.historyPage",initialize:function(a){this.log(this+".initialize:",a);if(!a.urlTemplates){throw ("HDAView needs urlTemplates on initialize")}if(!a.urlTemplates.history){throw ("HDAView needs urlTemplates.history on initialize")}if(!a.urlTemplates.hda){throw ("HDAView needs urlTemplates.hda on initialize")}this.urlTemplates=a.urlTemplates.history;this.hdaUrlTemplates=a.urlTemplates.hda;this.storage=new PersistantStorage("HistoryView."+this.model.get("id"),{expandedHdas:{}});this.model.bind("change:nice_size",this.updateHistoryDiskSize,this);this.model.hdas.bind("add",this.add,this);this.model.hdas.bind("reset",this.addAll,this);this.model.hdas.bind("all",this.all,this);this.hdaViews={};this.urls={}},add:function(a){},addAll:function(){this.render()},all:function(a){},renderUrls:function(a){var b=this;b.urls={};_.each(this.urlTemplates,function(d,c){b.urls[c]=_.template(d,a)});return b.urls},render:function(){var b=this,d=b.toString()+".set-up",c=$("<div/>"),a=this.model.toJSON(),e=(this.$el.children().size()===0);a.urls=this.renderUrls(a);c.append(HistoryView.templates.historyPanel(a));c.find(".tooltip").tooltip();if(!this.model.hdas.length||!this.renderItems(c.find("#"+this.model.get("id")+"-datasets"))){c.find("#emptyHistoryMessage").show()}$(b).queue(d,function(f){b.$el.fadeOut("fast",function(){f()})});$(b).queue(d,function(f){b.$el.html("");b.$el.append(c.children());b.$el.fadeIn("fast",function(){f()})});$(b).queue(d,function(f){this.log(b+" rendered:",b.$el);b.setUpBehaviours();if(e){b.trigger("rendered:initial")}else{b.trigger("rendered")}f()});$(b).dequeue(d);return this},renderItems:function(c){this.hdaViews={};var b=this,a=this.model.get("show_deleted"),e=this.model.get("show_hidden"),d=this.model.hdas.getVisible(a,e);_.each(d,function(h){var g=h.get("id"),f=b.storage.get("expandedHdas").get(g);b.hdaViews[g]=new HDAView({model:h,expanded:f,urlTemplates:b.hdaUrlTemplates});b.setUpHdaListeners(b.hdaViews[g]);c.prepend(b.hdaViews[g].render().$el)});return d.length},setUpHdaListeners:function(b){var a=this;b.bind("toggleBodyVisibility",function(d,c){if(c){a.storage.get("expandedHdas").set(d,true)}else{a.storage.get("expandedHdas").deleteKey(d)}});b.bind("rendered:ready",function(){a.trigger("hda:rendered:ready")})},setUpBehaviours:function(){if(!(this.model.get("user")&&this.model.get("user").email)){return}var a=this.$("#history-annotation-area");this.$("#history-annotate").click(function(){if(a.is(":hidden")){a.slideDown("fast")}else{a.slideUp("fast")}return false});async_save_text("history-name-container","history-name",this.urls.rename,"new_name",18);async_save_text("history-annotation-container","history-annotation",this.urls.annotate,"new_annotation",18,true,4)},updateHistoryDiskSize:function(){this.$el.find("#history-size").text(this.model.get("nice_size"))},showQuotaMessage:function(a){var b=this.$el.find("#quota-message-container");if(b.is(":hidden")){b.slideDown("fast")}},hideQuotaMessage:function(a){var b=this.$el.find("#quota-message-container");if(!b.is(":hidden")){b.slideUp("fast")}},events:{"click #history-collapse-all":"hideAllHdaBodies","click #history-tag":"loadAndDisplayTags"},hideAllHdaBodies:function(){_.each(this.hdaViews,function(a){a.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},loadAndDisplayTags:function(c){this.log(this+".loadAndDisplayTags",c);var d=this.$el.find("#history-tag-area"),b=d.find(".tag-elt");this.log("\t tagArea",d," tagElt",b);if(d.is(":hidden")){if(!jQuery.trim(b.html())){var a=this;$.ajax({url:a.urls.tag,error:function(){alert("Tagging failed")},success:function(e){b.html(e);b.find(".tooltip").tooltip();d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toString:function(){var a=this.model.get("name")||"";return"HistoryView("+a+")"}});HistoryView.templates={historyPanel:Handlebars.templates["template-history-historyPanel"]};
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/mvc/user/user-model.js
--- /dev/null
+++ b/static/scripts/packed/mvc/user/user-model.js
@@ -0,0 +1,1 @@
+var User=BaseModel.extend(LoggableMixin).extend({defaults:{id:null,username:"(anonymous user)",email:"",total_disk_usage:0,nice_total_disk_usage:"0 bytes"},initialize:function(a){this.log("User.initialize:",a);this.on("loaded",function(b,c){this.log(this+" has loaded:",b,c)});this.on("change",function(b,c){this.log(this+" has changed:",b,c.changes)})},urlRoot:"api/users",loadFromApi:function(d,b){d=d||User.CURRENT_ID_STR;b=b||{};var a=this,c=b.success;b.success=function(f,e){a.trigger("loaded",f,e);if(c){c(f,e)}};if(d===User.CURRENT_ID_STR){b.url=this.urlRoot+"/"+User.CURRENT_ID_STR}return BaseModel.prototype.fetch.call(this,b)},toString:function(){var a=[this.get("username")];if(this.get("id")){a.unshift(this.get("id"));a.push(this.get("email"))}return"User("+a.join(":")+")"}});User.CURRENT_ID_STR="current";User.getCurrentUserFromApi=function(b){var a=new User();a.loadFromApi(User.CURRENT_ID_STR,b);return a};var UserCollection=Backbone.Collection.extend(LoggableMixin).extend({model:User,logger:console,urlRoot:"api/users"});
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/mvc/user/user-quotameter.js
--- /dev/null
+++ b/static/scripts/packed/mvc/user/user-quotameter.js
@@ -0,0 +1,1 @@
+var UserQuotaMeter=BaseView.extend(LoggableMixin).extend({options:{warnAtPercent:85,errorAtPercent:100,meterDocument:window.top.document,containerSelector:".quota-meter-container",meterSelector:"#quota-meter",barSelector:"#quota-meter-bar",textSelector:"#quota-meter-text",msgDocument:(top.frames.galaxy_history)?(top.frames.galaxy_history.document):(top.document),msgSelector:"#quota-message-container",warnClass:"quota-meter-bar-warn",errorClass:"quota-meter-bar-error",usageTemplate:"Using <%= nice_total_disk_usage %>",quotaTemplate:"Using <%= quota_percent %>%",meterTemplate:"",animationSpeed:"fast"},initialize:function(a){this.log(this+".initialize:",a);_.extend(this.options,a);this.model.bind("change:quota_percent change:total_disk_usage",this.render,this)},update:function(a){this.log(this+" updating user data...",a);this.model.loadFromApi(this.model.get("id"),a);return this},isOverQuota:function(){return(this.model.get("quota_percent")!==null&&this.model.get("quota_percent")>=this.options.errorAtPercent)},_render_quota:function(){var a=this.model.toJSON(),b=a.quota_percent,c=$(UserQuotaMeter.templates.quota(a));if(this.isOverQuota()){c.addClass("progress-danger");c.find("#quota-meter-text").css("color","white");this.trigger("quota:over",a)}else{if(b>=this.options.warnAtPercent){c.addClass("progress-warning");this.trigger("quota:under quota:under:approaching",a)}else{c.addClass("progress-success");this.trigger("quota:under quota:under:ok",a)}}return c},_render_usage:function(){var a=$(UserQuotaMeter.templates.usage(this.model.toJSON()));this.log(this+".rendering usage:",a);return a},render:function(){var a=null;this.log(this+".model.quota_percent:",this.model.get("quota_percent"));if((this.model.get("quota_percent")===null)||(this.model.get("quota_percent")===undefined)){a=this._render_usage()}else{a=this._render_quota()}this.$el.html(a);return this},toString:function(){return"UserQuotaMeter("+this.model+")"}});UserQuotaMeter.templates={quota:Handlebars.templates["template-user-quotaMeter-quota"],usage:Handlebars.templates["template-user-quotaMeter-usage"]};
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/templates/compiled/template-history-historyPanel.js
--- a/static/scripts/packed/templates/compiled/template-history-historyPanel.js
+++ b/static/scripts/packed/templates/compiled/template-history-historyPanel.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(k,y,w,r,G){w=w||k.helpers;var x="",n,m,f="function",e=this.escapeExpression,u=this,c=w.blockHelperMissing;function t(L,K){var I="",J,H;I+='\n <div id="history-name" style="margin-right: 50px;" class="tooltip editable-text"\n title="Click to rename history">';H=w.name;if(H){J=H.call(L,{hash:{}})}else{J=L.name;J=typeof J===f?J():J}I+=e(J)+"</div>\n ";return I}function s(I,H){return"refresh"}function q(I,H){return"collapse all"}function p(L,K){var I="",J,H;I+='\n <a id="history-tag" title="';H=w.local;if(H){J=H.call(L,{hash:{},inverse:u.noop,fn:u.program(8,l,K)})}else{J=L.local;J=typeof J===f?J():J}if(!w.local){J=c.call(L,J,{hash:{},inverse:u.noop,fn:u.program(8,l,K)})}if(J||J===0){I+=J}I+='"\n class="icon-button tags tooltip" target="galaxy_main" href="javascript:void(0)"></a>\n <a id="history-annotate" title="';H=w.local;if(H){J=H.call(L,{hash:{},inverse:u.noop,fn:u.program(10,F,K)})}else{J=L.local;J=typeof J===f?J():J}if(!w.local){J=c.call(L,J,{hash:{},inverse:u.noop,fn:u.program(10,F,K)})}if(J||J===0){I+=J}I+='"\n class="icon-button annotate tooltip" target="galaxy_main" href="javascript:void(0)"></a>\n ';return I}function l(I,H){return"Edit history tags"}function F(I,H){return"Edit history annotation"}function E(L,K){var I="",J,H;I+='\n <a href="';J=L.urls;J=J==null||J===false?J:J.hide_deleted;J=typeof J===f?J():J;I+=e(J)+'">';H=w.local;if(H){J=H.call(L,{hash:{},inverse:u.noop,fn:u.program(13,D,K)})}else{J=L.local;J=typeof J===f?J():J}if(!w.local){J=c.call(L,J,{hash:{},inverse:u.noop,fn:u.program(13,D,K)})}if(J||J===0){I+=J}I+="</a>\n ";return I}function D(I,H){return"hide deleted"}function C(L,K){var I="",J,H;I+='\n <a href="';J=L.urls;J=J==null||J===false?J:J.hide_hidden;J=typeof J===f?J():J;I+=e(J)+'">';H=w.local;if(H){J=H.call(L,{hash:{},inverse:u.noop,fn:u.program(16,B,K)})}else{J=L.local;J=typeof J===f?J():J}if(!w.local){J=c.call(L,J,{hash:{},inverse:u.noop,fn:u.program(16,B,K)})}if(J||J===0){I+=J}I+="</a>\n ";return I}function B(I,H){return"hide hidden"}function A(L,K){var I="",J,H;I+="\n";H=w.warningmessagesmall;if(H){J=H.call(L,{hash:{},inverse:u.noop,fn:u.program(19,z,K)})}else{J=L.warningmessagesmall;J=typeof J===f?J():J}if(!w.warningmessagesmall){J=c.call(L,J,{hash:{},inverse:u.noop,fn:u.program(19,z,K)})}if(J||J===0){I+=J}I+="\n";return I}function z(K,J){var I,H;H=w.local;if(H){I=H.call(K,{hash:{},inverse:u.noop,fn:u.program(20,o,J)})}else{I=K.local;I=typeof I===f?I():I}if(!w.local){I=c.call(K,I,{hash:{},inverse:u.noop,fn:u.program(20,o,J)})}if(I||I===0){return I}else{return""}}function o(I,H){return"You are currently viewing a deleted history!"}function j(K,J){var H="",I;H+='\n <div id="history-tag-area" style="display: none">\n <strong>Tags:</strong>\n <div class="tag-elt"></div>\n </div>\n\n <div id="history-annotation-area" style="display: none">\n <strong>Annotation / Notes:</strong>\n <div id="history-annotation-container">\n <div id="history-annotation" class="tooltip editable-text" title="Click to edit annotation">\n ';I=K.annotation;I=w["if"].call(K,I,{hash:{},inverse:u.program(25,h,J),fn:u.program(23,i,J)});if(I||I===0){H+=I}H+="\n </div>\n </div>\n </div>\n ";return H}function i(L,K){var I="",J,H;I+="\n ";H=w.annotation;if(H){J=H.call(L,{hash:{}})}else{J=L.annotation;J=typeof J===f?J():J}I+=e(J)+"\n ";return I}function h(I,H){return"\n <em>Describe or add notes to history</em>\n "}function g(L,K){var I="",J,H;I+='\n<div id="message-container">\n <div class="';H=w.status;if(H){J=H.call(L,{hash:{}})}else{J=L.status;J=typeof J===f?J():J}I+=e(J)+'message">\n ';H=w.message;if(H){J=H.call(L,{hash:{}})}else{J=L.message;J=typeof J===f?J():J}I+=e(J)+"\n </div><br />\n</div>\n";return I}function d(I,H){return'\n <div id="quota-message" class="errormessage">\n You are over your disk quota. Tool execution is on hold until your disk usage drops below your allocated quota.\n </div>\n <br/>\n '}function v(I,H){return"Your history is empty. Click 'Get Data' on the left pane to start"}x+='\n<div id="history-name-area" class="historyLinks">\n <div id="history-name-container" style="position: relative;">\n ';x+='\n <div id="history-size" style="position: absolute; top: 3px; right: 0px;">';m=w.nice_size;if(m){n=m.call(y,{hash:{}})}else{n=y.nice_size;n=typeof n===f?n():n}x+=e(n)+"</div>\n ";n=y.user;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(1,t,G)});if(n||n===0){x+=n}x+='\n </div> \n</div>\n<div style="clear: both;"></div>\n\n<div id="top-links" class="historyLinks">\n <a title="';m=w.local;if(m){n=m.call(y,{hash:{},inverse:u.noop,fn:u.program(3,s,G)})}else{n=y.local;n=typeof n===f?n():n}if(!w.local){n=c.call(y,n,{hash:{},inverse:u.noop,fn:u.program(3,s,G)})}if(n||n===0){x+=n}x+='" class="icon-button arrow-circle tooltip" href="';n=y.urls;n=n==null||n===false?n:n.base;n=typeof n===f?n():n;x+=e(n)+"\"></a>\n <a title='";m=w.local;if(m){n=m.call(y,{hash:{},inverse:u.noop,fn:u.program(5,q,G)})}else{n=y.local;n=typeof n===f?n():n}if(!w.local){n=c.call(y,n,{hash:{},inverse:u.noop,fn:u.program(5,q,G)})}if(n||n===0){x+=n}x+="' id=\"history-collapse-all\"\n class='icon-button toggle tooltip' href='javascript:void(0);'></a>\n <div style=\"width: 40px; float: right; white-space: nowrap;\">\n ";n=y.user;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(7,p,G)});if(n||n===0){x+=n}x+='\n </div>\n</div>\n<div style="clear: both;"></div>\n\n';x+='\n<div class="historyLinks">\n ';n=y.show_deleted;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(12,E,G)});if(n||n===0){x+=n}x+="\n ";n=y.show_hidden;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(15,C,G)});if(n||n===0){x+=n}x+="\n</div>\n\n";n=y.deleted;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(18,A,G)});if(n||n===0){x+=n}x+="\n\n";x+="\n";x+='\n<div style="margin: 0px 5px 10px 5px">\n\n ';n=y.user;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(22,j,G)});if(n||n===0){x+=n}x+="\n</div>\n\n";n=y.message;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(27,g,G)});if(n||n===0){x+=n}x+='\n\n<div id="quota-message-container">\n ';n=y.over_quota;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(29,d,G)});if(n||n===0){x+=n}x+='\n</div>\n\n<div id="';m=w.id;if(m){n=m.call(y,{hash:{}})}else{n=y.id;n=typeof n===f?n():n}x+=e(n)+'-datasets" class="history-datasets-list"></div>\n\n<div class="infomessagesmall" id="emptyHistoryMessage" style="display: none;">\n ';m=w.local;if(m){n=m.call(y,{hash:{},inverse:u.noop,fn:u.program(31,v,G)})}else{n=y.local;n=typeof n===f?n():n}if(!w.local){n=c.call(y,n,{hash:{},inverse:u.noop,fn:u.program(31,v,G)})}if(n||n===0){x+=n}x+="\n</div>";return x})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(l,z,x,s,G){x=x||l.helpers;var y="",o,n,f="function",e=this.escapeExpression,v=this,c=x.blockHelperMissing;function u(L,K){var I="",J,H;I+='\n <div id="history-name" style="margin-right: 50px;" class="tooltip editable-text"\n title="Click to rename history">';H=x.name;if(H){J=H.call(L,{hash:{}})}else{J=L.name;J=typeof J===f?J():J}I+=e(J)+"</div>\n ";return I}function t(L,K){var I="",J,H;I+='\n <div id="history-name" style="margin-right: 50px;" class="tooltip"\n title="You must be logged in to edit your history name">';H=x.name;if(H){J=H.call(L,{hash:{}})}else{J=L.name;J=typeof J===f?J():J}I+=e(J)+"</div>\n ";return I}function r(I,H){return"refresh"}function q(I,H){return"collapse all"}function k(L,K){var I="",J,H;I+='\n <a id="history-tag" title="';H=x.local;if(H){J=H.call(L,{hash:{},inverse:v.noop,fn:v.program(10,F,K)})}else{J=L.local;J=typeof J===f?J():J}if(!x.local){J=c.call(L,J,{hash:{},inverse:v.noop,fn:v.program(10,F,K)})}if(J||J===0){I+=J}I+='"\n class="icon-button tags tooltip" target="galaxy_main" href="javascript:void(0)"></a>\n <a id="history-annotate" title="';H=x.local;if(H){J=H.call(L,{hash:{},inverse:v.noop,fn:v.program(12,E,K)})}else{J=L.local;J=typeof J===f?J():J}if(!x.local){J=c.call(L,J,{hash:{},inverse:v.noop,fn:v.program(12,E,K)})}if(J||J===0){I+=J}I+='"\n class="icon-button annotate tooltip" target="galaxy_main" href="javascript:void(0)"></a>\n ';return I}function F(I,H){return"Edit history tags"}function E(I,H){return"Edit history annotation"}function D(L,K){var I="",J,H;I+='\n <a href="';J=L.urls;J=J==null||J===false?J:J.hide_deleted;J=typeof J===f?J():J;I+=e(J)+'">';H=x.local;if(H){J=H.call(L,{hash:{},inverse:v.noop,fn:v.program(15,C,K)})}else{J=L.local;J=typeof J===f?J():J}if(!x.local){J=c.call(L,J,{hash:{},inverse:v.noop,fn:v.program(15,C,K)})}if(J||J===0){I+=J}I+="</a>\n ";return I}function C(I,H){return"hide deleted"}function B(L,K){var I="",J,H;I+='\n <a href="';J=L.urls;J=J==null||J===false?J:J.hide_hidden;J=typeof J===f?J():J;I+=e(J)+'">';H=x.local;if(H){J=H.call(L,{hash:{},inverse:v.noop,fn:v.program(18,A,K)})}else{J=L.local;J=typeof J===f?J():J}if(!x.local){J=c.call(L,J,{hash:{},inverse:v.noop,fn:v.program(18,A,K)})}if(J||J===0){I+=J}I+="</a>\n ";return I}function A(I,H){return"hide hidden"}function p(L,K){var I="",J,H;I+="\n";H=x.warningmessagesmall;if(H){J=H.call(L,{hash:{},inverse:v.noop,fn:v.program(21,m,K)})}else{J=L.warningmessagesmall;J=typeof J===f?J():J}if(!x.warningmessagesmall){J=c.call(L,J,{hash:{},inverse:v.noop,fn:v.program(21,m,K)})}if(J||J===0){I+=J}I+="\n";return I}function m(K,J){var I,H;H=x.local;if(H){I=H.call(K,{hash:{},inverse:v.noop,fn:v.program(22,j,J)})}else{I=K.local;I=typeof I===f?I():I}if(!x.local){I=c.call(K,I,{hash:{},inverse:v.noop,fn:v.program(22,j,J)})}if(I||I===0){return I}else{return""}}function j(I,H){return"You are currently viewing a deleted history!"}function i(K,J){var H="",I;H+='\n <div id="history-tag-area" style="display: none">\n <strong>Tags:</strong>\n <div class="tag-elt"></div>\n </div>\n\n <div id="history-annotation-area" style="display: none">\n <strong>Annotation / Notes:</strong>\n <div id="history-annotation-container">\n <div id="history-annotation" class="tooltip editable-text" title="Click to edit annotation">\n ';I=K.annotation;I=x["if"].call(K,I,{hash:{},inverse:v.program(27,g,J),fn:v.program(25,h,J)});if(I||I===0){H+=I}H+="\n </div>\n </div>\n </div>\n ";return H}function h(L,K){var I="",J,H;I+="\n ";H=x.annotation;if(H){J=H.call(L,{hash:{}})}else{J=L.annotation;J=typeof J===f?J():J}I+=e(J)+"\n ";return I}function g(I,H){return"\n <em>Describe or add notes to history</em>\n "}function d(L,K){var I="",J,H;I+='\n<div id="message-container">\n <div class="';H=x.status;if(H){J=H.call(L,{hash:{}})}else{J=L.status;J=typeof J===f?J():J}I+=e(J)+'message">\n ';H=x.message;if(H){J=H.call(L,{hash:{}})}else{J=L.message;J=typeof J===f?J():J}I+=e(J)+"\n </div><br />\n</div>\n";return I}function w(I,H){return"Your history is empty. Click 'Get Data' on the left pane to start"}y+='\n<div id="history-name-area" class="historyLinks">\n <div id="history-name-container" style="position: relative;">\n ';y+='\n <div id="history-size" style="position: absolute; top: 3px; right: 0px;">';n=x.nice_size;if(n){o=n.call(z,{hash:{}})}else{o=z.nice_size;o=typeof o===f?o():o}y+=e(o)+"</div>\n ";o=z.user;o=o==null||o===false?o:o.email;o=x["if"].call(z,o,{hash:{},inverse:v.program(3,t,G),fn:v.program(1,u,G)});if(o||o===0){y+=o}y+='\n </div> \n</div>\n<div style="clear: both;"></div>\n\n<div id="top-links" class="historyLinks">\n <a title="';n=x.local;if(n){o=n.call(z,{hash:{},inverse:v.noop,fn:v.program(5,r,G)})}else{o=z.local;o=typeof o===f?o():o}if(!x.local){o=c.call(z,o,{hash:{},inverse:v.noop,fn:v.program(5,r,G)})}if(o||o===0){y+=o}y+='" class="icon-button arrow-circle tooltip" href="';o=z.urls;o=o==null||o===false?o:o.base;o=typeof o===f?o():o;y+=e(o)+"\"></a>\n <a title='";n=x.local;if(n){o=n.call(z,{hash:{},inverse:v.noop,fn:v.program(7,q,G)})}else{o=z.local;o=typeof o===f?o():o}if(!x.local){o=c.call(z,o,{hash:{},inverse:v.noop,fn:v.program(7,q,G)})}if(o||o===0){y+=o}y+="' id=\"history-collapse-all\"\n class='icon-button toggle tooltip' href='javascript:void(0);'></a>\n <div style=\"width: 40px; float: right; white-space: nowrap;\">\n ";o=z.user;o=o==null||o===false?o:o.email;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(9,k,G)});if(o||o===0){y+=o}y+='\n </div>\n</div>\n<div style="clear: both;"></div>\n\n';y+='\n<div class="historyLinks">\n ';o=z.show_deleted;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(14,D,G)});if(o||o===0){y+=o}y+="\n ";o=z.show_hidden;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(17,B,G)});if(o||o===0){y+=o}y+="\n</div>\n\n";o=z.deleted;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(20,p,G)});if(o||o===0){y+=o}y+="\n\n";y+="\n";y+='\n<div style="margin: 0px 5px 10px 5px">\n\n ';o=z.user;o=o==null||o===false?o:o.email;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(24,i,G)});if(o||o===0){y+=o}y+="\n</div>\n\n";o=z.message;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(29,d,G)});if(o||o===0){y+=o}y+='\n\n<div id="quota-message-container" style="display: none">\n <div id="quota-message" class="errormessage">\n You are over your disk quota. Tool execution is on hold until your disk usage drops below your allocated quota.\n </div>\n</div>\n\n<div id="';n=x.id;if(n){o=n.call(z,{hash:{}})}else{o=z.id;o=typeof o===f?o():o}y+=e(o)+'-datasets" class="history-datasets-list"></div>\n\n<div class="infomessagesmall" id="emptyHistoryMessage" style="display: none;">\n ';n=x.local;if(n){o=n.call(z,{hash:{},inverse:v.noop,fn:v.program(31,w,G)})}else{o=z.local;o=typeof o===f?o():o}if(!x.local){o=c.call(z,o,{hash:{},inverse:v.noop,fn:v.program(31,w,G)})}if(o||o===0){y+=o}y+="\n</div>";return y})})();
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/templates/compiled/template-user-quotaMeter-quota.js
--- /dev/null
+++ b/static/scripts/packed/templates/compiled/template-user-quotaMeter-quota.js
@@ -0,0 +1,1 @@
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-user-quotaMeter-quota"]=b(function(e,l,d,k,j){d=d||e.helpers;var h="",c,g,f="function",i=this.escapeExpression;h+='<div id="quota-meter" class="quota-meter progress">\n <div id="quota-meter-bar" class="quota-meter-bar bar" style="width: ';g=d.quota_percent;if(g){c=g.call(l,{hash:{}})}else{c=l.quota_percent;c=typeof c===f?c():c}h+=i(c)+'%"></div>\n ';h+='\n <div id="quota-meter-text" class="quota-meter-text"style="top: 6px">\n Using ';g=d.quota_percent;if(g){c=g.call(l,{hash:{}})}else{c=l.quota_percent;c=typeof c===f?c():c}h+=i(c)+"%\n </div>\n</div>";return h})})();
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/templates/compiled/template-user-quotaMeter-usage.js
--- /dev/null
+++ b/static/scripts/packed/templates/compiled/template-user-quotaMeter-usage.js
@@ -0,0 +1,1 @@
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-user-quotaMeter-usage"]=b(function(e,l,d,k,j){d=d||e.helpers;var h="",c,g,f="function",i=this.escapeExpression;h+='\n<div id="quota-meter" class="quota-meter" style="background-color: transparent">\n <div id="quota-meter-text" class="quota-meter-text" style="top: 6px; color: white">\n Using ';g=d.nice_total_disk_usage;if(g){c=g.call(l,{hash:{}})}else{c=l.nice_total_disk_usage;c=typeof c===f?c():c}h+=i(c)+"\n </div>\n</div>";return h})})();
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/viz/circster.js
--- a/static/scripts/packed/viz/circster.js
+++ b/static/scripts/packed/viz/circster.js
@@ -1,1 +1,1 @@
-define(["libs/underscore","libs/d3","viz/visualization"],function(g,l,i){var m=Backbone.Model.extend({is_visible:function(q,n){var o=q.getBoundingClientRect(),p=$("svg")[0].getBoundingClientRect();if(o.right<0||o.left>p.right||o.bottom<0||o.top>p.bottom){return false}return true}});var h={drawTicks:function(r,q,v,p,n){var u=r.append("g").selectAll("g").data(q).enter().append("g").selectAll("g").data(v).enter().append("g").attr("class","tick").attr("transform",function(w){return"rotate("+(w.angle*180/Math.PI-90)+")translate("+w.radius+",0)"});var t=[],s=[],o=function(w){return w.angle>Math.PI?"end":null};if(n){t=[0,0,0,-4];s=[4,0,"",".35em"];o=null}else{t=[1,0,4,0];s=[0,4,".35em",""]}u.append("line").attr("x1",t[0]).attr("y1",t[1]).attr("x2",t[2]).attr("y1",t[3]).style("stroke","#000");u.append("text").attr("x",s[0]).attr("y",s[1]).attr("dx",s[2]).attr("dy",s[3]).attr("text-anchor",o).attr("transform",p).text(function(w){return w.label})},formatNum:function(o,n){var q=null;if(o<1){q=o.toPrecision(n)}else{var p=Math.round(o.toPrecision(n));if(o<1000){q=p}else{if(o<1000000){q=Math.round((p/1000).toPrecision(3)).toFixed(0)+"K"}else{if(o<1000000000){q=Math.round((p/1000000).toPrecision(3)).toFixed(0)+"M"}}}}return q}};var c=Backbone.Model.extend({});var a=Backbone.View.extend({className:"circster",initialize:function(n){this.total_gap=n.total_gap;this.genome=n.genome;this.dataset_arc_height=n.dataset_arc_height;this.track_gap=10;this.label_arc_height=50;this.scale=1;this.circular_views=null;this.chords_views=null;this.model.get("tracks").on("add",this.add_track,this);this.model.get("tracks").on("remove",this.remove_track,this);this.get_circular_tracks()},get_circular_tracks:function(){return this.model.get("tracks").filter(function(n){return n.get("track_type")!=="DiagonalHeatmapTrack"})},get_chord_tracks:function(){return this.model.get("tracks").filter(function(n){return n.get("track_type")==="DiagonalHeatmapTrack"})},get_tracks_bounds:function(){var o=this.get_circular_tracks();dataset_arc_height=this.dataset_arc_height,min_dimension=Math.min(this.$el.width(),this.$el.height()),radius_start=min_dimension/2-o.length*(this.dataset_arc_height+this.track_gap)-(this.label_arc_height+this.track_gap),tracks_start_radii=l.range(radius_start,min_dimension/2,this.dataset_arc_height+this.track_gap);var n=this;return g.map(tracks_start_radii,function(p){return[p,p+n.dataset_arc_height]})},render:function(){var w=this,q=this.dataset_arc_height,n=w.$el.width(),v=w.$el.height(),s=this.get_circular_tracks(),p=this.get_chord_tracks(),r=this.get_tracks_bounds(),o=l.select(w.$el[0]).append("svg").attr("width",n).attr("height",v).attr("pointer-events","all").append("svg:g").call(l.behavior.zoom().on("zoom",function(){var x=l.event.scale;o.attr("transform","translate("+l.event.translate+") scale("+x+")");if(w.scale!==x){if(w.zoom_drag_timeout){clearTimeout(w.zoom_drag_timeout)}w.zoom_drag_timeout=setTimeout(function(){},400)}})).attr("transform","translate("+n/2+","+v/2+")").append("svg:g").attr("class","tracks");this.circular_views=s.map(function(y,z){var A=(y.get("track_type")==="LineTrack"?d:e),x=new A({el:o.append("g")[0],track:y,radius_bounds:r[z],genome:w.genome,total_gap:w.total_gap});x.render();return x});this.chords_views=p.map(function(y){var x=new j({el:o.append("g")[0],track:y,radius_bounds:r[0],genome:w.genome,total_gap:w.total_gap});x.render();return x});var u=this.circular_views[this.circular_views.length-1].radius_bounds[1],t=[u,u+this.label_arc_height];this.label_track_view=new b({el:o.append("g")[0],track:new c(),radius_bounds:t,genome:w.genome,total_gap:w.total_gap});this.label_track_view.render()},add_track:function(t){if(t.get("track_type")==="DiagonalHeatmapTrack"){var p=this.circular_views[0].radius_bounds,s=new j({el:l.select("g.tracks").append("g")[0],track:t,radius_bounds:p,genome:this.genome,total_gap:this.total_gap});s.render();this.chords_views.push(s)}else{var r=this.get_tracks_bounds();g.each(this.circular_views,function(v,w){v.update_radius_bounds(r[w])});g.each(this.chords_views,function(v){v.update_radius_bounds(r[0])});var q=this.circular_views.length,u=(t.get("track_type")==="LineTrack"?d:e),n=new u({el:l.select("g.tracks").append("g")[0],track:t,radius_bounds:r[q],genome:this.genome,total_gap:this.total_gap});n.render();this.circular_views.push(n);var o=r[r.length-1];o[1]=o[0];this.label_track_view.update_radius_bounds(o)}},remove_track:function(o,q,p){var n=this.circular_views[p.index];this.circular_views.splice(p.index,1);n.$el.remove();var r=this.get_tracks_bounds();g.each(this.circular_views,function(s,t){s.update_radius_bounds(r[t])})}});var k=Backbone.View.extend({tagName:"g",initialize:function(n){this.bg_stroke="ccc";this.loading_bg_fill="000";this.bg_fill="ccc";this.total_gap=n.total_gap;this.track=n.track;this.radius_bounds=n.radius_bounds;this.genome=n.genome;this.chroms_layout=this._chroms_layout();this.data_bounds=[];this.scale=1;this.parent_elt=l.select(this.$el[0])},get_fill_color:function(){var n=this.track.get("config").get_value("block_color");if(!n){n=this.track.get("config").get_value("color")}return n},render:function(){var r=this.parent_elt;if(!r){console.log("no parent elt")}var q=this.chroms_layout,t=l.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]),n=r.selectAll("g").data(q).enter().append("svg:g"),p=n.append("path").attr("d",t).attr("class","chrom-background").style("stroke",this.bg_stroke).style("fill",this.loading_bg_fill);p.append("title").text(function(v){return v.data.chrom});var o=this,s=o.track.get("data_manager"),u=(s?s.data_is_ready():true);$.when(u).then(function(){$.when(o._render_data(r)).then(function(){p.style("fill",o.bg_fill);o.render_labels()})})},render_labels:function(){},update_radius_bounds:function(o){this.radius_bounds=o;var n=l.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]);this.parent_elt.selectAll("g>path.chrom-background").transition().duration(1000).attr("d",n);this._transition_chrom_data();this._transition_labels()},update_scale:function(q){var p=this.scale;this.scale=q;if(q<=p){return}var o=this,n=new m();this.parent_elt.selectAll("path.chrom-data").filter(function(s,r){return n.is_visible(this)}).each(function(x,t){var w=l.select(this),s=w.attr("chrom"),v=o.genome.get_chrom_region(s),u=o.track.get("data_manager"),r;if(!u.can_get_more_detailed_data(v)){return}r=o.track.get("data_manager").get_more_detailed_data(v,"Coverage",0,q);$.when(r).then(function(A){w.remove();o._update_data_bounds();var z=g.find(o.chroms_layout,function(B){return B.data.chrom===s});var y=o.get_fill_color();o._render_chrom_data(o.parent_elt,z,A).style("stroke",y).style("fill",y)})});return o},_transition_chrom_data:function(){var o=this.track,q=this.chroms_layout,n=this.parent_elt.selectAll("g>path.chrom-data"),r=n[0].length;if(r>0){var p=this;$.when(o.get("data_manager").get_genome_wide_data(this.genome)).then(function(t){var s=g.reject(g.map(t,function(u,v){var w=null,x=p._get_path_function(q[v],u);if(x){w=x(u.data)}return w}),function(u){return u===null});n.each(function(v,u){l.select(this).transition().duration(1000).attr("d",s[u])})})}},_transition_labels:function(){},_update_data_bounds:function(){var n=this.data_bounds;this.data_bounds=this.get_data_bounds(this.track.get("data_manager").get_genome_wide_data(this.genome));if(this.data_bounds[0]<n[0]||this.data_bounds[1]>n[1]){this._transition_chrom_data()}},_render_data:function(q){var p=this,o=this.chroms_layout,n=this.track,r=$.Deferred();$.when(n.get("data_manager").get_genome_wide_data(this.genome)).then(function(t){p.data_bounds=p.get_data_bounds(t);layout_and_data=g.zip(o,t),chroms_data_layout=g.map(layout_and_data,function(u){var v=u[0],w=u[1];return p._render_chrom_data(q,v,w)});var s=p.get_fill_color();p.parent_elt.selectAll("path.chrom-data").style("stroke",s).style("fill",s);r.resolve(q)});return r},_render_chrom_data:function(n,o,p){},_get_path_function:function(o,n){},_chroms_layout:function(){var o=this.genome.get_chroms_info(),q=l.layout.pie().value(function(s){return s.len}).sort(null),r=q(o),n=this.total_gap/o.length,p=g.map(r,function(u,t){var s=u.endAngle-n;u.endAngle=(s>u.startAngle?s:u.startAngle);return u});return p}});var b=k.extend({initialize:function(n){k.prototype.initialize.call(this,n);this.innerRadius=this.radius_bounds[0];this.radius_bounds[0]=this.radius_bounds[1];this.bg_stroke="fff";this.bg_fill="fff";this.min_arc_len=0.08},_render_data:function(p){var o=this,n=p.selectAll("g");n.selectAll("path").attr("id",function(t){return"label-"+t.data.chrom});n.append("svg:text").filter(function(t){return t.endAngle-t.startAngle>o.min_arc_len}).attr("text-anchor","middle").append("svg:textPath").attr("xlink:href",function(t){return"#label-"+t.data.chrom}).attr("startOffset","25%").attr("font-weight","bold").text(function(t){return t.data.chrom});var q=function(v){var t=(v.endAngle-v.startAngle)/v.value,u=l.range(0,v.value,25000000).map(function(w,x){return{radius:o.innerRadius,angle:w*t+v.startAngle,label:x===0?0:(x%3?null:o.formatNum(w))}});if(u.length<4){u[u.length-1].label=o.formatNum(Math.round((u[u.length-1].angle-v.startAngle)/t))}return u};var s=function(t){return t.angle>Math.PI?"rotate(180)translate(-16)":null};var r=g.filter(this.chroms_layout,function(t){return t.endAngle-t.startAngle>o.min_arc_len});this.drawTicks(this.parent_elt,r,q,s)}});g.extend(b.prototype,h);var f=k.extend({_render_chrom_data:function(n,q,o){var r=this._get_path_function(q,o);if(!r){return null}var p=n.datum(o.data),s=p.append("path").attr("class","chrom-data").attr("chrom",q.data.chrom).attr("d",r);return s},_get_path_function:function(q,p){if(typeof p==="string"||!p.data||p.data.length===0){return null}var n=l.scale.linear().domain(this.data_bounds).range(this.radius_bounds);var r=l.scale.linear().domain([0,p.data.length]).range([q.startAngle,q.endAngle]);var o=l.svg.line.radial().interpolate("linear").radius(function(s){return n(s[1])}).angle(function(t,s){return r(s)});return l.svg.area.radial().interpolate(o.interpolate()).innerRadius(n(0)).outerRadius(o.radius()).angle(o.angle())},render_labels:function(){var n=this,q=function(){return"rotate(90)"};var p=g.filter(this.chroms_layout,function(r){return r.endAngle-r.startAngle>0.08}),o=g.filter(p,function(s,r){return r%3===0});this.drawTicks(this.parent_elt,o,this._data_bounds_ticks_fn(),q,true)},_transition_labels:function(){var o=this,q=g.filter(this.chroms_layout,function(r){return r.endAngle-r.startAngle>0.08}),p=g.filter(q,function(s,r){return r%3===0}),n=g.flatten(g.map(p,function(r){return o._data_bounds_ticks_fn()(r)}));this.parent_elt.selectAll("g.tick").data(n).transition().attr("transform",function(r){return"rotate("+(r.angle*180/Math.PI-90)+")translate("+r.radius+",0)"})},_data_bounds_ticks_fn:function(){var n=this;visibleChroms=0;return function(o){return[{radius:n.radius_bounds[0],angle:o.startAngle,label:n.formatNum(n.data_bounds[0])},{radius:n.radius_bounds[1],angle:o.startAngle,label:n.formatNum(n.data_bounds[1])}]}},get_data_bounds:function(n){}});g.extend(f.prototype,h);var e=f.extend({get_data_bounds:function(o){var n=g.map(o,function(p){if(typeof p==="string"||!p.max){return 0}return p.max});return[0,(n&&typeof n!=="string"?g.max(n):0)]}});var d=f.extend({get_data_bounds:function(o){var n=g.flatten(g.map(o,function(p){if(p){return g.map(p.data,function(q){return q[1]})}else{return 0}}));return[g.min(n),g.max(n)]}});var j=k.extend({render:function(){var n=this;$.when(n.track.get("data_manager").data_is_ready()).then(function(){$.when(n.track.get("data_manager").get_genome_wide_data(n.genome)).then(function(q){var p=[],o=n.genome.get_chroms_info();g.each(q,function(u,t){var r=o[t].chrom;var s=g.map(u.data,function(w){var v=n._get_region_angle(r,w[1]),x=n._get_region_angle(w[3],w[4]);return{source:{startAngle:v,endAngle:v+0.01},target:{startAngle:x,endAngle:x+0.01}}});p=p.concat(s)});n.parent_elt.append("g").attr("class","chord").selectAll("path").data(p).enter().append("path").style("fill",n.get_fill_color()).attr("d",l.svg.chord().radius(n.radius_bounds[0])).style("opacity",1)})})},update_radius_bounds:function(n){this.radius_bounds=n;this.parent_elt.selectAll("path").transition().attr("d",l.svg.chord().radius(this.radius_bounds[0]))},_get_region_angle:function(p,n){var o=g.find(this.chroms_layout,function(q){return q.data.chrom===p});return o.endAngle-((o.endAngle-o.startAngle)*(o.data.len-n)/o.data.len)}});return{CircsterView:a}});
\ No newline at end of file
+define(["libs/underscore","libs/d3","viz/visualization"],function(g,l,i){var m=Backbone.Model.extend({is_visible:function(q,n){var o=q.getBoundingClientRect(),p=$("svg")[0].getBoundingClientRect();if(o.right<0||o.left>p.right||o.bottom<0||o.top>p.bottom){return false}return true}});var h={drawTicks:function(r,q,v,p,n){var u=r.append("g").selectAll("g").data(q).enter().append("g").selectAll("g").data(v).enter().append("g").attr("class","tick").attr("transform",function(w){return"rotate("+(w.angle*180/Math.PI-90)+")translate("+w.radius+",0)"});var t=[],s=[],o=function(w){return w.angle>Math.PI?"end":null};if(n){t=[0,0,0,-4];s=[4,0,"",".35em"];o=null}else{t=[1,0,4,0];s=[0,4,".35em",""]}u.append("line").attr("x1",t[0]).attr("y1",t[1]).attr("x2",t[2]).attr("y1",t[3]).style("stroke","#000");u.append("text").attr("x",s[0]).attr("y",s[1]).attr("dx",s[2]).attr("dy",s[3]).attr("text-anchor",o).attr("transform",p).text(function(w){return w.label})},formatNum:function(o,n){var q=null;if(o<1){q=o.toPrecision(n)}else{var p=Math.round(o.toPrecision(n));if(o<1000){q=p}else{if(o<1000000){q=Math.round((p/1000).toPrecision(3)).toFixed(0)+"K"}else{if(o<1000000000){q=Math.round((p/1000000).toPrecision(3)).toFixed(0)+"M"}}}}return q}};var c=Backbone.Model.extend({});var a=Backbone.View.extend({className:"circster",initialize:function(n){this.total_gap=n.total_gap;this.genome=n.genome;this.dataset_arc_height=n.dataset_arc_height;this.track_gap=10;this.label_arc_height=50;this.scale=1;this.circular_views=null;this.chords_views=null;this.model.get("tracks").on("add",this.add_track,this);this.model.get("tracks").on("remove",this.remove_track,this);this.get_circular_tracks()},get_circular_tracks:function(){return this.model.get("tracks").filter(function(n){return n.get("track_type")!=="DiagonalHeatmapTrack"})},get_chord_tracks:function(){return this.model.get("tracks").filter(function(n){return n.get("track_type")==="DiagonalHeatmapTrack"})},get_tracks_bounds:function(){var o=this.get_circular_tracks();dataset_arc_height=this.dataset_arc_height,min_dimension=Math.min(this.$el.width(),this.$el.height()),radius_start=min_dimension/2-o.length*(this.dataset_arc_height+this.track_gap)-(this.label_arc_height+this.track_gap),tracks_start_radii=l.range(radius_start,min_dimension/2,this.dataset_arc_height+this.track_gap);var n=this;return g.map(tracks_start_radii,function(p){return[p,p+n.dataset_arc_height]})},render:function(){var w=this,q=this.dataset_arc_height,n=w.$el.width(),v=w.$el.height(),s=this.get_circular_tracks(),p=this.get_chord_tracks(),r=this.get_tracks_bounds(),o=l.select(w.$el[0]).append("svg").attr("width",n).attr("height",v).attr("pointer-events","all").append("svg:g").call(l.behavior.zoom().on("zoom",function(){var x=l.event.scale;o.attr("transform","translate("+l.event.translate+") scale("+x+")");if(w.scale!==x){if(w.zoom_drag_timeout){clearTimeout(w.zoom_drag_timeout)}w.zoom_drag_timeout=setTimeout(function(){},400)}})).attr("transform","translate("+n/2+","+v/2+")").append("svg:g").attr("class","tracks");this.circular_views=s.map(function(y,z){var A=(y.get("track_type")==="LineTrack"?d:e),x=new A({el:o.append("g")[0],track:y,radius_bounds:r[z],genome:w.genome,total_gap:w.total_gap});x.render();return x});this.chords_views=p.map(function(y){var x=new j({el:o.append("g")[0],track:y,radius_bounds:r[0],genome:w.genome,total_gap:w.total_gap});x.render();return x});var u=this.circular_views[this.circular_views.length-1].radius_bounds[1],t=[u,u+this.label_arc_height];this.label_track_view=new b({el:o.append("g")[0],track:new c(),radius_bounds:t,genome:w.genome,total_gap:w.total_gap});this.label_track_view.render()},add_track:function(t){if(t.get("track_type")==="DiagonalHeatmapTrack"){var p=this.circular_views[0].radius_bounds,s=new j({el:l.select("g.tracks").append("g")[0],track:t,radius_bounds:p,genome:this.genome,total_gap:this.total_gap});s.render();this.chords_views.push(s)}else{var r=this.get_tracks_bounds();g.each(this.circular_views,function(v,w){v.update_radius_bounds(r[w])});g.each(this.chords_views,function(v){v.update_radius_bounds(r[0])});var q=this.circular_views.length,u=(t.get("track_type")==="LineTrack"?d:e),n=new u({el:l.select("g.tracks").append("g")[0],track:t,radius_bounds:r[q],genome:this.genome,total_gap:this.total_gap});n.render();this.circular_views.push(n);var o=r[r.length-1];o[1]=o[0];this.label_track_view.update_radius_bounds(o)}},remove_track:function(o,q,p){var n=this.circular_views[p.index];this.circular_views.splice(p.index,1);n.$el.remove();var r=this.get_tracks_bounds();g.each(this.circular_views,function(s,t){s.update_radius_bounds(r[t])})}});var k=Backbone.View.extend({tagName:"g",initialize:function(n){this.bg_stroke="ccc";this.loading_bg_fill="000";this.bg_fill="ccc";this.total_gap=n.total_gap;this.track=n.track;this.radius_bounds=n.radius_bounds;this.genome=n.genome;this.chroms_layout=this._chroms_layout();this.data_bounds=[];this.scale=1;this.parent_elt=l.select(this.$el[0])},get_fill_color:function(){var n=this.track.get("config").get_value("block_color");if(!n){n=this.track.get("config").get_value("color")}return n},render:function(){var r=this.parent_elt;if(!r){console.log("no parent elt")}var q=this.chroms_layout,t=l.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]),n=r.selectAll("g").data(q).enter().append("svg:g"),p=n.append("path").attr("d",t).attr("class","chrom-background").style("stroke",this.bg_stroke).style("fill",this.loading_bg_fill);p.append("title").text(function(v){return v.data.chrom});var o=this,s=o.track.get("data_manager"),u=(s?s.data_is_ready():true);$.when(u).then(function(){$.when(o._render_data(r)).then(function(){p.style("fill",o.bg_fill);o.render_labels()})})},render_labels:function(){},update_radius_bounds:function(o){this.radius_bounds=o;var n=l.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]);this.parent_elt.selectAll("g>path.chrom-background").transition().duration(1000).attr("d",n);this._transition_chrom_data();this._transition_labels()},update_scale:function(q){var p=this.scale;this.scale=q;if(q<=p){return}var o=this,n=new m();this.parent_elt.selectAll("path.chrom-data").filter(function(s,r){return n.is_visible(this)}).each(function(x,t){var w=l.select(this),s=w.attr("chrom"),v=o.genome.get_chrom_region(s),u=o.track.get("data_manager"),r;if(!u.can_get_more_detailed_data(v)){return}r=o.track.get("data_manager").get_more_detailed_data(v,"Coverage",0,q);$.when(r).then(function(A){w.remove();o._update_data_bounds();var z=g.find(o.chroms_layout,function(B){return B.data.chrom===s});var y=o.get_fill_color();o._render_chrom_data(o.parent_elt,z,A).style("stroke",y).style("fill",y)})});return o},_transition_chrom_data:function(){var o=this.track,q=this.chroms_layout,n=this.parent_elt.selectAll("g>path.chrom-data"),r=n[0].length;if(r>0){var p=this;$.when(o.get("data_manager").get_genome_wide_data(this.genome)).then(function(t){var s=g.reject(g.map(t,function(u,v){var w=null,x=p._get_path_function(q[v],u);if(x){w=x(u.data)}return w}),function(u){return u===null});n.each(function(v,u){l.select(this).transition().duration(1000).attr("d",s[u])})})}},_transition_labels:function(){},_update_data_bounds:function(){var n=this.data_bounds;this.data_bounds=this.get_data_bounds(this.track.get("data_manager").get_genome_wide_data(this.genome));if(this.data_bounds[0]<n[0]||this.data_bounds[1]>n[1]){this._transition_chrom_data()}},_render_data:function(q){var p=this,o=this.chroms_layout,n=this.track,r=$.Deferred();$.when(n.get("data_manager").get_genome_wide_data(this.genome)).then(function(t){p.data_bounds=p.get_data_bounds(t);layout_and_data=g.zip(o,t),chroms_data_layout=g.map(layout_and_data,function(u){var v=u[0],w=u[1];return p._render_chrom_data(q,v,w)});var s=p.get_fill_color();p.parent_elt.selectAll("path.chrom-data").style("stroke",s).style("fill",s);r.resolve(q)});return r},_render_chrom_data:function(n,o,p){},_get_path_function:function(o,n){},_chroms_layout:function(){var o=this.genome.get_chroms_info(),q=l.layout.pie().value(function(s){return s.len}).sort(null),r=q(o),n=this.total_gap/o.length,p=g.map(r,function(u,t){var s=u.endAngle-n;u.endAngle=(s>u.startAngle?s:u.startAngle);return u});return p}});var b=k.extend({initialize:function(n){k.prototype.initialize.call(this,n);this.innerRadius=this.radius_bounds[0];this.radius_bounds[0]=this.radius_bounds[1];this.bg_stroke="fff";this.bg_fill="fff";this.min_arc_len=0.08},_render_data:function(p){var o=this,n=p.selectAll("g");n.selectAll("path").attr("id",function(t){return"label-"+t.data.chrom});n.append("svg:text").filter(function(t){return t.endAngle-t.startAngle>o.min_arc_len}).attr("text-anchor","middle").append("svg:textPath").attr("xlink:href",function(t){return"#label-"+t.data.chrom}).attr("startOffset","25%").attr("font-weight","bold").text(function(t){return t.data.chrom});var q=function(v){var t=(v.endAngle-v.startAngle)/v.value,u=l.range(0,v.value,25000000).map(function(w,x){return{radius:o.innerRadius,angle:w*t+v.startAngle,label:x===0?0:(x%3?null:o.formatNum(w))}});if(u.length<4){u[u.length-1].label=o.formatNum(Math.round((u[u.length-1].angle-v.startAngle)/t))}return u};var s=function(t){return t.angle>Math.PI?"rotate(180)translate(-16)":null};var r=g.filter(this.chroms_layout,function(t){return t.endAngle-t.startAngle>o.min_arc_len});this.drawTicks(this.parent_elt,r,q,s)}});g.extend(b.prototype,h);var f=k.extend({_quantile:function(o,n){o.sort(l.ascending);return l.quantile(o,n)},_render_chrom_data:function(n,q,o){var r=this._get_path_function(q,o);if(!r){return null}var p=n.datum(o.data),s=p.append("path").attr("class","chrom-data").attr("chrom",q.data.chrom).attr("d",r);return s},_get_path_function:function(q,p){if(typeof p==="string"||!p.data||p.data.length===0){return null}var n=l.scale.linear().domain(this.data_bounds).range(this.radius_bounds).clamp(true);var r=l.scale.linear().domain([0,p.data.length]).range([q.startAngle,q.endAngle]);var o=l.svg.line.radial().interpolate("linear").radius(function(s){return n(s[1])}).angle(function(t,s){return r(s)});return l.svg.area.radial().interpolate(o.interpolate()).innerRadius(n(0)).outerRadius(o.radius()).angle(o.angle())},render_labels:function(){var n=this,q=function(){return"rotate(90)"};var p=g.filter(this.chroms_layout,function(r){return r.endAngle-r.startAngle>0.08}),o=g.filter(p,function(s,r){return r%3===0});this.drawTicks(this.parent_elt,o,this._data_bounds_ticks_fn(),q,true)},_transition_labels:function(){var o=this,q=g.filter(this.chroms_layout,function(r){return r.endAngle-r.startAngle>0.08}),p=g.filter(q,function(s,r){return r%3===0}),n=g.flatten(g.map(p,function(r){return o._data_bounds_ticks_fn()(r)}));this.parent_elt.selectAll("g.tick").data(n).transition().attr("transform",function(r){return"rotate("+(r.angle*180/Math.PI-90)+")translate("+r.radius+",0)"})},_data_bounds_ticks_fn:function(){var n=this;visibleChroms=0;return function(o){return[{radius:n.radius_bounds[0],angle:o.startAngle,label:n.formatNum(n.data_bounds[0])},{radius:n.radius_bounds[1],angle:o.startAngle,label:n.formatNum(n.data_bounds[1])}]}},get_data_bounds:function(n){}});g.extend(f.prototype,h);var e=f.extend({get_data_bounds:function(o){var n=g.map(o,function(p){if(typeof p==="string"||!p.max){return 0}return p.max});return[0,(n&&typeof n!=="string"?this._quantile(values,0.98):0)]}});var d=f.extend({get_data_bounds:function(o){var n=g.flatten(g.map(o,function(p){if(p){return g.map(p.data,function(q){return q[1]})}else{return 0}}));return[g.min(n),this._quantile(n,0.98)]}});var j=k.extend({render:function(){var n=this;$.when(n.track.get("data_manager").data_is_ready()).then(function(){$.when(n.track.get("data_manager").get_genome_wide_data(n.genome)).then(function(q){var p=[],o=n.genome.get_chroms_info();g.each(q,function(u,t){var r=o[t].chrom;var s=g.map(u.data,function(w){var v=n._get_region_angle(r,w[1]),x=n._get_region_angle(w[3],w[4]);return{source:{startAngle:v,endAngle:v+0.01},target:{startAngle:x,endAngle:x+0.01}}});p=p.concat(s)});n.parent_elt.append("g").attr("class","chord").selectAll("path").data(p).enter().append("path").style("fill",n.get_fill_color()).attr("d",l.svg.chord().radius(n.radius_bounds[0])).style("opacity",1)})})},update_radius_bounds:function(n){this.radius_bounds=n;this.parent_elt.selectAll("path").transition().attr("d",l.svg.chord().radius(this.radius_bounds[0]))},_get_region_angle:function(p,n){var o=g.find(this.chroms_layout,function(q){return q.data.chrom===p});return o.endAngle-((o.endAngle-o.startAngle)*(o.data.len-n)/o.data.len)}});return{CircsterView:a}});
\ 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
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/5dcbbdfe1087/
changeset: 5dcbbdfe1087
user: dan
date: 2012-11-06 18:57:23
summary: Allow rerun to access hidden datasets.
affected #: 1 file
diff -r 6624cd467f30618d5a1319e14b2a41ab7c6a2407 -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -1434,7 +1434,7 @@
else:
hid = str( hda.hid )
if not hda.dataset.state in [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED] and \
- hda.visible and \
+ ( hda.visible or ( value and hda in value and not hda.implicitly_converted_parent_datasets ) ) and \
trans.app.security_agent.can_access_dataset( current_user_roles, hda.dataset ):
# If we are sending data to an external application, then we need to make sure there are no roles
# associated with the dataset that restrict it's access from "public".
@@ -1444,7 +1444,11 @@
continue
if isinstance( hda.datatype, self.formats):
selected = ( value and ( hda in value ) )
- field.add_option( "%s: %s" % ( hid, hda_name ), hda.id, selected )
+ if hda.visible:
+ hidden_text = ""
+ else:
+ hidden_text = " (hidden)"
+ field.add_option( "%s:%s %s" % ( hid, hidden_text, hda_name ), hda.id, selected )
else:
target_ext, converted_dataset = hda.find_conversion_destination( self.formats )
if target_ext:
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: richard_burhans: Fixed installation of toolsheds containing proprietary datatypes
by Bitbucket 06 Nov '12
by Bitbucket 06 Nov '12
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/6624cd467f30/
changeset: 6624cd467f30
user: richard_burhans
date: 2012-11-06 17:22:56
summary: Fixed installation of toolsheds containing proprietary datatypes
affected #: 1 file
diff -r efccb227d72ccb2764544fd461195789653b4509 -r 6624cd467f30618d5a1319e14b2a41ab7c6a2407 lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -745,10 +745,11 @@
Generate the metadata for the installed tool shed repository, among other things. This method is called from Galaxy (never the tool shed)
when an admin is installing a new repository or reinstalling an uninstalled repository.
"""
+ shed_config_dict = trans.app.toolbox.get_shed_config_dict_by_filename( shed_tool_conf )
metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=trans.app,
repository=tool_shed_repository,
repository_clone_url=repository_clone_url,
- shed_config_dict = trans.app.toolbox.get_shed_config_dict_by_filename( shed_tool_conf ),
+ shed_config_dict=shed_config_dict,
relative_install_dir=relative_install_dir,
repository_files_dir=None,
resetting_all_metadata_on_repository=False,
@@ -791,9 +792,12 @@
tool_shed_repository.includes_datatypes = True
trans.sa_session.add( tool_shed_repository )
trans.sa_session.flush()
- datatypes_config = get_config_from_disk( 'datatypes_conf.xml', relative_install_dir )
+ files_dir = relative_install_dir
+ if shed_config_dict.get( 'tool_path' ):
+ files_dir = os.path.join( shed_config_dict['tool_path'], files_dir )
+ datatypes_config = get_config_from_disk( 'datatypes_conf.xml', files_dir )
# Load data types required by tools.
- converter_path, display_path = alter_config_and_load_prorietary_datatypes( trans.app, datatypes_config, relative_install_dir, override=False )
+ converter_path, display_path = alter_config_and_load_prorietary_datatypes( trans.app, datatypes_config, files_dir, override=False )
if converter_path or display_path:
# Create a dictionary of tool shed repository related information.
repository_dict = create_repository_dict_for_proprietary_datatypes( tool_shed=tool_shed,
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: Circster improvements: (a) fetch a smaller amount of data on load and (b) use large quantile rather than max for top data range to limit impact of exceptionally large values.
by Bitbucket 06 Nov '12
by Bitbucket 06 Nov '12
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/efccb227d72c/
changeset: efccb227d72c
user: jgoecks
date: 2012-11-06 15:52:23
summary: Circster improvements: (a) fetch a smaller amount of data on load and (b) use large quantile rather than max for top data range to limit impact of exceptionally large values.
affected #: 2 files
diff -r dd4786c5b4e48db780f6ec5da42dfafec19538f4 -r efccb227d72ccb2764544fd461195789653b4509 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -636,9 +636,12 @@
data_provider = trans.app.data_provider_registry.get_data_provider( trans,
original_dataset=dataset,
source=source )
- # HACK: pass in additional params which are used for only some types of data providers;
- # level, cutoffs used for summary tree, and interchromosomal used for chromatin interactions.
- rval = data_provider.get_genome_data( chroms_info, level=4, detail_cutoff=0, draw_cutoff=0,
+ # HACK: pass in additional params which are used for only some
+ # types of data providers; level, cutoffs used for summary tree,
+ # num_samples for BBI, and interchromosomal used for chromatin interactions.
+ rval = data_provider.get_genome_data( chroms_info,
+ level=4, detail_cutoff=0, draw_cutoff=0,
+ num_samples=150,
interchromosomal=True )
return rval
diff -r dd4786c5b4e48db780f6ec5da42dfafec19538f4 -r efccb227d72ccb2764544fd461195789653b4509 static/scripts/viz/circster.js
--- a/static/scripts/viz/circster.js
+++ b/static/scripts/viz/circster.js
@@ -720,6 +720,14 @@
var CircsterQuantitativeTrackView = CircsterTrackView.extend({
/**
+ * Returns quantile for an array of numbers.
+ */
+ _quantile: function(numbers, quantile) {
+ numbers.sort(d3.ascending);
+ return d3.quantile(numbers, quantile);
+ },
+
+ /**
* Renders quantitative data with the form [x, value] and assumes data is equally spaced across
* chromosome. Attachs a dict with track and chrom name information to DOM element.
*/
@@ -750,7 +758,8 @@
// Radius scaler.
var radius = d3.scale.linear()
.domain(this.data_bounds)
- .range(this.radius_bounds);
+ .range(this.radius_bounds)
+ .clamp(true);
// Scaler for placing data points across arc.
var angle = d3.scale.linear()
@@ -854,7 +863,7 @@
if (typeof d === 'string' || !d.max) { return 0; }
return d.max;
});
- return [ 0, (max_data && typeof max_data !== 'string' ? _.max(max_data) : 0) ];
+ return [ 0, (max_data && typeof max_data !== 'string' ? this._quantile(values, 0.98) : 0) ];
}
});
@@ -865,7 +874,7 @@
get_data_bounds: function(data) {
// Set max across dataset by extracting all values, flattening them into a
- // single array, and getting the min and max.
+ // single array, and getting third quartile.
var values = _.flatten( _.map(data, function(d) {
if (d) {
// Each data point has the form [position, value], so return all values.
@@ -878,7 +887,7 @@
}
}) );
- return [ _.min(values), _.max(values) ];
+ return [ _.min(values), this._quantile(values, 0.98) ];
}
});
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: greg: Per James Johnson, handle files with a .tar extension when installing tool dependencies along with a tool shed repository.
by Bitbucket 06 Nov '12
by Bitbucket 06 Nov '12
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/dd4786c5b4e4/
changeset: dd4786c5b4e4
user: greg
date: 2012-11-06 14:38:52
summary: Per James Johnson, handle files with a .tar extension when installing tool dependencies along with a tool shed repository.
affected #: 1 file
diff -r b996950ffcf2342f1211dc49e6ca75c4dfd7050f -r dd4786c5b4e48db780f6ec5da42dfafec19538f4 lib/galaxy/tool_shed/tool_dependencies/common_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/common_util.py
+++ b/lib/galaxy/tool_shed/tool_dependencies/common_util.py
@@ -93,7 +93,7 @@
def tar_extraction_directory( file_path, file_name ):
"""Try to return the correct extraction directory."""
file_name = file_name.strip()
- extensions = [ '.tar.gz', '.tgz', '.tar.bz2', '.zip' ]
+ extensions = [ '.tar.gz', '.tgz', '.tar.bz2', '.tar', '.zip' ]
for extension in extensions:
if file_name.find( extension ) > 0:
dir_name = file_name[ :-len( extension ) ]
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
7 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/74f946b09dd0/
changeset: 74f946b09dd0
user: carlfeberhard
date: 2012-11-05 22:42:16
summary: util/debugging.py: add stack trace formatter
affected #: 1 file
diff -r 9d6a61f060d359a0289b3163990f6a8ba122d253 -r 74f946b09dd06341a02d009a81c00e25dd5dfb59 lib/galaxy/util/debugging.py
--- /dev/null
+++ b/lib/galaxy/util/debugging.py
@@ -0,0 +1,29 @@
+
+import inspect
+import pprint
+
+import logging
+log = logging.getLogger( __name__ )
+
+def stack_trace_string( max_depth=None, line_format="{index}:{file}:{function}:{line}" ):
+ """
+ Returns a string representation of the current stack.
+
+ :param depth: positive integer to control how many levels of the stack are
+ returned. max_depth=None returns the entire stack (default).
+ """
+ stack_list = []
+ for index, caller in enumerate( inspect.stack() ):
+ # don't include this function
+ if index == 0: continue
+ if max_depth and index > max_depth: break
+
+ caller_data = {
+ 'index' : str( index ),
+ 'file' : caller[1],
+ 'function' : caller[3],
+ 'line' : caller[2]
+ }
+ stack_list.append( line_format.format( **caller_data ) )
+
+ return '\n'.join( stack_list )
https://bitbucket.org/galaxy/galaxy-central/changeset/472a8f0ef7f5/
changeset: 472a8f0ef7f5
user: carlfeberhard
date: 2012-11-05 22:45:10
summary: fix to model/__init__: was importing sys from galaxy.web.form_builder.*
affected #: 1 file
diff -r 74f946b09dd06341a02d009a81c00e25dd5dfb59 -r 472a8f0ef7f5517d20cecaa8a0a65d952fc7d35e lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -18,7 +18,7 @@
from galaxy.model.item_attrs import UsesAnnotations, APIItem
from sqlalchemy.orm import object_session
from sqlalchemy.sql.expression import func
-import os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
+import sys, os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
if sys.version_info[:2] < ( 2, 5 ):
from sets import Set as set
https://bitbucket.org/galaxy/galaxy-central/changeset/613df4d4ca7d/
changeset: 613df4d4ca7d
user: carlfeberhard
date: 2012-11-05 22:47:14
summary: comments & docs for QuotaAgent.get_percent
affected #: 1 file
diff -r 472a8f0ef7f5517d20cecaa8a0a65d952fc7d35e -r 613df4d4ca7d41c240311991022148dedb2e620d lib/galaxy/quota/__init__.py
--- a/lib/galaxy/quota/__init__.py
+++ b/lib/galaxy/quota/__init__.py
@@ -122,20 +122,26 @@
dqa = self.model.DefaultQuotaAssociation( default_type, quota )
self.sa_session.add( dqa )
self.sa_session.flush()
+
def get_percent( self, trans=None, user=False, history=False, usage=False, quota=False ):
+ """
+ Return the percentage of any storage quota applicable to the user/transaction.
+ """
+ # if trans passed, use it to get the user, history (instead of/override vals passed)
if trans:
user = trans.user
history = trans.history
+ # if quota wasn't passed, attempt to get the quota
if quota is False:
quota = self.get_quota( user )
+ # return none if no applicable quotas or quotas disabled
if quota is None:
return None
+ # get the usage, if it wasn't passed
if usage is False:
usage = self.get_usage( trans, user, history )
- percent = int( float( usage ) / quota * 100 )
- if percent > 100:
- percent = 100
- return percent
+ return min( ( int( float( usage ) / quota * 100 ), 100 ) )
+
def set_entity_quota_associations( self, quotas=[], users=[], groups=[], delete_existing_assocs=True ):
for quota in quotas:
if delete_existing_assocs:
https://bitbucket.org/galaxy/galaxy-central/changeset/c57bcb1d78fc/
changeset: c57bcb1d78fc
user: carlfeberhard
date: 2012-11-05 22:49:58
summary: api/history_contents: allow anon-user to query own, current history; improve doc string
affected #: 1 file
diff -r 613df4d4ca7d41c240311991022148dedb2e620d -r c57bcb1d78fcb49e8b856a0c4e28d59626e3403f 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
@@ -26,51 +26,100 @@
def index( self, trans, history_id, ids=None, **kwd ):
"""
GET /api/histories/{encoded_history_id}/contents
- Displays a collection (list) of history contents
+ Displays a collection (list) of history contents (HDAs)
+
+ :param history_id: an encoded id string of the `History` to search
+ :param ids: (optional) a comma separated list of encoded `HDA` ids
+
+ If Ids is not given, index returns a list of *summary* json objects for
+ every `HDA` associated with the given `history_id`.
+ See _summary_hda_dict.
+
+ If ids is given, index returns a *more complete* json object for each
+ HDA in the ids list.
+
+ Note: Anonymous users are allowed to get their current history contents
+ (generally useful for browser UI access of the api)
"""
rval = []
try:
- history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
+ # get the history, if anon user and requesting current history - allow it
+ if( ( trans.user == None )
+ and ( history_id == trans.security.encode_id( trans.history.id ) ) ):
+ #TODO:?? is secure?
+ history = trans.history
- # if no ids passed, return a _SUMMARY_ of _all_ datasets in the history
- if not ids:
- for dataset in history.datasets:
- api_type = "file"
- encoded_id = trans.security.encode_id( dataset.id )
- # build the summary
- rval.append( dict( id = encoded_id,
- type = api_type,
- name = dataset.name,
- url = url_for( 'history_content', history_id=history_id, id=encoded_id, ) ) )
+ # otherwise, check permissions for the history first
+ else:
+ history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
- # if ids, return _FULL_ data (as show) for each id passed
- #NOTE: this might not be the best form (passing all info),
- # but we(I?) need an hda collection with full data somewhere
+ # build the return hda data list
+ if ids:
+ # if ids, return _FULL_ data (as show) for each id passed
+ #NOTE: this might not be the best form (passing all info),
+ # but we(I?) need an hda collection with full data somewhere
+ ids = ids.split( ',' )
+ for hda in history.datasets:
+ if trans.security.encode_id( hda.id ) in ids:
+ rval.append( get_hda_dict( trans, history, hda, for_editing=True ) )
+
else:
- ids = ids.split( ',' )
- for id in ids:
- hda = self.get_history_dataset_association( trans, history, id,
- check_ownership=True, check_accessible=True, check_state=False )
- rval.append( get_hda_dict( trans, history, hda, for_editing=True ) )
+ # if no ids passed, return a _SUMMARY_ of _all_ datasets in the history
+ for hda in history.datasets:
+ rval.append( self._summary_hda_dict( trans, history_id, hda ) )
except Exception, e:
rval = "Error in history API at listing contents"
- log.error( rval + ": %s" % str(e) )
+ log.error( rval + ": %s, %s" % ( type( e ), str( e ) ) )
trans.response.status = 500
+
return rval
+ def _summary_hda_dict( self, trans, history_id, hda ):
+ """
+ Returns a dictionary based on the HDA in .. _summary form::
+ {
+ 'id' : < the encoded dataset id >,
+ 'name' : < currently only returns 'file' >,
+ 'type' : < name of the dataset >,
+ 'url' : < api url to retrieve this datasets full data >,
+ }
+ """
+ api_type = "file"
+ encoded_id = trans.security.encode_id( hda.id )
+ return {
+ 'id' : encoded_id,
+ 'name' : hda.name,
+ 'type' : api_type,
+ 'url' : url_for( 'history_content', history_id=history_id, id=encoded_id, ),
+ }
+
@web.expose_api
def show( self, trans, id, history_id, **kwd ):
"""
GET /api/histories/{encoded_history_id}/contents/{encoded_content_id}
Displays information about a history content (dataset).
+
+
"""
hda_dict = {}
try:
- history = self.get_history( trans, history_id,
- check_ownership=True, check_accessible=True, deleted=False )
- hda = self.get_history_dataset_association( trans, history, id,
- check_ownership=True, check_accessible=True )
+ # for anon users:
+ #TODO: check login_required?
+ #TODO: this isn't actually most_recently_used (as defined in histories)
+ if( ( trans.user == None )
+ and ( history_id == trans.security.encode_id( trans.history.id ) ) ):
+ history = trans.history
+ #TODO: dataset/hda by id (from history) OR check_ownership for anon user
+ hda = self.get_history_dataset_association( trans, history, id,
+ check_ownership=False, check_accessible=True )
+
+ else:
+ history = self.get_history( trans, history_id,
+ check_ownership=True, check_accessible=True, deleted=False )
+ hda = self.get_history_dataset_association( trans, history, id,
+ check_ownership=True, check_accessible=True )
+
hda_dict = get_hda_dict( trans, history, hda, for_editing=True )
except Exception, e:
@@ -112,7 +161,7 @@
return "Not implemented."
-# move these into model?? hell if I know...doesn't seem like the urls should go here
+#TODO: move these into model
def get_hda_dict( trans, history, hda, for_editing ):
hda_dict = hda.get_api_value( view='element' )
@@ -146,13 +195,14 @@
if meta_files:
hda_dict[ 'meta_files' ] = meta_files
- #hda_dict[ 'display_apps' ] = get_display_apps( trans, hda )
+ hda_dict[ 'display_apps' ] = get_display_apps( trans, hda )
hda_dict[ 'visualizations' ] = hda.get_visualizations()
hda_dict[ 'peek' ] = to_unicode( hda.display_peek() )
return hda_dict
def get_display_apps( trans, hda ):
+ #TODO: make more straightforward (somehow)
display_apps = []
def get_display_app_url( display_app_link, hda, trans ):
@@ -165,7 +215,6 @@
app_name=urllib.quote_plus( display_app_link.display_application.id ),
link_name=urllib.quote_plus( display_app_link.id ) )
-
for display_app in hda.get_display_applications( trans ).itervalues():
app_links = []
for display_app_link in display_app.links.itervalues():
https://bitbucket.org/galaxy/galaxy-central/changeset/7942fb2a20c5/
changeset: 7942fb2a20c5
user: carlfeberhard
date: 2012-11-05 22:51:58
summary: api/users.show: added current as viable id to display json for trans.user; show, index: added key 'auota_percent' to json, returns null if no quota on user, percent otherwise
affected #: 1 file
diff -r c57bcb1d78fcb49e8b856a0c4e28d59626e3403f -r 7942fb2a20c5bd4b66f02e5f58335fc1b74cb040 lib/galaxy/webapps/galaxy/api/users.py
--- a/lib/galaxy/webapps/galaxy/api/users.py
+++ b/lib/galaxy/webapps/galaxy/api/users.py
@@ -26,6 +26,7 @@
# only admins can see deleted users
if not trans.user_is_admin():
return []
+
else:
route = 'user'
query = query.filter( trans.app.model.User.table.c.deleted == False )
@@ -33,9 +34,13 @@
if not trans.user_is_admin():
item = trans.user.get_api_value( value_mapper={ 'id': trans.security.encode_id } )
item['url'] = url_for( route, id=item['id'] )
+ item['quota_percent'] = trans.app.quota_agent.get_percent( trans=trans )
return [item]
+
for user in query:
item = user.get_api_value( value_mapper={ 'id': trans.security.encode_id } )
+ #TODO: move into api_values
+ item['quota_percent'] = trans.app.quota_agent.get_percent( trans=trans )
item['url'] = url_for( route, id=item['id'] )
rval.append( item )
return rval
@@ -50,20 +55,36 @@
"""
deleted = util.string_as_bool( deleted )
try:
+ # user is requesting data about themselves
if id == "current":
- user = trans.user
+ # ...and is anonymous - return usage and quota (if any)
+ if not trans.user:
+ item = self.anon_user_api_value( trans )
+ return item
+
+ # ...and is logged in - return full
+ else:
+ user = trans.user
else:
user = self.get_user( trans, id, deleted=deleted )
+
+ # check that the user is requesting themselves (and they aren't del'd) unless admin
if not trans.user_is_admin():
assert trans.user == user
assert not user.deleted
+
except:
if trans.user_is_admin():
raise
else:
raise HTTPBadRequest( detail='Invalid user id ( %s ) specified' % id )
+
item = user.get_api_value( view='element', value_mapper={ 'id': trans.security.encode_id,
'total_disk_usage': float } )
+ #TODO: move into api_values (needs trans, tho - can we do that with api_keys/@property??)
+ #TODO: works with other users (from admin)??
+ item['quota_percent'] = trans.app.quota_agent.get_percent( trans=trans )
+
return item
@web.expose_api
@@ -93,3 +114,16 @@
@web.expose_api
def undelete( self, trans, **kwd ):
raise HTTPNotImplemented()
+
+ #TODO: move to more basal, common resource than this
+ def anon_user_api_value( self, trans ):
+ """
+ Returns data for an anonymous user, truncated to only usage and quota_percent
+ """
+ usage = trans.app.quota_agent.get_usage( trans )
+ percent = trans.app.quota_agent.get_percent( trans=trans, usage=usage )
+ return {
+ 'total_disk_usage' : int( usage ),
+ 'nice_total_disk_usage' : util.nice_size( usage ),
+ 'quota_percent' : percent
+ }
https://bitbucket.org/galaxy/galaxy-central/changeset/24ce9866b5af/
changeset: 24ce9866b5af
user: carlfeberhard
date: 2012-11-05 22:56:28
summary: added user backbone model; added quota meter view on user model
affected #: 5 files
diff -r 7942fb2a20c5bd4b66f02e5f58335fc1b74cb040 -r 24ce9866b5af82325d2047b5d6c346ceae64c041 static/scripts/mvc/user/user-model.js
--- /dev/null
+++ b/static/scripts/mvc/user/user-model.js
@@ -0,0 +1,56 @@
+var User = BaseModel.extend( LoggableMixin ).extend({
+ //logger : console,
+
+ defaults : {
+ id : null,
+ username : "(anonymous user)",
+ email : "",
+ total_disk_usage : 0,
+ nice_total_disk_usage : "0 bytes"
+ },
+
+ initialize : function( data ){
+ this.log( 'User.initialize:', data );
+
+ this.on( 'loaded', function( model, resp ){ this.log( this + ' has loaded:', model, resp ); });
+ this.on( 'change', function( model, data ){ this.log( this + ' has changed:', model, data.changes ); });
+ },
+
+ urlRoot : 'api/users',
+ loadFromApi : function( idOrCurrent, options ){
+ idOrCurrent = idOrCurrent || User.CURRENT_ID_STR;
+ options = options || {};
+ var model = this,
+ userFn = options.success;
+ options.success = function( model, response ){
+ model.trigger( 'loaded', model, response );
+ if( userFn ){ userFn( model, response ); }
+ };
+ if( idOrCurrent === User.CURRENT_ID_STR ){
+ options.url = this.urlRoot + '/' + User.CURRENT_ID_STR;
+ }
+ return BaseModel.prototype.fetch.call( this, options );
+ },
+
+ toString : function(){
+ var userInfo = [ this.get( 'username' ) ];
+ if( this.get( 'id' ) ){
+ userInfo.unshift( this.get( 'id' ) );
+ userInfo.push( this.get( 'email' ) );
+ }
+ return 'User(' + userInfo.join( ':' ) + ')';
+ }
+});
+User.CURRENT_ID_STR = 'current';
+
+User.getCurrentUserFromApi = function( options ){
+ var currentUser = new User();
+ currentUser.loadFromApi( User.CURRENT_ID_STR, options );
+ return currentUser;
+};
+
+var UserCollection = Backbone.Collection.extend( LoggableMixin ).extend({
+ model : User,
+ logger : console,
+ urlRoot : 'api/users'
+});
diff -r 7942fb2a20c5bd4b66f02e5f58335fc1b74cb040 -r 24ce9866b5af82325d2047b5d6c346ceae64c041 static/scripts/mvc/user/user-quotameter.js
--- /dev/null
+++ b/static/scripts/mvc/user/user-quotameter.js
@@ -0,0 +1,118 @@
+// strange view that spans two frames: renders to two separate elements based on a User's disk usage:
+// a quota/usage bar (curr. masthead), and
+// an over-quota message (curr. history panel)
+
+// for now, keep the view in the history panel (where the message is), but render ALSO to the masthead
+
+var UserQuotaMeter = BaseView.extend( LoggableMixin ).extend({
+ logger : console,
+
+ options : {
+ warnAtPercent : 85,
+ errorAtPercent : 100,
+
+ // the quota/usage bar is in the masthead
+ meterDocument : window.top.document,
+ containerSelector : '.quota-meter-container',
+ meterSelector : '#quota-meter',
+ barSelector : '#quota-meter-bar',
+ textSelector : '#quota-meter-text',
+
+ // the quota message currently displays in the history panel
+ msgDocument : ( top.frames.galaxy_history )?( top.frames.galaxy_history.document )
+ :( top.document ),
+ msgSelector : '#quota-message-container',
+
+ warnClass : 'quota-meter-bar-warn',
+ errorClass : 'quota-meter-bar-error',
+ usageTemplate : 'Using <%= nice_total_disk_usage %>',
+ quotaTemplate : 'Using <%= quota_percent %>%',
+ meterTemplate : '', // see where I'm going?
+ animationSpeed : 'fast'
+ },
+
+ initialize : function( options ){
+ this.log( this + '.initialize:', options );
+
+ _.extend( this.options, options );
+
+ //this.bind( 'all', function( event, data ){ this.log( this + ' event:', event, data ); }, this );
+ this.model.bind( 'change:quota_percent change:total_disk_usage', this.render, this );
+ },
+
+ update : function( options ){
+ this.log( this + ' updating user data...', options );
+ this.model.loadFromApi( this.model.get( 'id' ), options );
+ return this;
+ },
+
+ isOverQuota : function(){
+ return ( this.model.get( 'quota_percent' ) !== null
+ && this.model.get( 'quota_percent' ) >= this.options.errorAtPercent );
+ },
+
+ _render_quota : function(){
+ var modelJson = this.model.toJSON(),
+ //prevPercent = this.model.previous( 'quota_percent' ),
+ percent = modelJson.quota_percent,
+ meter = $( UserQuotaMeter.templates.quota( modelJson ) );
+ //this.log( this + '.rendering quota, percent:', percent, 'meter:', meter );
+
+ // OVER QUOTA: color the quota bar and show the quota error message
+ if( this.isOverQuota() ){
+ //this.log( '\t over quota' );
+ meter.addClass( 'progress-danger' );
+ meter.find( '#quota-meter-text' ).css( 'color', 'white' );
+ //TODO: only trigger event if state has changed
+ this.trigger( 'quota:over', modelJson );
+
+ // APPROACHING QUOTA: color the quota bar
+ } else if( percent >= this.options.warnAtPercent ){
+ //this.log( '\t approaching quota' );
+ meter.addClass( 'progress-warning' );
+ //TODO: only trigger event if state has changed
+ this.trigger( 'quota:under quota:under:approaching', modelJson );
+
+ // otherwise, hide/don't use the msg box
+ } else {
+ meter.addClass( 'progress-success' );
+ //TODO: only trigger event if state has changed
+ this.trigger( 'quota:under quota:under:ok', modelJson );
+ }
+ return meter;
+ },
+
+ _render_usage : function(){
+ var usage = $( UserQuotaMeter.templates.usage( this.model.toJSON() ) );
+ this.log( this + '.rendering usage:', usage );
+ return usage;
+ },
+
+ render : function(){
+ //this.log( this + '.rendering' );
+ var meterHtml = null;
+
+ // no quota on server ('quota_percent' === null (can be valid at 0)), show usage instead
+ this.log( this + '.model.quota_percent:', this.model.get( 'quota_percent' ) );
+ if( ( this.model.get( 'quota_percent' ) === null )
+ || ( this.model.get( 'quota_percent' ) === undefined ) ){
+ meterHtml = this._render_usage();
+
+ // otherwise, render percent of quota (and warning, error)
+ } else {
+ meterHtml = this._render_quota();
+ }
+
+ this.$el.html( meterHtml );
+ //this.log( this + '.$el:', this.$el );
+ return this;
+ },
+
+ toString : function(){
+ return 'UserQuotaMeter(' + this.model + ')';
+ }
+});
+UserQuotaMeter.templates = {
+ quota : Handlebars.templates[ 'template-user-quotaMeter-quota' ],
+ usage : Handlebars.templates[ 'template-user-quotaMeter-usage' ]
+};
diff -r 7942fb2a20c5bd4b66f02e5f58335fc1b74cb040 -r 24ce9866b5af82325d2047b5d6c346ceae64c041 static/scripts/templates/compiled/template-user-quotaMeter-quota.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-user-quotaMeter-quota.js
@@ -0,0 +1,19 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-user-quotaMeter-quota'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var buffer = "", stack1, foundHelper, functionType="function", escapeExpression=this.escapeExpression;
+
+
+ buffer += "<div id=\"quota-meter\" class=\"quota-meter progress\">\n <div id=\"quota-meter-bar\" class=\"quota-meter-bar bar\" style=\"width: ";
+ foundHelper = helpers.quota_percent;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.quota_percent; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "%\"></div>\n ";
+ buffer += "\n <div id=\"quota-meter-text\" class=\"quota-meter-text\"style=\"top: 6px\">\n Using ";
+ foundHelper = helpers.quota_percent;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.quota_percent; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "%\n </div>\n</div>";
+ return buffer;});
+})();
\ No newline at end of file
diff -r 7942fb2a20c5bd4b66f02e5f58335fc1b74cb040 -r 24ce9866b5af82325d2047b5d6c346ceae64c041 static/scripts/templates/compiled/template-user-quotaMeter-usage.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-user-quotaMeter-usage.js
@@ -0,0 +1,14 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-user-quotaMeter-usage'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var buffer = "", stack1, foundHelper, functionType="function", escapeExpression=this.escapeExpression;
+
+
+ buffer += "\n<div id=\"quota-meter\" class=\"quota-meter\" style=\"background-color: transparent\">\n <div id=\"quota-meter-text\" class=\"quota-meter-text\" style=\"top: 6px; color: white\">\n Using ";
+ foundHelper = helpers.nice_total_disk_usage;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.nice_total_disk_usage; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\n </div>\n</div>";
+ return buffer;});
+})();
\ No newline at end of file
diff -r 7942fb2a20c5bd4b66f02e5f58335fc1b74cb040 -r 24ce9866b5af82325d2047b5d6c346ceae64c041 static/scripts/templates/ui-templates.html
--- /dev/null
+++ b/static/scripts/templates/ui-templates.html
@@ -0,0 +1,18 @@
+<script type="text/template" class="template-history" id="template-user-quotaMeter-quota">
+<div id="quota-meter" class="quota-meter progress">
+ <div id="quota-meter-bar" class="quota-meter-bar bar" style="width: {{quota_percent}}%"></div>
+ {{! TODO: remove the hardcoded style }}
+ <div id="quota-meter-text" class="quota-meter-text"style="top: 6px">
+ Using {{ quota_percent }}%
+ </div>
+</div>
+</script>
+
+<script type="text/template" class="template-history" id="template-user-quotaMeter-usage">
+{{! TODO: remove the hardcoded styles }}
+<div id="quota-meter" class="quota-meter" style="background-color: transparent">
+ <div id="quota-meter-text" class="quota-meter-text" style="top: 6px; color: white">
+ Using {{ nice_total_disk_usage }}
+ </div>
+</div>
+</script>
https://bitbucket.org/galaxy/galaxy-central/changeset/b996950ffcf2/
changeset: b996950ffcf2
user: carlfeberhard
date: 2012-11-05 22:59:20
summary: (alt)history: incorporate quota meter; bugifxes
affected #: 4 files
diff -r 24ce9866b5af82325d2047b5d6c346ceae64c041 -r b996950ffcf2342f1211dc49e6ca75c4dfd7050f static/scripts/mvc/history.js
--- a/static/scripts/mvc/history.js
+++ b/static/scripts/mvc/history.js
@@ -6,19 +6,44 @@
Backbone.js implementation of history panel
TODO:
+ bug:
+ anon, mako:
+ tooltips not rendered
+ anno, tags rendered
+ title editable
+ bug:
+ when over quota history is re-rendered, over quota msg is not displayed
+ bc the quota:over event isn't fired
+ bc the user state hasn't changed
+
+ anon user, mako template init:
+ bug: rename url seems to be wrong url
+
currently, adding a dataset (via tool execute, etc.) creates a new dataset and refreshes the page
- from mako template:
+ logged in, mako template:
+ BUG: am able to start upload even if over quota - 'runs' forever
+
+ BUG: from above sit, delete uploading hda - now in state 'discarded'! ...new state to handle
+ bug: quotaMeter bar rendering square in chrome
+ BUG: quotaMsg not showing when 100% (on load)
+ BUG: upload, history size, doesn't change
+ TODO: on hdas state:final, update ONLY the size...from what? histories.py? in js?
BUG: imported, shared history with unaccessible dataset errs in historycontents when getting history
(entire history is inaccessible)
- BUG: anon user, broken updater (upload)
- added check_state to UsesHistoryDatasetAssocMixin
- BUG: anon user
- BUG: historyItem, error'd ds show display, download?
+ ??: still happening?
- from loadFromAPI:
+ from loadFromApi:
BUG: not showing previous annotations
fixed:
+ BUG: historyItem, error'd ds show display, download?
+ FIXED: removed
+ bug: loading hdas (alt_hist)
+ FIXED: added anon user api request ( trans.user == None and trans.history.id == requested id )
+ bug: quota meter not updating on upload/tool run
+ FIXED: quotaMeter now listens for 'state:final' from glx_history in alternate_history.mako
+ bug: use of new HDACollection with event listener in init doesn't die...keeps reporting
+ FIXED: change getVisible to return an array
BUG: history, broken intial hist state (running, updater, etc.)
??: doesn't seem to happen anymore
BUG: collapse all should remove all expanded from storage
@@ -40,16 +65,16 @@
HDACollection, meta_files, display_apps, etc.
- break this file up
- localize all
- ?: render url templates on init or render?
- ?: history, annotation won't accept unicode
quota mgr
show_deleted/hidden:
use storage
on/off ui
move histview fadein/out in render to app?
don't draw body until it's first expand event
+ localize all
+ break this file up
+ ?: render url templates on init or render?
+ ?: history, annotation won't accept unicode
hierarchy:
dataset -> hda
@@ -62,6 +87,7 @@
css/html class/id 'item' -> hda
add classes, ids on empty divs
events (local/ui and otherwise)
+ list in docs as well
require.js
convert function comments to jsDoc style, complete comments
move inline styles into base.less
@@ -118,7 +144,7 @@
// based on trans.user (is_admin or security_agent.can_access_dataset( <user_roles>, hda.dataset ))
accessible : false,
- // this needs to be removed (it is a function of the view type (e.g. HDAForEditingView))
+ //TODO: this needs to be removed (it is a function of the view type (e.g. HDAForEditingView))
for_editing : true
},
@@ -139,8 +165,14 @@
this.set( 'state', HistoryDatasetAssociation.STATES.NOT_VIEWABLE );
}
- this.on( 'change', function( event, x, y, z ){
- this.log( this + ' has changed:', event, x, y, z );
+ //this.on( 'change', function( currModel, changedList ){
+ // this.log( this + ' has changed:', currModel, changedList );
+ //});
+ this.on( 'change:state', function( currModel, newState ){
+ this.log( this + ' has changed state:', currModel, newState );
+ if( this.inFinalState() ){
+ this.trigger( 'state:final', currModel, newState, this.previous( 'state' ) );
+ }
});
},
@@ -148,15 +180,6 @@
return ( this.get( 'deleted' ) || this.get( 'purged' ) );
},
- // roughly can_edit from history_common.mako - not deleted or purged = editable
- isEditable : function(){
- return (
- //this.get( 'for_editing' )
- //&& !( this.get( 'deleted' ) || this.get( 'purged' ) )??
- !this.isDeletedOrPurged()
- );
- },
-
// based on show_deleted, show_hidden (gen. from the container control), would this ds show in the list of ds's?
//TODO: too many visibles
isVisible : function( show_deleted, show_hidden ){
@@ -174,11 +197,15 @@
// 'final' states are states where no processing (for the ds) is left to do on the server
inFinalState : function(){
+ var state = this.get( 'state' );
return (
- ( this.get( 'state' ) === HistoryDatasetAssociation.STATES.OK )
- || ( this.get( 'state' ) === HistoryDatasetAssociation.STATES.FAILED_METADATA )
- || ( this.get( 'state' ) === HistoryDatasetAssociation.STATES.EMPTY )
- || ( this.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
+ ( state === HistoryDatasetAssociation.STATES.NEW )
+ || ( state === HistoryDatasetAssociation.STATES.OK )
+ || ( state === HistoryDatasetAssociation.STATES.EMPTY )
+ || ( state === HistoryDatasetAssociation.STATES.FAILED_METADATA )
+ || ( state === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
+ || ( state === HistoryDatasetAssociation.STATES.DISCARDED )
+ || ( state === HistoryDatasetAssociation.STATES.ERROR )
);
},
@@ -199,24 +226,32 @@
//------------------------------------------------------------------------------
HistoryDatasetAssociation.STATES = {
- NOT_VIEWABLE : 'noPermission', // not in trans.app.model.Dataset.states
- NEW : 'new',
UPLOAD : 'upload',
QUEUED : 'queued',
RUNNING : 'running',
+ SETTING_METADATA : 'setting_metadata',
+
+ NEW : 'new',
OK : 'ok',
EMPTY : 'empty',
- ERROR : 'error',
+
+ FAILED_METADATA : 'failed_metadata',
+ NOT_VIEWABLE : 'noPermission', // not in trans.app.model.Dataset.states
DISCARDED : 'discarded',
- SETTING_METADATA : 'setting_metadata',
- FAILED_METADATA : 'failed_metadata'
+ ERROR : 'error'
};
//==============================================================================
var HDACollection = Backbone.Collection.extend( LoggableMixin ).extend({
model : HistoryDatasetAssociation,
- logger : console,
+ //logger : console,
+
+ initialize : function(){
+ //this.bind( 'all', function( x, y, z ){
+ // console.info( this + '', x, y, z );
+ //});
+ },
// return the ids of every hda in this collection
ids : function(){
@@ -225,9 +260,7 @@
// return an HDA collection containing every 'shown' hda based on show_deleted/hidden
getVisible : function( show_deleted, show_hidden ){
- return new HDACollection(
- this.filter( function( item ){ return item.isVisible( show_deleted, show_hidden ); })
- );
+ return this.filter( function( item ){ return item.isVisible( show_deleted, show_hidden ); });
},
// get a map where <possible hda state> : [ <list of hda ids in that state> ]
@@ -243,6 +276,17 @@
return stateLists;
},
+ // returns the id of every hda still running (not in a final state)
+ running : function(){
+ var idList = [];
+ this.each( function( item ){
+ if( !item.inFinalState() ){
+ idList.push( item.get( 'id' ) );
+ }
+ });
+ return idList;
+ },
+
// update (fetch -> render) the hdas with the ids given
update : function( ids ){
this.log( this + 'update:', ids );
@@ -307,13 +351,13 @@
this.checkForUpdates();
}
- this.on( 'change', function( event, x, y, z ){
- this.log( this + ' has changed:', event, x, y, z );
- });
+ //this.on( 'change', function( currModel, changedList ){
+ // this.log( this + ' has changed:', currModel, changedList );
+ //});
},
// get data via the api (alternative to sending options,hdas to initialize)
- loadFromAPI : function( historyId, callback ){
+ loadFromApi : function( historyId, callback ){
var history = this;
// fetch the history AND the user (mainly to see if they're logged in at this point)
@@ -353,43 +397,13 @@
// get the history's state from it's cummulative ds states, delay + update if needed
checkForUpdates : function( datasets ){
// get overall History state from collection, run updater if History has running/queued hdas
- this.stateFromStateIds();
- if( ( this.get( 'state' ) === HistoryDatasetAssociation.STATES.RUNNING )
- || ( this.get( 'state' ) === HistoryDatasetAssociation.STATES.QUEUED ) ){
+ // boiling it down on the client to running/not
+ if( this.hdas.running().length ){
this.stateUpdater();
}
return this;
},
- // sets history state based on current hdas' states
- // ported from api/histories.traverse (with placement of error state changed)
- stateFromStateIds : function(){
- var stateIdLists = this.hdas.getStateLists();
- this.attributes.state_ids = stateIdLists;
-
- //TODO: make this more concise
- if( ( stateIdLists.running.length > 0 )
- || ( stateIdLists.upload.length > 0 )
- || ( stateIdLists.setting_metadata.length > 0 ) ){
- this.set( 'state', HistoryDatasetAssociation.STATES.RUNNING );
-
- } else if( stateIdLists.queued.length > 0 ){
- this.set( 'state', HistoryDatasetAssociation.STATES.QUEUED );
-
- } else if( ( stateIdLists.error.length > 0 )
- || ( stateIdLists.failed_metadata.length > 0 ) ){
- this.set( 'state', HistoryDatasetAssociation.STATES.ERROR );
-
- } else if( stateIdLists.ok.length === this.hdas.length ){
- this.set( 'state', HistoryDatasetAssociation.STATES.OK );
-
- } else {
- throw( this + '.stateFromStateDetails: unable to determine '
- + 'history state from hda states: ' + this.get( 'state_ids' ) );
- }
- return this;
- },
-
// update this history, find any hda's running/queued, update ONLY those that have changed states,
// set up to run this again in some interval of time
stateUpdater : function(){
@@ -407,6 +421,7 @@
history.set( response );
history.log( 'current history state:', history.get( 'state' ), '(was)', oldState );
+ //TODO: revisit this - seems too elaborate, need something straightforward
// for each state, check for the difference between old dataset states and new
// the goal here is to check ONLY those datasets that have changed states (not all datasets)
var changedIds = [];
@@ -451,7 +466,7 @@
// view for HistoryDatasetAssociation model above
// uncomment this out see log messages
- logger : console,
+ //logger : console,
tagName : "div",
className : "historyItemContainer",
@@ -537,6 +552,9 @@
view.$el.append( itemWrapper ).fadeIn( 'fast', function(){
view.log( view + ' rendered:', view.$el );
view.trigger( 'rendered' );
+ if( view.model.inFinalState() ){
+ view.trigger( 'rendered:final' );
+ }
});
});
return this;
@@ -697,7 +715,7 @@
var modelData = _.extend( this.model.toJSON(), { urls: this.urls } );
// if there's no dbkey and it's editable : pass a flag to the template to render a link to editing in the '?'
if( this.model.get( 'metadata_dbkey' ) === '?'
- && this.model.isEditable() ){
+ && !this.model.isDeletedOrPurged() ){
_.extend( modelData, { dbkey_unknown_and_editable : true });
}
return HDAView.templates.hdaSummary( modelData );
@@ -1290,7 +1308,7 @@
var HistoryView = BaseView.extend( LoggableMixin ).extend({
// uncomment this out see log messages
- logger : console,
+ //logger : console,
// direct attachment to existing element
el : 'body.historyPage',
@@ -1369,7 +1387,7 @@
// render the main template, tooltips
//NOTE: this is done before the items, since item views should handle theirs themselves
newRender.append( HistoryView.templates.historyPanel( modelJson ) );
- historyView.$el.find( '.tooltip' ).tooltip();
+ newRender.find( '.tooltip' ).tooltip();
// render hda views (if any)
if( !this.model.hdas.length
@@ -1394,6 +1412,7 @@
//TODO: ideally, these would be set up before the fade in (can't because of async save text)
historyView.setUpBehaviours();
+
historyView.trigger( 'rendered' );
next();
});
@@ -1410,7 +1429,7 @@
visibleHdas = this.model.hdas.getVisible( show_deleted, show_hidden );
// only render the shown hdas
- visibleHdas.each( function( hda ){
+ _.each( visibleHdas, function( hda ){
var hdaId = hda.get( 'id' ),
expanded = historyView.storage.get( 'expandedHdas' ).get( hdaId );
historyView.hdaViews[ hdaId ] = new HDAView({
@@ -1428,21 +1447,28 @@
// set up HistoryView->HDAView listeners
setUpHdaListeners : function( hdaView ){
- var history = this;
+ var historyView = this;
// use storage to maintain a list of hdas whose bodies are expanded
hdaView.bind( 'toggleBodyVisibility', function( id, visible ){
if( visible ){
- history.storage.get( 'expandedHdas' ).set( id, true );
+ historyView.storage.get( 'expandedHdas' ).set( id, true );
} else {
- history.storage.get( 'expandedHdas' ).deleteKey( id );
+ historyView.storage.get( 'expandedHdas' ).deleteKey( id );
}
});
+
+ // rendering listeners
+ //hdaView.bind( 'rendered', function(){});
+ hdaView.bind( 'rendered:final', function(){ historyView.trigger( 'hda:rendered:final' ); });
},
// set up js/widget behaviours: tooltips,
//TODO: these should be either sub-MVs, or handled by events
setUpBehaviours : function(){
+ // anon users shouldn't have access to any of these
+ if( !( this.model.get( 'user' ) && this.model.get( 'user' ).email ) ){ return; }
+
// annotation slide down
var historyAnnotationArea = this.$( '#history-annotation-area' );
this.$( '#history-annotate' ).click( function() {
@@ -1463,6 +1489,20 @@
this.urls.annotate, "new_annotation", 18, true, 4 );
},
+ //TODO: this seems more like a per user message than a history message; IOW, this doesn't belong here
+ showQuotaMessage : function( userData ){
+ var msg = this.$el.find( '#quota-message-container' );
+ //this.log( this + ' showing quota message:', msg, userData );
+ if( msg.is( ':hidden' ) ){ msg.slideDown( 'fast' ); }
+ },
+
+ //TODO: this seems more like a per user message than a history message
+ hideQuotaMessage : function( userData ){
+ var msg = this.$el.find( '#quota-message-container' );
+ //this.log( this + ' hiding quota message:', msg, userData );
+ if( !msg.is( ':hidden' ) ){ msg.slideUp( 'fast' ); }
+ },
+
events : {
'click #history-collapse-all' : 'hideAllHdaBodies',
'click #history-tag' : 'loadAndDisplayTags'
@@ -1470,7 +1510,7 @@
// collapse all hda bodies
hideAllHdaBodies : function(){
- _.each( this.itemViews, function( item ){
+ _.each( this.hdaViews, function( item ){
item.toggleBodyVisibility( null, false );
});
this.storage.set( 'expandedHdas', {} );
diff -r 24ce9866b5af82325d2047b5d6c346ceae64c041 -r b996950ffcf2342f1211dc49e6ca75c4dfd7050f static/scripts/templates/compiled/template-history-historyPanel.js
--- a/static/scripts/templates/compiled/template-history-historyPanel.js
+++ b/static/scripts/templates/compiled/template-history-historyPanel.js
@@ -16,42 +16,52 @@
function program3(depth0,data) {
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n <div id=\"history-name\" style=\"margin-right: 50px;\" class=\"tooltip\"\n title=\"You must be logged in to edit your history name\">";
+ foundHelper = helpers.name;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</div>\n ";
+ return buffer;}
+
+function program5(depth0,data) {
+
return "refresh";}
-function program5(depth0,data) {
+function program7(depth0,data) {
return "collapse all";}
-function program7(depth0,data) {
+function program9(depth0,data) {
var buffer = "", stack1, foundHelper;
buffer += "\n <a id=\"history-tag\" title=\"";
foundHelper = helpers.local;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(8, program8, data)}); }
- else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(8, program8, data)}); }
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\"\n class=\"icon-button tags tooltip\" target=\"galaxy_main\" href=\"javascript:void(0)\"></a>\n <a id=\"history-annotate\" title=\"";
- foundHelper = helpers.local;
if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(10, program10, data)}); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(10, program10, data)}); }
if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\"\n class=\"icon-button tags tooltip\" target=\"galaxy_main\" href=\"javascript:void(0)\"></a>\n <a id=\"history-annotate\" title=\"";
+ foundHelper = helpers.local;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(12, program12, data)}); }
+ else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(12, program12, data)}); }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\"\n class=\"icon-button annotate tooltip\" target=\"galaxy_main\" href=\"javascript:void(0)\"></a>\n ";
return buffer;}
-function program8(depth0,data) {
+function program10(depth0,data) {
return "Edit history tags";}
-function program10(depth0,data) {
+function program12(depth0,data) {
return "Edit history annotation";}
-function program12(depth0,data) {
+function program14(depth0,data) {
var buffer = "", stack1, foundHelper;
buffer += "\n <a href=\"";
@@ -60,18 +70,18 @@
stack1 = typeof stack1 === functionType ? stack1() : stack1;
buffer += escapeExpression(stack1) + "\">";
foundHelper = helpers.local;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(13, program13, data)}); }
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(15, program15, data)}); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(13, program13, data)}); }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(15, program15, data)}); }
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "</a>\n ";
return buffer;}
-function program13(depth0,data) {
+function program15(depth0,data) {
return "hide deleted";}
-function program15(depth0,data) {
+function program17(depth0,data) {
var buffer = "", stack1, foundHelper;
buffer += "\n <a href=\"";
@@ -80,52 +90,52 @@
stack1 = typeof stack1 === functionType ? stack1() : stack1;
buffer += escapeExpression(stack1) + "\">";
foundHelper = helpers.local;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(16, program16, data)}); }
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(18, program18, data)}); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(16, program16, data)}); }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(18, program18, data)}); }
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "</a>\n ";
return buffer;}
-function program16(depth0,data) {
+function program18(depth0,data) {
return "hide hidden";}
-function program18(depth0,data) {
+function program20(depth0,data) {
var buffer = "", stack1, foundHelper;
buffer += "\n";
foundHelper = helpers.warningmessagesmall;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(19, program19, data)}); }
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(21, program21, data)}); }
else { stack1 = depth0.warningmessagesmall; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- if (!helpers.warningmessagesmall) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(19, program19, data)}); }
+ if (!helpers.warningmessagesmall) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(21, program21, data)}); }
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n";
return buffer;}
-function program19(depth0,data) {
+function program21(depth0,data) {
var stack1, foundHelper;
foundHelper = helpers.local;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(20, program20, data)}); }
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(22, program22, data)}); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(20, program20, data)}); }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(22, program22, data)}); }
if(stack1 || stack1 === 0) { return stack1; }
else { return ''; }}
-function program20(depth0,data) {
+function program22(depth0,data) {
return "You are currently viewing a deleted history!";}
-function program22(depth0,data) {
+function program24(depth0,data) {
var buffer = "", stack1;
buffer += "\n <div id=\"history-tag-area\" style=\"display: none\">\n <strong>Tags:</strong>\n <div class=\"tag-elt\"></div>\n </div>\n\n <div id=\"history-annotation-area\" style=\"display: none\">\n <strong>Annotation / Notes:</strong>\n <div id=\"history-annotation-container\">\n <div id=\"history-annotation\" class=\"tooltip editable-text\" title=\"Click to edit annotation\">\n ";
stack1 = depth0.annotation;
- stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.program(25, program25, data),fn:self.program(23, program23, data)});
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.program(27, program27, data),fn:self.program(25, program25, data)});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </div>\n </div>\n </div>\n ";
return buffer;}
-function program23(depth0,data) {
+function program25(depth0,data) {
var buffer = "", stack1, foundHelper;
buffer += "\n ";
@@ -135,12 +145,12 @@
buffer += escapeExpression(stack1) + "\n ";
return buffer;}
-function program25(depth0,data) {
+function program27(depth0,data) {
return "\n <em>Describe or add notes to history</em>\n ";}
-function program27(depth0,data) {
+function program29(depth0,data) {
var buffer = "", stack1, foundHelper;
buffer += "\n<div id=\"message-container\">\n <div class=\"";
@@ -154,11 +164,6 @@
buffer += escapeExpression(stack1) + "\n </div><br />\n</div>\n";
return buffer;}
-function program29(depth0,data) {
-
-
- return "\n <div id=\"quota-message\" class=\"errormessage\">\n You are over your disk quota. Tool execution is on hold until your disk usage drops below your allocated quota.\n </div>\n <br/>\n ";}
-
function program31(depth0,data) {
@@ -171,13 +176,14 @@
else { stack1 = depth0.nice_size; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
buffer += escapeExpression(stack1) + "</div>\n ";
stack1 = depth0.user;
- stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
+ stack1 = stack1 == null || stack1 === false ? stack1 : stack1.email;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.program(3, program3, data),fn:self.program(1, program1, data)});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </div> \n</div>\n<div style=\"clear: both;\"></div>\n\n<div id=\"top-links\" class=\"historyLinks\">\n <a title=\"";
foundHelper = helpers.local;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(3, program3, data)}); }
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(5, program5, data)}); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(3, program3, data)}); }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(5, program5, data)}); }
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\" class=\"icon-button arrow-circle tooltip\" href=\"";
stack1 = depth0.urls;
@@ -185,42 +191,40 @@
stack1 = typeof stack1 === functionType ? stack1() : stack1;
buffer += escapeExpression(stack1) + "\"></a>\n <a title='";
foundHelper = helpers.local;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(5, program5, data)}); }
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{},inverse:self.noop,fn:self.program(7, program7, data)}); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(5, program5, data)}); }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(7, program7, data)}); }
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "' id=\"history-collapse-all\"\n class='icon-button toggle tooltip' href='javascript:void(0);'></a>\n <div style=\"width: 40px; float: right; white-space: nowrap;\">\n ";
stack1 = depth0.user;
- stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(7, program7, data)});
+ stack1 = stack1 == null || stack1 === false ? stack1 : stack1.email;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(9, program9, data)});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </div>\n</div>\n<div style=\"clear: both;\"></div>\n\n";
buffer += "\n<div class=\"historyLinks\">\n ";
stack1 = depth0.show_deleted;
- stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(12, program12, data)});
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(14, program14, data)});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n ";
stack1 = depth0.show_hidden;
- stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(15, program15, data)});
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(17, program17, data)});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n</div>\n\n";
stack1 = depth0.deleted;
- stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(18, program18, data)});
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(20, program20, data)});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n\n";
buffer += "\n";
buffer += "\n<div style=\"margin: 0px 5px 10px 5px\">\n\n ";
stack1 = depth0.user;
- stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(22, program22, data)});
+ stack1 = stack1 == null || stack1 === false ? stack1 : stack1.email;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(24, program24, data)});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n</div>\n\n";
stack1 = depth0.message;
- stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(27, program27, data)});
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n\n<div id=\"quota-message-container\">\n ";
- stack1 = depth0.over_quota;
stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(29, program29, data)});
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n</div>\n\n<div id=\"";
+ buffer += "\n\n<div id=\"quota-message-container\" style=\"display: none\">\n <div id=\"quota-message\" class=\"errormessage\">\n You are over your disk quota. Tool execution is on hold until your disk usage drops below your allocated quota.\n </div>\n</div>\n\n<div id=\"";
foundHelper = helpers.id;
if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
diff -r 24ce9866b5af82325d2047b5d6c346ceae64c041 -r b996950ffcf2342f1211dc49e6ca75c4dfd7050f static/scripts/templates/history-templates.html
--- a/static/scripts/templates/history-templates.html
+++ b/static/scripts/templates/history-templates.html
@@ -110,9 +110,12 @@
<div id="history-name-container" style="position: relative;">
{{! TODO: factor out conditional css }}
<div id="history-size" style="position: absolute; top: 3px; right: 0px;">{{nice_size}}</div>
- {{#if user}}
+ {{#if user.email}}
<div id="history-name" style="margin-right: 50px;" class="tooltip editable-text"
title="Click to rename history">{{name}}</div>
+ {{else}}
+ <div id="history-name" style="margin-right: 50px;" class="tooltip"
+ title="You must be logged in to edit your history name">{{name}}</div>
{{/if}}
</div></div>
@@ -123,7 +126,7 @@
<a title='{{#local}}collapse all{{/local}}' id="history-collapse-all"
class='icon-button toggle tooltip' href='javascript:void(0);'></a><div style="width: 40px; float: right; white-space: nowrap;">
- {{#if user}}
+ {{#if user.email}}
<a id="history-tag" title="{{#local}}Edit history tags{{/local}}"
class="icon-button tags tooltip" target="galaxy_main" href="javascript:void(0)"></a><a id="history-annotate" title="{{#local}}Edit history annotation{{/local}}"
@@ -151,7 +154,7 @@
{{! TODO: move inline styles out }}
<div style="margin: 0px 5px 10px 5px">
- {{#if user}}
+ {{#if user.email}}
<div id="history-tag-area" style="display: none"><strong>Tags:</strong><div class="tag-elt"></div>
@@ -180,13 +183,10 @@
</div>
{{/if}}
-<div id="quota-message-container">
- {{#if over_quota}}
+<div id="quota-message-container" style="display: none"><div id="quota-message" class="errormessage">
You are over your disk quota. Tool execution is on hold until your disk usage drops below your allocated quota.
</div>
- <br/>
- {{/if}}
</div><div id="{{id}}-datasets" class="history-datasets-list"></div>
diff -r 24ce9866b5af82325d2047b5d6c346ceae64c041 -r b996950ffcf2342f1211dc49e6ca75c4dfd7050f templates/root/alternate_history.mako
--- a/templates/root/alternate_history.mako
+++ b/templates/root/alternate_history.mako
@@ -90,10 +90,13 @@
hda_class_name = 'HistoryDatasetAssociation'
encoded_id_template = '<%= id %>'
- username_template = '<%= username %>'
+ #username_template = '<%= username %>'
hda_ext_template = '<%= file_ext %>'
meta_type_template = '<%= file_type %>'
+ display_app_name_template = '<%= name %>'
+ display_app_link_template = '<%= link %>'
+
url_dict = {
# ................................................................ warning message links
'purge' : h.url_for( controller='dataset', action='purge',
@@ -108,6 +111,7 @@
# ................................................................ title actions (display, edit, delete),
'display' : h.url_for( controller='dataset', action='display',
dataset_id=encoded_id_template, preview=True, filename='' ),
+ #TODO:
#'user_display_url' : h.url_for( controller='dataset', action='display_by_username_and_slug',
# username=username_template, slug=encoded_id_template, filename='' ),
'edit' : h.url_for( controller='dataset', action='edit',
@@ -141,8 +145,9 @@
item_class=hda_class_name, item_id=encoded_id_template ),
},
'annotation' : {
+ #TODO: needed? doesn't look like this is used (unless graceful degradation)
#'annotate_url' : h.url_for( controller='dataset', action='annotate',
- # id=encoded_id_template ), # doesn't look like this is used (unless graceful degradation)
+ # id=encoded_id_template ),
'get' : h.url_for( controller='dataset', action='get_annotation_async',
id=encoded_id_template ),
'set' : h.url_for( controller='/dataset', action='annotate_async',
@@ -167,33 +172,24 @@
<%def name="get_current_user()"><%
- if not trans.user:
- return '{}'
- return trans.webapp.api_controllers[ 'users' ].show(
- trans, trans.security.encode_id( trans.user.id ) )
+ return trans.webapp.api_controllers[ 'users' ].show( trans, 'current' )
%></%def><%def name="get_hdas( history_id, hdas )"><%
+ #BUG: one inaccessible dataset will error entire list
+
if not hdas:
return '[]'
# rather just use the history.id (wo the datasets), but...
- #BUG: one inaccessible dataset will error entire list
return trans.webapp.api_controllers[ 'history_contents' ].index(
+ #trans, trans.security.encode_id( history_id ),
trans, trans.security.encode_id( history_id ),
ids=( ','.join([ trans.security.encode_id( hda.id ) for hda in hdas ]) ) )
%></%def>
-<%def name="print_visualizations( datasets )">
-<%
- for dataset in datasets:
- print trans.security.encode_id( dataset.id )
- print dataset.get_visualizations()
-
-%>
-</%def>
## -----------------------------------------------------------------------------
<%def name="javascripts()">
@@ -220,13 +216,17 @@
"template-history-annotationArea",
"template-history-displayApps",
- "template-history-historyPanel"
+ "template-history-historyPanel",
+
+ "template-user-quotaMeter-quota",
+ "template-user-quotaMeter-usage"
)}
##TODO: fix: curr hasta be _after_ h.templates - move somehow
${h.js(
- "mvc/history"
- ##"mvc/tags", "mvc/annotations"
+ "mvc/history",
+ ##"mvc/tags", "mvc/annotations",
+ "mvc/user/user-model", "mvc/user/user-quotameter"
)}
<script type="text/javascript">
@@ -243,31 +243,65 @@
if( console && console.debug ){
//if( console.clear ){ console.clear(); }
console.debug( 'using backbone.js in history panel' );
+ console.pretty = function( o ){ $( '<pre/>' ).text( JSON.stringify( o, null, ' ' ) ).appendTo( 'body' ); }
}
- // Navigate to a dataset.
- console.debug( 'getting data' );
+
+ // load initial data in this page - since we're already sending it...
var user = ${ get_current_user() },
history = ${ get_history( history.id ) },
hdas = ${ get_hdas( history.id, datasets ) };
- //console.debug( 'user:', user );
- //console.debug( 'history:', history );
- //console.debug( 'hdas:', hdas );
+ console.debug( 'user:', user );
+ console.debug( 'history:', history );
+ console.debug( 'hdas:', hdas );
- // i don't like this, but user authentication changes views/behaviour
+ // i don't like this relationship, but user authentication changes views/behaviour
history.user = user;
history.show_deleted = ${ 'true' if show_deleted else 'false' };
history.show_hidden = ${ 'true' if show_hidden else 'false' };
-
+
//console.debug( 'galaxy_paths:', galaxy_paths );
var glx_history = new History( history, hdas );
- var glx_history_view = new HistoryView({ model: glx_history, urlTemplates: galaxy_paths.attributes }).render();
+ glx_history.logger = console;
+ var glx_history_view = new HistoryView({
+ model: glx_history,
+ urlTemplates: galaxy_paths.attributes,
+ logger: console
+ });
+ glx_history_view.render();
+
+
+ // ...OR load from the api
//var glx_history = new History().setPaths( galaxy_paths ),
// glx_history_view = new HistoryView({ model: glx_history });
//console.warn( 'fetching' );
- //glx_history.loadFromAPI( pageData.history.id );
+ //glx_history.loadFromApi( pageData.history.id );
+
+ // quota meter is a cross-frame ui element (meter in masthead, over quota message in history)
+ // create it and join them here for now (via events)
+ window.currUser = new User( user );
+ //window.currUser.logger = console;
+ window.quotaMeter = new UserQuotaMeter({ model: currUser, el: $( top.document ).find( '.quota-meter-container' ) });
+ window.quotaMeter.render();
+ //window.quotaMeter.logger = console;
+
+ // show/hide the 'over quota message' in the history when the meter tells it to
+ quotaMeter.bind( 'quota:over', glx_history_view.showQuotaMessage, glx_history_view );
+ quotaMeter.bind( 'quota:under', glx_history_view.hideQuotaMessage, glx_history_view );
+ // having to add this to handle re-render of hview while overquota (the above do not fire)
+ glx_history_view.on( 'rendered', function(){
+ if( window.quotaMeter.isOverQuota() ){
+ glx_history_view.showQuotaMessage();
+ }
+ });
+
+ // update the quota meter when any hda reaches a 'final' state
+ //NOTE: use an anon function or update will be passed the hda and think it's the options param
+ glx_history_view.on( 'hda:rendered:final', function(){ window.quotaMeter.update({}) }, window.quotaMeter )
+
+
if( console && console.debug ){
window.user = top.user = user;
window._history = top._history = history;
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: Cloud Launch: Fix non-keyboard paste detection for cluster lookup step.
by Bitbucket 05 Nov '12
by Bitbucket 05 Nov '12
05 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/9d6a61f060d3/
changeset: 9d6a61f060d3
user: dannon
date: 2012-11-05 21:30:44
summary: Cloud Launch: Fix non-keyboard paste detection for cluster lookup step.
affected #: 1 file
diff -r 4d0bfc13fd582083eb2caec31029a3b4664da4ce -r 9d6a61f060d359a0289b3163990f6a8ba122d253 templates/cloud/index.mako
--- a/templates/cloud/index.mako
+++ b/templates/cloud/index.mako
@@ -76,7 +76,7 @@
}
});
//When id_secret and id_key are complete, submit to get_account_info
- $("#id_secret, #id_key_id").bind("change paste keyup", function(){
+ $("#id_secret, #id_key_id").bind("change paste keyup input propertychange", function(){
secret_el = $("#id_secret");
key_el = $("#id_key_id");
if (secret_el.val().length === 40 && key_el.val().length === 20){
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: Security: Prevent potential reflected XSS via MessageException (possible in the case of invalid/malicious id lookups, for instance). Cleanup imports.
by Bitbucket 05 Nov '12
by Bitbucket 05 Nov '12
05 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/4d0bfc13fd58/
changeset: 4d0bfc13fd58
user: dannon
date: 2012-11-05 21:21:05
summary: Security: Prevent potential reflected XSS via MessageException (possible in the case of invalid/malicious id lookups, for instance). Cleanup imports.
affected #: 1 file
diff -r 873dba0459da52335fab1dab38de88a92d44a264 -r 4d0bfc13fd582083eb2caec31029a3b4664da4ce lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -4,19 +4,19 @@
import pkg_resources
-import os, sys, time, socket, random, string
+import os, time, socket, random, string
import inspect
from Cookie import CookieError
pkg_resources.require( "Cheetah" )
from Cheetah.Template import Template
import base
-import pickle
from functools import wraps
from galaxy import util
from galaxy.exceptions import MessageException
from galaxy.util.json import to_json_string, from_json_string
from galaxy.util.backports.importlib import import_module
+from galaxy.util.sanitize_html import sanitize_html
pkg_resources.require( "simplejson" )
import simplejson
@@ -240,11 +240,11 @@
output_encoding = 'utf-8' )
def handle_controller_exception( self, e, trans, **kwargs ):
+ if isinstance( e, MessageException ):
+ #In the case of a controller exception, sanitize to make sure unsafe html input isn't reflected back to the user
+ return trans.show_message( sanitize_html(e.err_msg), e.type )
- if isinstance( e, MessageException ):
- return trans.show_message( e.err_msg, e.type )
def make_body_iterable( self, trans, body ):
-
if isinstance( body, FormBuilder ):
body = trans.show_form( body )
return base.WebApplication.make_body_iterable( self, trans, body )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dan: Add a message about tools that will not be enabled during migration due to not being in the tool_conf.xml.
by Bitbucket 05 Nov '12
by Bitbucket 05 Nov '12
05 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/873dba0459da/
changeset: 873dba0459da
user: dan
date: 2012-11-05 17:09:50
summary: Add a message about tools that will not be enabled during migration due to not being in the tool_conf.xml.
affected #: 1 file
diff -r 8afb981f46c11886a6106fa317936e709c311791 -r 873dba0459da52335fab1dab38de88a92d44a264 lib/galaxy/tool_shed/install_manager.py
--- a/lib/galaxy/tool_shed/install_manager.py
+++ b/lib/galaxy/tool_shed/install_manager.py
@@ -143,6 +143,8 @@
tool_panel_dict_for_tool_config = generate_tool_panel_dict_for_tool_config( guid, tool_config, tool_sections=tool_sections )
for k, v in tool_panel_dict_for_tool_config.items():
tool_panel_dict_for_display[ k ] = v
+ else:
+ print 'The tool "%s" (%s) has not been enabled because it is not defined in a proprietary tool config (%s).' % ( guid, tool_config, ", ".join( self.proprietary_tool_confs or [] ) )
metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=self.app,
repository=tool_shed_repository,
repository_clone_url=repository_clone_url,
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
4 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/a1a547faab1e/
changeset: a1a547faab1e
user: jgoecks
date: 2012-11-04 17:04:55
summary: Unordered GTF parsing enhancements: (a) make more lenient by default so that transcript_ids are local to chromosomes, as is the case for some popular data sources and (b) add 'strict' mode for strict parsing.
affected #: 1 file
diff -r b3585fb0c98639a57d8934f09f4553a3ad69e80b -r a1a547faab1e43c09fa53c6bb8470bd11deaef7d lib/galaxy/datatypes/util/gff_util.py
--- a/lib/galaxy/datatypes/util/gff_util.py
+++ b/lib/galaxy/datatypes/util/gff_util.py
@@ -359,23 +359,35 @@
attrs_strs.append( format_string % ( name, value ) )
return " ; ".join( attrs_strs )
-def read_unordered_gtf( iterator ):
+def read_unordered_gtf( iterator, strict=False ):
"""
Returns GTF features found in an iterator. GTF lines need not be ordered
or clustered for reader to work. Reader returns GFFFeature objects sorted
by transcript_id, chrom, and start position.
"""
+
+ # -- Get function that generates line/feature key. --
+
+ get_transcript_id = lambda fields: parse_gff_attributes( fields[8] )[ 'transcript_id' ]
+ if strict:
+ # Strict GTF parsing uses transcript_id only to group lines into feature.
+ key_fn = get_transcript_id
+ else:
+ # Use lenient parsing where chromosome + transcript_id is the key. This allows
+ # transcripts with same ID on different chromosomes; this occurs in some popular
+ # datasources, such as RefGenes in UCSC.
+ key_fn = lambda fields: fields[0] + '_' + get_transcript_id( fields )
+
# Aggregate intervals by transcript_id.
feature_intervals = odict()
for count, line in enumerate( iterator ):
- line_attrs = parse_gff_attributes( line.split('\t')[8] )
- transcript_id = line_attrs[ 'transcript_id' ]
- if transcript_id in feature_intervals:
- feature = feature_intervals[ transcript_id ]
+ line_key = key_fn( line.split('\t') )
+ if line_key in feature_intervals:
+ feature = feature_intervals[ line_key ]
else:
feature = []
- feature_intervals[ transcript_id ] = feature
+ feature_intervals[ line_key ] = feature
feature.append( GFFInterval( None, line.split( '\t' ) ) )
# Create features.
https://bitbucket.org/galaxy/galaxy-central/changeset/b49bf5d522ee/
changeset: b49bf5d522ee
user: jgoecks
date: 2012-11-04 20:49:46
summary: Transition track labels when adding/removing tracks.
affected #: 1 file
diff -r a1a547faab1e43c09fa53c6bb8470bd11deaef7d -r b49bf5d522eef420b1dff41690aa09abb6492202 static/scripts/viz/circster.js
--- a/static/scripts/viz/circster.js
+++ b/static/scripts/viz/circster.js
@@ -40,6 +40,7 @@
.selectAll("g")
.data(dataHandler)
.enter().append("g")
+ .attr("class", "tick")
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" +
"translate(" + d.radius + ",0)";
@@ -214,9 +215,14 @@
}
self.zoom_drag_timeout = setTimeout(function() {
// Render more detail in tracks' visible elements.
+ // FIXME: do not do this right now; it is not fully implemented--e.g. data bounds
+ // are not updated when new data is fetched--and fetching more detailed quantitative
+ // data is not that useful.
+ /*
_.each(self.circular_views, function(view) {
view.update_scale(scale);
});
+ */
}, 400);
}
}))
@@ -452,6 +458,8 @@
this.parent_elt.selectAll('g>path.chrom-background').transition().duration(1000).attr('d', new_d);
this._transition_chrom_data();
+
+ this._transition_labels();
},
/**
@@ -547,6 +555,11 @@
},
/**
+ * Transition labels to new values (e.g new radius or data bounds).
+ */
+ _transition_labels: function() {},
+
+ /**
* Update data bounds.
*/
_update_data_bounds: function() {
@@ -669,7 +682,6 @@
/** Returns an array of tick angles and labels, given a chrom arc. */
var chromArcTicks = function(d) {
- if (d.endAngle - d.startAngle < self.min_arc_len) { return []; }
var k = (d.endAngle - d.startAngle) / d.value,
ticks = d3.range(0, d.value, 25000000).map(function(v, i) {
return {
@@ -694,7 +706,10 @@
return d.angle > Math.PI ? "rotate(180)translate(-16)" : null;
};
- this.drawTicks(this.parent_elt, this.chroms_layout, chromArcTicks, textTransform);
+ // Filter chroms for only those large enough for display.
+ var visibleChroms = _.filter(this.chroms_layout, function(c) { return c.endAngle - c.startAngle > self.min_arc_len; });
+
+ this.drawTicks(this.parent_elt, visibleChroms, chromArcTicks, textTransform);
}
});
_.extend(CircsterChromLabelTrackView.prototype, UsesTicks);
@@ -755,37 +770,67 @@
.angle(line.angle());
},
+ /**
+ * Render track min, max using ticks.
+ */
render_labels: function() {
- // -- Render min and max using ticks. --
var self = this,
// Keep counter of visible chroms.
- visibleChroms = 0,
- dataBoundsTicks = function(d) {
- // Do not add ticks to small chroms.
- if (d.endAngle - d.startAngle < 0.08) { return []; }
-
- // Only show bounds on every 3rd chromosome; also update visibleChroms count.
- if (visibleChroms++ % 3 !== 0) { return []; }
-
- // Set up data to display min, max ticks.
- return [
- {
- radius: self.radius_bounds[0],
- angle: d.startAngle,
- label: self.formatNum(self.data_bounds[0])
- },
- {
- radius: self.radius_bounds[1],
- angle: d.startAngle,
- label: self.formatNum(self.data_bounds[1])
- }
- ];
- },
textTransform = function() {
return "rotate(90)";
};
- this.drawTicks(this.parent_elt, this.chroms_layout, dataBoundsTicks, textTransform, true);
+ // Filter for visible chroms, then for every third chrom so that labels attached to only every
+ // third chrom.
+ var visibleChroms = _.filter(this.chroms_layout, function(c) { return c.endAngle - c.startAngle > 0.08; }),
+ labeledChroms = _.filter(visibleChroms, function(c, i) { return i % 3 === 0; });
+ this.drawTicks(this.parent_elt, labeledChroms, this._data_bounds_ticks_fn(), textTransform, true);
+ },
+
+ /**
+ * Transition labels to new values (e.g new radius or data bounds).
+ */
+ _transition_labels: function() {
+ // FIXME: (a) pull out function for getting labeled chroms? and (b) function used in transition below
+ // is copied from UseTicks mixin, so pull out and make generally available.
+
+ // Transition labels to new radius bounds.
+ var self = this,
+ visibleChroms = _.filter(this.chroms_layout, function(c) { return c.endAngle - c.startAngle > 0.08; }),
+ labeledChroms = _.filter(visibleChroms, function(c, i) { return i % 3 === 0; }),
+ new_data = _.flatten( _.map(labeledChroms, function(c) {
+ return self._data_bounds_ticks_fn()(c);
+ }));
+ this.parent_elt.selectAll('g.tick').data(new_data).transition().attr("transform", function(d) {
+ return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" +
+ "translate(" + d.radius + ",0)";
+ });
+ },
+
+ /**
+ * Get function for locating data bounds ticks.
+ */
+ _data_bounds_ticks_fn: function() {
+ // Closure vars.
+ var self = this;
+ visibleChroms = 0;
+
+ // Return function for locating ticks based on chrom arc data.
+ return function(d) {
+ // Set up data to display min, max ticks.
+ return [
+ {
+ radius: self.radius_bounds[0],
+ angle: d.startAngle,
+ label: self.formatNum(self.data_bounds[0])
+ },
+ {
+ radius: self.radius_bounds[1],
+ angle: d.startAngle,
+ label: self.formatNum(self.data_bounds[1])
+ }
+ ];
+ };
},
/**
https://bitbucket.org/galaxy/galaxy-central/changeset/54ed06c15085/
changeset: 54ed06c15085
user: jgoecks
date: 2012-11-05 01:26:33
summary: When running tools in Trackster, use Region object.
affected #: 1 file
diff -r b49bf5d522eef420b1dff41690aa09abb6492202 -r 54ed06c150857fb4e3d9a96b2f5d177208bad8cf static/scripts/viz/trackster/tracks.js
--- a/static/scripts/viz/trackster/tracks.js
+++ b/static/scripts/viz/trackster/tracks.js
@@ -1695,22 +1695,24 @@
//
// Create track for tool's output immediately to provide user feedback.
//
- var
+ var region = new visualization.GenomeRegion({
+ chrom: this.track.view.chrom,
+ start: this.track.view.low,
+ end: this.track.view.high
+ }),
url_params =
{
target_dataset_id: this.track.original_dataset_id,
action: 'rerun',
tool_id: this.name,
- regions: [{
- chrom: this.track.view.chrom,
- start: this.track.view.low,
- end: this.track.view.high
- }]
+ regions: [
+ region.toJSON()
+ ]
},
current_track = this.track,
// Set name of track to include tool name, parameters, and region used.
track_name = url_params.tool_id +
- current_track.tool_region_and_parameters_str(url_params.chrom, url_params.low, url_params.high),
+ current_track.tool_region_and_parameters_str(region),
container;
// If track not in a group, create a group for it and add new track to group. If track
@@ -3040,13 +3042,10 @@
/**
* Utility function that creates a label string describing the region and parameters of a track's tool.
*/
- tool_region_and_parameters_str: function(chrom, low, high) {
- // Region is chrom:low-high or 'all.'
- var
- track = this,
- region = (chrom !== undefined && low !== undefined && high !== undefined ?
- chrom + ":" + low + "-" + high : "all");
- return " - region=[" + region + "], parameters=[" + track.tool.get_param_values().join(", ") + "]";
+ tool_region_and_parameters_str: function(region) {
+ var track = this,
+ region_str = (region !== undefined ? region.toString() : "all");
+ return " - region=[" + region_str + "], parameters=[" + track.tool.get_param_values().join(", ") + "]";
},
/**
* Returns true if data is compatible with a given mode. Defaults to true because, for many tracks,
https://bitbucket.org/galaxy/galaxy-central/changeset/8afb981f46c1/
changeset: 8afb981f46c1
user: jgoecks
date: 2012-11-05 03:44:29
summary: Pack scripts.
affected #: 2 files
diff -r 54ed06c150857fb4e3d9a96b2f5d177208bad8cf -r 8afb981f46c11886a6106fa317936e709c311791 static/scripts/packed/viz/circster.js
--- a/static/scripts/packed/viz/circster.js
+++ b/static/scripts/packed/viz/circster.js
@@ -1,1 +1,1 @@
-define(["libs/underscore","libs/d3","viz/visualization"],function(g,k,h){var l=Backbone.Model.extend({is_visible:function(p,m){var n=p.getBoundingClientRect(),o=$("svg")[0].getBoundingClientRect();if(n.right<0||n.left>o.right||n.bottom<0||n.top>o.bottom){return false}return true}});var c=Backbone.Model.extend({});var a=Backbone.View.extend({className:"circster",initialize:function(m){this.total_gap=m.total_gap;this.genome=m.genome;this.dataset_arc_height=m.dataset_arc_height;this.track_gap=5;this.label_arc_height=50;this.scale=1;this.circular_views=null;this.chords_views=null;this.model.get("tracks").on("add",this.add_track,this);this.model.get("tracks").on("remove",this.remove_track,this);this.get_circular_tracks()},get_circular_tracks:function(){return this.model.get("tracks").filter(function(m){return m.get("track_type")!=="DiagonalHeatmapTrack"})},get_chord_tracks:function(){return this.model.get("tracks").filter(function(m){return m.get("track_type")==="DiagonalHeatmapTrack"})},get_tracks_bounds:function(){var n=this.get_circular_tracks();dataset_arc_height=this.dataset_arc_height,min_dimension=Math.min(this.$el.width(),this.$el.height()),radius_start=min_dimension/2-n.length*(this.dataset_arc_height+this.track_gap)-(this.label_arc_height+this.track_gap),tracks_start_radii=k.range(radius_start,min_dimension/2,this.dataset_arc_height+this.track_gap);var m=this;return g.map(tracks_start_radii,function(o){return[o,o+m.dataset_arc_height]})},render:function(){var v=this,p=this.dataset_arc_height,m=v.$el.width(),u=v.$el.height(),r=this.get_circular_tracks(),o=this.get_chord_tracks(),q=this.get_tracks_bounds(),n=k.select(v.$el[0]).append("svg").attr("width",m).attr("height",u).attr("pointer-events","all").append("svg:g").call(k.behavior.zoom().on("zoom",function(){var w=k.event.scale;n.attr("transform","translate("+k.event.translate+") scale("+w+")");if(v.scale!==w){if(v.zoom_drag_timeout){clearTimeout(v.zoom_drag_timeout)}v.zoom_drag_timeout=setTimeout(function(){g.each(v.circular_views,function(x){x.update_scale(w)})},400)}})).attr("transform","translate("+m/2+","+u/2+")").append("svg:g").attr("class","tracks");this.circular_views=r.map(function(x,y){var z=(x.get("track_type")==="LineTrack"?d:e),w=new z({el:n.append("g")[0],track:x,radius_bounds:q[y],genome:v.genome,total_gap:v.total_gap});w.render();return w});this.chords_views=o.map(function(x){var w=new i({el:n.append("g")[0],track:x,radius_bounds:q[0],genome:v.genome,total_gap:v.total_gap});w.render();return w});var t=this.circular_views[this.circular_views.length-1].radius_bounds[1],s=[t,t+this.label_arc_height];this.label_track_view=new b({el:n.append("g")[0],track:new c(),radius_bounds:s,genome:v.genome,total_gap:v.total_gap});this.label_track_view.render()},add_track:function(s){if(s.get("track_type")==="DiagonalHeatmapTrack"){var o=this.circular_views[0].radius_bounds,r=new i({el:k.select("g.tracks").append("g")[0],track:s,radius_bounds:o,genome:this.genome,total_gap:this.total_gap});r.render();this.chords_views.push(r)}else{var q=this.get_tracks_bounds();g.each(this.circular_views,function(u,v){u.update_radius_bounds(q[v])});g.each(this.chords_views,function(u){u.update_radius_bounds(q[0])});var p=this.circular_views.length,t=(s.get("track_type")==="LineTrack"?d:e),m=new t({el:k.select("g.tracks").append("g")[0],track:s,radius_bounds:q[p],genome:this.genome,total_gap:this.total_gap});m.render();this.circular_views.push(m);var n=q[q.length-1];n[1]=n[0];this.label_track_view.update_radius_bounds(n)}},remove_track:function(n,p,o){var m=this.circular_views[o.index];this.circular_views.splice(o.index,1);m.$el.remove();var q=this.get_tracks_bounds();g.each(this.circular_views,function(r,s){r.update_radius_bounds(q[s])})}});var j=Backbone.View.extend({tagName:"g",initialize:function(m){this.bg_stroke="ccc";this.loading_bg_fill="000";this.bg_fill="ccc";this.total_gap=m.total_gap;this.track=m.track;this.radius_bounds=m.radius_bounds;this.genome=m.genome;this.chroms_layout=this._chroms_layout();this.data_bounds=[];this.scale=1;this.parent_elt=k.select(this.$el[0])},get_fill_color:function(){var m=this.track.get("config").get_value("block_color");if(!m){m=this.track.get("config").get_value("color")}return m},render:function(){var q=this.parent_elt;if(!q){console.log("no parent elt")}var p=this.chroms_layout,s=k.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]),m=q.selectAll("g").data(p).enter().append("svg:g"),o=m.append("path").attr("d",s).attr("class","chrom-background").style("stroke",this.bg_stroke).style("fill",this.loading_bg_fill);o.append("title").text(function(u){return u.data.chrom});var n=this,r=n.track.get("data_manager"),t=(r?r.data_is_ready():true);$.when(t).then(function(){$.when(n._render_data(q)).then(function(){o.style("fill",n.bg_fill)})})},update_radius_bounds:function(n){this.radius_bounds=n;var m=k.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]);this.parent_elt.selectAll("g>path.chrom-background").transition().duration(1000).attr("d",m);this._transition_chrom_data()},update_scale:function(p){var o=this.scale;this.scale=p;if(p<=o){return}var n=this,m=new l();this.parent_elt.selectAll("path.chrom-data").filter(function(r,q){return m.is_visible(this)}).each(function(w,s){var v=k.select(this),r=v.attr("chrom"),u=n.genome.get_chrom_region(r),t=n.track.get("data_manager"),q;if(!t.can_get_more_detailed_data(u)){return}q=n.track.get("data_manager").get_more_detailed_data(u,"Coverage",0,p);$.when(q).then(function(z){v.remove();n._update_data_bounds();var y=g.find(n.chroms_layout,function(A){return A.data.chrom===r});var x=n.get_fill_color();n._render_chrom_data(n.parent_elt,y,z).style("stroke",x).style("fill",x)})});return n},_transition_chrom_data:function(){var n=this.track,p=this.chroms_layout,m=this.parent_elt.selectAll("g>path.chrom-data"),q=m[0].length;if(q>0){var o=this;$.when(n.get("data_manager").get_genome_wide_data(this.genome)).then(function(s){var r=g.reject(g.map(s,function(t,u){var v=null,w=o._get_path_function(p[u],t);if(w){v=w(t.data)}return v}),function(t){return t===null});m.each(function(u,t){k.select(this).transition().duration(1000).attr("d",r[t])})})}},_update_data_bounds:function(){var m=this.data_bounds;this.data_bounds=this.get_data_bounds(this.track.get("data_manager").get_genome_wide_data(this.genome));if(this.data_bounds[0]<m[0]||this.data_bounds[1]>m[1]){this._transition_chrom_data()}},_render_data:function(p){var o=this,n=this.chroms_layout,m=this.track,q=$.Deferred();$.when(m.get("data_manager").get_genome_wide_data(this.genome)).then(function(s){o.data_bounds=o.get_data_bounds(s);layout_and_data=g.zip(n,s),chroms_data_layout=g.map(layout_and_data,function(t){var u=t[0],v=t[1];return o._render_chrom_data(p,u,v)});var r=o.get_fill_color();o.parent_elt.selectAll("path.chrom-data").style("stroke",r).style("fill",r);q.resolve(p)});return q},_render_chrom_data:function(m,n,o){},_get_path_function:function(n,m){},_chroms_layout:function(){var n=this.genome.get_chroms_info(),p=k.layout.pie().value(function(r){return r.len}).sort(null),q=p(n),m=this.total_gap/n.length,o=g.map(q,function(t,s){var r=t.endAngle-m;t.endAngle=(r>t.startAngle?r:t.startAngle);return t});return o}});var b=j.extend({initialize:function(m){j.prototype.initialize.call(this,m);this.innerRadius=this.radius_bounds[0];this.radius_bounds[0]=this.radius_bounds[1];this.bg_stroke="fff";this.bg_fill="fff";this.min_arc_len=0.08},_render_data:function(o){var n=this,m=o.selectAll("g");m.selectAll("path").attr("id",function(r){return"label-"+r.data.chrom});m.append("svg:text").filter(function(r){return r.endAngle-r.startAngle>n.min_arc_len}).attr("text-anchor","middle").append("svg:textPath").attr("xlink:href",function(r){return"#label-"+r.data.chrom}).attr("startOffset","25%").attr("font-weight","bold").text(function(r){return r.data.chrom});var q=function(t){if(t.endAngle-t.startAngle<n.min_arc_len){return[]}var r=(t.endAngle-t.startAngle)/t.value,s=k.range(0,t.value,25000000).map(function(u,w){return{angle:u*r+t.startAngle,label:w===0?0:(w%3?null:u/1000000+"M")}});if(s.length<4){s[s.length-1].label=Math.round((s[s.length-1].angle-t.startAngle)/r/1000000)+"M"}return s};var p=this.parent_elt.append("g").selectAll("g").data(this.chroms_layout).enter().append("g").selectAll("g").data(q).enter().append("g").attr("transform",function(r){return"rotate("+(r.angle*180/Math.PI-90)+")translate("+n.innerRadius+",0)"});p.append("line").attr("x1",1).attr("y1",0).attr("x2",4).attr("y2",0).style("stroke","#000");p.append("text").attr("x",4).attr("dy",".35em").attr("text-anchor",function(r){return r.angle>Math.PI?"end":null}).attr("transform",function(r){return r.angle>Math.PI?"rotate(180)translate(-16)":null}).text(function(r){return r.label})}});var f=j.extend({_render_chrom_data:function(m,p,n){var q=this._get_path_function(p,n);if(!q){return null}var o=m.datum(n.data),r=o.append("path").attr("class","chrom-data").attr("chrom",p.data.chrom).attr("d",q);return r},_get_path_function:function(p,o){if(typeof o==="string"||!o.data||o.data.length===0){return null}var m=k.scale.linear().domain(this.data_bounds).range(this.radius_bounds);var q=k.scale.linear().domain([0,o.data.length]).range([p.startAngle,p.endAngle]);var n=k.svg.line.radial().interpolate("linear").radius(function(r){return m(r[1])}).angle(function(s,r){return q(r)});return k.svg.area.radial().interpolate(n.interpolate()).innerRadius(m(0)).outerRadius(n.radius()).angle(n.angle())},get_data_bounds:function(m){}});var e=f.extend({get_data_bounds:function(n){var m=g.map(n,function(o){if(typeof o==="string"||!o.max){return 0}return o.max});return[0,(m&&typeof m!=="string"?g.max(m):0)]}});var d=f.extend({get_data_bounds:function(n){var m=g.flatten(g.map(n,function(o){if(o){return g.map(o.data,function(q){return q[1]})}else{return 0}}));return[g.min(m),g.max(m)]}});var i=j.extend({render:function(){var m=this;$.when(m.track.get("data_manager").data_is_ready()).then(function(){$.when(m.track.get("data_manager").get_genome_wide_data(m.genome)).then(function(p){var o=[],n=m.genome.get_chroms_info();g.each(p,function(t,s){var q=n[s].chrom;var r=g.map(t.data,function(v){var u=m._get_region_angle(q,v[1]),w=m._get_region_angle(v[3],v[4]);return{source:{startAngle:u,endAngle:u+0.01},target:{startAngle:w,endAngle:w+0.01}}});o=o.concat(r)});m.parent_elt.append("g").attr("class","chord").selectAll("path").data(o).enter().append("path").style("fill",m.get_fill_color()).attr("d",k.svg.chord().radius(m.radius_bounds[0])).style("opacity",1)})})},update_radius_bounds:function(m){this.radius_bounds=m;this.parent_elt.selectAll("path").transition().attr("d",k.svg.chord().radius(this.radius_bounds[0]))},_get_region_angle:function(o,m){var n=g.find(this.chroms_layout,function(p){return p.data.chrom===o});return n.endAngle-((n.endAngle-n.startAngle)*(n.data.len-m)/n.data.len)}});return{CircsterView:a}});
\ No newline at end of file
+define(["libs/underscore","libs/d3","viz/visualization"],function(g,l,i){var m=Backbone.Model.extend({is_visible:function(q,n){var o=q.getBoundingClientRect(),p=$("svg")[0].getBoundingClientRect();if(o.right<0||o.left>p.right||o.bottom<0||o.top>p.bottom){return false}return true}});var h={drawTicks:function(r,q,v,p,n){var u=r.append("g").selectAll("g").data(q).enter().append("g").selectAll("g").data(v).enter().append("g").attr("class","tick").attr("transform",function(w){return"rotate("+(w.angle*180/Math.PI-90)+")translate("+w.radius+",0)"});var t=[],s=[],o=function(w){return w.angle>Math.PI?"end":null};if(n){t=[0,0,0,-4];s=[4,0,"",".35em"];o=null}else{t=[1,0,4,0];s=[0,4,".35em",""]}u.append("line").attr("x1",t[0]).attr("y1",t[1]).attr("x2",t[2]).attr("y1",t[3]).style("stroke","#000");u.append("text").attr("x",s[0]).attr("y",s[1]).attr("dx",s[2]).attr("dy",s[3]).attr("text-anchor",o).attr("transform",p).text(function(w){return w.label})},formatNum:function(o,n){var q=null;if(o<1){q=o.toPrecision(n)}else{var p=Math.round(o.toPrecision(n));if(o<1000){q=p}else{if(o<1000000){q=Math.round((p/1000).toPrecision(3)).toFixed(0)+"K"}else{if(o<1000000000){q=Math.round((p/1000000).toPrecision(3)).toFixed(0)+"M"}}}}return q}};var c=Backbone.Model.extend({});var a=Backbone.View.extend({className:"circster",initialize:function(n){this.total_gap=n.total_gap;this.genome=n.genome;this.dataset_arc_height=n.dataset_arc_height;this.track_gap=10;this.label_arc_height=50;this.scale=1;this.circular_views=null;this.chords_views=null;this.model.get("tracks").on("add",this.add_track,this);this.model.get("tracks").on("remove",this.remove_track,this);this.get_circular_tracks()},get_circular_tracks:function(){return this.model.get("tracks").filter(function(n){return n.get("track_type")!=="DiagonalHeatmapTrack"})},get_chord_tracks:function(){return this.model.get("tracks").filter(function(n){return n.get("track_type")==="DiagonalHeatmapTrack"})},get_tracks_bounds:function(){var o=this.get_circular_tracks();dataset_arc_height=this.dataset_arc_height,min_dimension=Math.min(this.$el.width(),this.$el.height()),radius_start=min_dimension/2-o.length*(this.dataset_arc_height+this.track_gap)-(this.label_arc_height+this.track_gap),tracks_start_radii=l.range(radius_start,min_dimension/2,this.dataset_arc_height+this.track_gap);var n=this;return g.map(tracks_start_radii,function(p){return[p,p+n.dataset_arc_height]})},render:function(){var w=this,q=this.dataset_arc_height,n=w.$el.width(),v=w.$el.height(),s=this.get_circular_tracks(),p=this.get_chord_tracks(),r=this.get_tracks_bounds(),o=l.select(w.$el[0]).append("svg").attr("width",n).attr("height",v).attr("pointer-events","all").append("svg:g").call(l.behavior.zoom().on("zoom",function(){var x=l.event.scale;o.attr("transform","translate("+l.event.translate+") scale("+x+")");if(w.scale!==x){if(w.zoom_drag_timeout){clearTimeout(w.zoom_drag_timeout)}w.zoom_drag_timeout=setTimeout(function(){},400)}})).attr("transform","translate("+n/2+","+v/2+")").append("svg:g").attr("class","tracks");this.circular_views=s.map(function(y,z){var A=(y.get("track_type")==="LineTrack"?d:e),x=new A({el:o.append("g")[0],track:y,radius_bounds:r[z],genome:w.genome,total_gap:w.total_gap});x.render();return x});this.chords_views=p.map(function(y){var x=new j({el:o.append("g")[0],track:y,radius_bounds:r[0],genome:w.genome,total_gap:w.total_gap});x.render();return x});var u=this.circular_views[this.circular_views.length-1].radius_bounds[1],t=[u,u+this.label_arc_height];this.label_track_view=new b({el:o.append("g")[0],track:new c(),radius_bounds:t,genome:w.genome,total_gap:w.total_gap});this.label_track_view.render()},add_track:function(t){if(t.get("track_type")==="DiagonalHeatmapTrack"){var p=this.circular_views[0].radius_bounds,s=new j({el:l.select("g.tracks").append("g")[0],track:t,radius_bounds:p,genome:this.genome,total_gap:this.total_gap});s.render();this.chords_views.push(s)}else{var r=this.get_tracks_bounds();g.each(this.circular_views,function(v,w){v.update_radius_bounds(r[w])});g.each(this.chords_views,function(v){v.update_radius_bounds(r[0])});var q=this.circular_views.length,u=(t.get("track_type")==="LineTrack"?d:e),n=new u({el:l.select("g.tracks").append("g")[0],track:t,radius_bounds:r[q],genome:this.genome,total_gap:this.total_gap});n.render();this.circular_views.push(n);var o=r[r.length-1];o[1]=o[0];this.label_track_view.update_radius_bounds(o)}},remove_track:function(o,q,p){var n=this.circular_views[p.index];this.circular_views.splice(p.index,1);n.$el.remove();var r=this.get_tracks_bounds();g.each(this.circular_views,function(s,t){s.update_radius_bounds(r[t])})}});var k=Backbone.View.extend({tagName:"g",initialize:function(n){this.bg_stroke="ccc";this.loading_bg_fill="000";this.bg_fill="ccc";this.total_gap=n.total_gap;this.track=n.track;this.radius_bounds=n.radius_bounds;this.genome=n.genome;this.chroms_layout=this._chroms_layout();this.data_bounds=[];this.scale=1;this.parent_elt=l.select(this.$el[0])},get_fill_color:function(){var n=this.track.get("config").get_value("block_color");if(!n){n=this.track.get("config").get_value("color")}return n},render:function(){var r=this.parent_elt;if(!r){console.log("no parent elt")}var q=this.chroms_layout,t=l.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]),n=r.selectAll("g").data(q).enter().append("svg:g"),p=n.append("path").attr("d",t).attr("class","chrom-background").style("stroke",this.bg_stroke).style("fill",this.loading_bg_fill);p.append("title").text(function(v){return v.data.chrom});var o=this,s=o.track.get("data_manager"),u=(s?s.data_is_ready():true);$.when(u).then(function(){$.when(o._render_data(r)).then(function(){p.style("fill",o.bg_fill);o.render_labels()})})},render_labels:function(){},update_radius_bounds:function(o){this.radius_bounds=o;var n=l.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]);this.parent_elt.selectAll("g>path.chrom-background").transition().duration(1000).attr("d",n);this._transition_chrom_data();this._transition_labels()},update_scale:function(q){var p=this.scale;this.scale=q;if(q<=p){return}var o=this,n=new m();this.parent_elt.selectAll("path.chrom-data").filter(function(s,r){return n.is_visible(this)}).each(function(x,t){var w=l.select(this),s=w.attr("chrom"),v=o.genome.get_chrom_region(s),u=o.track.get("data_manager"),r;if(!u.can_get_more_detailed_data(v)){return}r=o.track.get("data_manager").get_more_detailed_data(v,"Coverage",0,q);$.when(r).then(function(A){w.remove();o._update_data_bounds();var z=g.find(o.chroms_layout,function(B){return B.data.chrom===s});var y=o.get_fill_color();o._render_chrom_data(o.parent_elt,z,A).style("stroke",y).style("fill",y)})});return o},_transition_chrom_data:function(){var o=this.track,q=this.chroms_layout,n=this.parent_elt.selectAll("g>path.chrom-data"),r=n[0].length;if(r>0){var p=this;$.when(o.get("data_manager").get_genome_wide_data(this.genome)).then(function(t){var s=g.reject(g.map(t,function(u,v){var w=null,x=p._get_path_function(q[v],u);if(x){w=x(u.data)}return w}),function(u){return u===null});n.each(function(v,u){l.select(this).transition().duration(1000).attr("d",s[u])})})}},_transition_labels:function(){},_update_data_bounds:function(){var n=this.data_bounds;this.data_bounds=this.get_data_bounds(this.track.get("data_manager").get_genome_wide_data(this.genome));if(this.data_bounds[0]<n[0]||this.data_bounds[1]>n[1]){this._transition_chrom_data()}},_render_data:function(q){var p=this,o=this.chroms_layout,n=this.track,r=$.Deferred();$.when(n.get("data_manager").get_genome_wide_data(this.genome)).then(function(t){p.data_bounds=p.get_data_bounds(t);layout_and_data=g.zip(o,t),chroms_data_layout=g.map(layout_and_data,function(u){var v=u[0],w=u[1];return p._render_chrom_data(q,v,w)});var s=p.get_fill_color();p.parent_elt.selectAll("path.chrom-data").style("stroke",s).style("fill",s);r.resolve(q)});return r},_render_chrom_data:function(n,o,p){},_get_path_function:function(o,n){},_chroms_layout:function(){var o=this.genome.get_chroms_info(),q=l.layout.pie().value(function(s){return s.len}).sort(null),r=q(o),n=this.total_gap/o.length,p=g.map(r,function(u,t){var s=u.endAngle-n;u.endAngle=(s>u.startAngle?s:u.startAngle);return u});return p}});var b=k.extend({initialize:function(n){k.prototype.initialize.call(this,n);this.innerRadius=this.radius_bounds[0];this.radius_bounds[0]=this.radius_bounds[1];this.bg_stroke="fff";this.bg_fill="fff";this.min_arc_len=0.08},_render_data:function(p){var o=this,n=p.selectAll("g");n.selectAll("path").attr("id",function(t){return"label-"+t.data.chrom});n.append("svg:text").filter(function(t){return t.endAngle-t.startAngle>o.min_arc_len}).attr("text-anchor","middle").append("svg:textPath").attr("xlink:href",function(t){return"#label-"+t.data.chrom}).attr("startOffset","25%").attr("font-weight","bold").text(function(t){return t.data.chrom});var q=function(v){var t=(v.endAngle-v.startAngle)/v.value,u=l.range(0,v.value,25000000).map(function(w,x){return{radius:o.innerRadius,angle:w*t+v.startAngle,label:x===0?0:(x%3?null:o.formatNum(w))}});if(u.length<4){u[u.length-1].label=o.formatNum(Math.round((u[u.length-1].angle-v.startAngle)/t))}return u};var s=function(t){return t.angle>Math.PI?"rotate(180)translate(-16)":null};var r=g.filter(this.chroms_layout,function(t){return t.endAngle-t.startAngle>o.min_arc_len});this.drawTicks(this.parent_elt,r,q,s)}});g.extend(b.prototype,h);var f=k.extend({_render_chrom_data:function(n,q,o){var r=this._get_path_function(q,o);if(!r){return null}var p=n.datum(o.data),s=p.append("path").attr("class","chrom-data").attr("chrom",q.data.chrom).attr("d",r);return s},_get_path_function:function(q,p){if(typeof p==="string"||!p.data||p.data.length===0){return null}var n=l.scale.linear().domain(this.data_bounds).range(this.radius_bounds);var r=l.scale.linear().domain([0,p.data.length]).range([q.startAngle,q.endAngle]);var o=l.svg.line.radial().interpolate("linear").radius(function(s){return n(s[1])}).angle(function(t,s){return r(s)});return l.svg.area.radial().interpolate(o.interpolate()).innerRadius(n(0)).outerRadius(o.radius()).angle(o.angle())},render_labels:function(){var n=this,q=function(){return"rotate(90)"};var p=g.filter(this.chroms_layout,function(r){return r.endAngle-r.startAngle>0.08}),o=g.filter(p,function(s,r){return r%3===0});this.drawTicks(this.parent_elt,o,this._data_bounds_ticks_fn(),q,true)},_transition_labels:function(){var o=this,q=g.filter(this.chroms_layout,function(r){return r.endAngle-r.startAngle>0.08}),p=g.filter(q,function(s,r){return r%3===0}),n=g.flatten(g.map(p,function(r){return o._data_bounds_ticks_fn()(r)}));this.parent_elt.selectAll("g.tick").data(n).transition().attr("transform",function(r){return"rotate("+(r.angle*180/Math.PI-90)+")translate("+r.radius+",0)"})},_data_bounds_ticks_fn:function(){var n=this;visibleChroms=0;return function(o){return[{radius:n.radius_bounds[0],angle:o.startAngle,label:n.formatNum(n.data_bounds[0])},{radius:n.radius_bounds[1],angle:o.startAngle,label:n.formatNum(n.data_bounds[1])}]}},get_data_bounds:function(n){}});g.extend(f.prototype,h);var e=f.extend({get_data_bounds:function(o){var n=g.map(o,function(p){if(typeof p==="string"||!p.max){return 0}return p.max});return[0,(n&&typeof n!=="string"?g.max(n):0)]}});var d=f.extend({get_data_bounds:function(o){var n=g.flatten(g.map(o,function(p){if(p){return g.map(p.data,function(q){return q[1]})}else{return 0}}));return[g.min(n),g.max(n)]}});var j=k.extend({render:function(){var n=this;$.when(n.track.get("data_manager").data_is_ready()).then(function(){$.when(n.track.get("data_manager").get_genome_wide_data(n.genome)).then(function(q){var p=[],o=n.genome.get_chroms_info();g.each(q,function(u,t){var r=o[t].chrom;var s=g.map(u.data,function(w){var v=n._get_region_angle(r,w[1]),x=n._get_region_angle(w[3],w[4]);return{source:{startAngle:v,endAngle:v+0.01},target:{startAngle:x,endAngle:x+0.01}}});p=p.concat(s)});n.parent_elt.append("g").attr("class","chord").selectAll("path").data(p).enter().append("path").style("fill",n.get_fill_color()).attr("d",l.svg.chord().radius(n.radius_bounds[0])).style("opacity",1)})})},update_radius_bounds:function(n){this.radius_bounds=n;this.parent_elt.selectAll("path").transition().attr("d",l.svg.chord().radius(this.radius_bounds[0]))},_get_region_angle:function(p,n){var o=g.find(this.chroms_layout,function(q){return q.data.chrom===p});return o.endAngle-((o.endAngle-o.startAngle)*(o.data.len-n)/o.data.len)}});return{CircsterView:a}});
\ No newline at end of file
diff -r 54ed06c150857fb4e3d9a96b2f5d177208bad8cf -r 8afb981f46c11886a6106fa317936e709c311791 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","mvc/data","viz/trackster/filters"],function(ab,x,l,u,L,Y,i){var q=ab.extend;var V=function(ac){return("isResolved" in ac)};var n={};var k=function(ac,ad){n[ac.attr("id")]=ad};var m=function(ac,ae,ag,af){ag=".group";var ad={};n[ac.attr("id")]=af;ac.bind("drag",{handle:"."+ae,relative:true},function(ao,ap){var an=$(this),at=$(this).parent(),ak=at.children(),am=n[$(this).attr("id")],aj,ai,aq,ah,al;ai=$(this).parents(ag);if(ai.length!==0){aq=ai.position().top;ah=aq+ai.outerHeight();if(ap.offsetY<aq){$(this).insertBefore(ai);var ar=n[ai.attr("id")];ar.remove_drawable(am);ar.container.add_drawable_before(am,ar);return}else{if(ap.offsetY>ah){$(this).insertAfter(ai);var ar=n[ai.attr("id")];ar.remove_drawable(am);ar.container.add_drawable(am);return}}}ai=null;for(al=0;al<ak.length;al++){aj=$(ak.get(al));aq=aj.position().top;ah=aq+aj.outerHeight();if(aj.is(ag)&&this!==aj.get(0)&&ap.offsetY>=aq&&ap.offsetY<=ah){if(ap.offsetY-aq<ah-ap.offsetY){aj.find(".content-div").prepend(this)}else{aj.find(".content-div").append(this)}if(am.container){am.container.remove_drawable(am)}n[aj.attr("id")].add_drawable(am);return}}for(al=0;al<ak.length;al++){aj=$(ak.get(al));if(ap.offsetY<aj.position().top&&!(aj.hasClass("reference-track")||aj.hasClass("intro"))){break}}if(al===ak.length){if(this!==ak.get(al-1)){at.append(this);n[at.attr("id")].move_drawable(am,al)}}else{if(this!==ak.get(al)){$(this).insertBefore(ak.get(al));n[at.attr("id")].move_drawable(am,(ap.deltaY>0?al-1:al))}}}).bind("dragstart",function(){ad["border-top"]=ac.css("border-top");ad["border-bottom"]=ac.css("border-bottom");$(this).css({"border-top":"1px solid blue","border-bottom":"1px solid blue"})}).bind("dragend",function(){$(this).css(ad)})};var aa=16,G=9,D=20,A=100,I=12000,S=400,K=5000,w=100,o="Cannot display dataset due to an error. ",J="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",E="No data for this chrom/contig.",v="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",y="Tool cannot be rerun: ",a="Loading data...",U="Ready for display",Q=10,H=20;function W(ad,ac){if(!ac){ac=0}var ae=Math.pow(10,ac);return Math.round(ad*ae)/ae}var r=function(ad,ac,af){if(!r.id_counter){r.id_counter=0}this.id=r.id_counter++;this.name=af.name;this.view=ad;this.container=ac;this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name}],saved_values:af.prefs,onchange:function(){this.track.set_name(this.track.config.values.name)}});this.prefs=this.config.values;this.drag_handle_class=af.drag_handle_class;this.is_overview=false;this.action_icons={};this.content_visible=true;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/>").css("float","left").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(ag){ag.stopPropagation()});var ae=this;this.container_div.hover(function(){ae.icons_div.show()},function(){ae.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};r.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(ac){if(ac.content_visible){ac.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");ac.hide_contents();ac.content_visible=false}else{ac.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");ac.content_visible=true;ac.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"settings-icon",on_click_fn:function(ad){var af=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},ac=function(){ad.config.update_from_form($(".dialog-box"));hide_modal();$(window).unbind("keypress.check_enter_esc")},ae=function(ag){if((ag.keyCode||ag.which)===27){af()}else{if((ag.keyCode||ag.which)===13){ac()}}};$(window).bind("keypress.check_enter_esc",ae);show_modal("Configure",ad.config.build_form(),{Cancel:af,OK:ac})}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(ac){$(".bs-tooltip").remove();ac.remove()}}];q(r.prototype,{init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.content_visible){return true}return false},request_draw:function(){},_draw:function(){},to_dict:function(){},set_name:function(ac){this.old_name=this.name;this.name=ac;this.name_div.text(this.name)},revert_name:function(){if(this.old_name){this.name=this.old_name;this.name_div.text(this.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(){},build_header_div:function(){},add_action_icon:function(ad,ai,ah,ag,ac,af){var ae=this;this.action_icons[ad]=$("<a/>").attr("href","javascript:void(0);").attr("title",ai).addClass("icon-button").addClass(ah).tooltip().click(function(){ag(ae)}).appendTo(this.icons_div);if(af){this.action_icons[ad].hide()}},build_action_icons:function(ac){var ae;for(var ad=0;ad<ac.length;ad++){ae=ac[ad];this.add_action_icon(ae.name,ae.title,ae.css_class,ae.on_click_fn,ae.prepend,ae.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){},get_drawables:function(){}});var z=function(ad,ac,ae){r.call(this,ad,ac,ae);this.obj_type=ae.obj_type;this.drawables=[]};q(z.prototype,r.prototype,{unpack_drawables:function(ae){this.drawables=[];var ad;for(var ac=0;ac<ae.length;ac++){ad=p(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(){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac]._draw()}},to_dict:function(){var ad=[];for(var ac=0;ac<this.drawables.length;ac++){ad.push(this.drawables[ac].to_dict())}return{name:this.name,prefs:this.prefs,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}});var P=function(ad,ac,af){q(af,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});z.call(this,ad,ac,af);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);k(this.container_div,this);k(this.content_div,this);m(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new i.FiltersManager(this);this.header_div.after(this.filters_manager.parent_div);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 i.FiltersManager(this,af.filters);ae.parent_div.replaceWith(this.filters_manager.parent_div);if(af.filters.visible){this.setup_multitrack_filtering()}}};q(P.prototype,r.prototype,z.prototype,{action_icons_def:[r.prototype.action_icons_def[0],r.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(ac){$(".bs-tooltip").remove();ac.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",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(true)}ac.filters_manager.toggle()}},r.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},build_header_div:function(){var ac=$("<div/>").addClass("track-header");ac.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.name).appendTo(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){if(this.drawables[0] instanceof f){this.action_icons.composite_icon.show()}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||ac===1){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".bs-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 i.NumberFilter({name:af[0].name,index:af[0].index});this.filters_manager.add_filter(ah)}}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 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 ag=[];for(var ad=0;ad<this.drawables.length;ad++){ag.push(this.drawables[ad].name)}var ae="Composite Track of "+this.drawables.length+" tracks ("+ag.join(", ")+")";var af=new f(this.view,this.view,{name:ae,drawables:this.drawables});var ac=this.container.replace_drawable(this,af,true);af.request_draw()},add_drawable:function(ac){z.prototype.add_drawable.call(this,ac);this.update_icons()},remove_drawable:function(ac){z.prototype.remove_drawable.call(this,ac);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var ac=q(z.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,ae){for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad].request_draw(ac,ae)}}});var Z=Backbone.View.extend({initialize:function(ac){q(ac,{obj_type:"View"});z.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 x.CanvasManager(this.container.get(0).ownerDocument);this.reset()},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").css("position","relative").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;k(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(){x.select_datasets(select_datasets_url,add_track_async_url,{"f-dbkey":ac.dbkey},function(ag){ab.each(ag,function(ah){ac.add_drawable(p(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("href","javascript:void(0);").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"}).css("width","15em").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()}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ad).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("original-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_drawables(),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]}}))})}})});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();ac.request_redraw()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tooltip({placement:"bottom"}).click(function(){ac.zoom_in();ac.request_redraw()}).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)}).bind("mousewheel",function(ai,ak,ah,ag){if(ah){ah*=50;var aj=Math.round(-ah/ac.viewport_container.width()*(ac.high-ac.low));ac.move_delta(aj)}});this.top_labeltrack.bind("dragstart",function(ag,ah){return $("<div />").css({height:ac.browser_content_div.height()+ac.top_labeltrack.height()+ac.nav_labeltrack.height()+1,top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).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 X(this,{content_div:this.top_labeltrack}));this.add_label_track(new X(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")}});q(Z.prototype,z.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(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=w;var ac=this,ad=$.Deferred();$.ajax({url:chrom_url+"/"+this.dbkey,data:ae,dataType:"json",success:function(ag){if(ag.chrom_info.length===0){return}if(ag.reference){ac.add_label_track(new B(ac))}ac.chrom_data=ag.chrom_info;var aj='<option value="">Select Chrom/Contig</option>';for(var ai=0,af=ac.chrom_data.length;ai<af;ai++){var ah=ac.chrom_data[ai].chrom;aj+='<option value="'+ah+'">'+ah+"</option>"}if(ag.prev_chroms){aj+='<option value="previous">Previous '+w+"</option>"}if(ag.next_chroms){aj+='<option value="next">Next '+w+"</option>"}ac.chrom_select.html(aj);ac.chrom_start_index=ag.start_index;ad.resolve(ag)},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-w});return}if(ah==="next"){ae.load_chroms({low:this.chrom_start_index+w});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();ae.request_redraw(true);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=Math.max(ad,0);ae.high=Math.min(aj,ae.max_high)}else{ae.low=0;ae.high=ae.max_high}ae.reset_overview();ae.request_redraw()}},go_to:function(ag){ag=ag.replace(/ |,/g,"");var ak=this,ac,af,ad=ag.split(":"),ai=ad[0],aj=ad[1];if(aj!==undefined){try{var ah=aj.split("-");ac=parseInt(ah[0],10);af=parseInt(ah[1],10)}catch(ae){return false}}ak.change_chrom(ai,ac,af)},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();var ad=ac.chrom_select.val();this.trigger_navigate(ad,ac.low,ac.high,true)},add_drawable:function(ac){z.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){z.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(ak,ac,aj,al){var ai=this,ah=(al?[al]:ai.drawables),ae;var ad;for(var ag=0;ag<ah.length;ag++){ad=ah[ag];ae=-1;for(var af=0;af<ai.tracks_to_be_redrawn.length;af++){if(ai.tracks_to_be_redrawn[af][0]===ad){ae=af;break}}if(ae<0){ai.tracks_to_be_redrawn.push([ad,ac,aj])}else{ai.tracks_to_be_redrawn[ag][1]=ac;ai.tracks_to_be_redrawn[ag][2]=aj}}if(!this.requested_redraw){requestAnimationFrame(function(){ai._redraw(ak)});this.requested_redraw=true}},_redraw:function(am){this.requested_redraw=false;var aj=this.low,af=this.high;if(aj<this.max_low){aj=this.max_low}if(af>this.max_high){af=this.max_high}var al=this.high-this.low;if(this.high!==0&&al<this.min_separation){af=aj+this.min_separation}this.low=Math.floor(aj);this.high=Math.ceil(af);this.update_location(this.low,this.high);this.resolution_b_px=(this.high-this.low)/this.viewport_container.width();this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var ac=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ai=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var an=13;this.overview_box.css({left:ac,width:Math.max(an,ai)}).show();if(ai<an){this.overview_box.css("left",ac-(an-ai)/2)}if(this.overview_highlight){this.overview_highlight.css({left:ac,width:ai})}if(!am){var ae,ad,ak;for(var ag=0,ah=this.tracks_to_be_redrawn.length;ag<ah;ag++){ae=this.tracks_to_be_redrawn[ag][0];ad=this.tracks_to_be_redrawn[ag][1];ak=this.tracks_to_be_redrawn[ag][2];if(ae){ae._draw(ad,ak)}}this.tracks_to_be_redrawn=[];for(ag=0,ah=this.label_tracks.length;ag<ah;ag++){this.label_tracks[ag]._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_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());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(){$(".bs-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 s=function(ae,aj,af){this.track=ae;this.name=aj.name;this.params=[];var aq=aj.params;for(var ag=0;ag<aq.length;ag++){var al=aq[ag],ad=al.name,ap=al.label,ah=unescape(al.html),ar=al.value,an=al.type;if(an==="number"){this.params.push(new e(ad,ap,ah,(ad in af?af[ad]:ar),al.min,al.max))}else{if(an==="select"){this.params.push(new N(ad,ap,ah,(ad in af?af[ad]:ar)))}else{console.log("WARNING: unrecognized tool parameter type:",ad,an)}}}this.parent_div=$("<div/>").addClass("dynamic-tool").hide();this.parent_div.bind("drag",function(au){au.stopPropagation()}).click(function(au){au.stopPropagation()}).bind("dblclick",function(au){au.stopPropagation()});var ao=$("<div class='tool-name'>").appendTo(this.parent_div).text(this.name);var am=this.params;var ak=this;$.each(this.params,function(av,ay){var ax=$("<div>").addClass("param-row").appendTo(ak.parent_div);var au=$("<div>").addClass("param-label").text(ay.label).appendTo(ax);var aw=$("<div/>").addClass("param-input").html(ay.html).appendTo(ax);aw.find(":input").val(ay.value);$("<div style='clear: both;'/>").appendTo(ax)});this.parent_div.find("input").click(function(){$(this).select()});var at=$("<div>").addClass("param-row").appendTo(this.parent_div);var ai=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(at);var ac=$("<input type='submit'>").attr("value","Run on visible region").css("margin-left","3em").appendTo(at);ac.click(function(){ak.run_on_region()});ai.click(function(){ak.run_on_dataset()});if("visible" in af&&af.visible){this.parent_div.show()}};q(s.prototype,{update_params:function(){for(var ac=0;ac<this.params.length;ac++){this.params[ac].update_value()}},state_dict:function(){var ad={};for(var ac=0;ac<this.params.length;ac++){ad[this.params[ac].name]=this.params[ac].value}ad.visible=this.parent_div.is(":visible");return ad},get_param_values_dict:function(){var ac={};this.parent_div.find(":input").each(function(){var ad=$(this).attr("name"),ae=$(this).val();ac[ad]=ae});return ac},get_param_values:function(){var ac=[];this.parent_div.find(":input").each(function(){var ad=$(this).attr("name"),ae=$(this).val();if(ad){ac[ac.length]=ae}});return ac},run_on_dataset:function(){var ac=this;ac.run({target_dataset_id:this.track.original_dataset_id,tool_id:ac.name},null,function(ad){show_modal(ac.name+" is Running",ac.name+" is running on the complete dataset. Tool outputs are in dataset's history.",{Close:hide_modal})})},run_on_region:function(){var ad={target_dataset_id:this.track.original_dataset_id,action:"rerun",tool_id:this.name,regions:[{chrom:this.track.view.chrom,start:this.track.view.low,end:this.track.view.high}]},ah=this.track,ae=ad.tool_id+ah.tool_region_and_parameters_str(ad.chrom,ad.low,ad.high),ac;if(ah.container===view){var ag=new P(view,view,{name:this.name});var af=ah.container.replace_drawable(ah,ag,false);ag.container_div.insertBefore(ah.view.content_div.children()[af]);ag.add_drawable(ah);ah.container_div.appendTo(ag.content_div);ac=ag}else{ac=ah.container}var ai=new ah.constructor(view,ac,{name:ae,hda_ldda:"hda"});ai.init_for_tool_data();ai.change_mode(ah.mode);ai.set_filters_manager(ah.filters_manager.copy(ai));ai.update_icons();ac.add_drawable(ai);ai.tiles_div.text("Starting job.");this.update_params();this.run(ad,ai,function(aj){ai.set_dataset(new Y.Dataset(aj));ai.tiles_div.text("Running job.");ai.init()})},run:function(ac,ae,af){ac.inputs=this.get_param_values_dict();var ad=new l.ServerStateDeferred({ajax_settings:{url:galaxy_paths.get("tool_url"),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(J)}else{if(ag.error){ae.container_div.addClass("error");ae.content_div.text(y+ag.message)}else{af(ag)}}})}});var N=function(ad,ac,ae,af){this.name=ad;this.label=ac;this.html=$(ae);this.value=af};q(N.prototype,{update_value:function(){this.value=$(this.html).val()}});var e=function(ae,ad,ag,ah,af,ac){N.call(this,ae,ad,ag,ah);this.min=af;this.max=ac};q(e.prototype,N.prototype,{update_value:function(){N.prototype.update_value.call(this);this.value=parseFloat(this.value)}});var C=function(ac,ad){L.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 F=function(ac){this.track=ac.track;this.params=ac.params;this.values={};this.restore_values((ac.saved_values?ac.saved_values:{}));this.onchange=ac.onchange};q(F.prototype,{restore_values:function(ac){var ad=this;$.each(this.params,function(ae,af){if(ac[af.key]!==undefined){ad.values[af.key]=ac[af.key]}else{ad.values[af.key]=af.default_value}})},build_form:function(){var af=this;var ac=$("<div />");var ae;function ad(ak,ag){for(var ao=0;ao<ak.length;ao++){ae=ak[ao];if(ae.hidden){continue}var ai="param_"+ao;var at=af.values[ae.key];var av=$("<div class='form-row' />").appendTo(ag);av.append($("<label />").attr("for",ai).text(ae.label+":"));if(ae.type==="bool"){av.append($('<input type="checkbox" />').attr("id",ai).attr("name",ai).attr("checked",at))}else{if(ae.type==="text"){av.append($('<input type="text"/>').attr("id",ai).val(at).click(function(){$(this).select()}))}else{if(ae.type==="select"){var aq=$("<select />").attr("id",ai);for(var am=0;am<ae.options.length;am++){$("<option/>").text(ae.options[am].label).attr("value",ae.options[am].value).appendTo(aq)}aq.val(at);av.append(aq)}else{if(ae.type==="color"){var au=$("<div/>").appendTo(av),ap=$("<input />").attr("id",ai).attr("name",ai).val(at).css("float","left").appendTo(au).click(function(ax){$(".bs-tooltip").removeClass("in");var aw=$(this).siblings(".bs-tooltip").addClass("in");aw.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(aw).height()/2)+($(this).height()/2)}).show();aw.click(function(ay){ay.stopPropagation()});$(document).bind("click.color-picker",function(){aw.hide();$(document).unbind("click.color-picker")});ax.stopPropagation()}),an=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(au).attr("title","Set new random color").tooltip(),ar=$("<div class='bs-tooltip right' style='position: absolute;' />").appendTo(au).hide(),aj=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(ar),ah=$("<div class='tooltip-arrow'></div>").appendTo(ar),al=$.farbtastic(aj,{width:100,height:100,callback:ap,color:at});au.append($("<div/>").css("clear","both"));(function(aw){an.click(function(){aw.setColor(l.get_random_color())})})(al)}else{av.append($("<input />").attr("id",ai).attr("name",ai).val(at))}}}}if(ae.help){av.append($("<div class='help'/>").text(ae.help))}}}ad(this.params,ac);return ac},update_from_form:function(ac){var ae=this;var ad=false;$.each(this.params,function(af,ah){if(!ah.hidden){var ai="param_"+af;var ag=ac.find("#"+ai).val();if(ah.type==="float"){ag=parseFloat(ag)}else{if(ah.type==="int"){ag=parseInt(ag)}else{if(ah.type==="bool"){ag=ac.find("#"+ai).is(":checked")}}}if(ag!==ae.values[ah.key]){ae.values[ah.key]=ag;ad=true}}});if(ad){this.onchange();this.track.changed()}}});var b=function(ac,ag,ae,ad,af){this.track=ac;this.region=ag;this.low=ag.get("start");this.high=ag.get("end");this.resolution=ae;this.html_elt=$("<div class='track-tile'/>").append(ad).height($(ad).attr("height"));this.data=af;this.stale=false};b.prototype.predisplay_actions=function(){};var j=function(ac,ah,ae,ad,af,ag){b.call(this,ac,ah,ae,ad,af);this.max_val=ag};q(j.prototype,b.prototype);var O=function(af,an,ag,ae,ai,ap,aj,aq,ad,am){b.call(this,af,an,ag,ae,ai);this.mode=aj;this.all_slotted=ad;this.feature_mapper=am;this.has_icons=false;if(aq){this.has_icons=true;var ak=this;ae=this.html_elt.children()[0],message_div=$("<div/>").addClass("tile-message").css({height:D-1,width:ae.width}).prependTo(this.html_elt);var al=new x.GenomeRegion({chrom:af.view.chrom,start:this.low,end:this.high}),ao=ai.length,ah=$("<a href='javascript:void(0);'/>").addClass("icon more-down").attr("title","For speed, only the first "+ao+" features in this region were obtained from server. Click to get more data including depth").tooltip().appendTo(message_div),ac=$("<a href='javascript:void(0);'/>").addClass("icon more-across").attr("title","For speed, only the first "+ao+" features in this region were obtained from server. Click to get more data excluding depth").tooltip().appendTo(message_div);ah.click(function(){ak.stale=true;af.data_manager.get_more_data(al,af.mode,ak.resolution,{},af.data_manager.DEEP_DATA_REQ);$(".bs-tooltip").hide();af.request_draw(true)}).dblclick(function(ar){ar.stopPropagation()});ac.click(function(){ak.stale=true;af.data_manager.get_more_data(al,af.mode,ak.resolution,{},af.data_manager.BROAD_DATA_REQ);$(".bs-tooltip").hide();af.request_draw(true)}).dblclick(function(ar){ar.stopPropagation()})}};q(O.prototype,b.prototype);O.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 ak=at[0],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]}var af=$("<div/>").attr("id",ak).addClass("feature-popup"),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"?W(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"))-af.width()/2,ae=am+parseInt(ad.html_elt.css("top"))+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 g=function(ad,ac,ae){q(ae,{drag_handle_class:"draghandle"});r.call(this,ad,ac,ae);this.dataset=new Y.Dataset({id:ae.dataset_id,hda_ldda:ae.hda_ldda});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:K);this.data_manager=("data_manager" in ae?ae.data_manager:new x.GenomeDataManager({dataset:this.dataset,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=0;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()}}};q(g.prototype,r.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},r.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"overview-icon",on_click_fn:function(ac){ac.view.set_overview(ac)}},r.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"filters-icon",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.dynamic_tool_div.toggle();if(ac.dynamic_tool_div.is(":visible")){ac.set_name(ac.name+ac.tool_region_and_parameters_str())}else{ac.revert_name()}$(".bs-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.name %><br/><strong>Dataset</strong>: <%= track.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(){hide_modal();$(window).unbind("keypress.check_enter_esc")},ad=function(){var aj=$('select[name="regions"] option:selected').val(),al,ai=new x.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),ak=ab.map($(".bookmark"),function(am){return new x.GenomeRegion({from_str:$(am).children(".position").text()})});if(aj==="cur"){al=[ai]}else{if(aj==="bookmarks"){al=ak}else{al=[ai].concat(ak)}}hide_modal();window.location.href=galaxy_paths.get("sweepster_url")+"?"+$.param({dataset_id:ac.dataset_id,hda_ldda:ac.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()}}};show_modal("Visualize tool parameter space and output from different parameter settings?",ae,{No:ah,Yes:ad})}},r.prototype.action_icons_def[2]],can_draw:function(){if(this.dataset_id&&r.prototype.can_draw.call(this)){return true}return false},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var ac=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(ac)}this.name_div=$("<div/>").addClass("track-name").appendTo(ac).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return ac},on_resize:function(){},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.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.values.height=ac.visible_height_px;ac.changed()}).appendTo(ac.container_div)},set_display_modes:function(af,ai){this.display_modes=af;this.mode=(ai?ai:(this.config&&this.config.values.mode?this.config.values.mode:this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var ad=this,ag={};for(var ae=0,ac=ad.display_modes.length;ae<ac;ae++){var ah=ad.display_modes[ae];ag[ah]=function(aj){return function(){ad.change_mode(aj);ad.icons_div.show();ad.container_div.mouseleave(function(){ad.icons_div.hide()})}}(ah)}make_popupmenu(this.action_icons.mode_icon,ag)},build_action_icons:function(){r.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 X){return"LabelTrack"}else{if(this instanceof B){return"ReferenceTrack"}else{if(this instanceof h){return"LineTrack"}else{if(this instanceof T){return"ReadTrack"}else{if(this instanceof R){return"VcfTrack"}else{if(this instanceof f){return"CompositeTrack"}else{if(this instanceof c){return"FeatureTrack"}}}}}}}return""},init:function(ae){var ad=this;ad.enabled=false;ad.tile_cache.clear();ad.data_manager.clear();ad.content_div.css("height","auto");ad.tiles_div.text("").children().remove();ad.container_div.removeClass("nodata error pending");if(!ad.dataset_id){return}var ac=$.Deferred(),af={hda_ldda:ad.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");ad.tiles_div.text(o);if(ag.message){ad.tiles_div.append($("<a href='javascript:void(0);'></a>").text("View error").click(function(){show_modal("Trackster Error","<pre>"+ag.message+"</pre>",{Close:hide_modal})}));ad.tiles_div.append($("<span/>").text(" "));ad.tiles_div.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.tiles_div.text(J)}else{if(ag==="no data"||(ag.data!==undefined&&(ag.data===null||ag.data.length===0))){ad.container_div.addClass("nodata");ad.tiles_div.text(E)}else{if(ag==="pending"){ad.container_div.addClass("pending");ad.tiles_div.html(v);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(U);if(ad.view.chrom){ad.tiles_div.text("");ad.tiles_div.css("height",ad.visible_height_px+"px");ad.enabled=true;$.when(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(){},get_drawables:function(){return this}});var M=function(ae,ad,af){g.call(this,ae,ad,af);var ac=this;m(ac.container_div,ac.drag_handle_class,".group",ac);this.filters_manager=new i.FiltersManager(this,("filters" in af?af.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=("tool" in af&&af.tool?new s(this,af.tool,af.tool_state):null);this.tile_cache=new x.Cache(Q);if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){this.dynamic_tool_div=this.tool.parent_div;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(af.mode){this.change_mode(af.mode)}};q(M.prototype,r.prototype,g.prototype,{action_icons_def:g.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){$(".bs-tooltip").remove();ac.slotters[ac.view.resolution_px_b].max_rows*=2;ac.request_draw(true)},hide:true}]),copy:function(ac){var ad=this.to_dict();q(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(),name:this.name,hda_ldda:this.hda_ldda,dataset_id:this.dataset_id,prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},change_mode:function(ad){var ac=this;ac.mode=ad;ac.config.values.mode=ad;ac.tile_cache.clear();ac.request_draw();this.action_icons.mode_icon.attr("title","Set display mode (now: "+ac.mode+")");return ac},update_icons:function(){var ac=this;if(ac.filters_available){ac.action_icons.filters_icon.show()}else{ac.action_icons.filters_icon.hide()}if(ac.tool){ac.action_icons.tools_icon.show();ac.action_icons.param_space_viz_icon.show()}else{ac.action_icons.tools_icon.hide();ac.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(ad,ae,ac){return ad+"_"+ae+"_"+ac},request_draw:function(ad,ac){this.view.request_redraw(false,ad,ac,this)},before_draw:function(){},_draw:function(ad,an){if(!this.can_draw()){return}var al=this.view.low,ah=this.view.high,aj=ah-al,ae=this.view.container.width(),ap=this.view.resolution_px_b,ag=this.view.resolution_b_px;if(this.is_overview){al=this.view.max_low;ah=this.view.max_high;ag=(view.max_high-view.max_low)/ae;ap=1/ag}this.before_draw();this.tiles_div.children().addClass("remove");var ac=Math.floor(al/(ag*S)),ak=true,ao=[],ai=function(aq){return(aq&&"track" in aq)};while((ac*S*ag)<ah){var am=this.draw_helper(ad,ae,ac,ag,this.tiles_div,ap);if(ai(am)){ao.push(am)}else{ak=false}ac+=1}if(!an){this.tiles_div.children(".remove").removeClass("remove").remove()}var af=this;if(ak){this.tiles_div.children(".remove").remove();af.postdraw_actions(ao,ae,ap,an)}},postdraw_actions:function(ae,af,ah,ac){var ag=false;for(var ad=0;ad<ae.length;ad++){if(ae[ad].has_icons){ag=true;break}}if(ag){for(var ad=0;ad<ae.length;ad++){tile=ae[ad];if(!tile.has_icons){tile.html_elt.css("padding-top",D)}}}},draw_helper:function(ac,ao,au,ar,ah,ai,ap){var an=this,ax=this._gen_tile_cache_key(ao,ai,au),af=this._get_tile_bounds(au,ar);if(!ap){ap={}}var aw=(ac?undefined:an.tile_cache.get_elt(ax));if(aw){an.show_tile(aw,ah,ai);return aw}var al=true;var at=an.data_manager.get_data(af,an.mode,ar,an.data_url_extra_params);if(V(at)){al=false}var aj;if(view.reference_track&&ai>view.canvas_manager.char_width_px){aj=view.reference_track.data_manager.get_data(af,an.mode,ar,view.reference_track.data_url_extra_params);if(V(aj)){al=false}}if(al){q(at,ap.more_tile_data);var ak=an.mode;if(ak==="Auto"){ak=an.get_mode(at);an.update_auto_mode(ak)}var ae=an.view.canvas_manager.new_canvas(),av=af.get("start"),ad=af.get("end"),ao=Math.ceil((ad-av)*ai)+an.left_offset,am=an.get_canvas_height(at,ak,ai,ao);ae.width=ao;ae.height=am;var aq=ae.getContext("2d");aq.translate(this.left_offset,0);var aw=an.draw_tile(at,aq,ak,ar,af,ai,aj);if(aw!==undefined){an.tile_cache.set_elt(ax,aw);an.show_tile(aw,ah,ai)}return aw}var ag=$.Deferred();$.when(at,aj).then(function(){view.request_redraw(false,false,false,an);ag.resolve()});return ag},get_canvas_height:function(ac,ae,af,ad){return this.visible_height_px},draw_tile:function(ac,ad,ah,af,ag,ai,ae){console.log("Warning: TiledTrack.draw_tile() not implemented.")},show_tile:function(ae,ah,ai){var ad=this,ac=ae.html_elt;ae.predisplay_actions();var ag=(ae.low-(this.is_overview?this.view.max_low:this.view.low))*ai;if(this.left_offset){ag-=this.left_offset}ac.css({position:"absolute",top:0,left:ag});if(ac.hasClass("remove")){ac.removeClass("remove")}else{ah.append(ac)}ae.html_elt.height("auto");this.max_height_px=Math.max(this.max_height_px,ae.html_elt.height());ae.html_elt.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")},_get_tile_bounds:function(ac,ad){var af=Math.floor(ac*S*ad),ag=Math.ceil(S*ad),ae=(af+ag<=this.view.max_high?af+ag:this.view.max_high);return new x.GenomeRegion({chrom:this.view.chrom,start:af,end:ae})},tool_region_and_parameters_str:function(ae,ac,af){var ad=this,ag=(ae!==undefined&&ac!==undefined&&af!==undefined?ae+":"+ac+"-"+af:"all");return" - region=["+ag+"], parameters=["+ad.tool.get_param_values().join(", ")+"]"},data_and_mode_compatible:function(ac,ad){return true},can_subset:function(ac){return false},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data");this.data_query_wait=1000;this.dataset_check_type="state";this.normal_postdraw_actions=this.postdraw_actions;this.postdraw_actions=function(ae,af,ah,ac){var ad=this;ad.normal_postdraw_actions(ae,af,ah,ac);ad.dataset_check_type="converted_datasets_state";ad.data_query_wait=K;var ag=new l.ServerStateDeferred({url:ad.dataset_state_url,url_params:{dataset_id:ad.dataset_id,hda_ldda:ad.hda_ldda},interval:ad.data_query_wait,success_fn:function(ai){return ai!=="pending"}});$.when(ag.go()).then(function(){ad.data_manager.set("data_type","data")});ad.postdraw_actions=ad.normal_postdraw_actions}}});var X=function(ad,ac){var ae={resize:false};g.call(this,ad,ac,ae);this.container_div.addClass("label-track")};q(X.prototype,g.prototype,{build_header_div:function(){},init:function(){this.enabled=true},_draw:function(){var ae=this.view,af=ae.high-ae.low,ai=Math.floor(Math.pow(10,Math.floor(Math.log(af)/Math.log(10)))),ac=Math.floor(ae.low/ai)*ai,ag=this.view.container.width(),ad=$("<div style='position: relative; height: 1.3em;'></div>");while(ac<ae.high){var ah=(ac-ae.low)/af*ag;ad.append($("<div class='label'>"+commatize(ac)+"</div>").css({position:"absolute",left:ah-1}));ac+=ai}this.content_div.children(":first").remove();this.content_div.append(ad)}});var f=function(ad,ac,ag){M.call(this,ad,ac,ag);this.drawables=[];this.left_offset=0;if("drawables" in ag){var af;for(var ae=0;ae<ag.drawables.length;ae++){af=ag.drawables[ae];this.drawables[ae]=p(af,ad,null);if(af.left_offset>this.left_offset){this.left_offset=af.left_offset}}this.enabled=true}if(this.drawables.length!==0){this.set_display_modes(this.drawables[0].display_modes,this.drawables[0].mode)}this.update_icons();this.obj_type="CompositeTrack"};q(f.prototype,M.prototype,{action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(ac){$(".bs-tooltip").remove();ac.show_group()}}].concat(M.prototype.action_icons_def),to_dict:z.prototype.to_dict,add_drawable:z.prototype.add_drawable,unpack_drawables:z.prototype.unpack_drawables,change_mode:function(ac){M.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:r.prototype.can_draw,draw_helper:function(ad,at,az,aw,ak,am,au){var ar=this,aD=this._gen_tile_cache_key(at,am,az),ah=this._get_tile_bounds(az,aw);if(!au){au={}}var aC=(ad?undefined:ar.tile_cache.get_elt(aD));if(aC){ar.show_tile(aC,ak,am);return aC}var al=[],ar,ap=true,ax,an;for(var ay=0;ay<this.drawables.length;ay++){ar=this.drawables[ay];ax=ar.data_manager.get_data(ah,ar.mode,aw,ar.data_url_extra_params);if(V(ax)){ap=false}al.push(ax);an=null;if(view.reference_track&&am>view.canvas_manager.char_width_px){an=view.reference_track.data_manager.get_data(ah,ar.mode,aw,view.reference_track.data_url_extra_params);if(V(an)){ap=false}}al.push(an)}if(ap){q(ax,au.more_tile_data);this.tile_predraw_init();var ag=ar.view.canvas_manager.new_canvas(),ai=ar._get_tile_bounds(az,aw),aA=ah.get("start"),ae=ah.get("end"),aB=0,at=Math.ceil((ae-aA)*am)+this.left_offset,aq=0,af=[],ay;var ac=0;for(ay=0;ay<this.drawables.length;ay++,aB+=2){ar=this.drawables[ay];ax=al[aB];var ao=ar.mode;if(ao==="Auto"){ao=ar.get_mode(ax);ar.update_auto_mode(ao)}af.push(ao);ac=ar.get_canvas_height(ax,ao,am,at);if(ac>aq){aq=ac}}ag.width=at;ag.height=(au.height?au.height:aq);aB=0;var av=ag.getContext("2d");av.translate(this.left_offset,0);av.globalAlpha=0.5;av.globalCompositeOperation="source-over";for(ay=0;ay<this.drawables.length;ay++,aB+=2){ar=this.drawables[ay];ax=al[aB];an=al[aB+1];aC=ar.draw_tile(ax,av,af[ay],aw,ah,am,an)}this.tile_cache.set_elt(aD,aC);this.show_tile(aC,ak,am);return aC}var aj=$.Deferred(),ar=this;$.when.apply($,al).then(function(){view.request_redraw(false,false,false,ar);aj.resolve()});return aj},show_group:function(){var af=new P(this.view,this.container,{name:this.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()},tile_predraw_init:function(){var af=Number.MAX_VALUE,ac=-af,ad;for(var ae=0;ae<this.drawables.length;ae++){ad=this.drawables[ae];if(ad instanceof h){if(ad.prefs.min_value<af){af=ad.prefs.min_value}if(ad.prefs.max_value>ac){ac=ad.prefs.max_value}}}for(var ae=0;ae<this.drawables.length;ae++){ad=this.drawables[ae];ad.prefs.min_value=af;ad.prefs.max_value=ac}},postdraw_actions:function(ae,ah,aj,ad){M.prototype.postdraw_actions.call(this,ae,ah,aj,ad);var ag=-1;for(var af=0;af<ae.length;af++){var ac=ae[af].html_elt.find("canvas").height();if(ac>ag){ag=ac}}for(var af=0;af<ae.length;af++){var ai=ae[af];if(ai.html_elt.find("canvas").height()!==ag){this.draw_helper(true,ah,ai.index,ai.resolution,ai.html_elt.parent(),aj,{height:ag});ai.html_elt.remove()}}}});var B=function(ac){M.call(this,ac,{content_div:ac.top_labeltrack},{resize:false});ac.reference_track=this;this.left_offset=200;this.visible_height_px=12;this.container_div.addClass("reference-track");this.content_div.css("background","none");this.content_div.css("min-height","0px");this.content_div.css("border","none");this.data_url=reference_url+"/"+this.view.dbkey;this.data_url_extra_params={reference:true};this.data_manager=new x.ReferenceTrackDataManager({data_url:this.data_url});this.hide_contents()};q(B.prototype,r.prototype,M.prototype,{build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},can_draw:r.prototype.can_draw,draw_helper:function(ag,ae,ac,ad,ah,ai,af){if(ai>this.view.canvas_manager.char_width_px){return M.prototype.draw_helper.call(this,ag,ae,ac,ad,ah,ai,af)}else{this.hide_contents();return null}},draw_tile:function(ak,al,ag,af,ai,am){var ae=this;if(am>this.view.canvas_manager.char_width_px){if(ak.data===null){this.hide_contents();return}var ad=al.canvas;al.font=al.canvas.manager.default_font;al.textAlign="center";ak=ak.data;for(var ah=0,aj=ak.length;ah<aj;ah++){var ac=Math.floor(ah*am);al.fillText(ak[ah],ac,10)}this.show_contents();return new b(ae,ai,af,ad,ak)}this.hide_contents()}});var h=function(ae,ad,af){var ac=this;this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";M.call(this,ae,ad,af);this.hda_ldda=af.hda_ldda;this.dataset_id=af.dataset_id;this.original_dataset_id=this.dataset_id;this.left_offset=0;this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"color",label:"Color",type:"color",default_value:l.get_random_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:32,hidden:true}],saved_values:af.prefs,onchange:function(){ac.set_name(ac.prefs.name);ac.vertical_range=ac.prefs.max_value-ac.prefs.min_value;ac.set_min_value(ac.prefs.min_value);ac.set_max_value(ac.prefs.max_value)}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value};q(h.prototype,r.prototype,M.prototype,{on_resize:function(){this.request_draw(true)},set_min_value:function(ac){this.prefs.min_value=ac;$("#linetrack_"+this.dataset_id+"_minval").text(this.prefs.min_value);this.tile_cache.clear();this.request_draw()},set_max_value:function(ac){this.prefs.max_value=ac;$("#linetrack_"+this.dataset_id+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.request_draw()},predraw_init:function(){var ac=this;ac.vertical_range=undefined;return $.getJSON(ac.dataset.url(),{data_type:"data",stats:true,chrom:ac.view.chrom,low:0,high:ac.view.max_high,hda_ldda:ac.hda_ldda},function(ad){ac.container_div.addClass("line-track");var ag=ad.data;if(isNaN(parseFloat(ac.prefs.min_value))||isNaN(parseFloat(ac.prefs.max_value))){var ae=ag.min,ai=ag.max;ae=Math.floor(Math.min(0,Math.max(ae,ag.mean-2*ag.sd)));ai=Math.ceil(Math.max(0,Math.min(ai,ag.mean+2*ag.sd)));ac.prefs.min_value=ae;ac.prefs.max_value=ai;$("#track_"+ac.dataset_id+"_minval").val(ac.prefs.min_value);$("#track_"+ac.dataset_id+"_maxval").val(ac.prefs.max_value)}ac.vertical_range=ac.prefs.max_value-ac.prefs.min_value;ac.total_frequency=ag.total_frequency;ac.container_div.find(".yaxislabel").remove();var ah=$("<div/>").text(W(ac.prefs.min_value,3)).make_text_editable({num_cols:6,on_finish:function(aj){$(".bs-tooltip").remove();var aj=parseFloat(aj);if(!isNaN(aj)){ac.set_min_value(aj)}},help_text:"Set min value"}).addClass("yaxislabel bottom").attr("id","linetrack_"+ac.dataset_id+"_minval").prependTo(ac.container_div),af=$("<div/>").text(W(ac.prefs.max_value,3)).make_text_editable({num_cols:6,on_finish:function(aj){$(".bs-tooltip").remove();var aj=parseFloat(aj);if(!isNaN(aj)){ac.set_max_value(aj)}},help_text:"Set max value"}).addClass("yaxislabel top").attr("id","linetrack_"+ac.dataset_id+"_maxval").prependTo(ac.container_div)})},draw_tile:function(al,aj,ae,ad,ag,ak){var ac=aj.canvas,af=ag.get("start"),ai=ag.get("end"),ah=new L.LinePainter(al.data,af,ai,this.prefs,ae);ah.draw(aj,ac.width,ac.height,ak);return new b(this,ag,ad,ac,al.data)},can_subset:function(ac){return false}});var t=function(ae,ad,af){var ac=this;this.display_modes=["Heatmap"];this.mode="Heatmap";M.call(this,ae,ad,af);this.hda_ldda=af.hda_ldda;this.dataset_id=af.dataset_id;this.original_dataset_id=this.dataset_id;this.left_offset=0;this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{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}],saved_values:af.prefs,onchange:function(){ac.set_name(ac.prefs.name);ac.vertical_range=ac.prefs.max_value-ac.prefs.min_value;ac.set_min_value(ac.prefs.min_value);ac.set_max_value(ac.prefs.max_value)}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value};q(t.prototype,r.prototype,M.prototype,{on_resize:function(){this.request_draw(true)},set_min_value:function(ac){this.prefs.min_value=ac;this.tile_cache.clear();this.request_draw()},set_max_value:function(ac){this.prefs.max_value=ac;this.tile_cache.clear();this.request_draw()},draw_tile:function(ac,ae,ai,ag,ah,aj){var af=ae.canvas,ad=new L.DiagonalHeatmapPainter(ac.data,ah.get("start"),ah.get("end"),this.prefs,ai);ad.draw(ae,af.width,af.height,aj);return new b(this,ah,ag,af,ac.data)}});var c=function(af,ae,ah){var ad=this;this.display_modes=["Auto","Coverage","Dense","Squish","Pack"];M.call(this,af,ae,ah);var ag=l.get_random_color(),ac=l.get_random_color([ag,"#FFFFFF"]);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:ag},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:ac},{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:"histogram_max",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:this.visible_height_px,hidden:true}],saved_values:ah.prefs,onchange:function(){ad.set_name(ad.prefs.name);ad.tile_cache.clear();ad.set_painter_from_config();ad.request_draw()}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.container_div.addClass("feature-track");this.hda_ldda=ah.hda_ldda;this.dataset_id=ah.dataset_id;this.original_dataset_id=ah.dataset_id;this.show_labels_scale=0.001;this.showing_details=false;this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};q(c.prototype,r.prototype,M.prototype,{set_dataset:function(ac){this.dataset_id=ac.get("id");this.hda_ldda=ac.get("hda_ldda");this.dataset=ac;this.data_manager.set("dataset",ac)},set_painter_from_config:function(){if(this.config.values.connector_style==="arcs"){this.painter=L.ArcLinkedFeaturePainter}else{this.painter=L.LinkedFeaturePainter}},before_draw:function(){this.max_height_px=0},postdraw_actions:function(ar,am,ah,ag){M.prototype.postdraw_actions.call(this,ar,ag);var al=this,ao;if(al.mode==="Coverage"){var ad=-1;for(ao=0;ao<ar.length;ao++){var an=ar[ao].max_val;if(an>ad){ad=an}}for(ao=0;ao<ar.length;ao++){var au=ar[ao];if(au.max_val!==ad){au.html_elt.remove();al.draw_helper(true,am,au.index,au.resolution,au.html_elt.parent(),ah,{more_tile_data:{max:ad}})}}}if(al.filters_manager){var ai=al.filters_manager.filters;for(var aq=0;aq<ai.length;aq++){ai[aq].update_ui_elt()}var at=false,ac,aj;for(ao=0;ao<ar.length;ao++){if(ar[ao].data.length){ac=ar[ao].data[0];for(var aq=0;aq<ai.length;aq++){aj=ai[aq];if(aj.applies_to(ac)&&aj.min!==aj.max){at=true;break}}}}if(al.filters_available!==at){al.filters_available=at;if(!al.filters_available){al.filters_manager.hide()}al.update_icons()}}this.container_div.find(".yaxislabel").remove();var af=ar[0];if(af instanceof j){var ak=(this.prefs.histogram_max?this.prefs.histogram_max:af.max_val),ae=$("<div/>").text(ak).make_text_editable({num_cols:12,on_finish:function(av){$(".bs-tooltip").remove();var av=parseFloat(av);al.prefs.histogram_max=(!isNaN(av)?av:null);al.tile_cache.clear();al.request_draw()},help_text:"Set max value; leave blank to use default"}).addClass("yaxislabel top").css("color",this.prefs.label_color);this.container_div.prepend(ae)}if(af instanceof O){var ap=true;for(ao=0;ao<ar.length;ao++){if(!ar[ao].all_slotted){ap=false;break}}if(!ap){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(ac){var ac;if(this.mode==="Auto"){if(ac==="no_detail"){ac="feature spans"}else{if(ac==="summary_tree"){ac="coverage histogram"}}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 (u.FeatureSlotter)(ag,af,A,function(ah){return ad.measureText(ah)});this.slotters[ag]=ae}return ae.slot_features(ac)},get_mode:function(ac){if(ac.dataset_type==="summary_tree"){mode="summary_tree"}else{if(ac.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>I){mode="Squish"}else{mode="Pack"}}}return mode},get_canvas_height:function(ac,ag,ah,ad){if(ag==="summary_tree"||ag==="Coverage"){return this.summary_draw_height}else{var af=this.incremental_slots(ah,ac.data,ag);var ae=new (this.painter)(null,null,null,this.prefs,ag);return Math.max(aa,ae.get_required_height(af,ad))}},draw_tile:function(am,aq,ao,ar,af,aj,ae){var ap=this,ad=aq.canvas,ay=af.get("start"),ac=af.get("end"),ag=this.left_offset;if(ao==="summary_tree"||ao==="Coverage"){var aA=new L.SummaryTreePainter(am,ay,ac,this.prefs);aA.draw(aq,ad.width,ad.height,aj);return new j(ap,af,ar,ad,am.data,am.max)}var ai=[],an=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 an)){all_slotted=false}}}}var aB=(this.filters_manager.alpha_filter?new C(this.filters_manager.alpha_filter):null);var az=(this.filters_manager.height_filter?new C(this.filters_manager.height_filter):null);var aA=new (this.painter)(ai,ay,ac,this.prefs,ao,aB,az,ae);var aw=null;aq.fillStyle=this.prefs.block_color;aq.font=aq.canvas.manager.default_font;aq.textAlign="right";if(am.data){aw=aA.draw(aq,ad.width,ad.height,aj,an);aw.translation=-ag}return new O(ap,af,ar,ad,am.data,aj,ao,am.message,all_slotted,aw)},data_and_mode_compatible:function(ac,ad){if(ad==="Auto"){return true}else{if(ad==="Coverage"){return ac.dataset_type==="summary_tree"}else{if(ac.extra_info==="no_detail"||ac.dataset_type==="summary_tree"){return false}else{return true}}}},can_subset:function(ac){if(ac.dataset_type==="summary_tree"||ac.message||ac.extra_info==="no_detail"){return false}return true}});var R=function(ad,ac,ae){c.call(this,ad,ac,ae);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:l.get_random_color()},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}],saved_values:ae.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=L.ReadPainter};q(R.prototype,r.prototype,M.prototype,c.prototype);var T=function(ae,ad,ag){c.call(this,ae,ad,ag);var af=l.get_random_color(),ac=l.get_random_color([af,"#ffffff"]);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block and sense strand color",type:"color",default_value:af},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:ac},{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:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"Clear value to set automatically"},{key:"mode",type:"string",default_value:this.mode,hidden:true}],saved_values:ag.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=L.ReadPainter;this.update_icons()};q(T.prototype,r.prototype,M.prototype,c.prototype);var d={LineTrack:h,FeatureTrack:c,VcfTrack:R,ReadTrack:T,DiagonalHeatmapTrack:t,CompositeTrack:f,DrawableGroup:P};var p=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:Z,DrawableGroup:P,LineTrack:h,FeatureTrack:c,DiagonalHeatmapTrack:t,ReadTrack:T,VcfTrack:R,CompositeTrack:f,object_from_template:p}});
\ No newline at end of file
+define(["libs/underscore","viz/visualization","viz/trackster/util","viz/trackster/slotting","viz/trackster/painters","mvc/data","viz/trackster/filters"],function(ab,x,l,u,L,Y,i){var q=ab.extend;var V=function(ac){return("isResolved" in ac)};var n={};var k=function(ac,ad){n[ac.attr("id")]=ad};var m=function(ac,ae,ag,af){ag=".group";var ad={};n[ac.attr("id")]=af;ac.bind("drag",{handle:"."+ae,relative:true},function(ao,ap){var an=$(this),at=$(this).parent(),ak=at.children(),am=n[$(this).attr("id")],aj,ai,aq,ah,al;ai=$(this).parents(ag);if(ai.length!==0){aq=ai.position().top;ah=aq+ai.outerHeight();if(ap.offsetY<aq){$(this).insertBefore(ai);var ar=n[ai.attr("id")];ar.remove_drawable(am);ar.container.add_drawable_before(am,ar);return}else{if(ap.offsetY>ah){$(this).insertAfter(ai);var ar=n[ai.attr("id")];ar.remove_drawable(am);ar.container.add_drawable(am);return}}}ai=null;for(al=0;al<ak.length;al++){aj=$(ak.get(al));aq=aj.position().top;ah=aq+aj.outerHeight();if(aj.is(ag)&&this!==aj.get(0)&&ap.offsetY>=aq&&ap.offsetY<=ah){if(ap.offsetY-aq<ah-ap.offsetY){aj.find(".content-div").prepend(this)}else{aj.find(".content-div").append(this)}if(am.container){am.container.remove_drawable(am)}n[aj.attr("id")].add_drawable(am);return}}for(al=0;al<ak.length;al++){aj=$(ak.get(al));if(ap.offsetY<aj.position().top&&!(aj.hasClass("reference-track")||aj.hasClass("intro"))){break}}if(al===ak.length){if(this!==ak.get(al-1)){at.append(this);n[at.attr("id")].move_drawable(am,al)}}else{if(this!==ak.get(al)){$(this).insertBefore(ak.get(al));n[at.attr("id")].move_drawable(am,(ap.deltaY>0?al-1:al))}}}).bind("dragstart",function(){ad["border-top"]=ac.css("border-top");ad["border-bottom"]=ac.css("border-bottom");$(this).css({"border-top":"1px solid blue","border-bottom":"1px solid blue"})}).bind("dragend",function(){$(this).css(ad)})};var aa=16,G=9,D=20,A=100,I=12000,S=400,K=5000,w=100,o="Cannot display dataset due to an error. ",J="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",E="No data for this chrom/contig.",v="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",y="Tool cannot be rerun: ",a="Loading data...",U="Ready for display",Q=10,H=20;function W(ad,ac){if(!ac){ac=0}var ae=Math.pow(10,ac);return Math.round(ad*ae)/ae}var r=function(ad,ac,af){if(!r.id_counter){r.id_counter=0}this.id=r.id_counter++;this.name=af.name;this.view=ad;this.container=ac;this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name}],saved_values:af.prefs,onchange:function(){this.track.set_name(this.track.config.values.name)}});this.prefs=this.config.values;this.drag_handle_class=af.drag_handle_class;this.is_overview=false;this.action_icons={};this.content_visible=true;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/>").css("float","left").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(ag){ag.stopPropagation()});var ae=this;this.container_div.hover(function(){ae.icons_div.show()},function(){ae.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};r.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(ac){if(ac.content_visible){ac.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");ac.hide_contents();ac.content_visible=false}else{ac.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");ac.content_visible=true;ac.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"settings-icon",on_click_fn:function(ad){var af=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},ac=function(){ad.config.update_from_form($(".dialog-box"));hide_modal();$(window).unbind("keypress.check_enter_esc")},ae=function(ag){if((ag.keyCode||ag.which)===27){af()}else{if((ag.keyCode||ag.which)===13){ac()}}};$(window).bind("keypress.check_enter_esc",ae);show_modal("Configure",ad.config.build_form(),{Cancel:af,OK:ac})}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(ac){$(".bs-tooltip").remove();ac.remove()}}];q(r.prototype,{init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.content_visible){return true}return false},request_draw:function(){},_draw:function(){},to_dict:function(){},set_name:function(ac){this.old_name=this.name;this.name=ac;this.name_div.text(this.name)},revert_name:function(){if(this.old_name){this.name=this.old_name;this.name_div.text(this.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(){},build_header_div:function(){},add_action_icon:function(ad,ai,ah,ag,ac,af){var ae=this;this.action_icons[ad]=$("<a/>").attr("href","javascript:void(0);").attr("title",ai).addClass("icon-button").addClass(ah).tooltip().click(function(){ag(ae)}).appendTo(this.icons_div);if(af){this.action_icons[ad].hide()}},build_action_icons:function(ac){var ae;for(var ad=0;ad<ac.length;ad++){ae=ac[ad];this.add_action_icon(ae.name,ae.title,ae.css_class,ae.on_click_fn,ae.prepend,ae.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){},get_drawables:function(){}});var z=function(ad,ac,ae){r.call(this,ad,ac,ae);this.obj_type=ae.obj_type;this.drawables=[]};q(z.prototype,r.prototype,{unpack_drawables:function(ae){this.drawables=[];var ad;for(var ac=0;ac<ae.length;ac++){ad=p(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(){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac]._draw()}},to_dict:function(){var ad=[];for(var ac=0;ac<this.drawables.length;ac++){ad.push(this.drawables[ac].to_dict())}return{name:this.name,prefs:this.prefs,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}});var P=function(ad,ac,af){q(af,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});z.call(this,ad,ac,af);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);k(this.container_div,this);k(this.content_div,this);m(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new i.FiltersManager(this);this.header_div.after(this.filters_manager.parent_div);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 i.FiltersManager(this,af.filters);ae.parent_div.replaceWith(this.filters_manager.parent_div);if(af.filters.visible){this.setup_multitrack_filtering()}}};q(P.prototype,r.prototype,z.prototype,{action_icons_def:[r.prototype.action_icons_def[0],r.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(ac){$(".bs-tooltip").remove();ac.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",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(true)}ac.filters_manager.toggle()}},r.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},build_header_div:function(){var ac=$("<div/>").addClass("track-header");ac.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.name).appendTo(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){if(this.drawables[0] instanceof f){this.action_icons.composite_icon.show()}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||ac===1){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".bs-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 i.NumberFilter({name:af[0].name,index:af[0].index});this.filters_manager.add_filter(ah)}}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 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 ag=[];for(var ad=0;ad<this.drawables.length;ad++){ag.push(this.drawables[ad].name)}var ae="Composite Track of "+this.drawables.length+" tracks ("+ag.join(", ")+")";var af=new f(this.view,this.view,{name:ae,drawables:this.drawables});var ac=this.container.replace_drawable(this,af,true);af.request_draw()},add_drawable:function(ac){z.prototype.add_drawable.call(this,ac);this.update_icons()},remove_drawable:function(ac){z.prototype.remove_drawable.call(this,ac);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var ac=q(z.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,ae){for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad].request_draw(ac,ae)}}});var Z=Backbone.View.extend({initialize:function(ac){q(ac,{obj_type:"View"});z.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 x.CanvasManager(this.container.get(0).ownerDocument);this.reset()},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").css("position","relative").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;k(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(){x.select_datasets(select_datasets_url,add_track_async_url,{"f-dbkey":ac.dbkey},function(ag){ab.each(ag,function(ah){ac.add_drawable(p(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("href","javascript:void(0);").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"}).css("width","15em").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()}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ad).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("original-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_drawables(),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]}}))})}})});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();ac.request_redraw()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tooltip({placement:"bottom"}).click(function(){ac.zoom_in();ac.request_redraw()}).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)}).bind("mousewheel",function(ai,ak,ah,ag){if(ah){ah*=50;var aj=Math.round(-ah/ac.viewport_container.width()*(ac.high-ac.low));ac.move_delta(aj)}});this.top_labeltrack.bind("dragstart",function(ag,ah){return $("<div />").css({height:ac.browser_content_div.height()+ac.top_labeltrack.height()+ac.nav_labeltrack.height()+1,top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).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 X(this,{content_div:this.top_labeltrack}));this.add_label_track(new X(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")}});q(Z.prototype,z.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(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=w;var ac=this,ad=$.Deferred();$.ajax({url:chrom_url+"/"+this.dbkey,data:ae,dataType:"json",success:function(ag){if(ag.chrom_info.length===0){return}if(ag.reference){ac.add_label_track(new B(ac))}ac.chrom_data=ag.chrom_info;var aj='<option value="">Select Chrom/Contig</option>';for(var ai=0,af=ac.chrom_data.length;ai<af;ai++){var ah=ac.chrom_data[ai].chrom;aj+='<option value="'+ah+'">'+ah+"</option>"}if(ag.prev_chroms){aj+='<option value="previous">Previous '+w+"</option>"}if(ag.next_chroms){aj+='<option value="next">Next '+w+"</option>"}ac.chrom_select.html(aj);ac.chrom_start_index=ag.start_index;ad.resolve(ag)},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-w});return}if(ah==="next"){ae.load_chroms({low:this.chrom_start_index+w});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();ae.request_redraw(true);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=Math.max(ad,0);ae.high=Math.min(aj,ae.max_high)}else{ae.low=0;ae.high=ae.max_high}ae.reset_overview();ae.request_redraw()}},go_to:function(ag){ag=ag.replace(/ |,/g,"");var ak=this,ac,af,ad=ag.split(":"),ai=ad[0],aj=ad[1];if(aj!==undefined){try{var ah=aj.split("-");ac=parseInt(ah[0],10);af=parseInt(ah[1],10)}catch(ae){return false}}ak.change_chrom(ai,ac,af)},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();var ad=ac.chrom_select.val();this.trigger_navigate(ad,ac.low,ac.high,true)},add_drawable:function(ac){z.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){z.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(ak,ac,aj,al){var ai=this,ah=(al?[al]:ai.drawables),ae;var ad;for(var ag=0;ag<ah.length;ag++){ad=ah[ag];ae=-1;for(var af=0;af<ai.tracks_to_be_redrawn.length;af++){if(ai.tracks_to_be_redrawn[af][0]===ad){ae=af;break}}if(ae<0){ai.tracks_to_be_redrawn.push([ad,ac,aj])}else{ai.tracks_to_be_redrawn[ag][1]=ac;ai.tracks_to_be_redrawn[ag][2]=aj}}if(!this.requested_redraw){requestAnimationFrame(function(){ai._redraw(ak)});this.requested_redraw=true}},_redraw:function(am){this.requested_redraw=false;var aj=this.low,af=this.high;if(aj<this.max_low){aj=this.max_low}if(af>this.max_high){af=this.max_high}var al=this.high-this.low;if(this.high!==0&&al<this.min_separation){af=aj+this.min_separation}this.low=Math.floor(aj);this.high=Math.ceil(af);this.update_location(this.low,this.high);this.resolution_b_px=(this.high-this.low)/this.viewport_container.width();this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var ac=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ai=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var an=13;this.overview_box.css({left:ac,width:Math.max(an,ai)}).show();if(ai<an){this.overview_box.css("left",ac-(an-ai)/2)}if(this.overview_highlight){this.overview_highlight.css({left:ac,width:ai})}if(!am){var ae,ad,ak;for(var ag=0,ah=this.tracks_to_be_redrawn.length;ag<ah;ag++){ae=this.tracks_to_be_redrawn[ag][0];ad=this.tracks_to_be_redrawn[ag][1];ak=this.tracks_to_be_redrawn[ag][2];if(ae){ae._draw(ad,ak)}}this.tracks_to_be_redrawn=[];for(ag=0,ah=this.label_tracks.length;ag<ah;ag++){this.label_tracks[ag]._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_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());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(){$(".bs-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 s=function(ae,aj,af){this.track=ae;this.name=aj.name;this.params=[];var aq=aj.params;for(var ag=0;ag<aq.length;ag++){var al=aq[ag],ad=al.name,ap=al.label,ah=unescape(al.html),ar=al.value,an=al.type;if(an==="number"){this.params.push(new e(ad,ap,ah,(ad in af?af[ad]:ar),al.min,al.max))}else{if(an==="select"){this.params.push(new N(ad,ap,ah,(ad in af?af[ad]:ar)))}else{console.log("WARNING: unrecognized tool parameter type:",ad,an)}}}this.parent_div=$("<div/>").addClass("dynamic-tool").hide();this.parent_div.bind("drag",function(au){au.stopPropagation()}).click(function(au){au.stopPropagation()}).bind("dblclick",function(au){au.stopPropagation()});var ao=$("<div class='tool-name'>").appendTo(this.parent_div).text(this.name);var am=this.params;var ak=this;$.each(this.params,function(av,ay){var ax=$("<div>").addClass("param-row").appendTo(ak.parent_div);var au=$("<div>").addClass("param-label").text(ay.label).appendTo(ax);var aw=$("<div/>").addClass("param-input").html(ay.html).appendTo(ax);aw.find(":input").val(ay.value);$("<div style='clear: both;'/>").appendTo(ax)});this.parent_div.find("input").click(function(){$(this).select()});var at=$("<div>").addClass("param-row").appendTo(this.parent_div);var ai=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(at);var ac=$("<input type='submit'>").attr("value","Run on visible region").css("margin-left","3em").appendTo(at);ac.click(function(){ak.run_on_region()});ai.click(function(){ak.run_on_dataset()});if("visible" in af&&af.visible){this.parent_div.show()}};q(s.prototype,{update_params:function(){for(var ac=0;ac<this.params.length;ac++){this.params[ac].update_value()}},state_dict:function(){var ad={};for(var ac=0;ac<this.params.length;ac++){ad[this.params[ac].name]=this.params[ac].value}ad.visible=this.parent_div.is(":visible");return ad},get_param_values_dict:function(){var ac={};this.parent_div.find(":input").each(function(){var ad=$(this).attr("name"),ae=$(this).val();ac[ad]=ae});return ac},get_param_values:function(){var ac=[];this.parent_div.find(":input").each(function(){var ad=$(this).attr("name"),ae=$(this).val();if(ad){ac[ac.length]=ae}});return ac},run_on_dataset:function(){var ac=this;ac.run({target_dataset_id:this.track.original_dataset_id,tool_id:ac.name},null,function(ad){show_modal(ac.name+" is Running",ac.name+" is running on the complete dataset. Tool outputs are in dataset's history.",{Close:hide_modal})})},run_on_region:function(){var ai=new x.GenomeRegion({chrom:this.track.view.chrom,start:this.track.view.low,end:this.track.view.high}),ad={target_dataset_id:this.track.original_dataset_id,action:"rerun",tool_id:this.name,regions:[ai.toJSON()]},ah=this.track,ae=ad.tool_id+ah.tool_region_and_parameters_str(ai),ac;if(ah.container===view){var ag=new P(view,view,{name:this.name});var af=ah.container.replace_drawable(ah,ag,false);ag.container_div.insertBefore(ah.view.content_div.children()[af]);ag.add_drawable(ah);ah.container_div.appendTo(ag.content_div);ac=ag}else{ac=ah.container}var aj=new ah.constructor(view,ac,{name:ae,hda_ldda:"hda"});aj.init_for_tool_data();aj.change_mode(ah.mode);aj.set_filters_manager(ah.filters_manager.copy(aj));aj.update_icons();ac.add_drawable(aj);aj.tiles_div.text("Starting job.");this.update_params();this.run(ad,aj,function(ak){aj.set_dataset(new Y.Dataset(ak));aj.tiles_div.text("Running job.");aj.init()})},run:function(ac,ae,af){ac.inputs=this.get_param_values_dict();var ad=new l.ServerStateDeferred({ajax_settings:{url:galaxy_paths.get("tool_url"),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(J)}else{if(ag.error){ae.container_div.addClass("error");ae.content_div.text(y+ag.message)}else{af(ag)}}})}});var N=function(ad,ac,ae,af){this.name=ad;this.label=ac;this.html=$(ae);this.value=af};q(N.prototype,{update_value:function(){this.value=$(this.html).val()}});var e=function(ae,ad,ag,ah,af,ac){N.call(this,ae,ad,ag,ah);this.min=af;this.max=ac};q(e.prototype,N.prototype,{update_value:function(){N.prototype.update_value.call(this);this.value=parseFloat(this.value)}});var C=function(ac,ad){L.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 F=function(ac){this.track=ac.track;this.params=ac.params;this.values={};this.restore_values((ac.saved_values?ac.saved_values:{}));this.onchange=ac.onchange};q(F.prototype,{restore_values:function(ac){var ad=this;$.each(this.params,function(ae,af){if(ac[af.key]!==undefined){ad.values[af.key]=ac[af.key]}else{ad.values[af.key]=af.default_value}})},build_form:function(){var af=this;var ac=$("<div />");var ae;function ad(ak,ag){for(var ao=0;ao<ak.length;ao++){ae=ak[ao];if(ae.hidden){continue}var ai="param_"+ao;var at=af.values[ae.key];var av=$("<div class='form-row' />").appendTo(ag);av.append($("<label />").attr("for",ai).text(ae.label+":"));if(ae.type==="bool"){av.append($('<input type="checkbox" />').attr("id",ai).attr("name",ai).attr("checked",at))}else{if(ae.type==="text"){av.append($('<input type="text"/>').attr("id",ai).val(at).click(function(){$(this).select()}))}else{if(ae.type==="select"){var aq=$("<select />").attr("id",ai);for(var am=0;am<ae.options.length;am++){$("<option/>").text(ae.options[am].label).attr("value",ae.options[am].value).appendTo(aq)}aq.val(at);av.append(aq)}else{if(ae.type==="color"){var au=$("<div/>").appendTo(av),ap=$("<input />").attr("id",ai).attr("name",ai).val(at).css("float","left").appendTo(au).click(function(ax){$(".bs-tooltip").removeClass("in");var aw=$(this).siblings(".bs-tooltip").addClass("in");aw.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(aw).height()/2)+($(this).height()/2)}).show();aw.click(function(ay){ay.stopPropagation()});$(document).bind("click.color-picker",function(){aw.hide();$(document).unbind("click.color-picker")});ax.stopPropagation()}),an=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(au).attr("title","Set new random color").tooltip(),ar=$("<div class='bs-tooltip right' style='position: absolute;' />").appendTo(au).hide(),aj=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(ar),ah=$("<div class='tooltip-arrow'></div>").appendTo(ar),al=$.farbtastic(aj,{width:100,height:100,callback:ap,color:at});au.append($("<div/>").css("clear","both"));(function(aw){an.click(function(){aw.setColor(l.get_random_color())})})(al)}else{av.append($("<input />").attr("id",ai).attr("name",ai).val(at))}}}}if(ae.help){av.append($("<div class='help'/>").text(ae.help))}}}ad(this.params,ac);return ac},update_from_form:function(ac){var ae=this;var ad=false;$.each(this.params,function(af,ah){if(!ah.hidden){var ai="param_"+af;var ag=ac.find("#"+ai).val();if(ah.type==="float"){ag=parseFloat(ag)}else{if(ah.type==="int"){ag=parseInt(ag)}else{if(ah.type==="bool"){ag=ac.find("#"+ai).is(":checked")}}}if(ag!==ae.values[ah.key]){ae.values[ah.key]=ag;ad=true}}});if(ad){this.onchange();this.track.changed()}}});var b=function(ac,ag,ae,ad,af){this.track=ac;this.region=ag;this.low=ag.get("start");this.high=ag.get("end");this.resolution=ae;this.html_elt=$("<div class='track-tile'/>").append(ad).height($(ad).attr("height"));this.data=af;this.stale=false};b.prototype.predisplay_actions=function(){};var j=function(ac,ah,ae,ad,af,ag){b.call(this,ac,ah,ae,ad,af);this.max_val=ag};q(j.prototype,b.prototype);var O=function(af,an,ag,ae,ai,ap,aj,aq,ad,am){b.call(this,af,an,ag,ae,ai);this.mode=aj;this.all_slotted=ad;this.feature_mapper=am;this.has_icons=false;if(aq){this.has_icons=true;var ak=this;ae=this.html_elt.children()[0],message_div=$("<div/>").addClass("tile-message").css({height:D-1,width:ae.width}).prependTo(this.html_elt);var al=new x.GenomeRegion({chrom:af.view.chrom,start:this.low,end:this.high}),ao=ai.length,ah=$("<a href='javascript:void(0);'/>").addClass("icon more-down").attr("title","For speed, only the first "+ao+" features in this region were obtained from server. Click to get more data including depth").tooltip().appendTo(message_div),ac=$("<a href='javascript:void(0);'/>").addClass("icon more-across").attr("title","For speed, only the first "+ao+" features in this region were obtained from server. Click to get more data excluding depth").tooltip().appendTo(message_div);ah.click(function(){ak.stale=true;af.data_manager.get_more_data(al,af.mode,ak.resolution,{},af.data_manager.DEEP_DATA_REQ);$(".bs-tooltip").hide();af.request_draw(true)}).dblclick(function(ar){ar.stopPropagation()});ac.click(function(){ak.stale=true;af.data_manager.get_more_data(al,af.mode,ak.resolution,{},af.data_manager.BROAD_DATA_REQ);$(".bs-tooltip").hide();af.request_draw(true)}).dblclick(function(ar){ar.stopPropagation()})}};q(O.prototype,b.prototype);O.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 ak=at[0],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]}var af=$("<div/>").attr("id",ak).addClass("feature-popup"),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"?W(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"))-af.width()/2,ae=am+parseInt(ad.html_elt.css("top"))+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 g=function(ad,ac,ae){q(ae,{drag_handle_class:"draghandle"});r.call(this,ad,ac,ae);this.dataset=new Y.Dataset({id:ae.dataset_id,hda_ldda:ae.hda_ldda});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:K);this.data_manager=("data_manager" in ae?ae.data_manager:new x.GenomeDataManager({dataset:this.dataset,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=0;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()}}};q(g.prototype,r.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},r.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"overview-icon",on_click_fn:function(ac){ac.view.set_overview(ac)}},r.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"filters-icon",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.dynamic_tool_div.toggle();if(ac.dynamic_tool_div.is(":visible")){ac.set_name(ac.name+ac.tool_region_and_parameters_str())}else{ac.revert_name()}$(".bs-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.name %><br/><strong>Dataset</strong>: <%= track.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(){hide_modal();$(window).unbind("keypress.check_enter_esc")},ad=function(){var aj=$('select[name="regions"] option:selected').val(),al,ai=new x.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),ak=ab.map($(".bookmark"),function(am){return new x.GenomeRegion({from_str:$(am).children(".position").text()})});if(aj==="cur"){al=[ai]}else{if(aj==="bookmarks"){al=ak}else{al=[ai].concat(ak)}}hide_modal();window.location.href=galaxy_paths.get("sweepster_url")+"?"+$.param({dataset_id:ac.dataset_id,hda_ldda:ac.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()}}};show_modal("Visualize tool parameter space and output from different parameter settings?",ae,{No:ah,Yes:ad})}},r.prototype.action_icons_def[2]],can_draw:function(){if(this.dataset_id&&r.prototype.can_draw.call(this)){return true}return false},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var ac=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(ac)}this.name_div=$("<div/>").addClass("track-name").appendTo(ac).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return ac},on_resize:function(){},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.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.values.height=ac.visible_height_px;ac.changed()}).appendTo(ac.container_div)},set_display_modes:function(af,ai){this.display_modes=af;this.mode=(ai?ai:(this.config&&this.config.values.mode?this.config.values.mode:this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var ad=this,ag={};for(var ae=0,ac=ad.display_modes.length;ae<ac;ae++){var ah=ad.display_modes[ae];ag[ah]=function(aj){return function(){ad.change_mode(aj);ad.icons_div.show();ad.container_div.mouseleave(function(){ad.icons_div.hide()})}}(ah)}make_popupmenu(this.action_icons.mode_icon,ag)},build_action_icons:function(){r.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 X){return"LabelTrack"}else{if(this instanceof B){return"ReferenceTrack"}else{if(this instanceof h){return"LineTrack"}else{if(this instanceof T){return"ReadTrack"}else{if(this instanceof R){return"VcfTrack"}else{if(this instanceof f){return"CompositeTrack"}else{if(this instanceof c){return"FeatureTrack"}}}}}}}return""},init:function(ae){var ad=this;ad.enabled=false;ad.tile_cache.clear();ad.data_manager.clear();ad.content_div.css("height","auto");ad.tiles_div.text("").children().remove();ad.container_div.removeClass("nodata error pending");if(!ad.dataset_id){return}var ac=$.Deferred(),af={hda_ldda:ad.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");ad.tiles_div.text(o);if(ag.message){ad.tiles_div.append($("<a href='javascript:void(0);'></a>").text("View error").click(function(){show_modal("Trackster Error","<pre>"+ag.message+"</pre>",{Close:hide_modal})}));ad.tiles_div.append($("<span/>").text(" "));ad.tiles_div.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.tiles_div.text(J)}else{if(ag==="no data"||(ag.data!==undefined&&(ag.data===null||ag.data.length===0))){ad.container_div.addClass("nodata");ad.tiles_div.text(E)}else{if(ag==="pending"){ad.container_div.addClass("pending");ad.tiles_div.html(v);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(U);if(ad.view.chrom){ad.tiles_div.text("");ad.tiles_div.css("height",ad.visible_height_px+"px");ad.enabled=true;$.when(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(){},get_drawables:function(){return this}});var M=function(ae,ad,af){g.call(this,ae,ad,af);var ac=this;m(ac.container_div,ac.drag_handle_class,".group",ac);this.filters_manager=new i.FiltersManager(this,("filters" in af?af.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=("tool" in af&&af.tool?new s(this,af.tool,af.tool_state):null);this.tile_cache=new x.Cache(Q);if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){this.dynamic_tool_div=this.tool.parent_div;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(af.mode){this.change_mode(af.mode)}};q(M.prototype,r.prototype,g.prototype,{action_icons_def:g.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){$(".bs-tooltip").remove();ac.slotters[ac.view.resolution_px_b].max_rows*=2;ac.request_draw(true)},hide:true}]),copy:function(ac){var ad=this.to_dict();q(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(),name:this.name,hda_ldda:this.hda_ldda,dataset_id:this.dataset_id,prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},change_mode:function(ad){var ac=this;ac.mode=ad;ac.config.values.mode=ad;ac.tile_cache.clear();ac.request_draw();this.action_icons.mode_icon.attr("title","Set display mode (now: "+ac.mode+")");return ac},update_icons:function(){var ac=this;if(ac.filters_available){ac.action_icons.filters_icon.show()}else{ac.action_icons.filters_icon.hide()}if(ac.tool){ac.action_icons.tools_icon.show();ac.action_icons.param_space_viz_icon.show()}else{ac.action_icons.tools_icon.hide();ac.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(ad,ae,ac){return ad+"_"+ae+"_"+ac},request_draw:function(ad,ac){this.view.request_redraw(false,ad,ac,this)},before_draw:function(){},_draw:function(ad,an){if(!this.can_draw()){return}var al=this.view.low,ah=this.view.high,aj=ah-al,ae=this.view.container.width(),ap=this.view.resolution_px_b,ag=this.view.resolution_b_px;if(this.is_overview){al=this.view.max_low;ah=this.view.max_high;ag=(view.max_high-view.max_low)/ae;ap=1/ag}this.before_draw();this.tiles_div.children().addClass("remove");var ac=Math.floor(al/(ag*S)),ak=true,ao=[],ai=function(aq){return(aq&&"track" in aq)};while((ac*S*ag)<ah){var am=this.draw_helper(ad,ae,ac,ag,this.tiles_div,ap);if(ai(am)){ao.push(am)}else{ak=false}ac+=1}if(!an){this.tiles_div.children(".remove").removeClass("remove").remove()}var af=this;if(ak){this.tiles_div.children(".remove").remove();af.postdraw_actions(ao,ae,ap,an)}},postdraw_actions:function(ae,af,ah,ac){var ag=false;for(var ad=0;ad<ae.length;ad++){if(ae[ad].has_icons){ag=true;break}}if(ag){for(var ad=0;ad<ae.length;ad++){tile=ae[ad];if(!tile.has_icons){tile.html_elt.css("padding-top",D)}}}},draw_helper:function(ac,ao,au,ar,ah,ai,ap){var an=this,ax=this._gen_tile_cache_key(ao,ai,au),af=this._get_tile_bounds(au,ar);if(!ap){ap={}}var aw=(ac?undefined:an.tile_cache.get_elt(ax));if(aw){an.show_tile(aw,ah,ai);return aw}var al=true;var at=an.data_manager.get_data(af,an.mode,ar,an.data_url_extra_params);if(V(at)){al=false}var aj;if(view.reference_track&&ai>view.canvas_manager.char_width_px){aj=view.reference_track.data_manager.get_data(af,an.mode,ar,view.reference_track.data_url_extra_params);if(V(aj)){al=false}}if(al){q(at,ap.more_tile_data);var ak=an.mode;if(ak==="Auto"){ak=an.get_mode(at);an.update_auto_mode(ak)}var ae=an.view.canvas_manager.new_canvas(),av=af.get("start"),ad=af.get("end"),ao=Math.ceil((ad-av)*ai)+an.left_offset,am=an.get_canvas_height(at,ak,ai,ao);ae.width=ao;ae.height=am;var aq=ae.getContext("2d");aq.translate(this.left_offset,0);var aw=an.draw_tile(at,aq,ak,ar,af,ai,aj);if(aw!==undefined){an.tile_cache.set_elt(ax,aw);an.show_tile(aw,ah,ai)}return aw}var ag=$.Deferred();$.when(at,aj).then(function(){view.request_redraw(false,false,false,an);ag.resolve()});return ag},get_canvas_height:function(ac,ae,af,ad){return this.visible_height_px},draw_tile:function(ac,ad,ah,af,ag,ai,ae){console.log("Warning: TiledTrack.draw_tile() not implemented.")},show_tile:function(ae,ah,ai){var ad=this,ac=ae.html_elt;ae.predisplay_actions();var ag=(ae.low-(this.is_overview?this.view.max_low:this.view.low))*ai;if(this.left_offset){ag-=this.left_offset}ac.css({position:"absolute",top:0,left:ag});if(ac.hasClass("remove")){ac.removeClass("remove")}else{ah.append(ac)}ae.html_elt.height("auto");this.max_height_px=Math.max(this.max_height_px,ae.html_elt.height());ae.html_elt.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")},_get_tile_bounds:function(ac,ad){var af=Math.floor(ac*S*ad),ag=Math.ceil(S*ad),ae=(af+ag<=this.view.max_high?af+ag:this.view.max_high);return new x.GenomeRegion({chrom:this.view.chrom,start:af,end:ae})},tool_region_and_parameters_str:function(ae){var ac=this,ad=(ae!==undefined?ae.toString():"all");return" - region=["+ad+"], parameters=["+ac.tool.get_param_values().join(", ")+"]"},data_and_mode_compatible:function(ac,ad){return true},can_subset:function(ac){return false},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data");this.data_query_wait=1000;this.dataset_check_type="state";this.normal_postdraw_actions=this.postdraw_actions;this.postdraw_actions=function(ae,af,ah,ac){var ad=this;ad.normal_postdraw_actions(ae,af,ah,ac);ad.dataset_check_type="converted_datasets_state";ad.data_query_wait=K;var ag=new l.ServerStateDeferred({url:ad.dataset_state_url,url_params:{dataset_id:ad.dataset_id,hda_ldda:ad.hda_ldda},interval:ad.data_query_wait,success_fn:function(ai){return ai!=="pending"}});$.when(ag.go()).then(function(){ad.data_manager.set("data_type","data")});ad.postdraw_actions=ad.normal_postdraw_actions}}});var X=function(ad,ac){var ae={resize:false};g.call(this,ad,ac,ae);this.container_div.addClass("label-track")};q(X.prototype,g.prototype,{build_header_div:function(){},init:function(){this.enabled=true},_draw:function(){var ae=this.view,af=ae.high-ae.low,ai=Math.floor(Math.pow(10,Math.floor(Math.log(af)/Math.log(10)))),ac=Math.floor(ae.low/ai)*ai,ag=this.view.container.width(),ad=$("<div style='position: relative; height: 1.3em;'></div>");while(ac<ae.high){var ah=(ac-ae.low)/af*ag;ad.append($("<div class='label'>"+commatize(ac)+"</div>").css({position:"absolute",left:ah-1}));ac+=ai}this.content_div.children(":first").remove();this.content_div.append(ad)}});var f=function(ad,ac,ag){M.call(this,ad,ac,ag);this.drawables=[];this.left_offset=0;if("drawables" in ag){var af;for(var ae=0;ae<ag.drawables.length;ae++){af=ag.drawables[ae];this.drawables[ae]=p(af,ad,null);if(af.left_offset>this.left_offset){this.left_offset=af.left_offset}}this.enabled=true}if(this.drawables.length!==0){this.set_display_modes(this.drawables[0].display_modes,this.drawables[0].mode)}this.update_icons();this.obj_type="CompositeTrack"};q(f.prototype,M.prototype,{action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(ac){$(".bs-tooltip").remove();ac.show_group()}}].concat(M.prototype.action_icons_def),to_dict:z.prototype.to_dict,add_drawable:z.prototype.add_drawable,unpack_drawables:z.prototype.unpack_drawables,change_mode:function(ac){M.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:r.prototype.can_draw,draw_helper:function(ad,at,az,aw,ak,am,au){var ar=this,aD=this._gen_tile_cache_key(at,am,az),ah=this._get_tile_bounds(az,aw);if(!au){au={}}var aC=(ad?undefined:ar.tile_cache.get_elt(aD));if(aC){ar.show_tile(aC,ak,am);return aC}var al=[],ar,ap=true,ax,an;for(var ay=0;ay<this.drawables.length;ay++){ar=this.drawables[ay];ax=ar.data_manager.get_data(ah,ar.mode,aw,ar.data_url_extra_params);if(V(ax)){ap=false}al.push(ax);an=null;if(view.reference_track&&am>view.canvas_manager.char_width_px){an=view.reference_track.data_manager.get_data(ah,ar.mode,aw,view.reference_track.data_url_extra_params);if(V(an)){ap=false}}al.push(an)}if(ap){q(ax,au.more_tile_data);this.tile_predraw_init();var ag=ar.view.canvas_manager.new_canvas(),ai=ar._get_tile_bounds(az,aw),aA=ah.get("start"),ae=ah.get("end"),aB=0,at=Math.ceil((ae-aA)*am)+this.left_offset,aq=0,af=[],ay;var ac=0;for(ay=0;ay<this.drawables.length;ay++,aB+=2){ar=this.drawables[ay];ax=al[aB];var ao=ar.mode;if(ao==="Auto"){ao=ar.get_mode(ax);ar.update_auto_mode(ao)}af.push(ao);ac=ar.get_canvas_height(ax,ao,am,at);if(ac>aq){aq=ac}}ag.width=at;ag.height=(au.height?au.height:aq);aB=0;var av=ag.getContext("2d");av.translate(this.left_offset,0);av.globalAlpha=0.5;av.globalCompositeOperation="source-over";for(ay=0;ay<this.drawables.length;ay++,aB+=2){ar=this.drawables[ay];ax=al[aB];an=al[aB+1];aC=ar.draw_tile(ax,av,af[ay],aw,ah,am,an)}this.tile_cache.set_elt(aD,aC);this.show_tile(aC,ak,am);return aC}var aj=$.Deferred(),ar=this;$.when.apply($,al).then(function(){view.request_redraw(false,false,false,ar);aj.resolve()});return aj},show_group:function(){var af=new P(this.view,this.container,{name:this.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()},tile_predraw_init:function(){var af=Number.MAX_VALUE,ac=-af,ad;for(var ae=0;ae<this.drawables.length;ae++){ad=this.drawables[ae];if(ad instanceof h){if(ad.prefs.min_value<af){af=ad.prefs.min_value}if(ad.prefs.max_value>ac){ac=ad.prefs.max_value}}}for(var ae=0;ae<this.drawables.length;ae++){ad=this.drawables[ae];ad.prefs.min_value=af;ad.prefs.max_value=ac}},postdraw_actions:function(ae,ah,aj,ad){M.prototype.postdraw_actions.call(this,ae,ah,aj,ad);var ag=-1;for(var af=0;af<ae.length;af++){var ac=ae[af].html_elt.find("canvas").height();if(ac>ag){ag=ac}}for(var af=0;af<ae.length;af++){var ai=ae[af];if(ai.html_elt.find("canvas").height()!==ag){this.draw_helper(true,ah,ai.index,ai.resolution,ai.html_elt.parent(),aj,{height:ag});ai.html_elt.remove()}}}});var B=function(ac){M.call(this,ac,{content_div:ac.top_labeltrack},{resize:false});ac.reference_track=this;this.left_offset=200;this.visible_height_px=12;this.container_div.addClass("reference-track");this.content_div.css("background","none");this.content_div.css("min-height","0px");this.content_div.css("border","none");this.data_url=reference_url+"/"+this.view.dbkey;this.data_url_extra_params={reference:true};this.data_manager=new x.ReferenceTrackDataManager({data_url:this.data_url});this.hide_contents()};q(B.prototype,r.prototype,M.prototype,{build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},can_draw:r.prototype.can_draw,draw_helper:function(ag,ae,ac,ad,ah,ai,af){if(ai>this.view.canvas_manager.char_width_px){return M.prototype.draw_helper.call(this,ag,ae,ac,ad,ah,ai,af)}else{this.hide_contents();return null}},draw_tile:function(ak,al,ag,af,ai,am){var ae=this;if(am>this.view.canvas_manager.char_width_px){if(ak.data===null){this.hide_contents();return}var ad=al.canvas;al.font=al.canvas.manager.default_font;al.textAlign="center";ak=ak.data;for(var ah=0,aj=ak.length;ah<aj;ah++){var ac=Math.floor(ah*am);al.fillText(ak[ah],ac,10)}this.show_contents();return new b(ae,ai,af,ad,ak)}this.hide_contents()}});var h=function(ae,ad,af){var ac=this;this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";M.call(this,ae,ad,af);this.hda_ldda=af.hda_ldda;this.dataset_id=af.dataset_id;this.original_dataset_id=this.dataset_id;this.left_offset=0;this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"color",label:"Color",type:"color",default_value:l.get_random_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:32,hidden:true}],saved_values:af.prefs,onchange:function(){ac.set_name(ac.prefs.name);ac.vertical_range=ac.prefs.max_value-ac.prefs.min_value;ac.set_min_value(ac.prefs.min_value);ac.set_max_value(ac.prefs.max_value)}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value};q(h.prototype,r.prototype,M.prototype,{on_resize:function(){this.request_draw(true)},set_min_value:function(ac){this.prefs.min_value=ac;$("#linetrack_"+this.dataset_id+"_minval").text(this.prefs.min_value);this.tile_cache.clear();this.request_draw()},set_max_value:function(ac){this.prefs.max_value=ac;$("#linetrack_"+this.dataset_id+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.request_draw()},predraw_init:function(){var ac=this;ac.vertical_range=undefined;return $.getJSON(ac.dataset.url(),{data_type:"data",stats:true,chrom:ac.view.chrom,low:0,high:ac.view.max_high,hda_ldda:ac.hda_ldda},function(ad){ac.container_div.addClass("line-track");var ag=ad.data;if(isNaN(parseFloat(ac.prefs.min_value))||isNaN(parseFloat(ac.prefs.max_value))){var ae=ag.min,ai=ag.max;ae=Math.floor(Math.min(0,Math.max(ae,ag.mean-2*ag.sd)));ai=Math.ceil(Math.max(0,Math.min(ai,ag.mean+2*ag.sd)));ac.prefs.min_value=ae;ac.prefs.max_value=ai;$("#track_"+ac.dataset_id+"_minval").val(ac.prefs.min_value);$("#track_"+ac.dataset_id+"_maxval").val(ac.prefs.max_value)}ac.vertical_range=ac.prefs.max_value-ac.prefs.min_value;ac.total_frequency=ag.total_frequency;ac.container_div.find(".yaxislabel").remove();var ah=$("<div/>").text(W(ac.prefs.min_value,3)).make_text_editable({num_cols:6,on_finish:function(aj){$(".bs-tooltip").remove();var aj=parseFloat(aj);if(!isNaN(aj)){ac.set_min_value(aj)}},help_text:"Set min value"}).addClass("yaxislabel bottom").attr("id","linetrack_"+ac.dataset_id+"_minval").prependTo(ac.container_div),af=$("<div/>").text(W(ac.prefs.max_value,3)).make_text_editable({num_cols:6,on_finish:function(aj){$(".bs-tooltip").remove();var aj=parseFloat(aj);if(!isNaN(aj)){ac.set_max_value(aj)}},help_text:"Set max value"}).addClass("yaxislabel top").attr("id","linetrack_"+ac.dataset_id+"_maxval").prependTo(ac.container_div)})},draw_tile:function(al,aj,ae,ad,ag,ak){var ac=aj.canvas,af=ag.get("start"),ai=ag.get("end"),ah=new L.LinePainter(al.data,af,ai,this.prefs,ae);ah.draw(aj,ac.width,ac.height,ak);return new b(this,ag,ad,ac,al.data)},can_subset:function(ac){return false}});var t=function(ae,ad,af){var ac=this;this.display_modes=["Heatmap"];this.mode="Heatmap";M.call(this,ae,ad,af);this.hda_ldda=af.hda_ldda;this.dataset_id=af.dataset_id;this.original_dataset_id=this.dataset_id;this.left_offset=0;this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{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}],saved_values:af.prefs,onchange:function(){ac.set_name(ac.prefs.name);ac.vertical_range=ac.prefs.max_value-ac.prefs.min_value;ac.set_min_value(ac.prefs.min_value);ac.set_max_value(ac.prefs.max_value)}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value};q(t.prototype,r.prototype,M.prototype,{on_resize:function(){this.request_draw(true)},set_min_value:function(ac){this.prefs.min_value=ac;this.tile_cache.clear();this.request_draw()},set_max_value:function(ac){this.prefs.max_value=ac;this.tile_cache.clear();this.request_draw()},draw_tile:function(ac,ae,ai,ag,ah,aj){var af=ae.canvas,ad=new L.DiagonalHeatmapPainter(ac.data,ah.get("start"),ah.get("end"),this.prefs,ai);ad.draw(ae,af.width,af.height,aj);return new b(this,ah,ag,af,ac.data)}});var c=function(af,ae,ah){var ad=this;this.display_modes=["Auto","Coverage","Dense","Squish","Pack"];M.call(this,af,ae,ah);var ag=l.get_random_color(),ac=l.get_random_color([ag,"#FFFFFF"]);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:ag},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:ac},{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:"histogram_max",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:this.visible_height_px,hidden:true}],saved_values:ah.prefs,onchange:function(){ad.set_name(ad.prefs.name);ad.tile_cache.clear();ad.set_painter_from_config();ad.request_draw()}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.container_div.addClass("feature-track");this.hda_ldda=ah.hda_ldda;this.dataset_id=ah.dataset_id;this.original_dataset_id=ah.dataset_id;this.show_labels_scale=0.001;this.showing_details=false;this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};q(c.prototype,r.prototype,M.prototype,{set_dataset:function(ac){this.dataset_id=ac.get("id");this.hda_ldda=ac.get("hda_ldda");this.dataset=ac;this.data_manager.set("dataset",ac)},set_painter_from_config:function(){if(this.config.values.connector_style==="arcs"){this.painter=L.ArcLinkedFeaturePainter}else{this.painter=L.LinkedFeaturePainter}},before_draw:function(){this.max_height_px=0},postdraw_actions:function(ar,am,ah,ag){M.prototype.postdraw_actions.call(this,ar,ag);var al=this,ao;if(al.mode==="Coverage"){var ad=-1;for(ao=0;ao<ar.length;ao++){var an=ar[ao].max_val;if(an>ad){ad=an}}for(ao=0;ao<ar.length;ao++){var au=ar[ao];if(au.max_val!==ad){au.html_elt.remove();al.draw_helper(true,am,au.index,au.resolution,au.html_elt.parent(),ah,{more_tile_data:{max:ad}})}}}if(al.filters_manager){var ai=al.filters_manager.filters;for(var aq=0;aq<ai.length;aq++){ai[aq].update_ui_elt()}var at=false,ac,aj;for(ao=0;ao<ar.length;ao++){if(ar[ao].data.length){ac=ar[ao].data[0];for(var aq=0;aq<ai.length;aq++){aj=ai[aq];if(aj.applies_to(ac)&&aj.min!==aj.max){at=true;break}}}}if(al.filters_available!==at){al.filters_available=at;if(!al.filters_available){al.filters_manager.hide()}al.update_icons()}}this.container_div.find(".yaxislabel").remove();var af=ar[0];if(af instanceof j){var ak=(this.prefs.histogram_max?this.prefs.histogram_max:af.max_val),ae=$("<div/>").text(ak).make_text_editable({num_cols:12,on_finish:function(av){$(".bs-tooltip").remove();var av=parseFloat(av);al.prefs.histogram_max=(!isNaN(av)?av:null);al.tile_cache.clear();al.request_draw()},help_text:"Set max value; leave blank to use default"}).addClass("yaxislabel top").css("color",this.prefs.label_color);this.container_div.prepend(ae)}if(af instanceof O){var ap=true;for(ao=0;ao<ar.length;ao++){if(!ar[ao].all_slotted){ap=false;break}}if(!ap){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(ac){var ac;if(this.mode==="Auto"){if(ac==="no_detail"){ac="feature spans"}else{if(ac==="summary_tree"){ac="coverage histogram"}}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 (u.FeatureSlotter)(ag,af,A,function(ah){return ad.measureText(ah)});this.slotters[ag]=ae}return ae.slot_features(ac)},get_mode:function(ac){if(ac.dataset_type==="summary_tree"){mode="summary_tree"}else{if(ac.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>I){mode="Squish"}else{mode="Pack"}}}return mode},get_canvas_height:function(ac,ag,ah,ad){if(ag==="summary_tree"||ag==="Coverage"){return this.summary_draw_height}else{var af=this.incremental_slots(ah,ac.data,ag);var ae=new (this.painter)(null,null,null,this.prefs,ag);return Math.max(aa,ae.get_required_height(af,ad))}},draw_tile:function(am,aq,ao,ar,af,aj,ae){var ap=this,ad=aq.canvas,ay=af.get("start"),ac=af.get("end"),ag=this.left_offset;if(ao==="summary_tree"||ao==="Coverage"){var aA=new L.SummaryTreePainter(am,ay,ac,this.prefs);aA.draw(aq,ad.width,ad.height,aj);return new j(ap,af,ar,ad,am.data,am.max)}var ai=[],an=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 an)){all_slotted=false}}}}var aB=(this.filters_manager.alpha_filter?new C(this.filters_manager.alpha_filter):null);var az=(this.filters_manager.height_filter?new C(this.filters_manager.height_filter):null);var aA=new (this.painter)(ai,ay,ac,this.prefs,ao,aB,az,ae);var aw=null;aq.fillStyle=this.prefs.block_color;aq.font=aq.canvas.manager.default_font;aq.textAlign="right";if(am.data){aw=aA.draw(aq,ad.width,ad.height,aj,an);aw.translation=-ag}return new O(ap,af,ar,ad,am.data,aj,ao,am.message,all_slotted,aw)},data_and_mode_compatible:function(ac,ad){if(ad==="Auto"){return true}else{if(ad==="Coverage"){return ac.dataset_type==="summary_tree"}else{if(ac.extra_info==="no_detail"||ac.dataset_type==="summary_tree"){return false}else{return true}}}},can_subset:function(ac){if(ac.dataset_type==="summary_tree"||ac.message||ac.extra_info==="no_detail"){return false}return true}});var R=function(ad,ac,ae){c.call(this,ad,ac,ae);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:l.get_random_color()},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}],saved_values:ae.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=L.ReadPainter};q(R.prototype,r.prototype,M.prototype,c.prototype);var T=function(ae,ad,ag){c.call(this,ae,ad,ag);var af=l.get_random_color(),ac=l.get_random_color([af,"#ffffff"]);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block and sense strand color",type:"color",default_value:af},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:ac},{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:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"Clear value to set automatically"},{key:"mode",type:"string",default_value:this.mode,hidden:true}],saved_values:ag.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=L.ReadPainter;this.update_icons()};q(T.prototype,r.prototype,M.prototype,c.prototype);var d={LineTrack:h,FeatureTrack:c,VcfTrack:R,ReadTrack:T,DiagonalHeatmapTrack:t,CompositeTrack:f,DrawableGroup:P};var p=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:Z,DrawableGroup:P,LineTrack:h,FeatureTrack:c,DiagonalHeatmapTrack:t,ReadTrack:T,VcfTrack:R,CompositeTrack:f,object_from_template:p}});
\ 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: smcmanus: The input datatype for display changed from LibraryDataset to Dataset, causing permission-checking issues. They've been fixed.
by Bitbucket 04 Nov '12
by Bitbucket 04 Nov '12
04 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/b3585fb0c986/
changeset: b3585fb0c986
user: smcmanus
date: 2012-11-04 07:49:43
summary: The input datatype for display changed from LibraryDataset to Dataset, causing permission-checking issues. They've been fixed.
affected #: 2 files
diff -r c0f9edeb7ab137bf7adf76478eccb7fc32557cef -r b3585fb0c98639a57d8934f09f4553a3ad69e80b lib/galaxy/security/__init__.py
--- a/lib/galaxy/security/__init__.py
+++ b/lib/galaxy/security/__init__.py
@@ -275,11 +275,12 @@
# then the returned permissions will not carry an entry for the dataset.
ret_permissions = {}
if ( len( permission_items ) > 0 ):
+ # SM: NB: LibraryDatasets became Datasets for some odd reason.
if ( isinstance( permission_items[0], trans.model.LibraryDataset ) ):
ids = [ item.library_dataset_id for item in permission_items ]
permissions = trans.sa_session.query( trans.model.LibraryDatasetPermissions ) \
.filter( and_( trans.model.LibraryDatasetPermissions.library_dataset_id.in_( ids ),
- trans.model.LibraryDatasetPermissions.action == action ) ) \
+ trans.model.LibraryDatasetPermissions.action == action.action ) ) \
.all()
# Massage the return data. We will return a list of permissions
@@ -292,6 +293,23 @@
ret_permissions[ item.library_dataset_id ] = []
for permission in permissions:
ret_permissions[ permission.library_dataset_id ].append( permission )
+ elif ( isinstance( permission_items[0], trans.model.Dataset ) ):
+ ids = [ item.id for item in permission_items ]
+ permissions = trans.sa_session.query( trans.model.DatasetPermissions ) \
+ .filter( and_( trans.model.DatasetPermissions.dataset_id.in_( ids ),
+ trans.model.DatasetPermissions.action == action.action ) ) \
+ .all()
+
+ # Massage the return data. We will return a list of permissions
+ # for each library dataset. So we initialize the return list to
+ # have an empty list for each dataset. Then each permission is
+ # appended to the right lib dataset.
+ # TODO: Consider eliminating the initialization and just return
+ # empty values for each library dataset id.
+ for item in permission_items:
+ ret_permissions[ item.id ] = []
+ for permission in permissions:
+ ret_permissions[ permission.dataset_id ].append( permission )
# Test that we get the same response from get_item_actions each item:
test_code = False
@@ -333,14 +351,13 @@
LDDAs.
"""
all_items_actions = self.get_actions_for_items( trans, action, items )
+ ret_allow_action = {}
- ret_allow_action = {}
# Change item to lib_dataset or vice-versa.
for item in items:
if all_items_actions.has_key( item.id ):
item_actions = all_items_actions[ item.id ]
- # For access, all of the dataset's
if self.permitted_actions.DATASET_ACCESS == action:
ret_allow_action[ item.id ] = True
for item_action in item_actions:
diff -r c0f9edeb7ab137bf7adf76478eccb7fc32557cef -r b3585fb0c98639a57d8934f09f4553a3ad69e80b lib/galaxy/webapps/galaxy/controllers/library_common.py
--- a/lib/galaxy/webapps/galaxy/controllers/library_common.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library_common.py
@@ -131,7 +131,17 @@
comptypes = get_comptypes( trans )
try:
if self.app.config.new_lib_browse:
- pass
+ return trans.fill_template( 'library/common/browse_library_opt.mako',
+ cntrller=cntrller,
+ use_panels=use_panels,
+ library=library,
+ created_ldda_ids=created_ldda_ids,
+ hidden_folder_ids=hidden_folder_ids,
+ show_deleted=show_deleted,
+ comptypes=comptypes,
+ current_user_roles=current_user_roles,
+ message=message,
+ status=status )
else:
return trans.fill_template( 'library/common/browse_library.mako',
cntrller=cntrller,
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