galaxy-commits
Threads by month
- ----- 2024 -----
- 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
December 2011
- 1 participants
- 92 discussions
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/7dd3a0891011/
changeset: 7dd3a0891011
user: greg
date: 2011-12-13 15:44:50
summary: Enhance the tool shed repository installation process so that repository tools can be loaded into the tool panel outside of any sections. Enhance the install manager to use this enhancement. Create a new shed_util module that contains common mentods used between the install manager and the admin_toolshed controller, and modify these components to import these common methods. Significant code cleanup and miscellaneous bug fixes included as well.
affected #: 5 files
diff -r 9c46a216e24c529a496a50afec13ebcb78106b96 -r 7dd3a089101138a4796eb73a5f1391d2f436723e lib/galaxy/tools/install_manager.py
--- a/lib/galaxy/tools/install_manager.py
+++ b/lib/galaxy/tools/install_manager.py
@@ -4,18 +4,7 @@
shed. Tools included in tool_shed_install.xml that have already been installed will not be
re-installed.
"""
-from galaxy import util
-from galaxy.tools import ToolSection
-from galaxy.tools.search import ToolBoxSearch
-from galaxy import model
-from galaxy.web.controllers.admin_toolshed import generate_metadata, generate_tool_panel_section, add_shed_tool_conf_entry, create_or_undelete_tool_shed_repository
-from galaxy.web.controllers.admin_toolshed import handle_missing_data_table_entry, handle_missing_index_file, handle_tool_dependencies
-from galaxy.model.orm import *
-import os, subprocess, tempfile, logging
-
-pkg_resources.require( 'elementtree' )
-from elementtree import ElementTree, ElementInclude
-from elementtree.ElementTree import Element
+from galaxy.util.shed_util import *
log = logging.getLogger( __name__ )
@@ -39,192 +28,110 @@
self.tool_shed_install_config = tool_shed_install_config
tree = util.parse_xml( tool_shed_install_config )
root = tree.getroot()
- self.tool_shed = root.get( 'name' )
+ self.tool_shed = clean_tool_shed_url( root.get( 'name' ) )
log.debug( "Repositories will be installed from tool shed '%s' into configured tool_path location '%s'" % ( str( self.tool_shed ), str( self.tool_path ) ) )
self.repository_owner = 'devteam'
for elem in root:
- if elem.tag == 'tool':
- self.check_tool( elem )
+ if elem.tag == 'repository':
+ self.install_repository( elem )
elif elem.tag == 'section':
- self.check_section( elem )
- def check_tool( self, elem ):
- # TODO: write this method.
- pass
- def check_section( self, elem ):
+ self.install_section( elem )
+ def install_repository( self, elem, section_name='', section_id='' ):
+ # Install a single repository into the tool config. If outside of any sections, the entry looks something like:
+ # <repository name="cut_wrapper" description="Galaxy wrapper for the Cut tool" changeset_revision="f3ed6cfe6402">
+ # <tool id="Cut1" version="1.0.1" />
+ # </repository>
+ name = elem.get( 'name' )
+ description = elem.get( 'description' )
+ changeset_revision = elem.get( 'changeset_revision' )
+ # Install path is of the form: <tool path>/<tool shed>/repos/<repository owner>/<repository name>/<changeset revision>
+ clone_dir = os.path.join( self.tool_path, self.tool_shed, 'repos', self.repository_owner, name, changeset_revision )
+ if self.__isinstalled( elem, clone_dir ):
+ log.debug( "Skipping automatic install of repository '%s' because it has already been installed in location '%s'" % ( name, clone_dir ) )
+ else:
+ if section_name and section_id:
+ section_key = 'section_%s' % str( section_id )
+ if section_key in self.app.toolbox.tool_panel:
+ # Appending a tool to an existing section in self.app.toolbox.tool_panel
+ log.debug( "Appending to tool panel section: %s" % section_name )
+ tool_section = self.app.toolbox.tool_panel[ section_key ]
+ else:
+ # Appending a new section to self.app.toolbox.tool_panel
+ log.debug( "Loading new tool panel section: %s" % section_name )
+ new_section_elem = Element( 'section' )
+ new_section_elem.attrib[ 'name' ] = section_name
+ new_section_elem.attrib[ 'id' ] = section_id
+ tool_section = ToolSection( new_section_elem )
+ self.app.toolbox.tool_panel[ section_key ] = tool_section
+ else:
+ tool_section = None
+ current_working_dir = os.getcwd()
+ tool_shed_url = self.__get_url_from_tool_shed( self.tool_shed )
+ repository_clone_url = os.path.join( tool_shed_url, 'repos', self.repository_owner, name )
+ relative_install_dir = os.path.join( clone_dir, name )
+ returncode, tmp_name = clone_repository( name, clone_dir, current_working_dir, repository_clone_url )
+ if returncode == 0:
+ returncode, tmp_name = update_repository( current_working_dir, relative_install_dir, changeset_revision )
+ if returncode == 0:
+ metadata_dict = load_repository_contents( self.app,
+ name,
+ description,
+ self.repository_owner,
+ changeset_revision,
+ repository_clone_url,
+ self.install_tool_config,
+ self.tool_path,
+ tool_section,
+ relative_install_dir,
+ current_working_dir,
+ tmp_name )
+ # Add a new record to the tool_id_guid_map table for each
+ # tool in the repository if one doesn't already exist.
+ if 'tools' in metadata_dict:
+ tools_mapped = 0
+ for tool_dict in metadata_dict[ 'tools' ]:
+ flush_needed = False
+ tool_id = tool_dict[ 'id' ]
+ tool_version = tool_dict[ 'version' ]
+ guid = tool_dict[ 'guid' ]
+ tool_id_guid_map = get_tool_id_guid_map( self.app, tool_id, tool_version, self.tool_shed, self.repository_owner, name )
+ if tool_id_guid_map:
+ if tool_id_guid_map.guid != guid:
+ tool_id_guid_map.guid = guid
+ flush_needed = True
+ else:
+ tool_id_guid_map = self.app.model.ToolIdGuidMap( tool_id=tool_id,
+ tool_version=tool_version,
+ tool_shed=self.tool_shed,
+ repository_owner=self.repository_owner,
+ repository_name=name,
+ guid=guid )
+ flush_needed = True
+ if flush_needed:
+ self.sa_session.add( tool_id_guid_map )
+ self.sa_session.flush()
+ tools_mapped += 1
+ log.debug( "Mapped tool ids to guids for %d tools included in repository '%s'." % ( tools_mapped, name ) )
+ else:
+ tmp_stderr = open( tmp_name, 'rb' )
+ log.debug( "Error updating repository '%s': %s" % ( name, tmp_stderr.read() ) )
+ tmp_stderr.close()
+ else:
+ tmp_stderr = open( tmp_name, 'rb' )
+ log.debug( "Error cloning repository '%s': %s" % ( name, tmp_stderr.read() ) )
+ tmp_stderr.close()
+ def install_section( self, elem ):
+ # Install 1 or more repositories into a section in the tool config. An entry looks something like:
+ # <section name="EMBOSS" id="EMBOSSLite">
+ # <repository name="emboss_5" description="Galaxy wrappers for EMBOSS version 5 tools" changeset_revision="bdd88ae5d0ac">
+ # <tool file="emboss_5/emboss_antigenic.xml" id="EMBOSS: antigenic1" version="5.0.0" />
+ # ...
+ # </repository>
+ # </section>
section_name = elem.get( 'name' )
section_id = elem.get( 'id' )
for repository_elem in elem:
- name = repository_elem.get( 'name' )
- description = repository_elem.get( 'description' )
- changeset_revision = repository_elem.get( 'changeset_revision' )
- installed = False
- for tool_elem in repository_elem:
- tool_config = tool_elem.get( 'file' )
- tool_id = tool_elem.get( 'id' )
- tool_version = tool_elem.get( 'version' )
- tigm = self.__get_tool_id_guid_map_by_id_version( tool_id, tool_version )
- if tigm:
- # A record exists in the tool_id_guid_map
- # table, so see if the tool is still installed.
- install_path = self.__generate_install_path( tigm )
- if os.path.exists( install_path ):
- message = "Skipping automatic install of repository '%s' because it has already been installed in location '%s'" % \
- ( name, install_path )
- log.debug( message )
- installed = True
- break
- if not installed:
- log.debug( "Installing repository '%s' from tool shed '%s'" % ( name, self.tool_shed ) )
- current_working_dir = os.getcwd()
- tool_shed_url = self.__get_url_from_tool_shed( self.tool_shed )
- repository_clone_url = '%s/repos/devteam/%s' % ( tool_shed_url, name )
- # Install path is of the form: <tool path><tool shed>/repos/<repository owner>/<repository name>/<changeset revision>
- clone_dir = os.path.join( self.tool_path, self.tool_shed, 'repos/devteam', name, changeset_revision )
- if not os.path.isdir( clone_dir ):
- os.makedirs( clone_dir )
- log.debug( 'Cloning %s...' % repository_clone_url )
- cmd = 'hg clone %s' % repository_clone_url
- tmp_name = tempfile.NamedTemporaryFile().name
- tmp_stderr = open( tmp_name, 'wb' )
- os.chdir( clone_dir )
- proc = subprocess.Popen( args=cmd, shell=True, stderr=tmp_stderr.fileno() )
- returncode = proc.wait()
- os.chdir( current_working_dir )
- tmp_stderr.close()
- if returncode == 0:
- # Update the cloned repository to changeset_revision. It is imperative that the
- # installed repository is updated to the desired changeset_revision before metadata
- # is set because the process for setting metadata uses the repository files on disk.
- relative_install_dir = os.path.join( clone_dir, name )
- log.debug( 'Updating cloned repository to revision "%s"' % changeset_revision )
- cmd = 'hg update -r %s' % changeset_revision
- tmp_name = tempfile.NamedTemporaryFile().name
- tmp_stderr = open( tmp_name, 'wb' )
- os.chdir( relative_install_dir )
- proc = subprocess.Popen( cmd, shell=True, stderr=tmp_stderr.fileno() )
- returncode = proc.wait()
- os.chdir( current_working_dir )
- tmp_stderr.close()
- if returncode == 0:
- # Generate the metadata for the installed tool shed repository. It is imperative that
- # the installed repository is updated to the desired changeset_revision before metadata
- # is set because the process for setting metadata uses the repository files on disk.
- metadata_dict = generate_metadata( self.app.toolbox, relative_install_dir, repository_clone_url )
- if 'datatypes_config' in metadata_dict:
- datatypes_config = os.path.abspath( metadata_dict[ 'datatypes_config' ] )
- # Load data types required by tools.
- self.__load_datatypes( trans, datatypes_config, relative_install_dir )
- if 'tools' in metadata_dict:
- repository_tools_tups = []
- for tool_dict in metadata_dict[ 'tools' ]:
- relative_path = tool_dict[ 'tool_config' ]
- guid = tool_dict[ 'guid' ]
- tool = self.app.toolbox.load_tool( os.path.abspath( relative_path ) )
- repository_tools_tups.append( ( relative_path, guid, tool ) )
- if repository_tools_tups:
- sample_files = metadata_dict.get( 'sample_files', [] )
- # Handle missing data table entries for tool parameters that are dynamically generated select lists.
- repository_tools_tups = handle_missing_data_table_entry( self.app, self.tool_path, sample_files, repository_tools_tups )
- # Handle missing index files for tool parameters that are dynamically generated select lists.
- repository_tools_tups = handle_missing_index_file( self.app, self.tool_path, sample_files, repository_tools_tups )
- # Handle tools that use fabric scripts to install dependencies.
- handle_tool_dependencies( current_working_dir, relative_install_dir, repository_tools_tups )
- section_key = 'section_%s' % str( section_id )
- if section_key in self.app.toolbox.tool_panel:
- # Appending a tool to an existing section in self.app.toolbox.tool_panel
- log.debug( "Appending to tool panel section: %s" % section_name )
- tool_section = self.app.toolbox.tool_panel[ section_key ]
- else:
- # Appending a new section to self.app.toolbox.tool_panel
- log.debug( "Loading new tool panel section: %s" % section_name )
- elem = Element( 'section' )
- elem.attrib[ 'name' ] = section_name
- elem.attrib[ 'id' ] = section_id
- tool_section = ToolSection( elem )
- self.app.toolbox.tool_panel[ section_key ] = tool_section
- # Generate an in-memory tool conf section that includes the new tools.
- new_tool_section = generate_tool_panel_section( name,
- repository_clone_url,
- changeset_revision,
- tool_section,
- repository_tools_tups,
- owner=self.repository_owner )
- # Create a temporary file to persist the in-memory tool section
- # TODO: Figure out how to do this in-memory using xml.etree.
- tmp_name = tempfile.NamedTemporaryFile().name
- persisted_new_tool_section = open( tmp_name, 'wb' )
- persisted_new_tool_section.write( new_tool_section )
- persisted_new_tool_section.close()
- # Parse the persisted tool panel section
- tree = util.parse_xml( tmp_name )
- root = tree.getroot()
- # Load the tools in the section into the tool panel.
- self.app.toolbox.load_section_tag_set( root, self.app.toolbox.tool_panel, self.tool_path )
- # Remove the temporary file
- try:
- os.unlink( tmp_name )
- except:
- pass
- # Append the new section to the shed_tool_config file.
- add_shed_tool_conf_entry( self.app, self.install_tool_config, new_tool_section )
- if self.app.toolbox_search.enabled:
- # If search support for tools is enabled, index the new installed tools.
- self.app.toolbox_search = ToolBoxSearch( self.app.toolbox )
- # Add a new record to the tool_shed_repository table if one doesn't
- # already exist. If one exists but is marked deleted, undelete it.
- log.debug( "Adding new row to tool_shed_repository table for repository '%s'" % name )
- create_or_undelete_tool_shed_repository( self.app,
- name,
- description,
- changeset_revision,
- repository_clone_url,
- metadata_dict,
- owner=self.repository_owner )
- # Add a new record to the tool_id_guid_map table for each
- # tool in the repository if one doesn't already exist.
- if 'tools' in metadata_dict:
- tools_mapped = 0
- for tool_dict in metadata_dict[ 'tools' ]:
- tool_id = tool_dict[ 'id' ]
- tool_version = tool_dict[ 'version' ]
- guid = tool_dict[ 'guid' ]
- tool_id_guid_map = model.ToolIdGuidMap( tool_id=tool_id,
- tool_version=tool_version,
- tool_shed=self.tool_shed,
- repository_owner=self.repository_owner,
- repository_name=name,
- guid=guid )
- self.sa_session.add( tool_id_guid_map )
- self.sa_session.flush()
- tools_mapped += 1
- log.debug( "Mapped tool ids to guids for %d tools included in repository '%s'." % ( tools_mapped, name ) )
- def __generate_install_path( self, tool_id_guid_map ):
- """
- Generate a tool path in which a tool is or will be installed. The tool path will be of the form:
- <tool shed>/repos/<repository owner>/<repository name>/<changeset revision>
- """
- tool_shed = tool_id_guid_map.tool_shed
- repository_name = tool_id_guid_map.repository_name
- tool_shed_repository = self.__get_repository_by_tool_shed_name_owner( tool_shed, repository_name, self.repository_owner )
- changeset_revision = tool_shed_repository.changeset_revision
- return '%s/repos%s/%s/%s/%s' % ( tool_shed, self.repository_owner, repository_name, changeset_revision )
- def __get_repository_by_tool_shed_name_owner( tool_shed, name, owner ):
- """Get a repository from the database via tool_shed, name and owner."""
- # CRITICAL: this assumes that a single changeset_revision exists for each repository
- # in the tool shed. In other words, if a repository has multiple changset_revisions
- # there will be problems. We're probably safe here because only a single changeset_revision
- # for each tool shed repository will be installed using this installation process.
- return self.sa_session.query( self.app.model.ToolShedRepository ) \
- .filter( and_( self.app.model.ToolShedRepository.table.c.tool_shed == tool_shed,
- self.app.model.ToolShedRepository.table.c.name == name,
- self.app.model.ToolShedRepository.table.c.owner == owner ) ) \
- .first()
- def __get_tool_id_guid_map_by_id_version( self, tool_id, tool_version ):
- """Get a tool_id_guid_map from the database via tool_id and tool_version."""
- return self.sa_session.query( self.app.model.ToolIdGuidMap ) \
- .filter( and_( self.app.model.ToolIdGuidMap.table.c.tool_id == tool_id,
- self.app.model.ToolIdGuidMap.table.c.tool_version == tool_version ) ) \
- .first()
+ self.install_repository( repository_elem, section_name=section_name, section_id=section_id )
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:
@@ -237,3 +144,17 @@
# The tool shed from which the repository was originally
# installed must no longer be configured in tool_sheds_conf.xml.
return None
+ def __isinstalled( self, repository_elem, clone_dir ):
+ name = repository_elem.get( 'name' )
+ installed = False
+ for tool_elem in repository_elem:
+ tool_config = tool_elem.get( 'file' )
+ tool_id = tool_elem.get( 'id' )
+ tool_version = tool_elem.get( 'version' )
+ tigm = get_tool_id_guid_map( self.app, tool_id, tool_version, self.tool_shed, self.repository_owner, name )
+ if tigm:
+ # A record exists in the tool_id_guid_map table, so see if the repository is installed.
+ if os.path.exists( clone_dir ):
+ installed = True
+ break
+ return installed
diff -r 9c46a216e24c529a496a50afec13ebcb78106b96 -r 7dd3a089101138a4796eb73a5f1391d2f436723e lib/galaxy/util/shed_util.py
--- /dev/null
+++ b/lib/galaxy/util/shed_util.py
@@ -0,0 +1,548 @@
+import os, tempfile, shutil, subprocess, logging
+from datetime import date, datetime, timedelta
+from time import strftime
+from galaxy import util
+from galaxy.tools import ToolSection
+from galaxy.tools.search import ToolBoxSearch
+from galaxy.model.orm import *
+
+pkg_resources.require( 'elementtree' )
+from elementtree import ElementTree, ElementInclude
+from elementtree.ElementTree import Element, SubElement, tostring
+
+log = logging.getLogger( __name__ )
+
+def add_shed_tool_conf_entry( app, shed_tool_conf, tool_panel_entry ):
+ """
+ Add an entry in the shed_tool_conf file. An entry looks something like:
+ <section name="Filter and Sort" id="filter">
+ <tool file="filter/filtering.xml" guid="toolshed.g2.bx.psu.edu/repos/test/filter/1.0.2"/>
+ </section>
+ This method is used by the InstallManager, which does not have access to trans.
+ """
+ # Make a backup of the hgweb.config file since we're going to be changing it.
+ if not os.path.exists( shed_tool_conf ):
+ output = open( shed_tool_conf, 'w' )
+ output.write( '<?xml version="1.0"?>\n' )
+ output.write( '<toolbox tool_path="%s">\n' % tool_path )
+ output.write( '</toolbox>\n' )
+ output.close()
+ # Make a backup of the shed_tool_conf file.
+ today = date.today()
+ backup_date = today.strftime( "%Y_%m_%d" )
+ shed_tool_conf_copy = '%s/%s_%s_backup' % ( app.config.root, shed_tool_conf, backup_date )
+ shutil.copy( os.path.abspath( shed_tool_conf ), os.path.abspath( shed_tool_conf_copy ) )
+ tmp_fd, tmp_fname = tempfile.mkstemp()
+ new_shed_tool_conf = open( tmp_fname, 'wb' )
+ for i, line in enumerate( open( shed_tool_conf ) ):
+ if line.startswith( '</toolbox>' ):
+ # We're at the end of the original config file, so add our entry.
+ new_shed_tool_conf.write( ' ' )
+ new_shed_tool_conf.write( tostring( pretty_print_xml( tool_panel_entry ) ) )
+ new_shed_tool_conf.write( line )
+ else:
+ new_shed_tool_conf.write( line )
+ new_shed_tool_conf.close()
+ shutil.move( tmp_fname, os.path.abspath( shed_tool_conf ) )
+def clean_repository_clone_url( repository_clone_url ):
+ if repository_clone_url.find( '@' ) > 0:
+ # We have an url that includes an authenticated user, something like:
+ # http://test@bx.psu.edu:9009/repos/some_username/column
+ items = repository_clone_url.split( '@' )
+ tmp_url = items[ 1 ]
+ elif repository_clone_url.find( '//' ) > 0:
+ # We have an url that includes only a protocol, something like:
+ # http://bx.psu.edu:9009/repos/some_username/column
+ items = repository_clone_url.split( '//' )
+ tmp_url = items[ 1 ]
+ else:
+ tmp_url = repository_clone_url
+ return tmp_url
+def clean_tool_shed_url( tool_shed_url ):
+ if tool_shed_url.find( ':' ) > 0:
+ # Eliminate the port, if any, since it will result in an invalid directory name.
+ return tool_shed_url.split( ':' )[ 0 ]
+ return tool_shed_url.rstrip( '/' )
+def clone_repository( name, clone_dir, current_working_dir, repository_clone_url ):
+ log.debug( "Installing repository '%s'" % name )
+ os.makedirs( clone_dir )
+ log.debug( 'Cloning %s' % repository_clone_url )
+ cmd = 'hg clone %s' % repository_clone_url
+ tmp_name = tempfile.NamedTemporaryFile().name
+ tmp_stderr = open( tmp_name, 'wb' )
+ os.chdir( clone_dir )
+ proc = subprocess.Popen( args=cmd, shell=True, stderr=tmp_stderr.fileno() )
+ returncode = proc.wait()
+ os.chdir( current_working_dir )
+ tmp_stderr.close()
+ return returncode, tmp_name
+def create_or_undelete_tool_shed_repository( app, name, description, changeset_revision, repository_clone_url, metadata_dict, owner='' ):
+ # This method is used by the InstallManager, which does not have access to trans.
+ sa_session = app.model.context.current
+ tmp_url = clean_repository_clone_url( repository_clone_url )
+ tool_shed = tmp_url.split( 'repos' )[ 0 ].rstrip( '/' )
+ if not owner:
+ owner = get_repository_owner( tmp_url )
+ includes_datatypes = 'datatypes_config' in metadata_dict
+ flush_needed = False
+ tool_shed_repository = get_repository_by_shed_name_owner_changeset_revision( app, tool_shed, name, owner, changeset_revision )
+ if tool_shed_repository:
+ if tool_shed_repository.deleted:
+ tool_shed_repository.deleted = False
+ # Reset includes_datatypes in case metadata changed since last installed.
+ tool_shed_repository.includes_datatypes = includes_datatypes
+ flush_needed = True
+ else:
+ tool_shed_repository = app.model.ToolShedRepository( tool_shed=tool_shed,
+ name=name,
+ description=description,
+ owner=owner,
+ changeset_revision=changeset_revision,
+ metadata=metadata_dict,
+ includes_datatypes=includes_datatypes )
+ flush_needed = True
+ if flush_needed:
+ sa_session.add( tool_shed_repository )
+ sa_session.flush()
+def generate_datatypes_metadata( datatypes_config, metadata_dict ):
+ """
+ Update the received metadata_dict with changes that have been applied
+ to the received datatypes_config. This method is used by the InstallManager,
+ which does not have access to trans.
+ """
+ # Parse datatypes_config.
+ tree = ElementTree.parse( datatypes_config )
+ root = tree.getroot()
+ ElementInclude.include( root )
+ repository_datatype_code_files = []
+ datatype_files = root.find( 'datatype_files' )
+ if datatype_files:
+ for elem in datatype_files.findall( 'datatype_file' ):
+ name = elem.get( 'name', None )
+ repository_datatype_code_files.append( name )
+ metadata_dict[ 'datatype_files' ] = repository_datatype_code_files
+ datatypes = []
+ registration = root.find( 'registration' )
+ if registration:
+ for elem in registration.findall( 'datatype' ):
+ extension = elem.get( 'extension', None )
+ dtype = elem.get( 'type', None )
+ mimetype = elem.get( 'mimetype', None )
+ datatypes.append( dict( extension=extension,
+ dtype=dtype,
+ mimetype=mimetype ) )
+ metadata_dict[ 'datatypes' ] = datatypes
+ return metadata_dict
+def generate_metadata( toolbox, relative_install_dir, repository_clone_url ):
+ """
+ Browse the repository files on disk to generate metadata. Since we are using disk files, it
+ is imperative that the repository is updated to the desired change set revision before metadata
+ is generated. This method is used by the InstallManager, which does not have access to trans.
+ """
+ metadata_dict = {}
+ sample_files = []
+ datatypes_config = None
+ # Find datatypes_conf.xml if it exists.
+ for root, dirs, files in os.walk( relative_install_dir ):
+ if root.find( '.hg' ) < 0:
+ for name in files:
+ if name == 'datatypes_conf.xml':
+ relative_path = os.path.join( root, name )
+ datatypes_config = os.path.abspath( relative_path )
+ break
+ if datatypes_config:
+ metadata_dict[ 'datatypes_config' ] = relative_path
+ metadata_dict = generate_datatypes_metadata( datatypes_config, metadata_dict )
+ # Find all special .sample files.
+ for root, dirs, files in os.walk( relative_install_dir ):
+ if root.find( '.hg' ) < 0:
+ for name in files:
+ if name.endswith( '.sample' ):
+ sample_files.append( os.path.join( root, name ) )
+ if sample_files:
+ metadata_dict[ 'sample_files' ] = sample_files
+ # Find all tool configs and exported workflows.
+ for root, dirs, files in os.walk( relative_install_dir ):
+ if root.find( '.hg' ) < 0 and root.find( 'hgrc' ) < 0:
+ if '.hg' in dirs:
+ dirs.remove( '.hg' )
+ for name in files:
+ # Find all tool configs.
+ if name != 'datatypes_conf.xml' and name.endswith( '.xml' ):
+ full_path = os.path.abspath( os.path.join( root, name ) )
+ try:
+ tool = toolbox.load_tool( full_path )
+ except Exception, e:
+ tool = None
+ if tool is not None:
+ tool_config = os.path.join( root, name )
+ metadata_dict = generate_tool_metadata( tool_config, tool, repository_clone_url, metadata_dict )
+ # Find all exported workflows
+ elif name.endswith( '.ga' ):
+ relative_path = os.path.join( root, name )
+ fp = open( relative_path, 'rb' )
+ workflow_text = fp.read()
+ fp.close()
+ exported_workflow_dict = from_json_string( workflow_text )
+ if 'a_galaxy_workflow' in exported_workflow_dict and exported_workflow_dict[ 'a_galaxy_workflow' ] == 'true':
+ metadata_dict = generate_workflow_metadata( relative_path, exported_workflow_dict, metadata_dict )
+ return metadata_dict
+def generate_tool_guid( repository_clone_url, tool ):
+ """
+ Generate a guid for the installed tool. It is critical that this guid matches the guid for
+ the tool in the Galaxy tool shed from which it is being installed. The form of the guid is
+ <tool shed host>/repos/<repository owner>/<repository name>/<tool id>/<tool version>
+ """
+ tmp_url = clean_repository_clone_url( repository_clone_url )
+ return '%s/%s/%s' % ( tmp_url, tool.id, tool.version )
+def generate_tool_metadata( tool_config, tool, repository_clone_url, metadata_dict ):
+ """
+ Update the received metadata_dict with changes that have been
+ applied to the received tool. This method is used by the InstallManager,
+ which does not have access to trans.
+ """
+ # Generate the guid
+ guid = generate_tool_guid( repository_clone_url, tool )
+ # Handle tool.requirements.
+ tool_requirements = []
+ for tr in tool.requirements:
+ name=tr.name
+ type=tr.type
+ if type == 'fabfile':
+ version = None
+ fabfile = tr.fabfile
+ method = tr.method
+ else:
+ version = tr.version
+ fabfile = None
+ method = None
+ requirement_dict = dict( name=name,
+ type=type,
+ version=version,
+ fabfile=fabfile,
+ method=method )
+ tool_requirements.append( requirement_dict )
+ # Handle tool.tests.
+ tool_tests = []
+ if tool.tests:
+ for ttb in tool.tests:
+ test_dict = dict( name=ttb.name,
+ required_files=ttb.required_files,
+ inputs=ttb.inputs,
+ outputs=ttb.outputs )
+ tool_tests.append( test_dict )
+ tool_dict = dict( id=tool.id,
+ guid=guid,
+ name=tool.name,
+ version=tool.version,
+ description=tool.description,
+ version_string_cmd = tool.version_string_cmd,
+ tool_config=tool_config,
+ requirements=tool_requirements,
+ tests=tool_tests )
+ if 'tools' in metadata_dict:
+ metadata_dict[ 'tools' ].append( tool_dict )
+ else:
+ metadata_dict[ 'tools' ] = [ tool_dict ]
+ return metadata_dict
+def generate_tool_panel_elem_list( repository_name, repository_clone_url, changeset_revision, repository_tools_tups, tool_section=None, owner='' ):
+ """Generate a list of ElementTree Element objects for each section or list of tools."""
+ elem_list = []
+ tmp_url = clean_repository_clone_url( repository_clone_url )
+ if not owner:
+ owner = get_repository_owner( tmp_url )
+ if tool_section:
+ root_elem = Element( 'section' )
+ root_elem.attrib[ 'name' ] = tool_section.name
+ root_elem.attrib[ 'id' ] = tool_section.id
+ for repository_tool_tup in repository_tools_tups:
+ tool_file_path, guid, tool = repository_tool_tup
+ if tool_section:
+ tool_elem = SubElement( root_elem, 'tool' )
+ else:
+ tool_elem = Element( 'tool' )
+ tool_elem.attrib[ 'file' ] = tool_file_path
+ tool_elem.attrib[ 'guid' ] = guid
+ tool_shed_elem = SubElement( tool_elem, 'tool_shed' )
+ tool_shed_elem.text = tmp_url.split( 'repos' )[ 0 ].rstrip( '/' )
+ repository_name_elem = SubElement( tool_elem, 'repository_name' )
+ repository_name_elem.text = repository_name
+ repository_owner_elem = SubElement( tool_elem, 'repository_owner' )
+ repository_owner_elem.text = owner
+ changeset_revision_elem = SubElement( tool_elem, 'changeset_revision' )
+ changeset_revision_elem.text = changeset_revision
+ id_elem = SubElement( tool_elem, 'id' )
+ id_elem.text = tool.id
+ version_elem = SubElement( tool_elem, 'version' )
+ version_elem.text = tool.version
+ if tool_section:
+ elem_list.append( root_elem )
+ else:
+ elem_list.append( tool_elem )
+ return elem_list
+def generate_workflow_metadata( relative_path, exported_workflow_dict, metadata_dict ):
+ """
+ Update the received metadata_dict with changes that have been applied
+ to the received exported_workflow_dict. Store everything in the database.
+ This method is used by the InstallManager, which does not have access to trans.
+ """
+ if 'workflows' in metadata_dict:
+ metadata_dict[ 'workflows' ].append( ( relative_path, exported_workflow_dict ) )
+ else:
+ metadata_dict[ 'workflows' ] = [ ( relative_path, exported_workflow_dict ) ]
+ return metadata_dict
+def get_repository_by_shed_name_owner_changeset_revision( app, tool_shed, name, owner, changeset_revision ):
+ # This method is used by the InstallManager, which does not have access to trans.
+ sa_session = app.model.context.current
+ if tool_shed.find( '//' ) > 0:
+ tool_shed = tool_shed.split( '//' )[1]
+ return sa_session.query( app.model.ToolShedRepository ) \
+ .filter( and_( app.model.ToolShedRepository.table.c.tool_shed == tool_shed,
+ app.model.ToolShedRepository.table.c.name == name,
+ app.model.ToolShedRepository.table.c.owner == owner,
+ app.model.ToolShedRepository.table.c.changeset_revision == changeset_revision ) ) \
+ .first()
+def get_repository_owner( cleaned_repository_url ):
+ items = cleaned_repository_url.split( 'repos' )
+ repo_path = items[ 1 ]
+ if repo_path.startswith( '/' ):
+ repo_path = repo_path.replace( '/', '', 1 )
+ return repo_path.lstrip( '/' ).split( '/' )[ 0 ]
+def get_tool_id_guid_map( app, tool_id, version, tool_shed, repository_owner, repository_name ):
+ # This method is used by the InstallManager, which does not have access to trans.
+ sa_session = app.model.context.current
+ return sa_session.query( app.model.ToolIdGuidMap ) \
+ .filter( and_( app.model.ToolIdGuidMap.table.c.tool_id == tool_id,
+ app.model.ToolIdGuidMap.table.c.tool_version == version,
+ app.model.ToolIdGuidMap.table.c.tool_shed == tool_shed,
+ app.model.ToolIdGuidMap.table.c.repository_owner == repository_owner,
+ app.model.ToolIdGuidMap.table.c.repository_name == repository_name ) ) \
+ .first()
+def handle_missing_data_table_entry( app, tool_path, sample_files, repository_tools_tups ):
+ """
+ Inspect each tool to see if any have input parameters that are dynamically
+ generated select lists that require entries in the tool_data_table_conf.xml file.
+ This method is used by the InstallManager, which does not have access to trans.
+ """
+ missing_data_table_entry = False
+ for index, repository_tools_tup in enumerate( repository_tools_tups ):
+ tup_path, guid, repository_tool = repository_tools_tup
+ if repository_tool.params_with_missing_data_table_entry:
+ missing_data_table_entry = True
+ break
+ if missing_data_table_entry:
+ # The repository must contain a tool_data_table_conf.xml.sample file that includes
+ # all required entries for all tools in the repository.
+ for sample_file in sample_files:
+ head, tail = os.path.split( sample_file )
+ if tail == 'tool_data_table_conf.xml.sample':
+ break
+ error, correction_msg = handle_sample_tool_data_table_conf_file( app, sample_file )
+ if error:
+ # TODO: Do more here than logging an exception.
+ log.debug( exception_msg )
+ # Reload the tool into the local list of repository_tools_tups.
+ repository_tool = app.toolbox.load_tool( os.path.join( tool_path, tup_path ) )
+ repository_tools_tups[ index ] = ( tup_path, repository_tool )
+ return repository_tools_tups
+def handle_missing_index_file( app, tool_path, sample_files, repository_tools_tups ):
+ """
+ Inspect each tool to see if it has any input parameters that
+ are dynamically generated select lists that depend on a .loc file.
+ This method is used by the InstallManager, which does not have access to trans.
+ """
+ missing_files_handled = []
+ for index, repository_tools_tup in enumerate( repository_tools_tups ):
+ tup_path, guid, repository_tool = repository_tools_tup
+ params_with_missing_index_file = repository_tool.params_with_missing_index_file
+ for param in params_with_missing_index_file:
+ options = param.options
+ missing_head, missing_tail = os.path.split( options.missing_index_file )
+ if missing_tail not in missing_files_handled:
+ # The repository must contain the required xxx.loc.sample file.
+ for sample_file in sample_files:
+ sample_head, sample_tail = os.path.split( sample_file )
+ if sample_tail == '%s.sample' % missing_tail:
+ copy_sample_loc_file( app, sample_file )
+ if options.tool_data_table and options.tool_data_table.missing_index_file:
+ options.tool_data_table.handle_found_index_file( options.missing_index_file )
+ missing_files_handled.append( missing_tail )
+ break
+ # Reload the tool into the local list of repository_tools_tups.
+ repository_tool = app.toolbox.load_tool( os.path.join( tool_path, tup_path ) )
+ repository_tools_tups[ index ] = ( tup_path, guid, repository_tool )
+ return repository_tools_tups
+def handle_tool_dependencies( current_working_dir, repo_files_dir, repository_tools_tups ):
+ """
+ Inspect each tool to see if it includes a "requirement" that refers to a fabric
+ script. For those that do, execute the fabric script to install tool dependencies.
+ This method is used by the InstallManager, which does not have access to trans.
+ """
+ for index, repository_tools_tup in enumerate( repository_tools_tups ):
+ tup_path, guid, repository_tool = repository_tools_tup
+ for requirement in repository_tool.requirements:
+ if requirement.type == 'fabfile':
+ log.debug( 'Executing fabric script to install dependencies for tool "%s"...' % repository_tool.name )
+ fabfile = requirement.fabfile
+ method = requirement.method
+ # Find the relative path to the fabfile.
+ relative_fabfile_path = None
+ for root, dirs, files in os.walk( repo_files_dir ):
+ for name in files:
+ if name == fabfile:
+ relative_fabfile_path = os.path.join( root, name )
+ break
+ if relative_fabfile_path:
+ # cmd will look something like: fab -f fabfile.py install_bowtie
+ cmd = 'fab -f %s %s' % ( relative_fabfile_path, method )
+ tmp_name = tempfile.NamedTemporaryFile().name
+ tmp_stderr = open( tmp_name, 'wb' )
+ os.chdir( repo_files_dir )
+ proc = subprocess.Popen( cmd, shell=True, stderr=tmp_stderr.fileno() )
+ returncode = proc.wait()
+ os.chdir( current_working_dir )
+ tmp_stderr.close()
+ if returncode != 0:
+ # TODO: do something more here than logging the problem.
+ tmp_stderr = open( tmp_name, 'rb' )
+ error = tmp_stderr.read()
+ tmp_stderr.close()
+ log.debug( 'Problem installing dependencies for tool "%s"\n%s' % ( repository_tool.name, error ) )
+def load_datatypes( app, datatypes_config, relative_intall_dir ):
+ # This method is used by the InstallManager, which does not have access to trans.
+ imported_module = None
+ # Parse datatypes_config.
+ tree = parse_xml( datatypes_config )
+ datatypes_config_root = tree.getroot()
+ relative_path_to_datatype_file_name = None
+ datatype_files = datatypes_config_root.find( 'datatype_files' )
+ # Currently only a single datatype_file is supported. For example:
+ # <datatype_files>
+ # <datatype_file name="gmap.py"/>
+ # </datatype_files>
+ for elem in datatype_files.findall( 'datatype_file' ):
+ datatype_file_name = elem.get( 'name', None )
+ if datatype_file_name:
+ # Find the file in the installed repository.
+ for root, dirs, files in os.walk( relative_intall_dir ):
+ if root.find( '.hg' ) < 0:
+ for name in files:
+ if name == datatype_file_name:
+ relative_path_to_datatype_file_name = os.path.join( root, name )
+ break
+ break
+ if relative_path_to_datatype_file_name:
+ relative_head, relative_tail = os.path.split( relative_path_to_datatype_file_name )
+ registration = datatypes_config_root.find( 'registration' )
+ # Get the module by parsing the <datatype> tag.
+ for elem in registration.findall( 'datatype' ):
+ # A 'type' attribute is currently required. The attribute
+ # should be something like: type="gmap:GmapDB".
+ dtype = elem.get( 'type', None )
+ if dtype:
+ fields = dtype.split( ':' )
+ datatype_module = fields[0]
+ datatype_class_name = fields[1]
+ # Since we currently support only a single datatype_file,
+ # we have what we need.
+ break
+ try:
+ sys.path.insert( 0, relative_head )
+ imported_module = __import__( datatype_module )
+ sys.path.pop( 0 )
+ except Exception, e:
+ log.debug( "Exception importing datatypes code file included in installed repository: %s" % str( e ) )
+ app.datatypes_registry.load_datatypes( root_dir=app.config.root, config=datatypes_config, imported_module=imported_module )
+def load_repository_contents( app, name, description, owner, changeset_revision, repository_clone_url, shed_tool_conf,
+ tool_path, tool_section, relative_install_dir, current_working_dir, tmp_name ):
+ # This method is used by the InstallManager, which does not have access to trans.
+ # Generate the metadata for the installed tool shed repository. It is imperative that
+ # the installed repository is updated to the desired changeset_revision before metadata
+ # is set because the process for setting metadata uses the repository files on disk.
+ metadata_dict = generate_metadata( app.toolbox, relative_install_dir, repository_clone_url )
+ if 'datatypes_config' in metadata_dict:
+ datatypes_config = os.path.abspath( metadata_dict[ 'datatypes_config' ] )
+ # Load data types required by tools.
+ load_datatypes( app, datatypes_config, relative_install_dir )
+ if 'tools' in metadata_dict:
+ repository_tools_tups = []
+ for tool_dict in metadata_dict[ 'tools' ]:
+ relative_path = tool_dict[ 'tool_config' ]
+ guid = tool_dict[ 'guid' ]
+ tool = app.toolbox.load_tool( os.path.abspath( relative_path ) )
+ repository_tools_tups.append( ( relative_path, guid, tool ) )
+ if repository_tools_tups:
+ sample_files = metadata_dict.get( 'sample_files', [] )
+ # Handle missing data table entries for tool parameters that are dynamically generated select lists.
+ repository_tools_tups = handle_missing_data_table_entry( app, tool_path, sample_files, repository_tools_tups )
+ # Handle missing index files for tool parameters that are dynamically generated select lists.
+ repository_tools_tups = handle_missing_index_file( app, tool_path, sample_files, repository_tools_tups )
+ # Handle tools that use fabric scripts to install dependencies.
+ handle_tool_dependencies( current_working_dir, relative_install_dir, repository_tools_tups )
+ # Generate a new entry for the tool config.
+ elem_list = generate_tool_panel_elem_list( name,
+ repository_clone_url,
+ changeset_revision,
+ repository_tools_tups,
+ tool_section=tool_section,
+ owner=owner )
+ if tool_section:
+ for section_elem in elem_list:
+ # Load the section into the tool panel.
+ app.toolbox.load_section_tag_set( section_elem, app.toolbox.tool_panel, tool_path )
+ else:
+ # Load the tools into the tool panel outside of any sections.
+ for tool_elem in elem_list:
+ guid = tool_elem.get( 'guid' )
+ app.toolbox.load_tool_tag_set( tool_elem, app.toolbox.tool_panel, tool_path=tool_path, guid=guid )
+ # Remove the temporary file
+ try:
+ os.unlink( tmp_name )
+ except:
+ pass
+ for elem_entry in elem_list:
+ # Append the new entry (either section or list of tools) to the shed_tool_config file.
+ add_shed_tool_conf_entry( app, shed_tool_conf, elem_entry )
+ if app.toolbox_search.enabled:
+ # If search support for tools is enabled, index the new installed tools.
+ app.toolbox_search = ToolBoxSearch( app.toolbox )
+ # Add a new record to the tool_shed_repository table if one doesn't
+ # already exist. If one exists but is marked deleted, undelete it.
+ log.debug( "Adding new row to tool_shed_repository table for repository '%s'" % name )
+ create_or_undelete_tool_shed_repository( app,
+ name,
+ description,
+ changeset_revision,
+ repository_clone_url,
+ metadata_dict )
+ return metadata_dict
+def pretty_print_xml( elem, level=0 ):
+ pad = ' '
+ i = "\n" + level * pad
+ if len( elem ):
+ if not elem.text or not elem.text.strip():
+ elem.text = i + pad + pad
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ for e in elem:
+ pretty_print_xml( e, level + 1 )
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ else:
+ if level and ( not elem.tail or not elem.tail.strip() ):
+ elem.tail = i + pad
+ return elem
+def update_repository( current_working_dir, relative_install_dir, changeset_revision ):
+ # Update the cloned repository to changeset_revision. It is imperative that the
+ # installed repository is updated to the desired changeset_revision before metadata
+ # is set because the process for setting metadata uses the repository files on disk.
+ log.debug( 'Updating cloned repository to revision "%s"' % changeset_revision )
+ cmd = 'hg update -r %s' % changeset_revision
+ tmp_name = tempfile.NamedTemporaryFile().name
+ tmp_stderr = open( tmp_name, 'wb' )
+ os.chdir( relative_install_dir )
+ proc = subprocess.Popen( cmd, shell=True, stderr=tmp_stderr.fileno() )
+ returncode = proc.wait()
+ os.chdir( current_working_dir )
+ tmp_stderr.close()
+ return returncode, tmp_name
diff -r 9c46a216e24c529a496a50afec13ebcb78106b96 -r 7dd3a089101138a4796eb73a5f1391d2f436723e lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -24,7 +24,7 @@
pkg_resources.require( 'elementtree' )
from elementtree import ElementTree, ElementInclude
-from elementtree.ElementTree import Element
+from elementtree.ElementTree import Element, SubElement, tostring
log = logging.getLogger( __name__ )
diff -r 9c46a216e24c529a496a50afec13ebcb78106b96 -r 7dd3a089101138a4796eb73a5f1391d2f436723e lib/galaxy/web/controllers/admin_toolshed.py
--- a/lib/galaxy/web/controllers/admin_toolshed.py
+++ b/lib/galaxy/web/controllers/admin_toolshed.py
@@ -1,5 +1,5 @@
from galaxy.web.controllers.admin import *
-import logging
+from galaxy.util.shed_util import *
log = logging.getLogger( __name__ )
@@ -159,131 +159,68 @@
else:
section_key = 'section_%s' % tool_panel_section
tool_section = trans.app.toolbox.tool_panel[ section_key ]
- # Decode the encoded repo_info_dict param value.
- repo_info_dict = tool_shed_decode( repo_info_dict )
- # Clone the repository to the configured location.
- current_working_dir = os.getcwd()
- installed_repository_names = []
- for name, repo_info_tuple in repo_info_dict.items():
- metadata_dict = None
- description, repository_clone_url, changeset_revision = repo_info_tuple
- clone_dir = os.path.join( tool_path, self.__generate_tool_path( repository_clone_url, changeset_revision ) )
- if os.path.exists( clone_dir ):
- # Repository and revision has already been cloned.
- # TODO: implement the ability to re-install or revert an existing repository.
- message += 'Revision <b>%s</b> of repository <b>%s</b> was previously installed.<br/>' % ( changeset_revision, name )
- else:
- os.makedirs( clone_dir )
- log.debug( 'Cloning %s...' % repository_clone_url )
- cmd = 'hg clone %s' % repository_clone_url
- tmp_name = tempfile.NamedTemporaryFile().name
- tmp_stderr = open( tmp_name, 'wb' )
- os.chdir( clone_dir )
- proc = subprocess.Popen( args=cmd, shell=True, stderr=tmp_stderr.fileno() )
- returncode = proc.wait()
- os.chdir( current_working_dir )
- tmp_stderr.close()
+ else:
+ tool_section = None
+ # Decode the encoded repo_info_dict param value.
+ repo_info_dict = tool_shed_decode( repo_info_dict )
+ # Clone the repository to the configured location.
+ current_working_dir = os.getcwd()
+ installed_repository_names = []
+ for name, repo_info_tuple in repo_info_dict.items():
+ description, repository_clone_url, changeset_revision = repo_info_tuple
+ clone_dir = os.path.join( tool_path, self.__generate_tool_path( repository_clone_url, changeset_revision ) )
+ relative_install_dir = os.path.join( clone_dir, name )
+ if os.path.exists( clone_dir ):
+ # Repository and revision has already been cloned.
+ # TODO: implement the ability to re-install or revert an existing repository.
+ message += 'Revision <b>%s</b> of repository <b>%s</b> was previously installed.<br/>' % ( changeset_revision, name )
+ else:
+ returncode, tmp_name = clone_repository( name, clone_dir, current_working_dir, repository_clone_url )
+ if returncode == 0:
+ returncode, tmp_name = update_repository( current_working_dir, relative_install_dir, changeset_revision )
if returncode == 0:
- # Update the cloned repository to changeset_revision. It is imperative that the
- # installed repository is updated to the desired changeset_revision before metadata
- # is set because the process for setting metadata uses the repository files on disk.
- relative_install_dir = os.path.join( clone_dir, name )
- log.debug( 'Updating cloned repository to revision "%s"...' % changeset_revision )
- cmd = 'hg update -r %s' % changeset_revision
- tmp_name = tempfile.NamedTemporaryFile().name
- tmp_stderr = open( tmp_name, 'wb' )
- os.chdir( relative_install_dir )
- proc = subprocess.Popen( cmd, shell=True, stderr=tmp_stderr.fileno() )
- returncode = proc.wait()
- os.chdir( current_working_dir )
- tmp_stderr.close()
- if returncode == 0:
- # Generate the metadata for the installed tool shed repository. It is imperative that
- # the installed repository is updated to the desired changeset_revision before metadata
- # is set because the process for setting metadata uses the repository files on disk.
- metadata_dict = generate_metadata( trans.app.toolbox, relative_install_dir, repository_clone_url )
- if 'datatypes_config' in metadata_dict:
- datatypes_config = os.path.abspath( metadata_dict[ 'datatypes_config' ] )
- # Load data types required by tools.
- self.__load_datatypes( trans, datatypes_config, relative_install_dir )
- if 'tools' in metadata_dict:
- repository_tools_tups = []
- for tool_dict in metadata_dict[ 'tools' ]:
- relative_path = tool_dict[ 'tool_config' ]
- guid = tool_dict[ 'guid' ]
- tool = trans.app.toolbox.load_tool( os.path.abspath( relative_path ) )
- repository_tools_tups.append( ( relative_path, guid, tool ) )
- if repository_tools_tups:
- sample_files = metadata_dict.get( 'sample_files', [] )
- # Handle missing data table entries for tool parameters that are dynamically generated select lists.
- repository_tools_tups = handle_missing_data_table_entry( trans.app, tool_path, sample_files, repository_tools_tups )
- # Handle missing index files for tool parameters that are dynamically generated select lists.
- repository_tools_tups = handle_missing_index_file( trans.app, tool_path, sample_files, repository_tools_tups )
- # Handle tools that use fabric scripts to install dependencies.
- handle_tool_dependencies( current_working_dir, relative_install_dir, repository_tools_tups )
- # Generate an in-memory tool conf section that includes the new tools.
- new_tool_section = generate_tool_panel_section( name,
- repository_clone_url,
- changeset_revision,
- tool_section,
- repository_tools_tups )
- # Create a temporary file to persist the in-memory tool section
- # TODO: Figure out how to do this in-memory using xml.etree.
- tmp_name = tempfile.NamedTemporaryFile().name
- persisted_new_tool_section = open( tmp_name, 'wb' )
- persisted_new_tool_section.write( new_tool_section )
- persisted_new_tool_section.close()
- # Parse the persisted tool panel section
- tree = parse_xml( tmp_name )
- root = tree.getroot()
- # Load the tools in the section into the tool panel.
- trans.app.toolbox.load_section_tag_set( root, trans.app.toolbox.tool_panel, tool_path )
- # Remove the temporary file
- try:
- os.unlink( tmp_name )
- except:
- pass
- # Append the new section to the shed_tool_config file.
- add_shed_tool_conf_entry( trans.app, shed_tool_conf, new_tool_section )
- if trans.app.toolbox_search.enabled:
- # If search support for tools is enabled, index the new installed tools.
- trans.app.toolbox_search = ToolBoxSearch( trans.app.toolbox )
- # Add a new record to the tool_shed_repository table if one doesn't
- # already exist. If one exists but is marked deleted, undelete it.
- create_or_undelete_tool_shed_repository( trans.app,
- name,
- description,
- changeset_revision,
- repository_clone_url,
- metadata_dict )
- installed_repository_names.append( name )
- else:
- tmp_stderr = open( tmp_name, 'rb' )
- message += '%s<br/>' % tmp_stderr.read()
- tmp_stderr.close()
- status = 'error'
+ owner = get_repository_owner( clean_repository_clone_url( repository_clone_url ) )
+ metadata_dict = load_repository_contents( trans.app,
+ name,
+ description,
+ owner,
+ changeset_revision,
+ repository_clone_url,
+ shed_tool_conf,
+ tool_path,
+ tool_section,
+ relative_install_dir,
+ current_working_dir,
+ tmp_name )
+ installed_repository_names.append( name )
else:
tmp_stderr = open( tmp_name, 'rb' )
message += '%s<br/>' % tmp_stderr.read()
tmp_stderr.close()
status = 'error'
- if installed_repository_names:
- installed_repository_names.sort()
- num_repositories_installed = len( installed_repository_names )
+ else:
+ tmp_stderr = open( tmp_name, 'rb' )
+ message += '%s<br/>' % tmp_stderr.read()
+ tmp_stderr.close()
+ status = 'error'
+ if installed_repository_names:
+ installed_repository_names.sort()
+ num_repositories_installed = len( installed_repository_names )
+ if tool_section:
message += 'Installed %d %s and all tools were loaded into tool panel section <b>%s</b>:<br/>Installed repositories: ' % \
( num_repositories_installed, inflector.cond_plural( num_repositories_installed, 'repository' ), tool_section.name )
- for i, repo_name in enumerate( installed_repository_names ):
- if i == len( installed_repository_names ) -1:
- message += '%s.<br/>' % repo_name
- else:
- message += '%s, ' % repo_name
- return trans.response.send_redirect( web.url_for( controller='admin_toolshed',
- action='browse_repositories',
- message=message,
- status=status ) )
- else:
- message = 'Choose the section in your tool panel to contain the installed tools.'
- status = 'error'
+ else:
+ message += 'Installed %d %s and all tools were loaded into the tool panel outside of any sections.<br/>Installed repositories: ' % \
+ ( num_repositories_installed, inflector.cond_plural( num_repositories_installed, 'repository' ) )
+ for i, repo_name in enumerate( installed_repository_names ):
+ if i == len( installed_repository_names ) -1:
+ message += '%s.<br/>' % repo_name
+ else:
+ message += '%s, ' % repo_name
+ return trans.response.send_redirect( web.url_for( controller='admin_toolshed',
+ action='browse_repositories',
+ message=message,
+ status=status ) )
if len( trans.app.toolbox.shed_tool_confs.keys() ) > 1:
shed_tool_conf_select_field = build_shed_tool_conf_select_field( trans )
shed_tool_conf = None
@@ -432,7 +369,7 @@
status=status )
def __get_relative_install_dir( self, trans, repository ):
# Get the directory where the repository is install.
- tool_shed = self.__clean_tool_shed_url( repository.tool_shed )
+ tool_shed = clean_tool_shed_url( repository.tool_shed )
partial_install_dir = '%s/repos/%s/%s/%s' % ( tool_shed, repository.owner, repository.name, repository.changeset_revision )
# Get the relative tool installation paths from each of the shed tool configs.
shed_tool_confs = trans.app.toolbox.shed_tool_confs
@@ -443,55 +380,6 @@
if os.path.isdir( relative_install_dir ):
break
return relative_install_dir
- def __load_datatypes( self, trans, datatypes_config, relative_intall_dir ):
- imported_module = None
- # Parse datatypes_config.
- tree = parse_xml( datatypes_config )
- datatypes_config_root = tree.getroot()
- relative_path_to_datatype_file_name = None
- datatype_files = datatypes_config_root.find( 'datatype_files' )
- # Currently only a single datatype_file is supported. For example:
- # <datatype_files>
- # <datatype_file name="gmap.py"/>
- # </datatype_files>
- for elem in datatype_files.findall( 'datatype_file' ):
- datatype_file_name = elem.get( 'name', None )
- if datatype_file_name:
- # Find the file in the installed repository.
- for root, dirs, files in os.walk( relative_intall_dir ):
- if root.find( '.hg' ) < 0:
- for name in files:
- if name == datatype_file_name:
- relative_path_to_datatype_file_name = os.path.join( root, name )
- break
- break
- if relative_path_to_datatype_file_name:
- relative_head, relative_tail = os.path.split( relative_path_to_datatype_file_name )
- registration = datatypes_config_root.find( 'registration' )
- # Get the module by parsing the <datatype> tag.
- for elem in registration.findall( 'datatype' ):
- # A 'type' attribute is currently required. The attribute
- # should be something like: type="gmap:GmapDB".
- dtype = elem.get( 'type', None )
- if dtype:
- fields = dtype.split( ':' )
- datatype_module = fields[0]
- datatype_class_name = fields[1]
- # Since we currently support only a single datatype_file,
- # we have what we need.
- break
- try:
- sys.path.insert( 0, relative_head )
- imported_module = __import__( datatype_module )
- sys.path.pop( 0 )
- except Exception, e:
- log.debug( "Exception importing datatypes code file included in installed repository: %s" % str( e ) )
- trans.app.datatypes_registry.load_datatypes( root_dir=trans.app.config.root, config=datatypes_config, imported_module=imported_module )
- def __clean_tool_shed_url( self, tool_shed_url ):
- if tool_shed_url.find( ':' ) > 0:
- # Eliminate the port, if any, since it will result in an invalid directory name.
- return tool_shed_url.split( ':' )[ 0 ]
- return tool_shed_url.rstrip( '/' )
def __generate_tool_path( self, repository_clone_url, changeset_revision ):
"""
Generate a tool path that guarantees repositories with the same name will always be installed
@@ -504,7 +392,7 @@
items = tmp_url.split( 'repos' )
tool_shed_url = items[ 0 ]
repo_path = items[ 1 ]
- tool_shed_url = self.__clean_tool_shed_url( tool_shed_url )
+ tool_shed_url = clean_tool_shed_url( tool_shed_url )
return '%s/repos%s/%s' % ( tool_shed_url, repo_path, changeset_revision )
def __generate_clone_url( self, trans, repository ):
"""Generate the URL for cloning a repository."""
@@ -545,17 +433,6 @@
trans.model.ToolShedRepository.table.c.owner == owner,
trans.model.ToolShedRepository.table.c.changeset_revision == changeset_revision ) ) \
.first()
-def get_repository_by_shed_name_owner_changeset_revision( app, tool_shed, name, owner, changeset_revision ):
- # This method is used by the InstallManager, which does not have access to trans.
- sa_session = app.model.context.current
- if tool_shed.find( '//' ) > 0:
- tool_shed = tool_shed.split( '//' )[1]
- return sa_session.query( app.model.ToolShedRepository ) \
- .filter( and_( app.model.ToolShedRepository.table.c.tool_shed == tool_shed,
- app.model.ToolShedRepository.table.c.name == name,
- app.model.ToolShedRepository.table.c.owner == owner,
- app.model.ToolShedRepository.table.c.changeset_revision == changeset_revision ) ) \
- .first()
def get_url_from_repository_tool_shed( trans, repository ):
# The stored value of repository.tool_shed is something like:
# toolshed.g2.bx.psu.edu
@@ -569,347 +446,3 @@
# The tool shed from which the repository was originally
# installed must no longer be configured in tool_sheds_conf.xml.
return None
-def generate_tool_panel_section( repository_name, repository_clone_url, changeset_revision, tool_section, repository_tools_tups, owner='' ):
- """
- Write an in-memory tool panel section so we can load it into the tool panel and then
- append it to the appropriate shed tool config.
- TODO: re-write using ElementTree.
- """
- tmp_url = clean_repository_clone_url( repository_clone_url )
- if not owner:
- owner = get_repository_owner( tmp_url )
- section_str = ''
- section_str += ' <section name="%s" id="%s">\n' % ( tool_section.name, tool_section.id )
- for repository_tool_tup in repository_tools_tups:
- tool_file_path, guid, tool = repository_tool_tup
- section_str += ' <tool file="%s" guid="%s">\n' % ( tool_file_path, guid )
- section_str += ' <tool_shed>%s</tool_shed>\n' % tmp_url.split( 'repos' )[ 0 ].rstrip( '/' )
- section_str += ' <repository_name>%s</repository_name>\n' % repository_name
- section_str += ' <repository_owner>%s</repository_owner>\n' % owner
- section_str += ' <changeset_revision>%s</changeset_revision>\n' % changeset_revision
- section_str += ' <id>%s</id>\n' % tool.id
- section_str += ' <version>%s</version>\n' % tool.version
- section_str += ' </tool>\n'
- section_str += ' </section>\n'
- return section_str
-def get_repository_owner( cleaned_repository_url ):
- items = cleaned_repository_url.split( 'repos' )
- repo_path = items[ 1 ]
- if repo_path.startswith( '/' ):
- repo_path = repo_path.replace( '/', '', 1 )
- return repo_path.lstrip( '/' ).split( '/' )[ 0 ]
-def generate_tool_guid( repository_clone_url, tool ):
- """
- Generate a guid for the installed tool. It is critical that this guid matches the guid for
- the tool in the Galaxy tool shed from which it is being installed. The form of the guid is
- <tool shed host>/repos/<repository owner>/<repository name>/<tool id>/<tool version>
- """
- tmp_url = clean_repository_clone_url( repository_clone_url )
- return '%s/%s/%s' % ( tmp_url, tool.id, tool.version )
-def clean_repository_clone_url( repository_clone_url ):
- if repository_clone_url.find( '@' ) > 0:
- # We have an url that includes an authenticated user, something like:
- # http://test@bx.psu.edu:9009/repos/some_username/column
- items = repository_clone_url.split( '@' )
- tmp_url = items[ 1 ]
- elif repository_clone_url.find( '//' ) > 0:
- # We have an url that includes only a protocol, something like:
- # http://bx.psu.edu:9009/repos/some_username/column
- items = repository_clone_url.split( '//' )
- tmp_url = items[ 1 ]
- else:
- tmp_url = repository_clone_url
- return tmp_url
-def generate_metadata( toolbox, relative_install_dir, repository_clone_url ):
- """
- Browse the repository files on disk to generate metadata. Since we are using disk files, it
- is imperative that the repository is updated to the desired change set revision before metadata
- is generated. This method is used by the InstallManager, which does not have access to trans.
- """
- metadata_dict = {}
- sample_files = []
- datatypes_config = None
- # Find datatypes_conf.xml if it exists.
- for root, dirs, files in os.walk( relative_install_dir ):
- if root.find( '.hg' ) < 0:
- for name in files:
- if name == 'datatypes_conf.xml':
- relative_path = os.path.join( root, name )
- datatypes_config = os.path.abspath( relative_path )
- break
- if datatypes_config:
- metadata_dict[ 'datatypes_config' ] = relative_path
- metadata_dict = generate_datatypes_metadata( datatypes_config, metadata_dict )
- # Find all special .sample files.
- for root, dirs, files in os.walk( relative_install_dir ):
- if root.find( '.hg' ) < 0:
- for name in files:
- if name.endswith( '.sample' ):
- sample_files.append( os.path.join( root, name ) )
- if sample_files:
- metadata_dict[ 'sample_files' ] = sample_files
- # Find all tool configs and exported workflows.
- for root, dirs, files in os.walk( relative_install_dir ):
- if root.find( '.hg' ) < 0 and root.find( 'hgrc' ) < 0:
- if '.hg' in dirs:
- dirs.remove( '.hg' )
- for name in files:
- # Find all tool configs.
- if name != 'datatypes_conf.xml' and name.endswith( '.xml' ):
- full_path = os.path.abspath( os.path.join( root, name ) )
- try:
- tool = toolbox.load_tool( full_path )
- except Exception, e:
- tool = None
- if tool is not None:
- tool_config = os.path.join( root, name )
- metadata_dict = generate_tool_metadata( tool_config, tool, repository_clone_url, metadata_dict )
- # Find all exported workflows
- elif name.endswith( '.ga' ):
- relative_path = os.path.join( root, name )
- fp = open( relative_path, 'rb' )
- workflow_text = fp.read()
- fp.close()
- exported_workflow_dict = from_json_string( workflow_text )
- if 'a_galaxy_workflow' in exported_workflow_dict and exported_workflow_dict[ 'a_galaxy_workflow' ] == 'true':
- metadata_dict = generate_workflow_metadata( relative_path, exported_workflow_dict, metadata_dict )
- return metadata_dict
-def generate_datatypes_metadata( datatypes_config, metadata_dict ):
- """
- Update the received metadata_dict with changes that have been applied
- to the received datatypes_config. This method is used by the InstallManager,
- which does not have access to trans.
- """
- # Parse datatypes_config.
- tree = ElementTree.parse( datatypes_config )
- root = tree.getroot()
- ElementInclude.include( root )
- repository_datatype_code_files = []
- datatype_files = root.find( 'datatype_files' )
- if datatype_files:
- for elem in datatype_files.findall( 'datatype_file' ):
- name = elem.get( 'name', None )
- repository_datatype_code_files.append( name )
- metadata_dict[ 'datatype_files' ] = repository_datatype_code_files
- datatypes = []
- registration = root.find( 'registration' )
- if registration:
- for elem in registration.findall( 'datatype' ):
- extension = elem.get( 'extension', None )
- dtype = elem.get( 'type', None )
- mimetype = elem.get( 'mimetype', None )
- datatypes.append( dict( extension=extension,
- dtype=dtype,
- mimetype=mimetype ) )
- metadata_dict[ 'datatypes' ] = datatypes
- return metadata_dict
-def generate_tool_metadata( tool_config, tool, repository_clone_url, metadata_dict ):
- """
- Update the received metadata_dict with changes that have been
- applied to the received tool. This method is used by the InstallManager,
- which does not have access to trans.
- """
- # Generate the guid
- guid = generate_tool_guid( repository_clone_url, tool )
- # Handle tool.requirements.
- tool_requirements = []
- for tr in tool.requirements:
- name=tr.name
- type=tr.type
- if type == 'fabfile':
- version = None
- fabfile = tr.fabfile
- method = tr.method
- else:
- version = tr.version
- fabfile = None
- method = None
- requirement_dict = dict( name=name,
- type=type,
- version=version,
- fabfile=fabfile,
- method=method )
- tool_requirements.append( requirement_dict )
- # Handle tool.tests.
- tool_tests = []
- if tool.tests:
- for ttb in tool.tests:
- test_dict = dict( name=ttb.name,
- required_files=ttb.required_files,
- inputs=ttb.inputs,
- outputs=ttb.outputs )
- tool_tests.append( test_dict )
- tool_dict = dict( id=tool.id,
- guid=guid,
- name=tool.name,
- version=tool.version,
- description=tool.description,
- version_string_cmd = tool.version_string_cmd,
- tool_config=tool_config,
- requirements=tool_requirements,
- tests=tool_tests )
- if 'tools' in metadata_dict:
- metadata_dict[ 'tools' ].append( tool_dict )
- else:
- metadata_dict[ 'tools' ] = [ tool_dict ]
- return metadata_dict
-def generate_workflow_metadata( relative_path, exported_workflow_dict, metadata_dict ):
- """
- Update the received metadata_dict with changes that have been applied
- to the received exported_workflow_dict. Store everything in the database.
- This method is used by the InstallManager, which does not have access to trans.
- """
- if 'workflows' in metadata_dict:
- metadata_dict[ 'workflows' ].append( ( relative_path, exported_workflow_dict ) )
- else:
- metadata_dict[ 'workflows' ] = [ ( relative_path, exported_workflow_dict ) ]
- return metadata_dict
-def handle_missing_data_table_entry( app, tool_path, sample_files, repository_tools_tups ):
- """
- Inspect each tool to see if any have input parameters that are dynamically
- generated select lists that require entries in the tool_data_table_conf.xml file.
- This method is used by the InstallManager, which does not have access to trans.
- """
- missing_data_table_entry = False
- for index, repository_tools_tup in enumerate( repository_tools_tups ):
- tup_path, guid, repository_tool = repository_tools_tup
- if repository_tool.params_with_missing_data_table_entry:
- missing_data_table_entry = True
- break
- if missing_data_table_entry:
- # The repository must contain a tool_data_table_conf.xml.sample file that includes
- # all required entries for all tools in the repository.
- for sample_file in sample_files:
- head, tail = os.path.split( sample_file )
- if tail == 'tool_data_table_conf.xml.sample':
- break
- error, correction_msg = handle_sample_tool_data_table_conf_file( app, sample_file )
- if error:
- # TODO: Do more here than logging an exception.
- log.debug( exception_msg )
- # Reload the tool into the local list of repository_tools_tups.
- repository_tool = app.toolbox.load_tool( os.path.join( tool_path, tup_path ) )
- repository_tools_tups[ index ] = ( tup_path, repository_tool )
- return repository_tools_tups
-def handle_missing_index_file( app, tool_path, sample_files, repository_tools_tups ):
- """
- Inspect each tool to see if it has any input parameters that
- are dynamically generated select lists that depend on a .loc file.
- This method is used by the InstallManager, which does not have access to trans.
- """
- missing_files_handled = []
- for index, repository_tools_tup in enumerate( repository_tools_tups ):
- tup_path, guid, repository_tool = repository_tools_tup
- params_with_missing_index_file = repository_tool.params_with_missing_index_file
- for param in params_with_missing_index_file:
- options = param.options
- missing_head, missing_tail = os.path.split( options.missing_index_file )
- if missing_tail not in missing_files_handled:
- # The repository must contain the required xxx.loc.sample file.
- for sample_file in sample_files:
- sample_head, sample_tail = os.path.split( sample_file )
- if sample_tail == '%s.sample' % missing_tail:
- copy_sample_loc_file( app, sample_file )
- if options.tool_data_table and options.tool_data_table.missing_index_file:
- options.tool_data_table.handle_found_index_file( options.missing_index_file )
- missing_files_handled.append( missing_tail )
- break
- # Reload the tool into the local list of repository_tools_tups.
- repository_tool = app.toolbox.load_tool( os.path.join( tool_path, tup_path ) )
- repository_tools_tups[ index ] = ( tup_path, guid, repository_tool )
- return repository_tools_tups
-def handle_tool_dependencies( current_working_dir, repo_files_dir, repository_tools_tups ):
- """
- Inspect each tool to see if it includes a "requirement" that refers to a fabric
- script. For those that do, execute the fabric script to install tool dependencies.
- This method is used by the InstallManager, which does not have access to trans.
- """
- for index, repository_tools_tup in enumerate( repository_tools_tups ):
- tup_path, guid, repository_tool = repository_tools_tup
- for requirement in repository_tool.requirements:
- if requirement.type == 'fabfile':
- log.debug( 'Executing fabric script to install dependencies for tool "%s"...' % repository_tool.name )
- fabfile = requirement.fabfile
- method = requirement.method
- # Find the relative path to the fabfile.
- relative_fabfile_path = None
- for root, dirs, files in os.walk( repo_files_dir ):
- for name in files:
- if name == fabfile:
- relative_fabfile_path = os.path.join( root, name )
- break
- if relative_fabfile_path:
- # cmd will look something like: fab -f fabfile.py install_bowtie
- cmd = 'fab -f %s %s' % ( relative_fabfile_path, method )
- tmp_name = tempfile.NamedTemporaryFile().name
- tmp_stderr = open( tmp_name, 'wb' )
- os.chdir( repo_files_dir )
- proc = subprocess.Popen( cmd, shell=True, stderr=tmp_stderr.fileno() )
- returncode = proc.wait()
- os.chdir( current_working_dir )
- tmp_stderr.close()
- if returncode != 0:
- # TODO: do something more here than logging the problem.
- tmp_stderr = open( tmp_name, 'rb' )
- error = tmp_stderr.read()
- tmp_stderr.close()
- log.debug( 'Problem installing dependencies for tool "%s"\n%s' % ( repository_tool.name, error ) )
-def add_shed_tool_conf_entry( app, shed_tool_conf, new_tool_section ):
- """
- Add an entry in the shed_tool_conf file. An entry looks something like:
- <section name="Filter and Sort" id="filter">
- <tool file="filter/filtering.xml" guid="toolshed.g2.bx.psu.edu/repos/test/filter/1.0.2"/>
- </section>
- This method is used by the InstallManager, which does not have access to trans.
- """
- # Make a backup of the hgweb.config file since we're going to be changing it.
- if not os.path.exists( shed_tool_conf ):
- output = open( shed_tool_conf, 'w' )
- output.write( '<?xml version="1.0"?>\n' )
- output.write( '<toolbox tool_path="%s">\n' % tool_path )
- output.write( '</toolbox>\n' )
- output.close()
- # Make a backup of the shed_tool_conf file.
- today = date.today()
- backup_date = today.strftime( "%Y_%m_%d" )
- shed_tool_conf_copy = '%s/%s_%s_backup' % ( app.config.root, shed_tool_conf, backup_date )
- shutil.copy( os.path.abspath( shed_tool_conf ), os.path.abspath( shed_tool_conf_copy ) )
- tmp_fd, tmp_fname = tempfile.mkstemp()
- new_shed_tool_conf = open( tmp_fname, 'wb' )
- for i, line in enumerate( open( shed_tool_conf ) ):
- if line.startswith( '</toolbox>' ):
- # We're at the end of the original config file, so add our entry.
- new_shed_tool_conf.write( new_tool_section )
- new_shed_tool_conf.write( line )
- else:
- new_shed_tool_conf.write( line )
- new_shed_tool_conf.close()
- shutil.move( tmp_fname, os.path.abspath( shed_tool_conf ) )
-def create_or_undelete_tool_shed_repository( app, name, description, changeset_revision, repository_clone_url, metadata_dict, owner='' ):
- # This method is used by the InstallManager, which does not have access to trans.
- sa_session = app.model.context.current
- tmp_url = clean_repository_clone_url( repository_clone_url )
- tool_shed = tmp_url.split( 'repos' )[ 0 ].rstrip( '/' )
- if not owner:
- owner = get_repository_owner( tmp_url )
- includes_datatypes = 'datatypes_config' in metadata_dict
- flush_needed = False
- tool_shed_repository = get_repository_by_shed_name_owner_changeset_revision( app, tool_shed, name, owner, changeset_revision )
- if tool_shed_repository:
- if tool_shed_repository.deleted:
- tool_shed_repository.deleted = False
- # Reset includes_datatypes in case metadata changed since last installed.
- tool_shed_repository.includes_datatypes = includes_datatypes
- flush_needed = True
- else:
- tool_shed_repository = app.model.ToolShedRepository( tool_shed=tool_shed,
- name=name,
- description=description,
- owner=owner,
- changeset_revision=changeset_revision,
- metadata=metadata_dict,
- includes_datatypes=includes_datatypes )
- flush_needed = True
- if flush_needed:
- sa_session.add( tool_shed_repository )
- sa_session.flush()
diff -r 9c46a216e24c529a496a50afec13ebcb78106b96 -r 7dd3a089101138a4796eb73a5f1391d2f436723e templates/admin/tool_shed_repository/select_tool_panel_section.mako
--- a/templates/admin/tool_shed_repository/select_tool_panel_section.mako
+++ b/templates/admin/tool_shed_repository/select_tool_panel_section.mako
@@ -23,39 +23,42 @@
<br/><div class="toolForm">
- <div class="toolFormTitle">Choose section to load tools into tool panel</div>
+ <div class="toolFormTitle">Choose tool panel section to contain installed tools (optional)</div><div class="toolFormBody">
- <form name="select_tool_panel_section" id="select_tool_panel_section" action="${h.url_for( controller='admin_toolshed', action='install_repository', tool_shed_url=tool_shed_url, repo_info_dict=repo_info_dict )}" method="post" >
- %if shed_tool_conf_select_field:
+ <form name="select_tool_panel_section" id="select_tool_panel_section" action="${h.url_for( controller='admin_toolshed', action='install_repository', tool_shed_url=tool_shed_url, repo_info_dict=repo_info_dict )}" method="post" >
+ %if shed_tool_conf_select_field:
+ <div class="form-row">
+ <label>Shed tool configuration file:</label>
+ ${shed_tool_conf_select_field.get_html()}
+ <div class="toolParamHelp" style="clear: both;">
+ Your Galaxy instance is configured with ${len( shed_tool_conf_select_field.options )} shed tool configuration files,
+ so choose one in which to configure the installed tools.
+ </div>
+ </div>
+ <div style="clear: both"></div>
+ %else:
+ <input type="hidden" name="shed_tool_conf" value="${shed_tool_conf}"/>
+ %endif
<div class="form-row">
- <label>Shed tool configuration file:</label>
- ${shed_tool_conf_select_field.get_html()}
+ <label>Add new tool panel section:</label>
+ <input name="new_tool_panel_section" type="textfield" value="${new_tool_panel_section}" size="40"/><div class="toolParamHelp" style="clear: both;">
- Your Galaxy instance is configured with ${len( shed_tool_conf_select_field.options )} shed tool configuration files,
- so choose one in which to configure the installed tools.
+ Add a new tool panel section contain the installed tools (optional).
</div></div>
- <div style="clear: both"></div>
- %else:
- <input type="hidden" name="shed_tool_conf" value="${shed_tool_conf}"/>
- %endif
- <div class="form-row">
- <label>Add new tool panel section:</label>
- <input name="new_tool_panel_section" type="textfield" value="${new_tool_panel_section}" size="40"/>
- <div class="toolParamHelp" style="clear: both;">
- Add a new tool panel section or choose an existing section in your tool panel below to contain the installed tools.
+ <div class="form-row">
+ <label>Select existing tool panel section:</label>
+ ${tool_panel_section_select_field.get_html()}
+ <div class="toolParamHelp" style="clear: both;">
+ Choose an existing section in your tool panel to contain the installed tools (optional).
+ </div></div>
- </div>
- <div class="form-row">
- <label>Select existing tool panel section:</label>
- ${tool_panel_section_select_field.get_html()}
- <div class="toolParamHelp" style="clear: both;">
- Choose an existing section in your tool panel to contain the installed tools.
+ <div class="form-row">
+ <input type="submit" name="select_tool_panel_section_button" value="Install"/>
+ <div class="toolParamHelp" style="clear: both;">
+ Clicking <b>Install</b> without selecting a tool panel section will load the installed tools into the tool panel outside of any sections.
+ </div></div>
- </div>
- <div class="form-row">
- <input type="submit" name="select_tool_panel_section_button" value="Install"/>
- </div>
- </form>
+ </form></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: natefoo: Fix extra files display with object store and the distributed object store config.
by Bitbucket 13 Dec '11
by Bitbucket 13 Dec '11
13 Dec '11
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/9c46a216e24c/
changeset: 9c46a216e24c
user: natefoo
date: 2011-12-13 06:45:52
summary: Fix extra files display with object store and the distributed object store config.
affected #: 3 files
diff -r a638b139a24187786916bc9b5c5491f4fab744fc -r 9c46a216e24c529a496a50afec13ebcb78106b96 lib/galaxy/objectstore/__init__.py
--- a/lib/galaxy/objectstore/__init__.py
+++ b/lib/galaxy/objectstore/__init__.py
@@ -868,10 +868,10 @@
def __init__(self, config):
super(DistributedObjectStore, self).__init__()
- assert config is not None, "distributed object store ('object_store = distributed') " \
- "requires a config file, please set one in " \
- "'distributed_object_store_config_file')"
- self.distributed_config = config
+ self.distributed_config = config.distributed_object_store_config_file
+ assert self.distributed_config is not None, "distributed object store ('object_store = distributed') " \
+ "requires a config file, please set one in " \
+ "'distributed_object_store_config_file')"
self.backends = {}
self.weighted_backend_names = []
@@ -1006,7 +1006,7 @@
os.environ['AWS_SECRET_ACCESS_KEY'] = config.aws_secret_key
return S3ObjectStore(config=config)
elif store == 'distributed':
- return DistributedObjectStore(config.distributed_object_store_config_file)
+ return DistributedObjectStore(config=config)
elif store == 'hierarchical':
return HierarchicalObjectStore()
diff -r a638b139a24187786916bc9b5c5491f4fab744fc -r 9c46a216e24c529a496a50afec13ebcb78106b96 lib/galaxy/web/controllers/dataset.py
--- a/lib/galaxy/web/controllers/dataset.py
+++ b/lib/galaxy/web/controllers/dataset.py
@@ -382,7 +382,7 @@
if filename and filename != "index":
# For files in extra_files_path
- file_path = trans.app.object_store.get_filename(data.id, extra_dir='dataset_%s_files' % data.id, alt_name=filename)
+ file_path = trans.app.object_store.get_filename(data.dataset.id, extra_dir='dataset_%s_files' % data.dataset.id, alt_name=filename)
if os.path.exists( file_path ):
if os.path.isdir( file_path ):
return trans.show_error_message( "Directory listing is not allowed." ) #TODO: Reconsider allowing listing of directories?
diff -r a638b139a24187786916bc9b5c5491f4fab744fc -r 9c46a216e24c529a496a50afec13ebcb78106b96 scripts/functional_tests.py
--- a/scripts/functional_tests.py
+++ b/scripts/functional_tests.py
@@ -59,7 +59,7 @@
tool_config_file = os.environ.get( 'GALAXY_TEST_TOOL_CONF', 'tool_conf.xml.sample' )
tool_data_table_config_path = 'tool_data_table_conf.xml'
tool_dependency_dir = os.environ.get( 'GALAXY_TOOL_DEPENDENCY_DIR', None )
- use_hierarchical_object_store = os.environ.get( 'GALAXY_USE_HIERARCHICAL_OBJECT_STORE', False )
+ use_distributed_object_store = os.environ.get( 'GALAXY_USE_DISTRIBUTED_OBJECT_STORE', False )
if os.path.exists( 'tool_data_table_conf.test.xml' ):
tool_data_table_config_path = 'tool_data_table_conf.test.xml'
if start_server:
@@ -154,9 +154,9 @@
if tool_dependency_dir is not None:
kwargs['tool_dependency_dir'] = tool_dependency_dir
- if use_hierarchical_object_store:
- kwargs['object_store'] = 'hierarchical'
- kwargs['hierarchical_object_store_config_file'] = 'hierarchical_object_store_conf.xml.sample'
+ if use_distributed_object_store:
+ kwargs['object_store'] = 'distributed'
+ kwargs['distributed_object_store_config_file'] = 'distributed_object_store_conf.xml.sample'
# Build the Universe Application
app = UniverseApplication( job_queue_workers = 5,
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: natefoo: Better queries for the job queue, especially when checking the user job count.
by Bitbucket 13 Dec '11
by Bitbucket 13 Dec '11
13 Dec '11
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/a638b139a241/
changeset: a638b139a241
user: natefoo
date: 2011-12-13 05:26:59
summary: Better queries for the job queue, especially when checking the user job count.
affected #: 1 file
diff -r 55bb95ec5c2a3ce2ef7eaf0a79ec60365d7d1aec -r a638b139a24187786916bc9b5c5491f4fab744fc lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -2,7 +2,6 @@
import galaxy
from galaxy import util, model
-from galaxy.model.orm import lazyload
from galaxy.datatypes.tabular import *
from galaxy.datatypes.interval import *
# tabular/interval imports appear to be unused. Clean up?
@@ -114,7 +113,7 @@
else:
log.debug( "no runner: %s is still in new state, adding to the jobs queue" %job.id )
self.queue.put( ( job.id, job.tool_id ) )
- for job in self.sa_session.query( model.Job ).options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ).filter( ( model.Job.state == model.Job.states.RUNNING ) | ( model.Job.state == model.Job.states.QUEUED ) ):
+ for job in self.sa_session.query( model.Job ).enable_eagerloads( False ).filter( ( model.Job.state == model.Job.states.RUNNING ) | ( model.Job.state == model.Job.states.QUEUED ) ):
if job.tool_id not in self.app.toolbox.tools_by_id:
log.warning( "Tool '%s' removed from tool config, unable to recover job: %s" % ( job.tool_id, job.id ) )
JobWrapper( job, self ).fail( 'This tool was disabled before the job completed. Please contact your Galaxy administrator, or' )
@@ -161,8 +160,7 @@
# Clear the session so we get fresh states for job and all datasets
self.sa_session.expunge_all()
# Fetch all new jobs
- jobs_to_check = self.sa_session.query( model.Job ) \
- .options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ) \
+ jobs_to_check = self.sa_session.query( model.Job ).enable_eagerloads( False ) \
.filter( model.Job.state == model.Job.states.NEW ).all()
else:
# Get job objects and append to watch queue for any which were
@@ -264,21 +262,19 @@
if not self.app.config.user_job_limit:
return JOB_READY
if job.user:
- user_jobs = self.sa_session.query( model.Job ) \
- .options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ) \
- .filter( and_( model.Job.user_id == job.user.id,
- or_( model.Job.state == model.Job.states.RUNNING,
- model.Job.state == model.Job.states.QUEUED ) ) ).all()
+ count = self.sa_session.query( model.Job ).enable_eagerloads( False ) \
+ .filter( and_( model.Job.user_id == job.user.id,
+ or_( model.Job.state == model.Job.states.RUNNING,
+ model.Job.state == model.Job.states.QUEUED ) ) ).count()
elif job.galaxy_session:
- user_jobs = self.sa_session.query( model.Job ) \
- .options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ) \
- .filter( and_( model.Job.session_id == job.galaxy_session.id,
- or_( model.Job.state == model.Job.states.RUNNING,
- model.Job.state == model.Job.states.QUEUED ) ) ).all()
+ count = self.sa_session.query( model.Job ).enable_eagerloads( False ) \
+ .filter( and_( model.Job.session_id == job.galaxy_session.id,
+ or_( model.Job.state == model.Job.states.RUNNING,
+ model.Job.state == model.Job.states.QUEUED ) ) ).count()
else:
log.warning( 'Job %s is not associated with a user or session so job concurrency limit cannot be checked.' % job.id )
return JOB_READY
- if len( user_jobs ) >= self.app.config.user_job_limit:
+ if count >= self.app.config.user_job_limit:
return JOB_WAIT
return JOB_READY
@@ -1245,8 +1241,7 @@
# Clear the session so we get fresh states for job and all datasets
self.sa_session.expunge_all()
# Fetch all new jobs
- newly_deleted_jobs = self.sa_session.query( model.Job ) \
- .options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ) \
+ newly_deleted_jobs = self.sa_session.query( model.Job ).enable_eagerloads( False ) \
.filter( model.Job.state == model.Job.states.DELETED_NEW ).all()
for job in newly_deleted_jobs:
jobs_to_check.append( ( job, None ) )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: natefoo: If there is no env.sh in a tool dependency directory, but there is a subdirectory named 'bin', add the bin directory to $PATH.
by Bitbucket 13 Dec '11
by Bitbucket 13 Dec '11
13 Dec '11
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/55bb95ec5c2a/
changeset: 55bb95ec5c2a
user: natefoo
date: 2011-12-13 03:40:37
summary: If there is no env.sh in a tool dependency directory, but there is a subdirectory named 'bin', add the bin directory to $PATH.
affected #: 3 files
diff -r 09c6c980e463538443134a39c92ff7930738424c -r 55bb95ec5c2a3ce2ef7eaf0a79ec60365d7d1aec lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -1754,8 +1754,10 @@
log.debug( "Dependency %s", requirement.name )
if requirement.type == 'package':
script_file, base_path, version = self.app.toolbox.dependency_manager.find_dep( requirement.name, requirement.version )
- if script_file is None:
+ if script_file is None and base_path is None:
log.warn( "Failed to resolve dependency on '%s', ignoring", requirement.name )
+ elif script_file is None:
+ commands.append( 'PACKAGE_BASE=%s; export PACKAGE_BASE; PATH="%s/bin:$PATH"; export PATH' % ( base_path, base_path ) )
else:
commands.append( 'PACKAGE_BASE=%s; export PACKAGE_BASE; . %s' % ( base_path, script_file ) )
return commands
diff -r 09c6c980e463538443134a39c92ff7930738424c -r 55bb95ec5c2a3ce2ef7eaf0a79ec60365d7d1aec lib/galaxy/tools/deps/__init__.py
--- a/lib/galaxy/tools/deps/__init__.py
+++ b/lib/galaxy/tools/deps/__init__.py
@@ -47,6 +47,8 @@
script = os.path.join( path, 'env.sh' )
if os.path.exists( script ):
return script, path, version
+ elif os.path.exists( os.path.join( path, 'bin' ) ):
+ return None, path, version
else:
return None, None, None
def _find_dep_default( self, name ):
@@ -55,9 +57,12 @@
path = os.path.join( base_path, name, 'default' )
if os.path.islink( path ):
real_path = os.path.realpath( path )
+ real_bin = os.path.join( real_path, 'bin' )
real_version = os.path.basename( real_path )
script = os.path.join( real_path, 'env.sh' )
if os.path.exists( script ):
return script, real_path, real_version
+ elif os.path.exists( os.path.join( real_path, 'bin' ) ):
+ return None, real_path, real_version
else:
return None, None, None
diff -r 09c6c980e463538443134a39c92ff7930738424c -r 55bb95ec5c2a3ce2ef7eaf0a79ec60365d7d1aec lib/galaxy/tools/deps/tests.py
--- a/lib/galaxy/tools/deps/tests.py
+++ b/lib/galaxy/tools/deps/tests.py
@@ -14,20 +14,19 @@
# Setup directories
base_path = tempfile.mkdtemp()
# mkdir( base_path )
- for name, version in [ ( "dep1", "1.0" ), ( "dep1", "2.0" ), ( "dep2", "1.0" ) ]:
- p = os.path.join( base_path, name, version )
+ for name, version, sub in [ ( "dep1", "1.0", "env.sh" ), ( "dep1", "2.0", "bin" ), ( "dep2", "1.0", None ) ]:
+ if sub == "bin":
+ p = os.path.join( base_path, name, version, "bin" )
+ else:
+ p = os.path.join( base_path, name, version )
try:
makedirs( p )
except:
pass
- touch( os.path.join( p, "env.sh" ) )
+ if sub == "env.sh":
+ touch( os.path.join( p, "env.sh" ) )
dm = galaxy.tools.deps.DependencyManager( [ base_path ] )
+ print dm.find_dep( "dep1", "1.0" )
print dm.find_dep( "dep1", "2.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
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/94ebc0972fa2/
changeset: 94ebc0972fa2
user: jgoecks
date: 2011-12-12 19:26:58
summary: Trackster: remove unused parameter from draw_helper function.
affected #: 1 file
diff -r 1de19069c32f05fed1b2b8ac63bf310e4c90aeb6 -r 94ebc0972fa2c891be3f62ded9e6ced18835f05e static/scripts/trackster.js
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -3071,7 +3071,7 @@
* Retrieves from cache, draws, or sets up drawing for a single tile. Returns either a Tile object or a
* jQuery.Deferred object that is fulfilled when tile can be drawn again.
*/
- draw_helper: function(force, width, tile_index, resolution, parent_element, w_scale, drawn_tiles, more_tile_data) {
+ draw_helper: function(force, width, tile_index, resolution, parent_element, w_scale, more_tile_data) {
var track = this,
key = this._gen_tile_cache_key(width, w_scale, tile_index),
tile_low = tile_index * DENSITY * resolution,
@@ -3348,7 +3348,7 @@
this.action_icons.tools_icon.hide();
},
can_draw: Drawable.prototype.can_draw,
- draw_helper: function(force, width, tile_index, resolution, parent_element, w_scale, drawn_tiles, more_tile_data) {
+ draw_helper: function(force, width, tile_index, resolution, parent_element, w_scale, more_tile_data) {
// Check tile cache, if found show existing tile in correct position
var
key = this._gen_tile_cache_key(width, w_scale, tile_index),
@@ -3373,7 +3373,7 @@
for (var i = 0; i < this.drawables.length; i++) {
// Build drawable tile and show it.
draw_result = this.drawables[i].draw_helper(force, width, tile_index, resolution, dummy_parent,
- w_scale, drawn_tiles, more_tile_data);
+ w_scale, more_tile_data);
if (draw_result instanceof Tile) {
tiles.push(draw_result);
}
@@ -3722,7 +3722,7 @@
var tile = tiles[i];
if (tile.max_val !== global_max) {
tile.html_elt.remove();
- track.draw_helper(true, width, tile.index, tile.resolution, tile.html_elt.parent(), w_scale, [], { max: global_max });
+ track.draw_helper(true, width, tile.index, tile.resolution, tile.html_elt.parent(), w_scale, { max: global_max });
}
}
}
https://bitbucket.org/galaxy/galaxy-central/changeset/09c6c980e463/
changeset: 09c6c980e463
user: jgoecks
date: 2011-12-12 21:58:36
summary: Trackster: more memory-efficent drawing of composite tracks. Composite tracks are now drawn and composited on a single tile rather than compositing using multiple tiles. Currently only LineTracks are supported.
affected #: 1 file
diff -r 94ebc0972fa2c891be3f62ded9e6ced18835f05e -r 09c6c980e463538443134a39c92ff7930738424c static/scripts/trackster.js
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -3109,9 +3109,20 @@
// If we can draw now, do so.
if ( can_draw_now ) {
- extend( tile_data, more_tile_data );
- var canvas = this.view.canvas_manager.new_canvas();
+ // Set up and draw tile.
+ extend(tile_data, more_tile_data);
+ var
+ canvas = track.view.canvas_manager.new_canvas(),
+ tile_bounds = track._get_tile_bounds(tile_index, resolution),
+ tile_low = tile_bounds[0],
+ tile_high = tile_bounds[1],
+ width = Math.ceil( (tile_high - tile_low) * w_scale ),
+ height = track.get_canvas_height(tile_data);
+
+ canvas.width = width;
+ canvas.height = height;
var tile = track.draw_tile(tile_data, canvas, track.mode, resolution, tile_index, w_scale, seq_data);
+
// Don't cache, show if no tile.
if (tile !== undefined) {
track.tile_cache.set(key, tile);
@@ -3131,6 +3142,13 @@
return can_draw;
},
/**
+ * Returns canvas height needed to display data; return value is an integer that denotes the
+ * number of pixels required.
+ */
+ get_canvas_height: function(data) {
+ return this.height_px;
+ },
+ /**
* Draw a track tile.
* @param result result from server
* @param canvas canvas to draw on
@@ -3141,7 +3159,7 @@
* @param ref_seq reference sequence data
*/
draw_tile: function(result, canvas, mode, resolution, tile_index, w_scale, ref_seq) {
- console.log("Warning: TiledTrack.draw_tile() not implemented.")
+ console.log("Warning: TiledTrack.draw_tile() not implemented.");
},
/**
* Show track tile and perform associated actions. Showing tile may actually move
@@ -3349,74 +3367,94 @@
},
can_draw: Drawable.prototype.can_draw,
draw_helper: function(force, width, tile_index, resolution, parent_element, w_scale, more_tile_data) {
+ // FIXME: this function is similar to TiledTrack.draw_helper -- can the two be merged/refactored?
+ var track = this,
+ key = this._gen_tile_cache_key(width, w_scale, tile_index),
+ tile_low = tile_index * DENSITY * resolution,
+ tile_high = tile_low + DENSITY * resolution;
+
// Check tile cache, if found show existing tile in correct position
- var
- key = this._gen_tile_cache_key(width, w_scale, tile_index),
- tile = ( force ? undefined : this.tile_cache.get(key) );
+ var tile = (force ? undefined : track.tile_cache.get(key));
if (tile) {
- this.show_tile(tile, parent_element, w_scale);
+ track.show_tile(tile, parent_element, w_scale);
return tile;
}
+
+ // Helper to determine if object is jQuery deferred
+ var is_deferred = function ( d ) {
+ return ( 'isResolved' in d );
+ };
- // Get/build tiles.
- // FIXME: using very naive and simple approach for building tiles.
- // issues not addressed include:
- // (a) choosing tile size;
- // (b) any kind of normalization.
- var
- drawable,
- dummy_parent = $("<div/>"),
- draw_result,
- tiles = [],
- deferreds = [],
- can_draw = true;
+ // Try to get drawables' data.
+ var all_data = [],
+ track,
+ // Flag to track whether we can draw everything now
+ can_draw_now = true,
+ tile_data,
+ seq_data;
for (var i = 0; i < this.drawables.length; i++) {
- // Build drawable tile and show it.
- draw_result = this.drawables[i].draw_helper(force, width, tile_index, resolution, dummy_parent,
- w_scale, more_tile_data);
- if (draw_result instanceof Tile) {
- tiles.push(draw_result);
+ track = this.drawables[i];
+ // Get the track data, maybe a deferred.
+ tile_data = track.data_manager.get_data( tile_low, tile_high, track.mode, resolution, track.data_url_extra_params );
+ if ( is_deferred( tile_data ) ) {
+ can_draw_now = false;
}
- else {
- deferreds.push(draw_result);
- can_draw = false;
+ all_data.push(tile_data);
+
+ // Get seq data if needed, maybe a deferred.
+ seq_data = null;
+ if ( view.reference_track && w_scale > view.canvas_manager.char_width_px ) {
+ seq_data = view.reference_track.data_manager.get_data(tile_low, tile_high, track.mode, resolution, view.reference_track.data_url_extra_params)
+ if ( is_deferred( seq_data ) ) {
+ can_draw_now = false;
+ }
}
+ all_data.push(seq_data);
}
-
- if (can_draw) {
- // Build composite tile by compositing canvases.
+
+ // If we can draw now, do so.
+ if ( can_draw_now ) {
+ // Set up and draw tile.
+ extend(tile_data, more_tile_data);
+ var
+ canvas = track.view.canvas_manager.new_canvas(),
+ tile_bounds = track._get_tile_bounds(tile_index, resolution),
+ tile_low = tile_bounds[0],
+ tile_high = tile_bounds[1],
+ width = Math.ceil( (tile_high - tile_low) * w_scale ),
+ height = track.get_canvas_height(tile_data);
- // Copy first canvas.
- var src_canvas = tiles[0].html_elt.find("canvas"),
- cmp_canvas = src_canvas.clone(),
- src_ctx = src_canvas.get(0).getContext("2d"),
- cmp_ctx = cmp_canvas.get(0).getContext("2d"),
- imageData = src_ctx.getImageData(0, 0, src_ctx.canvas.width, src_ctx.canvas.height);
-
- cmp_ctx.putImageData(imageData, 0, 0);
-
- // Composite additional canvases.
- cmp_ctx.globalCompositeOperation = "darker";
- var another_canvas;
- for (var i = 1; i < tiles.length; i++) {
- another_canvas = tiles[i].html_elt.find("canvas").get(0);
- cmp_ctx.drawImage(another_canvas, 0, 0);
+ // FIXME:
+ // (a) right now, only LineTracks respect width/height setting and do not set it in draw_tile;
+ // however, other track types be modified to work this way. This is necessary b/c setting the width/height
+ // clears the canvas and hence prevents non-compliant tracks from being used in CompositeTracks.
+ // (b) need to normalize across tracks before displaying, e.g. set LineTracks' min/max appropriately.
+ canvas.width = width;
+ canvas.height = height;
+ var all_data_index = 0
+ for (var i = 0; i < this.drawables.length; i++, all_data_index += 2) {
+ track = this.drawables[i];
+ tile_data = all_data[ all_data_index ];
+ seq_data = all_data[ all_data_index + 1 ];
+ tile = track.draw_tile(tile_data, canvas, track.mode, resolution, tile_index, w_scale, seq_data);
}
- tile = new Tile(this, tile_index, resolution, cmp_canvas);
- this.tile_cache.set(key, tile);
- this.show_tile(tile, parent_element, w_scale);
+ // Don't cache, show if no tile.
+ track.tile_cache.set(key, tile);
+ track.show_tile(tile, parent_element, w_scale);
+ return tile;
}
- else {
- // Wait until tiles can be drawn, then try again.
- var track = this;
- $.when.apply($, deferreds).then(function() {
- track.request_draw();
- });
- tile = {};
- }
+
+ // Can't draw now, so trigger another redraw when the data is ready
+ var can_draw = $.Deferred(),
+ track = this;
+ $.when.apply($, all_data).then(function() {
+ view.request_redraw(false, false, false, track);
+ can_draw.resolve();
+ });
- return tile;
+ // Returned Deferred that is resolved when tile can be drawn.
+ return can_draw;
}
});
@@ -3595,28 +3633,25 @@
* Draw LineTrack tile.
*/
draw_tile: function(result, canvas, mode, resolution, tile_index, w_scale) {
+ // FIXME: is this needed?
if (this.vertical_range === undefined) {
return;
}
-
+
+ // Paint onto canvas.
var
+ ctx = canvas.getContext("2d"),
tile_bounds = this._get_tile_bounds(tile_index, resolution),
tile_low = tile_bounds[0],
tile_high = tile_bounds[1],
- width = Math.ceil( (tile_high - tile_low) * w_scale ),
- height = this.height_px;
-
- // Canvas setup.
- canvas.width = width,
- canvas.height = height;
-
- // Paint line onto full canvas
- var ctx = canvas.getContext("2d");
- var painter = new painters.LinePainter(result.data, tile_low, tile_high, this.prefs, mode);
- painter.draw(ctx, width, height);
+ painter = new painters.LinePainter(result.data, tile_low, tile_high, this.prefs, mode);
+ // HACK: this is needed for compositing tracks. This should be a config setting somewhere; also,
+ // "darker" may not be included in future canvas implementations.
+ ctx.globalCompositeOperation = "darker";
+ painter.draw(ctx, canvas.width, canvas.height);
return new Tile(this.track, tile_index, resolution, canvas, result.data);
- }
+ }
});
var FeatureTrack = function(name, view, container, hda_ldda, dataset_id, prefs, filters, tool, data_manager) {
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: Trackster: (a) fix small bug in drawing composite tracks and (b) pass canvas in as argument to draw_tile.
by Bitbucket 10 Dec '11
by Bitbucket 10 Dec '11
10 Dec '11
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/1de19069c32f/
changeset: 1de19069c32f
user: jgoecks
date: 2011-12-10 18:25:44
summary: Trackster: (a) fix small bug in drawing composite tracks and (b) pass canvas in as argument to draw_tile.
affected #: 1 file
diff -r 67d388232ff37b5d9890f2e30a29608676928fa5 -r 1de19069c32f05fed1b2b8ac63bf310e4c90aeb6 static/scripts/trackster.js
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -3110,7 +3110,8 @@
// If we can draw now, do so.
if ( can_draw_now ) {
extend( tile_data, more_tile_data );
- var tile = track.draw_tile(tile_data, track.mode, resolution, tile_index, w_scale, seq_data);
+ var canvas = this.view.canvas_manager.new_canvas();
+ var tile = track.draw_tile(tile_data, canvas, track.mode, resolution, tile_index, w_scale, seq_data);
// Don't cache, show if no tile.
if (tile !== undefined) {
track.tile_cache.set(key, tile);
@@ -3132,13 +3133,14 @@
/**
* Draw a track tile.
* @param result result from server
+ * @param canvas canvas to draw on
* @param mode mode to draw in
* @param resolution view resolution
* @param tile_index index of tile to be drawn
* @param w_scale pixels per base
* @param ref_seq reference sequence data
*/
- draw_tile: function(result, mode, resolution, tile_index, w_scale, ref_seq) {
+ draw_tile: function(result, canvas, mode, resolution, tile_index, w_scale, ref_seq) {
console.log("Warning: TiledTrack.draw_tile() not implemented.")
},
/**
@@ -3411,6 +3413,7 @@
$.when.apply($, deferreds).then(function() {
track.request_draw();
});
+ tile = {};
}
return tile;
@@ -3441,7 +3444,7 @@
/**
* Draw ReferenceTrack tile.
*/
- draw_tile: function(seq, mode, resolution, tile_index, w_scale) {
+ draw_tile: function(seq, canvas, mode, resolution, tile_index, w_scale) {
var track = this,
tile_length = DENSITY * resolution;
@@ -3450,7 +3453,6 @@
track.content_div.css("height", "0px");
return;
}
- var canvas = this.view.canvas_manager.new_canvas();
var ctx = canvas.getContext("2d");
canvas.width = Math.ceil(tile_length * w_scale + track.left_offset);
canvas.height = track.height_px;
@@ -3592,7 +3594,7 @@
/**
* Draw LineTrack tile.
*/
- draw_tile: function(result, mode, resolution, tile_index, w_scale) {
+ draw_tile: function(result, canvas, mode, resolution, tile_index, w_scale) {
if (this.vertical_range === undefined) {
return;
}
@@ -3604,8 +3606,7 @@
width = Math.ceil( (tile_high - tile_low) * w_scale ),
height = this.height_px;
- // Create canvas
- var canvas = this.view.canvas_manager.new_canvas();
+ // Canvas setup.
canvas.width = width,
canvas.height = height;
@@ -3883,13 +3884,14 @@
/**
* Draw FeatureTrack tile.
* @param result result from server
+ * @param canvas canvas to draw on
* @param mode mode to draw in
* @param resolution view resolution
* @param tile_index index of tile to be drawn
* @param w_scale pixels per base
* @param ref_seq reference sequence data
*/
- draw_tile: function(result, mode, resolution, tile_index, w_scale, ref_seq) {
+ draw_tile: function(result, canvas, mode, resolution, tile_index, w_scale, ref_seq) {
var track = this,
tile_bounds = track._get_tile_bounds(tile_index, resolution),
tile_low = tile_bounds[0],
@@ -3942,8 +3944,6 @@
max_label.text(result.max);
max_label.css({ position: "absolute", top: "24px", left: "10px", color: this.prefs.label_color });
max_label.prependTo(this.container_div);
- // Create canvas
- var canvas = this.view.canvas_manager.new_canvas();
canvas.width = width + left_offset;
// Extra padding at top of summary tree
canvas.height = required_height + SUMMARY_TREE_TOP_PADDING;
@@ -4002,7 +4002,6 @@
// HACK: ref_seq will only be defined for ReadTracks, and only the ReadPainter accepts that argument
var painter = new (this.painter)(filtered, tile_low, tile_high, this.prefs, mode, filter_alpha_scaler, filter_height_scaler, ref_seq);
var required_height = Math.max(MIN_TRACK_HEIGHT, painter.get_required_height(slots_required,width));
- var canvas = this.view.canvas_manager.new_canvas();
var feature_mapper = null;
canvas.width = width + left_offset;
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: Trackster bug fixes for random color generation and LineTrack display in line and intensity modes.
by Bitbucket 10 Dec '11
by Bitbucket 10 Dec '11
10 Dec '11
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/67d388232ff3/
changeset: 67d388232ff3
user: jgoecks
date: 2011-12-10 16:35:31
summary: Trackster bug fixes for random color generation and LineTrack display in line and intensity modes.
affected #: 1 file
diff -r f148a319a3138843926a26b278793d3fd4a55fbc -r 67d388232ff37b5d9890f2e30a29608676928fa5 static/scripts/trackster.js
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -120,31 +120,34 @@
var new_color, nr, ng, nb,
other_color, or, og, ob,
n_brightness, o_brightness,
- diff, ok = false;
+ diff, ok = false,
+ num_tries = 0;
do {
// New color is never white b/c random in [0,1)
- new_color = Math.random() * 0xffffff;
- nr = new_color | 0xff0000;
- ng = new_color | 0x00ff00;
- nb = new_color | 0x0000ff;
+ new_color = Math.round( Math.random() * 0xffffff );
+ nr = ( new_color & 0xff0000 ) >> 16;
+ ng = ( new_color & 0x00ff00 ) >> 8;
+ nb = new_color & 0x0000ff;
n_brightness = brightness(nr, ng, nb);
ok = true;
for (var i = 0; i < colors.length; i++) {
other_color = colors[i];
- or = other_color | 0xff0000;
- og = other_color | 0x00ff00;
- ob = other_color | 0x0000ff;
+ or = ( other_color & 0xff0000 ) >> 16;
+ og = ( other_color & 0x00ff00 ) >> 8;
+ ob = other_color & 0x0000ff;
o_brightness = brightness(or, og, ob);
diff = difference(nr, ng, nb, or, og, ob);
- // Thresholds for brightness difference and color difference
- // are from W3C link above.
- if ( ( Math.abs(n_brightness - o_brightness) < 125 ) ||
- ( diff < 500 ) ) {
+ // These thresholds may need to be adjusted. Brightness difference range is 125;
+ // color difference range is 500.
+ if ( ( Math.abs(n_brightness - o_brightness) < 40 ) ||
+ ( diff < 200 ) ) {
ok = false;
break;
}
}
- } while (!ok);
+
+ num_tries++
+ } while (!ok && num_tries <= 10 );
// Add 0x1000000 to left pad number with 0s.
return '#' + ( 0x1000000 + new_color ).toString(16).substr(1,6);
@@ -4445,8 +4448,17 @@
} else {
delta_x_px = 10;
}
+
+ // Extract RGB from preference color.
+ var
+ pref_color = parseInt( this.prefs.color.slice(1), 16 ),
+ pref_r = (pref_color & 0xff0000) >> 16,
+ pref_g = (pref_color & 0x00ff00) >> 8,
+ pref_b = pref_color & 0x0000ff;
+
+ // Paint track.
for (var i = 0, len = data.length; i < len; i++) {
- ctx.fillStyle = this.prefs.color;
+ ctx.fillStyle = ctx.strokeStyle = this.prefs.color;
x_scaled = Math.round((data[i][0] - view_start) * w_scale);
y = data[i][1];
var top_overflow = false, bot_overflow = false;
@@ -4469,9 +4481,14 @@
// y becomes the bar height in pixels, which is the negated for canvas coords
y = Math.round( y / vertical_range * height_px );
ctx.fillRect(x_scaled, y_zero, delta_x_px, - y );
- } else if (mode === "Intensity" ) {
- y = 255 - Math.floor( (y - min_value) / vertical_range * 255 );
- ctx.fillStyle = "rgb(" +y+ "," +y+ "," +y+ ")";
+ } else if (mode === "Intensity") {
+ var
+ saturation = (y - min_value) / vertical_range,
+ // Range is [pref_color, 255] where saturation = 0 --> 255 and saturation = 1 --> pref color
+ new_r = Math.round( pref_r + (255 - pref_r) * (1 - saturation) ),
+ new_g = Math.round( pref_g + (255 - pref_g) * (1 - saturation) ),
+ new_b = Math.round( pref_b + (255 - pref_b) * (1 - saturation) );
+ ctx.fillStyle = "rgb(" + new_r + "," + new_g + "," + new_b + ")";
ctx.fillRect(x_scaled, 0, delta_x_px, height_px);
} else {
// console.log(y, track.min_value, track.vertical_range, (y - track.min_value) / track.vertical_range * track.height_px);
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: Trackster: fix bugs in e1da72b42db7 that prevented adding tracks.
by Bitbucket 09 Dec '11
by Bitbucket 09 Dec '11
09 Dec '11
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/f148a319a313/
changeset: f148a319a313
user: jgoecks
date: 2011-12-09 23:03:33
summary: Trackster: fix bugs in e1da72b42db7 that prevented adding tracks.
affected #: 2 files
diff -r e1da72b42db7abbb3a1e3a9d842341aef5c28d9e -r f148a319a3138843926a26b278793d3fd4a55fbc static/scripts/trackster_ui.js
--- a/static/scripts/trackster_ui.js
+++ b/static/scripts/trackster_ui.js
@@ -29,6 +29,19 @@
};
/**
+ * Create object from a dictionary.
+ */
+var object_from_dict = function(track_dict, container) {
+ var
+ drawable_type = track_dict['obj_type'];
+ // For backward compatibility:
+ if (!drawable_type) {
+ drawable_type = track_dict['track_type'];
+ }
+ return addable_objects[ drawable_type ].prototype.from_dict(track_dict, container);
+};
+
+/**
* Objects that can be added to a view.
*/
var addable_objects = {
@@ -68,14 +81,7 @@
drawable_type,
drawable;
for (var i = 0; i < drawables_config.length; i++) {
- drawable_config = drawables_config[i];
- drawable_type = drawable_config['obj_type'];
- // For backward compatibility:
- if (!drawable_type) {
- drawable_type = drawable_config['track_type'];
- }
- drawable = addable_objects[ drawable_type ].prototype.from_dict( drawable_config, view );
- view.add_drawable( drawable );
+ view.add_drawable( object_from_dict( drawables_config[i], view ) );
}
}
diff -r e1da72b42db7abbb3a1e3a9d842341aef5c28d9e -r f148a319a3138843926a26b278793d3fd4a55fbc templates/tracks/browser.mako
--- a/templates/tracks/browser.mako
+++ b/templates/tracks/browser.mako
@@ -100,7 +100,7 @@
[ arguments[0] ]
);
for (var i= 0; i < track_defs.length; i++) {
- view.add_drawable( track_from_dict(track_defs[i], view) );
+ view.add_drawable( object_from_dict(track_defs[i], view) );
}
});
hide_modal();
@@ -214,7 +214,7 @@
url: "${h.url_for( action='add_track_async' )}",
data: { hda_id: "${add_dataset}" },
dataType: "json",
- success: function(track_data) { view.add_drawable( track_from_dict(track_data, view) ) }
+ success: function(track_data) { view.add_drawable( object_from_dict(track_data, view) ) }
});
%endif
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/e1da72b42db7/
changeset: e1da72b42db7
user: jgoecks
date: 2011-12-09 21:38:03
summary: First pass on composite tracks. To facilitate composite tracks, draw_helper now returns jQuery Deferred objects rather than null when drawing cannot occur so that tile drawing can be more closely tracked. Current limitations of composite tracks: (a) can be created only inside groups; (b) only tracks of the same type can be composited; and (c) no normalization is done b/t tracks. Also implemented generic save/restore functionality. Pack JS scripts.
affected #: 6 files
diff -r 1b3c54debe5813a6a6b91ab1cd3581f489d09abb -r e1da72b42db7abbb3a1e3a9d842341aef5c28d9e static/scripts/packed/galaxy.panels.js
--- a/static/scripts/packed/galaxy.panels.js
+++ b/static/scripts/packed/galaxy.panels.js
@@ -1,1 +1,1 @@
-function ensure_dd_helper(){if($("#DD-helper").length==0){$("<div id='DD-helper'/>").css({background:"white",opacity:0,zIndex:9000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}var MIN_PANEL_WIDTH=100,MAX_PANEL_WIDTH=1000;function make_left_panel(h,c,e){var g=false;var f=null;var d=function(i){var j=i;if(i<0){i=0}$(h).css("width",i);$(e).css("left",j);$(c).css("left",i+7);if(document.recalc){document.recalc()}};var a=function(){if(g){$(e).removeClass("hover");$(e).animate({left:f},"fast");$(h).css("left",-f).show().animate({left:0},"fast",function(){d(f);$(e).removeClass("hidden")});g=false}else{f=$(e).position().left;$(c).css("left",$(e).innerWidth());if(document.recalc){document.recalc()}$(e).removeClass("hover");$(h).animate({left:-f},"fast");$(e).animate({left:-1},"fast",function(){$(this).addClass("hidden")});g=true}};$(e).bind("dragstart",function(){$("#DD-helper").show()}).bind("dragend",function(){$("#DD-helper").hide()}).bind("drag",function(i,j){x=j.offsetX;x=Math.min(MAX_PANEL_WIDTH,Math.max(MIN_PANEL_WIDTH,x));if(g){$(h).css("left",0);$(e).removeClass("hidden");g=false}d(x)}).bind("dragclickonly",function(){a()}).find("div").show();var b=function(i){if((g&&i=="show")||(!g&&i=="hide")){a()}};return{force_panel:b}}function make_right_panel(a,e,h){var j=false,g=false,c=null;var d=function(k){$(a).css("width",k);$(e).css("right",k+9);$(h).css("right",k).css("left","");if(document.recalc){document.recalc()}};var i=function(){if(j){$(h).removeClass("hover");$(h).animate({right:c},"fast");$(a).css("right",-c).show().animate({right:0},"fast",function(){d(c);$(h).removeClass("hidden")});j=false}else{c=$(document).width()-$(h).position().left-$(h).outerWidth();$(e).css("right",$(h).innerWidth()+1);if(document.recalc){document.recalc()}$(h).removeClass("hover");$(a).animate({right:-c},"fast");$(h).animate({right:-1},"fast",function(){$(this).addClass("hidden")});j=true}g=false};var b=function(k){var l=$(e).width()-(j?c:0);if(l<k){if(!j){i();g=true}}else{if(g){i();g=false}}};$(h).hover(function(){$(this).addClass("hover")},function(){$(this).removeClass("hover")}).bind("dragstart",function(){$("#DD-helper").show()}).bind("dragend",function(){$("#DD-helper").hide()}).bind("drag",function(k,l){x=l.offsetX;w=$(window).width();x=Math.min(w-MIN_PANEL_WIDTH,x);x=Math.max(w-MAX_PANEL_WIDTH,x);if(j){$(a).css("right",0);$(h).removeClass("hidden");j=false}d(w-x-$(this).outerWidth())}).bind("dragclickonly",function(){i()}).find("div").show();var f=function(k){if((j&&k=="show")||(!j&&k=="hide")){i()}};return{handle_minwidth_hint:b,force_panel:f}}function hide_modal(){$(".dialog-box-container").hide(0,function(){$("#overlay").hide();$("#overlay").removeClass("modal");$(".dialog-box").find(".body").children().remove()})}function show_modal(){$("#overlay").addClass("modal");_show_modal.apply(this,arguments)}function show_message(){_show_modal.apply(this,arguments)}function _show_modal(h,c,f,d,g){if(h){$(".dialog-box").find(".title").html(h);$(".dialog-box").find(".unified-panel-header").show()}else{$(".dialog-box").find(".unified-panel-header").hide()}var a=$(".dialog-box").find(".buttons").html("");if(f){$.each(f,function(b,i){a.append($("<button/>").text(b).click(i));a.append(" ")});a.show()}else{a.hide()}var a=$(".dialog-box").find(".extra_buttons").html("");if(d){$.each(d,function(b,i){a.append($("<button/>").text(b).click(i));a.append(" ")});a.show()}else{a.hide()}if(c=="progress"){c=$("<img/>").attr("src",image_path+"/yui/rel_interstitial_loading.gif")}var e=$(".dialog-box").find(".body");e.css("min-width","");$(".dialog-box").find(".body").html(c);if(!$(".dialog-box-container").is(":visible")){$("#overlay").show();$(".dialog-box-container").show()}e.css("min-width",e.width());if(g){g()}}function show_in_overlay(c){var d=c.width||"600";var b=c.height||"400";var a=c.scroll||"auto";$("#overlay-background").bind("click.overlay",function(){hide_modal();$("#overlay-background").unbind("click.overlay")});show_modal(null,$("<div style='margin: -5px;'><img id='close_button' style='position:absolute;right:-17px;top:-15px;src='"+image_path+"/closebox.png'><iframe style='margin: 0; padding: 0;' src='"+c.url+"' width='"+d+"' height='"+b+"' scrolling='"+a+"' frameborder='0'></iframe></div>"));$("#close_button").bind("click",function(){hide_modal()})}$(function(){$(".tab").each(function(){var a=$(this).children(".submenu");if(a.length>0){if($.browser.msie){a.prepend("<iframe style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; filter:Alpha(Opacity='0');\"></iframe>")}$(this).hover(function(){a.show()},function(){a.hide()});a.click(function(){a.hide()})}})});function user_changed(a,b){if(a){$(".loggedin-only").show();$(".loggedout-only").hide();$("#user-email").text(a);if(b){$(".admin-only").show()}}else{$(".loggedin-only").hide();$(".loggedout-only").show();$(".admin-only").hide()}};
\ No newline at end of file
+function ensure_dd_helper(){if($("#DD-helper").length==0){$("<div id='DD-helper'/>").css({background:"white",opacity:0,zIndex:9000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}var MIN_PANEL_WIDTH=100,MAX_PANEL_WIDTH=1000;function make_left_panel(h,c,e){var g=false;var f=null;var d=function(i){var j=i;if(i<0){i=0}$(h).css("width",i);$(e).css("left",j);$(c).css("left",i+7);if(document.recalc){document.recalc()}};var a=function(){if(g){$(e).removeClass("hover");$(e).animate({left:f},"fast");$(h).css("left",-f).show().animate({left:0},"fast",function(){d(f);$(e).removeClass("hidden")});g=false}else{f=$(e).position().left;$(c).css("left",$(e).innerWidth());if(document.recalc){document.recalc()}$(e).removeClass("hover");$(h).animate({left:-f},"fast");$(e).animate({left:-1},"fast",function(){$(this).addClass("hidden")});g=true}};$(e).bind("dragstart",function(){$("#DD-helper").show()}).bind("dragend",function(){$("#DD-helper").hide()}).bind("drag",function(i,j){x=j.offsetX;x=Math.min(MAX_PANEL_WIDTH,Math.max(MIN_PANEL_WIDTH,x));if(g){$(h).css("left",0);$(e).removeClass("hidden");g=false}d(x)}).bind("dragclickonly",function(){a()}).find("div").show();var b=function(i){if((g&&i=="show")||(!g&&i=="hide")){a()}};return{force_panel:b}}function make_right_panel(a,e,h){var j=false,g=false,c=null;var d=function(k){$(a).css("width",k);$(e).css("right",k+9);$(h).css("right",k).css("left","");if(document.recalc){document.recalc()}};var i=function(){if(j){$(h).removeClass("hover");$(h).animate({right:c},"fast");$(a).css("right",-c).show().animate({right:0},"fast",function(){d(c);$(h).removeClass("hidden")});j=false}else{c=$(document).width()-$(h).position().left-$(h).outerWidth();$(e).css("right",$(h).innerWidth()+1);if(document.recalc){document.recalc()}$(h).removeClass("hover");$(a).animate({right:-c},"fast");$(h).animate({right:-1},"fast",function(){$(this).addClass("hidden")});j=true}g=false};var b=function(k){var l=$(e).width()-(j?c:0);if(l<k){if(!j){i();g=true}}else{if(g){i();g=false}}};$(h).hover(function(){$(this).addClass("hover")},function(){$(this).removeClass("hover")}).bind("dragstart",function(){$("#DD-helper").show()}).bind("dragend",function(){$("#DD-helper").hide()}).bind("drag",function(k,l){x=l.offsetX;w=$(window).width();x=Math.min(w-MIN_PANEL_WIDTH,x);x=Math.max(w-MAX_PANEL_WIDTH,x);if(j){$(a).css("right",0);$(h).removeClass("hidden");j=false}d(w-x-$(this).outerWidth())}).bind("dragclickonly",function(){i()}).find("div").show();var f=function(k){if((j&&k=="show")||(!j&&k=="hide")){i()}};return{handle_minwidth_hint:b,force_panel:f}}function hide_modal(){$(".dialog-box-container").hide(0,function(){$("#overlay").hide();$("#overlay").removeClass("modal");$(".dialog-box").find(".body").children().remove()})}function show_modal(){$("#overlay").addClass("modal");_show_modal.apply(this,arguments)}function show_message(){_show_modal.apply(this,arguments)}function _show_modal(h,c,f,d,g){if(h){$(".dialog-box").find(".title").html(h);$(".dialog-box").find(".unified-panel-header").show()}else{$(".dialog-box").find(".unified-panel-header").hide()}var a=$(".dialog-box").find(".buttons").html("");if(f){$.each(f,function(b,i){a.append($("<button/>").text(b).click(i));a.append(" ")});a.show()}else{a.hide()}var a=$(".dialog-box").find(".extra_buttons").html("");if(d){$.each(d,function(b,i){a.append($("<button/>").text(b).click(i));a.append(" ")});a.show()}else{a.hide()}if(c=="progress"){c=$("<img/>").attr("src",image_path+"/yui/rel_interstitial_loading.gif")}var e=$(".dialog-box").find(".body");e.css("min-width","0px");$(".dialog-box").find(".body").html(c);if(!$(".dialog-box-container").is(":visible")){$("#overlay").show();$(".dialog-box-container").show()}e.css("min-width",e.width());if(g){g()}}function show_in_overlay(c){var d=c.width||"600";var b=c.height||"400";var a=c.scroll||"auto";$("#overlay-background").bind("click.overlay",function(){hide_modal();$("#overlay-background").unbind("click.overlay")});show_modal(null,$("<div style='margin: -5px;'><img id='close_button' style='position:absolute;right:-17px;top:-15px;src='"+image_path+"/closebox.png'><iframe style='margin: 0; padding: 0;' src='"+c.url+"' width='"+d+"' height='"+b+"' scrolling='"+a+"' frameborder='0'></iframe></div>"));$("#close_button").bind("click",function(){hide_modal()})}$(function(){$(".tab").each(function(){var a=$(this).children(".submenu");if(a.length>0){if($.browser.msie){a.prepend("<iframe style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; filter:Alpha(Opacity='0');\"></iframe>")}$(this).hover(function(){a.show()},function(){a.hide()});a.click(function(){a.hide()})}})});function user_changed(a,b){if(a){$(".loggedin-only").show();$(".loggedout-only").hide();$("#user-email").text(a);if(b){$(".admin-only").show()}}else{$(".loggedin-only").hide();$(".loggedout-only").show();$(".admin-only").hide()}};
\ No newline at end of file
diff -r 1b3c54debe5813a6a6b91ab1cd3581f489d09abb -r e1da72b42db7abbb3a1e3a9d842341aef5c28d9e static/scripts/packed/trackster.js
--- a/static/scripts/packed/trackster.js
+++ b/static/scripts/packed/trackster.js
@@ -1,1 +1,1 @@
-var class_module=function(b,a){var c=function(){var f=arguments[0];for(var e=1;e<arguments.length;e++){var d=arguments[e];for(key in d){f[key]=d[key]}}return f};a.extend=c};var requestAnimationFrame=(function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(b,a){window.setTimeout(b,1000/60)}})();var BEFORE=1001,CONTAINS=1002,OVERLAP_START=1003,OVERLAP_END=1004,CONTAINED_BY=1005,AFTER=1006;var compute_overlap=function(e,b){var g=e[0],f=e[1],d=b[0],c=b[1],a;if(g<d){if(f<d){a=BEFORE}else{if(f<=c){a=OVERLAP_START}else{a=CONTAINS}}}else{if(g>c){a=AFTER}else{if(f<=c){a=CONTAINED_BY}else{a=OVERLAP_END}}}return a};var is_overlap=function(c,b){var a=compute_overlap(c,b);return(a!==BEFORE&&a!==AFTER)};var get_random_color=function(a){if(!a){a="#ffffff"}if(typeof(a)==="string"){a=[a]}for(var j=0;j<a.length;j++){a[j]=parseInt(a[j].slice(1),16)}var m=function(t,s,i){return((t*299)+(s*587)+(i*114))/1000};var e=function(u,t,v,r,i,s){return(Math.max(u,r)-Math.min(u,r))+(Math.max(t,i)-Math.min(t,i))+(Math.max(v,s)-Math.min(v,s))};var g,n,f,k,p,h,q,c,d,b,o,l=false;do{g=Math.random()*16777215;n=g|16711680;f=g|65280;k=g|255;d=m(n,f,k);l=true;for(var j=0;j<a.length;j++){p=a[j];h=p|16711680;q=p|65280;c=p|255;b=m(h,q,c);o=e(n,f,k,h,q,c);if((Math.abs(d-b)<125)||(o<500)){l=false;break}}}while(!l);return"#"+(16777216+g).toString(16).substr(1,6)};var trackster_module=function(f,X){var p=f("class").extend,s=f("slotting"),M=f("painters");var ae=function(af,ag){this.document=af;this.default_font=ag!==undefined?ag:"9px Monaco, Lucida Console, monospace";this.dummy_canvas=this.new_canvas();this.dummy_context=this.dummy_canvas.getContext("2d");this.dummy_context.font=this.default_font;this.char_width_px=this.dummy_context.measureText("A").width;this.patterns={};this.load_pattern("right_strand","/visualization/strand_right.png");this.load_pattern("left_strand","/visualization/strand_left.png");this.load_pattern("right_strand_inv","/visualization/strand_right_inv.png");this.load_pattern("left_strand_inv","/visualization/strand_left_inv.png")};p(ae.prototype,{load_pattern:function(af,aj){var ag=this.patterns,ah=this.dummy_context,ai=new Image();ai.src=image_path+aj;ai.onload=function(){ag[af]=ah.createPattern(ai,"repeat")}},get_pattern:function(af){return this.patterns[af]},new_canvas:function(){var af=this.document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(af)}af.manager=this;return af}});var n={};var l=function(af,ag){n[af.attr("id")]=ag};var m=function(af,ah,aj,ai){aj=".group";var ag={};n[af.attr("id")]=ai;af.bind("drag",{handle:"."+ah,relative:true},function(ar,at){var aq=$(this);var aw=$(this).parent(),an=aw.children(),ap=n[$(this).attr("id")],am,al,au,ak,ao;al=$(this).parents(aj);if(al.length!==0){au=al.position().top;ak=au+al.outerHeight();if(at.offsetY<au){$(this).insertBefore(al);var av=n[al.attr("id")];av.remove_drawable(ap);av.container.add_drawable_before(ap,av);return}else{if(at.offsetY>ak){$(this).insertAfter(al);var av=n[al.attr("id")];av.remove_drawable(ap);av.container.add_drawable(ap);return}}}al=null;for(ao=0;ao<an.length;ao++){am=$(an.get(ao));au=am.position().top;ak=au+am.outerHeight();if(am.is(aj)&&this!==am.get(0)&&at.offsetY>=au&&at.offsetY<=ak){if(at.offsetY-au<ak-at.offsetY){am.find(".content-div").prepend(this)}else{am.find(".content-div").append(this)}if(ap.container){ap.container.remove_drawable(ap)}n[am.attr("id")].add_drawable(ap);return}}for(ao=0;ao<an.length;ao++){if(at.offsetY<$(an.get(ao)).position().top){break}}if(ao===an.length){if(this!==an.get(ao-1)){aw.append(this);n[aw.attr("id")].move_drawable(ap,ao)}}else{if(this!==an.get(ao)){$(this).insertBefore(an.get(ao));n[aw.attr("id")].move_drawable(ap,(at.deltaY>0?ao-1:ao))}}}).bind("dragstart",function(){ag["border-top"]=af.css("border-top");ag["border-bottom"]=af.css("border-bottom");$(this).css({"border-top":"1px solid blue","border-bottom":"1px solid blue"})}).bind("dragend",function(){$(this).css(ag)})};X.moveable=m;var ad=16,H=9,E=20,T=H+2,z=100,J=12000,R=200,C=5,v=10,L=5000,w=100,o="There was an error in indexing this dataset. ",K="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",F="No data for this chrom/contig.",t="Currently indexing... please wait",x="Tool cannot be rerun: ",a="Loading data...",Y="Ready for display",d=10,u=5,B=5;function Z(ag,af){if(!af){af=0}var ah=Math.pow(10,af);return Math.round(ag*ah)/ah}var c=function(af){this.num_elements=af;this.clear()};p(c.prototype,{get:function(ag){var af=this.key_ary.indexOf(ag);if(af!==-1){if(this.obj_cache[ag].stale){this.key_ary.splice(af,1);delete this.obj_cache[ag]}else{this.move_key_to_end(ag,af)}}return this.obj_cache[ag]},set:function(ag,ah){if(!this.obj_cache[ag]){if(this.key_ary.length>=this.num_elements){var af=this.key_ary.shift();delete this.obj_cache[af]}this.key_ary.push(ag)}this.obj_cache[ag]=ah;return ah},move_key_to_end:function(ag,af){this.key_ary.splice(af,1);this.key_ary.push(ag)},clear:function(){this.obj_cache={};this.key_ary=[]},size:function(){return this.key_ary.length}});var S=function(ag,af,ah){c.call(this,ag);this.track=af;this.subset=(ah!==undefined?ah:true)};p(S.prototype,c.prototype,{load_data:function(ao,aj,am,ag,al){var an=this.track.view.chrom,ai={chrom:an,low:ao,high:aj,mode:am,resolution:ag,dataset_id:this.track.dataset_id,hda_ldda:this.track.hda_ldda};$.extend(ai,al);if(this.track.filters_manager){var ap=[];var af=this.track.filters_manager.filters;for(var ak=0;ak<af.length;ak++){ap[ap.length]=af[ak].name}ai.filter_cols=JSON.stringify(ap)}var ah=this;return $.getJSON(this.track.data_url,ai,function(aq){ah.set_data(ao,aj,am,aq)})},get_data:function(af,aj,ak,ag,ai){var ah=this.get_data_from_cache(af,aj,ak);if(ah){return ah}ah=this.load_data(af,aj,ak,ag,ai);this.set_data(af,aj,ak,ah);return ah},DEEP_DATA_REQ:"deep",BROAD_DATA_REQ:"breadth",get_more_data:function(an,ai,am,ah,al,aj){var ao=this.get_data_from_cache(an,ai,am);if(!ao){console.log("ERROR: no current data for: ",this.track,an,ai,am,ah,al);return}ao.stale=true;var ag=an;if(aj===this.DEEP_DATA_REQ){$.extend(al,{start_val:ao.data.length+1})}else{if(aj===this.BROAD_DATA_REQ){ag=(ao.max_high?ao.max_high:ao.data[ao.data.length-1][2])+1}}var af=this,ak=this.load_data(ag,ai,am,ah,al);new_data_available=$.Deferred();this.set_data(an,ai,am,new_data_available);$.when(ak).then(function(ap){if(ap.data){ap.data=ao.data.concat(ap.data);if(ap.max_low){ap.max_low=ao.max_low}if(ap.message){ap.message=ap.message.replace(/[0-9]+/,ap.data.length)}}af.set_data(an,ai,am,ap);new_data_available.resolve(ap)});return new_data_available},get_data_from_cache:function(af,ag,ah){return this.get(this.gen_key(af,ag,ah))},set_data:function(ag,ah,ai,af){return this.set(this.gen_key(ag,ah,ai),af)},gen_key:function(af,ah,ai){var ag=af+"_"+ah+"_"+ai;return ag},split_key:function(af){return af.split("_")}});var I=function(ag,af,ah){S.call(this,ag,af,ah)};p(I.prototype,S.prototype,c.prototype,{load_data:function(af,ai,aj,ag,ah){if(ag>1){return{data:null}}return S.prototype.load_data.call(this,af,ai,aj,ag,ah)}});var q=function(ai,ag,af,ah,ak){if(!q.id_counter){q.id_counter=0}this.id=q.id_counter++;this.name=ai;this.view=ag;this.container=af;this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:ai}],saved_values:ah,onchange:function(){this.track.set_name(this.track.config.values.name)}});this.prefs=this.config.values;this.drag_handle_class=ak;this.is_overview=false;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=this.build_icons_div().hide();this.header_div.append(this.icons_div);this.header_div.dblclick(function(al){al.stopPropagation()});var aj=this;this.container_div.hover(function(){aj.icons_div.show()},function(){aj.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};p(q.prototype,{init:function(){},request_draw:function(){},_draw:function(){},to_json:function(){},update_icons:function(){},set_name:function(af){this.old_name=this.name;this.name=af;this.name_div.text(this.name)},revert_name:function(){this.name=this.old_name;this.name_div.text(this.name)},remove:function(){this.container.remove_drawable(this);this.container_div.hide(0,function(){$(this).remove();view.update_intro_div();view.has_changes=true})},build_container_div:function(){},build_header_div:function(){},build_icons_div:function(){},update_icons:function(){},hide_contents:function(){},show_contents:function(){}});var y=function(aj,ai,ag,af,ah,ak){q.call(this,ai,ag,af,ah,ak);this.obj_type=aj;this.drawables=[]};p(y.prototype,q.prototype,{init:function(){for(var af=0;af<this.drawables.length;af++){this.drawables[af].init()}},_draw:function(){for(var af=0;af<this.drawables.length;af++){this.drawables[af]._draw()}},to_json:function(){var ag=[];for(var af=0;af<this.drawables.length;af++){ag.push(this.drawables[af].to_json())}return{name:this.name,prefs:this.prefs,obj_type:this.obj_type,drawables:ag}},add_drawable:function(af){this.drawables.push(af);af.container=this},add_drawable_before:function(ah,af){var ag=this.drawables.indexOf(af);if(ag!=-1){this.drawables.splice(ag,0,ah);return true}return false},remove_drawable:function(ag){var af=this.drawables.indexOf(ag);if(af!=-1){this.drawables.splice(af,1);ag.container=null;return true}return false},move_drawable:function(ag,ah){var af=this.drawables.indexOf(ag);if(af!=-1){this.drawables.splice(af,1);this.drawables.splice(ah,0,ag);return true}return false}});var Q=function(ai,ag,af,ah){y.call(this,"DrawableGroup",ai,ag,af,ah,"group-handle");this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);l(this.container_div,this);l(this.content_div,this);m(this.container_div,this.drag_handle_class,".group",this)};p(Q.prototype,q.prototype,y.prototype,{build_container_div:function(){return $("<div/>").addClass("group").attr("id","group_"+this.id).appendTo(this.container.content_div)},build_header_div:function(){var af=$("<div/>").addClass("track-header");af.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.name).appendTo(af);return af},build_icons_div:function(){var af=$("<div/>").css("float","left");this.toggle_icon=$("<a/>").attr("href","javascript:void(0);").attr("title","Hide/show group content").addClass("icon-button toggle").tipsy({gravity:"s"}).appendTo(af);this.settings_icon=$("<a/>").attr("href","javascript:void(0);").attr("title","Edit settings").addClass("icon-button settings-icon").tipsy({gravity:"s"}).appendTo(af);this.remove_icon=$("<a/>").attr("href","javascript:void(0);").attr("title","Remove").addClass("icon-button remove-icon").tipsy({gravity:"s"}).appendTo(af);var ag=this;this.toggle_icon.click(function(){if(ag.content_visible){ag.toggle_icon.addClass("toggle-expand").removeClass("toggle");ag.hide_contents();ag.content_visible=false}else{ag.toggle_icon.addClass("toggle").removeClass("toggle-expand");ag.content_visible=true;ag.show_contents()}});this.settings_icon.click(function(){var aj=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},ah=function(){ag.config.update_from_form($(".dialog-box"));hide_modal();$(window).unbind("keypress.check_enter_esc")},ai=function(ak){if((ak.keyCode||ak.which)===27){aj()}else{if((ak.keyCode||ak.which)===13){ah()}}};$(window).bind("keypress.check_enter_esc",ai);show_modal("Configure Group",ag.config.build_form(),{Cancel:aj,OK:ah})});this.remove_icon.click(function(){$(".tipsy").remove();ag.remove()});return af},hide_contents:function(){this.content_div.hide()},show_contents:function(){this.content_div.show();this.request_draw()}});var ac=function(af,ai,ah,ag){y.call(this,"View");this.container=af;this.chrom=null;this.vis_id=ah;this.dbkey=ag;this.title=ai;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.init();this.canvas_manager=new ae(af.get(0).ownerDocument);this.reset()};p(ac.prototype,y.prototype,{init:function(){var ah=this.container,af=this;this.top_container=$("<div/>").addClass("top-container").appendTo(ah);this.browser_content_div=$("<div/>").addClass("content").css("position","relative").appendTo(ah);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(ah);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;l(this.viewport_container,af);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var ai=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){add_tracks()});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("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").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ag=function(aj){if(aj.type==="focusout"||(aj.keyCode||aj.which)===13||(aj.keyCode||aj.which)===27){if((aj.keyCode||aj.which)!==27){af.go_to($(this).val())}$(this).hide();$(this).val("");af.location_span.show();af.chrom_select.show()}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ag).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("original-title","Click to change location").tipsy({gravity:"n"}).appendTo(this.nav_controls);this.location_span.click(function(){af.location_span.hide();af.chrom_select.hide();af.nav_input.val(af.chrom+":"+af.low+"-"+af.high);af.nav_input.css("display","inline-block");af.nav_input.select();af.nav_input.focus()});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").tipsy({gravity:"n"}).click(function(){af.zoom_out();af.request_redraw()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tipsy({gravity:"n"}).click(function(){af.zoom_in();af.request_redraw()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){af.change_chrom(af.chrom_select.val())});this.browser_content_div.click(function(aj){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(aj){af.zoom_in(aj.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(aj,ak){this.current_x=ak.offsetX}).bind("drag",function(aj,al){var am=al.offsetX-this.current_x;this.current_x=al.offsetX;var ak=Math.round(am/af.viewport_container.width()*(af.max_high-af.max_low));af.move_delta(-ak)});this.overview_close.click(function(){af.reset_overview()});this.viewport_container.bind("draginit",function(aj,ak){if(aj.clientX>af.viewport_container.width()-16){return false}}).bind("dragstart",function(aj,ak){ak.original_low=af.low;ak.current_height=aj.clientY;ak.current_x=ak.offsetX}).bind("drag",function(al,an){var aj=$(this);var ao=an.offsetX-an.current_x;var ak=aj.scrollTop()-(al.clientY-an.current_height);aj.scrollTop(ak);an.current_height=al.clientY;an.current_x=an.offsetX;var am=Math.round(ao/af.viewport_container.width()*(af.high-af.low));af.move_delta(am)}).bind("mousewheel",function(al,an,ak,aj){if(ak){ak*=50;var am=Math.round(-ak/af.viewport_container.width()*(af.high-af.low));af.move_delta(am)}});this.top_labeltrack.bind("dragstart",function(aj,ak){return $("<div />").css({height:af.browser_content_div.height()+af.top_labeltrack.height()+af.nav_labeltrack.height()+1,top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag",function(an,ao){$(ao.proxy).css({left:Math.min(an.pageX,ao.startX),width:Math.abs(an.pageX-ao.startX)});var ak=Math.min(an.pageX,ao.startX)-af.container.offset().left,aj=Math.max(an.pageX,ao.startX)-af.container.offset().left,am=(af.high-af.low),al=af.viewport_container.width();af.update_location(Math.round(ak/al*am)+af.low,Math.round(aj/al*am)+af.low)}).bind("dragend",function(ao,ap){var ak=Math.min(ao.pageX,ap.startX),aj=Math.max(ao.pageX,ap.startX),am=(af.high-af.low),al=af.viewport_container.width(),an=af.low;af.low=Math.round(ak/al*am)+an;af.high=Math.round(aj/al*am)+an;$(ap.proxy).remove();af.request_redraw()});this.add_label_track(new ab(this,{content_div:this.top_labeltrack}));this.add_label_track(new ab(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){af.resize_window()});$(document).bind("redraw",function(){af.redraw()});this.reset();$(window).trigger("resize")},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},update_location:function(af,ag){this.location_span.text(commatize(af)+" - "+commatize(ag));this.nav_input.val(this.chrom+":"+commatize(af)+"-"+commatize(ag))},load_chroms:function(ah){ah.num=w;$.extend(ah,(this.vis_id!==undefined?{vis_id:this.vis_id}:{dbkey:this.dbkey}));var af=this,ag=$.Deferred();$.ajax({url:chrom_url,data:ah,dataType:"json",success:function(aj){if(aj.chrom_info.length===0){alert("Invalid chromosome: "+ah.chrom);return}if(aj.reference){af.add_label_track(new A(af))}af.chrom_data=aj.chrom_info;var am='<option value="">Select Chrom/Contig</option>';for(var al=0,ai=af.chrom_data.length;al<ai;al++){var ak=af.chrom_data[al].chrom;am+='<option value="'+ak+'">'+ak+"</option>"}if(aj.prev_chroms){am+='<option value="previous">Previous '+w+"</option>"}if(aj.next_chroms){am+='<option value="next">Next '+w+"</option>"}af.chrom_select.html(am);af.chrom_start_index=aj.start_index;ag.resolve(aj)},error:function(){alert("Could not load chroms for this dbkey:",af.dbkey)}});return ag},change_chrom:function(ak,ag,am){if(!ak||ak==="None"){return}var ah=this;if(ak==="previous"){ah.load_chroms({low:this.chrom_start_index-w});return}if(ak==="next"){ah.load_chroms({low:this.chrom_start_index+w});return}var al=$.grep(ah.chrom_data,function(an,ao){return an.chrom===ak})[0];if(al===undefined){ah.load_chroms({chrom:ak},function(){ah.change_chrom(ak,ag,am)});return}else{if(ak!==ah.chrom){ah.chrom=ak;ah.chrom_select.val(ah.chrom);ah.max_high=al.len-1;ah.reset();ah.request_redraw(true);for(var aj=0,af=ah.drawables.length;aj<af;aj++){var ai=ah.drawables[aj];if(ai.init){ai.init()}}}if(ag!==undefined&&am!==undefined){ah.low=Math.max(ag,0);ah.high=Math.min(am,ah.max_high)}ah.reset_overview();ah.request_redraw()}},go_to:function(aj){aj=aj.replace(/ |,/g,"");var an=this,af,ai,ag=aj.split(":"),al=ag[0],am=ag[1];if(am!==undefined){try{var ak=am.split("-");af=parseInt(ak[0],10);ai=parseInt(ak[1],10)}catch(ah){return false}}an.change_chrom(al,af,ai)},move_fraction:function(ah){var af=this;var ag=af.high-af.low;this.move_delta(ah*ag)},move_delta:function(ah){var af=this;var ag=af.high-af.low;if(af.low-ah<af.max_low){af.low=af.max_low;af.high=af.max_low+ag}else{if(af.high-ah>af.max_high){af.high=af.max_high;af.low=af.max_high-ag}else{af.high-=ah;af.low-=ah}}af.request_redraw()},add_drawable:function(af){y.prototype.add_drawable.call(this,af);af.init();this.has_changes=true;this.update_intro_div()},add_label_track:function(af){af.view=this;af.init();this.label_tracks.push(af)},remove_drawable:function(ah,ag){y.prototype.remove_drawable.call(this,ah);if(ag){var af=this;ah.container_div.hide(0,function(){$(this).remove();af.update_intro_div()});this.has_changes=true}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(an,af,am,ag){var al=this,aj=(ag?[ag]:al.drawables),ah;var ag;for(var ak=0;ak<aj.length;ak++){ag=aj[ak];ah=-1;for(var ai=0;ai<al.tracks_to_be_redrawn.length;ai++){if(al.tracks_to_be_redrawn[ai][0]===ag){ah=ai;break}}if(ah<0){al.tracks_to_be_redrawn.push([ag,af,am])}else{al.tracks_to_be_redrawn[ak][1]=af;al.tracks_to_be_redrawn[ak][2]=am}}requestAnimationFrame(function(){al._redraw(an)})},_redraw:function(ap){var am=this.low,ai=this.high;if(am<this.max_low){am=this.max_low}if(ai>this.max_high){ai=this.max_high}var ao=this.high-this.low;if(this.high!==0&&ao<this.min_separation){ai=am+this.min_separation}this.low=Math.floor(am);this.high=Math.ceil(ai);this.resolution=Math.pow(C,Math.ceil(Math.log((this.high-this.low)/R)/Math.log(C)));this.zoom_res=Math.pow(v,Math.max(0,Math.ceil(Math.log(this.resolution,v)/Math.log(v))));var af=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var al=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var aq=13;this.overview_box.css({left:af,width:Math.max(aq,al)}).show();if(al<aq){this.overview_box.css("left",af-(aq-al)/2)}if(this.overview_highlight){this.overview_highlight.css({left:af,width:al})}this.update_location(this.low,this.high);if(!ap){var ah,ag,an;for(var aj=0,ak=this.tracks_to_be_redrawn.length;aj<ak;aj++){ah=this.tracks_to_be_redrawn[aj][0];ag=this.tracks_to_be_redrawn[aj][1];an=this.tracks_to_be_redrawn[aj][2];if(ah){ah._draw(ag,an)}}this.tracks_to_be_redrawn=[];for(aj=0,ak=this.label_tracks.length;aj<ak;aj++){this.label_tracks[aj]._draw()}}},zoom_in:function(ag,ah){if(this.max_high===0||this.high-this.low<this.min_separation){return}var ai=this.high-this.low,aj=ai/2+this.low,af=(ai/this.zoom_factor)/2;if(ag){aj=ag/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(aj-af);this.high=Math.round(aj+af);this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ag=this.high-this.low,ah=ag/2+this.low,af=(ag*this.zoom_factor)/2;this.low=Math.round(ah-af);this.high=Math.round(ah+af);this.request_redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.nav_container.width(this.container.width());this.request_redraw()},set_overview:function(ah){if(this.overview_drawable){if(this.overview_drawable.dataset_id===ah.dataset_id){return}this.overview_viewport.find(".track").remove()}var ag=ah.copy({content_div:this.overview_viewport}),af=this;ag.header_div.hide();ag.is_overview=true;af.overview_drawable=ag;this.overview_drawable.postdraw_actions=function(){af.overview_highlight.show().height(af.overview_drawable.content_div.height());af.overview_viewport.height(af.overview_drawable.content_div.height()+af.overview_box.outerHeight());af.overview_close.show();af.resize_window()};this.overview_drawable.init();af.has_changes=true},reset_overview:function(){$(".tipsy").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 r=function(ah,al){this.track=ah;this.name=al.name;this.params=[];var at=al.params;for(var ai=0;ai<at.length;ai++){var an=at[ai],ag=an.name,ar=an.label,aj=unescape(an.html),au=an.value,ap=an.type;if(ap==="number"){this.params[this.params.length]=new g(ag,ar,aj,au,an.min,an.max)}else{if(ap=="select"){this.params[this.params.length]=new O(ag,ar,aj,au)}else{console.log("WARNING: unrecognized tool parameter type:",ag,ap)}}}this.parent_div=$("<div/>").addClass("dynamic-tool").hide();this.parent_div.bind("drag",function(aw){aw.stopPropagation()}).click(function(aw){aw.stopPropagation()}).bind("dblclick",function(aw){aw.stopPropagation()});var aq=$("<div class='tool-name'>").appendTo(this.parent_div).text(this.name);var ao=this.params;var am=this;$.each(this.params,function(ax,aA){var az=$("<div>").addClass("param-row").appendTo(am.parent_div);var aw=$("<div>").addClass("param-label").text(aA.label).appendTo(az);var ay=$("<div/>").addClass("slider").html(aA.html).appendTo(az);ay.find(":input").val(aA.value);$("<div style='clear: both;'/>").appendTo(az)});this.parent_div.find("input").click(function(){$(this).select()});var av=$("<div>").addClass("param-row").appendTo(this.parent_div);var ak=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(av);var af=$("<input type='submit'>").attr("value","Run on visible region").css("margin-left","3em").appendTo(av);var am=this;af.click(function(){am.run_on_region()});ak.click(function(){am.run_on_dataset()})};p(r.prototype,{get_param_values_dict:function(){var af={};this.parent_div.find(":input").each(function(){var ag=$(this).attr("name"),ah=$(this).val();af[ag]=JSON.stringify(ah)});return af},get_param_values:function(){var ag=[];var af={};this.parent_div.find(":input").each(function(){var ah=$(this).attr("name"),ai=$(this).val();if(ah){ag[ag.length]=ai}});return ag},run_on_dataset:function(){var af=this;af.run({dataset_id:this.track.original_dataset_id,tool_id:af.name},null,function(ag){show_modal(af.name+" is Running",af.name+" is running on the complete dataset. Tool outputs are in dataset's history.",{Close:hide_modal})})},run_on_region:function(){var ag={dataset_id:this.track.original_dataset_id,chrom:this.track.view.chrom,low:this.track.view.low,high:this.track.view.high,tool_id:this.name},aj=this.track,ah=ag.tool_id+aj.tool_region_and_parameters_str(ag.chrom,ag.low,ag.high),af;if(aj.container===view){var ai=new Q(this.name,this.track.view,this.track.container);aj.container.add_drawable(ai);aj.container.remove_drawable(aj);ai.add_drawable(aj);aj.container_div.appendTo(ai.content_div);af=ai}else{af=aj.container}var ak=new aj.constructor(ah,view,af,"hda");ak.init_for_tool_data();ak.change_mode(aj.mode);af.add_drawable(ak);ak.content_div.text("Starting job.");this.run(ag,ak,function(al){ak.dataset_id=al.dataset_id;ak.content_div.text("Running job.");ak.init()})},run:function(ag,ah,ai){$.extend(ag,this.get_param_values_dict());var af=function(){$.getJSON(rerun_tool_url,ag,function(aj){if(aj==="no converter"){ah.container_div.addClass("error");ah.content_div.text(K)}else{if(aj.error){ah.container_div.addClass("error");ah.content_div.text(x+aj.message)}else{if(aj==="pending"){ah.container_div.addClass("pending");ah.content_div.text("Converting input data so that it can be used quickly with tool.");setTimeout(af,2000)}else{ai(aj)}}}})};af()}});var O=function(ag,af,ah,ai){this.name=ag;this.label=af;this.html=ah;this.value=ai};var g=function(ah,ag,aj,ak,ai,af){O.call(this,ah,ag,aj,ak);this.min=ai;this.max=af};var h=function(ag,af,ah,ai){this.name=ag;this.index=af;this.tool_id=ah;this.tool_exp_name=ai};var V=function(ag,af,ah,ai){h.call(this,ag,af,ah,ai);this.low=-Number.MAX_VALUE;this.high=Number.MAX_VALUE;this.min=Number.MAX_VALUE;this.max=-Number.MAX_VALUE;this.container=null;this.slider=null;this.slider_label=null};p(V.prototype,{applies_to:function(af){if(af.length>this.index){return true}return false},keep:function(af){if(!this.applies_to(af)){return true}var ag=af[this.index];return(isNaN(ag)||(ag>=this.low&&ag<=this.high))},update_attrs:function(ag){var af=false;if(!this.applies_to(ag)){return af}if(ag[this.index]<this.min){this.min=Math.floor(ag[this.index]);af=true}if(ag[this.index]>this.max){this.max=Math.ceil(ag[this.index]);af=true}return af},update_ui_elt:function(){if(this.min!=this.max){this.container.show()}else{this.container.hide()}var ah=function(ak,ai){var aj=ai-ak;return(aj<=2?0.01:1)};var ag=this.slider.slider("option","min"),af=this.slider.slider("option","max");if(this.min<ag||this.max>af){this.slider.slider("option","min",this.min);this.slider.slider("option","max",this.max);this.slider.slider("option","step",ah(this.min,this.max));this.slider.slider("option","values",[this.min,this.max])}}});var aa=function(aq,ay){this.track=aq;this.filters=[];for(var at=0;at<ay.length;at++){var au=ay[at],az=au.name,af=au.type,ah=au.index,ax=au.tool_id,aw=au.tool_exp_name;if(af==="int"||af==="float"){this.filters[at]=new V(az,ah,ax,aw)}else{console.log("ERROR: unsupported filter: ",az,af)}}var ai=function(aA,aB,aC){aA.click(function(){var aD=aB.text();max=parseFloat(aC.slider("option","max")),input_size=(max<=1?4:max<=1000000?max.toString().length:6),multi_value=false;if(aC.slider("option","values")){input_size=2*input_size+1;multi_value=true}aB.text("");$("<input type='text'/>").attr("size",input_size).attr("maxlength",input_size).attr("value",aD).appendTo(aB).focus().select().click(function(aE){aE.stopPropagation()}).blur(function(){$(this).remove();aB.text(aD)}).keyup(function(aI){if(aI.keyCode===27){$(this).trigger("blur")}else{if(aI.keyCode===13){var aG=aC.slider("option","min"),aE=aC.slider("option","max"),aH=function(aJ){return(isNaN(aJ)||aJ>aE||aJ<aG)},aF=$(this).val();if(!multi_value){aF=parseFloat(aF);if(aH(aF)){alert("Parameter value must be in the range ["+aG+"-"+aE+"]");return $(this)}}else{aF=aF.split("-");aF=[parseFloat(aF[0]),parseFloat(aF[1])];if(aH(aF[0])||aH(aF[1])){alert("Parameter value must be in the range ["+aG+"-"+aE+"]");return $(this)}}aC.slider((multi_value?"values":"value"),aF)}}})})};this.parent_div=$("<div/>").addClass("filters").hide();this.parent_div.bind("drag",function(aA){aA.stopPropagation()}).click(function(aA){aA.stopPropagation()}).bind("dblclick",function(aA){aA.stopPropagation()}).bind("keydown",function(aA){aA.stopPropagation()});var av=$("<div/>").addClass("sliders").appendTo(this.parent_div);var an=this;$.each(this.filters,function(aD,aF){aF.container=$("<div/>").addClass("filter-row slider-row").appendTo(av);var aE=$("<div/>").addClass("elt-label").appendTo(aF.container);var aC=$("<span/>").addClass("slider-name").text(aF.name+" ").appendTo(aE);var aB=$("<span/>");var aH=$("<span/>").addClass("slider-value").appendTo(aE).append("[").append(aB).append("]");var aA=$("<div/>").addClass("slider").appendTo(aF.container);aF.control_element=$("<div/>").attr("id",aF.name+"-filter-control").appendTo(aA);var aG=[0,0];aF.control_element.slider({range:true,min:Number.MAX_VALUE,max:-Number.MIN_VALUE,values:[0,0],slide:function(aJ,aK){var aI=aK.values;aB.text(aI[0]+"-"+aI[1]);aF.low=aI[0];aF.high=aI[1];an.track.request_draw(true,true)},change:function(aI,aJ){aF.control_element.slider("option","slide").call(aF.control_element,aI,aJ)}});aF.slider=aF.control_element;aF.slider_label=aB;ai(aH,aB,aF.control_element);$("<div style='clear: both;'/>").appendTo(aF.container)});if(this.filters.length!==0){var ak=$("<div/>").addClass("param-row").appendTo(av);var am=$("<input type='submit'/>").attr("value","Run on complete dataset").appendTo(ak);var ag=this;am.click(function(){ag.run_on_dataset()})}var ap=$("<div/>").addClass("display-controls").appendTo(this.parent_div),ar,al,ao,aj={Transparency:function(aA){an.alpha_filter=aA},Height:function(aA){an.height_filter=aA}};$.each(aj,function(aC,aB){ar=$("<div/>").addClass("filter-row").appendTo(ap),al=$("<span/>").addClass("elt-label").text(aC+":").appendTo(ar),ao=$("<select/>").attr("name",aC+"_dropdown").css("float","right").appendTo(ar);$("<option/>").attr("value",-1).text("== None ==").appendTo(ao);for(var aA=0;aA<an.filters.length;aA++){$("<option/>").attr("value",aA).text(an.filters[aA].name).appendTo(ao)}ao.change(function(){$(this).children("option:selected").each(function(){var aD=parseInt($(this).val());aj[aC]((aD>=0?an.filters[aD]:null));an.track.request_draw(true,true)})});$("<div style='clear: both;'/>").appendTo(ar)});$("<div style='clear: both;'/>").appendTo(this.parent_div)};p(aa.prototype,{reset_filters:function(){for(var af=0;af<this.filters.length;af++){filter=this.filters[af];filter.slider.slider("option","values",[filter.min,filter.max])}this.alpha_filter=null;this.height_filter=null},run_on_dataset:function(){var an=function(ar,ap,aq){if(!(ap in ar)){ar[ap]=aq}return ar[ap]};var ah={},af,ag,ai;for(var aj=0;aj<this.filters.length;aj++){af=this.filters[aj];if(af.tool_id){if(af.min!=af.low){ag=an(ah,af.tool_id,[]);ag[ag.length]=af.tool_exp_name+" >= "+af.low}if(af.max!=af.high){ag=an(ah,af.tool_id,[]);ag[ag.length]=af.tool_exp_name+" <= "+af.high}}}var al=[];for(var ao in ah){al[al.length]=[ao,ah[ao]]}var am=al.length;(function ak(aw,at){var aq=at[0],ar=aq[0],av=aq[1],au="("+av.join(") and (")+")",ap={cond:au,input:aw,target_dataset_id:aw,tool_id:ar},at=at.slice(1);$.getJSON(run_tool_url,ap,function(ax){if(ax.error){show_modal("Filter Dataset","Error running tool "+ar,{Close:hide_modal})}else{if(at.length===0){show_modal("Filtering Dataset","Filter(s) are running on the complete dataset. Outputs are in dataset's history.",{Close:hide_modal})}else{ak(ax.dataset_id,at)}}})})(this.track.dataset_id,al)}});var D=function(af,ag){M.Scaler.call(this,ag);this.filter=af};D.prototype.gen_val=function(af){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(af[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var G=function(af){this.track=af.track;this.params=af.params;this.values={};this.restore_values((af.saved_values?af.saved_values:{}));this.onchange=af.onchange};p(G.prototype,{restore_values:function(af){var ag=this;$.each(this.params,function(ah,ai){if(af[ai.key]!==undefined){ag.values[ai.key]=af[ai.key]}else{ag.values[ai.key]=ai.default_value}})},build_form:function(){var ai=this;var af=$("<div />");var ah;function ag(am,aj){for(var ao=0;ao<am.length;ao++){ah=am[ao];if(ah.hidden){continue}var ak="param_"+ao;var at=ai.values[ah.key];var av=$("<div class='form-row' />").appendTo(aj);av.append($("<label />").attr("for",ak).text(ah.label+":"));if(ah.type==="bool"){av.append($('<input type="checkbox" />').attr("id",ak).attr("name",ak).attr("checked",at))}else{if(ah.type==="text"){av.append($('<input type="text"/>').attr("id",ak).val(at).click(function(){$(this).select()}))}else{if(ah.type=="select"){var aq=$("<select />").attr("id",ak);for(var an=0;an<ah.options.length;an++){$("<option/>").text(ah.options[an].label).attr("value",ah.options[an].value).appendTo(aq)}aq.val(at);av.append(aq)}else{if(ah.type==="color"){var ap=$("<input />").attr("id",ak).attr("name",ak).val(at);var ar=$("<div class='tipsy tipsy-west' style='position: absolute;' />").hide();var al=$("<div style='background-color: black; padding: 10px;'></div>").appendTo(ar);var au=$("<div/>").appendTo(al).farbtastic({width:100,height:100,callback:ap,color:at});$("<div />").append(ap).append(ar).appendTo(av).bind("click",function(aw){ar.css({left:$(this).position().left+$(ap).width()+5,top:$(this).position().top-($(ar).height()/2)+($(ap).height()/2)}).show();$(document).bind("click.color-picker",function(){ar.hide();$(document).unbind("click.color-picker")});aw.stopPropagation()})}else{av.append($("<input />").attr("id",ak).attr("name",ak).val(at))}}}}if(ah.help){av.append($("<div class='help'/>").text(ah.help))}}}ag(this.params,af);return af},update_from_form:function(af){var ah=this;var ag=false;$.each(this.params,function(ai,ak){if(!ak.hidden){var al="param_"+ai;var aj=af.find("#"+al).val();if(ak.type==="float"){aj=parseFloat(aj)}else{if(ak.type==="int"){aj=parseInt(aj)}else{if(ak.type==="bool"){aj=af.find("#"+al).is(":checked")}}}if(aj!==ah.values[ak.key]){ah.values[ak.key]=aj;ag=true}}});if(ag){this.onchange()}}});var b=function(af,ai,ah,ag,aj){this.track=af;this.index=ai;this.low=ai*R*ah;this.high=(ai+1)*R*ah;this.resolution=ah;this.canvas=$("<div class='track-tile'/>").append(ag);this.data=aj;this.stale=false};b.prototype.predisplay_actions=function(){};var k=function(af,ai,ah,ag,aj,ak){b.call(this,af,ai,ah,ag,aj);this.max_val=ak};p(k.prototype,b.prototype);var P=function(ah,am,ai,ag,ak,al,ap,an){b.call(this,ah,am,ai,ag,ak);this.mode=al;this.message=ap;this.feature_mapper=an;if(this.message){var ag=this.canvas.children()[0],ao=$("<div/>").addClass("tile-message").text(this.message).css({height:E-1,width:ag.width}).prependTo(this.canvas),aj=$("<a href='javascript:void(0);'/>").addClass("icon more-down").appendTo(ao),af=$("<a href='javascript:void(0);'/>").addClass("icon more-across").appendTo(ao);aj.click(function(){tile.stale=true;ah.data_manager.get_more_data(tile.low,tile.high,ah.mode,tile.resolution,{},ah.data_manager.DEEP_DATA_REQ);ah.request_draw()}).dblclick(function(aq){aq.stopPropagation()});af.click(function(){tile.stale=true;ah.data_manager.get_more_data(tile.low,tile.high,ah.mode,tile.resolution,{},ah.data_manager.BROAD_DATA_REQ);ah.request_draw()}).dblclick(function(aq){aq.stopPropagation()})}};p(P.prototype,b.prototype);P.prototype.predisplay_actions=function(){var ag=this,af={};if(ag.mode!=="Pack"){return}$(this.canvas).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).siblings(".feature-popup").remove()}).mousemove(function(ar){if(!this.hovered){return}var am=$(this).offset(),aq=ar.pageX-am.left,ap=ar.pageY-am.top,aw=ag.feature_mapper.get_feature_data(aq,ap),an=(aw?aw[0]:null);$(this).siblings(".feature-popup").each(function(){if(!an||$(this).attr("id")!==an.toString()){$(this).remove()}});if(aw){var ai=af[an];if(!ai){var an=aw[0],at={name:aw[3],start:aw[1],end:aw[2],strand:aw[4]},al=ag.track.filters_manager.filters,ak;for(var ao=0;ao<al.length;ao++){ak=al[ao];at[ak.name]=aw[ak.index]}var ai=$("<div/>").attr("id",an).addClass("feature-popup"),ax=$("<table/>"),av,au,ay;for(av in at){au=at[av];ay=$("<tr/>").appendTo(ax);$("<th/>").appendTo(ay).text(av);$("<td/>").attr("align","left").appendTo(ay).text(typeof(au)=="number"?Z(au,2):au)}ai.append($("<div class='feature-popup-inner'>").append(ax));af[an]=ai}ai.appendTo($(ag.canvas).parent());var aj=aq+parseInt(ag.canvas.css("left"))-ai.width()/2,ah=ap+parseInt(ag.canvas.css("top"))+7;ai.css("left",aj+"px").css("top",ah+"px")}else{if(!ar.isPropagationStopped()){ar.stopPropagation();$(this).siblings().each(function(){$(this).trigger(ar)})}}}).mouseleave(function(){$(this).siblings(".feature-popup").remove()})};var i=function(ai,ag,af,ah,aj,ak){q.call(this,ai,ag,af,{},"draghandle");this.data_url=(aj?aj:default_data_url);this.data_url_extra_params={};this.data_query_wait=(ak?ak:L);this.dataset_check_url=converted_datasets_state_url;if(!i.id_counter){i.id_counter=0}this.id=i.id_counter++;this.content_div=$("<div class='track-content'>").appendTo(this.container_div);this.container.content_div.append(this.container_div)};p(i.prototype,q.prototype,{build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var af=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(af)}this.name_div=$("<div/>").addClass("track-name").appendTo(af).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return af},build_icons_div:function(){var aj=$("<div/>").css("float","left");this.mode_icon=$("<a/>").attr("href","javascript:void(0);").attr("title","Set display mode").addClass("icon-button chevron-expand").tipsy({gravity:"s"}).appendTo(aj);this.toggle_icon=$("<a/>").attr("href","javascript:void(0);").attr("title","Hide/show track content").addClass("icon-button toggle").tipsy({gravity:"s"}).appendTo(aj);this.settings_icon=$("<a/>").attr("href","javascript:void(0);").attr("title","Edit settings").addClass("icon-button settings-icon").tipsy({gravity:"s"}).appendTo(aj);this.overview_icon=$("<a/>").attr("href","javascript:void(0);").attr("title","Set as overview").addClass("icon-button overview-icon").tipsy({gravity:"s"}).appendTo(aj);this.filters_icon=$("<a/>").attr("href","javascript:void(0);").attr("title","Filters").addClass("icon-button filters-icon").tipsy({gravity:"s"}).appendTo(aj).hide();this.tools_icon=$("<a/>").attr("href","javascript:void(0);").attr("title","Tools").addClass("icon-button tools-icon").tipsy({gravity:"s"}).appendTo(aj).hide();this.remove_icon=$("<a/>").attr("href","javascript:void(0);").attr("title","Remove").addClass("icon-button remove-icon").tipsy({gravity:"s"}).appendTo(aj);var ag=this;if(ag.display_modes!==undefined){var al=(ag.config&&ag.config.values.mode?ag.config.values.mode:ag.display_modes[0]);ag.mode=al;this.mode_icon.attr("title","Set display mode (now: "+ag.mode+")");var ai={};for(var ah=0,af=ag.display_modes.length;ah<af;ah++){var ak=ag.display_modes[ah];ai[ak]=function(am){return function(){ag.change_mode(am);ag.icons_div.show();ag.container_div.mouseleave(function(){ag.icons_div.hide()})}}(ak)}make_popupmenu(this.mode_icon,ai)}this.toggle_icon.click(function(){if(ag.content_visible){ag.toggle_icon.addClass("toggle-expand").removeClass("toggle");ag.hide_contents();ag.content_visible=false}else{ag.toggle_icon.addClass("toggle").removeClass("toggle-expand");ag.content_visible=true;ag.show_contents()}});this.settings_icon.click(function(){var ao=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},am=function(){ag.config.update_from_form($(".dialog-box"));hide_modal();$(window).unbind("keypress.check_enter_esc")},an=function(ap){if((ap.keyCode||ap.which)===27){ao()}else{if((ap.keyCode||ap.which)===13){am()}}};$(window).bind("keypress.check_enter_esc",an);show_modal("Configure Track",ag.config.build_form(),{Cancel:ao,OK:am})});this.overview_icon.click(function(){ag.view.set_overview(ag)});this.filters_icon.click(function(){ag.filters_div.toggle();ag.filters_manager.reset_filters()});this.tools_icon.click(function(){ag.dynamic_tool_div.toggle();if(ag.dynamic_tool_div.is(":visible")){ag.set_name(ag.name+ag.tool_region_and_parameters_str())}else{ag.revert_name()}$(".tipsy").remove()});this.remove_icon.click(function(){$(".tipsy").remove();ag.remove()});return aj},hide_contents:function(){this.content_div.children().remove();this.content_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.content_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof ab){return"LabelTrack"}else{if(this instanceof A){return"ReferenceTrack"}else{if(this instanceof j){return"LineTrack"}else{if(this instanceof W){return"ReadTrack"}else{if(this instanceof U){return"VcfTrack"}else{if(this instanceof e){return"FeatureTrack"}}}}}}return""},init:function(){var af=this;af.enabled=false;af.tile_cache.clear();af.data_manager.clear();af.initial_canvas=undefined;af.content_div.css("height","auto");af.container_div.removeClass("nodata error pending");if(!af.dataset_id){return}$.getJSON(converted_datasets_state_url,{hda_ldda:af.hda_ldda,dataset_id:af.dataset_id,chrom:af.view.chrom},function(ag){if(!ag||ag==="error"||ag.kind==="error"){af.container_div.addClass("error");af.content_div.text(o);if(ag.message){var ah=$(" <a href='javascript:void(0);'></a>").text("View error").click(function(){show_modal("Trackster Error","<pre>"+ag.message+"</pre>",{Close:hide_modal})});af.content_div.append(ah)}}else{if(ag==="no converter"){af.container_div.addClass("error");af.content_div.text(K)}else{if(ag==="no data"||(ag.data!==undefined&&(ag.data===null||ag.data.length===0))){af.container_div.addClass("nodata");af.content_div.text(F)}else{if(ag==="pending"){af.container_div.addClass("pending");af.content_div.text(t);setTimeout(function(){af.init()},af.data_query_wait)}else{if(ag.status==="data"){if(ag.valid_chroms){af.valid_chroms=ag.valid_chroms;af.update_icons()}af.content_div.text(Y);if(af.view.chrom){af.content_div.text("");af.content_div.css("height",af.height_px+"px");af.enabled=true;$.when(af.predraw_init()).done(function(){af.container_div.removeClass("nodata error pending");af.request_draw()})}}}}}}});this.update_icons()},predraw_init:function(){}});var N=function(aj,ah,ag,ai,al,ak){i.call(this,aj,ah,ag,ai);var af=this,ah=af.view;m(af.container_div,af.drag_handle_class,".group",af);this.filters_manager=new aa(this,(al!==undefined?al:{}));this.filters_available=false;this.filters_visible=false;this.tool=(ak!==undefined&&obj_length(ak)>0?new r(this,ak):undefined);if(this.header_div){if(this.filters_manager){this.filters_div=this.filters_manager.parent_div;this.header_div.after(this.filters_div)}if(this.tool){this.dynamic_tool_div=this.tool.parent_div;this.header_div.after(this.dynamic_tool_div)}}};p(N.prototype,q.prototype,i.prototype,{copy:function(af){return new this.constructor(this.name,this.view,af,this.hda_ldda,this.dataset_id,this.prefs,this.filters,this.tool)},to_json: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,}},change_mode:function(ag){var af=this;af.mode=ag;af.config.values.mode=ag;af.tile_cache.clear();af.request_draw();this.mode_icon.attr("title","Set display mode (now: "+af.mode+")");return af},update_icons:function(){var af=this;if(af.filters_available>0){af.filters_icon.show()}else{af.filters_icon.hide()}if(af.tool){af.tools_icon.show()}else{af.tools_icon.hide()}},_gen_tile_cache_key:function(ag,ah,af){return ag+"_"+ah+"_"+af},request_draw:function(ag,af){this.view.request_redraw(false,ag,af,this)},_draw:function(ah,ap){if(!this.enabled){return}if(!this.content_visible){return}if(!(this instanceof A)&&(!this.dataset_id)){return}var ao=this.view.low,al=this.view.high,am=al-ao,ai=this.view.container.width(),at=ai/am,ak=this.view.resolution,ar=this.content_div;if(this.is_overview){ao=this.view.max_low;al=this.view.max_high;ak=Math.pow(C,Math.ceil(Math.log((view.max_high-view.max_low)/R)/Math.log(C)));at=ai/(view.max_high-view.max_low)}if(!ap){this.content_div.children().addClass("remove")}this.max_height=0;var ag=Math.floor(ao/ak/R);var an=true;var aq=[];var af=0;while((ag*R*ak)<al){tile=this.draw_helper(ah,ai,ag,ak,ar,at);if(tile){aq.push(tile)}else{an=false}ag+=1;af++}if(!ap){this.content_div.children(".remove").remove()}var aj=this;if(an){aj.postdraw_actions(aq,ai,at,ap)}},postdraw_actions:function(aj,ak,al,af){var ah=this;var ai=false;for(var ag=0;ag<aj.length;ag++){if(aj[ag].message){ai=true;break}}if(ai){for(var ag=0;ag<aj.length;ag++){tile=aj[ag];if(!tile.message){tile.canvas.css("padding-top",E)}}}},draw_helper:function(ag,ah,ai,al,ar,aw,at,am){var aj=this,aq=this._gen_tile_cache_key(ah,aw,ai),an=ai*R*al,av=an+R*al;var ao=(ag?undefined:aj.tile_cache.get(aq));if(ao){aj.show_tile(ao,ar,aw);return ao}var ap=function(ax){return("isResolved" in ax)};var ak=true;var af=aj.data_manager.get_data(an,av,aj.mode,al,aj.data_url_extra_params);if(ap(af)){ak=false}var au;if(view.reference_track&&aw>view.canvas_manager.char_width_px){au=view.reference_track.data_manager.get_data(an,av,aj.mode,al,view.reference_track.data_url_extra_params);if(ap(au)){ak=false}}if(ak){p(af,am);var ao=aj.draw_tile(af,aj.mode,al,ai,aw,au);if(ao!==undefined){aj.tile_cache.set(aq,ao);aj.show_tile(ao,ar,aw)}return ao}$.when(af,au).then(function(){view.request_redraw(false,false,false,aj)});return null},show_tile:function(ah,aj,ak){var ag=this,af=ah.canvas;ah.predisplay_actions();var ai=(ah.low-(this.is_overview?this.view.max_low:this.view.low))*ak;if(this.left_offset){ai-=this.left_offset}af.css({position:"absolute",top:0,left:ai,height:""});if(af.hasClass("remove")){af.removeClass("remove")}else{aj.append(af)}ag.max_height=Math.max(ag.max_height,af.height());ag.content_div.css("height",ag.max_height+"px");aj.children().css("height",ag.max_height+"px")},_get_tile_bounds:function(af,ag){var ai=af*R*ag,aj=R*ag,ah=(ai+aj<=this.view.max_high?ai+aj:this.view.max_high);return[ai,ah]},tool_region_and_parameters_str:function(ah,af,ai){var ag=this,aj=(ah!==undefined&&af!==undefined&&ai!==undefined?ah+":"+af+"-"+ai:"all");return" - region=["+aj+"], parameters=["+ag.tool.get_param_values().join(", ")+"]"},init_for_tool_data:function(){this.data_url=raw_data_url;this.data_query_wait=1000;this.dataset_check_url=dataset_state_url;this.predraw_init=function(){var ag=this;var af=function(){if(ag.data_manager.size()===0){setTimeout(af,300)}else{ag.data_url=default_data_url;ag.data_query_wait=L;ag.dataset_state_url=converted_datasets_state_url;$.getJSON(ag.dataset_state_url,{dataset_id:ag.dataset_id,hda_ldda:ag.hda_ldda},function(ah){})}};af()}}});var ab=function(ag,af){i.call(this,"label",ag,af,false,{});this.container_div.addClass("label-track")};p(ab.prototype,i.prototype,{build_header_div:function(){},init:function(){this.enabled=true},_draw:function(){var ah=this.view,ai=ah.high-ah.low,al=Math.floor(Math.pow(10,Math.floor(Math.log(ai)/Math.log(10)))),af=Math.floor(ah.low/al)*al,aj=this.view.container.width(),ag=$("<div style='position: relative; height: 1.3em;'></div>");while(af<ah.high){var ak=(af-ah.low)/ai*aj;ag.append($("<div class='label'>"+commatize(af)+"</div>").css({position:"absolute",left:ak-1}));af+=al}this.content_div.children(":first").remove();this.content_div.append(ag)}});var A=function(af){N.call(this,"reference",af,{content_div:af.top_labeltrack},{});af.reference_track=this;this.left_offset=200;this.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.data_url_extra_params={dbkey:af.dbkey};this.data_manager=new I(B,this,false);this.tile_cache=new c(u)};p(A.prototype,q.prototype,N.prototype,{build_header_div:function(){},init:function(){this.enabled=true},draw_tile:function(ap,al,ak,ag,aq){var aj=this,ah=R*ak;if(aq>this.view.canvas_manager.char_width_px){if(ap.data===null){aj.content_div.css("height","0px");return}var ai=this.view.canvas_manager.new_canvas();var ao=ai.getContext("2d");ai.width=Math.ceil(ah*aq+aj.left_offset);ai.height=aj.height_px;ao.font=ao.canvas.manager.default_font;ao.textAlign="center";ap=ap.data;for(var am=0,an=ap.length;am<an;am++){var af=Math.round(am*aq);ao.fillText(ap[am],af+aj.left_offset,10)}return new b(aj,ag,ak,ai,ap)}this.content_div.css("height","0px")}});var j=function(ak,ai,ah,al,af,aj){var ag=this;this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";N.call(this,ak,ai,ah,aj);this.min_height_px=16;this.max_height_px=400;this.height_px=32;this.hda_ldda=al;this.dataset_id=af;this.original_dataset_id=af;this.data_manager=new S(B,this);this.tile_cache=new c(u);this.left_offset=0;this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:ak},{key:"color",label:"Color",type:"color",default_value: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:this.height_px,hidden:true}],saved_values:aj,onchange:function(){ag.set_name(ag.prefs.name);ag.vertical_range=ag.prefs.max_value-ag.prefs.min_value;$("#linetrack_"+ag.dataset_id+"_minval").text(ag.prefs.min_value);$("#linetrack_"+ag.dataset_id+"_maxval").text(ag.prefs.max_value);ag.tile_cache.clear();ag.request_draw()}});this.prefs=this.config.values;this.height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value;this.add_resize_handle()};p(j.prototype,q.prototype,N.prototype,{add_resize_handle:function(){var af=this;var ai=false;var ah=false;var ag=$("<div class='track-resize'>");$(af.container_div).hover(function(){if(af.content_visible){ai=true;ag.show()}},function(){ai=false;if(!ah){ag.hide()}});ag.hide().bind("dragstart",function(aj,ak){ah=true;ak.original_height=$(af.content_div).height()}).bind("drag",function(ak,al){var aj=Math.min(Math.max(al.original_height+al.deltaY,af.min_height_px),af.max_height_px);$(af.content_div).css("height",aj);af.height_px=aj;af.request_draw(true)}).bind("dragend",function(aj,ak){af.tile_cache.clear();ah=false;if(!ai){ag.hide()}af.config.values.height=af.height_px}).appendTo(af.container_div)},predraw_init:function(){var af=this;af.vertical_range=undefined;return $.getJSON(af.data_url,{stats:true,chrom:af.view.chrom,low:null,high:null,hda_ldda:af.hda_ldda,dataset_id:af.dataset_id},function(ag){af.container_div.addClass("line-track");var aj=ag.data;if(isNaN(parseFloat(af.prefs.min_value))||isNaN(parseFloat(af.prefs.max_value))){var ah=aj.min;var al=aj.max;ah=Math.floor(Math.min(0,Math.max(ah,aj.mean-2*aj.sd)));al=Math.ceil(Math.max(0,Math.min(al,aj.mean+2*aj.sd)));af.prefs.min_value=ah;af.prefs.max_value=al;$("#track_"+af.dataset_id+"_minval").val(af.prefs.min_value);$("#track_"+af.dataset_id+"_maxval").val(af.prefs.max_value)}af.vertical_range=af.prefs.max_value-af.prefs.min_value;af.total_frequency=aj.total_frequency;af.container_div.find(".yaxislabel").remove();var ak=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+af.dataset_id+"_minval").text(Z(af.prefs.min_value,3));var ai=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+af.dataset_id+"_maxval").text(Z(af.prefs.max_value,3));ai.css({position:"absolute",top:"24px",left:"10px"});ai.prependTo(af.container_div);ak.css({position:"absolute",bottom:"2px",left:"10px"});ak.prependTo(af.container_div)})},draw_tile:function(ar,ak,aj,ah,aq){if(this.vertical_range===undefined){return}var af=this._get_tile_bounds(ah,aj),al=af[0],ap=af[1],ag=Math.ceil((ap-al)*aq),an=this.height_px;var ai=this.view.canvas_manager.new_canvas();ai.width=ag,ai.height=an;var ao=ai.getContext("2d");var am=new M.LinePainter(ar.data,al,ap,this.prefs,ak);am.draw(ao,ag,an);return new b(this.track,ah,aj,ai,ar.data)}});var e=function(af,al,ag,ak,an,am,ai,aj){var ah=this;this.display_modes=["Auto","Histogram","Dense","Squish","Pack"];N.call(this,af,al,ag,am,ai,aj);this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:af},{key:"block_color",label:"Block color",type:"color",default_value:get_random_color()},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"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},],saved_values:am,onchange:function(){ah.set_name(ah.prefs.name);ah.tile_cache.clear();ah.set_painter_from_config();ah.request_draw()}});this.prefs=this.config.values;this.height_px=0;this.container_div.addClass("feature-track");this.hda_ldda=ak;this.dataset_id=an;this.original_dataset_id=an;this.show_labels_scale=0.001;this.showing_details=false;this.summary_draw_height=30;this.inc_slots={};this.start_end_dct={};this.tile_cache=new c(d);this.data_manager=new S(20,this);this.left_offset=200;this.set_painter_from_config()};p(e.prototype,q.prototype,N.prototype,{set_painter_from_config:function(){if(this.config.values.connector_style=="arcs"){this.painter=M.ArcLinkedFeaturePainter}else{this.painter=M.LinkedFeaturePainter}},postdraw_actions:function(av,af,aw,au){N.prototype.postdraw_actions.call(this,av,au);var ai=this;if(au){var ak=ai.content_div.children();var al=false;for(var aj=ak.length-1,ap=0;aj>=ap;aj--){var ah=$(ak[aj]);if(al){ah.remove()}else{if(ah.children().length!==0){al=true}}}}if(ai.mode=="Histogram"){var ao=-1;for(var aj=0;aj<av.length;aj++){var at=av[aj].max_val;if(at>ao){ao=at}}for(var aj=0;aj<av.length;aj++){var ar=av[aj];if(ar.max_val!==ao){ar.canvas.remove();ai.draw_helper(true,af,ar.index,ar.resolution,ar.canvas.parent(),aw,[],{max:ao})}}}if(ai.filters_manager){var ag=ai.filters_manager.filters;for(var an=0;an<ag.length;an++){ag[an].update_ui_elt()}var am=false,aq;for(var aj=0;aj<av.length;aj++){if(av[aj].data.length){aq=av[aj].data[0];for(var an=0;an<ag.length;an++){if(ag[an].applies_to(aq)){am=true;break}}}}if(ai.filters_available!==am){ai.filters_available=am;if(!ai.filters_available){ai.filters_div.hide()}ai.update_icons()}}},update_auto_mode:function(af){var af;if(this.mode=="Auto"){if(af=="no_detail"){af="feature spans"}else{if(af=="summary_tree"){af="coverage histogram"}}this.mode_icon.attr("title","Set display mode (now: Auto/"+af+")")}},incremental_slots:function(aj,ag,ai){var ah=this.view.canvas_manager.dummy_context,af=this.inc_slots[aj];if(!af||(af.mode!==ai)){af=new (s.FeatureSlotter)(aj,ai==="Pack",z,function(ak){return ah.measureText(ak)});af.mode=ai;this.inc_slots[aj]=af}return af.slot_features(ag)},get_summary_tree_data:function(aj,am,ah,av){if(av>ah-am){av=ah-am}var aq=Math.floor((ah-am)/av),au=[],ai=0;var ak=0,al=0,ap,at=0,an=[],ar,ao;var ag=function(ay,ax,az,aw){ay[0]=ax+az*aw;ay[1]=ax+(az+1)*aw};while(at<av&&ak!==aj.length){var af=false;for(;at<av&&!af;at++){ag(an,am,at,aq);for(al=ak;al<aj.length;al++){ap=aj[al].slice(1,3);if(is_overlap(ap,an)){af=true;break}}if(af){break}}data_start_index=al;au[au.length]=ar=[an[0],0];for(;al<aj.length;al++){ap=aj[al].slice(1,3);if(is_overlap(ap,an)){ar[1]++}else{break}}if(ar[1]>ai){ai=ar[1]}at++}return{max:ai,delta:aq,data:au}},draw_tile:function(au,ax,aB,aF,ap,ai){var ay=this,ak=ay._get_tile_bounds(aF,aB),aI=ak[0],ag=ak[1],aw=ag-aI,az=Math.ceil(aw*ap),aO=25,aj=this.left_offset,av,al;if(ax==="Auto"){if(au.dataset_type==="summary_tree"){ax=au.dataset_type}else{if(au.extra_info==="no_detail"||ay.is_overview){ax="no_detail"}else{var aN=au.data;if(this.view.high-this.view.low>J){ax="Squish"}else{ax="Pack"}}}this.update_auto_mode(ax)}if(ax==="summary_tree"||ax==="Histogram"){al=this.summary_draw_height;this.container_div.find(".yaxislabel").remove();var af=$("<div />").addClass("yaxislabel");af.text(au.max);af.css({position:"absolute",top:"24px",left:"10px",color:this.prefs.label_color});af.prependTo(this.container_div);var ah=this.view.canvas_manager.new_canvas();ah.width=az+aj;ah.height=al+T;if(au.dataset_type!="summary_tree"){var aq=this.get_summary_tree_data(au.data,aI,ag,200);if(au.max){aq.max=au.max}au=aq}var aK=new M.SummaryTreePainter(au,aI,ag,this.prefs);var aA=ah.getContext("2d");aA.translate(aj,T);aK.draw(aA,az,al);return new k(ay,aF,aB,ah,au.data,au.max)}var av,an=1;if(ax==="no_detail"||ax==="Squish"||ax==="Pack"){an=this.incremental_slots(ap,au.data,ax);av=this.inc_slots[ap].slots}var ao=[];if(au.data){var ar=this.filters_manager.filters;for(var aC=0,aE=au.data.length;aC<aE;aC++){var am=au.data[aC];var aD=false;var at;for(var aH=0,aM=ar.length;aH<aM;aH++){at=ar[aH];at.update_attrs(am);if(!at.keep(am)){aD=true;break}}if(!aD){ao.push(am)}}}var aL=(this.filters_manager.alpha_filter?new D(this.filters_manager.alpha_filter):null);var aJ=(this.filters_manager.height_filter?new D(this.filters_manager.height_filter):null);var aK=new (this.painter)(ao,aI,ag,this.prefs,ax,aL,aJ,ai);var al=Math.max(ad,aK.get_required_height(an,az));var ah=this.view.canvas_manager.new_canvas();var aG=null;ah.width=az+aj;ah.height=al;var aA=ah.getContext("2d");aA.fillStyle=this.prefs.block_color;aA.font=aA.canvas.manager.default_font;aA.textAlign="right";this.container_div.find(".yaxislabel").remove();if(au.data){aA.translate(aj,0);aG=aK.draw(aA,az,al,av);aG.translation=-aj}return new P(ay,aF,aB,ah,au.data,ax,au.message,aG)}});var U=function(ak,ah,ag,am,af,aj,al,ai){e.call(this,ak,ah,ag,am,af,aj,al,ai);this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:ak},{key:"block_color",label:"Block color",type:"color",default_value: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:aj,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=M.ReadPainter};p(U.prototype,q.prototype,N.prototype,e.prototype);var W=function(af,ak,ag,aj,an,am,ah){e.call(this,af,ak,ag,aj,an,am,ah);var ai=get_random_color(),al=get_random_color([ai,"#ffffff"]);this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:af},{key:"block_color",label:"Block and sense strand color",type:"color",default_value:ai},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:al},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},],saved_values:am,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=M.ReadPainter;this.update_icons()};p(W.prototype,q.prototype,N.prototype,e.prototype);X.View=ac;X.DrawableGroup=Q;X.LineTrack=j;X.FeatureTrack=e;X.ReadTrack=W;X.VcfTrack=U};var slotting_module=function(c,b){var e=c("class").extend;var d=2,a=5;b.FeatureSlotter=function(i,h,f,g){this.slots={};this.start_end_dct={};this.w_scale=i;this.include_label=h;this.max_rows=f;this.measureText=g};e(b.FeatureSlotter.prototype,{slot_features:function(m){var p=this.w_scale,s=this.slots,h=this.start_end_dct,y=[],A=[],n=0,z=this.max_rows;for(var w=0,x=m.length;w<x;w++){var l=m[w],o=l[0];if(s[o]!==undefined){n=Math.max(n,s[o]);A.push(s[o])}else{y.push(w)}}var q=function(F,G){for(var E=0;E<=z;E++){var C=false,H=h[E];if(H!==undefined){for(var B=0,D=H.length;B<D;B++){var i=H[B];if(G>i[0]&&F<i[1]){C=true;break}}}if(!C){return E}}return -1};for(var w=0,x=y.length;w<x;w++){var l=m[y[w]],o=l[0],u=l[1],f=l[2],r=l[3],g=Math.floor(u*p),k=Math.ceil(f*p),v=this.measureText(r).width,j;if(r!==undefined&&this.include_label){v+=(d+a);if(g-v>=0){g-=v;j="left"}else{k+=v;j="right"}}var t=q(g,k);if(t>=0){if(h[t]===undefined){h[t]=[]}h[t].push([g,k]);s[o]=t;n=Math.max(n,t)}else{}}return n+1}})};var painters_module=function(i,x){var u=i("class").extend;var p=function(I,A,G,z,F,D){if(D===undefined){D=4}var C=z-A;var B=F-G;var E=Math.floor(Math.sqrt(C*C+B*B)/D);var J=C/E;var H=B/E;var y;for(y=0;y<E;y++,A+=J,G+=H){if(y%2!==0){continue}I.fillRect(A,G,D,1)}};var q=function(B,A,z,E){var D=A-E/2,C=A+E/2,F=z-Math.sqrt(E*3/2);B.beginPath();B.moveTo(D,F);B.lineTo(C,F);B.lineTo(A,z);B.lineTo(D,F);B.strokeStyle=this.fillStyle;B.fill();B.stroke();B.closePath()};var d=function(y){this.default_val=(y?y:1)};d.prototype.gen_val=function(y){return this.default_val};var l=function(A,C,y,z,B){this.data=A;this.view_start=C;this.view_end=y;this.prefs=u({},this.default_prefs,z);this.mode=B};l.prototype.default_prefs={};var v=function(A,C,y,z,B){l.call(this,A,C,y,z,B)};v.prototype.default_prefs={show_counts:false};v.prototype.draw=function(M,z,L){var E=this.view_start,O=this.view_end-this.view_start,N=z/O;var J=this.data.data,I=this.data.delta,G=this.data.max,B=L;delta_x_px=Math.ceil(I*N);M.save();for(var C=0,D=J.length;C<D;C++){var H=Math.floor((J[C][0]-E)*N);var F=J[C][1];if(!F){continue}var K=F/G*L;if(F!==0&&K<1){K=1}M.fillStyle=this.prefs.block_color;M.fillRect(H,B-K,delta_x_px,K);var A=4;if(this.prefs.show_counts&&(M.measureText(F).width+A)<delta_x_px){M.fillStyle=this.prefs.label_color;M.textAlign="center";M.fillText(F,H+(delta_x_px/2),10)}}M.restore()};var b=function(y,C,E,F,A){l.call(this,y,C,E,F,A);if(this.prefs.min_value===undefined){var G=Infinity;for(var z=0,B=this.data.length;z<B;z++){G=Math.min(G,this.data[z][1])}this.prefs.min_value=G}if(this.prefs.max_value===undefined){var D=-Infinity;for(var z=0,B=this.data.length;z<B;z++){D=Math.max(D,this.data[z][1])}this.prefs.max_value=D}};b.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Histogram",color:"#000",overflow_color:"#F66"};b.prototype.draw=function(N,M,K){var F=false,H=this.prefs.min_value,D=this.prefs.max_value,J=D-H,z=K,A=this.view_start,L=this.view_end-this.view_start,B=M/L,I=this.mode,T=this.data;N.save();var U=Math.round(K+H/J*K);if(I!=="Intensity"){N.fillStyle="#aaa";N.fillRect(0,U,M,1)}N.beginPath();var R,E,C;if(T.length>1){C=Math.ceil((T[1][0]-T[0][0])*B)}else{C=10}for(var O=0,P=T.length;O<P;O++){N.fillStyle=this.prefs.color;R=Math.round((T[O][0]-A)*B);E=T[O][1];var Q=false,G=false;if(E===null){if(F&&I==="Filled"){N.lineTo(R,z)}F=false;continue}if(E<H){G=true;E=H}else{if(E>D){Q=true;E=D}}if(I==="Histogram"){E=Math.round(E/J*z);N.fillRect(R,U,C,-E)}else{if(I==="Intensity"){E=255-Math.floor((E-H)/J*255);N.fillStyle="rgb("+E+","+E+","+E+")";N.fillRect(R,0,C,z)}else{E=Math.round(z-(E-H)/J*z);if(F){N.lineTo(R,E)}else{F=true;if(I==="Filled"){N.moveTo(R,z);N.lineTo(R,E)}else{N.moveTo(R,E)}}}}N.fillStyle=this.prefs.overflow_color;if(Q||G){var S;if(I==="Histogram"||I==="Intensity"){S=C}else{R-=2;S=4}if(Q){N.fillRect(R,0,S,3)}if(G){N.fillRect(R,z-3,S,3)}}N.fillStyle=this.prefs.color}if(I==="Filled"){if(F){N.lineTo(R,U);N.lineTo(0,U)}N.fill()}else{N.stroke()}N.restore()};var m=function(y){this.feature_positions={};this.slot_height=y;this.translation=0;this.y_translation=0};m.prototype.map_feature_data=function(z,B,y,A){if(!this.feature_positions[B]){this.feature_positions[B]=[]}this.feature_positions[B].push({data:z,x_start:y,x_end:A})};m.prototype.get_feature_data=function(z,D){var C=Math.floor((D-this.y_translation)/this.slot_height),B;if(!this.feature_positions[C]){return null}z+=this.translation;for(var A=0;A<this.feature_positions[C].length;A++){B=this.feature_positions[C][A];if(z>=B.x_start&&z<=B.x_end){return B.data}}};var o=function(A,D,y,z,C,E,B){l.call(this,A,D,y,z,C);this.alpha_scaler=(E?E:new d());this.height_scaler=(B?B:new d())};o.prototype.default_prefs={block_color:"#FFF",connector_color:"#FFF"};u(o.prototype,{get_required_height:function(A,z){var y=y_scale=this.get_row_height(),B=this.mode;if(B==="no_detail"||B==="Squish"||B==="Pack"){y=A*y_scale}return y+this.get_top_padding(z)+this.get_bottom_padding(z)},get_top_padding:function(y){return 0},get_bottom_padding:function(y){return Math.max(Math.round(this.get_row_height()/2),5)},draw:function(K,I,G,F){var Q=this.data,D=this.view_start,M=this.view_end;K.save();K.fillStyle=this.prefs.block_color;K.textAlign="right";var H=this.view_end-this.view_start,E=I/H,L=this.get_row_height(),P=new m(L),B;for(var N=0,O=Q.length;N<O;N++){var A=Q[N],C=A[0],J=A[1],y=A[2],z=(F&&F[C]!==undefined?F[C]:null);if((J<M&&y>D)&&(this.mode=="Dense"||z!==null)){B=this.draw_element(K,this.mode,A,z,D,M,E,L,I);P.map_feature_data(A,z,B[0],B[1])}}K.restore();P.y_translation=this.get_top_padding(I);return P},draw_element:function(E,A,G,C,B,D,F,z,y){console.log("WARNING: Unimplemented function.");return[0,0]}});var c=10,h=3,k=5,w=10,f=1,s=9,e=3,a=9,j=2,g="#ccc";var r=function(A,D,y,z,C,E,B){o.call(this,A,D,y,z,C,E,B);this.draw_background_connector=true;this.draw_individual_connectors=false};u(r.prototype,o.prototype,{get_row_height:function(){var z=this.mode,y;if(z==="Dense"){y=c}else{if(z==="no_detail"){y=h}else{if(z==="Squish"){y=k}else{y=w}}}return y},draw_element:function(M,D,X,H,O,aj,an,ap,y){var T=X[0],al=X[1],ad=X[2],Q=X[3],ae=Math.floor(Math.max(0,(al-O)*an)),N=Math.ceil(Math.min(y,Math.max(0,(ad-O)*an))),ac=ae,ao=N,aa=(D==="Dense"?0:(0+H))*ap+this.get_top_padding(y),L,ah,R=null,ar=null,B=this.prefs.block_color,ag=this.prefs.label_color;M.globalAlpha=this.alpha_scaler.gen_val(X);if(D==="Dense"){H=1}if(D==="no_detail"){M.fillStyle=B;M.fillRect(ae,aa+5,N-ae,f)}else{var K=X[4],Z=X[5],af=X[6],C=X[7],V=true;if(Z&&af){R=Math.floor(Math.max(0,(Z-O)*an));ar=Math.ceil(Math.min(y,Math.max(0,(af-O)*an)))}var am,U;if(D==="Squish"){am=1;U=e;V=false}else{if(D==="Dense"){am=5;U=s}else{am=5;U=a}}if(!C){M.fillStyle=B;M.fillRect(ae,aa+1,N-ae,U);if(K&&V){if(K==="+"){M.fillStyle=M.canvas.manager.get_pattern("right_strand_inv")}else{if(K==="-"){M.fillStyle=M.canvas.manager.get_pattern("left_strand_inv")}}M.fillRect(ae,aa+1,N-ae,U)}}else{var J,W;if(D==="Squish"||D==="Dense"){J=aa+Math.floor(e/2)+1;W=1}else{if(K){J=aa;W=U}else{J+=(e/2)+1;W=1}}if(this.draw_background_connector){if(D==="Squish"||D==="Dense"){M.fillStyle=g}else{if(K){if(K==="+"){M.fillStyle=M.canvas.manager.get_pattern("right_strand")}else{if(K==="-"){M.fillStyle=M.canvas.manager.get_pattern("left_strand")}}}else{M.fillStyle=g}}M.fillRect(ae,J,N-ae,W)}var E;for(var ak=0,A=C.length;ak<A;ak++){var F=C[ak],z=Math.floor(Math.max(0,(F[0]-O)*an)),Y=Math.ceil(Math.min(y,Math.max((F[1]-O)*an))),S,ab;if(z>Y){continue}M.fillStyle=B;M.fillRect(z,aa+(U-am)/2+1,Y-z,am);if(R!==undefined&&af>Z&&!(z>ar||Y<R)){var ai=Math.max(z,R),I=Math.min(Y,ar);M.fillRect(ai,aa+1,I-ai,U);if(C.length==1&&D=="Pack"){if(K==="+"){M.fillStyle=M.canvas.manager.get_pattern("right_strand_inv")}else{if(K==="-"){M.fillStyle=M.canvas.manager.get_pattern("left_strand_inv")}}if(ai+14<I){ai+=2;I-=2}M.fillRect(ai,aa+1,I-ai,U)}}if(this.draw_individual_connectors&&S){this.draw_connector(M,S,ab,z,Y,aa)}S=z;ab=Y}if(D==="Pack"){M.globalAlpha=1;M.fillStyle="white";var G=this.height_scaler.gen_val(X),P=Math.ceil(U*G),aq=Math.round((U-P)/2);if(G!==1){M.fillRect(ae,J+1,N-ae,aq);M.fillRect(ae,J+U-aq+1,N-ae,aq)}}}M.globalAlpha=1;if(D==="Pack"&&al>O){M.fillStyle=ag;if(O===0&&ae-M.measureText(Q).width<0){M.textAlign="left";M.fillText(Q,N+j,aa+8);ao+=M.measureText(Q).width+j}else{M.textAlign="right";M.fillText(Q,ae-j,aa+8);ac-=M.measureText(Q).width+j}}}M.globalAlpha=1;return[ac,ao]}});var t=function(B,E,y,A,D,F,C,z){o.call(this,B,E,y,A,D,F,C);this.ref_seq=(z?z.data:null)};u(t.prototype,o.prototype,{get_row_height:function(){var y,z=this.mode;if(z==="Dense"){y=c}else{if(z==="Squish"){y=k}else{y=w;if(this.prefs.show_insertions){y*=2}}}return y},draw_read:function(K,A,ag,V,L,aa,ad,C,B,M){K.textAlign="center";var J=this,R=[L,aa],Z=0,W=0,D=0,F=K.canvas.manager.char_width_px,y=(B==="+"?this.prefs.block_color:this.prefs.reverse_strand_color);var O=[];if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){D=Math.round(ag/2)}if(!C){C=[[0,M.length]]}for(var G=0,I=C.length;G<I;G++){var z=C[G],E="MIDNSHP=X"[z[0]],S=z[1];if(E==="H"||E==="S"){Z-=S}var U=ad+Z,Y=Math.floor(Math.max(0,(U-L)*ag)),ab=Math.floor(Math.max(0,(U+S-L)*ag));if(Y===ab){ab+=1}switch(E){case"H":break;case"S":case"M":case"=":if(is_overlap([U,U+S],R)){var N=M.slice(W,W+S);if(D>0){K.fillStyle=y;K.fillRect(Y-D,V+1,ab-Y,9);K.fillStyle=g;for(var af=0,H=N.length;af<H;af++){if(this.prefs.show_differences&&this.ref_seq){var P=this.ref_seq[U-L+af];if(!P||P.toLowerCase()===N[af].toLowerCase()){continue}}if(U+af>=L&&U+af<=aa){var X=Math.floor(Math.max(0,(U+af-L)*ag));K.fillText(N[af],X,V+9)}}}else{K.fillStyle=y;K.fillRect(Y,V+4,ab-Y,e)}}W+=S;Z+=S;break;case"N":K.fillStyle=g;K.fillRect(Y-D,V+5,ab-Y,1);Z+=S;break;case"D":K.fillStyle="red";K.fillRect(Y-D,V+4,ab-Y,3);Z+=S;break;case"P":break;case"I":var ah=Y-D;if(is_overlap([U,U+S],R)){var N=M.slice(W,W+S);if(this.prefs.show_insertions){var T=Y-(ab-Y)/2;if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){K.fillStyle="yellow";K.fillRect(T-D,V-9,ab-Y,9);O[O.length]={type:"triangle",data:[ah,V+4,5]};K.fillStyle=g;switch(compute_overlap([U,U+S],R)){case (OVERLAP_START):N=N.slice(L-U);break;case (OVERLAP_END):N=N.slice(0,U-aa);break;case (CONTAINED_BY):break;case (CONTAINS):N=N.slice(L-U,U-aa);break}for(var af=0,H=N.length;af<H;af++){var X=Math.floor(Math.max(0,(U+af-L)*ag));K.fillText(N[af],X-(ab-Y)/2,V)}}else{K.fillStyle="yellow";K.fillRect(T,V+(this.mode!=="Dense"?2:5),ab-Y,(A!=="Dense"?e:s))}}else{if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){O.push({type:"text",data:[N.length,ah,V+9]})}else{}}}W+=S;break;case"X":W+=S;break}}K.fillStyle="yellow";var Q,ai,ae;for(var ac=0;ac<O.length;ac++){Q=O[ac];ai=Q.type;ae=Q.data;if(ai==="text"){K.save();K.font="bold "+K.font;K.fillText(ae[0],ae[1],ae[2]);K.restore()}else{if(ai=="triangle"){q(K,ae[0],ae[1],ae[2])}}}},draw_element:function(R,M,E,B,U,z,I,S,P){var H=E[0],Q=E[1],A=E[2],J=E[3],D=Math.floor(Math.max(0,(Q-U)*I)),F=Math.ceil(Math.min(P,Math.max(0,(A-U)*I))),C=(M==="Dense"?0:(0+B))*S,G=this.prefs.label_color,O=0;if((M==="Pack"||this.mode==="Auto")&&I>R.canvas.manager.char_width_px){var O=Math.round(I/2)}if(E[5] instanceof Array){var N=Math.floor(Math.max(0,(E[4][0]-U)*I)),L=Math.ceil(Math.min(P,Math.max(0,(E[4][1]-U)*I))),K=Math.floor(Math.max(0,(E[5][0]-U)*I)),y=Math.ceil(Math.min(P,Math.max(0,(E[5][1]-U)*I)));if(E[4][1]>=U&&E[4][0]<=z&&E[4][2]){this.draw_read(R,M,I,C,U,z,E[4][0],E[4][2],E[4][3],E[4][4])}if(E[5][1]>=U&&E[5][0]<=z&&E[5][2]){this.draw_read(R,M,I,C,U,z,E[5][0],E[5][2],E[5][3],E[5][4])}if(K>L){R.fillStyle=g;p(R,L-O,C+5,K-O,C+5)}}else{this.draw_read(R,M,I,C,U,z,Q,E[4],E[5],E[6])}if(M==="Pack"&&Q>U&&J!=="."){R.fillStyle=this.prefs.label_color;var T=1;if(T===0&&D-R.measureText(J).width<0){R.textAlign="left";R.fillText(J,F+j-O,C+8)}else{R.textAlign="right";R.fillText(J,D-j-O,C+8)}}return[0,0]}});var n=function(A,D,y,z,C,E,B){r.call(this,A,D,y,z,C,E,B);this.longest_feature_length=this.calculate_longest_feature_length();this.draw_background_connector=false;this.draw_individual_connectors=true};u(n.prototype,o.prototype,r.prototype,{calculate_longest_feature_length:function(){var z=0;for(var C=0,y=this.data.length;C<y;C++){var B=this.data[C],A=B[1],D=B[2];z=Math.max(z,D-A)}return z},get_top_padding:function(z){var y=this.view_end-this.view_start,A=z/y;return Math.min(128,Math.ceil((this.longest_feature_length/2)*A))},draw_connector:function(G,B,F,H,E,D){var y=(F+H)/2,C=H-y;var A=Math.PI,z=0;if(C>0){G.beginPath();G.arc(y,D,H-y,Math.PI,0);G.stroke()}}});x.Scaler=d;x.SummaryTreePainter=v;x.LinePainter=b;x.LinkedFeaturePainter=r;x.ReadPainter=t;x.ArcLinkedFeaturePainter=n};(function(d){var c={};var b=function(e){return c[e]};var a=function(f,g){var e={};g(b,e);c[f]=e};a("class",class_module);a("slotting",slotting_module);a("painters",painters_module);a("trackster",trackster_module);for(key in c.trackster){d[key]=c.trackster[key]}})(window);
\ No newline at end of file
+var class_module=function(b,a){var c=function(){var f=arguments[0];for(var e=1;e<arguments.length;e++){var d=arguments[e];for(key in d){f[key]=d[key]}}return f};a.extend=c};var requestAnimationFrame=(function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(b,a){window.setTimeout(b,1000/60)}})();var BEFORE=1001,CONTAINS=1002,OVERLAP_START=1003,OVERLAP_END=1004,CONTAINED_BY=1005,AFTER=1006;var compute_overlap=function(e,b){var g=e[0],f=e[1],d=b[0],c=b[1],a;if(g<d){if(f<d){a=BEFORE}else{if(f<=c){a=OVERLAP_START}else{a=CONTAINS}}}else{if(g>c){a=AFTER}else{if(f<=c){a=CONTAINED_BY}else{a=OVERLAP_END}}}return a};var is_overlap=function(c,b){var a=compute_overlap(c,b);return(a!==BEFORE&&a!==AFTER)};var get_random_color=function(a){if(!a){a="#ffffff"}if(typeof(a)==="string"){a=[a]}for(var j=0;j<a.length;j++){a[j]=parseInt(a[j].slice(1),16)}var m=function(t,s,i){return((t*299)+(s*587)+(i*114))/1000};var e=function(u,t,v,r,i,s){return(Math.max(u,r)-Math.min(u,r))+(Math.max(t,i)-Math.min(t,i))+(Math.max(v,s)-Math.min(v,s))};var g,n,f,k,p,h,q,c,d,b,o,l=false;do{g=Math.random()*16777215;n=g|16711680;f=g|65280;k=g|255;d=m(n,f,k);l=true;for(var j=0;j<a.length;j++){p=a[j];h=p|16711680;q=p|65280;c=p|255;b=m(h,q,c);o=e(n,f,k,h,q,c);if((Math.abs(d-b)<125)||(o<500)){l=false;break}}}while(!l);return"#"+(16777216+g).toString(16).substr(1,6)};var trackster_module=function(e,X){var p=e("class").extend,s=e("slotting"),L=e("painters");var ae=function(af,ag){this.document=af;this.default_font=ag!==undefined?ag:"9px Monaco, Lucida Console, monospace";this.dummy_canvas=this.new_canvas();this.dummy_context=this.dummy_canvas.getContext("2d");this.dummy_context.font=this.default_font;this.char_width_px=this.dummy_context.measureText("A").width;this.patterns={};this.load_pattern("right_strand","/visualization/strand_right.png");this.load_pattern("left_strand","/visualization/strand_left.png");this.load_pattern("right_strand_inv","/visualization/strand_right_inv.png");this.load_pattern("left_strand_inv","/visualization/strand_left_inv.png")};p(ae.prototype,{load_pattern:function(af,aj){var ag=this.patterns,ah=this.dummy_context,ai=new Image();ai.src=image_path+aj;ai.onload=function(){ag[af]=ah.createPattern(ai,"repeat")}},get_pattern:function(af){return this.patterns[af]},new_canvas:function(){var af=this.document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(af)}af.manager=this;return af}});var n={};var l=function(af,ag){n[af.attr("id")]=ag};var m=function(af,ah,aj,ai){aj=".group";var ag={};n[af.attr("id")]=ai;af.bind("drag",{handle:"."+ah,relative:true},function(ar,at){var aq=$(this);var aw=$(this).parent(),an=aw.children(),ap=n[$(this).attr("id")],am,al,au,ak,ao;al=$(this).parents(aj);if(al.length!==0){au=al.position().top;ak=au+al.outerHeight();if(at.offsetY<au){$(this).insertBefore(al);var av=n[al.attr("id")];av.remove_drawable(ap);av.container.add_drawable_before(ap,av);return}else{if(at.offsetY>ak){$(this).insertAfter(al);var av=n[al.attr("id")];av.remove_drawable(ap);av.container.add_drawable(ap);return}}}al=null;for(ao=0;ao<an.length;ao++){am=$(an.get(ao));au=am.position().top;ak=au+am.outerHeight();if(am.is(aj)&&this!==am.get(0)&&at.offsetY>=au&&at.offsetY<=ak){if(at.offsetY-au<ak-at.offsetY){am.find(".content-div").prepend(this)}else{am.find(".content-div").append(this)}if(ap.container){ap.container.remove_drawable(ap)}n[am.attr("id")].add_drawable(ap);return}}for(ao=0;ao<an.length;ao++){if(at.offsetY<$(an.get(ao)).position().top){break}}if(ao===an.length){if(this!==an.get(ao-1)){aw.append(this);n[aw.attr("id")].move_drawable(ap,ao)}}else{if(this!==an.get(ao)){$(this).insertBefore(an.get(ao));n[aw.attr("id")].move_drawable(ap,(at.deltaY>0?ao-1:ao))}}}).bind("dragstart",function(){ag["border-top"]=af.css("border-top");ag["border-bottom"]=af.css("border-bottom");$(this).css({"border-top":"1px solid blue","border-bottom":"1px solid blue"})}).bind("dragend",function(){$(this).css(ag)})};X.moveable=m;var ad=16,F=9,C=20,T=F+2,y=100,I=12000,Q=200,A=5,u=10,K=5000,v=100,o="There was an error in indexing this dataset. ",J="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",D="No data for this chrom/contig.",t="Currently indexing... please wait",w="Tool cannot be rerun: ",a="Loading data...",Y="Ready for display",R=10,H=20;function Z(ag,af){if(!af){af=0}var ah=Math.pow(10,af);return Math.round(ag*ah)/ah}var c=function(af){this.num_elements=af;this.clear()};p(c.prototype,{get:function(ag){var af=this.key_ary.indexOf(ag);if(af!==-1){if(this.obj_cache[ag].stale){this.key_ary.splice(af,1);delete this.obj_cache[ag]}else{this.move_key_to_end(ag,af)}}return this.obj_cache[ag]},set:function(ag,ah){if(!this.obj_cache[ag]){if(this.key_ary.length>=this.num_elements){var af=this.key_ary.shift();delete this.obj_cache[af]}this.key_ary.push(ag)}this.obj_cache[ag]=ah;return ah},move_key_to_end:function(ag,af){this.key_ary.splice(af,1);this.key_ary.push(ag)},clear:function(){this.obj_cache={};this.key_ary=[]},size:function(){return this.key_ary.length}});var S=function(ag,af,ah){c.call(this,ag);this.track=af;this.subset=(ah!==undefined?ah:true)};p(S.prototype,c.prototype,{load_data:function(ao,aj,am,ag,al){var an=this.track.view.chrom,ai={chrom:an,low:ao,high:aj,mode:am,resolution:ag,dataset_id:this.track.dataset_id,hda_ldda:this.track.hda_ldda};$.extend(ai,al);if(this.track.filters_manager){var ap=[];var af=this.track.filters_manager.filters;for(var ak=0;ak<af.length;ak++){ap[ap.length]=af[ak].name}ai.filter_cols=JSON.stringify(ap)}var ah=this;return $.getJSON(this.track.data_url,ai,function(aq){ah.set_data(ao,aj,am,aq)})},get_data:function(af,aj,ak,ag,ai){var ah=this.get_data_from_cache(af,aj,ak);if(ah){return ah}ah=this.load_data(af,aj,ak,ag,ai);this.set_data(af,aj,ak,ah);return ah},DEEP_DATA_REQ:"deep",BROAD_DATA_REQ:"breadth",get_more_data:function(an,ai,am,ah,al,aj){var ao=this.get_data_from_cache(an,ai,am);if(!ao){console.log("ERROR: no current data for: ",this.track,an,ai,am,ah,al);return}ao.stale=true;var ag=an;if(aj===this.DEEP_DATA_REQ){$.extend(al,{start_val:ao.data.length+1})}else{if(aj===this.BROAD_DATA_REQ){ag=(ao.max_high?ao.max_high:ao.data[ao.data.length-1][2])+1}}var af=this,ak=this.load_data(ag,ai,am,ah,al);new_data_available=$.Deferred();this.set_data(an,ai,am,new_data_available);$.when(ak).then(function(ap){if(ap.data){ap.data=ao.data.concat(ap.data);if(ap.max_low){ap.max_low=ao.max_low}if(ap.message){ap.message=ap.message.replace(/[0-9]+/,ap.data.length)}}af.set_data(an,ai,am,ap);new_data_available.resolve(ap)});return new_data_available},get_data_from_cache:function(af,ag,ah){return this.get(this.gen_key(af,ag,ah))},set_data:function(ag,ah,ai,af){return this.set(this.gen_key(ag,ah,ai),af)},gen_key:function(af,ah,ai){var ag=af+"_"+ah+"_"+ai;return ag},split_key:function(af){return af.split("_")}});var G=function(ag,af,ah){S.call(this,ag,af,ah)};p(G.prototype,S.prototype,c.prototype,{load_data:function(af,ai,aj,ag,ah){if(ag>1){return{data:null}}return S.prototype.load_data.call(this,af,ai,aj,ag,ah)}});var q=function(ai,ag,af,ah,ak){if(!q.id_counter){q.id_counter=0}this.id=q.id_counter++;this.name=ai;this.view=ag;this.container=af;this.config=new E({track:this,params:[{key:"name",label:"Name",type:"text",default_value:ai}],saved_values:ah,onchange:function(){this.track.set_name(this.track.config.values.name)}});this.prefs=this.config.values;this.drag_handle_class=ak;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(al){al.stopPropagation()});var aj=this;this.container_div.hover(function(){aj.icons_div.show()},function(){aj.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};q.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(af){if(af.content_visible){af.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");af.hide_contents();af.content_visible=false}else{af.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");af.content_visible=true;af.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"settings-icon",on_click_fn:function(ag){var ai=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},af=function(){ag.config.update_from_form($(".dialog-box"));hide_modal();$(window).unbind("keypress.check_enter_esc")},ah=function(aj){if((aj.keyCode||aj.which)===27){ai()}else{if((aj.keyCode||aj.which)===13){af()}}};$(window).bind("keypress.check_enter_esc",ah);show_modal("Configure",ag.config.build_form(),{Cancel:ai,OK:af})}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(af){$(".tipsy").remove();af.remove()}}];p(q.prototype,{init:function(){},can_draw:function(){if(this.enabled&&this.content_visible){return true}return false},request_draw:function(){},_draw:function(){},to_dict:function(){},from_dict:function(af){},update_icons:function(){},set_name:function(af){this.old_name=this.name;this.name=af;this.name_div.text(this.name)},revert_name:function(){this.name=this.old_name;this.name_div.text(this.name)},remove:function(){this.container.remove_drawable(this);this.container_div.hide(0,function(){$(this).remove();view.update_intro_div();view.has_changes=true})},build_container_div:function(){},build_header_div:function(){},add_action_icon:function(ag,ak,aj,ai,af){var ah=this;this.action_icons[ag]=$("<a/>").attr("href","javascript:void(0);").attr("title",ak).addClass("icon-button").addClass(aj).tipsy({gravity:"s"}).click(function(){ai(ah)}).appendTo(this.icons_div)},build_action_icons:function(af){var ah;for(var ag=0;ag<af.length;ag++){ah=af[ag];this.add_action_icon(ah.name,ah.title,ah.css_class,ah.on_click_fn,ah.prepend)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){}});var x=function(aj,ai,ag,af,ah,ak){q.call(this,ai,ag,af,ah,ak);this.obj_type=aj;this.drawables=[]};p(x.prototype,q.prototype,{init:function(){for(var af=0;af<this.drawables.length;af++){this.drawables[af].init()}},_draw:function(){for(var af=0;af<this.drawables.length;af++){this.drawables[af]._draw()}},to_dict:function(){var ag=[];for(var af=0;af<this.drawables.length;af++){ag.push(this.drawables[af].to_dict())}return{name:this.name,prefs:this.prefs,obj_type:this.obj_type,drawables:ag}},from_dict:function(al,af){var ak=new this.constructor(al.name,view,af,al.prefs,view.viewport_container,view);var ag,aj,ai;for(var ah=0;ah<al.drawables.length;ah++){ag=al.drawables[ah];aj=ag.obj_type;if(!aj){aj=ag.track_type}ai=addable_objects[aj].prototype.from_dict(ag,ak);ak.add_drawable(ai)}return ak},add_drawable:function(af){this.drawables.push(af);af.container=this},add_drawable_before:function(ah,af){var ag=this.drawables.indexOf(af);if(ag!=-1){this.drawables.splice(ag,0,ah);return true}return false},remove_drawable:function(ag){var af=this.drawables.indexOf(ag);if(af!=-1){this.drawables.splice(af,1);ag.container=null;return true}return false},move_drawable:function(ag,ah){var af=this.drawables.indexOf(ag);if(af!=-1){this.drawables.splice(af,1);this.drawables.splice(ah,0,ag);return true}return false}});var P=function(ai,ag,af,ah){x.call(this,"DrawableGroup",ai,ag,af,ah,"group-handle");this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);l(this.container_div,this);l(this.content_div,this);m(this.container_div,this.drag_handle_class,".group",this)};p(P.prototype,q.prototype,x.prototype,{action_icons_def:[{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(af){af.show_composite_track()}}].concat(q.prototype.action_icons_def),build_container_div:function(){return $("<div/>").addClass("group").attr("id","group_"+this.id).appendTo(this.container.content_div)},build_header_div:function(){var af=$("<div/>").addClass("track-header");af.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.name).appendTo(af);return af},hide_contents:function(){this.content_div.hide()},show_contents:function(){this.content_div.show();this.request_draw()},update_icons:function(){var af=true,ah=this.drawables[0].get_type();for(var ag=0;ag<this.drawables.length;ag++){if(this.drawables[ag].get_type()!==ah){af=false;break}}if(af){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".tipsy").remove()}},show_composite_track:function(){var af=new h("Composite Track",this.view,this,this.drawables);this.add_drawable(af);af.request_draw()},add_drawable:function(af){x.prototype.add_drawable.call(this,af);this.update_icons()},remove_drawable:function(af){x.prototype.remove_drawable.call(this,af);this.update_icons()},from_dict:function(ai,af){var ah=x.prototype.from_dict.call(this,ai,af);for(var ag=0;ag<ah.drawables.length;ag++){ah.content_div.append(ah.drawables[ag].container_div)}return ah}});var ac=function(af,ai,ah,ag){x.call(this,"View");this.container=af;this.chrom=null;this.vis_id=ah;this.dbkey=ag;this.title=ai;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.init();this.canvas_manager=new ae(af.get(0).ownerDocument);this.reset()};p(ac.prototype,x.prototype,{init:function(){var ah=this.container,af=this;this.top_container=$("<div/>").addClass("top-container").appendTo(ah);this.browser_content_div=$("<div/>").addClass("content").css("position","relative").appendTo(ah);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(ah);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;l(this.viewport_container,af);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var ai=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){add_tracks()});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("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").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ag=function(aj){if(aj.type==="focusout"||(aj.keyCode||aj.which)===13||(aj.keyCode||aj.which)===27){if((aj.keyCode||aj.which)!==27){af.go_to($(this).val())}$(this).hide();$(this).val("");af.location_span.show();af.chrom_select.show()}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ag).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("original-title","Click to change location").tipsy({gravity:"n"}).appendTo(this.nav_controls);this.location_span.click(function(){af.location_span.hide();af.chrom_select.hide();af.nav_input.val(af.chrom+":"+af.low+"-"+af.high);af.nav_input.css("display","inline-block");af.nav_input.select();af.nav_input.focus()});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").tipsy({gravity:"n"}).click(function(){af.zoom_out();af.request_redraw()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tipsy({gravity:"n"}).click(function(){af.zoom_in();af.request_redraw()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){af.change_chrom(af.chrom_select.val())});this.browser_content_div.click(function(aj){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(aj){af.zoom_in(aj.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(aj,ak){this.current_x=ak.offsetX}).bind("drag",function(aj,al){var am=al.offsetX-this.current_x;this.current_x=al.offsetX;var ak=Math.round(am/af.viewport_container.width()*(af.max_high-af.max_low));af.move_delta(-ak)});this.overview_close.click(function(){af.reset_overview()});this.viewport_container.bind("draginit",function(aj,ak){if(aj.clientX>af.viewport_container.width()-16){return false}}).bind("dragstart",function(aj,ak){ak.original_low=af.low;ak.current_height=aj.clientY;ak.current_x=ak.offsetX}).bind("drag",function(al,an){var aj=$(this);var ao=an.offsetX-an.current_x;var ak=aj.scrollTop()-(al.clientY-an.current_height);aj.scrollTop(ak);an.current_height=al.clientY;an.current_x=an.offsetX;var am=Math.round(ao/af.viewport_container.width()*(af.high-af.low));af.move_delta(am)}).bind("mousewheel",function(al,an,ak,aj){if(ak){ak*=50;var am=Math.round(-ak/af.viewport_container.width()*(af.high-af.low));af.move_delta(am)}});this.top_labeltrack.bind("dragstart",function(aj,ak){return $("<div />").css({height:af.browser_content_div.height()+af.top_labeltrack.height()+af.nav_labeltrack.height()+1,top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag",function(an,ao){$(ao.proxy).css({left:Math.min(an.pageX,ao.startX),width:Math.abs(an.pageX-ao.startX)});var ak=Math.min(an.pageX,ao.startX)-af.container.offset().left,aj=Math.max(an.pageX,ao.startX)-af.container.offset().left,am=(af.high-af.low),al=af.viewport_container.width();af.update_location(Math.round(ak/al*am)+af.low,Math.round(aj/al*am)+af.low)}).bind("dragend",function(ao,ap){var ak=Math.min(ao.pageX,ap.startX),aj=Math.max(ao.pageX,ap.startX),am=(af.high-af.low),al=af.viewport_container.width(),an=af.low;af.low=Math.round(ak/al*am)+an;af.high=Math.round(aj/al*am)+an;$(ap.proxy).remove();af.request_redraw()});this.add_label_track(new ab(this,{content_div:this.top_labeltrack}));this.add_label_track(new ab(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){af.resize_window()});$(document).bind("redraw",function(){af.redraw()});this.reset();$(window).trigger("resize")},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},update_location:function(af,ag){this.location_span.text(commatize(af)+" - "+commatize(ag));this.nav_input.val(this.chrom+":"+commatize(af)+"-"+commatize(ag))},load_chroms:function(ah){ah.num=v;ah.dbkey=this.dbkey;var af=this,ag=$.Deferred();$.ajax({url:chrom_url,data:ah,dataType:"json",success:function(aj){if(aj.chrom_info.length===0){alert("Invalid chromosome: "+ah.chrom);return}if(aj.reference){af.add_label_track(new z(af))}af.chrom_data=aj.chrom_info;var am='<option value="">Select Chrom/Contig</option>';for(var al=0,ai=af.chrom_data.length;al<ai;al++){var ak=af.chrom_data[al].chrom;am+='<option value="'+ak+'">'+ak+"</option>"}if(aj.prev_chroms){am+='<option value="previous">Previous '+v+"</option>"}if(aj.next_chroms){am+='<option value="next">Next '+v+"</option>"}af.chrom_select.html(am);af.chrom_start_index=aj.start_index;ag.resolve(aj)},error:function(){alert("Could not load chroms for this dbkey:",af.dbkey)}});return ag},change_chrom:function(ak,ag,am){if(!ak||ak==="None"){return}var ah=this;if(ak==="previous"){ah.load_chroms({low:this.chrom_start_index-v});return}if(ak==="next"){ah.load_chroms({low:this.chrom_start_index+v});return}var al=$.grep(ah.chrom_data,function(an,ao){return an.chrom===ak})[0];if(al===undefined){ah.load_chroms({chrom:ak},function(){ah.change_chrom(ak,ag,am)});return}else{if(ak!==ah.chrom){ah.chrom=ak;ah.chrom_select.val(ah.chrom);ah.max_high=al.len-1;ah.reset();ah.request_redraw(true);for(var aj=0,af=ah.drawables.length;aj<af;aj++){var ai=ah.drawables[aj];if(ai.init){ai.init()}}}if(ag!==undefined&&am!==undefined){ah.low=Math.max(ag,0);ah.high=Math.min(am,ah.max_high)}ah.reset_overview();ah.request_redraw()}},go_to:function(aj){aj=aj.replace(/ |,/g,"");var an=this,af,ai,ag=aj.split(":"),al=ag[0],am=ag[1];if(am!==undefined){try{var ak=am.split("-");af=parseInt(ak[0],10);ai=parseInt(ak[1],10)}catch(ah){return false}}an.change_chrom(al,af,ai)},move_fraction:function(ah){var af=this;var ag=af.high-af.low;this.move_delta(ah*ag)},move_delta:function(ah){var af=this;var ag=af.high-af.low;if(af.low-ah<af.max_low){af.low=af.max_low;af.high=af.max_low+ag}else{if(af.high-ah>af.max_high){af.high=af.max_high;af.low=af.max_high-ag}else{af.high-=ah;af.low-=ah}}af.request_redraw()},add_drawable:function(af){x.prototype.add_drawable.call(this,af);af.init();this.has_changes=true;this.update_intro_div()},add_label_track:function(af){af.view=this;af.init();this.label_tracks.push(af)},remove_drawable:function(ah,ag){x.prototype.remove_drawable.call(this,ah);if(ag){var af=this;ah.container_div.hide(0,function(){$(this).remove();af.update_intro_div()});this.has_changes=true}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(an,af,am,ag){var al=this,aj=(ag?[ag]:al.drawables),ah;var ag;for(var ak=0;ak<aj.length;ak++){ag=aj[ak];ah=-1;for(var ai=0;ai<al.tracks_to_be_redrawn.length;ai++){if(al.tracks_to_be_redrawn[ai][0]===ag){ah=ai;break}}if(ah<0){al.tracks_to_be_redrawn.push([ag,af,am])}else{al.tracks_to_be_redrawn[ak][1]=af;al.tracks_to_be_redrawn[ak][2]=am}}requestAnimationFrame(function(){al._redraw(an)})},_redraw:function(ap){var am=this.low,ai=this.high;if(am<this.max_low){am=this.max_low}if(ai>this.max_high){ai=this.max_high}var ao=this.high-this.low;if(this.high!==0&&ao<this.min_separation){ai=am+this.min_separation}this.low=Math.floor(am);this.high=Math.ceil(ai);this.resolution=Math.pow(A,Math.ceil(Math.log((this.high-this.low)/Q)/Math.log(A)));this.zoom_res=Math.pow(u,Math.max(0,Math.ceil(Math.log(this.resolution,u)/Math.log(u))));var af=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var al=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var aq=13;this.overview_box.css({left:af,width:Math.max(aq,al)}).show();if(al<aq){this.overview_box.css("left",af-(aq-al)/2)}if(this.overview_highlight){this.overview_highlight.css({left:af,width:al})}this.update_location(this.low,this.high);if(!ap){var ah,ag,an;for(var aj=0,ak=this.tracks_to_be_redrawn.length;aj<ak;aj++){ah=this.tracks_to_be_redrawn[aj][0];ag=this.tracks_to_be_redrawn[aj][1];an=this.tracks_to_be_redrawn[aj][2];if(ah){ah._draw(ag,an)}}this.tracks_to_be_redrawn=[];for(aj=0,ak=this.label_tracks.length;aj<ak;aj++){this.label_tracks[aj]._draw()}}},zoom_in:function(ag,ah){if(this.max_high===0||this.high-this.low<this.min_separation){return}var ai=this.high-this.low,aj=ai/2+this.low,af=(ai/this.zoom_factor)/2;if(ag){aj=ag/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(aj-af);this.high=Math.round(aj+af);this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ag=this.high-this.low,ah=ag/2+this.low,af=(ag*this.zoom_factor)/2;this.low=Math.round(ah-af);this.high=Math.round(ah+af);this.request_redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.nav_container.width(this.container.width());this.request_redraw()},set_overview:function(ah){if(this.overview_drawable){if(this.overview_drawable.dataset_id===ah.dataset_id){return}this.overview_viewport.find(".track").remove()}var ag=ah.copy({content_div:this.overview_viewport}),af=this;ag.header_div.hide();ag.is_overview=true;af.overview_drawable=ag;this.overview_drawable.postdraw_actions=function(){af.overview_highlight.show().height(af.overview_drawable.content_div.height());af.overview_viewport.height(af.overview_drawable.content_div.height()+af.overview_box.outerHeight());af.overview_close.show();af.resize_window()};af.overview_drawable.request_draw();af.has_changes=true},reset_overview:function(){$(".tipsy").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 r=function(ah,al){this.track=ah;this.name=al.name;this.params=[];var at=al.params;for(var ai=0;ai<at.length;ai++){var an=at[ai],ag=an.name,ar=an.label,aj=unescape(an.html),au=an.value,ap=an.type;if(ap==="number"){this.params[this.params.length]=new f(ag,ar,aj,au,an.min,an.max)}else{if(ap=="select"){this.params[this.params.length]=new N(ag,ar,aj,au)}else{console.log("WARNING: unrecognized tool parameter type:",ag,ap)}}}this.parent_div=$("<div/>").addClass("dynamic-tool").hide();this.parent_div.bind("drag",function(aw){aw.stopPropagation()}).click(function(aw){aw.stopPropagation()}).bind("dblclick",function(aw){aw.stopPropagation()});var aq=$("<div class='tool-name'>").appendTo(this.parent_div).text(this.name);var ao=this.params;var am=this;$.each(this.params,function(ax,aA){var az=$("<div>").addClass("param-row").appendTo(am.parent_div);var aw=$("<div>").addClass("param-label").text(aA.label).appendTo(az);var ay=$("<div/>").addClass("slider").html(aA.html).appendTo(az);ay.find(":input").val(aA.value);$("<div style='clear: both;'/>").appendTo(az)});this.parent_div.find("input").click(function(){$(this).select()});var av=$("<div>").addClass("param-row").appendTo(this.parent_div);var ak=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(av);var af=$("<input type='submit'>").attr("value","Run on visible region").css("margin-left","3em").appendTo(av);var am=this;af.click(function(){am.run_on_region()});ak.click(function(){am.run_on_dataset()})};p(r.prototype,{get_param_values_dict:function(){var af={};this.parent_div.find(":input").each(function(){var ag=$(this).attr("name"),ah=$(this).val();af[ag]=JSON.stringify(ah)});return af},get_param_values:function(){var ag=[];var af={};this.parent_div.find(":input").each(function(){var ah=$(this).attr("name"),ai=$(this).val();if(ah){ag[ag.length]=ai}});return ag},run_on_dataset:function(){var af=this;af.run({dataset_id:this.track.original_dataset_id,tool_id:af.name},null,function(ag){show_modal(af.name+" is Running",af.name+" is running on the complete dataset. Tool outputs are in dataset's history.",{Close:hide_modal})})},run_on_region:function(){var ag={dataset_id:this.track.original_dataset_id,chrom:this.track.view.chrom,low:this.track.view.low,high:this.track.view.high,tool_id:this.name},aj=this.track,ah=ag.tool_id+aj.tool_region_and_parameters_str(ag.chrom,ag.low,ag.high),af;if(aj.container===view){var ai=new P(this.name,this.track.view,this.track.container);aj.container.add_drawable(ai);aj.container.remove_drawable(aj);ai.add_drawable(aj);aj.container_div.appendTo(ai.content_div);af=ai}else{af=aj.container}var ak=new aj.constructor(ah,view,af,"hda");ak.init_for_tool_data();ak.change_mode(aj.mode);af.add_drawable(ak);ak.content_div.text("Starting job.");this.run(ag,ak,function(al){ak.dataset_id=al.dataset_id;ak.content_div.text("Running job.");ak.init()})},run:function(ag,ah,ai){$.extend(ag,this.get_param_values_dict());var af=function(){$.getJSON(rerun_tool_url,ag,function(aj){if(aj==="no converter"){ah.container_div.addClass("error");ah.content_div.text(J)}else{if(aj.error){ah.container_div.addClass("error");ah.content_div.text(w+aj.message)}else{if(aj==="pending"){ah.container_div.addClass("pending");ah.content_div.text("Converting input data so that it can be used quickly with tool.");setTimeout(af,2000)}else{ai(aj)}}}})};af()}});var N=function(ag,af,ah,ai){this.name=ag;this.label=af;this.html=ah;this.value=ai};var f=function(ah,ag,aj,ak,ai,af){N.call(this,ah,ag,aj,ak);this.min=ai;this.max=af};var g=function(ag,af,ah,ai){this.name=ag;this.index=af;this.tool_id=ah;this.tool_exp_name=ai};var V=function(ag,af,ah,ai){g.call(this,ag,af,ah,ai);this.low=-Number.MAX_VALUE;this.high=Number.MAX_VALUE;this.min=Number.MAX_VALUE;this.max=-Number.MAX_VALUE;this.container=null;this.slider=null;this.slider_label=null};p(V.prototype,{applies_to:function(af){if(af.length>this.index){return true}return false},keep:function(af){if(!this.applies_to(af)){return true}var ag=af[this.index];return(isNaN(ag)||(ag>=this.low&&ag<=this.high))},update_attrs:function(ag){var af=false;if(!this.applies_to(ag)){return af}if(ag[this.index]<this.min){this.min=Math.floor(ag[this.index]);af=true}if(ag[this.index]>this.max){this.max=Math.ceil(ag[this.index]);af=true}return af},update_ui_elt:function(){if(this.min!=this.max){this.container.show()}else{this.container.hide()}var ah=function(ak,ai){var aj=ai-ak;return(aj<=2?0.01:1)};var ag=this.slider.slider("option","min"),af=this.slider.slider("option","max");if(this.min<ag||this.max>af){this.slider.slider("option","min",this.min);this.slider.slider("option","max",this.max);this.slider.slider("option","step",ah(this.min,this.max));this.slider.slider("option","values",[this.min,this.max])}}});var aa=function(aq,ay){this.track=aq;this.filters=[];for(var at=0;at<ay.length;at++){var au=ay[at],az=au.name,af=au.type,ah=au.index,ax=au.tool_id,aw=au.tool_exp_name;if(af==="int"||af==="float"){this.filters[at]=new V(az,ah,ax,aw)}else{console.log("ERROR: unsupported filter: ",az,af)}}var ai=function(aA,aB,aC){aA.click(function(){var aD=aB.text();max=parseFloat(aC.slider("option","max")),input_size=(max<=1?4:max<=1000000?max.toString().length:6),multi_value=false;if(aC.slider("option","values")){input_size=2*input_size+1;multi_value=true}aB.text("");$("<input type='text'/>").attr("size",input_size).attr("maxlength",input_size).attr("value",aD).appendTo(aB).focus().select().click(function(aE){aE.stopPropagation()}).blur(function(){$(this).remove();aB.text(aD)}).keyup(function(aI){if(aI.keyCode===27){$(this).trigger("blur")}else{if(aI.keyCode===13){var aG=aC.slider("option","min"),aE=aC.slider("option","max"),aH=function(aJ){return(isNaN(aJ)||aJ>aE||aJ<aG)},aF=$(this).val();if(!multi_value){aF=parseFloat(aF);if(aH(aF)){alert("Parameter value must be in the range ["+aG+"-"+aE+"]");return $(this)}}else{aF=aF.split("-");aF=[parseFloat(aF[0]),parseFloat(aF[1])];if(aH(aF[0])||aH(aF[1])){alert("Parameter value must be in the range ["+aG+"-"+aE+"]");return $(this)}}aC.slider((multi_value?"values":"value"),aF)}}})})};this.parent_div=$("<div/>").addClass("filters").hide();this.parent_div.bind("drag",function(aA){aA.stopPropagation()}).click(function(aA){aA.stopPropagation()}).bind("dblclick",function(aA){aA.stopPropagation()}).bind("keydown",function(aA){aA.stopPropagation()});var av=$("<div/>").addClass("sliders").appendTo(this.parent_div);var an=this;$.each(this.filters,function(aD,aF){aF.container=$("<div/>").addClass("filter-row slider-row").appendTo(av);var aE=$("<div/>").addClass("elt-label").appendTo(aF.container);var aC=$("<span/>").addClass("slider-name").text(aF.name+" ").appendTo(aE);var aB=$("<span/>");var aH=$("<span/>").addClass("slider-value").appendTo(aE).append("[").append(aB).append("]");var aA=$("<div/>").addClass("slider").appendTo(aF.container);aF.control_element=$("<div/>").attr("id",aF.name+"-filter-control").appendTo(aA);var aG=[0,0];aF.control_element.slider({range:true,min:Number.MAX_VALUE,max:-Number.MIN_VALUE,values:[0,0],slide:function(aJ,aK){var aI=aK.values;aB.text(aI[0]+"-"+aI[1]);aF.low=aI[0];aF.high=aI[1];an.track.request_draw(true,true)},change:function(aI,aJ){aF.control_element.slider("option","slide").call(aF.control_element,aI,aJ)}});aF.slider=aF.control_element;aF.slider_label=aB;ai(aH,aB,aF.control_element);$("<div style='clear: both;'/>").appendTo(aF.container)});if(this.filters.length!==0){var ak=$("<div/>").addClass("param-row").appendTo(av);var am=$("<input type='submit'/>").attr("value","Run on complete dataset").appendTo(ak);var ag=this;am.click(function(){ag.run_on_dataset()})}var ap=$("<div/>").addClass("display-controls").appendTo(this.parent_div),ar,al,ao,aj={Transparency:function(aA){an.alpha_filter=aA},Height:function(aA){an.height_filter=aA}};$.each(aj,function(aC,aB){ar=$("<div/>").addClass("filter-row").appendTo(ap),al=$("<span/>").addClass("elt-label").text(aC+":").appendTo(ar),ao=$("<select/>").attr("name",aC+"_dropdown").css("float","right").appendTo(ar);$("<option/>").attr("value",-1).text("== None ==").appendTo(ao);for(var aA=0;aA<an.filters.length;aA++){$("<option/>").attr("value",aA).text(an.filters[aA].name).appendTo(ao)}ao.change(function(){$(this).children("option:selected").each(function(){var aD=parseInt($(this).val());aj[aC]((aD>=0?an.filters[aD]:null));an.track.request_draw(true,true)})});$("<div style='clear: both;'/>").appendTo(ar)});$("<div style='clear: both;'/>").appendTo(this.parent_div)};p(aa.prototype,{reset_filters:function(){for(var af=0;af<this.filters.length;af++){filter=this.filters[af];filter.slider.slider("option","values",[filter.min,filter.max])}this.alpha_filter=null;this.height_filter=null},run_on_dataset:function(){var an=function(ar,ap,aq){if(!(ap in ar)){ar[ap]=aq}return ar[ap]};var ah={},af,ag,ai;for(var aj=0;aj<this.filters.length;aj++){af=this.filters[aj];if(af.tool_id){if(af.min!=af.low){ag=an(ah,af.tool_id,[]);ag[ag.length]=af.tool_exp_name+" >= "+af.low}if(af.max!=af.high){ag=an(ah,af.tool_id,[]);ag[ag.length]=af.tool_exp_name+" <= "+af.high}}}var al=[];for(var ao in ah){al[al.length]=[ao,ah[ao]]}var am=al.length;(function ak(aw,at){var aq=at[0],ar=aq[0],av=aq[1],au="("+av.join(") and (")+")",ap={cond:au,input:aw,target_dataset_id:aw,tool_id:ar},at=at.slice(1);$.getJSON(run_tool_url,ap,function(ax){if(ax.error){show_modal("Filter Dataset","Error running tool "+ar,{Close:hide_modal})}else{if(at.length===0){show_modal("Filtering Dataset","Filter(s) are running on the complete dataset. Outputs are in dataset's history.",{Close:hide_modal})}else{ak(ax.dataset_id,at)}}})})(this.track.dataset_id,al)}});var B=function(af,ag){L.Scaler.call(this,ag);this.filter=af};B.prototype.gen_val=function(af){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(af[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var E=function(af){this.track=af.track;this.params=af.params;this.values={};this.restore_values((af.saved_values?af.saved_values:{}));this.onchange=af.onchange};p(E.prototype,{restore_values:function(af){var ag=this;$.each(this.params,function(ah,ai){if(af[ai.key]!==undefined){ag.values[ai.key]=af[ai.key]}else{ag.values[ai.key]=ai.default_value}})},build_form:function(){var ai=this;var af=$("<div />");var ah;function ag(am,aj){for(var ao=0;ao<am.length;ao++){ah=am[ao];if(ah.hidden){continue}var ak="param_"+ao;var at=ai.values[ah.key];var av=$("<div class='form-row' />").appendTo(aj);av.append($("<label />").attr("for",ak).text(ah.label+":"));if(ah.type==="bool"){av.append($('<input type="checkbox" />').attr("id",ak).attr("name",ak).attr("checked",at))}else{if(ah.type==="text"){av.append($('<input type="text"/>').attr("id",ak).val(at).click(function(){$(this).select()}))}else{if(ah.type=="select"){var aq=$("<select />").attr("id",ak);for(var an=0;an<ah.options.length;an++){$("<option/>").text(ah.options[an].label).attr("value",ah.options[an].value).appendTo(aq)}aq.val(at);av.append(aq)}else{if(ah.type==="color"){var ap=$("<input />").attr("id",ak).attr("name",ak).val(at);var ar=$("<div class='tipsy tipsy-west' style='position: absolute;' />").hide();var al=$("<div style='background-color: black; padding: 10px;'></div>").appendTo(ar);var au=$("<div/>").appendTo(al).farbtastic({width:100,height:100,callback:ap,color:at});$("<div />").append(ap).append(ar).appendTo(av).bind("click",function(aw){ar.css({left:$(this).position().left+$(ap).width()+5,top:$(this).position().top-($(ar).height()/2)+($(ap).height()/2)}).show();$(document).bind("click.color-picker",function(){ar.hide();$(document).unbind("click.color-picker")});aw.stopPropagation()})}else{av.append($("<input />").attr("id",ak).attr("name",ak).val(at))}}}}if(ah.help){av.append($("<div class='help'/>").text(ah.help))}}}ag(this.params,af);return af},update_from_form:function(af){var ah=this;var ag=false;$.each(this.params,function(ai,ak){if(!ak.hidden){var al="param_"+ai;var aj=af.find("#"+al).val();if(ak.type==="float"){aj=parseFloat(aj)}else{if(ak.type==="int"){aj=parseInt(aj)}else{if(ak.type==="bool"){aj=af.find("#"+al).is(":checked")}}}if(aj!==ah.values[ak.key]){ah.values[ak.key]=aj;ag=true}}});if(ag){this.onchange()}}});var b=function(af,ai,ah,ag,aj){this.track=af;this.index=ai;this.low=ai*Q*ah;this.high=(ai+1)*Q*ah;this.resolution=ah;this.html_elt=$("<div class='track-tile'/>").append(ag);this.data=aj;this.stale=false};b.prototype.predisplay_actions=function(){};var k=function(af,ai,ah,ag,aj,ak){b.call(this,af,ai,ah,ag,aj);this.max_val=ak};p(k.prototype,b.prototype);var O=function(ah,am,ai,ag,ak,al,aq,ao){b.call(this,ah,am,ai,ag,ak);this.mode=al;this.message=aq;this.feature_mapper=ao;if(this.message){var ag=this.html_elt.children()[0],ap=$("<div/>").addClass("tile-message").text(this.message).css({height:C-1,width:ag.width}).prependTo(this.html_elt),aj=$("<a href='javascript:void(0);'/>").addClass("icon more-down").appendTo(ap),af=$("<a href='javascript:void(0);'/>").addClass("icon more-across").appendTo(ap);var an=this;aj.click(function(){an.stale=true;ah.data_manager.get_more_data(an.low,an.high,ah.mode,an.resolution,{},ah.data_manager.DEEP_DATA_REQ);ah.request_draw()}).dblclick(function(ar){ar.stopPropagation()});af.click(function(){an.stale=true;ah.data_manager.get_more_data(an.low,an.high,ah.mode,an.resolution,{},ah.data_manager.BROAD_DATA_REQ);ah.request_draw()}).dblclick(function(ar){ar.stopPropagation()})}};p(O.prototype,b.prototype);O.prototype.predisplay_actions=function(){var ag=this,af={};if(ag.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).siblings(".feature-popup").remove()}).mousemove(function(ar){if(!this.hovered){return}var am=$(this).offset(),aq=ar.pageX-am.left,ap=ar.pageY-am.top,aw=ag.feature_mapper.get_feature_data(aq,ap),an=(aw?aw[0]:null);$(this).siblings(".feature-popup").each(function(){if(!an||$(this).attr("id")!==an.toString()){$(this).remove()}});if(aw){var ai=af[an];if(!ai){var an=aw[0],at={name:aw[3],start:aw[1],end:aw[2],strand:aw[4]},al=ag.track.filters_manager.filters,ak;for(var ao=0;ao<al.length;ao++){ak=al[ao];at[ak.name]=aw[ak.index]}var ai=$("<div/>").attr("id",an).addClass("feature-popup"),ax=$("<table/>"),av,au,ay;for(av in at){au=at[av];ay=$("<tr/>").appendTo(ax);$("<th/>").appendTo(ay).text(av);$("<td/>").attr("align","left").appendTo(ay).text(typeof(au)=="number"?Z(au,2):au)}ai.append($("<div class='feature-popup-inner'>").append(ax));af[an]=ai}ai.appendTo($(ag.html_elt).parent());var aj=aq+parseInt(ag.html_elt.css("left"))-ai.width()/2,ah=ap+parseInt(ag.html_elt.css("top"))+7;ai.css("left",aj+"px").css("top",ah+"px")}else{if(!ar.isPropagationStopped()){ar.stopPropagation();$(this).siblings().each(function(){$(this).trigger(ar)})}}}).mouseleave(function(){$(this).siblings(".feature-popup").remove()})};var i=function(ai,ag,af,ah,ak,aj,al){q.call(this,ai,ag,af,{},"draghandle");this.data_url=(aj?aj:default_data_url);this.data_url_extra_params={};this.data_query_wait=(al?al:K);this.dataset_check_url=converted_datasets_state_url;this.data_manager=(ak?ak:new S(H,this));this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div)}};p(i.prototype,q.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},q.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"overview-icon",on_click_fn:function(af){af.view.set_overview(af)}},q.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(af){af.filters_div.toggle();af.filters_manager.reset_filters()}},{name:"tools_icon",title:"Tools",css_class:"tools-icon",on_click_fn:function(af){af.dynamic_tool_div.toggle();if(af.dynamic_tool_div.is(":visible")){af.set_name(af.name+af.tool_region_and_parameters_str())}else{af.revert_name()}$(".tipsy").remove()}},q.prototype.action_icons_def[2]],can_draw:function(){if(this.dataset_id&&q.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 af=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(af)}this.name_div=$("<div/>").addClass("track-name").appendTo(af).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return af},build_action_icons:function(){q.prototype.build_action_icons.call(this,this.action_icons_def);var ag=this;if(ag.display_modes!==undefined){var ak=(ag.config&&ag.config.values.mode?ag.config.values.mode:ag.display_modes[0]);ag.mode=ak;this.action_icons.mode_icon.attr("title","Set display mode (now: "+ag.mode+")");var ai={};for(var ah=0,af=ag.display_modes.length;ah<af;ah++){var aj=ag.display_modes[ah];ai[aj]=function(al){return function(){ag.change_mode(al);ag.icons_div.show();ag.container_div.mouseleave(function(){ag.icons_div.hide()})}}(aj)}make_popupmenu(this.action_icons.mode_icon,ai)}},hide_contents:function(){this.content_div.children().remove();this.content_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.content_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof ab){return"LabelTrack"}else{if(this instanceof z){return"ReferenceTrack"}else{if(this instanceof j){return"LineTrack"}else{if(this instanceof W){return"ReadTrack"}else{if(this instanceof U){return"VcfTrack"}else{if(this instanceof h){return"CompositeTrack"}else{if(this instanceof d){return"FeatureTrack"}}}}}}}return""},init:function(){var af=this;af.enabled=false;af.tile_cache.clear();af.data_manager.clear();af.content_div.css("height","auto");af.container_div.removeClass("nodata error pending");if(!af.dataset_id){return}$.getJSON(converted_datasets_state_url,{hda_ldda:af.hda_ldda,dataset_id:af.dataset_id,chrom:af.view.chrom},function(ag){if(!ag||ag==="error"||ag.kind==="error"){af.container_div.addClass("error");af.content_div.text(o);if(ag.message){var ah=$(" <a href='javascript:void(0);'></a>").text("View error").click(function(){show_modal("Trackster Error","<pre>"+ag.message+"</pre>",{Close:hide_modal})});af.content_div.append(ah)}}else{if(ag==="no converter"){af.container_div.addClass("error");af.content_div.text(J)}else{if(ag==="no data"||(ag.data!==undefined&&(ag.data===null||ag.data.length===0))){af.container_div.addClass("nodata");af.content_div.text(D)}else{if(ag==="pending"){af.container_div.addClass("pending");af.content_div.text(t);setTimeout(function(){af.init()},af.data_query_wait)}else{if(ag.status==="data"){if(ag.valid_chroms){af.valid_chroms=ag.valid_chroms;af.update_icons()}af.content_div.text(Y);if(af.view.chrom){af.content_div.text("");af.content_div.css("height",af.height_px+"px");af.enabled=true;$.when(af.predraw_init()).done(function(){af.container_div.removeClass("nodata error pending");af.request_draw()})}}}}}}});this.update_icons()},predraw_init:function(){}});var M=function(aj,ah,ag,ai,am,al,ak){i.call(this,aj,ah,ag,ai,ak);var af=this,ah=af.view;m(af.container_div,af.drag_handle_class,".group",af);this.filters_manager=new aa(this,(am!==undefined?am:{}));this.filters_available=false;this.filters_visible=false;this.tool=(al!==undefined&&obj_length(al)>0?new r(this,al):undefined);this.tile_cache=new c(R);if(this.header_div){if(this.filters_manager){this.filters_div=this.filters_manager.parent_div;this.header_div.after(this.filters_div)}if(this.tool){this.dynamic_tool_div=this.tool.parent_div;this.header_div.after(this.dynamic_tool_div)}}};p(M.prototype,q.prototype,i.prototype,{copy:function(af){var ag=new this.constructor(this.name,this.view,af,this.hda_ldda,this.dataset_id,this.prefs,this.filters,this.tool,this.data_manager);ag.enabled=this.enabled;return ag},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,}},from_dict:function(ah,ag){var af=new this.constructor(ah.name,view,ag,ah.hda_ldda,ah.dataset_id,ah.prefs,ah.filters,ah.tool);if(ah.mode){af.change_mode(ah.mode)}return af},change_mode:function(ag){var af=this;af.mode=ag;af.config.values.mode=ag;af.tile_cache.clear();af.request_draw();this.action_icons.mode_icon.attr("title","Set display mode (now: "+af.mode+")");return af},update_icons:function(){var af=this;if(af.filters_available){af.action_icons.filters_icon.show()}else{af.action_icons.filters_icon.hide()}if(af.tool){af.action_icons.tools_icon.show()}else{af.action_icons.tools_icon.hide()}},_gen_tile_cache_key:function(ag,ah,af){return ag+"_"+ah+"_"+af},request_draw:function(ag,af){this.view.request_redraw(false,ag,af,this)},_draw:function(ah,ar){if(!this.can_draw()){return}var ap=this.view.low,al=this.view.high,an=al-ap,ai=this.view.container.width(),av=ai/an,ak=this.view.resolution,au=this.content_div;if(this.is_overview){ap=this.view.max_low;al=this.view.max_high;ak=Math.pow(A,Math.ceil(Math.log((view.max_high-view.max_low)/Q)/Math.log(A)));av=ai/(view.max_high-view.max_low)}if(!ar){this.content_div.children().addClass("remove")}this.max_height=0;var ag=Math.floor(ap/ak/Q);var ao=true;var at=[];var af=0;var am=function(aw){return("track" in aw)};while((ag*Q*ak)<al){var aq=this.draw_helper(ah,ai,ag,ak,au,av);if(am(aq)){at.push(aq)}else{ao=false}ag+=1;af++}if(!ar){this.content_div.children(".remove").remove()}var aj=this;if(ao){aj.postdraw_actions(at,ai,av,ar)}},postdraw_actions:function(aj,ak,al,af){var ah=this;var ai=false;for(var ag=0;ag<aj.length;ag++){if(aj[ag].message){ai=true;break}}if(ai){for(var ag=0;ag<aj.length;ag++){tile=aj[ag];if(!tile.message){tile.html_elt.css("padding-top",C)}}}},draw_helper:function(af,aq,au,ar,ai,aj,al,an){var ap=this,ax=this._gen_tile_cache_key(aq,aj,au),av=au*Q*ar,ag=av+Q*ar;var aw=(af?undefined:ap.tile_cache.get(ax));if(aw){ap.show_tile(aw,ai,aj);return aw}var am=function(ay){return("isResolved" in ay)};var ao=true;var at=ap.data_manager.get_data(av,ag,ap.mode,ar,ap.data_url_extra_params);if(am(at)){ao=false}var ak;if(view.reference_track&&aj>view.canvas_manager.char_width_px){ak=view.reference_track.data_manager.get_data(av,ag,ap.mode,ar,view.reference_track.data_url_extra_params);if(am(ak)){ao=false}}if(ao){p(at,an);var aw=ap.draw_tile(at,ap.mode,ar,au,aj,ak);if(aw!==undefined){ap.tile_cache.set(ax,aw);ap.show_tile(aw,ai,aj)}return aw}var ah=$.Deferred();$.when(at,ak).then(function(){view.request_redraw(false,false,false,ap);ah.resolve()});return ah},draw_tile:function(af,aj,ai,ag,ak,ah){console.log("Warning: TiledTrack.draw_tile() not implemented.")},show_tile:function(ah,aj,ak){var ag=this,af=ah.html_elt;ah.predisplay_actions();var ai=(ah.low-(this.is_overview?this.view.max_low:this.view.low))*ak;if(this.left_offset){ai-=this.left_offset}af.css({position:"absolute",top:0,left:ai,height:""});if(af.hasClass("remove")){af.removeClass("remove")}else{aj.append(af)}ag.max_height=Math.max(ag.max_height,af.height());ag.content_div.css("height",ag.max_height+"px");aj.children().css("height",ag.max_height+"px")},_get_tile_bounds:function(af,ag){var ai=af*Q*ag,aj=Q*ag,ah=(ai+aj<=this.view.max_high?ai+aj:this.view.max_high);return[ai,ah]},tool_region_and_parameters_str:function(ah,af,ai){var ag=this,aj=(ah!==undefined&&af!==undefined&&ai!==undefined?ah+":"+af+"-"+ai:"all");return" - region=["+aj+"], parameters=["+ag.tool.get_param_values().join(", ")+"]"},init_for_tool_data:function(){this.data_url=raw_data_url;this.data_query_wait=1000;this.dataset_check_url=dataset_state_url;this.predraw_init=function(){var ag=this;var af=function(){if(ag.data_manager.size()===0){setTimeout(af,300)}else{ag.data_url=default_data_url;ag.data_query_wait=K;ag.dataset_state_url=converted_datasets_state_url;$.getJSON(ag.dataset_state_url,{dataset_id:ag.dataset_id,hda_ldda:ag.hda_ldda},function(ah){})}};af()}}});var ab=function(ag,af){i.call(this,"label",ag,af,false,{});this.container_div.addClass("label-track")};p(ab.prototype,i.prototype,{build_header_div:function(){},init:function(){this.enabled=true},_draw:function(){var ah=this.view,ai=ah.high-ah.low,al=Math.floor(Math.pow(10,Math.floor(Math.log(ai)/Math.log(10)))),af=Math.floor(ah.low/al)*al,aj=this.view.container.width(),ag=$("<div style='position: relative; height: 1.3em;'></div>");while(af<ah.high){var ak=(af-ah.low)/ai*aj;ag.append($("<div class='label'>"+commatize(af)+"</div>").css({position:"absolute",left:ak-1}));af+=al}this.content_div.children(":first").remove();this.content_div.append(ag)}});var h=function(ai,ah,ag,af){this.display_modes=["Histogram","Line","Filled","Intensity"];M.call(this,ai,ah,ag);this.drawables=[];if(af){var al=[],ak;for(var aj=0;aj<af.length;aj++){ak=af[aj];al.push(ak.dataset_id);this.drawables[aj]=ak.copy()}this.enabled=true}this.update_icons();this.obj_type="CompositeTrack"};p(h.prototype,M.prototype,{to_dict:x.prototype.to_dict,add_drawable:x.prototype.add_drawable,from_dict:function(al,af){var ak=new this.constructor(al.name,view,af,al.prefs,view.viewport_container,view);var ag,aj,ai;for(var ah=0;ah<al.drawables.length;ah++){ag=al.drawables[ah];aj=ag.obj_type;if(!aj){aj=ag.track_type}ai=addable_objects[aj].prototype.from_dict(ag);ak.add_drawable(ai)}return ak},change_mode:function(af){M.prototype.change_mode.call(this,af);for(var ag=0;ag<this.drawables.length;ag++){this.drawables[ag].change_mode(af)}},init:function(){this.enabled=true;this.request_draw()},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide()},can_draw:q.prototype.can_draw,draw_helper:function(ag,av,ay,aw,aj,an,ao,aq){var aD=this._gen_tile_cache_key(av,an,ay),aC=(ag?undefined:this.tile_cache.get(aD));if(aC){this.show_tile(aC,aj,an);return aC}var ah,ak=$("<div/>"),al,aA=[],at=[],ai=true;for(var ax=0;ax<this.drawables.length;ax++){al=this.drawables[ax].draw_helper(ag,av,ay,aw,ak,an,ao,aq);if(al instanceof b){aA.push(al)}else{at.push(al);ai=false}}if(ai){var am=aA[0].html_elt.find("canvas"),aB=am.clone(),ar=am.get(0).getContext("2d"),ap=aB.get(0).getContext("2d"),az=ar.getImageData(0,0,ar.canvas.width,ar.canvas.height);ap.putImageData(az,0,0);ap.globalCompositeOperation="darker";var af;for(var ax=1;ax<aA.length;ax++){af=aA[ax].html_elt.find("canvas").get(0);ap.drawImage(af,0,0)}aC=new b(this,ay,aw,aB);this.tile_cache.set(aD,aC);this.show_tile(aC,aj,an)}else{var au=this;$.when.apply($,at).then(function(){au.request_draw()})}return aC}});var z=function(af){M.call(this,"reference",af,{content_div:af.top_labeltrack},{});af.reference_track=this;this.left_offset=200;this.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.data_url_extra_params={dbkey:af.dbkey};this.data_manager=new G(H,this,false)};p(z.prototype,q.prototype,M.prototype,{build_header_div:function(){},init:function(){this.enabled=true},can_draw:q.prototype.can_draw,draw_tile:function(ap,al,ak,ag,aq){var aj=this,ah=Q*ak;if(aq>this.view.canvas_manager.char_width_px){if(ap.data===null){aj.content_div.css("height","0px");return}var ai=this.view.canvas_manager.new_canvas();var ao=ai.getContext("2d");ai.width=Math.ceil(ah*aq+aj.left_offset);ai.height=aj.height_px;ao.font=ao.canvas.manager.default_font;ao.textAlign="center";ap=ap.data;for(var am=0,an=ap.length;am<an;am++){var af=Math.round(am*aq);ao.fillText(ap[am],af+aj.left_offset,10)}return new b(aj,ag,ak,ai,ap)}this.content_div.css("height","0px")}});var j=function(af,am,ag,al,ao,an,aj,ak,ah){var ai=this;this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";M.call(this,af,am,ag,an,aj,ak,ah);this.min_height_px=16;this.max_height_px=400;this.height_px=32;this.hda_ldda=al;this.dataset_id=ao;this.original_dataset_id=ao;this.left_offset=0;this.config=new E({track:this,params:[{key:"name",label:"Name",type:"text",default_value:af},{key:"color",label:"Color",type:"color",default_value: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:this.height_px,hidden:true}],saved_values:an,onchange:function(){ai.set_name(ai.prefs.name);ai.vertical_range=ai.prefs.max_value-ai.prefs.min_value;$("#linetrack_"+ai.dataset_id+"_minval").text(ai.prefs.min_value);$("#linetrack_"+ai.dataset_id+"_maxval").text(ai.prefs.max_value);ai.tile_cache.clear();ai.request_draw()}});this.prefs=this.config.values;this.height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value;this.add_resize_handle()};p(j.prototype,q.prototype,M.prototype,{add_resize_handle:function(){var af=this;var ai=false;var ah=false;var ag=$("<div class='track-resize'>");$(af.container_div).hover(function(){if(af.content_visible){ai=true;ag.show()}},function(){ai=false;if(!ah){ag.hide()}});ag.hide().bind("dragstart",function(aj,ak){ah=true;ak.original_height=$(af.content_div).height()}).bind("drag",function(ak,al){var aj=Math.min(Math.max(al.original_height+al.deltaY,af.min_height_px),af.max_height_px);$(af.content_div).css("height",aj);af.height_px=aj;af.request_draw(true)}).bind("dragend",function(aj,ak){af.tile_cache.clear();ah=false;if(!ai){ag.hide()}af.config.values.height=af.height_px}).appendTo(af.container_div)},predraw_init:function(){var af=this;af.vertical_range=undefined;return $.getJSON(af.data_url,{stats:true,chrom:af.view.chrom,low:null,high:null,hda_ldda:af.hda_ldda,dataset_id:af.dataset_id},function(ag){af.container_div.addClass("line-track");var aj=ag.data;if(isNaN(parseFloat(af.prefs.min_value))||isNaN(parseFloat(af.prefs.max_value))){var ah=aj.min;var al=aj.max;ah=Math.floor(Math.min(0,Math.max(ah,aj.mean-2*aj.sd)));al=Math.ceil(Math.max(0,Math.min(al,aj.mean+2*aj.sd)));af.prefs.min_value=ah;af.prefs.max_value=al;$("#track_"+af.dataset_id+"_minval").val(af.prefs.min_value);$("#track_"+af.dataset_id+"_maxval").val(af.prefs.max_value)}af.vertical_range=af.prefs.max_value-af.prefs.min_value;af.total_frequency=aj.total_frequency;af.container_div.find(".yaxislabel").remove();var ak=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+af.dataset_id+"_minval").text(Z(af.prefs.min_value,3));var ai=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+af.dataset_id+"_maxval").text(Z(af.prefs.max_value,3));ai.css({position:"absolute",top:"24px",left:"10px"});ai.prependTo(af.container_div);ak.css({position:"absolute",bottom:"2px",left:"10px"});ak.prependTo(af.container_div)})},draw_tile:function(ar,ak,aj,ah,aq){if(this.vertical_range===undefined){return}var af=this._get_tile_bounds(ah,aj),al=af[0],ap=af[1],ag=Math.ceil((ap-al)*aq),an=this.height_px;var ai=this.view.canvas_manager.new_canvas();ai.width=ag,ai.height=an;var ao=ai.getContext("2d");var am=new L.LinePainter(ar.data,al,ap,this.prefs,ak);am.draw(ao,ag,an);return new b(this.track,ah,aj,ai,ar.data)}});var d=function(af,am,ag,al,ao,an,aj,ak,ah){var ai=this;this.display_modes=["Auto","Histogram","Dense","Squish","Pack"];M.call(this,af,am,ag,an,aj,ak,ah);this.config=new E({track:this,params:[{key:"name",label:"Name",type:"text",default_value:af},{key:"block_color",label:"Block color",type:"color",default_value:get_random_color()},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"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},],saved_values:an,onchange:function(){ai.set_name(ai.prefs.name);ai.tile_cache.clear();ai.set_painter_from_config();ai.request_draw()}});this.prefs=this.config.values;this.height_px=0;this.container_div.addClass("feature-track");this.hda_ldda=al;this.dataset_id=ao;this.original_dataset_id=ao;this.show_labels_scale=0.001;this.showing_details=false;this.summary_draw_height=30;this.inc_slots={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};p(d.prototype,q.prototype,M.prototype,{set_painter_from_config:function(){if(this.config.values.connector_style=="arcs"){this.painter=L.ArcLinkedFeaturePainter}else{this.painter=L.LinkedFeaturePainter}},postdraw_actions:function(at,an,aj,ah){M.prototype.postdraw_actions.call(this,at,ah);var am=this;if(ah){var ax=am.content_div.children();var aw=false;for(var ap=ax.length-1,aq=0;ap>=aq;ap--){var ai=$(ax[ap]);if(aw){ai.remove()}else{if(ai.children().length!==0){aw=true}}}}if(am.mode=="Histogram"){var ag=-1;for(var ap=0;ap<at.length;ap++){var ao=at[ap].max_val;if(ao>ag){ag=ao}}for(var ap=0;ap<at.length;ap++){var av=at[ap];if(av.max_val!==ag){av.html_elt.remove();am.draw_helper(true,an,av.index,av.resolution,av.html_elt.parent(),aj,[],{max:ag})}}}if(am.filters_manager){var ak=am.filters_manager.filters;for(var ar=0;ar<ak.length;ar++){ak[ar].update_ui_elt()}var au=false,af,al;for(var ap=0;ap<at.length;ap++){if(at[ap].data.length){af=at[ap].data[0];for(var ar=0;ar<ak.length;ar++){al=ak[ar];if(al.applies_to(af)&&al.min!==al.max){au=true;break}}}}if(am.filters_available!==au){am.filters_available=au;if(!am.filters_available){am.filters_div.hide()}am.update_icons()}}},update_auto_mode:function(af){var af;if(this.mode=="Auto"){if(af=="no_detail"){af="feature spans"}else{if(af=="summary_tree"){af="coverage histogram"}}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+af+")")}},incremental_slots:function(aj,ag,ai){var ah=this.view.canvas_manager.dummy_context,af=this.inc_slots[aj];if(!af||(af.mode!==ai)){af=new (s.FeatureSlotter)(aj,ai==="Pack",y,function(ak){return ah.measureText(ak)});af.mode=ai;this.inc_slots[aj]=af}return af.slot_features(ag)},get_summary_tree_data:function(aj,am,ah,av){if(av>ah-am){av=ah-am}var aq=Math.floor((ah-am)/av),au=[],ai=0;var ak=0,al=0,ap,at=0,an=[],ar,ao;var ag=function(ay,ax,az,aw){ay[0]=ax+az*aw;ay[1]=ax+(az+1)*aw};while(at<av&&ak!==aj.length){var af=false;for(;at<av&&!af;at++){ag(an,am,at,aq);for(al=ak;al<aj.length;al++){ap=aj[al].slice(1,3);if(is_overlap(ap,an)){af=true;break}}if(af){break}}data_start_index=al;au[au.length]=ar=[an[0],0];for(;al<aj.length;al++){ap=aj[al].slice(1,3);if(is_overlap(ap,an)){ar[1]++}else{break}}if(ar[1]>ai){ai=ar[1]}at++}return{max:ai,delta:aq,data:au}},draw_tile:function(au,ax,aB,aF,ap,ai){var ay=this,ak=ay._get_tile_bounds(aF,aB),aI=ak[0],ag=ak[1],aw=ag-aI,az=Math.ceil(aw*ap),aO=25,aj=this.left_offset,av,al;if(ax==="Auto"){if(au.dataset_type==="summary_tree"){ax=au.dataset_type}else{if(au.extra_info==="no_detail"||ay.is_overview){ax="no_detail"}else{var aN=au.data;if(this.view.high-this.view.low>I){ax="Squish"}else{ax="Pack"}}}this.update_auto_mode(ax)}if(ax==="summary_tree"||ax==="Histogram"){al=this.summary_draw_height;this.container_div.find(".yaxislabel").remove();var af=$("<div />").addClass("yaxislabel");af.text(au.max);af.css({position:"absolute",top:"24px",left:"10px",color:this.prefs.label_color});af.prependTo(this.container_div);var ah=this.view.canvas_manager.new_canvas();ah.width=az+aj;ah.height=al+T;if(au.dataset_type!="summary_tree"){var aq=this.get_summary_tree_data(au.data,aI,ag,200);if(au.max){aq.max=au.max}au=aq}var aK=new L.SummaryTreePainter(au,aI,ag,this.prefs);var aA=ah.getContext("2d");aA.translate(aj,T);aK.draw(aA,az,al);return new k(ay,aF,aB,ah,au.data,au.max)}var av,an=1;if(ax==="no_detail"||ax==="Squish"||ax==="Pack"){an=this.incremental_slots(ap,au.data,ax);av=this.inc_slots[ap].slots}var ao=[];if(au.data){var ar=this.filters_manager.filters;for(var aC=0,aE=au.data.length;aC<aE;aC++){var am=au.data[aC];var aD=false;var at;for(var aH=0,aM=ar.length;aH<aM;aH++){at=ar[aH];at.update_attrs(am);if(!at.keep(am)){aD=true;break}}if(!aD){ao.push(am)}}}var aL=(this.filters_manager.alpha_filter?new B(this.filters_manager.alpha_filter):null);var aJ=(this.filters_manager.height_filter?new B(this.filters_manager.height_filter):null);var aK=new (this.painter)(ao,aI,ag,this.prefs,ax,aL,aJ,ai);var al=Math.max(ad,aK.get_required_height(an,az));var ah=this.view.canvas_manager.new_canvas();var aG=null;ah.width=az+aj;ah.height=al;var aA=ah.getContext("2d");aA.fillStyle=this.prefs.block_color;aA.font=aA.canvas.manager.default_font;aA.textAlign="right";this.container_div.find(".yaxislabel").remove();if(au.data){aA.translate(aj,0);aG=aK.draw(aA,az,al,av);aG.translation=-aj}return new O(ay,aF,aB,ah,au.data,ax,au.message,aG)}});var U=function(af,al,ag,ak,an,am,ai,aj,ah){d.call(this,af,al,ag,ak,an,am,ai,aj,ah);this.config=new E({track:this,params:[{key:"name",label:"Name",type:"text",default_value:af},{key:"block_color",label:"Block color",type:"color",default_value: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:am,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};p(U.prototype,q.prototype,M.prototype,d.prototype);var W=function(af,al,ag,ak,ao,an,ai,ah){d.call(this,af,al,ag,ak,ao,an,ai,ah);var aj=get_random_color(),am=get_random_color([aj,"#ffffff"]);this.config=new E({track:this,params:[{key:"name",label:"Name",type:"text",default_value:af},{key:"block_color",label:"Block and sense strand color",type:"color",default_value:aj},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:am},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},],saved_values:an,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()};p(W.prototype,q.prototype,M.prototype,d.prototype);X.View=ac;X.DrawableGroup=P;X.LineTrack=j;X.FeatureTrack=d;X.ReadTrack=W;X.VcfTrack=U;X.CompositeTrack=h};var slotting_module=function(c,b){var e=c("class").extend;var d=2,a=5;b.FeatureSlotter=function(i,h,f,g){this.slots={};this.start_end_dct={};this.w_scale=i;this.include_label=h;this.max_rows=f;this.measureText=g};e(b.FeatureSlotter.prototype,{slot_features:function(m){var p=this.w_scale,s=this.slots,h=this.start_end_dct,y=[],A=[],n=0,z=this.max_rows;for(var w=0,x=m.length;w<x;w++){var l=m[w],o=l[0];if(s[o]!==undefined){n=Math.max(n,s[o]);A.push(s[o])}else{y.push(w)}}var q=function(F,G){for(var E=0;E<=z;E++){var C=false,H=h[E];if(H!==undefined){for(var B=0,D=H.length;B<D;B++){var i=H[B];if(G>i[0]&&F<i[1]){C=true;break}}}if(!C){return E}}return -1};for(var w=0,x=y.length;w<x;w++){var l=m[y[w]],o=l[0],u=l[1],f=l[2],r=l[3],g=Math.floor(u*p),k=Math.ceil(f*p),v=this.measureText(r).width,j;if(r!==undefined&&this.include_label){v+=(d+a);if(g-v>=0){g-=v;j="left"}else{k+=v;j="right"}}var t=q(g,k);if(t>=0){if(h[t]===undefined){h[t]=[]}h[t].push([g,k]);s[o]=t;n=Math.max(n,t)}else{}}return n+1}})};var painters_module=function(i,x){var u=i("class").extend;var p=function(I,A,G,z,F,D){if(D===undefined){D=4}var C=z-A;var B=F-G;var E=Math.floor(Math.sqrt(C*C+B*B)/D);var J=C/E;var H=B/E;var y;for(y=0;y<E;y++,A+=J,G+=H){if(y%2!==0){continue}I.fillRect(A,G,D,1)}};var q=function(B,A,z,E){var D=A-E/2,C=A+E/2,F=z-Math.sqrt(E*3/2);B.beginPath();B.moveTo(D,F);B.lineTo(C,F);B.lineTo(A,z);B.lineTo(D,F);B.strokeStyle=this.fillStyle;B.fill();B.stroke();B.closePath()};var d=function(y){this.default_val=(y?y:1)};d.prototype.gen_val=function(y){return this.default_val};var l=function(A,C,y,z,B){this.data=A;this.view_start=C;this.view_end=y;this.prefs=u({},this.default_prefs,z);this.mode=B};l.prototype.default_prefs={};var v=function(A,C,y,z,B){l.call(this,A,C,y,z,B)};v.prototype.default_prefs={show_counts:false};v.prototype.draw=function(M,z,L){var E=this.view_start,O=this.view_end-this.view_start,N=z/O;var J=this.data.data,I=this.data.delta,G=this.data.max,B=L;delta_x_px=Math.ceil(I*N);M.save();for(var C=0,D=J.length;C<D;C++){var H=Math.floor((J[C][0]-E)*N);var F=J[C][1];if(!F){continue}var K=F/G*L;if(F!==0&&K<1){K=1}M.fillStyle=this.prefs.block_color;M.fillRect(H,B-K,delta_x_px,K);var A=4;if(this.prefs.show_counts&&(M.measureText(F).width+A)<delta_x_px){M.fillStyle=this.prefs.label_color;M.textAlign="center";M.fillText(F,H+(delta_x_px/2),10)}}M.restore()};var b=function(y,C,E,F,A){l.call(this,y,C,E,F,A);if(this.prefs.min_value===undefined){var G=Infinity;for(var z=0,B=this.data.length;z<B;z++){G=Math.min(G,this.data[z][1])}this.prefs.min_value=G}if(this.prefs.max_value===undefined){var D=-Infinity;for(var z=0,B=this.data.length;z<B;z++){D=Math.max(D,this.data[z][1])}this.prefs.max_value=D}};b.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Histogram",color:"#000",overflow_color:"#F66"};b.prototype.draw=function(N,M,K){var F=false,H=this.prefs.min_value,D=this.prefs.max_value,J=D-H,z=K,A=this.view_start,L=this.view_end-this.view_start,B=M/L,I=this.mode,T=this.data;N.save();var U=Math.round(K+H/J*K);if(I!=="Intensity"){N.fillStyle="#aaa";N.fillRect(0,U,M,1)}N.beginPath();var R,E,C;if(T.length>1){C=Math.ceil((T[1][0]-T[0][0])*B)}else{C=10}for(var O=0,P=T.length;O<P;O++){N.fillStyle=this.prefs.color;R=Math.round((T[O][0]-A)*B);E=T[O][1];var Q=false,G=false;if(E===null){if(F&&I==="Filled"){N.lineTo(R,z)}F=false;continue}if(E<H){G=true;E=H}else{if(E>D){Q=true;E=D}}if(I==="Histogram"){E=Math.round(E/J*z);N.fillRect(R,U,C,-E)}else{if(I==="Intensity"){E=255-Math.floor((E-H)/J*255);N.fillStyle="rgb("+E+","+E+","+E+")";N.fillRect(R,0,C,z)}else{E=Math.round(z-(E-H)/J*z);if(F){N.lineTo(R,E)}else{F=true;if(I==="Filled"){N.moveTo(R,z);N.lineTo(R,E)}else{N.moveTo(R,E)}}}}N.fillStyle=this.prefs.overflow_color;if(Q||G){var S;if(I==="Histogram"||I==="Intensity"){S=C}else{R-=2;S=4}if(Q){N.fillRect(R,0,S,3)}if(G){N.fillRect(R,z-3,S,3)}}N.fillStyle=this.prefs.color}if(I==="Filled"){if(F){N.lineTo(R,U);N.lineTo(0,U)}N.fill()}else{N.stroke()}N.restore()};var m=function(y){this.feature_positions={};this.slot_height=y;this.translation=0;this.y_translation=0};m.prototype.map_feature_data=function(z,B,y,A){if(!this.feature_positions[B]){this.feature_positions[B]=[]}this.feature_positions[B].push({data:z,x_start:y,x_end:A})};m.prototype.get_feature_data=function(z,D){var C=Math.floor((D-this.y_translation)/this.slot_height),B;if(!this.feature_positions[C]){return null}z+=this.translation;for(var A=0;A<this.feature_positions[C].length;A++){B=this.feature_positions[C][A];if(z>=B.x_start&&z<=B.x_end){return B.data}}};var o=function(A,D,y,z,C,E,B){l.call(this,A,D,y,z,C);this.alpha_scaler=(E?E:new d());this.height_scaler=(B?B:new d())};o.prototype.default_prefs={block_color:"#FFF",connector_color:"#FFF"};u(o.prototype,{get_required_height:function(A,z){var y=y_scale=this.get_row_height(),B=this.mode;if(B==="no_detail"||B==="Squish"||B==="Pack"){y=A*y_scale}return y+this.get_top_padding(z)+this.get_bottom_padding(z)},get_top_padding:function(y){return 0},get_bottom_padding:function(y){return Math.max(Math.round(this.get_row_height()/2),5)},draw:function(K,I,G,F){var Q=this.data,D=this.view_start,M=this.view_end;K.save();K.fillStyle=this.prefs.block_color;K.textAlign="right";var H=this.view_end-this.view_start,E=I/H,L=this.get_row_height(),P=new m(L),B;for(var N=0,O=Q.length;N<O;N++){var A=Q[N],C=A[0],J=A[1],y=A[2],z=(F&&F[C]!==undefined?F[C]:null);if((J<M&&y>D)&&(this.mode=="Dense"||z!==null)){B=this.draw_element(K,this.mode,A,z,D,M,E,L,I);P.map_feature_data(A,z,B[0],B[1])}}K.restore();P.y_translation=this.get_top_padding(I);return P},draw_element:function(E,A,G,C,B,D,F,z,y){console.log("WARNING: Unimplemented function.");return[0,0]}});var c=10,h=3,k=5,w=10,f=1,s=9,e=3,a=9,j=2,g="#ccc";var r=function(A,D,y,z,C,E,B){o.call(this,A,D,y,z,C,E,B);this.draw_background_connector=true;this.draw_individual_connectors=false};u(r.prototype,o.prototype,{get_row_height:function(){var z=this.mode,y;if(z==="Dense"){y=c}else{if(z==="no_detail"){y=h}else{if(z==="Squish"){y=k}else{y=w}}}return y},draw_element:function(M,D,X,H,O,aj,an,ap,y){var T=X[0],al=X[1],ad=X[2]-1,Q=X[3],ae=Math.floor(Math.max(0,(al-O)*an)),N=Math.ceil(Math.min(y,Math.max(0,(ad-O)*an))),ac=ae,ao=N,aa=(D==="Dense"?0:(0+H))*ap+this.get_top_padding(y),L,ah,R=null,ar=null,B=this.prefs.block_color,ag=this.prefs.label_color;M.globalAlpha=this.alpha_scaler.gen_val(X);if(D==="Dense"){H=1}if(D==="no_detail"){M.fillStyle=B;M.fillRect(ae,aa+5,N-ae,f)}else{var K=X[4],Z=X[5],af=X[6],C=X[7],V=true;if(Z&&af){R=Math.floor(Math.max(0,(Z-O)*an));ar=Math.ceil(Math.min(y,Math.max(0,(af-O)*an)))}var am,U;if(D==="Squish"){am=1;U=e;V=false}else{if(D==="Dense"){am=5;U=s}else{am=5;U=a}}if(!C){M.fillStyle=B;M.fillRect(ae,aa+1,N-ae,U);if(K&&V){if(K==="+"){M.fillStyle=M.canvas.manager.get_pattern("right_strand_inv")}else{if(K==="-"){M.fillStyle=M.canvas.manager.get_pattern("left_strand_inv")}}M.fillRect(ae,aa+1,N-ae,U)}}else{var J,W;if(D==="Squish"||D==="Dense"){J=aa+Math.floor(e/2)+1;W=1}else{if(K){J=aa;W=U}else{J+=(e/2)+1;W=1}}if(this.draw_background_connector){if(D==="Squish"||D==="Dense"){M.fillStyle=g}else{if(K){if(K==="+"){M.fillStyle=M.canvas.manager.get_pattern("right_strand")}else{if(K==="-"){M.fillStyle=M.canvas.manager.get_pattern("left_strand")}}}else{M.fillStyle=g}}M.fillRect(ae,J,N-ae,W)}var E;for(var ak=0,A=C.length;ak<A;ak++){var F=C[ak],z=Math.floor(Math.max(0,(F[0]-O)*an)),Y=Math.ceil(Math.min(y,Math.max((F[1]-1-O)*an))),S,ab;if(z>Y){continue}M.fillStyle=B;M.fillRect(z,aa+(U-am)/2+1,Y-z,am);if(R!==undefined&&af>Z&&!(z>ar||Y<R)){var ai=Math.max(z,R),I=Math.min(Y,ar);M.fillRect(ai,aa+1,I-ai,U);if(C.length==1&&D=="Pack"){if(K==="+"){M.fillStyle=M.canvas.manager.get_pattern("right_strand_inv")}else{if(K==="-"){M.fillStyle=M.canvas.manager.get_pattern("left_strand_inv")}}if(ai+14<I){ai+=2;I-=2}M.fillRect(ai,aa+1,I-ai,U)}}if(this.draw_individual_connectors&&S){this.draw_connector(M,S,ab,z,Y,aa)}S=z;ab=Y}if(D==="Pack"){M.globalAlpha=1;M.fillStyle="white";var G=this.height_scaler.gen_val(X),P=Math.ceil(U*G),aq=Math.round((U-P)/2);if(G!==1){M.fillRect(ae,J+1,N-ae,aq);M.fillRect(ae,J+U-aq+1,N-ae,aq)}}}M.globalAlpha=1;if(D==="Pack"&&al>O){M.fillStyle=ag;if(O===0&&ae-M.measureText(Q).width<0){M.textAlign="left";M.fillText(Q,N+j,aa+8);ao+=M.measureText(Q).width+j}else{M.textAlign="right";M.fillText(Q,ae-j,aa+8);ac-=M.measureText(Q).width+j}}}M.globalAlpha=1;return[ac,ao]}});var t=function(B,E,y,A,D,F,C,z){o.call(this,B,E,y,A,D,F,C);this.ref_seq=(z?z.data:null)};u(t.prototype,o.prototype,{get_row_height:function(){var y,z=this.mode;if(z==="Dense"){y=c}else{if(z==="Squish"){y=k}else{y=w;if(this.prefs.show_insertions){y*=2}}}return y},draw_read:function(K,A,ag,V,L,aa,ad,C,B,M){K.textAlign="center";var J=this,R=[L,aa],Z=0,W=0,D=0,F=K.canvas.manager.char_width_px,y=(B==="+"?this.prefs.block_color:this.prefs.reverse_strand_color);var O=[];if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){D=Math.round(ag/2)}if(!C){C=[[0,M.length]]}for(var G=0,I=C.length;G<I;G++){var z=C[G],E="MIDNSHP=X"[z[0]],S=z[1];if(E==="H"||E==="S"){Z-=S}var U=ad+Z,Y=Math.floor(Math.max(0,(U-L)*ag)),ab=Math.floor(Math.max(0,(U+S-L)*ag));if(Y===ab){ab+=1}switch(E){case"H":break;case"S":case"M":case"=":if(is_overlap([U,U+S],R)){var N=M.slice(W,W+S);if(D>0){K.fillStyle=y;K.fillRect(Y-D,V+1,ab-Y,9);K.fillStyle=g;for(var af=0,H=N.length;af<H;af++){if(this.prefs.show_differences&&this.ref_seq){var P=this.ref_seq[U-L+af];if(!P||P.toLowerCase()===N[af].toLowerCase()){continue}}if(U+af>=L&&U+af<=aa){var X=Math.floor(Math.max(0,(U+af-L)*ag));K.fillText(N[af],X,V+9)}}}else{K.fillStyle=y;K.fillRect(Y,V+4,ab-Y,e)}}W+=S;Z+=S;break;case"N":K.fillStyle=g;K.fillRect(Y-D,V+5,ab-Y,1);Z+=S;break;case"D":K.fillStyle="red";K.fillRect(Y-D,V+4,ab-Y,3);Z+=S;break;case"P":break;case"I":var ah=Y-D;if(is_overlap([U,U+S],R)){var N=M.slice(W,W+S);if(this.prefs.show_insertions){var T=Y-(ab-Y)/2;if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){K.fillStyle="yellow";K.fillRect(T-D,V-9,ab-Y,9);O[O.length]={type:"triangle",data:[ah,V+4,5]};K.fillStyle=g;switch(compute_overlap([U,U+S],R)){case (OVERLAP_START):N=N.slice(L-U);break;case (OVERLAP_END):N=N.slice(0,U-aa);break;case (CONTAINED_BY):break;case (CONTAINS):N=N.slice(L-U,U-aa);break}for(var af=0,H=N.length;af<H;af++){var X=Math.floor(Math.max(0,(U+af-L)*ag));K.fillText(N[af],X-(ab-Y)/2,V)}}else{K.fillStyle="yellow";K.fillRect(T,V+(this.mode!=="Dense"?2:5),ab-Y,(A!=="Dense"?e:s))}}else{if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){O.push({type:"text",data:[N.length,ah,V+9]})}else{}}}W+=S;break;case"X":W+=S;break}}K.fillStyle="yellow";var Q,ai,ae;for(var ac=0;ac<O.length;ac++){Q=O[ac];ai=Q.type;ae=Q.data;if(ai==="text"){K.save();K.font="bold "+K.font;K.fillText(ae[0],ae[1],ae[2]);K.restore()}else{if(ai=="triangle"){q(K,ae[0],ae[1],ae[2])}}}},draw_element:function(R,M,E,B,U,z,I,S,P){var H=E[0],Q=E[1],A=E[2],J=E[3],D=Math.floor(Math.max(0,(Q-U)*I)),F=Math.ceil(Math.min(P,Math.max(0,(A-U)*I))),C=(M==="Dense"?0:(0+B))*S,G=this.prefs.label_color,O=0;if((M==="Pack"||this.mode==="Auto")&&I>R.canvas.manager.char_width_px){var O=Math.round(I/2)}if(E[5] instanceof Array){var N=Math.floor(Math.max(0,(E[4][0]-U)*I)),L=Math.ceil(Math.min(P,Math.max(0,(E[4][1]-U)*I))),K=Math.floor(Math.max(0,(E[5][0]-U)*I)),y=Math.ceil(Math.min(P,Math.max(0,(E[5][1]-U)*I)));if(E[4][1]>=U&&E[4][0]<=z&&E[4][2]){this.draw_read(R,M,I,C,U,z,E[4][0],E[4][2],E[4][3],E[4][4])}if(E[5][1]>=U&&E[5][0]<=z&&E[5][2]){this.draw_read(R,M,I,C,U,z,E[5][0],E[5][2],E[5][3],E[5][4])}if(K>L){R.fillStyle=g;p(R,L-O,C+5,K-O,C+5)}}else{this.draw_read(R,M,I,C,U,z,Q,E[4],E[5],E[6])}if(M==="Pack"&&Q>U&&J!=="."){R.fillStyle=this.prefs.label_color;var T=1;if(T===0&&D-R.measureText(J).width<0){R.textAlign="left";R.fillText(J,F+j-O,C+8)}else{R.textAlign="right";R.fillText(J,D-j-O,C+8)}}return[0,0]}});var n=function(A,D,y,z,C,E,B){r.call(this,A,D,y,z,C,E,B);this.longest_feature_length=this.calculate_longest_feature_length();this.draw_background_connector=false;this.draw_individual_connectors=true};u(n.prototype,o.prototype,r.prototype,{calculate_longest_feature_length:function(){var z=0;for(var C=0,y=this.data.length;C<y;C++){var B=this.data[C],A=B[1],D=B[2];z=Math.max(z,D-A)}return z},get_top_padding:function(z){var y=this.view_end-this.view_start,A=z/y;return Math.min(128,Math.ceil((this.longest_feature_length/2)*A))},draw_connector:function(G,B,F,H,E,D){var y=(F+H)/2,C=H-y;var A=Math.PI,z=0;if(C>0){G.beginPath();G.arc(y,D,H-y,Math.PI,0);G.stroke()}}});x.Scaler=d;x.SummaryTreePainter=v;x.LinePainter=b;x.LinkedFeaturePainter=r;x.ReadPainter=t;x.ArcLinkedFeaturePainter=n};(function(d){var c={};var b=function(e){return c[e]};var a=function(f,g){var e={};g(b,e);c[f]=e};a("class",class_module);a("slotting",slotting_module);a("painters",painters_module);a("trackster",trackster_module);for(key in c.trackster){d[key]=c.trackster[key]}})(window);
\ No newline at end of file
diff -r 1b3c54debe5813a6a6b91ab1cd3581f489d09abb -r e1da72b42db7abbb3a1e3a9d842341aef5c28d9e static/scripts/packed/trackster_ui.js
--- a/static/scripts/packed/trackster_ui.js
+++ b/static/scripts/packed/trackster_ui.js
@@ -1,1 +1,1 @@
-var add_bookmark=function(b,a){var g=$("#bookmarks-container"),d=$("<div/>").addClass("bookmark").appendTo(g),c=$("<div/>").addClass("delete-icon-container").appendTo(d).click(function(){d.slideUp("fast");d.remove();view.has_changes=true;return false}),e=$("<a href=''/>").addClass("icon-button delete").appendTo(c),f=$("<div/>").addClass("position").appendTo(d),h=$("<a href=''/>").text(b).appendTo(f).click(function(){view.go_to(b);return false});annotation_div=get_editable_text_elt(a,true).addClass("annotation").appendTo(d);view.has_changes=true;return d};var addable_objects={LineTrack:LineTrack,FeatureTrack:FeatureTrack,VcfTrack:VcfTrack,ReadTrack:ReadTrack,DrawableGroup:DrawableGroup};var track_from_dict=function(c,b){var a=new addable_objects[c.track_type](c.name,view,b,c.hda_ldda,c.dataset_id,c.prefs,c.filters,c.tool);if(c.mode){a.change_mode(c.mode)}return a};var drawable_collection_from_dict=function(f,a){var e=new addable_objects[f.obj_type](f.name,view,a,f.prefs,view.viewport_container,view);for(var d=0;d<f.drawables.length;d++){var b=f.drawables[d],c;if(b.track_type){c=track_from_dict(b,e)}else{c=drawable_collection_from_dict(b)}e.add_drawable(c);e.content_div.append(c.container_div)}return e};var drawable_from_dict=function(b,a){return(b.track_type?track_from_dict(b,a):drawable_collection_from_dict(b,a))};var create_visualization=function(b,e,g,c,a,d,f){view=new View(b,e,g,c);view.editor=true;$.when(view.load_chroms_deferred).then(function(){if(a){var k=a.chrom,p=a.start,h=a.end,m=a.overview;if(k&&(p!==undefined)&&h){view.change_chrom(k,p,h)}}if(d){var o;for(var j=0;j<d.length;j++){o=d[j];view.add_drawable(drawable_from_dict(o,view))}}view.update_intro_div();var n;for(var j=0;j<view.drawables.length;j++){if(view.drawables[j].name===m){view.set_overview(view.drawables[j]);break}}if(f){var l;for(var j=0;j<f.length;j++){l=f[j];add_bookmark(l.position,l.annotation)}}view.has_changes=false});return view};var init_keyboard_nav=function(a){$(document).keydown(function(b){if($(b.srcElement).is(":input")){return}switch(b.which){case 37:a.move_fraction(0.25);break;case 38:var c=Math.round(a.viewport_container.height()/15);a.viewport_container.scrollTop(a.viewport_container.scrollTop()-20);break;case 39:a.move_fraction(-0.25);break;case 40:var c=Math.round(a.viewport_container.height()/15);a.viewport_container.scrollTop(a.viewport_container.scrollTop()+20);break}})};
\ No newline at end of file
+var add_bookmark=function(b,a){var g=$("#bookmarks-container"),d=$("<div/>").addClass("bookmark").appendTo(g),c=$("<div/>").addClass("delete-icon-container").appendTo(d).click(function(){d.slideUp("fast");d.remove();view.has_changes=true;return false}),e=$("<a href=''/>").addClass("icon-button delete").appendTo(c),f=$("<div/>").addClass("position").appendTo(d),h=$("<a href=''/>").text(b).appendTo(f).click(function(){view.go_to(b);return false});annotation_div=get_editable_text_elt(a,true).addClass("annotation").appendTo(d);view.has_changes=true;return d};var addable_objects={LineTrack:LineTrack,FeatureTrack:FeatureTrack,VcfTrack:VcfTrack,ReadTrack:ReadTrack,CompositeTrack:CompositeTrack,DrawableGroup:DrawableGroup};var create_visualization=function(b,e,g,c,a,d,f){view=new View(b,e,g,c);view.editor=true;$.when(view.load_chroms_deferred).then(function(){if(a){var q=a.chrom,h=a.start,n=a.end,k=a.overview;if(q&&(h!==undefined)&&n){view.change_chrom(q,h,n)}}if(d){var l,j,m;for(var o=0;o<d.length;o++){l=d[o];j=l.obj_type;if(!j){j=l.track_type}m=addable_objects[j].prototype.from_dict(l,view);view.add_drawable(m)}}view.update_intro_div();var r;for(var o=0;o<view.drawables.length;o++){if(view.drawables[o].name===k){view.set_overview(view.drawables[o]);break}}if(f){var p;for(var o=0;o<f.length;o++){p=f[o];add_bookmark(p.position,p.annotation)}}view.has_changes=false});return view};var init_keyboard_nav=function(a){$(document).keydown(function(b){if($(b.srcElement).is(":input")){return}switch(b.which){case 37:a.move_fraction(0.25);break;case 38:var c=Math.round(a.viewport_container.height()/15);a.viewport_container.scrollTop(a.viewport_container.scrollTop()-20);break;case 39:a.move_fraction(-0.25);break;case 40:var c=Math.round(a.viewport_container.height()/15);a.viewport_container.scrollTop(a.viewport_container.scrollTop()+20);break}})};
\ No newline at end of file
diff -r 1b3c54debe5813a6a6b91ab1cd3581f489d09abb -r e1da72b42db7abbb3a1e3a9d842341aef5c28d9e static/scripts/trackster.js
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -777,7 +777,15 @@
},
request_draw: function() {},
_draw: function() {},
- to_json: function() {},
+ /**
+ * Returns representation of object in a dictionary for easy saving.
+ * Use from_dict to recreate object.
+ */
+ to_dict: function() {},
+ /**
+ * Restore object from a dictionary created by to_dict()
+ */
+ from_dict: function(object_dict) {},
update_icons: function() {},
/**
* Set drawable name.
@@ -878,22 +886,45 @@
this.drawables[i]._draw();
}
},
- /**
- * Returns jsonified representation of collection.
+ /**
+ * Returns representation of object in a dictionary for easy saving.
+ * Use from_dict to recreate object.
*/
- to_json: function() {
- var jsonified_drawables = [];
+ to_dict: function() {
+ var dictified_drawables = [];
for (var i = 0; i < this.drawables.length; i++) {
- jsonified_drawables.push(this.drawables[i].to_json());
+ dictified_drawables.push(this.drawables[i].to_dict());
}
return {
name: this.name,
prefs: this.prefs,
obj_type: this.obj_type,
- drawables: jsonified_drawables
+ drawables: dictified_drawables
};
},
/**
+ * Restore object from a dictionary created by to_dict()
+ */
+ from_dict: function(collection_dict, container) {
+ var collection = new this.constructor( collection_dict.name, view,
+ container, collection_dict.prefs,
+ view.viewport_container, view);
+ var drawable_dict,
+ drawable_type,
+ drawable;
+ for (var i = 0; i < collection_dict.drawables.length; i++) {
+ drawable_dict = collection_dict.drawables[i];
+ drawable_type = drawable_dict['obj_type'];
+ // For backward compatibility:
+ if (!drawable_type) {
+ drawable_type = drawable_dict['track_type'];
+ }
+ drawable = addable_objects[ drawable_type ].prototype.from_dict( drawable_dict, collection );
+ collection.add_drawable( drawable );
+ }
+ return collection;
+ },
+ /**
* Add a drawable to the end of the collection.
*/
add_drawable: function(drawable) {
@@ -956,11 +987,10 @@
};
extend(DrawableGroup.prototype, Drawable.prototype, DrawableCollection.prototype, {
- /*
action_icons_def: [
// Create composite track from group's tracks.
{
- name: "composite-icon",
+ name: "composite_icon",
title: "Show composite track",
css_class: "layers-stack",
on_click_fn: function(group) {
@@ -968,7 +998,6 @@
}
}
].concat(Drawable.prototype.action_icons_def),
- */
build_container_div: function() {
return $("<div/>").addClass("group").attr("id", "group_" + this.id).appendTo(this.container.content_div);
},
@@ -988,14 +1017,51 @@
this.request_draw();
},
update_icons: function() {
- // TODO: only show icon for compositing if all tracks are the same type.
+ // Only show composite icon if all tracks are the same type.
+ var can_composite = true,
+ a_type = this.drawables[0].get_type();
+ for (var i = 0; i < this.drawables.length; i++) {
+ if ( this.drawables[i].get_type() !== a_type ) {
+ can_composite = false;
+ break;
+ }
+ }
+
+ if (can_composite) {
+ this.action_icons.composite_icon.show();
+ }
+ else {
+ this.action_icons.composite_icon.hide();
+ $(".tipsy").remove();
+ }
},
/**
- * Show composite track using group's tracks.
+ * Add composite track to group that includes all of group's tracks.
*/
show_composite_track: function() {
- this.composite_track = new CompositeTiledTrack(this.drawables, this.view, this);
- this.composite_track.request_draw();
+ var composite_track = new CompositeTrack("Composite Track", this.view, this, this.drawables);
+ this.add_drawable(composite_track);
+ composite_track.request_draw();
+ },
+ add_drawable: function(drawable) {
+ DrawableCollection.prototype.add_drawable.call(this, drawable);
+ this.update_icons();
+ },
+ remove_drawable: function(drawable) {
+ DrawableCollection.prototype.remove_drawable.call(this, drawable);
+ this.update_icons();
+ },
+ /**
+ * Restore object from a dictionary created by to_dict()
+ */
+ from_dict: function(collection_dict, container) {
+ var group = DrawableCollection.prototype.from_dict.call(this, collection_dict, container);
+
+ // Add drawables to group's content div to make them visible.
+ for (var i = 0; i < group.drawables.length; i++) {
+ group.content_div.append(group.drawables[i].container_div);
+ }
+ return group;
}
});
@@ -2537,10 +2603,10 @@
this.data_manager = (data_manager ? data_manager : new DataManager(DATA_CACHE_SIZE, this));
//
- // Create content div, which is where track is displayed.
+ // Create content div, which is where track is displayed, and add to container if available.
//
this.content_div = $("<div class='track-content'>").appendTo(this.container_div);
- this.container.content_div.append(this.container_div);
+ if (this.container) { this.container.content_div.append(this.container_div); }
};
extend(Track.prototype, Drawable.prototype, {
@@ -2684,6 +2750,9 @@
else if (this instanceof VcfTrack) {
return "VcfTrack";
}
+ else if (this instanceof CompositeTrack) {
+ return "CompositeTrack";
+ }
else if (this instanceof FeatureTrack) {
return "FeatureTrack";
}
@@ -2810,10 +2879,11 @@
new_track.enabled = this.enabled;
return new_track;
},
- /**
- * Convert track to JSON object.
+ /**
+ * Returns representation of object in a dictionary for easy saving.
+ * Use from_dict to recreate object.
*/
- to_json: function() {
+ to_dict: function() {
return {
"track_type": this.get_type(),
"name": this.name,
@@ -2824,13 +2894,25 @@
};
},
/**
+ * Restore object from a dictionary created by to_dict()
+ */
+ from_dict: function(track_dict, container) {
+ var track = new this.constructor(
+ track_dict.name, view, container, track_dict.hda_ldda, track_dict.dataset_id,
+ track_dict.prefs, track_dict.filters, track_dict.tool);
+ if (track_dict.mode) {
+ track.change_mode(track_dict.mode);
+ }
+ return track;
+ },
+ /**
* Change track's mode.
*/
- change_mode: function(name) {
+ change_mode: function(new_mode) {
var track = this;
// TODO: is it necessary to store the mode in two places (.mode and track_config)?
- track.mode = name;
- track.config.values['mode'] = name;
+ track.mode = new_mode;
+ track.config.values['mode'] = new_mode;
track.tile_cache.clear();
track.request_draw();
this.action_icons.mode_icon.attr("title", "Set display mode (now: " + track.mode + ")");
@@ -2932,11 +3014,12 @@
var all_tiles_drawn = true;
var drawn_tiles = [];
var tile_count = 0;
+ var is_tile = function(o) { return ('track' in o) };
// Draw or fetch and show tiles.
while ( ( tile_index * DENSITY * resolution ) < high ) {
- tile = this.draw_helper( force, width, tile_index, resolution, parent_element, w_scale );
- if ( tile ) {
- drawn_tiles.push( tile );
+ var draw_result = this.draw_helper( force, width, tile_index, resolution, parent_element, w_scale );
+ if ( is_tile(draw_result) ) {
+ drawn_tiles.push( draw_result );
} else {
all_tiles_drawn = false;
}
@@ -2982,8 +3065,8 @@
}
},
/**
- * Handle a single tile, either from cache or by setting up a deferred
- * operation and then requesting another redraw
+ * Retrieves from cache, draws, or sets up drawing for a single tile. Returns either a Tile object or a
+ * jQuery.Deferred object that is fulfilled when tile can be drawn again.
*/
draw_helper: function(force, width, tile_index, resolution, parent_element, w_scale, drawn_tiles, more_tile_data) {
var track = this,
@@ -3001,7 +3084,7 @@
// Helper to determine if object is jQuery deferred
var is_deferred = function ( d ) {
return ( 'isResolved' in d );
- }
+ };
// Flag to track whether we can draw everything now
var can_draw_now = true
@@ -3034,13 +3117,27 @@
}
// Can't draw now, so trigger another redraw when the data is ready
+ var can_draw = $.Deferred();
$.when( tile_data, seq_data ).then( function() {
view.request_redraw(false, false, false, track);
+ can_draw.resolve();
});
- // Indicate to caller that this tile could not be drawn
- return null;
- },
+ // Returned Deferred is resolved when tile can be drawn.
+ return can_draw;
+ },
+ /**
+ * Draw a track tile.
+ * @param result result from server
+ * @param mode mode to draw in
+ * @param resolution view resolution
+ * @param tile_index index of tile to be drawn
+ * @param w_scale pixels per base
+ * @param ref_seq reference sequence data
+ */
+ draw_tile: function(result, mode, resolution, tile_index, w_scale, ref_seq) {
+ console.log("Warning: TiledTrack.draw_tile() not implemented.")
+ },
/**
* Show track tile and perform associated actions. Showing tile may actually move
* an existing tile rather than reshowing it.
@@ -3169,15 +3266,81 @@
/**
* A tiled track composed of multiple other tracks.
*/
-var CompositeTiledTrack = function(drawables, view, container) {
- TiledTrack.call(this, "Composite Track", view, container);
- this.drawables = drawables;
- this.enabled = true;
+var CompositeTrack = function(name, view, container, drawables) {
+ // HACK: modes should be static class vars for most tracks and should update as
+ // needed for CompositeTracks
+ this.display_modes = ["Histogram", "Line", "Filled", "Intensity"];
+ TiledTrack.call(this, name, view, container);
+
+ // Init drawables; each drawable is a copy so that config/preferences
+ // are independent of each other.
+ this.drawables = [];
+ if (drawables) {
+ var
+ ids = [],
+ drawable;
+ for (var i = 0; i < drawables.length; i++) {
+ drawable = drawables[i];
+ ids.push(drawable.dataset_id);
+ this.drawables[i] = drawable.copy();
+ }
+ this.enabled = true;
+ }
+
+ this.update_icons();
+
+ // HACK: needed for saving object for now. Need to generalize get_type() to all Drawables and use
+ // that for object type.
+ this.obj_type = "CompositeTrack";
};
-extend(CompositeTiledTrack.prototype, TiledTrack.prototype, {
+
+extend(CompositeTrack.prototype, TiledTrack.prototype, {
+ // HACK: CompositeTrack should inherit from DrawableCollection as well.
+ /**
+ * Returns representation of object in a dictionary for easy saving.
+ * Use from_dict to recreate object.
+ */
+ to_dict: DrawableCollection.prototype.to_dict,
+ add_drawable: DrawableCollection.prototype.add_drawable,
+ /**
+ * Restore object from a dictionary created by to_dict()
+ */
+ // TODO: unify with DrawableCollection.prototype.from_dict?
+ from_dict: function(collection_dict, container) {
+ var collection = new this.constructor( collection_dict.name, view,
+ container, collection_dict.prefs,
+ view.viewport_container, view);
+ var drawable_dict,
+ drawable_type,
+ drawable;
+ for (var i = 0; i < collection_dict.drawables.length; i++) {
+ drawable_dict = collection_dict.drawables[i];
+ drawable_type = drawable_dict['obj_type'];
+ // For backward compatibility:
+ if (!drawable_type) {
+ drawable_type = drawable_dict['track_type'];
+ }
+ // No container for tracks so that it is not made visible.
+ drawable = addable_objects[ drawable_type ].prototype.from_dict( drawable_dict );
+ collection.add_drawable( drawable );
+ }
+ return collection;
+ },
+ change_mode: function(new_mode) {
+ TiledTrack.prototype.change_mode.call(this, new_mode);
+ for (var i = 0; i < this.drawables.length; i++) {
+ this.drawables[i].change_mode(new_mode);
+ }
+ },
init: function() {
- console.log("initing CompositeTiledTrack");
- this.enabled = true;
+ // FIXME: probably should enable based on whether/how many drawaables are enabled.
+ this.enabled = true;
+ this.request_draw();
+ },
+ update_icons: function() {
+ // For now, hide filters and tool.
+ this.action_icons.filters_icon.hide();
+ this.action_icons.tools_icon.hide();
},
can_draw: Drawable.prototype.can_draw,
draw_helper: function(force, width, tile_index, resolution, parent_element, w_scale, drawn_tiles, more_tile_data) {
@@ -3190,7 +3353,62 @@
return tile;
}
- // TODO: Try to build tile.
+ // Get/build tiles.
+ // FIXME: using very naive and simple approach for building tiles.
+ // issues not addressed include:
+ // (a) choosing tile size;
+ // (b) any kind of normalization.
+ var
+ drawable,
+ dummy_parent = $("<div/>"),
+ draw_result,
+ tiles = [],
+ deferreds = [],
+ can_draw = true;
+ for (var i = 0; i < this.drawables.length; i++) {
+ // Build drawable tile and show it.
+ draw_result = this.drawables[i].draw_helper(force, width, tile_index, resolution, dummy_parent,
+ w_scale, drawn_tiles, more_tile_data);
+ if (draw_result instanceof Tile) {
+ tiles.push(draw_result);
+ }
+ else {
+ deferreds.push(draw_result);
+ can_draw = false;
+ }
+ }
+
+ if (can_draw) {
+ // Build composite tile by compositing canvases.
+
+ // Copy first canvas.
+ var src_canvas = tiles[0].html_elt.find("canvas"),
+ cmp_canvas = src_canvas.clone(),
+ src_ctx = src_canvas.get(0).getContext("2d"),
+ cmp_ctx = cmp_canvas.get(0).getContext("2d"),
+ imageData = src_ctx.getImageData(0, 0, src_ctx.canvas.width, src_ctx.canvas.height);
+
+ cmp_ctx.putImageData(imageData, 0, 0);
+
+ // Composite additional canvases.
+ cmp_ctx.globalCompositeOperation = "darker";
+ var another_canvas;
+ for (var i = 1; i < tiles.length; i++) {
+ another_canvas = tiles[i].html_elt.find("canvas").get(0);
+ cmp_ctx.drawImage(another_canvas, 0, 0);
+ }
+
+ tile = new Tile(this, tile_index, resolution, cmp_canvas);
+ this.tile_cache.set(key, tile);
+ this.show_tile(tile, parent_element, w_scale);
+ }
+ else {
+ // Wait until tiles can be drawn, then try again.
+ var track = this;
+ $.when.apply($, deferreds).then(function() {
+ track.request_draw();
+ });
+ }
return tile;
}
@@ -3865,13 +4083,13 @@
extend(ReadTrack.prototype, Drawable.prototype, TiledTrack.prototype, FeatureTrack.prototype);
// Exports
-
exports.View = View;
exports.DrawableGroup = DrawableGroup;
exports.LineTrack = LineTrack;
exports.FeatureTrack = FeatureTrack;
exports.ReadTrack = ReadTrack;
exports.VcfTrack = VcfTrack;
+exports.CompositeTrack = CompositeTrack;
// End trackster_module encapsulation
};
@@ -5008,9 +5226,6 @@
}
});
-
-
-
exports.Scaler = Scaler;
exports.SummaryTreePainter = SummaryTreePainter;
exports.LinePainter = LinePainter;
diff -r 1b3c54debe5813a6a6b91ab1cd3581f489d09abb -r e1da72b42db7abbb3a1e3a9d842341aef5c28d9e static/scripts/trackster_ui.js
--- a/static/scripts/trackster_ui.js
+++ b/static/scripts/trackster_ui.js
@@ -31,59 +31,18 @@
/**
* Objects that can be added to a view.
*/
-var addable_objects = { "LineTrack": LineTrack, "FeatureTrack": FeatureTrack, "VcfTrack": VcfTrack, "ReadTrack": ReadTrack, "DrawableGroup": DrawableGroup };
-
-/**
- * Decode a track from a dictionary.
- */
-var track_from_dict = function(track_dict, container) {
- var track = new addable_objects[track_dict.track_type](
- track_dict.name, view, container, track_dict.hda_ldda, track_dict.dataset_id,
- track_dict.prefs, track_dict.filters, track_dict.tool);
- if (track_dict.mode) {
- track.change_mode(track_dict.mode);
- }
- return track;
-};
-
-/**
- * Decode a drawable collection from a dictionary.
- */
-var drawable_collection_from_dict = function(collection_dict, container) {
- var collection = new addable_objects[collection_dict.obj_type](collection_dict.name, view, container, collection_dict.prefs, view.viewport_container, view);
- for (var i = 0; i < collection_dict.drawables.length; i++) {
- var
- drawable_dict = collection_dict.drawables[i],
- drawable;
- if (drawable_dict['track_type']) {
- drawable = track_from_dict(drawable_dict, collection);
- }
- else {
- drawable = drawable_collection_from_dict(drawable_dict);
- }
- collection.add_drawable(drawable);
- // HACK: move track from view to collection's content_div.
- // FIX: Tracks should be able to be be added to arbitrary containers;
- // every moveable should have a container_div, and every container should have
- // a content_div (though perhaps name changes are needed).
- collection.content_div.append(drawable.container_div);
- }
- return collection;
-};
-
-/**
- * Decode a drawable from a dict.
- */
-var drawable_from_dict = function(drawable_dict, container) {
- return (drawable_dict['track_type'] ?
- track_from_dict(drawable_dict, container) :
- drawable_collection_from_dict(drawable_dict, container));
-};
+var addable_objects = {
+ "LineTrack": LineTrack,
+ "FeatureTrack": FeatureTrack,
+ "VcfTrack": VcfTrack,
+ "ReadTrack": ReadTrack,
+ "CompositeTrack": CompositeTrack,
+ "DrawableGroup": DrawableGroup };
/**
* Create a complete Trackster visualization. Returns view.
*/
-var create_visualization = function(parent_elt, title, id, dbkey, viewport_config, tracks_config, bookmarks_config) {
+var create_visualization = function(parent_elt, title, id, dbkey, viewport_config, drawables_config, bookmarks_config) {
// Create view.
view = new View(parent_elt, title, id, dbkey);
@@ -95,7 +54,7 @@
chrom = viewport_config.chrom,
start = viewport_config.start,
end = viewport_config.end,
- overview_track_name = viewport_config.overview;
+ overview_drawable_name = viewport_config.overview;
if (chrom && (start !== undefined) && end) {
view.change_chrom(chrom, start, end);
@@ -103,11 +62,20 @@
}
// Add drawables to view.
- if (tracks_config) {
- var track_config;
- for (var i = 0; i < tracks_config.length; i++) {
- track_config = tracks_config[i];
- view.add_drawable( drawable_from_dict(track_config, view) );
+ if (drawables_config) {
+ // FIXME: can from_dict() be used to create view and add drawables?
+ var drawable_config,
+ drawable_type,
+ drawable;
+ for (var i = 0; i < drawables_config.length; i++) {
+ drawable_config = drawables_config[i];
+ drawable_type = drawable_config['obj_type'];
+ // For backward compatibility:
+ if (!drawable_type) {
+ drawable_type = drawable_config['track_type'];
+ }
+ drawable = addable_objects[ drawable_type ].prototype.from_dict( drawable_config, view );
+ view.add_drawable( drawable );
}
}
@@ -115,9 +83,9 @@
view.update_intro_div();
// Set overview.
- var overview_track;
+ var overview_drawable;
for (var i = 0; i < view.drawables.length; i++) {
- if (view.drawables[i].name === overview_track_name) {
+ if (view.drawables[i].name === overview_drawable_name) {
view.set_overview(view.drawables[i]);
break;
}
diff -r 1b3c54debe5813a6a6b91ab1cd3581f489d09abb -r e1da72b42db7abbb3a1e3a9d842341aef5c28d9e templates/tracks/browser.mako
--- a/templates/tracks/browser.mako
+++ b/templates/tracks/browser.mako
@@ -244,7 +244,7 @@
// FIXME: give unique IDs to Drawables and save overview as ID.
var overview_track_name = (view.overview_drawable ? view.overview_drawable.name : null);
var payload = {
- 'view': view.to_json(),
+ 'view': view.to_dict(),
'viewport': { 'chrom': view.chrom, 'start': view.low , 'end': view.high, 'overview': overview_track_name },
'bookmarks': bookmarks
};
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: natefoo: Object Store: Create datasets when accessed via file_name.
by Bitbucket 09 Dec '11
by Bitbucket 09 Dec '11
09 Dec '11
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/1b3c54debe58/
changeset: 1b3c54debe58
user: natefoo
date: 2011-12-09 20:56:58
summary: Object Store: Create datasets when accessed via file_name.
affected #: 3 files
diff -r 25f3c2c08e97dc2b8d5ea2885d5083b887c65fba -r 1b3c54debe5813a6a6b91ab1cd3581f489d09abb lib/galaxy/exceptions/__init__.py
--- a/lib/galaxy/exceptions/__init__.py
+++ b/lib/galaxy/exceptions/__init__.py
@@ -18,3 +18,7 @@
class ItemOwnershipException( MessageException ):
pass
+
+class ObjectNotFound( Exception ):
+ """ Accessed object was not found """
+ pass
diff -r 25f3c2c08e97dc2b8d5ea2885d5083b887c65fba -r 1b3c54debe5813a6a6b91ab1cd3581f489d09abb lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -16,6 +16,7 @@
from galaxy.util.hash_util import *
from galaxy.web.form_builder import *
from galaxy.model.item_attrs import UsesAnnotations, APIItem
+from galaxy.exceptions import ObjectNotFound
from sqlalchemy.orm import object_session
from sqlalchemy.sql.expression import func
import os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
@@ -649,10 +650,12 @@
if not self.external_filename:
assert self.id is not None, "ID must be set before filename used (commit the object)"
assert self.object_store is not None, "Object Store has not been initialized for dataset %s" % self.id
- filename = self.object_store.get_filename( self.id )
- if not self.object_store.exists( self.id ):
- # Create directory if it does not exist
- self.object_store.create( self.id, dir_only=True )
+ try:
+ filename = self.object_store.get_filename( self.id )
+ except ObjectNotFound, e:
+ # Create file if it does not exist
+ self.object_store.create( self.id )
+ filename = self.object_store.get_filename( self.id )
return filename
else:
filename = self.external_filename
diff -r 25f3c2c08e97dc2b8d5ea2885d5083b887c65fba -r 1b3c54debe5813a6a6b91ab1cd3581f489d09abb lib/galaxy/objectstore/__init__.py
--- a/lib/galaxy/objectstore/__init__.py
+++ b/lib/galaxy/objectstore/__init__.py
@@ -18,6 +18,7 @@
from galaxy.jobs import Sleeper
from galaxy.model import directory_hash_id
from galaxy.objectstore.s3_multipart_upload import multipart_upload
+from galaxy.exceptions import ObjectNotFound
from boto.s3.key import Key
from boto.s3.connection import S3Connection
@@ -27,11 +28,6 @@
logging.getLogger('boto').setLevel(logging.INFO) # Otherwise boto is quite noisy
-class ObjectNotFound(Exception):
- """ Accessed object was not found """
- pass
-
-
class ObjectStore(object):
"""
ObjectStore abstract interface
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