galaxy-dev
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
June 2009
- 6 participants
- 50 discussions
Hello,
What needs to be configured in order to limit users to their own datasets ?
What I mean is that on my local server (using external authentication,
but no security groups/roles defined), I can use the following URL to
view my datasets;
http://rave.cshl.edu/galaxy/datasets/35819/display/index
But I can also change the dataset ID and view any other file, regardless
of whether it is mine or not.
I'd like to block access to other users' datasets (but I prefer not to
create specialized roles/groups).
Thanks,
Gordon.
2
1
details: http://www.bx.psu.edu/hg/galaxy/rev/d9e58acd4dca
changeset: 2461:d9e58acd4dca
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Mon Jun 29 16:54:41 2009 -0400
description:
Bug fix for my last commit.
1 file(s) affected in this change:
lib/galaxy/model/migrate/versions/0007_sharing_histories.py
diffs (24 lines):
diff -r 533ae45c4440 -r d9e58acd4dca lib/galaxy/model/migrate/versions/0007_sharing_histories.py
--- a/lib/galaxy/model/migrate/versions/0007_sharing_histories.py Mon Jun 29 16:30:16 2009 -0400
+++ b/lib/galaxy/model/migrate/versions/0007_sharing_histories.py Mon Jun 29 16:54:41 2009 -0400
@@ -1,11 +1,15 @@
from sqlalchemy import *
+from sqlalchemy.orm import *
from migrate import *
+import sys, logging
-import datetime
-now = datetime.datetime.utcnow
-
-# Need our custom types, but don't import anything else from model
-from galaxy.model.custom_types import *
+log = logging.getLogger( __name__ )
+log.setLevel(logging.DEBUG)
+handler = logging.StreamHandler( sys.stdout )
+format = "%(name)s %(levelname)s %(asctime)s %(message)s"
+formatter = logging.Formatter( format )
+handler.setFormatter( formatter )
+log.addHandler( handler )
metadata = MetaData( migrate_engine )
1
0
29 Jun '09
details: http://www.bx.psu.edu/hg/galaxy/rev/8ad73e08978e
changeset: 2462:8ad73e08978e
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Mon Jun 29 17:04:18 2009 -0400
description:
Yet another bug fix - not sure why these were not caught in development.
1 file(s) affected in this change:
lib/galaxy/model/migrate/versions/0007_sharing_histories.py
diffs (11 lines):
diff -r d9e58acd4dca -r 8ad73e08978e lib/galaxy/model/migrate/versions/0007_sharing_histories.py
--- a/lib/galaxy/model/migrate/versions/0007_sharing_histories.py Mon Jun 29 16:54:41 2009 -0400
+++ b/lib/galaxy/model/migrate/versions/0007_sharing_histories.py Mon Jun 29 17:04:18 2009 -0400
@@ -1,6 +1,7 @@
from sqlalchemy import *
from sqlalchemy.orm import *
from migrate import *
+from migrate.changeset import *
import sys, logging
log = logging.getLogger( __name__ )
1
0
29 Jun '09
details: http://www.bx.psu.edu/hg/galaxy/rev/533ae45c4440
changeset: 2460:533ae45c4440
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Mon Jun 29 16:30:16 2009 -0400
description:
Many history-related feature enhancements, some fixes and enhanced functional tests:
- history sharing is now modeled after workflow sharing - fixes ticket # 63.
- all history features pass encoded history ids between requests
- new, empty histories are no longer created when a user logs in, instead their last accessed history is displayed
- some other things I'm forgetting, I'm sure
This commit also includes a fix for remote user authentication where private roles and default permissions were not created for users ( Ross's and Assaf's issues ).
Many functional test enhancements.
30 file(s) affected in this change:
lib/galaxy/model/__init__.py
lib/galaxy/model/mapping.py
lib/galaxy/model/migrate/versions/0007_sharing_histories.py
lib/galaxy/security/__init__.py
lib/galaxy/tools/parameters/basic.py
lib/galaxy/web/controllers/history.py
lib/galaxy/web/controllers/root.py
lib/galaxy/web/controllers/user.py
lib/galaxy/web/controllers/workflow.py
lib/galaxy/web/framework/__init__.py
lib/galaxy/web/framework/helpers/grids.py
lib/galaxy/web/security/__init__.py
templates/grid.mako
templates/history/grid.mako
templates/history/list_as_xml.mako
templates/history/list_shared.mako
templates/history/options.mako
templates/history/permissions.mako
templates/history/rename.mako
templates/history/share.mako
templates/history/sharing.mako
test/base/twilltestcase.py
test/functional/__init__.py
test/functional/test_DNAse_flanked_genes.py
test/functional/test_get_data.py
test/functional/test_history_functions.py
test/functional/test_metadata_editing.py
test/functional/test_security_and_libraries.py
test/functional/test_sniffing_and_metadata_settings.py
test/functional/test_toolbox.py
diffs (truncated from 3697 to 3000 lines):
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/model/__init__.py Mon Jun 29 16:30:16 2009 -0400
@@ -214,22 +214,29 @@
if genome_build not in [None, '?']:
self.genome_build = genome_build
self.datasets.append( dataset )
- def copy( self, target_user = None ):
+ def copy( self, name=None, target_user=None ):
+ if not name:
+ name = self.name
if not target_user:
target_user = self.user
- des = History( user = target_user )
- des.flush()
- des.name = self.name
+ new_history = History( name=name, user=target_user )
+ new_history.flush()
for data in self.datasets:
- new_data = data.copy( copy_children = True, target_history = des )
- des.add_dataset( new_data, set_hid = False )
+ new_data = data.copy( copy_children=True, target_history=new_history )
+ new_history.add_dataset( new_data, set_hid = False )
new_data.flush()
- des.hid_counter = self.hid_counter
- des.flush()
- return des
+ new_history.hid_counter = self.hid_counter
+ new_history.flush()
+ return new_history
@property
def activatable_datasets( self ):
- return [ hda for hda in self.datasets if not hda.dataset.deleted ] #this needs to be a list
+ # This needs to be a list
+ return [ hda for hda in self.datasets if not hda.dataset.deleted ]
+
+class HistoryUserShareAssociation( object ):
+ def __init__( self ):
+ self.history = None
+ self.user = None
class UserRoleAssociation( object ):
def __init__( self, user, role ):
@@ -988,7 +995,7 @@
remote_host=None,
remote_addr=None,
referer=None,
- current_history_id=None,
+ current_history=None,
session_key=None,
is_valid=False,
prev_session_id=None ):
@@ -997,12 +1004,11 @@
self.remote_host = remote_host
self.remote_addr = remote_addr
self.referer = referer
- self.current_history_id = current_history_id
+ self.current_history = current_history
self.session_key = session_key
self.is_valid = is_valid
self.prev_session_id = prev_session_id
self.histories = []
-
def add_history( self, history, association=None ):
if association is None:
self.histories.append( GalaxySessionToHistoryAssociation( self, history ) )
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/model/mapping.py Mon Jun 29 16:30:16 2009 -0400
@@ -56,7 +56,14 @@
Column( "hid_counter", Integer, default=1 ),
Column( "deleted", Boolean, index=True, default=False ),
Column( "purged", Boolean, index=True, default=False ),
- Column( "genome_build", TrimmedString( 40 ) ) )
+ Column( "genome_build", TrimmedString( 40 ) ),
+ Column( "importable", Boolean, default=False ) )
+
+HistoryUserShareAssociation.table = Table( "history_user_share_association", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
+ Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True )
+ )
HistoryDatasetAssociation.table = Table( "history_dataset_association", metadata,
Column( "id", Integer, primary_key=True ),
@@ -575,6 +582,11 @@
active_datasets=relation( HistoryDatasetAssociation, primaryjoin=( ( HistoryDatasetAssociation.table.c.history_id == History.table.c.id ) & ( not_( HistoryDatasetAssociation.table.c.deleted ) ) ), order_by=asc( HistoryDatasetAssociation.table.c.hid ), lazy=False, viewonly=True )
) )
+assign_mapper( context, HistoryUserShareAssociation, HistoryUserShareAssociation.table,
+ properties=dict( user=relation( User, backref='histories_shared_by_others' ),
+ history=relation( History, backref='users_shared_with' )
+ ) )
+
assign_mapper( context, User, User.table,
properties=dict( histories=relation( History, backref="user",
order_by=desc(History.table.c.update_time) ),
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/model/migrate/versions/0007_sharing_histories.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/model/migrate/versions/0007_sharing_histories.py Mon Jun 29 16:30:16 2009 -0400
@@ -0,0 +1,59 @@
+from sqlalchemy import *
+from migrate import *
+
+import datetime
+now = datetime.datetime.utcnow
+
+# Need our custom types, but don't import anything else from model
+from galaxy.model.custom_types import *
+
+metadata = MetaData( migrate_engine )
+
+HistoryUserShareAssociation_table = Table( "history_user_share_association", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
+ Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True )
+ )
+
+def upgrade():
+ # Load existing tables
+ metadata.reflect()
+ # Create the history_user_share_association table
+ try:
+ HistoryUserShareAssociation_table.create()
+ except Exception, e:
+ log.debug( "Creating history_user_share_association table failed: %s" % str( e ) )
+ # Add 1 column to the history table
+ try:
+ History_table = Table( "history", metadata, autoload=True )
+ except NoSuchTableError:
+ History_table = None
+ log.debug( "Failed loading table history" )
+ if History_table:
+ try:
+ col = Column( 'importable', Boolean, index=True, default=False )
+ col.create( History_table )
+ assert col is History_table.c.importable
+ except Exception, e:
+ log.debug( "Adding column 'importable' to history table failed: %s" % ( str( e ) ) )
+
+def downgrade():
+ # Load existing tables
+ metadata.reflect()
+ # Drop 1 column from the history table
+ try:
+ History_table = Table( "history", metadata, autoload=True )
+ except NoSuchTableError:
+ History_table = None
+ log.debug( "Failed loading table history" )
+ if History_table:
+ try:
+ col = History_table.c.importable
+ col.drop()
+ except Exception, e:
+ log.debug( "Dropping column 'importable' from history table failed: %s" % ( str( e ) ) )
+ # Drop the history_user_share_association table
+ try:
+ HistoryUserShareAssociation_table.drop()
+ except Exception, e:
+ log.debug( "Dropping history_user_share_association table failed: %s" % str( e ) )
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/security/__init__.py
--- a/lib/galaxy/security/__init__.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/security/__init__.py Mon Jun 29 16:30:16 2009 -0400
@@ -275,6 +275,8 @@
def set_all_dataset_permissions( self, dataset, permissions={} ):
# Set new permissions on a dataset, eliminating all current permissions
# Delete all of the current permissions on the dataset
+ # TODO: If setting ACCESS permission, at least 1 user must have every role associated with this dataset,
+ # or the dataset is inaccessible. See admin/library_dataset_dataset_association()
for dp in dataset.actions:
dp.delete()
dp.flush()
@@ -285,8 +287,9 @@
for dp in [ self.model.DatasetPermissions( action, dataset, role ) for role in roles ]:
dp.flush()
def set_dataset_permission( self, dataset, permission={} ):
- # TODO: is this method needed - see above method
# Set a specific permission on a dataset, leaving all other current permissions on the dataset alone
+ # TODO: If setting ACCESS permission, at least 1 user must have every role associated with this dataset,
+ # or the dataset is inaccessible. See admin/library_dataset_dataset_association()
for action, roles in permission.items():
if isinstance( action, Action ):
action = action.action
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/tools/parameters/basic.py Mon Jun 29 16:30:16 2009 -0400
@@ -1106,7 +1106,7 @@
except IndexError:
pass #no valid options
assert trans is not None, "DataToolParameter requires a trans"
- history = trans.history
+ history = trans.get_history()
assert history is not None, "DataToolParameter requires a history"
if value is not None:
if type( value ) != list:
@@ -1170,11 +1170,10 @@
if trans.workflow_building_mode:
return DummyDataset()
assert trans is not None, "DataToolParameter requires a trans"
- history = trans.history
+ history = trans.get_history()
assert history is not None, "DataToolParameter requires a history"
if self.optional:
return None
- history = trans.history
most_recent_dataset = [None]
filter_value = None
if self.options:
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/web/controllers/history.py
--- a/lib/galaxy/web/controllers/history.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/web/controllers/history.py Mon Jun 29 16:30:16 2009 -0400
@@ -1,6 +1,9 @@
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy import util
+from galaxy.model.mapping import desc
+from galaxy.model.orm import *
+from galaxy.util.json import *
import webhelpers, logging
from datetime import datetime
from cgi import escape
@@ -11,16 +14,19 @@
SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error"
class HistoryListGrid( grids.Grid ):
-
title = "Stored histories"
model_class = model.History
default_sort_key = "-create_time"
columns = [
grids.GridColumn( "Name", key="name",
- link=( lambda item: iff( item.deleted, None, dict( operation="switch", id=item.id ) ) ),
- attach_popup=True ),
+ link=( lambda item: iff( item.deleted, None, dict( operation="switch", id=item.id ) ) ),
+ attach_popup=True ),
grids.GridColumn( "Datasets (by state)", method='_build_datasets_by_state', ncells=4 ),
- grids.GridColumn( "Status", method='_build_status' ),
+ grids.GridColumn( "Status", method='_build_status',
+ link=( lambda item: iff( item.users_shared_with,
+ dict( operation="sharing", id=item.id ),
+ None ) ),
+ attach_popup=False ),
grids.GridColumn( "Age", key="create_time", format=time_ago ),
grids.GridColumn( "Last update", key="update_time", format=time_ago ),
# Valid for filtering but invisible
@@ -39,13 +45,10 @@
grids.GridColumnFilter( "All", args=dict( deleted='All' ) )
]
default_filter = dict( deleted=False )
-
def get_current_item( self, trans ):
- return trans.history
-
+ return trans.get_history()
def apply_default_filter( self, trans, query ):
return query.filter_by( user=trans.user, purged=False )
-
def _build_datasets_by_state( self, trans, history ):
rval = []
for state in ( 'ok', 'running', 'queued', 'error' ):
@@ -55,10 +58,11 @@
else:
rval.append( '' )
return rval
-
def _build_status( self, trans, history ):
if history.deleted:
return "deleted"
+ elif history.users_shared_with:
+ return "shared"
return ""
class HistoryController( BaseController ):
@@ -68,9 +72,7 @@
return ""
@web.expose
def list_as_xml( self, trans ):
- """
- XML history list for functional tests
- """
+ """XML history list for functional tests"""
return trans.fill_template( "/history/list_as_xml.mako" )
list_grid = HistoryListGrid()
@@ -79,29 +81,32 @@
@web.require_login( "work with multiple histories" )
def list( self, trans, **kwargs ):
"""List all available histories"""
- current_history = trans.history
+ current_history = trans.get_history()
status = message = None
if 'operation' in kwargs:
+ history_ids = util.listify( kwargs.get( 'id', [] ) )
+ histories = []
+ shared_by_others = []
operation = kwargs['operation'].lower()
if operation == "share":
return self.share( trans, **kwargs )
elif operation == "rename":
return self.rename( trans, **kwargs )
+ elif operation == 'sharing':
+ return self.sharing( trans, id=kwargs['id'] )
# Display no message by default
status, message = None, None
refresh_history = False
# Load the histories and ensure they all belong to the current user
- history_ids = util.listify( kwargs.get( 'id', [] ) )
- histories = []
- for hid in history_ids:
- history = model.History.get( hid )
+ for history_id in history_ids:
+ history = get_history( trans, history_id )
if history:
# Ensure history is owned by current user
if history.user_id != None and trans.user:
assert trans.user.id == history.user_id, "History does not belong to current user"
histories.append( history )
else:
- log.warn( "Invalid history id '%r' passed to list", hid )
+ log.warn( "Invalid history id '%r' passed to list", history_id )
if histories:
if operation == "switch":
status, message = self._list_switch( trans, histories )
@@ -110,33 +115,36 @@
elif operation == "delete":
status, message = self._list_delete( trans, histories )
if current_history in histories:
+ # Deleted the current history, so a new, empty history was
+ # created automatically, and we need to refresh the history frame
trans.template_context['refresh_frames'] = ['history']
elif operation == "undelete":
status, message = self._list_undelete( trans, histories )
trans.sa_session.flush()
# Render the list view
- return self.list_grid( trans, status=status, message=message, **kwargs )
+ return self.list_grid( trans, status=status, message=message, template='/history/grid.mako', **kwargs )
def _list_delete( self, trans, histories ):
"""Delete histories"""
n_deleted = 0
deleted_current = False
+ message_parts = []
for history in histories:
- if not history.deleted:
+ if history.users_shared_with:
+ message_parts.append( "History (%s) has been shared with others, unshare it before deleting it. " % history.name )
+ elif not history.deleted:
# We'll not eliminate any DefaultHistoryPermissions in case we undelete the history later
- # Mark history as deleted in db
history.deleted = True
# If deleting the current history, make a new current.
- if history == trans.history:
+ if history == trans.get_history():
deleted_current = True
trans.new_history()
- trans.log_event( "History id %d marked as deleted" % history.id )
+ trans.log_event( "History (%s) marked as deleted" % history.name )
n_deleted += 1
status = SUCCESS
- message_parts = []
if n_deleted:
- message_parts.append( "Deleted %d histories." % n_deleted )
+ message_parts.append( "Deleted %d histories. " % n_deleted )
if deleted_current:
- message_parts.append( "Your active history was deleted, a new empty history is now active.")
+ message_parts.append( "Your active history was deleted, a new empty history is now active. " )
status = INFO
return ( status, " ".join( message_parts ) )
def _list_undelete( self, trans, histories ):
@@ -151,20 +159,20 @@
if not history.default_permissions:
# For backward compatibility - for a while we were deleting all DefaultHistoryPermissions on
# the history when we deleted the history. We are no longer doing this.
- # Need to add default DefaultHistoryPermissions since they were deleted when the history was deleted
+ # Need to add default DefaultHistoryPermissions in case they were deleted when the history was deleted
default_action = trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS
private_user_role = trans.app.security_agent.get_private_user_role( history.user )
default_permissions = {}
default_permissions[ default_action ] = [ private_user_role ]
trans.app.security_agent.history_set_default_permissions( history, default_permissions )
n_undeleted += 1
- trans.log_event( "History id %d marked as undeleted" % history.id )
+ trans.log_event( "History (%s) %d marked as undeleted" % history.name )
status = SUCCESS
message_parts = []
if n_undeleted:
message_parts.append( "Undeleted %d histories." % n_undeleted )
if n_already_purged:
- message_parts.append( "%d have already been purged and cannot be undeleted." % n_already_purged )
+ message_parts.append( "%d histories have already been purged and cannot be undeleted." % n_already_purged )
status = WARNING
return status, "".join( message_parts )
def _list_switch( self, trans, histories ):
@@ -172,25 +180,39 @@
new_history = histories[0]
galaxy_session = trans.get_galaxy_session()
try:
- association = trans.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first()
+ association = trans.app.model.GalaxySessionToHistoryAssociation \
+ .filter_by( session_id=galaxy_session.id, history_id=trans.security.decode_id( new_history.id ) ).first()
except:
association = None
new_history.add_galaxy_session( galaxy_session, association=association )
new_history.flush()
trans.set_history( new_history )
- trans.log_event( "History switched to id: %s, name: '%s'" % (str(new_history.id), new_history.name ) )
# No message
return None, None
+ @web.expose
+ def list_shared( self, trans, **kwd ):
+ """List histories shared with current user by others"""
+ params = util.Params( kwd )
+ msg = util.restore_text( params.get( 'msg', '' ) )
+ shared_by_others = trans.sa_session \
+ .query( model.HistoryUserShareAssociation ) \
+ .filter_by( user=trans.user ) \
+ .join( 'history' ) \
+ .filter( model.History.deleted == False ) \
+ .order_by( desc( model.History.update_time ) ) \
+ .all()
+ return trans.fill_template( "/history/list_shared.mako", shared_by_others=shared_by_others, msg=msg, messagetype='done' )
@web.expose
def delete_current( self, trans ):
"""Delete just the active history -- this does not require a logged in user."""
history = trans.get_history()
+ if history.users_shared_with:
+ return trans.show_error_message( "History (%s) has been shared with others, unshare it before deleting it. " % history.name )
if not history.deleted:
history.deleted = True
history.flush()
trans.log_event( "History id %d marked as deleted" % history.id )
- # Regardless of whether it was previously deleted, we make a new
- # history active
+ # Regardless of whether it was previously deleted, we make a new history active
trans.new_history()
return trans.show_ok_message( "History deleted, a new history is active", refresh_frames=['history'] )
@web.expose
@@ -200,7 +222,7 @@
# user (if logged in) or the current history
assert history is not None
if history.user is None:
- assert history == trans.history
+ assert history == trans.get_history()
else:
assert history.user == trans.user
# Rename
@@ -208,48 +230,49 @@
trans.sa_session.flush()
@web.expose
def imp( self, trans, id=None, confirm=False, **kwd ):
- # TODO clean this up and make sure functionally correct
+ """Import another user's history via a shared URL"""
msg = ""
user = trans.get_user()
user_history = trans.get_history()
if not id:
- return trans.show_error_message( "You must specify a history you want to import.")
- id = trans.security.decode_id( id )
- import_history = trans.app.model.History.get( id )
+ return trans.show_error_message( "You must specify a history you want to import." )
+ import_history = get_history( trans, id )
if not import_history:
return trans.show_error_message( "The specified history does not exist.")
+ if not import_history.importable:
+ error( "The owner of this history has disabled imports via this link." )
if user:
if import_history.user_id == user.id:
- return trans.show_error_message( "You cannot import your own history.")
- new_history = import_history.copy( target_user=trans.user )
- new_history.name = "imported: "+new_history.name
+ return trans.show_error_message( "You cannot import your own history." )
+ new_history = import_history.copy( target_user=user )
+ new_history.name = "imported: " + new_history.name
new_history.user_id = user.id
galaxy_session = trans.get_galaxy_session()
try:
- association = trans.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first()
+ association = trans.app.model.GalaxySessionToHistoryAssociation \
+ .filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first()
except:
association = None
new_history.add_galaxy_session( galaxy_session, association=association )
new_history.flush()
if not user_history.datasets:
trans.set_history( new_history )
- trans.log_event( "History imported, id: %s, name: '%s': " % (str(new_history.id) , new_history.name ) )
return trans.show_ok_message( """
History "%s" has been imported. Click <a href="%s">here</a>
to begin.""" % ( new_history.name, web.url_for( '/' ) ) )
elif not user_history.datasets or confirm:
new_history = import_history.copy()
- new_history.name = "imported: "+new_history.name
+ new_history.name = "imported: " + new_history.name
new_history.user_id = None
galaxy_session = trans.get_galaxy_session()
try:
- association = trans.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first()
+ association = trans.app.model.GalaxySessionToHistoryAssociation \
+ .filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first()
except:
association = None
new_history.add_galaxy_session( galaxy_session, association=association )
new_history.flush()
trans.set_history( new_history )
- trans.log_event( "History imported, id: %s, name: '%s': " % (str(new_history.id) , new_history.name ) )
return trans.show_ok_message( """
History "%s" has been imported. Click <a href="%s">here</a>
to begin.""" % ( new_history.name, web.url_for( '/' ) ) )
@@ -262,106 +285,33 @@
def share( self, trans, id=None, email="", **kwd ):
# If a history contains both datasets that can be shared and others that cannot be shared with the desired user,
# then the entire history is shared, and the protected datasets will be visible, but inaccessible ( greyed out )
- # in the shared history
+ # in the cloned history
params = util.Params( kwd )
- action = params.get( 'action', None )
- if action == "no_share":
- trans.response.send_redirect( url_for( controller='root', action='history_options' ) )
- if not id:
- id = trans.get_history().id
- id = util.listify( id )
- send_to_err = ""
- histories = []
- for hid in id:
- histories.append( trans.app.model.History.get( hid ) )
+ user = trans.get_user()
if not email:
- return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
- user = trans.get_user()
- send_to_users = []
- for email_address in util.listify( email ):
- email_address = email_address.strip()
- if email_address:
- if email_address == user.email:
- send_to_err += "You can't send histories to yourself. "
- else:
- send_to_user = trans.app.model.User.filter( trans.app.model.User.table.c.email==email_address ).first()
- if send_to_user:
- send_to_users.append( send_to_user )
- else:
- send_to_err += "%s is not a valid Galaxy user. " % email_address
+ if not id:
+ # Default to the current history
+ id = trans.security.encode_id( trans.history.id )
+ id = util.listify( id )
+ send_to_err = ""
+ histories = []
+ for history_id in id:
+ histories.append( get_history( trans, history_id ) )
+ return trans.fill_template( "/history/share.mako",
+ histories=histories,
+ email=email,
+ send_to_err=send_to_err )
+ histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email )
if not send_to_users:
if not send_to_err:
send_to_err += "%s is not a valid Galaxy user. " % email
- return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
- if params.get( 'share_proceed_button', False ) and action == 'share':
- # We need to filter out all histories that cannot be shared
- filtered_histories = {}
- for history in histories:
- for send_to_user in send_to_users:
- # Only deal with datasets that have not been purged
- for hda in history.activatable_datasets:
- # The history can be shared if it contains at least 1 public dataset or 1 dataset that the
- # other user can access. Inaccessible datasets contained in the history will be displayed
- # in the shared history, but "greyed out", so they cannot be viewed or used.
- if trans.app.security_agent.dataset_is_public( hda.dataset ) or \
- trans.app.security_agent.allow_action( send_to_user,
- trans.app.security_agent.permitted_actions.DATASET_ACCESS,
- dataset=hda ):
- if send_to_user in filtered_histories:
- filtered_histories[ send_to_user ].append( history )
- else:
- filtered_histories[ send_to_user ] = [ history ]
- break
- return self._share_histories( trans, user, send_to_users, send_to_err, filtered_histories=filtered_histories )
- elif params.get( 'history_share_btn', False ) or action != 'share':
- # The user is attempting to share histories whose datasets cannot all be accessed by other users. In this case,
- # the user sharing the histories can:
- # 1) action=='public': chose to make the datasets public if he is permitted to do so
- # 2) action=='private': automatically create a new "sharing role" allowing protected
- # datasets to be accessed only by the desired users
- # 3) action=='share': share only what can be shared when no permissions are changed - this case is handled above
- # 4) action=='no_share': Do not share anything - this case is handled above.
- can_change = {}
- cannot_change = {}
- no_change_needed = {}
- for history in histories:
- # Only deal with datasets that have not been purged
- for hda in history.activatable_datasets:
- if trans.app.security_agent.dataset_is_public( hda.dataset ):
- if history not in no_change_needed:
- no_change_needed[ history ] = [ hda ]
- else:
- no_change_needed[ history ].append( hda )
- elif not trans.app.security_agent.allow_action( send_to_user,
- trans.app.security_agent.permitted_actions.DATASET_ACCESS,
- dataset=hda ):
- # The user with which we are sharing the history does not have access permission on the current dataset
- if trans.app.security_agent.allow_action( user,
- trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS,
- dataset=hda ) and not hda.dataset.library_associations:
- # The current user has authority to change permissions on the current dataset because
- # they have permission to manage permissions on the dataset and the dataset is not associated
- # with a library.
- if action == "private":
- trans.app.security_agent.privately_share_dataset( hda.dataset, users=[ user, send_to_user ] )
- elif action == "public":
- trans.app.security_agent.make_dataset_public( hda.dataset )
- elif history not in can_change:
- # Build the set of histories / datasets on which the current user has authority
- # to "manage permissions". This is used in /history/share.mako
- can_change[ history ] = [ hda ]
- else:
- can_change[ history ].append( hda )
- else:
- if action in [ "private", "public" ]:
- # Don't change stuff that the user doesn't have permission to change
- continue
- elif history not in cannot_change:
- # Build the set of histories / datasets on which the current user does
- # not have authority to "manage permissions". This is used in /history/share.mako
- cannot_change[ history ] = [ hda ]
- else:
- cannot_change[ history ].append( hda )
+ return trans.fill_template( "/history/share.mako",
+ histories=histories,
+ email=email,
+ send_to_err=send_to_err )
+ if params.get( 'share_button', False ):
+ can_change, cannot_change, no_change_needed = \
+ self._populate_restricted( trans, user, histories, send_to_users, None )
if can_change or cannot_change:
return trans.fill_template( "/history/share.mako",
histories=histories,
@@ -370,65 +320,253 @@
can_change=can_change,
cannot_change=cannot_change,
no_change_needed=no_change_needed )
- return self._share_histories( trans, user, send_to_users, send_to_err, histories=histories )
+ if no_change_needed:
+ return self._share_histories( trans, user, send_to_err, histories=no_change_needed )
+ elif not send_to_err:
+ # User seems to be sharing an empty history
+ send_to_err = "You cannot share an empty history. "
return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
- def _share_histories( self, trans, user, send_to_users, send_to_err, histories=[], filtered_histories={} ):
+ @web.expose
+ @web.require_login( "share restricted histories with other users" )
+ def share_restricted( self, trans, id=None, email="", **kwd ):
+ action = kwd[ 'action' ]
+ if action == "no_share":
+ trans.response.send_redirect( url_for( controller='root', action='history_options' ) )
+ user = trans.get_user()
+ histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email )
+ send_to_err = ''
+ can_change, cannot_change, no_change_needed = \
+ self._populate_restricted( trans, user, histories, send_to_users, action )
+ # Now that we've populated the can_change, cannot_change, and no_change_needed dictionaries,
+ # we'll populate the histories_for_sharing dictionary from each of them.
+ histories_for_sharing = {}
+ if no_change_needed:
+ # Don't need to change anything in cannot_change, so populate as is
+ histories_for_sharing, send_to_err = \
+ self._populate( trans, histories_for_sharing, no_change_needed, send_to_err )
+ if cannot_change:
+ # Can't change anything in cannot_change, so populate as is
+ histories_for_sharing, send_to_err = \
+ self._populate( trans, histories_for_sharing, cannot_change, send_to_err )
+ # The action here is either 'public' or 'private', so we'll continue to populate the
+ # histories_for_sharing dictionary from the can_change dictionary.
+ for send_to_user, history_dict in can_change.items():
+ for history in history_dict:
+ # Make sure the current history has not already been shared with the current send_to_user
+ if trans.app.model.HistoryUserShareAssociation \
+ .filter( and_( trans.app.model.HistoryUserShareAssociation.table.c.user_id == send_to_user.id,
+ trans.app.model.HistoryUserShareAssociation.table.c.history_id == history.id ) ) \
+ .count() > 0:
+ send_to_err += "History (%s) already shared with user (%s)" % ( history.name, send_to_user.email )
+ else:
+ # Only deal with datasets that have not been purged
+ for hda in history.activatable_datasets:
+ # If the current dataset is not public, we may need to perform an action on it to
+ # make it accessible by the other user.
+ if not trans.app.security_agent.allow_action( send_to_user,
+ trans.app.security_agent.permitted_actions.DATASET_ACCESS,
+ dataset=hda ):
+ # The user with which we are sharing the history does not have access permission on the current dataset
+ if trans.app.security_agent.allow_action( user,
+ trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS,
+ dataset=hda ) and not hda.dataset.library_associations:
+ # The current user has authority to change permissions on the current dataset because
+ # they have permission to manage permissions on the dataset and the dataset is not associated
+ # with a library.
+ if action == "private":
+ trans.app.security_agent.privately_share_dataset( hda.dataset, users=[ user, send_to_user ] )
+ elif action == "public":
+ trans.app.security_agent.make_dataset_public( hda.dataset )
+ # Populate histories_for_sharing with the history after performing any requested actions on
+ # it's datasets to make them accessible by the other user.
+ if send_to_user not in histories_for_sharing:
+ histories_for_sharing[ send_to_user ] = [ history ]
+ elif history not in histories_for_sharing[ send_to_user ]:
+ histories_for_sharing[ send_to_user ].append( history )
+ return self._share_histories( trans, user, send_to_err, histories=histories_for_sharing )
+ def _get_histories_and_users( self, trans, user, id, email ):
+ if not id:
+ # Default to the current history
+ id = trans.security.encode_id( trans.history.id )
+ id = util.listify( id )
+ send_to_err = ""
+ histories = []
+ for history_id in id:
+ histories.append( get_history( trans, history_id ) )
+ send_to_users = []
+ for email_address in util.listify( email ):
+ email_address = email_address.strip()
+ if email_address:
+ if email_address == user.email:
+ send_to_err += "You cannot send histories to yourself. "
+ else:
+ send_to_user = trans.app.model.User.filter( and_( trans.app.model.User.table.c.email==email_address,
+ trans.app.model.User.table.c.deleted==False ) ).first()
+ if send_to_user:
+ send_to_users.append( send_to_user )
+ else:
+ send_to_err += "%s is not a valid Galaxy user. " % email_address
+ return histories, send_to_users, send_to_err
+ def _populate( self, trans, histories_for_sharing, other, send_to_err ):
+ # this method will populate the histories_for_sharing dictionary with the users and
+ # histories in other, eliminating histories that have already been shared with the
+ # associated user. No security checking on datasets is performed.
+ # If not empty, the histories_for_sharing dictionary looks like:
+ # { userA: [ historyX, historyY ], userB: [ historyY ] }
+ # other looks like:
+ # ## { userA: {historyX : [hda, hda], historyY : [hda]}, userB: {historyY : [hda]} }
+ for send_to_user, history_dict in other.items():
+ for history in history_dict:
+ # Make sure the current history has not already been shared with the current send_to_user
+ if trans.app.model.HistoryUserShareAssociation \
+ .filter( and_( trans.app.model.HistoryUserShareAssociation.table.c.user_id == send_to_user.id,
+ trans.app.model.HistoryUserShareAssociation.table.c.history_id == history.id ) ) \
+ .count() > 0:
+ send_to_err += "History (%s) already shared with user (%s)" % ( history.name, send_to_user.email )
+ else:
+ # Build the dict that will be used for sharing
+ if send_to_user not in histories_for_sharing:
+ histories_for_sharing[ send_to_user ] = [ history ]
+ elif history not in histories_for_sharing[ send_to_user ]:
+ histories_for_sharing[ send_to_user ].append( history )
+ return histories_for_sharing, send_to_err
+ def _populate_restricted( self, trans, user, histories, send_to_users, action ):
+ # The user may be attempting to share histories whose datasets cannot all be accessed by other users.
+ # If this is the case, the user sharing the histories can:
+ # 1) action=='public': choose to make the datasets public if he is permitted to do so
+ # 2) action=='private': automatically create a new "sharing role" allowing protected
+ # datasets to be accessed only by the desired users
+ # 3) action=='share_anyway': share only what can be shared when no permissions are changed
+ # 4) action=='no_share': Do not share anything
+ # In addition, the user may be sharing a history with a user with which the history was already shared
+ # and it will not be shared twice.
+ # This method will populate the can_change, cannot_change and no_change_needed dictionaries.
+ can_change = {}
+ cannot_change = {}
+ no_change_needed = {}
+ for history in histories:
+ for send_to_user in send_to_users:
+ # Make sure the current history has not already been shared with the current send_to_user
+ if trans.app.model.HistoryUserShareAssociation \
+ .filter( and_( trans.app.model.HistoryUserShareAssociation.table.c.user_id == send_to_user.id,
+ trans.app.model.HistoryUserShareAssociation.table.c.history_id == history.id ) ) \
+ .count() > 0:
+ send_to_err += "History (%s) already shared with user (%s)" % ( history.name, send_to_user.email )
+ else:
+ # Only deal with datasets that have not been purged
+ for hda in history.activatable_datasets:
+ if trans.app.security_agent.dataset_is_public( hda.dataset ):
+ # Build the dict that will show the user what doesn't need to be changed
+ if send_to_user not in no_change_needed:
+ no_change_needed[ send_to_user ] = {}
+ if history not in no_change_needed[ send_to_user ]:
+ no_change_needed[ send_to_user ][ history ] = [ hda ]
+ else:
+ no_change_needed[ send_to_user ][ history ].append( hda )
+ elif not trans.app.security_agent.allow_action( send_to_user,
+ trans.app.security_agent.permitted_actions.DATASET_ACCESS,
+ dataset=hda ):
+ # The user with which we are sharing the history does not have access permission on the current dataset
+ if trans.app.security_agent.allow_action( user,
+ trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS,
+ dataset=hda ) and not hda.dataset.library_associations:
+ # The current user has authority to change permissions on the current dataset because
+ # they have permission to manage permissions on the dataset and the dataset is not associated
+ # with a library.
+ if send_to_user not in can_change:
+ # Build the set of histories / datasets on which the current user has authority
+ # to "manage permissions".
+ can_change[ send_to_user ] = {}
+ if history not in can_change[ send_to_user ]:
+ can_change[ send_to_user ][ history ] = [ hda ]
+ else:
+ can_change[ send_to_user ][ history ].append( hda )
+ else:
+ if action in [ "private", "public" ]:
+ # Don't change stuff that the user doesn't have permission to change
+ continue
+ #elif send_to_user not in cannot_change:
+ if send_to_user not in cannot_change:
+ # Build the set of histories / datasets on which the current user does
+ # not have authority to "manage permissions".
+ cannot_change[ send_to_user ] = {}
+ if history not in cannot_change[ send_to_user ]:
+ cannot_change[ send_to_user ][ history ] = [ hda ]
+ else:
+ cannot_change[ send_to_user ][ history ].append( hda )
+ return can_change, cannot_change, no_change_needed
+ def _share_histories( self, trans, user, send_to_err, histories={} ):
+ # histories looks like: { userA: [ historyX, historyY ], userB: [ historyY ] }
msg = ""
- if not send_to_users:
- msg = "No users have been specified with which to share histories"
sent_to_emails = []
- for sent_to_user in send_to_users:
- sent_to_emails.append( sent_to_user.email )
+ for send_to_user in histories.keys():
+ sent_to_emails.append( send_to_user.email )
emails = ",".join( e for e in sent_to_emails )
- if not histories and not filtered_histories:
- msg = "No histories can be sent to (%s) without changing dataset permissions associating a sharing role with them" % emails
- elif histories:
- history_names = []
- for history in histories:
- history_names.append( history.name )
- for send_to_user in send_to_users:
- new_history = history.copy( target_user=send_to_user )
- new_history.name = history.name + " from " + user.email
- new_history.user_id = send_to_user.id
- self.app.model.flush()
- msg = "Histories (%s) have been shared with: %s. " % ( ",".join( history_names ), emails )
- elif filtered_histories:
- # filtered_histories is a dictionary like: { user: [ history, history ], user: [ history ] }
- for send_to_user, histories in filtered_histories.items():
- history_names = []
- for history in histories:
- history_names.append( history.name )
- new_history = history.copy( target_user=send_to_user )
- new_history.name = history.name + " from " + user.email
- new_history.user_id = send_to_user.id
- self.app.model.flush()
- msg += "Histories (%s) have been shared with: %s. " % ( ",".join( history_names ), send_to_user.email )
+ if not histories:
+ send_to_err += "No users have been specified or no histories can be sent without changing permissions or associating a sharing role. "
+ else:
+ for send_to_user, send_to_user_histories in histories.items():
+ shared_histories = []
+ for history in send_to_user_histories:
+ share = trans.app.model.HistoryUserShareAssociation()
+ share.history = history
+ share.user = send_to_user
+ session = trans.sa_session
+ session.save_or_update( share )
+ session.flush()
+ if history not in shared_histories:
+ shared_histories.append( history )
if send_to_err:
msg += send_to_err
- return trans.show_message( msg )
+ return self.sharing( trans, histories=shared_histories, msg=msg )
+ @web.expose
+ @web.require_login( "share histories with other users" )
+ def sharing( self, trans, histories=[], id=None, **kwd ):
+ # histories looks like: [ historyX, historyY ]
+ params = util.Params( kwd )
+ msg = util.restore_text ( params.get( 'msg', '' ) )
+ if id:
+ histories = [ get_history( trans, id ) ]
+ for history in histories:
+ if params.get( 'enable_import_via_link', False ):
+ history.importable = True
+ history.flush()
+ elif params.get( 'disable_import_via_link', False ):
+ history.importable = False
+ history.flush()
+ elif params.get( 'unshare_user', False ):
+ user = trans.app.model.User.get( trans.security.decode_id( kwd[ 'unshare_user' ] ) )
+ if not user:
+ msg = 'History (%s) does not seem to be shared with user (%s)' % ( history.name, user.email )
+ return trans.fill_template( 'history/sharing.mako', histories=histories, msg=msg, messagetype='error' )
+ association = trans.app.model.HistoryUserShareAssociation.filter_by( user=user, history=history ).one()
+ association.delete()
+ association.flush()
+ if not id:
+ shared_msg = "History (%s) now shared with: %d users. " % ( history.name, len( history.users_shared_with ) )
+ msg = '%s%s' % ( shared_msg, msg )
+ return trans.fill_template( 'history/sharing.mako', histories=histories, msg=msg, messagetype='done' )
@web.expose
@web.require_login( "rename histories" )
def rename( self, trans, id=None, name=None, **kwd ):
user = trans.get_user()
- if not isinstance( id, list ):
- if id != None:
- id = [ id ]
- if not isinstance( name, list ):
- if name != None:
- name = [ name ]
+ if not id:
+ # Default to the current history
+ history = trans.get_history()
+ if not history.user:
+ return trans.show_error_message( "You must save your history before renaming it." )
+ id = trans.security.encode_id( history.id )
+ id = util.listify( id )
+ name = util.listify( name )
histories = []
cur_names = []
- if not id:
- if not trans.get_history().user:
- return trans.show_error_message( "You must save your history before renaming it." )
- id = [trans.get_history().id]
for history_id in id:
- history = trans.app.model.History.get( history_id )
+ history = get_history( trans, history_id )
if history and history.user_id == user.id:
- histories.append(history)
- cur_names.append(history.name)
- if not name or len(histories)!=len(name):
- return trans.fill_template( "/history/rename.mako",histories=histories )
+ histories.append( history )
+ cur_names.append( history.name )
+ if not name or len( histories ) != len( name ):
+ return trans.fill_template( "/history/rename.mako", histories=histories )
change_msg = ""
for i in range(len(histories)):
if histories[i].user_id == user.id:
@@ -445,3 +583,38 @@
else:
change_msg = change_msg + "<p>History: "+cur_names[i]+" does not appear to belong to you.</p>"
return trans.show_message( "<p>%s" % change_msg, refresh_frames=['history'] )
+ @web.expose
+ @web.require_login( "clone shared Galaxy history" )
+ def clone( self, trans, id ):
+ history = get_history( trans, id, check_ownership=False )
+ user = trans.get_user()
+ if history.user == user:
+ owner = True
+ else:
+ if trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ) \
+ .filter_by( user=user, history=history ).count() == 0:
+ return trans.show_error_message( "The history you are attempting to clone is not owned by you or shared with you. " )
+ owner = False
+ name = "Clone of '%s'" % history.name
+ if not owner:
+ name += " shared by '%s'" % history.user.email
+ new_history = history.copy( name=name, target_user=user )
+ # Render the list view
+ return trans.show_ok_message( 'Clone with name "%s" is now included in your list of stored histories.' % new_history.name )
+
+## ---- Utility methods -------------------------------------------------------
+
+def get_history( trans, id, check_ownership=True ):
+ """Get a History from the database by id, verifying ownership."""
+ # Load history from database
+ id = trans.security.decode_id( id )
+ history = trans.sa_session.query( model.History ).get( id )
+ if not history:
+ err+msg( "History not found" )
+ # Verify ownership
+ user = trans.get_user()
+ if not user:
+ error( "Must be logged in to manage histories" )
+ if check_ownership and not( history.user == user ):
+ error( "History is not owned by current user" )
+ return history
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/web/controllers/root.py
--- a/lib/galaxy/web/controllers/root.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/web/controllers/root.py Mon Jun 29 16:30:16 2009 -0400
@@ -50,19 +50,18 @@
@web.expose
def history( self, trans, as_xml=False, show_deleted=False ):
"""
- Display the current history, creating a new history if neccesary.
-
+ Display the current history, creating a new history if necessary.
NOTE: No longer accepts "id" or "template" options for security reasons.
"""
- history = trans.get_history()
if trans.app.config.require_login and not trans.user:
return trans.fill_template( '/no_access.mako', message = 'Please log in to access Galaxy histories.' )
+ history = trans.get_history( create=True )
if as_xml:
trans.response.set_content_type('text/xml')
return trans.fill_template_mako( "root/history_as_xml.mako", history=history, show_deleted=util.string_as_bool( show_deleted ) )
else:
template = "root/history.mako"
- return trans.fill_template( "root/history.mako", history = history, show_deleted = util.string_as_bool( show_deleted ) )
+ return trans.fill_template( "root/history.mako", history=history, show_deleted=util.string_as_bool( show_deleted ) )
@web.expose
def dataset_state ( self, trans, id=None, stamp=None ):
@@ -350,9 +349,10 @@
@web.expose
def history_options( self, trans ):
- """Displays a list of history related actions"""
+ """Displays a list of history related actions"""
return trans.fill_template( "/history/options.mako",
- user = trans.get_user(), history = trans.get_history() )
+ user=trans.get_user(),
+ history=trans.get_history( create=True ) )
@web.expose
def history_delete( self, trans, id ):
"""
@@ -421,7 +421,7 @@
@web.expose
def history_new( self, trans, name=None ):
trans.new_history( name=name )
- trans.log_event( "Created new History, id: %s." % str(trans.get_history().id) )
+ trans.log_event( "Created new History, id: %s." % str(trans.history.id) )
return trans.show_message( "New history created", refresh_frames = ['history'] )
@web.expose
def history_add_to( self, trans, history_id=None, file_data=None, name="Data Added to History",info=None,ext="txt",dbkey="?",copy_access_from=None,**kwd ):
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/web/controllers/user.py
--- a/lib/galaxy/web/controllers/user.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/web/controllers/user.py Mon Jun 29 16:30:16 2009 -0400
@@ -81,6 +81,7 @@
@web.expose
def login( self, trans, email='', password='' ):
+ log.debug( "###IN login, email:%s, password: %s" % ( email, password ))
email_error = password_error = None
# Attempt login
if trans.app.config.require_login:
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/web/controllers/workflow.py
--- a/lib/galaxy/web/controllers/workflow.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/web/controllers/workflow.py Mon Jun 29 16:30:16 2009 -0400
@@ -129,7 +129,7 @@
if stored.importable == False:
error( "The owner of this workflow has disabled imports via this link" )
elif stored.user == trans.user:
- error( "You are already the ownder of this workflow, can't import" )
+ error( "You are already the owner of this workflow, can't import" )
elif stored.deleted:
error( "This workflow has been deleted, can't import" )
elif session.query( model.StoredWorkflowUserShareAssociation ) \
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/web/framework/__init__.py Mon Jun 29 16:30:16 2009 -0400
@@ -180,7 +180,7 @@
except:
event.message = message
try:
- event.history = self.history
+ event.history = self.get_history()
except:
event.history = None
try:
@@ -337,11 +337,11 @@
remote_host = self.request.remote_host,
remote_addr = self.request.remote_addr,
referer = self.request.headers.get( 'Referer', None ) )
- # Invalidated an existing sesssion for some reason, keep track
if prev_galaxy_session:
+ # Invalidated an existing session for some reason, keep track
galaxy_session.prev_session_id = prev_galaxy_session.id
- # The new session should be immediately associated with a user
if user_for_new_session:
+ # The new session should be associated with the user
galaxy_session.user = user_for_new_session
return galaxy_session
def __get_or_create_remote_user( self, remote_user_email ):
@@ -350,12 +350,23 @@
"""
# remote_user middleware ensures HTTP_REMOTE_USER exists
user = self.app.model.User.filter( self.app.model.User.table.c.email==remote_user_email ).first()
- if user is None:
+ if user:
+ # GVK: June 29, 2009 - This is to correct the behavior of a previous bug where a private
+ # role and default user / history permissions were not set for remote users. When a
+ # remote user authenticates, we'll look for this information, and if missing, create it.
+ if not self.app.security_agent.get_private_user_role( user ):
+ self.app.security_agent.create_private_user_role( user )
+ if not user.default_permissions:
+ self.app.security_agent.user_set_default_permissions( user, history=True, dataset=True )
+ elif user is None:
random.seed()
user = self.app.model.User( email=remote_user_email )
user.set_password_cleartext( ''.join( random.sample( string.letters + string.digits, 12 ) ) )
user.external = True
user.flush()
+ self.app.security_agent.create_private_user_role( user )
+ # We set default user permissions, before we log in and set the default history permissions
+ self.app.security_agent.user_set_default_permissions( user )
#self.log_event( "Automatically created account '%s'", user.email )
elif user.deleted:
return self.show_error_message( "Your account is no longer valid, contact your Galaxy administrator to activate your account." )
@@ -365,7 +376,6 @@
Update the session cookie to match the current session.
"""
self.set_cookie( self.security.encode_session_key( self.galaxy_session.session_key ), name=name )
-
def handle_user_login( self, user ):
"""
Login a new user (possibly newly created)
@@ -374,24 +384,39 @@
- if old session had a history and it was not associated with a user, associate it with the new session,
otherwise associate the current session's history with the user
"""
+ # Set the previous session
prev_galaxy_session = self.galaxy_session
prev_galaxy_session.is_valid = False
+ # Define a new current_session
self.galaxy_session = self.__create_new_session( prev_galaxy_session, user )
- # If the session already had a history, we associate it with the new
- # session, but only if it does not belong to a different user.
- if prev_galaxy_session.current_history and \
- ( prev_galaxy_session.current_history.user == user or prev_galaxy_session.user is None ):
- history = prev_galaxy_session.current_history
- elif self.galaxy_session.current_history:
- history = self.galaxy_session.current_history
- else:
- history = self.history
+ # Associated the current user's last accessed history (if exists) with their new session
+ history = None
+ try:
+ users_last_session = user.galaxy_sessions[0]
+ last_accessed = True
+ except:
+ users_last_session = None
+ last_accessed = False
+ if users_last_session and users_last_session.current_history:
+ history = users_last_session.current_history
+ if not history:
+ if prev_galaxy_session.current_history:
+ if prev_galaxy_session.current_history.user is None or prev_galaxy_session.current_history.user == user:
+ # If the previous galaxy session had a history, associate it with the new
+ # session, but only if it didn't belong to a different user.
+ history = prev_galaxy_session.current_history
+ elif self.galaxy_session.current_history:
+ history = self.galaxy_session.current_history
+ else:
+ history = self.get_history( create=True )
if history not in self.galaxy_session.histories:
self.galaxy_session.add_history( history )
if history.user is None:
history.user = user
self.galaxy_session.current_history = history
- self.app.security_agent.history_set_default_permissions( history, dataset=True, bypass_manage_permission=True )
+ if not last_accessed:
+ # Only set default history permissions if current history is not from a previous session
+ self.app.security_agent.history_set_default_permissions( history, dataset=True, bypass_manage_permission=True )
self.sa_session.flush( [ prev_galaxy_session, self.galaxy_session, history ] )
# This method is not called from the Galaxy reports, so the cookie will always be galaxysession
self.__update_session_cookie( name='galaxysession' )
@@ -403,7 +428,7 @@
"""
prev_galaxy_session = self.galaxy_session
prev_galaxy_session.is_valid = False
- self.galaxy_session = self.__create_new_session( prev_galaxy_session, None )
+ self.galaxy_session = self.__create_new_session( prev_galaxy_session )
self.sa_session.flush( [ prev_galaxy_session, self.galaxy_session ] )
# This method is not called from the Galaxy reports, so the cookie will always be galaxysession
self.__update_session_cookie( name='galaxysession' )
@@ -416,16 +441,15 @@
def get_history( self, create=False ):
"""
- Load the current history.
-
- NOTE: It looks like create was being ignored for a long time, so this
- will currently *always* create a new history. This is wasteful
- though, and we should verify that callers are using the create
- flag correctly and fix.
+ Load the current history, creating a new one only if there is not
+ current history and we're told to create"
"""
history = self.galaxy_session.current_history
- if history is None:
- history = self.new_history()
+ if not history:
+ if util.string_as_bool( create ):
+ history = self.new_history()
+ else:
+ raise "get_history() returning None"
return history
def set_history( self, history ):
if history and not history.deleted:
@@ -557,7 +581,8 @@
the user (chromInfo in history).
"""
dbnames = list()
- datasets = self.app.model.HistoryDatasetAssociation.filter_by(deleted=False, history_id=self.history.id, extension="len").all()
+ datasets = self.app.model.HistoryDatasetAssociation \
+ .filter_by(deleted=False, history_id=self.history.id, extension="len").all()
if len(datasets) > 0:
dbnames.append( (util.dbnames.default_value, '--------- User Defined Builds ----------') )
for dataset in datasets:
@@ -569,7 +594,8 @@
"""
Returns the db_file dataset associated/needed by `dataset`, or `None`.
"""
- datasets = self.app.model.HistoryDatasetAssociation.filter_by(deleted=False, history_id=self.history.id, extension="len").all()
+ datasets = self.app.model.HistoryDatasetAssociation \
+ .filter_by(deleted=False, history_id=self.history.id, extension="len").all()
for ds in datasets:
if dbkey == ds.dbkey:
return ds
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/web/framework/helpers/grids.py Mon Jun 29 16:30:16 2009 -0400
@@ -3,7 +3,9 @@
from galaxy.web import url_for
-import sys
+import sys, logging
+
+log = logging.getLogger( __name__ )
class Grid( object ):
"""
@@ -12,6 +14,7 @@
title = ""
exposed = True
model_class = None
+ template = None
columns = []
standard_filters = []
default_filter = None
@@ -22,6 +25,7 @@
def __call__( self, trans, **kwargs ):
status = kwargs.get( 'status', None )
message = kwargs.get( 'message', None )
+ template = kwargs.get( 'template', None )
session = trans.sa_session
# Build initial query
query = self.build_initial_query( session )
@@ -70,8 +74,15 @@
if len(args) > 0:
new_kwargs.update( args[0] )
new_kwargs.update( kwargs )
+ # We need to encode item ids
+ if 'id' in new_kwargs:
+ id = new_kwargs[ 'id' ]
+ if isinstance( id, list ):
+ new_args[ 'id' ] = [ trans.security.encode_id( i ) for i in id ]
+ else:
+ new_kwargs[ 'id' ] = trans.security.encode_id( id )
return url_for( **new_kwargs )
- return trans.fill_template( "grid.mako",
+ return trans.fill_template( template,
grid=self,
query=query,
sort_key=sort_key,
diff -r b26e2ef8726c -r 533ae45c4440 lib/galaxy/web/security/__init__.py
--- a/lib/galaxy/web/security/__init__.py Fri Jun 26 09:22:31 2009 -0400
+++ b/lib/galaxy/web/security/__init__.py Mon Jun 29 16:30:16 2009 -0400
@@ -30,9 +30,7 @@
random_pool.stir()
return str( number.getRandomNumber( nbits, random_pool.get_bytes ) )
-
class SecurityHelper( object ):
- # TODO: checking if histories/datasets are owned by the current user) will be moved here.
def __init__( self, **config ):
self.id_secret = config['id_secret']
self.id_cipher = Blowfish.new( self.id_secret )
diff -r b26e2ef8726c -r 533ae45c4440 templates/grid.mako
--- a/templates/grid.mako Fri Jun 26 09:22:31 2009 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,216 +0,0 @@
-<%inherit file="/base.mako"/>
-<%def name="title()">${grid.title}</%def>
-
-%if message:
- <p>
- <div class="${message_type}message transient-message">${message}</div>
- <div style="clear: both"></div>
- </p>
-%endif
-
-<%def name="javascripts()">
- ${parent.javascripts()}
- <script type="text/javascript">
- ## TODO: generalize and move into galaxy.base.js
- $(document).ready(function() {
- $(".grid").each( function() {
- var grid = this;
- var checkboxes = $(this).find("input.grid-row-select-checkbox");
- var update = $(this).find( "span.grid-selected-count" );
- $(checkboxes).each( function() {
- $(this).change( function() {
- var n = $(checkboxes).filter("[checked]").size();
- update.text( n );
- });
- })
- });
- });
-
- ## Can this be moved into base.mako?
- %if refresh_frames:
- %if 'masthead' in refresh_frames:
- ## Refresh masthead == user changes (backward compatibility)
- if ( parent.user_changed ) {
- %if trans.user:
- parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
- %else:
- parent.user_changed( null, false );
- %endif
- }
- %endif
- %if 'history' in refresh_frames:
- if ( parent.frames && parent.frames.galaxy_history ) {
- parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}";
- if ( parent.force_right_panel ) {
- parent.force_right_panel( 'show' );
- }
- }
- %endif
- %if 'tools' in refresh_frames:
- if ( parent.frames && parent.frames.galaxy_tools ) {
- parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}";
- if ( parent.force_left_panel ) {
- parent.force_left_panel( 'show' );
- }
- }
- %endif
- %endif
- </script>
-</%def>
-
-<%def name="stylesheets()">
- <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
- <style>
- ## Not generic to all grids -- move to base?
- .count-box {
- min-width: 1.1em;
- padding: 5px;
- border-width: 1px;
- border-style: solid;
- text-align: center;
- display: inline-block;
- }
- </style>
-</%def>
-
-
-<div class="grid-header">
- <h2>${grid.title}</h2>
- <span class="title">Filter:</span>
- %for i, filter in enumerate( grid.standard_filters ):
- %if i > 0:
- <span>|</span>
- %endif
- <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span>
- %endfor
-</div>
-
-<form name="history_actions" action="${url()}" method="post" >
-
- <table class="grid">
- <thead>
- <tr>
- <th></th>
- %for column in grid.columns:
- %if column.visible:
- <%
- href = ""
- extra = ""
- if column.sortable:
- if sort_key == column.key:
- if sort_order == "asc":
- href = url( sort=( "-" + column.key ) )
- extra = "↓"
- else:
- href = url( sort=( column.key ) )
- extra = "↑"
- else:
- href = url( sort=column.key )
- %>
- <th \
- %if column.ncells > 1:
- colspan="${column.ncells}"
- %endif
- >
- %if href:
- <a href="${href}">${column.label}</a>
- %else:
- ${column.label}
- %endif
- <span>${extra}</span>
- </th>
- %endif
- %endfor
- <th></th>
-
- </tr>
- </thead>
-
- <tbody>
-
- %for i, item in enumerate( query ):
-
-
- <tr \
- %if current_item == item:
- class="current" \
- %endif
- >
-
- ## Item selection column
- <td style="width: 1.5em;"><input type="checkbox" name="id" value=${item.id} class="grid-row-select-checkbox"></input></td>
-
- ## Data columns
- %for column in grid.columns:
- %if column.visible:
- <%
- # Link
- if column.link and column.link( item ):
- href = url( **column.link( item ) )
- else:
- href = None
- # Value (coerced to list so we can loop)
- value = column.get_value( trans, grid, item )
- if column.ncells == 1:
- value = [ value ]
- %>
-
- %for cellnum, v in enumerate( value ):
- <%
- # Attach popup menu?
- if column.attach_popup and cellnum == 0:
- extra = '<a id="grid-%d-popup" class="popup-arrow" style="display: none;">▼</a>' % i
- else:
- extra = ""
- %>
-
- %if href:
- <td><a href="${href}">${v}</a> ${extra}</td>
- %else:
- <td >${v}${extra}</td>
- %endif
- </td>
- %endfor
- %endif
- %endfor
-
- ## Actions column
- <td>
- <div popupmenu="grid-${i}-popup">
-
- %for operation in grid.operations:
- %if operation.allowed( item ):
- <a class="action-button" href="${url( operation=operation.label, id=item.id )}">${operation.label}</a>
- %endif
- %endfor
-
- </div>
- </td>
-
- </tr>
-
- %endfor
-
- </tbody>
-
- <tfoot>
- <tr>
- <td></td>
- <td colspan="100">
- For <span class="grid-selected-count"></span> selected histories:
- %for operation in grid.operations:
- %if operation.allow_multiple:
- <input type="submit" name="operation" value="${operation.label}" class="action-button">
- %endif
- %endfor
-
- </td>
- </tr>
- </tfoot>
-
- </table>
-
- </form>
-
-
-
diff -r b26e2ef8726c -r 533ae45c4440 templates/history/grid.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/history/grid.mako Mon Jun 29 16:30:16 2009 -0400
@@ -0,0 +1,193 @@
+<%inherit file="/base.mako"/>
+<%def name="title()">${grid.title}</%def>
+
+%if message:
+ <p>
+ <div class="${message_type}message transient-message">${message}</div>
+ <div style="clear: both"></div>
+ </p>
+%endif
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ <script type="text/javascript">
+ ## TODO: generalize and move into galaxy.base.js
+ $(document).ready(function() {
+ $(".grid").each( function() {
+ var grid = this;
+ var checkboxes = $(this).find("input.grid-row-select-checkbox");
+ var update = $(this).find( "span.grid-selected-count" );
+ $(checkboxes).each( function() {
+ $(this).change( function() {
+ var n = $(checkboxes).filter("[checked]").size();
+ update.text( n );
+ });
+ })
+ });
+ });
+ ## Can this be moved into base.mako?
+ %if refresh_frames:
+ %if 'masthead' in refresh_frames:
+ ## Refresh masthead == user changes (backward compatibility)
+ if ( parent.user_changed ) {
+ %if trans.user:
+ parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
+ %else:
+ parent.user_changed( null, false );
+ %endif
+ }
+ %endif
+ %if 'history' in refresh_frames:
+ if ( parent.frames && parent.frames.galaxy_history ) {
+ parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}";
+ if ( parent.force_right_panel ) {
+ parent.force_right_panel( 'show' );
+ }
+ }
+ %endif
+ %if 'tools' in refresh_frames:
+ if ( parent.frames && parent.frames.galaxy_tools ) {
+ parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}";
+ if ( parent.force_left_panel ) {
+ parent.force_left_panel( 'show' );
+ }
+ }
+ %endif
+ %endif
+ </script>
+</%def>
+
+<%def name="stylesheets()">
+ <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
+ <style>
+ ## Not generic to all grids -- move to base?
+ .count-box {
+ min-width: 1.1em;
+ padding: 5px;
+ border-width: 1px;
+ border-style: solid;
+ text-align: center;
+ display: inline-block;
+ }
+ </style>
+</%def>
+
+<div class="grid-header">
+ <h2>${grid.title}</h2>
+ <span class="title">Filter:</span>
+ %for i, filter in enumerate( grid.standard_filters ):
+ %if i > 0:
+ <span>|</span>
+ %endif
+ <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span>
+ %endfor
+</div>
+
+<form name="history_actions" action="${url()}" method="post" >
+ <table class="grid">
+ <thead>
+ <tr>
+ <th></th>
+ %for column in grid.columns:
+ %if column.visible:
+ <%
+ href = ""
+ extra = ""
+ if column.sortable:
+ if sort_key == column.key:
+ if sort_order == "asc":
+ href = url( sort=( "-" + column.key ) )
+ extra = "↓"
+ else:
+ href = url( sort=( column.key ) )
+ extra = "↑"
+ else:
+ href = url( sort=column.key )
+ %>
+ <th\
+ %if column.ncells > 1:
+ colspan="${column.ncells}"
+ %endif
+ >
+ %if href:
+ <a href="${href}">${column.label}</a>
+ %else:
+ ${column.label}
+ %endif
+ <span>${extra}</span>
+ </th>
+ %endif
+ %endfor
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ %for i, item in enumerate( query ):
+ <tr \
+ %if current_item == item:
+ class="current" \
+ %endif
+ >
+ ## Item selection column
+ <td style="width: 1.5em;">
+ <input type="checkbox" name="id" value=${trans.security.encode_id( item.id )} class="grid-row-select-checkbox" />
+ </td>
+ ## Data columns
+ %for column in grid.columns:
+ %if column.visible:
+ <%
+ # Link
+ if column.link and column.link( item ):
+ href = url( **column.link( item ) )
+ else:
+ href = None
+ # Value (coerced to list so we can loop)
+ value = column.get_value( trans, grid, item )
+ if column.ncells == 1:
+ value = [ value ]
+ %>
+ %for cellnum, v in enumerate( value ):
+ <%
+ # Attach popup menu?
+ if column.attach_popup and cellnum == 0:
+ extra = '<a id="grid-%d-popup" class="popup-arrow" style="display: none;">▼</a>' % i
+ else:
+ extra = ""
+ %>
+ %if href:
+ <td><a href="${href}">${v}</a> ${extra}</td>
+ %else:
+ <td >${v}${extra}</td>
+ %endif
+ </td>
+ %endfor
+ %endif
+ %endfor
+ ## Actions column
+ <td>
+ <div popupmenu="grid-${i}-popup">
+ %for operation in grid.operations:
+ %if operation.allowed( item ):
+ <a class="action-button" href="${url( operation=operation.label, id=trans.security.encode_id( item.id ) )}">${operation.label}</a>
+ %endif
+ %endfor
+ </div>
+ </td>
+ </tr>
+ %endfor
+ </tbody>
+ <tfoot>
+ <tr>
+ <td></td>
+ <td colspan="100">
+ For <span class="grid-selected-count"></span> selected histories:
+ %for operation in grid.operations:
+ %if operation.allow_multiple:
+ <input type="submit" name="operation" value="${operation.label}" class="action-button">
+ %endif
+ %endfor
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+</form>
diff -r b26e2ef8726c -r 533ae45c4440 templates/history/list_as_xml.mako
--- a/templates/history/list_as_xml.mako Fri Jun 26 09:22:31 2009 -0400
+++ b/templates/history/list_as_xml.mako Mon Jun 29 16:30:16 2009 -0400
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<history_ids>
%for i, history in enumerate( t.user.histories ):
- <data id="${history.id}" hid="${i+1}" num="${len(history.datasets)}" name="${history.name}" create="${history.create_time}" update="${history.update_time}" >
+ <data id="${trans.security.encode_id( history.id )}" hid="${i+1}" num="${len(history.datasets)}" name="${history.name}" create="${history.create_time}" update="${history.update_time}" >
</data>
%endfor
</history_ids>
diff -r b26e2ef8726c -r 533ae45c4440 templates/history/list_shared.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/history/list_shared.mako Mon Jun 29 16:30:16 2009 -0400
@@ -0,0 +1,30 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if msg:
+ ${render_msg( msg, messagetype )}
+%endif
+
+%if shared_by_others:
+ <table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <tr class="header">
+ <th>Name</th>
+ <th>Owner</th>
+ </tr>
+ %for i, association in enumerate( shared_by_others ):
+ <% history = association.history %>
+ <tr>
+ <td>
+ ${history.name}
+ <a id="shared-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
+ <div popupmenu="shared-${i}-popup">
+ <a class="action-button" href="${h.url_for( controller='history', action='clone', id=trans.security.encode_id( history.id ) )}">Clone</a>
+ </div>
+ </td>
+ <td>${history.user.email}</td>
+ </tr>
+ %endfor
+ </table>
+%else:
+ No histories have been shared with you.
+%endif
diff -r b26e2ef8726c -r 533ae45c4440 templates/history/options.mako
--- a/templates/history/options.mako Fri Jun 26 09:22:31 2009 -0400
+++ b/templates/history/options.mako Mon Jun 29 16:30:16 2009 -0400
@@ -11,16 +11,22 @@
%endif
<ul>
-%if user:
- <li><a href="${h.url_for( controller='history', action='rename', id=history.id )}" target="galaxy_main">Rename</a> current history (stored as "${history.name}")</li>
- <li><a href="${h.url_for( controller='history', action='list')}" target="galaxy_main">List</a> previously stored histories</li>
- %if len( history.active_datasets ) > 0:
- <li><a href="${h.url_for( controller='root', action='history_new' )}">Create</a> a new empty history</li>
+ %if user:
+ <li><a href="${h.url_for( controller='history', action='list')}" target="galaxy_main">List</a> previously stored histories</li>
+ %if len( history.active_datasets ) > 0:
+ <li><a href="${h.url_for( controller='root', action='history_new' )}">Create</a> a new empty history</li>
+ <li><a href="${h.url_for( controller='workflow', action='build_from_current_history' )}">Construct workflow</a> from current history</li>
+ <li><a href="${h.url_for( controller='history', action='clone', id=trans.security.encode_id( history.id ) )}">Clone</a> current history</li>
+ %endif
+ <li><a href="${h.url_for( controller='history', action='share' )}" target="galaxy_main">Share</a> current history</div>
+ <li><a href="${h.url_for( action='history_set_default_permissions' )}">Change default permissions</a> for current history</li>
%endif
- <li><a href="${h.url_for( controller='workflow', action='build_from_current_history' )}">Construct workflow</a> from the current history</li>
- <li><a href="${h.url_for( controller='history', action='share' )}" target="galaxy_main">Share</a> current history</div>
- <li><a href="${h.url_for( action='history_set_default_permissions' )}">Change default permissions</a> for the current history</li>
-%endif
- <li><a href="${h.url_for( controller='root', action='history', show_deleted=True)}" target="galaxy_history">Show deleted</a> datasets in history</li>
+ %if len( history.activatable_datasets ) > 0:
+ <li><a href="${h.url_for( controller='root', action='history', show_deleted=True)}" target="galaxy_history">Show deleted</a> datasets in current history</li>
+ %endif
+ <li><a href="${h.url_for( controller='history', action='rename', id=trans.security.encode_id( history.id ) )}" target="galaxy_main">Rename</a> current history (stored as "${history.name}")</li>
<li><a href="${h.url_for( controller='history', action='delete_current' )}" confirm="Are you sure you want to delete the current history?">Delete</a> current history</div>
+ %if user and user.histories_shared_by_others:
+ <li><a href="${h.url_for( controller='history', action='list_shared')}" target="galaxy_main">List</a> histories shared with you by others</li>
+ %endif
</ul>
diff -r b26e2ef8726c -r 533ae45c4440 templates/history/permissions.mako
--- a/templates/history/permissions.mako Fri Jun 26 09:22:31 2009 -0400
+++ b/templates/history/permissions.mako Mon Jun 29 16:30:16 2009 -0400
@@ -3,5 +3,6 @@
<%namespace file="/dataset/security_common.mako" import="render_permission_form" />
%if trans.user:
- ${render_permission_form( trans.history, trans.history.name, h.url_for(), trans.user.all_roles() )}
+ <% history = trans.get_history() %>
+ ${render_permission_form( history, history.name, h.url_for(), trans.user.all_roles() )}
%endif
diff -r b26e2ef8726c -r 533ae45c4440 templates/history/rename.mako
--- a/templates/history/rename.mako Fri Jun 26 09:22:31 2009 -0400
+++ b/templates/history/rename.mako Mon Jun 29 16:30:16 2009 -0400
@@ -11,7 +11,7 @@
<tr>
<td>
<div class="form-row">
- <input type="hidden" name="id" value="${history.id}">
+ <input type="hidden" name="id" value="${trans.security.encode_id( history.id )}">
<label>${_('Current Name')}</label>
<div style="float: left; width: 250px; margin-right: 10px;">
${history.name}
diff -r b26e2ef8726c -r 533ae45c4440 templates/history/share.mako
--- a/templates/history/share.mako Fri Jun 26 09:22:31 2009 -0400
+++ b/templates/history/share.mako Mon Jun 29 16:30:16 2009 -0400
@@ -5,14 +5,16 @@
<div class="toolForm">
<div class="toolFormTitle">Share ${len( histories)} histories</div>
<div class="toolFormBody">
- %if not can_change and not cannot_change:
- <form action="${h.url_for( controller="history", action='share' )}" method="post" >
+ %if not can_change and not cannot_change and not no_change_needed:
+ ## We are sharing histories that contain only public datasets
+ <form name='share' id='share' action="${h.url_for( controller="history", action='share' )}" method="post" >
%for history in histories:
+ <input type="hidden" name="id" value="${trans.security.encode_id( history.id )}">
<div class="toolForm">
<div class="form-row">
<label>${_('History Name:')}</label>
<div style="float: left; width: 250px; margin-right: 10px;">
- ${history.name}<input type="hidden" name="id" value="${history.id}">
+ ${history.name}
</div>
</div>
<div style="clear: both"></div>
@@ -27,15 +29,7 @@
</td>
</div>
</div>
- ## TODO: this feature is not currently working
- ##<div style="clear: both"></div>
- ##<div class="form-row">
- ## <label>${_('Share Link')}</label>
- ## <div style="float: left; width: 250px; margin-right: 10px;">
- ## <a href="${h.url_for( controller='history', action='imp', id=trans.security.encode_id(history.id) )}">${_('copy link to share')}</a>
- ## </div>
- ##</div>
- ##<div style="clear: both"></div>
+ <div style="clear: both"></div>
<p/>
</div>
%endfor
@@ -58,15 +52,17 @@
%endif
<div style="clear: both"></div>
<div class="form-row">
- <input type="submit" name="history_share_btn" value="Submit">
+ <input type="submit" name="share_button" value="Submit">
</div>
</form>
%else:
- <form action="${h.url_for( controller='history', action='share' )}" method="post">
+ ## We are sharing restricted histories
+ <form name='share_restricted' id=share_restricted' action="${h.url_for( controller='history', action='share_restricted' )}" method="post">
+ ## Needed for rebuilding dicts
+ <input type="hidden" name="email" value="${email}" size="40">
%for history in histories:
- <input type="hidden" name="id" value="${history.id}">
+ <input type="hidden" name="id" value="${trans.security.encode_id( history.id )}">
%endfor
- <input type="hidden" name="email" value="${email}">
%if no_change_needed:
<div style="clear: both"></div>
<div class="form-row">
@@ -74,7 +70,29 @@
The following datasets can be shared with ${email} with no changes
</div>
</div>
- %for history, hdas in no_change_needed.items():
+ ## no_change_needed looks like:
+ ## { userA: {historyX : [hda, hda], historyY : [hda]}, userB: {historyY : [hda]} }
+ <%
+ # TODO: move generation of these unique dictionaries to the history controller
+ # Build the list of unique histories and datasets
+ unique_stuff = {}
+ %>
+ %for user, history_dict in no_change_needed.items():
+ %for history, hdas in history_dict.items():
+ <%
+ if history in unique_stuff:
+ for hda in hdas:
+ if hda not in unique_stuff[ history ]:
+ unique_stuff[ history ].append( hda )
+ else:
+ unique_stuff[ history ] = []
+ for hda in hdas:
+ if hda not in unique_stuff[ history ]:
+ unique_stuff[ history ].append( hda )
+ %>
+ %endfor
+ %endfor
+ %for history, hdas in unique_stuff.items():
<div class="form-row">
<label>History</label>
${history.name}
@@ -97,7 +115,29 @@
The following datasets can be shared with ${email} by updating their permissions
</div>
</div>
- %for history, hdas in can_change.items():
+ ## can_change looks like:
+ ## { userA: {historyX : [hda, hda], historyY : [hda]}, userB: {historyY : [hda]} }
+ <%
+ # TODO: move generation of these unique dictionaries to the history controller
+ # Build the list of unique histories and datasets
+ unique_stuff = {}
+ %>
+ %for user, history_dict in can_change.items():
+ %for history, hdas in history_dict.items():
+ <%
+ if history in unique_stuff:
+ for hda in hdas:
+ if hda not in unique_stuff[ history ]:
+ unique_stuff[ history ].append( hda )
+ else:
+ unique_stuff[ history ] = []
+ for hda in hdas:
+ if hda not in unique_stuff[ history ]:
+ unique_stuff[ history ].append( hda )
+ %>
+ %endfor
+ %endfor
+ %for history, hdas in unique_stuff.items():
<div class="form-row">
<label>History</label>
${history.name}
@@ -121,7 +161,29 @@
change the permissions on them
</div>
</div>
- %for history, hdas in cannot_change.items():
+ ## cannot_change looks like:
+ ## { userA: {historyX : [hda, hda], historyY : [hda]}, userB: {historyY : [hda]} }
+ <%
+ # TODO: move generation of these unique dictionaries to the history controller
+ # Build the list of unique histories and datasets
+ unique_stuff = {}
+ %>
+ %for user, history_dict in can_change.items():
+ %for history, hdas in history_dict.items():
+ <%
+ if history in unique_stuff:
+ for hda in hdas:
+ if hda not in unique_stuff[ history ]:
+ unique_stuff[ history ].append( hda )
+ else:
+ unique_stuff[ history ] = []
+ for hda in hdas:
+ if hda not in unique_stuff[ history ]:
+ unique_stuff[ history ].append( hda )
+ %>
+ %endfor
+ %endfor
+ %for history, hdas in unique_stuff.items():
<div class="form-row">
<label>History</label>
${history.name}
@@ -154,19 +216,17 @@
%endif
</div>
%endif
- %if no_change_needed:
- <div class="form-row">
- <input type="radio" name="action" value="share"> Share anyway
- %if can_change:
- (don't change any permissions)
- %endif
- </div>
- %endif
+ <div class="form-row">
+ <input type="radio" name="action" value="share_anyway"> Share anyway
+ %if can_change:
+ (don't change any permissions)
+ %endif
+ </div>
<div class="form-row">
<input type="radio" name="action" value="no_share"> Don't share
</div>
<div class="form-row">
- <input type="submit" name="share_proceed_button" value="Go"><br/>
+ <input type="submit" name="share_restricted_button" value="Go"><br/>
</div>
</form>
%endif
diff -r b26e2ef8726c -r 533ae45c4440 templates/history/sharing.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/history/sharing.mako Mon Jun 29 16:30:16 2009 -0400
@@ -0,0 +1,75 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+<h2>Public access via link</h2>
+
+%if msg:
+ ${render_msg( msg, messagetype )}
+%endif
+
+%for history in histories:
+ <p>
+ %if history.importable:
+ Send the following URL to users as an easy way for them to import the history, making a copy of their own:
+ <% url = h.url_for( controller='history', action='imp', id=trans.security.encode_id(history.id), qualified=True ) %>
+ <blockquote>
+ <a href="${url}">${url}</a>
+ </blockquote>
+ <br/>
+ <form action="${h.url_for( controller='history', action='sharing', id=trans.security.encode_id( history.id ) )}" method="POST">
+ <input class="action-button" type="submit" name="disable_import_via_link" value="Disable import via link">
+ </form>
+ %else:
+ This history is currently restricted (only you and the users listed below
+ can access it). Enabling the following option will generate a URL that you
+ can give to a user to allow them to import this history.
+ <br/>
+ <form action="${h.url_for( action='sharing', id=trans.security.encode_id(history.id) )}" method="POST">
+ <input class="action-button" type="submit" name="enable_import_via_link" value="Enable import via link">
+ </form>
+ %endif
+ </p>
+ <h2>Sharing with specific users</h2>
+ %if history.users_shared_with:
+ <ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( controller='history', action='share', id=trans.security.encode_id( history.id ) )}">
+ <span>Share with another user</span>
+ </a>
+ </li>
+ </ul>
+ <p>
+ The following users will see this history in their list of histories
+ shared with them by others, and they will be able to create their own copy of it:
+ </p>
+ <table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <tr class="header">
+ <th>History '${history.name}' currently shared with</th>
+ <th></th>
+ </tr>
+ %for i, association in enumerate( history.users_shared_with ):
+ <% user = association.user %>
+ <tr>
+ <td>
+ ${user.email}
+ <a id="user-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
+ </td>
+ <td>
+ %if len( histories ) == 1:
+ ## Only allow unsharing if we're dealing with 1 history, otherwise
+ ## page refreshes screw things up
+ <div popupmenu="user-${i}-popup">
+ <a class="action-button" href="${h.url_for( controller='history', action='sharing', id=trans.security.encode_id( history.id ), unshare_user=trans.security.encode_id( user.id ) )}">Unshare</a>
+ </div>
+ %endif
+ </td>
+ </tr>
+ %endfor
+ </table>
+ %else:
+ <p>You have not shared this history with any users.</p>
+ <a class="action-button" href="${h.url_for( controller='history', action='share', id=trans.security.encode_id(history.id) )}">
+ <span>Share with another user</span>
+ </a>
+ %endif
+%endfor
diff -r b26e2ef8726c -r 533ae45c4440 test/base/twilltestcase.py
--- a/test/base/twilltestcase.py Fri Jun 26 09:22:31 2009 -0400
+++ b/test/base/twilltestcase.py Mon Jun 29 16:30:16 2009 -0400
@@ -9,7 +9,8 @@
from twill.other_packages._mechanize_dist import ClientForm
pkg_resources.require( "elementtree" )
from elementtree import ElementTree
-
+from galaxy.web import security
+
buffer = StringIO.StringIO()
#Force twill to log to a buffer -- FIXME: Should this go to stdout and be captured by nose?
@@ -23,13 +24,15 @@
class TwillTestCase( unittest.TestCase ):
def setUp( self ):
+ # Security helper
+ self.security = security.SecurityHelper( id_secret='changethisinproductiontoo' )
self.history_id = os.environ.get( 'GALAXY_TEST_HISTORY_ID', None )
self.host = os.environ.get( 'GALAXY_TEST_HOST' )
self.port = os.environ.get( 'GALAXY_TEST_PORT' )
self.url = "http://%s:%s" % ( self.host, self.port )
self.file_dir = os.environ.get( 'GALAXY_TEST_FILE_DIR' )
self.home()
- self.set_history()
+ #self.set_history()
# Functions associated with files
def files_diff( self, file1, file2 ):
@@ -71,6 +74,7 @@
def upload_file( self, filename, ftype='auto', dbkey='unspecified (?)' ):
"""Uploads a file"""
filename = self.get_filename(filename)
+ self.home()
self.visit_page( "tool_runner/index?tool_id=upload1" )
try:
tc.fv("1","file_type", ftype)
@@ -79,9 +83,16 @@
tc.submit("runtool_btn")
self.home()
except AssertionError, err:
- errmsg = 'The file doesn\'t exsit. Please check' % file
+ errmsg = "The file (%s) doesn't exist." % filename
errmsg += str( err )
raise AssertionError( errmsg )
+ # Make sure every history item has a valid hid
+ hids = self.get_hids_in_history()
+ for hid in hids:
+ try:
+ valid_hid = int( hid )
+ except:
+ raise AssertionError, "Invalid hid (%s) created when uploading file %s" % ( hid, filename )
def upload_url_paste( self, url_paste, ftype='auto', dbkey='unspecified (?)' ):
"""Pasted data in the upload utility"""
self.visit_page( "tool_runner/index?tool_id=upload1" )
@@ -94,6 +105,13 @@
except Exception, e:
errmsg = "Problem executing upload utility using url_paste: %s" % str( e )
raise AssertionError( e )
+ # Make sure every history item has a valid hid
+ hids = self.get_hids_in_history()
+ for hid in hids:
+ try:
+ valid_hid = int( hid )
+ except:
+ raise AssertionError, "Invalid hid (%s) created when pasting %s" % ( hid, url_paste )
# Functions associated with histories
def check_history_for_errors( self ):
@@ -115,27 +133,30 @@
self.visit_page( "clear_history" )
self.check_history_for_string( 'Your history is empty' )
self.home()
- def delete_history( self, id='' ):
+ def delete_history( self, id ):
"""Deletes one or more histories"""
- history_list = self.get_histories()
+ history_list = self.get_histories_as_data_list()
self.assertTrue( history_list )
- num_deleted = 1
- if not id:
- history = history_list[0]
- id = history.get( 'id' )
- else:
- num_deleted = len( id.split( ',' ) )
+ num_deleted = len( id.split( ',' ) )
+ self.home()
self.visit_page( "history/list?operation=delete&id=%s" % ( id ) )
check_str = 'Deleted %d histories' % num_deleted
self.check_page_for_string( check_str )
self.home()
- def get_histories( self ):
- """Returns all histories"""
+ def delete_current_history( self, check_str='' ):
+ """Deletes the current history"""
+ self.home()
+ self.visit_page( "history/delete_current" )
+ if check_str:
+ self.check_page_for_string( check_str )
+ self.home()
+ def get_histories_as_data_list( self ):
+ """Returns the data elements of all histories"""
tree = self.histories_as_xml_tree()
data_list = [ elem for elem in tree.findall("data") ]
return data_list
- def get_history( self, show_deleted=False ):
- """Returns a history"""
+ def get_history_as_data_list( self, show_deleted=False ):
+ """Returns the data elements of a history"""
tree = self.history_as_xml_tree( show_deleted=show_deleted )
data_list = [ elem for elem in tree.findall("data") ]
return data_list
@@ -153,31 +174,24 @@
xml = self.last_page()
tree = ElementTree.fromstring(xml)
return tree
- def history_options( self, check_str='', upload=False ):
+ def history_options( self, user=False, active_datasets=False, activatable_datasets=False, histories_shared_by_others=False ):
"""Mimics user clicking on history options link"""
- self.visit_page( "history_options" )
- if check_str:
- self.check_page_for_string( check_str )
- else:
- self.check_page_for_string( 'Rename</a> current history' )
+ self.home()
+ self.visit_page( "root/history_options" )
+ if user:
self.check_page_for_string( 'List</a> previously stored histories' )
- self.check_page_for_string( 'Construct workflow</a> from the current history' )
+ if active_datasets:
+ self.check_page_for_string( 'Create</a> a new empty history' )
+ self.check_page_for_string( 'Construct workflow</a> from current history' )
+ self.check_page_for_string( 'Clone</a> current history' )
self.check_page_for_string( 'Share</a> current history' )
- # Tests for changing default history permissions are done in test_security_and_libraries.py
- self.check_page_for_string( 'Change default permissions</a> for the current history' )
- self.check_page_for_string( 'Show deleted</a> datasets in history' )
- self.check_page_for_string( 'Delete</a> current history' )
- # Need to add a history item in order to create a new empty history
- try:
- self.check_page_for_string( 'Create</a> a new empty history' )
- raise AssertionError, "Incorrectly able to create a new empty history when the current history is empty."
- except:
- pass
- if upload:
- self.upload_file( '1.bed', dbkey='hg18' )
- self.home()
- self.visit_page( "history_options" )
- self.check_page_for_string( 'Create</a> a new empty history' )
+ self.check_page_for_string( 'Change default permissions</a> for current history' )
+ if histories_shared_by_others:
+ self.check_page_for_string( 'List</a> histories shared with you by others' )
+ if activatable_datasets:
+ self.check_page_for_string( 'Show deleted</a> datasets in current history' )
+ self.check_page_for_string( 'Rename</a> current history' )
+ self.check_page_for_string( 'Delete</a> current history' )
self.home()
def new_history( self, name=None ):
"""Creates a new, empty history"""
@@ -198,26 +212,63 @@
def set_history( self ):
"""Sets the history (stores the cookies for this run)"""
if self.history_id:
+ self.home()
self.visit_page( "history?id=%s" % self.history_id )
else:
self.new_history()
self.home()
- def share_history( self, id, email, check_str, check_str2='', action=None, action_check_str=None ):
- """Share a history different users"""
- self.visit_url( "%s/history/share?id=%s&email=%s&history_share_btn=Submit" % ( self.url, id, email ) )
- self.check_page_for_string( check_str )
+ def share_current_history( self, email, check_str='', check_str_after_submit='', check_str_after_submit2='',
+ action='', action_check_str='', action_check_str_after_submit='' ):
+ """Share the current history with different users"""
+ self.visit_url( "%s/history/share" % self.url )
+ if check_str:
+ self.check_page_for_string( check_str )
+ tc.fv( 'share', 'email', email )
+ tc.submit( 'share_button' )
+ if check_str_after_submit:
+ self.check_page_for_string( check_str_after_submit )
+ if check_str_after_submit2:
+ self.check_page_for_string( check_str_after_submit2 )
+ if action:
+ # If we have an action, then we are sharing datasets with users that do not have access permissions on them
+ if action_check_str:
+ self.check_page_for_string( action_check_str )
+ tc.fv( 'share_restricted', 'action', action )
+ tc.submit( "share_restricted_button" )
+ if action_check_str_after_submit:
+ self.check_page_for_string( action_check_str_after_submit )
+ self.home()
+ def share_histories_with_users( self, ids, emails, check_str1='', check_str2='',
+ check_str_after_submit='', action=None, action_check_str=None ):
+ """Share one or more histories with one or more different users"""
+ self.visit_url( "%s/history/list?id=%s&operation=Share" % ( self.url, ids ) )
+ if check_str1:
+ self.check_page_for_string( check_str1 )
if check_str2:
self.check_page_for_string( check_str2 )
+ tc.fv( 'share', 'email', emails )
+ tc.submit( 'share_button' )
+ if check_str_after_submit:
+ self.check_page_for_string( check_str_after_submit )
if action:
# If we have an action, then we are sharing datasets with users that do not have access permissions on them
- tc.fv( '1', 'action', action )
- tc.submit( "share_proceed_button" )
+ tc.fv( 'share_restricted', 'action', action )
+ tc.submit( "share_restricted_button" )
if action_check_str:
self.check_page_for_string( action_check_str )
self.home()
+ def unshare_history( self, history_id, user_id, check_str1='', check_str2='', check_str_after_submit='' ):
+ """Unshare a history that has been shared with another user"""
+ self.visit_url( "%s/history/list?id=%s&operation=sharing" % ( self.url, history_id ) )
+ if check_str1:
+ self.check_page_for_string( check_str1 )
+ if check_str2:
+ self.check_page_for_string( check_str2 )
+ self.visit_url( "%s/history/sharing?unshare_user=%s&id=%s" % ( self.url, user_id, history_id ) )
+ self.home()
def switch_history( self, id='', name='' ):
"""Switches to a history in the current list of histories"""
- data_list = self.get_histories()
+ data_list = self.get_histories_as_data_list()
self.assertTrue( data_list )
if not id:
history = history_list[0]
@@ -227,6 +278,7 @@
self.check_history_for_string( name )
self.home()
def view_stored_active_histories( self, check_str='' ):
+ self.home()
self.visit_page( "history/list" )
self.check_page_for_string( 'Stored histories' )
self.check_page_for_string( '<input type="checkbox" name="id" value=' )
@@ -237,12 +289,57 @@
self.check_page_for_string( check_str )
self.home()
def view_stored_deleted_histories( self, check_str='' ):
+ self.home()
self.visit_page( "history/list?f-deleted=True" )
self.check_page_for_string( 'Stored histories' )
self.check_page_for_string( '<input type="checkbox" name="id" value=' )
self.check_page_for_string( 'operation=Undelete&id' )
if check_str:
self.check_page_for_string( check_str )
+ self.home()
+ def view_shared_histories( self, check_str='', check_str2='' ):
+ self.home()
+ self.visit_page( "history/list_shared" )
+ if check_str:
+ self.check_page_for_string( check_str )
+ if check_str2:
+ self.check_page_for_string( check_str2 )
+ self.home()
+ def clone_history( self, history_id, check_str1='' ):
+ self.home()
+ self.visit_page( "history/clone?id=%s" % history_id )
+ if check_str1:
+ self.check_page_for_string( check_str1 )
+ self.home()
+ def enable_import_via_link( self, history_id, check_str='', check_str_after_submit='' ):
+ self.home()
+ self.visit_page( "history/list?operation=sharing&id=%s" % history_id )
+ if check_str:
+ self.check_page_for_string( check_str )
+ # twill barfs on this form, possibly because it contains no fields, but not sure.
+ # In any case, we have to mimic the form submission
+ self.home()
+ self.visit_page( 'history/sharing?id=%s&enable_import_via_link=True' % history_id )
+ if check_str_after_submit:
+ self.check_page_for_string( check_str_after_submit )
+ self.home()
+ def disable_import_via_link( self, history_id, check_str='', check_str_after_submit='' ):
+ self.home()
+ self.visit_page( "history/list?operation=sharing&id=%s" % history_id )
+ if check_str:
+ self.check_page_for_string( check_str )
+ # twill barfs on this form, possibly because it contains no fields, but not sure.
+ # In any case, we have to mimic the form submission
+ self.home()
+ self.visit_page( 'history/sharing?id=%s&disable_import_via_link=True' % history_id )
+ if check_str_after_submit:
+ self.check_page_for_string( check_str_after_submit )
+ self.home()
+ def import_history_via_url( self, history_id, email, check_str_after_submit='' ):
+ self.home()
+ self.visit_page( "history/imp?&id=%s" % history_id )
+ if check_str_after_submit:
+ self.check_page_for_string( check_str_after_submit )
self.home()
# Functions associated with datasets (history items) and meta data
@@ -260,7 +357,7 @@
def check_metadata_for_string( self, patt, hid=None ):
"""Looks for 'patt' in the edit page when editing a dataset"""
- data_list = self.get_history()
+ data_list = self.get_history_as_data_list()
self.assertTrue( data_list )
if hid is None: # take last hid
elem = data_list[-1]
@@ -276,7 +373,7 @@
except:
raise AssertionError, "Invalid hid '%s' - must be int" % hid
hid = str(hid)
- data_list = self.get_history()
+ data_list = self.get_history_as_data_list()
self.assertTrue( data_list )
elems = [ elem for elem in data_list if elem.get( 'hid' ) == hid ]
self.assertEqual( len( elems ), 1 )
@@ -291,7 +388,7 @@
except:
raise AssertionError, "Invalid hid '%s' - must be int" % hid
hid = str( hid )
- data_list = self.get_history( show_deleted=show_deleted )
+ data_list = self.get_history_as_data_list( show_deleted=show_deleted )
self.assertTrue( data_list )
elems = [ elem for elem in data_list if elem.get( 'hid' ) == hid ]
self.assertEqual( len( elems ), 1 )
@@ -348,7 +445,8 @@
tc.submit( 'change' )
self.check_page_for_string( 'Edit Attributes' )
self.home()
- def copy_history_item( self, source_dataset_ids='', target_history_ids=[], all_target_history_ids=[], deleted_history_ids=[] ):
+ def copy_history_item( self, source_dataset_ids='', target_history_ids=[], all_target_history_ids=[],
+ deleted_history_ids=[] ):
"""Copy 1 or more history_dataset_associations to 1 or more histories"""
self.home()
self.visit_url( "%s/dataset/copy_datasets?source_dataset_ids=%s" % ( self.url, source_dataset_ids ) )
@@ -371,31 +469,28 @@
check_str = '%d datasets copied to %d histories.' % ( no_source_ids, len( target_history_ids ) )
self.check_page_for_string( check_str )
self.home()
- def get_dataset_ids_in_history( self ):
- """Returns the ids of datasets in a history"""
- data_list = self.get_history()
+ def get_hids_in_history( self ):
+ """Returns the list of hid values for items in a history"""
+ data_list = self.get_history_as_data_list()
hids = []
for elem in data_list:
hid = elem.get('hid')
hids.append(hid)
return hids
-
- def get_dataset_ids_in_histories( self ):
- """Returns the ids of datasets in all histories"""
- data_list = self.get_histories()
+ def get_hids_in_histories( self ):
+ """Returns the list of hids values for items in all histories"""
+ data_list = self.get_histories_as_data_list()
hids = []
for elem in data_list:
hid = elem.get('hid')
hids.append(hid)
return hids
-
def verify_dataset_correctness( self, filename, hid=None, wait=True ):
"""Verifies that the attributes and contents of a history item meet expectations"""
- if wait: self.wait() #wait for job to finish
-
- data_list = self.get_history()
+ if wait:
+ self.wait() #wait for job to finish
+ data_list = self.get_history_as_data_list()
self.assertTrue( data_list )
-
if hid is None: # take last hid
elem = data_list[-1]
hid = str( elem.get('hid') )
@@ -404,10 +499,8 @@
elems = [ elem for elem in data_list if elem.get('hid') == hid ]
self.assertTrue( len(elems) == 1 )
elem = elems[0]
-
self.assertTrue( hid )
self._assert_dataset_state( elem, 'ok' )
-
if self.is_zipped( filename ):
errmsg = 'History item %s is a zip archive which includes invalid files:\n' % hid
zip_file = zipfile.ZipFile( filename, "r" )
@@ -422,6 +515,7 @@
else:
local_name = self.get_filename( filename )
temp_name = self.get_filename( 'temp_%s' % filename )
+ self.home()
self.visit_page( "display?hid=" + hid )
data = self.last_page()
file( temp_name, 'wb' ).write(data)
@@ -455,7 +549,7 @@
def verify_genome_build( self, dbkey='hg17' ):
"""Verifies that the last used genome_build at history id 'hid' is as expected"""
- data_list = self.get_history()
+ data_list = self.get_history_as_data_list()
self.assertTrue( data_list )
elems = [ elem for elem in data_list ]
elem = elems[-1]
diff -r b26e2ef8726c -r 533ae45c4440 test/functional/__init__.py
--- a/test/functional/__init__.py Fri Jun 26 09:22:31 2009 -0400
+++ b/test/functional/__init__.py Mon Jun 29 16:30:16 2009 -0400
@@ -24,7 +24,7 @@
# server (for running the tests against a running instance)
default_galaxy_test_host = "localhost"
-default_galaxy_test_port = "9999"
+default_galaxy_test_port = "8777"
default_galaxy_locales = 'en'
galaxy_test_file_dir = "test-data"
server = None
@@ -63,10 +63,10 @@
default_cluster_job_runner = os.environ['GALAXY_TEST_DEF_RUNNER']
else:
default_cluster_job_runner = 'local:///'
-
app = UniverseApplication( job_queue_workers = 5,
start_job_runners = start_job_runners,
default_cluster_job_runner = default_cluster_job_runner,
+ id_secret = 'changethisinproductiontoo',
template_path = "templates",
database_connection = database_connection,
file_path = file_path,
diff -r b26e2ef8726c -r 533ae45c4440 test/functional/test_DNAse_flanked_genes.py
--- a/test/functional/test_DNAse_flanked_genes.py Fri Jun 26 09:22:31 2009 -0400
+++ b/test/functional/test_DNAse_flanked_genes.py Mon Jun 29 16:30:16 2009 -0400
@@ -6,10 +6,12 @@
class AnalysisDNAseHSSFlankedGenes( TwillTestCase ):
def test_get_DNAseHSS_flanked_genes( self ):
- self.login()
- self.new_history()
- global history1
- history1 = galaxy.model.History.query() \
+ self.logout()
+ self.login( email='test(a)bx.psu.edu' )
+ admin_user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test(a)bx.psu.edu' ).one()
+ self.new_history( name='DNAseHSS_flanked_genes' )
+ history1 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
.order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
track_params = dict(
db="hg17",
@@ -85,5 +87,5 @@
self.run_tool( 'Filter1', input="4", cond="c17==1000" )
self.wait()
self.verify_dataset_correctness( 'filteredJoinedFlanksDNAse.dat' )
- self.delete_history()
+ self.delete_history( self.security.encode_id( history1.id ) )
self.logout()
diff -r b26e2ef8726c -r 533ae45c4440 test/functional/test_get_data.py
--- a/test/functional/test_get_data.py Fri Jun 26 09:22:31 2009 -0400
+++ b/test/functional/test_get_data.py Mon Jun 29 16:30:16 2009 -0400
@@ -6,8 +6,12 @@
def test_000_upload_files_from_disk( self ):
"""Test uploading data files from disk"""
self.logout()
- self.login( email='tst(a)bx.psu.edu' )
- history1 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ self.login( email='test(a)bx.psu.edu' )
+ global admin_user
+ admin_user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test(a)bx.psu.edu' ).one()
+ history1 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
self.upload_file( '1.bed' )
hda1 = galaxy.model.HistoryDatasetAssociation.query() \
.order_by( desc( galaxy.model.HistoryDatasetAssociation.table.c.create_time ) ).first()
@@ -38,24 +42,28 @@
.order_by( desc( galaxy.model.HistoryDatasetAssociation.table.c.create_time ) ).first()
assert hda6 is not None, "Problem retrieving hda6 from database"
self.verify_dataset_correctness( '1.scf.zip', hid=str( hda6.hid ) )
- self.delete_history( id=str( history1.id ) )
+ self.delete_history( id=self.security.encode_id( history1.id ) )
def test_005_url_paste( self ):
"""Test url paste behavior"""
# Deleting the current history should have created a new history
self.check_history_for_string( 'Your history is empty' )
- history2 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ history2 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
self.upload_url_paste( 'hello world' )
self.check_history_for_string( 'Pasted Entry' )
self.check_history_for_string( 'hello world' )
self.upload_url_paste( u'hello world' )
self.check_history_for_string( 'Pasted Entry' )
self.check_history_for_string( 'hello world' )
- self.delete_history( id=str( history2.id ) )
+ self.delete_history( id=self.security.encode_id( history2.id ) )
def test_010_upload_encode_data( self ):
"""Test uploading encode data"""
# Deleting the current history should have created a new history
self.check_history_for_string( 'Your history is empty' )
- history3 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ history3 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
self.run_tool( 'encode_import_chromatin_and_chromosomes1', hg17=['cc.EarlyRepSeg.20051216.bed'] )
self.wait()
hda7 = galaxy.model.HistoryDatasetAssociation.query() \
@@ -68,7 +76,4 @@
.order_by( desc( galaxy.model.HistoryDatasetAssociation.table.c.create_time ) ).first()
assert hda8 is not None, "Problem retrieving hda8 from database"
self.verify_dataset_correctness( 'sc_3D_cds.bed', hid=str( hda8.hid ) )
- self.delete_history( id=str( history3.id ) )
- def test_015_reset_data_for_later_test_runs( self ):
- """Reseting data to enable later test runs to pass"""
- self.logout()
+ self.delete_history( id=self.security.encode_id( history3.id ) )
diff -r b26e2ef8726c -r 533ae45c4440 test/functional/test_history_functions.py
--- a/test/functional/test_history_functions.py Fri Jun 26 09:22:31 2009 -0400
+++ b/test/functional/test_history_functions.py Mon Jun 29 16:30:16 2009 -0400
@@ -5,12 +5,11 @@
class TestHistory( TwillTestCase ):
- def test_000_history_options_when_not_logged_in( self ):
- """Testing history options when not logged in"""
+ def test_000_history_behavior_between_logout_login( self ):
+ """Testing history behavior between logout and login"""
self.logout()
- check_str = 'logged in</a> to store or switch histories.'
- self.history_options( check_str=check_str )
- # Make sure we have created the following accounts
+ self.history_options()
+ # Make sure we have created the following 4 accounts
self.login( email='test1(a)bx.psu.edu' )
global regular_user1
regular_user1 = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test1(a)bx.psu.edu' ).first()
@@ -25,14 +24,12 @@
global regular_user3
regular_user3 = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test3(a)bx.psu.edu' ).first()
assert regular_user3 is not None, 'Problem retrieving user with email "test3(a)bx.psu.edu" from the database'
- def test_005_deleting_histories( self ):
- """Testing deleting histories"""
self.logout()
self.login( email='test(a)bx.psu.edu' )
global admin_user
- admin_user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test(a)bx.psu.edu' ).first()
+ admin_user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test(a)bx.psu.edu' ).one()
assert admin_user is not None, 'Problem retrieving user with email "test(a)bx.psu.edu" from the database'
- # Get the admin_user private role
+ # Get the admin_user private role for later use
global admin_user_private_role
admin_user_private_role = None
for role in admin_user.all_roles():
@@ -41,27 +38,48 @@
break
if not admin_user_private_role:
raise AssertionError( "Private role not found for user '%s'" % admin_user.email )
- latest_history = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
- assert latest_history is not None, "Problem retrieving latest history from database"
- assert not latest_history.deleted, "After login, associated history is deleted"
- self.delete_history( str( latest_history.id ) )
- latest_history.refresh()
- if not latest_history.deleted:
- raise AssertionError, "Problem deleting history id %d" % latest_history.id
+ historyA = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert historyA is not None, "Problem retrieving historyA from database"
+ assert not historyA.deleted, "After login, historyA is deleted"
+ # Make sure the last used history is set for the next session after login
+ self.logout()
+ self.login( email=admin_user.email )
+ historyB = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert historyB is not None, "Problem retrieving historyB from database"
+ assert historyA.id == historyB.id, "After the same user logged out and back in, their last used history was not associated with their new session"
+ def test_005_deleting_histories( self ):
+ """Testing deleting histories"""
+ # Logged in as admin_user
+ historyB = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert historyB is not None, "Problem retrieving historyB from database"
+ self.delete_history( self.security.encode_id( historyB.id ) )
+ historyB.refresh()
+ if not historyB.deleted:
+ raise AssertionError, "Problem deleting history id %d" % historyB.id
# Since we deleted the current history, make sure the history frame was refreshed
self.check_history_for_string( 'Your history is empty.' )
# We'll now test deleting a list of histories
# After deleting the current history, a new one should have been created
global history1
- history1 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ history1 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
assert history1 is not None, "Problem retrieving history1 from database"
self.upload_file( '1.bed', dbkey='hg18' )
self.new_history( name=urllib.quote( 'history2' ) )
global history2
- history2 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ history2 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
assert history2 is not None, "Problem retrieving history2 from database"
self.upload_file( '2.bed', dbkey='hg18' )
- ids = '%s,%s' % ( str( history1.id ), str( history2.id ) )
+ ids = '%s,%s' % ( self.security.encode_id( history1.id ), self.security.encode_id( history2.id ) )
self.delete_history( ids )
# Since we deleted the current history, make sure the history frame was refreshed
self.check_history_for_string( 'Your history is empty.' )
@@ -87,116 +105,153 @@
raise AssertionError, "Problem deleting history id %d" % history2.id
if not history2.default_permissions:
raise AssertionError, "Default permissions were incorrectly deleted from the db for history id %d when it was deleted" % history2.id
- def test_010_history_options_when_logged_in( self ):
- """Testing history options when logged in"""
- self.history_options()
- def test_015_history_rename( self ):
+ # Current history is empty
+ self.history_options( user=True )
+ def test_010_history_rename( self ):
"""Testing renaming a history"""
+ # Logged in as admin_user
global history3
- history3 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ history3 = galaxy.model.History \
+ .filter( galaxy.model.History.table.c.deleted==False ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ) \
+ .first()
assert history3 is not None, "Problem retrieving history3 from database"
if history3.deleted:
raise AssertionError, "History id %d deleted when it should not be" % latest_history.id
- self.rename_history( str( history3.id ), history3.name, new_name=urllib.quote( 'history 3' ) )
- def test_020_history_list( self ):
- """Testing viewing previously stored histories"""
+ self.rename_history( self.security.encode_id( history3.id ), history3.name, new_name=urllib.quote( 'history 3' ) )
+ history3.refresh()
+ def test_015_history_list( self ):
+ """Testing viewing previously stored active histories"""
+ # Logged in as admin_user
self.view_stored_active_histories()
- def test_025_history_share( self ):
- """Testing sharing histories containing only public datasets"""
+ def test_020_share_current_history( self ):
+ """Testing sharing the current history which contains only public datasets"""
+ # Logged in as admin_user
+ # Test sharing an empty history - current history is history3
+ self.share_current_history( regular_user1.email,
+ check_str=history3.name,
+ check_str_after_submit='You cannot share an empty history.' )
+ # Make history3 sharable by adding a dataset
+ self.upload_file( '1.bed', dbkey='hg18' )
+ # Current history is no longer empty
+ self.history_options( user=True, active_datasets=True, activatable_datasets=True )
+ # Test sharing history3 with yourself
+ self.share_current_history( admin_user.email,
+ check_str=history3.name,
+ check_str_after_submit='You cannot send histories to yourself.' )
+ # Share history3 with 1 valid user
+ self.share_current_history( regular_user1.email,
+ check_str=history3.name,
+ check_str_after_submit='History (%s) now shared with: 1 users' % history3.name )
+ # Check out list of histories to make sure history3 was shared
+ self.view_stored_active_histories( check_str='operation=sharing&id=%s">shared' % self.security.encode_id( history3.id ) )
+ # Enable importing history3 via a URL
+ self.enable_import_via_link( self.security.encode_id( history3.id ),
+ check_str='Unshare',
+ check_str_after_submit='Send the following URL to users' )
+ # Make sure history3 is now import-able
history3.refresh()
- self.upload_file( '1.bed', dbkey='hg18' )
- # Test sharing a history with yourself
- check_str = "You can't send histories to yourself."
- self.share_history( str( history3.id ), 'test(a)bx.psu.edu', check_str )
- # Share a history with 1 valid user
- check_str = 'Histories (%s) have been shared with: %s' % ( history3.name, regular_user1.email )
- self.share_history( str( history3.id ), regular_user1.email, check_str )
- # We need to keep track of all shared histories so they can later be deleted
- global history3_copy1
- history3_copy1 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
- assert history3_copy1 is not None, "Problem retrieving history3_copy1 from database"
+ if not history3.importable:
+ raise AssertionError, "History 3 is not marked as importable after enable_import_via_link"
+ # Try importing history3
+ self.import_history_via_url( self.security.encode_id( history3.id ),
+ admin_user.email,
+ check_str_after_submit='You cannot import your own history.' )
+ # Disable the import link for history3
+ self.disable_import_via_link( self.security.encode_id( history3.id ),
+ check_str='Send the following URL to users',
+ check_str_after_submit='Enable import via link' )
+ # Try importing history3 after disabling the URL
+ self.import_history_via_url( self.security.encode_id( history3.id ),
+ admin_user.email,
+ check_str_after_submit='The owner of this history has disabled imports via this link.' )
+ # Test sharing history3 with an invalid user
+ self.share_current_history( 'jack(a)jill.com',
+ check_str_after_submit='jack(a)jill.com is not a valid Galaxy user.' )
+
+
+
+
+
+ def test_025_delete_shared_current_history( self ):
+ """Testing deleting the current history after it was shared"""
+ # Logged in as admin_user
+ self.delete_current_history( check_str="History (%s) has been shared with others, unshare it before deleting it." % history3.name )
+ def test_030_clone_shared_history( self ):
+ """Testing cloning a shared history"""
+ # logged in as admin user
self.logout()
self.login( email=regular_user1.email )
- check_str = '%s from %s' % ( history3.name, admin_user.email )
+ # Shared history3 affects history options
+ self.history_options( user=True, histories_shared_by_others=True )
+ # Shared history3 should be in regular_user1's list of shared histories
+ self.view_shared_histories( check_str=history3.name, check_str2=admin_user.email )
+ self.clone_history( self.security.encode_id( history3.id ),
+ check_str1='is now included in your list of stored histories.' )
+ global history3_clone1
+ history3_clone1 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==regular_user1.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history3_clone1 is not None, "Problem retrieving history3_clone1 from database"
+ # Check list of histories to make sure shared history3 was cloned
+ check_str = "Clone of '%s' shared by '%s'" % ( history3.name, admin_user.email )
self.view_stored_active_histories( check_str=check_str )
- # Need to delete history3_copy1
- self.delete_history( id=str( history3_copy1.id ) )
+ def test_035_clone_current_history( self ):
+ """Testing cloning the current history"""
+ # logged in as regular_user1
self.logout()
self.login( email=admin_user.email )
- # Test sharing a history with an invalid user
- email = 'jack(a)jill.com'
- check_str = '%s is not a valid Galaxy user.' % email
- self.share_history( str( history3.id ), email, check_str )
- # Test sharing multiple histories with multiple users
+ self.clone_history( self.security.encode_id( history3.id ),
+ check_str1='is now included in your list of stored histories.' )
+ global history3_clone2
+ history3_clone2 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history3_clone2 is not None, "Problem retrieving history3_clone2 from database"
+ # Check list of histories to make sure shared history3 was cloned
+ self.view_stored_active_histories( check_str="Clone of '%s'" % history3.name )
+ def test_040_sharing_mulitple_histories_with_multiple_users( self ):
+ """Testing sharing multiple histories containing only public datasets with multiple users"""
+ # Logged in as admin_user
self.new_history()
global history4
- history4 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ history4 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
assert history4 is not None, "Problem retrieving history4 from database"
- self.rename_history( str( history4.id ), history4.name, new_name=urllib.quote( 'history 4' ) )
+ self.rename_history( self.security.encode_id( history4.id ), history4.name, new_name=urllib.quote( 'history 4' ) )
history4.refresh()
self.upload_file( '2.bed', dbkey='hg18' )
- id = '%s,%s' % ( str( history3.id ), str( history4.id ) )
- name = '%s,%s' % ( history3.name, history4.name )
- email = '%s,%s' % ( regular_user2.email, regular_user3.email )
- check_str = 'Histories (%s) have been shared with: %s' % ( name, email )
- self.share_history( id, email, check_str )
- # We need to keep track of all shared histories so they can later be deleted
- history3_copy_name = "%s from %s" % ( history3.name, admin_user.email )
- history3_to_use_for_regular_user2 = galaxy.model.History \
- .filter( and_( galaxy.model.History.table.c.name==history3_copy_name,
- galaxy.model.History.table.c.user_id==regular_user2.id,
- galaxy.model.History.table.c.deleted==False ) ) \
- .order_by( desc( galaxy.model.History.table.c.create_time ) ) \
- .first()
- assert history3_to_use_for_regular_user2 is not None, "Problem retrieving history3_to_use_for_regular_user2 from database"
- history3_to_use_for_regular_user3 = galaxy.model.History \
- .filter( and_( galaxy.model.History.table.c.name==history3_copy_name,
- galaxy.model.History.table.c.user_id==regular_user3.id,
- galaxy.model.History.table.c.deleted==False ) ) \
- .order_by( desc( galaxy.model.History.table.c.create_time ) ) \
- .first()
- assert history3_to_use_for_regular_user3 is not None, "Problem retrieving history3_to_use_for_regular_user3 from database"
- history4_copy_name = "%s from %s" % ( history4.name, admin_user.email )
- history4_to_use_for_regular_user2 = galaxy.model.History \
- .filter( and_( galaxy.model.History.table.c.name==history4_copy_name,
- galaxy.model.History.table.c.user_id==regular_user2.id,
- galaxy.model.History.table.c.deleted==False ) ) \
- .order_by( desc( galaxy.model.History.table.c.create_time ) ) \
- .first()
- assert history4_to_use_for_regular_user2 is not None, "Problem retrieving history4_to_use_for_regular_user2 from database"
- history4_to_use_for_regular_user3 = galaxy.model.History \
- .filter( and_( galaxy.model.History.table.c.name==history4_copy_name,
- galaxy.model.History.table.c.user_id==regular_user3.id,
- galaxy.model.History.table.c.deleted==False ) ) \
- .order_by( desc( galaxy.model.History.table.c.create_time ) ) \
- .first()
- assert history4_to_use_for_regular_user3 is not None, "Problem retrieving history4_to_use_for_regular_user3 from database"
+ ids = '%s,%s' % ( self.security.encode_id( history3.id ), self.security.encode_id( history4.id ) )
+ emails = '%s,%s' % ( regular_user2.email, regular_user3.email )
+ check_str_after_submit = 'History (%s) now shared with: 3 users.' % history3.name
+ self.share_histories_with_users( ids,
+ emails,
+ check_str1='Share 2 histories',
+ check_str2=history4.name,
+ check_str_after_submit=check_str_after_submit )
self.logout()
self.login( email=regular_user2.email )
- check_str = '%s from %s' % ( history3.name, admin_user.email )
- self.view_stored_active_histories( check_str=check_str )
- check_str = '%s from %s' % ( history4.name, admin_user.email )
- self.view_stored_active_histories( check_str=check_str )
- # Need to delete the copied histories, so later test runs are valid
- self.delete_history( id=str( history3_to_use_for_regular_user2.id ) )
- self.delete_history( id=str( history4_to_use_for_regular_user2.id ) )
+ # Shared history3 should be in regular_user2's list of shared histories
+ self.view_shared_histories( check_str=history3.name, check_str2=admin_user.email )
self.logout()
self.login( email=regular_user3.email )
- check_str = '%s from %s' % ( history3.name, admin_user.email )
- self.view_stored_active_histories( check_str=check_str )
- check_str = '%s from %s' % ( history4.name, admin_user.email )
- self.view_stored_active_histories( check_str=check_str )
- # Need to delete the copied histories, so later test runs are valid
- self.delete_history( id=str( history3_to_use_for_regular_user3.id ) )
- self.delete_history( id=str( history4_to_use_for_regular_user3.id ) )
+ # Shared history3 should be in regular_user3's list of shared histories
+ self.view_shared_histories( check_str=history3.name, check_str2=admin_user.email )
+ def test_045_change_permissions_on_current_history( self ):
+ """Testing changing permissions on the current history"""
+ # Logged in as regular_user3
self.logout()
self.login( email=admin_user.email )
- def test_030_change_permissions_on_current_history( self ):
- """Testing changing permissions on the current history"""
+ # Current history is history4
+ self.new_history()
global history5
- history5 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ history5 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
assert history5 is not None, "Problem retrieving history5 from database"
- self.rename_history( str( history5.id ), history5.name, new_name=urllib.quote( 'history5' ) )
+ self.rename_history( self.security.encode_id( history5.id ), history5.name, new_name=urllib.quote( 'history 5' ) )
+ # Current history is hostory5
history5.refresh()
# Due to the limitations of twill ( not functional with the permissions forms ), we're forced
# to do this manually. At this point, we just want to restrict the access permission on history5
@@ -205,81 +260,126 @@
access_action = galaxy.model.Dataset.permitted_actions.DATASET_ACCESS.action
dhp = galaxy.model.DefaultHistoryPermissions( history5, access_action, admin_user_private_role )
dhp.flush()
+ history5.refresh()
+ global history5_default_permissions
+ history5_default_permissions = [ dhp.action for dhp in history5.default_permissions ]
+ # Sort for later comparison
+ history5_default_permissions.sort()
self.upload_file( '1.bed', dbkey='hg18' )
history5_dataset1 = None
for hda in history5.datasets:
if hda.name == '1.bed':
history5_dataset1 = hda.dataset
+ break
assert history5_dataset1 is not None, "Problem retrieving history5_dataset1 from the database"
# The permissions on the dataset should be restricted from sharing with anyone due to the
# inherited history permissions
- restricted = False
- for action in history5_dataset1.actions:
- if action.action == access_action:
- restricted = True
- break
- if not restricted:
- raise AssertionError, "The 'access' permission is not set for history5_dataset1.actions"
- def test_035_sharing_history_by_making_datasets_public( self ):
+ dataset_permissions = [ a.action for a in history5_dataset1.actions ]
+ dataset_permissions.sort()
+ if dataset_permissions != history5_default_permissions:
+ err_msg = "Dataset permissions for history5_dataset1 (%s) were not correctly inherited from history permissions (%s)" \
+ % ( str( dataset_permissions ), str( history5_default_permissions ) )
+ raise AssertionError, err_msg
+ # Make sure when we logout and login, the history default permissions are preserved
+ self.logout()
+ self.login( email=admin_user.email )
+ history5.refresh()
+ current_history_permissions = [ dhp.action for dhp in history5.default_permissions ]
+ current_history_permissions.sort()
+ if current_history_permissions != history5_default_permissions:
+ raise AssertionError, "With logout and login, the history default permissions are not preserved"
+ def test_050_sharing_restricted_history_by_making_datasets_public( self ):
"""Testing sharing a restricted history by making the datasets public"""
- # We're still logged in as admin_user.email
- check_str = 'The following datasets can be shared with %s by updating their permissions' % regular_user1.email
- action_check_str = 'Histories (%s) have been shared with: %s' % ( history5.name, regular_user1.email )
- self.share_history( str( history5.id ), regular_user1.email, check_str, action='public', action_check_str=action_check_str )
- history5_copy1 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
- assert history5_copy1 is not None, "Problem retrieving history5_copy1 from database"
+ # Logged in as admin_user
+ action_check_str = 'The following datasets can be shared with %s by updating their permissions' % regular_user1.email
+ action_check_str_after_submit = 'History (%s) now shared with: 1 users.' % history5.name
+ # Current history is history5
+ self.share_current_history( regular_user1.email,
+ action='public',
+ action_check_str=action_check_str,
+ action_check_str_after_submit=action_check_str_after_submit )
self.logout()
self.login( email=regular_user1.email )
- self.visit_url( "%s/history/list" % self.url )
- self.check_page_for_string( history5_copy1.name )
- # Need to delete history5_copy1 on the history list page for regular_user1
- self.delete_history( id=str( history5_copy1.id ) )
+ # Shared history5 should be in regular_user1's list of shared histories
+ self.view_shared_histories( check_str=history5.name, check_str2=admin_user.email )
+ # Clone restricted history5
+ self.clone_history( self.security.encode_id( history5.id ),
+ check_str1='is now included in your list of stored histories.' )
+ global history5_clone1
+ history5_clone1 = galaxy.model.History.filter( and_( galaxy.model.History.table.c.deleted==False,
+ galaxy.model.History.table.c.user_id==regular_user1.id ) ) \
+ .order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ assert history5_clone1 is not None, "Problem retrieving history5_clone1 from database"
+ # Check list of histories to make sure shared history5 was cloned
+ self.view_stored_active_histories( check_str="Clone of '%s'" % history5.name )
+ # Make sure the dataset is accessible
+ self.switch_history( id=self.security.encode_id( history5_clone1.id ), name=history5_clone1.name )
+ self.check_history_for_string( 'chr1' )
self.logout()
self.login( email=admin_user.email )
- def test_040_sharing_history_by_making_new_sharing_role( self ):
+ def test_055_sharing_restricted_history_by_making_new_sharing_role( self ):
"""Testing sharing a restricted history by associating a new sharing role with protected datasets"""
- self.switch_history( id=str( history5.id ), name=history5.name )
# At this point, history5 should have 1 item, 1.bed, which is public. We'll add another
# item which will be private to admin_user due to the permissions on history5
self.upload_file( '2.bed', dbkey='hg18' )
- check_str = 'The following datasets can be shared with %s with no changes' % regular_user1.email
- check_str2 = 'The following datasets can be shared with %s by updating their permissions' % regular_user1.email
- action_check_str = 'Histories (%s) have been shared with: %s' % ( history5.name, regular_user1.email )
- self.share_history( str( history5.id ),
- regular_user1.email,
- check_str,
- check_str2=check_str2,
- action='private',
- action_check_str=action_check_str )
- history5_copy2 = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
- assert history5_copy2 is not None, "Problem retrieving history5_copy2 from database"
+ check_str_after_submit = 'The following datasets can be shared with %s with no changes' % regular_user2.email
+ check_str_after_submit2 = 'The following datasets can be shared with %s by updating their permissions' % regular_user2.email
+ action_check_str_after_submit = 'History (%s) now shared with: 2 users.' % history5.name
+ self.share_current_history( regular_user2.email,
1
0
Hello,
I have two small questions regarding galaxy server configuration:
1. The Galaxy Cookie.
Sometimes I see a fixed 'galaxysession' cookie,
and other times I see a cookie with the name set in
'universe_wsgi.ini' as the "session_key".
I'm not sure when is each one used.
What I'd like to do is run two galaxy servers on the server - I
don't want to cookies to interfere with one another.
(could it have something with using or not using external
authentication?)
2. setting path of Galaxy's file:
There's the big 'database' directory.
Inside it, there are:
files, import, pbs, job_working_directory, beaker_sessions, tmp,
compiled_templates.
Some of those are changeable from the universe_wsgi.ini,
(e.g. files, tmp, beaker_sessions)
but others are not ( job_working_directory, compiled_templates).
Is there a way to change those path ?
I'd like to have the galaxy source code in a completely read-only
location, and all of the runtime files in "/var/galaxy".
However, one the 'galaxy' base directory is read only, I get several
'permission denied' exceptions when I try to use the galaxy server.
Thanks,
Gordon.
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/b26e2ef8726c
changeset: 2459:b26e2ef8726c
user: Nate Coraor <nate(a)bx.psu.edu>
date: Fri Jun 26 09:22:31 2009 -0400
description:
New packed scripts
5 file(s) affected in this change:
static/scripts/packed/galaxy.panels.js
static/scripts/packed/jquery.event.drag.js
static/scripts/packed/jquery.event.drop.js
static/scripts/packed/jquery.js
static/scripts/packed/trackster.js
diffs (63 lines):
diff -r 842af32c4e73 -r b26e2ef8726c static/scripts/packed/galaxy.panels.js
--- a/static/scripts/packed/galaxy.panels.js Thu Jun 25 15:34:05 2009 -0400
+++ b/static/scripts/packed/galaxy.panels.js Fri Jun 26 09:22:31 2009 -0400
@@ -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()}}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).hover(function(){$(this).addClass("hover")},function(){$(this).removeClass("hover")}).bind("dragstart",function(i){$("#DD-helper").show()}).bind("dragend
",function(i){$("#DD-helper").hide()}).bind("drag",function(i){x=i.offsetX;x=Math.min(400,Math.max(100,x));if(g){$(h).css("left",0);$(e).removeClass("hidden");g=false}d(x)}).bind("dragclickonly",function(i){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;var g=false;var 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(k){$("#DD-helper").show()}).bind("dragend",function(k){$("#DD-helper").hide()}).bind("drag",function(k){x=k.offsetX;w=$(window).width();x=Math.min(w-100,x);x=Math.max(w-400,x);if(j){$(a).css("right",0);$(h).removeClass("hidden");j=false}d(w-x-$(this).outerWidth())}).bind("dragclickonly",function(k){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").fadeOut(function(){$("#overlay").hide()})}function show_modal(f,c,e,d){$(".dialog-box").find(".title").html(f);var a=$(".dialog-box").find(".buttons").html("");if(e){$.each(e,function(b,g){a.append($("<button/>").text(b).click(g));a.append(" ")});a.show()}else{a.hide()}var a=$(".dialog-box").f
ind(".extra_buttons").html("");if(d){$.each(d,function(b,g){a.append($("<button/>").text(b).click(g));a.append(" ")});a.show()}else{a.hide()}if(c=="progress"){c=$("<img src='../images/yui/rel_interstitial_loading.gif')' />")}$(".dialog-box").find(".body").html(c);if(!$(".dialog-box-container").is(":visible")){$("#overlay").show();$(".dialog-box-container").fadeIn()}}$(function(){$("span.tab").each(function(){var a=$(this).children("div.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()}}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).hover(function(){$(this).addClass("hover")},function(){$(this).removeClass("hover")}).bind("dragstart",function(i){$("#DD-helper").show()}).bind("dragend
",function(i){$("#DD-helper").hide()}).bind("drag",function(i){x=i.offsetX;x=Math.min(400,Math.max(100,x));if(g){$(h).css("left",0);$(e).removeClass("hidden");g=false}d(x)}).bind("dragclickonly",function(i){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;var g=false;var 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(k){$("#DD-helper").show()}).bind("dragend",function(k){$("#DD-helper").hide()}).bind("drag",function(k){x=k.offsetX;w=$(window).width();x=Math.min(w-100,x);x=Math.max(w-400,x);if(j){$(a).css("right",0);$(h).removeClass("hidden");j=false}d(w-x-$(this).outerWidth())}).bind("dragclickonly",function(k){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").fadeOut(function(){$("#overlay").hide();$(".dialog-box").find(".body").children().remove()})}function show_modal(f,c,e,d){if(f){$(".dialog-box").find(".title").html(f);$(".dialog-box").find(".unified-panel-header").show()}else{$(".dialog-box").find(".unified-panel-header").hide()}var a=$(".dia
log-box").find(".buttons").html("");if(e){$.each(e,function(b,g){a.append($("<button/>").text(b).click(g));a.append(" ")});a.show()}else{a.hide()}var a=$(".dialog-box").find(".extra_buttons").html("");if(d){$.each(d,function(b,g){a.append($("<button/>").text(b).click(g));a.append(" ")});a.show()}else{a.hide()}if(c=="progress"){c=$("<img src='../images/yui/rel_interstitial_loading.gif')' />")}$(".dialog-box").find(".body").html(c);if(!$(".dialog-box-container").is(":visible")){$("#overlay").show();$(".dialog-box-container").fadeIn()}}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;'><iframe style='margin: 0; padding: 0;' src='"+c.url+"' width='"+d+"' height='"+b+"' scrolling='"+a+"' frameborder='0'></iframe></div>"))}$(function(){$("span.tab").each(function(){var a=$(t
his).children("div.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 842af32c4e73 -r b26e2ef8726c static/scripts/packed/jquery.event.drag.js
--- a/static/scripts/packed/jquery.event.drag.js Thu Jun 25 15:34:05 2009 -0400
+++ b/static/scripts/packed/jquery.event.drag.js Fri Jun 26 09:22:31 2009 -0400
@@ -1,1 +1,5 @@
-(function(h){h.fn.drag=function(k,j,i){if(j){this.bind("dragstart",k)}if(i){this.bind("dragend",i)}return !k?this.trigger("drag"):this.bind("drag",j?j:k)};var d=h.event,b=d.special,f=b.drag={not:":input",distance:0,which:1,setup:function(i){i=h.extend({distance:f.distance,which:f.which,not:f.not},i||{});i.distance=g(i.distance);d.add(this,"mousedown",e,i)},teardown:function(){d.remove(this,"mousedown",e);if(this===f.dragging){f.dragging=f.proxy=null}c(this,true)}};function e(k){var j=this,i,l=k.data||{};if(l.elem){j=k.dragTarget=l.elem;k.dragProxy=f.proxy||j;k.cursorOffsetX=l.pageX-l.left;k.cursorOffsetY=l.pageY-l.top;k.offsetX=k.pageX-k.cursorOffsetX;k.offsetY=k.pageY-k.cursorOffsetY}else{if(f.dragging||(l.which>0&&k.which!=l.which)||h(k.target).is(l.not)){return}}switch(k.type){case"mousedown":h.extend(l,h(j).offset(),{elem:j,target:k.target,pageX:k.pageX,pageY:k.pageY});d.add(document,"mousemove mouseup",e,l);c(j,false);return false;case !f.dragging&&"mousemove":if(g(k.pa
geX-l.pageX)+g(k.pageY-l.pageY)<l.distance){break}k.target=l.target;i=a(k,"dragstart",j);if(i!==false){f.dragging=j;f.proxy=k.dragProxy=h(i||j)[0]}case"mousemove":if(f.dragging){i=a(k,"drag",j);if(b.drop){b.drop.allowed=(i!==false);b.drop.handler(k)}if(i!==false){break}k.type="mouseup"}case"mouseup":d.remove(document,"mousemove mouseup",e);if(f.dragging){if(b.drop){b.drop.handler(k)}a(k,"dragend",j)}else{a(k,"dragclickonly",j)}c(j,true);f.dragging=f.proxy=l.elem=null;break}}function a(l,j,k){l.type=j;var i=d.handle.call(k,l);return i===false?false:i||l.result}function g(i){return Math.pow(i,2)}function c(j,i){if(!j){return}j.unselectable=i?"off":"on";j.onselectstart=function(){return i};if(document.selection&&document.selection.empty){document.selection.empty()}if(j.style){j.style.MozUserSelect=i?"":"none"}}})(jQuery);
+/*
+jquery.event.drag.js ~ v1.4 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
+Liscensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
+*/
+(function(h){h.fn.drag=function(k,j,i){if(j){this.bind("dragstart",k)}if(i){this.bind("dragend",i)}return !k?this.trigger("drag"):this.bind("drag",j?j:k)};var d=h.event,b=d.special,f=b.drag={not:":input",distance:0,which:1,setup:function(i){i=h.extend({distance:f.distance,which:f.which,not:f.not},i||{});i.distance=g(i.distance);d.add(this,"mousedown",e,i)},teardown:function(){d.remove(this,"mousedown",e);if(this===f.dragging){f.dragging=f.proxy=null}c(this,true)}};function e(k){var j=this,i,l=k.data||{};if(l.elem){j=k.dragTarget=l.elem;k.dragProxy=f.proxy||j;k.cursorOffsetX=l.pageX-l.left;k.cursorOffsetY=l.pageY-l.top;k.offsetX=k.pageX-k.cursorOffsetX;k.offsetY=k.pageY-k.cursorOffsetY}else{if(f.dragging||(l.which>0&&k.which!=l.which)||h(k.target).is(l.not)){return}}switch(k.type){case"mousedown":h.extend(l,h(j).offset(),{elem:j,target:k.target,pageX:k.pageX,pageY:k.pageY});d.add(document,"mousemove mouseup",e,l);c(j,false);return false;case !f.dragging&&"mousemove":if(g(k.pa
geX-l.pageX)+g(k.pageY-l.pageY)<l.distance){break}k.target=l.target;i=a(k,"dragstart",j);if(i!==false){f.dragging=j;f.proxy=k.dragProxy=h(i||j)[0]}case"mousemove":if(f.dragging){i=a(k,"drag",j);if(b.drop){b.drop.allowed=(i!==false);b.drop.handler(k)}if(i!==false){break}k.type="mouseup"}case"mouseup":d.remove(document,"mousemove mouseup",e);if(f.dragging){if(b.drop){b.drop.handler(k)}a(k,"dragend",j)}else{a(k,"dragclickonly",j)}c(j,true);f.dragging=f.proxy=l.elem=null;break}}function a(l,j,k){l.type=j;var i=d.handle.call(k,l);return i===false?false:i||l.result}function g(i){return Math.pow(i,2)}function c(j,i){if(!j){return}j.unselectable=i?"off":"on";j.onselectstart=function(){return i};if(document.selection&&document.selection.empty){document.selection.empty()}if(j.style){j.style.MozUserSelect=i?"":"none"}}})(jQuery);
\ No newline at end of file
diff -r 842af32c4e73 -r b26e2ef8726c static/scripts/packed/jquery.event.drop.js
--- a/static/scripts/packed/jquery.event.drop.js Thu Jun 25 15:34:05 2009 -0400
+++ b/static/scripts/packed/jquery.event.drop.js Fri Jun 26 09:22:31 2009 -0400
@@ -1,1 +1,5 @@
-(function(f){f.fn.drop=function(i,h,g){if(h){this.bind("dropstart",i)}if(g){this.bind("dropend",g)}return !i?this.trigger("drop"):this.bind("drop",h?h:i)};f.dropManage=function(g){g=g||{};c.data=[];c.filter=g.filter||"*";c.delay=g.delay||c.delay;c.tolerance=g.tolerance||null;c.mode=g.mode||c.mode||"intersect";return c.$targets.filter(c.filter).each(function(){c.data[c.data.length]=c.locate(this)})};var d=f.event,b=d.special,c=b.drop={delay:100,mode:"intersect",$targets:f([]),data:[],setup:function(){c.$targets=c.$targets.add(this);c.data[c.data.length]=c.locate(this)},teardown:function(){var g=this;c.$targets=c.$targets.not(this);c.data=f.grep(c.data,function(h){return(h.elem!==g)})},handler:function(h){var g=null,i;h.dropTarget=c.dropping||undefined;if(c.data.length&&h.dragTarget){switch(h.type){case"drag":c.event=h;if(!c.timer){c.timer=setTimeout(e,20)}break;case"mouseup":c.timer=clearTimeout(c.timer);if(!c.dropping){break}if(c.allowed){i=a(h,"drop",c.dropping)}g=false;cas
e c.dropping&&"dropstart":g=g===null&&c.allowed?true:false;case c.dropping&&"dropend":a(h,"dropend",c.dropping);c.dropping=null;if(i===false){h.dropTarget=undefined}if(!g){break}case c.allowed&&"dropstart":h.dropTarget=this;c.dropping=a(h,"dropstart",this)!==false?this:null;break}}},locate:function(k){var i=f(k),l=i.offset(),j=i.outerHeight(),g=i.outerWidth();return{elem:k,L:l.left,R:l.left+g,T:l.top,B:l.top+j,W:g,H:j}},contains:function(g,h){return((h[0]||h.L)>=g.L&&(h[0]||h.R)<=g.R&&(h[1]||h.T)>=g.T&&(h[1]||h.B)<=g.B)},modes:{intersect:function(h,g,i){return this.contains(i,[h.pageX,h.pageY])?i:this.modes.overlap.apply(this,arguments)},overlap:function(h,g,i){i.overlap=Math.max(0,Math.min(i.B,g.B)-Math.max(i.T,g.T))*Math.max(0,Math.min(i.R,g.R)-Math.max(i.L,g.L));if(i.overlap>((this.best||{}).overlap||0)){this.best=i}return null},fit:function(h,g,i){return this.contains(i,g)?i:null},middle:function(h,g,i){return this.contains(i,[g.L+g.W/2,g.T+g.H/2])?i:null}}};function a(k
,i,j){k.type=i;try{var g=d.handle.call(j,k)}catch(h){}return g===false?false:g||k.result}function e(){var h=0,g,j,k=[c.event.pageX,c.event.pageY],l=c.locate(c.event.dragProxy);c.tolerance=c.tolerance||c.modes[c.mode];do{if(g=c.data[h]){j=c.tolerance?c.tolerance.call(c,c.event,l,g):c.contains(g,k)?g:null}}while(++h<c.data.length&&!j);c.event.type=(j=j||c.best)?"dropstart":"dropend";if(c.event.type=="dropend"||j.elem!=c.dropping){c.handler.call(j?j.elem:c.dropping,c.event)}if(c.last&&k[0]==c.last.pageX&&k[1]==c.last.pageY){delete c.timer}else{c.timer=setTimeout(e,c.delay)}c.last=c.event;c.best=null}})(jQuery);
+/* jquery.event.drop.js * v1.2
+Copyright (c) 2008-2009, Three Dub Media (http://threedubmedia.com)
+Liscensed under the MIT License (http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt)
+*/
+(function(f){f.fn.drop=function(i,h,g){if(h){this.bind("dropstart",i)}if(g){this.bind("dropend",g)}return !i?this.trigger("drop"):this.bind("drop",h?h:i)};f.dropManage=function(g){g=g||{};c.data=[];c.filter=g.filter||"*";c.delay=g.delay||c.delay;c.tolerance=g.tolerance||null;c.mode=g.mode||c.mode||"intersect";return c.$targets.filter(c.filter).each(function(){c.data[c.data.length]=c.locate(this)})};var d=f.event,b=d.special,c=b.drop={delay:100,mode:"intersect",$targets:f([]),data:[],setup:function(){c.$targets=c.$targets.add(this);c.data[c.data.length]=c.locate(this)},teardown:function(){var g=this;c.$targets=c.$targets.not(this);c.data=f.grep(c.data,function(h){return(h.elem!==g)})},handler:function(h){var g=null,i;h.dropTarget=c.dropping||undefined;if(c.data.length&&h.dragTarget){switch(h.type){case"drag":c.event=h;if(!c.timer){c.timer=setTimeout(e,20)}break;case"mouseup":c.timer=clearTimeout(c.timer);if(!c.dropping){break}if(c.allowed){i=a(h,"drop",c.dropping)}g=false;cas
e c.dropping&&"dropstart":g=g===null&&c.allowed?true:false;case c.dropping&&"dropend":a(h,"dropend",c.dropping);c.dropping=null;if(i===false){h.dropTarget=undefined}if(!g){break}case c.allowed&&"dropstart":h.dropTarget=this;c.dropping=a(h,"dropstart",this)!==false?this:null;break}}},locate:function(k){var i=f(k),l=i.offset(),j=i.outerHeight(),g=i.outerWidth();return{elem:k,L:l.left,R:l.left+g,T:l.top,B:l.top+j,W:g,H:j}},contains:function(g,h){return((h[0]||h.L)>=g.L&&(h[0]||h.R)<=g.R&&(h[1]||h.T)>=g.T&&(h[1]||h.B)<=g.B)},modes:{intersect:function(h,g,i){return this.contains(i,[h.pageX,h.pageY])?i:this.modes.overlap.apply(this,arguments)},overlap:function(h,g,i){i.overlap=Math.max(0,Math.min(i.B,g.B)-Math.max(i.T,g.T))*Math.max(0,Math.min(i.R,g.R)-Math.max(i.L,g.L));if(i.overlap>((this.best||{}).overlap||0)){this.best=i}return null},fit:function(h,g,i){return this.contains(i,g)?i:null},middle:function(h,g,i){return this.contains(i,[g.L+g.W/2,g.T+g.H/2])?i:null}}};function a(k
,i,j){k.type=i;try{var g=d.handle.call(j,k)}catch(h){}return g===false?false:g||k.result}function e(){var h=0,g,j,k=[c.event.pageX,c.event.pageY],l=c.locate(c.event.dragProxy);c.tolerance=c.tolerance||c.modes[c.mode];do{if(g=c.data[h]){j=c.tolerance?c.tolerance.call(c,c.event,l,g):c.contains(g,k)?g:null}}while(++h<c.data.length&&!j);c.event.type=(j=j||c.best)?"dropstart":"dropend";if(c.event.type=="dropend"||j.elem!=c.dropping){c.handler.call(j?j.elem:c.dropping,c.event)}if(c.last&&k[0]==c.last.pageX&&k[1]==c.last.pageY){delete c.timer}else{c.timer=setTimeout(e,c.delay)}c.last=c.event;c.best=null}})(jQuery);
\ No newline at end of file
diff -r 842af32c4e73 -r b26e2ef8726c static/scripts/packed/jquery.js
--- a/static/scripts/packed/jquery.js Thu Jun 25 15:34:05 2009 -0400
+++ b/static/scripts/packed/jquery.js Fri Jun 26 09:22:31 2009 -0400
@@ -1,2 +1,19 @@
+/*
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
+ *
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H
){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(funct
ion(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"fin
d",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},n
ot:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|ch
eckbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(
H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opaci
ty|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E
,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,funct
ion(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:fun
ction(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></tabl
e>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDo
c(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)
]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.
userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertB
efore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),1
0)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,
G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
-(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}
if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if
((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF
_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibli
ng",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;retur
n T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"c
heckbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W)
{var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U
:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasD
uplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElem
entsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if
(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}
}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1)
{T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEv
ent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}
}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp(
"(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX
!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this
.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this
.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=t
his.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",
function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("
script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML=' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attach
Event&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=
="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"scrip
t")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data
.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=
false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}el
se{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.
trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}r
eturn G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[
G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toStri
ng().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o
.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval
(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for
(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(sel
f.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.posit
ion==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hi
dden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}re
turn o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["of
fset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
+/*
+ * Sizzle CSS Selector Engine - v0.9.3
+ * Copyright 2009, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}
if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if
((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF
_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibli
ng",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;retur
n T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"c
heckbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W)
{var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U
:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasD
uplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElem
entsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if
(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}
}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1)
{T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEv
ent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}
}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp(
"(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX
!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this
.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this
.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=t
his.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",
function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("
script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML=' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attach
Event&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=
="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"scrip
t")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data
.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=
false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}el
se{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.
trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}r
eturn G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[
G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toStri
ng().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o
.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval
(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for
(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(sel
f.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.posit
ion==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hi
dden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}re
turn o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["of
fset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
\ No newline at end of file
diff -r 842af32c4e73 -r b26e2ef8726c static/scripts/packed/trackster.js
--- a/static/scripts/packed/trackster.js Thu Jun 25 15:34:05 2009 -0400
+++ b/static/scripts/packed/trackster.js Fri Jun 26 09:22:31 2009 -0400
@@ -1,1 +1,1 @@
-var DENSITY=1000;var View=function(b,c,a,d){this.chr=b;this.length=c;this.low=a;this.high=d};$.extend(View.prototype,{move:function(b,a){this.low=Math.max(0,Math.floor(b));this.high=Math.min(this.length,Math.ceil(a))},zoom_in:function(c){var a=(this.low+this.high)/2;var b=this.high-this.low;var d=b/c/2;this.low=Math.floor(a-d);this.high=Math.ceil(a+d);if(this.high-this.low<1){this.high=this.low+1}},zoom_out:function(c){var a=(this.low+this.high)/2;var b=this.high-this.low;var d=b*c/2;this.low=Math.floor(Math.max(0,a-d));this.high=Math.ceil(Math.min(this.length,a+d))},left:function(b){var a=this.high-this.low;var c=Math.floor(a/b);if(this.low-c<0){this.low=0;this.high=this.low+a}else{this.low-=c;this.high-=c}},right:function(b){var a=this.high-this.low;var c=Math.floor(a/b);if(this.high+c>this.length){this.high=this.length;this.low=this.high-a}else{this.low+=c;this.high+=c}}});var Track=function(b,a,c){this.name=b;this.view=a;this.parent_element=c;this.make_container()};$.ext
end(Track.prototype,{make_container:function(){this.header_div=$("<div class='track-header'>");this.header_div.text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div class='track'></div>");this.container_div.append(this.header_div);this.container_div.append(this.content_div);this.parent_element.append(this.container_div)}});var TiledTrack=function(b,a,c){Track.call(this,b,a,c);this.last_resolution=null;this.last_w_scale=null;this.tile_cache={}};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var k=this.view.low,c=this.view.high,e=c-k;var b=Math.pow(10,Math.ceil(Math.log(e/DENSITY)/Math.log(10)));b=Math.max(b,1);b=Math.min(b,10000);var o=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(o);var m=this.content_div.width(),d=this.content_div.height(),p=m/e,l={},n={};if(this.last_resolution==b&&this.last_w_scale==p){l=this.tile_cache}var g;var a=Math.floor(k/b/DENS
ITY);var j=0;while((a*1000*b)<c){if(a in l){g=l[a];var f=a*DENSITY*b;g.css({left:(f-this.view.low)*p});o.append(g)}else{g=this.draw_tile(b,a,o,p,d)}if(g){n[a]=g;j=Math.max(j,g.height())}a+=1}o.css("height",j);this.last_resolution=b;this.last_w_scale=p;this.tile_cache=n}});var DataCache=function(c,b,a){this.type=c;this.track=b;this.view=a;this.cache=Object()};$.extend(DataCache.prototype,{get:function(d,b){var c=this.cache;if(!(c[d]&&c[d][b])){if(!c[d]){c[d]=Object()}var a=b*DENSITY*d;var e=(b+1)*DENSITY*d;c[d][b]={state:"loading"};$.getJSON("data"+this.type,{chr:this.view.chr,low:a,high:e,dataset_id:this.track.dataset_id},function(f){c[d][b]={state:"loaded",values:f};$(document).trigger("redraw")})}return c[d][b]}});var LineTrack=function(c,b,d,a){Track.call(this,c,b,d);this.container_div.addClass("line-track");this.dataset_id=a;this.cache=new DataCache("",this,b)};$.extend(LineTrack.prototype,TiledTrack.prototype,{make_container:function(){Track.prototype.make_container.cal
l(this);this.content_div.css("height",100)},draw_tile:function(n,q,g,j,h){var s=q*DENSITY*n,d=(q+1)*DENSITY*n,a=DENSITY*n;var k=this.cache.get(n,q);var b;if(k.state=="loading"){b=$("<div class='loading tile'></div>")}else{b=$("<canvas class='tile'></canvas>")}b.css({position:"absolute",top:0,left:(s-this.view.low)*j,width:Math.ceil(a*j),height:100});g.append(b);if(k.state=="loading"){l=false;return null}var f=b;f.get(0).width=f.width();f.get(0).height=f.height();var m=f.get(0).getContext("2d");var l=false;m.beginPath();var t=k.values;for(var o=0;o<t.length-1;o++){var r=t[o][0]-s;var e=t[o][1];var p=t[o+1][0]-s;var c=t[o+1][1];if(isNaN(e)||isNaN(c)){l=false}else{r=r*j;p=p*j;e=h-e*(h);c=h-c*(h);if(l){m.lineTo(r,e,p,c)}else{m.moveTo(r,e,p,c);l=true}}}m.stroke();return b}});var LabelTrack=function(a,b){Track.call(this,null,a,b);this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Ma
th.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+a+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var itemHeight=13,itemPad=3,thinHeight=7,thinOffset=3;var FeatureTrack=function(c,b,d,a){Track.call(this,c,b,d);this.container_div.addClass("feature-track");this.dataset_id=a;this.cache=new DataCache("",this,b)};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{get_data_async:function(){var a=this;$.getJSON("data",{chr:this.view.chr,dataset_id:this.dataset_id},function(b){a.values=b;a.draw()})},draw_tile:function(x,y,h,m,l){var z=y*DENSITY*x,c=(y+1)*DENSITY*x,a=DENSITY*x;var q=this.view,s=q.high-q.low,u=this.content_div.width(),p=[],A=$("<div class='tile' style='position: relative;'></div>");var o=this.cache.get(x,y);
if(o.state=="loading"){return null}var b=o.values;for(var k in b){var v=b[k];var f=v[1],e=v[2],t=v[5];var g=(f-z)*m;var n=(e-z)*m;var w=n-g;var j=g;var d=p.length;for(i in p){if(p[i]<j){d=i;break}}p[d]=Math.ceil(n);var r=$("<div class='feature'></div>").css({position:"absolute",left:g,top:(d*(itemHeight+itemPad)),height:itemHeight,width:Math.max(w,1)});A.append(r)}A.css({position:"absolute",top:0,left:(z-this.view.low)*m,width:Math.ceil(a*m),height:p.length*(itemHeight+itemPad)+itemPad});h.append(A);return A}});var TrackLayout=function(a){this.view=a;this.tracks=[]};$.extend(TrackLayout.prototype,{add:function(a){this.tracks.push(a)},redraw:function(){for(var a in this.tracks){this.tracks[a].draw()}$("#overview-box").css({left:(this.view.low/this.view.length)*$("#overview-viewport").width(),width:Math.max(1,((this.view.high-this.view.low)/this.view.length)*$("#overview-viewport").width())}).show();$("#low").text(this.view.low);$("#high").text(this.view.high)}});
\ No newline at end of file
+var DENSITY=1000;var View=function(b,c,a,d){this.chr=b;this.length=c;this.low=a;this.high=d};$.extend(View.prototype,{move:function(b,a){this.low=Math.max(0,Math.floor(b));this.high=Math.min(this.length,Math.ceil(a))},zoom_in:function(c){var a=(this.low+this.high)/2;var b=this.high-this.low;var d=b/c/2;this.low=Math.floor(a-d);this.high=Math.ceil(a+d);if(this.high-this.low<1){this.high=this.low+1}},zoom_out:function(c){var a=(this.low+this.high)/2;var b=this.high-this.low;var d=b*c/2;this.low=Math.floor(Math.max(0,a-d));this.high=Math.ceil(Math.min(this.length,a+d))},left:function(b){var a=this.high-this.low;var c=Math.floor(a/b);if(this.low-c<0){this.low=0;this.high=this.low+a}else{this.low-=c;this.high-=c}},right:function(b){var a=this.high-this.low;var c=Math.floor(a/b);if(this.high+c>this.length){this.high=this.length;this.low=this.high-a}else{this.low+=c;this.high+=c}}});var Track=function(b,a,c){this.name=b;this.view=a;this.parent_element=c;this.make_container()};$.ext
end(Track.prototype,{make_container:function(){this.header_div=$("<div class='track-header'>");this.header_div.text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div class='track'></div>");this.container_div.append(this.header_div);this.container_div.append(this.content_div);this.parent_element.append(this.container_div)}});var TiledTrack=function(b,a,c){Track.call(this,b,a,c);this.last_resolution=null;this.last_w_scale=null;this.tile_cache={}};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var k=this.view.low,c=this.view.high,e=c-k;var b=Math.pow(10,Math.ceil(Math.log(e/DENSITY)/Math.log(10)));b=Math.max(b,1);b=Math.min(b,100000);var o=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(o);var m=this.content_div.width(),d=this.content_div.height(),p=m/e,l={},n={};if(this.last_resolution==b&&this.last_w_scale==p){l=this.tile_cache}var g;var a=Math.floor(k/b/DEN
SITY);var j=0;while((a*1000*b)<c){if(a in l){g=l[a];var f=a*DENSITY*b;g.css({left:(f-this.view.low)*p});o.append(g)}else{g=this.draw_tile(b,a,o,p,d)}if(g){n[a]=g;j=Math.max(j,g.height())}a+=1}o.css("height",j);this.last_resolution=b;this.last_w_scale=p;this.tile_cache=n}});var DataCache=function(c,b,a){this.type=c;this.track=b;this.view=a;this.cache=Object()};$.extend(DataCache.prototype,{get:function(d,b){var c=this.cache;if(!(c[d]&&c[d][b])){if(!c[d]){c[d]=Object()}var a=b*DENSITY*d;var f=(b+1)*DENSITY*d;c[d][b]={state:"loading"};var e=function(g){return function(){$.getJSON(TRACKSTER_DATA_URL+g.type,{chrom:g.view.chr,low:a,high:f,dataset_id:g.track.dataset_id},function(h){if(h=="pending"){setTimeout(e,5000)}else{c[d][b]={state:"loaded",values:h}}$(document).trigger("redraw")})}}(this);e()}return c[d][b]}});var LineTrack=function(c,b,d,a){Track.call(this,c,b,d);this.container_div.addClass("line-track");this.dataset_id=a;this.cache=new DataCache("",this,b)};$.extend(LineTra
ck.prototype,TiledTrack.prototype,{make_container:function(){Track.prototype.make_container.call(this);this.content_div.css("height",100)},draw_tile:function(n,q,g,j,h){var s=q*DENSITY*n,d=(q+1)*DENSITY*n,a=DENSITY*n;var k=this.cache.get(n,q);var b;if(k.state=="loading"){b=$("<div class='loading tile'></div>")}else{b=$("<canvas class='tile'></canvas>")}b.css({position:"absolute",top:0,left:(s-this.view.low)*j,width:Math.ceil(a*j),height:100});g.append(b);if(k.state=="loading"){l=false;return null}var f=b;f.get(0).width=f.width();f.get(0).height=f.height();var m=f.get(0).getContext("2d");var l=false;m.beginPath();var t=k.values;for(var o=0;o<t.length-1;o++){var r=t[o][0]-s;var e=t[o][1];var p=t[o+1][0]-s;var c=t[o+1][1];console.log(r,e,p,c);if(isNaN(e)||isNaN(c)){l=false}else{r=r*j;p=p*j;e=h-e*(h);c=h-c*(h);if(l){m.lineTo(r,e,p,c)}else{m.moveTo(r,e,p,c);l=true}}}m.stroke();return b}});var LabelTrack=function(a,b){Track.call(this,null,a,b);this.container_div.addClass("label-tr
ack")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+a+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var itemHeight=13,itemPad=3,thinHeight=7,thinOffset=3;var FeatureTrack=function(c,b,d,a){Track.call(this,c,b,d);this.container_div.addClass("feature-track");this.dataset_id=a;this.cache=new DataCache("",this,b)};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{get_data_async:function(){var a=this;$.getJSON("data",{chr:this.view.chr,dataset_id:this.dataset_id},function(b){a.values=b;a.draw()})},draw_tile:function(x,y,h,m,l){var z=y*DENSITY*x,c=(y+1)*DENSITY*x,a=DENSITY*x;var q=this.view,s=q.high-q.low,u=
this.content_div.width(),p=[],A=$("<div class='tile' style='position: relative;'></div>");var o=this.cache.get(x,y);if(o.state=="loading"){h.addClass("loading");return null}else{h.removeClass("loading")}var b=o.values;for(var k in b){var v=b[k];var f=v[1],e=v[2],t=v[5];var g=(f-z)*m;var n=(e-z)*m;var w=n-g;var j=g;var d=p.length;for(i in p){if(p[i]<j){d=i;break}}p[d]=Math.ceil(n);var r=$("<div class='feature'></div>").css({position:"absolute",left:g,top:(d*(itemHeight+itemPad)),height:itemHeight,width:Math.max(w,1)});A.append(r)}A.css({position:"absolute",top:0,left:(z-this.view.low)*m,width:Math.ceil(a*m),height:p.length*(itemHeight+itemPad)+itemPad});h.append(A);return A}});var TrackLayout=function(a){this.view=a;this.tracks=[]};$.extend(TrackLayout.prototype,{add:function(a){this.tracks.push(a)},redraw:function(){for(var a in this.tracks){this.tracks[a].draw()}$("#overview-box").css({left:(this.view.low/this.view.length)*$("#overview-viewport").width(),width:Math.max(1,((
this.view.high-this.view.low)/this.view.length)*$("#overview-viewport").width())}).show();$("#low").text(this.view.low);$("#high").text(this.view.high)}});
\ No newline at end of file
1
0
26 Jun '09
details: http://www.bx.psu.edu/hg/galaxy/rev/842af32c4e73
changeset: 2458:842af32c4e73
user: guru
date: Thu Jun 25 15:34:05 2009 -0400
description:
A tiny bug fix for Sort tool - the delimiter is now defaulted to tab.
2 file(s) affected in this change:
tools/filters/sorter.py
tools/filters/sorter.xml
diffs (21 lines):
diff -r 8dfe971fcc27 -r 842af32c4e73 tools/filters/sorter.py
--- a/tools/filters/sorter.py Thu Jun 25 15:29:24 2009 -0400
+++ b/tools/filters/sorter.py Thu Jun 25 15:34:05 2009 -0400
@@ -103,7 +103,7 @@
# Launch sort.
environ['LC_ALL'] = 'POSIX'
- commandline = "sort -f"+style+"-k "+str(column)+" -o "+outputfile+" "+inputfile+order
+ commandline = "sort -f"+style+"-k "+str(column)+" -t $'\t' -o "+outputfile+" "+inputfile+order
errorcode, stdout = commands.getstatusoutput(commandline)
diff -r 8dfe971fcc27 -r 842af32c4e73 tools/filters/sorter.xml
--- a/tools/filters/sorter.xml Thu Jun 25 15:29:24 2009 -0400
+++ b/tools/filters/sorter.xml Thu Jun 25 15:34:05 2009 -0400
@@ -1,4 +1,4 @@
-<tool id="sort1" name="Sort">
+<tool id="sort1" name="Sort" version="1.0.1">
<description>data in ascending or descending order</description>
<command interpreter="python">sorter.py -i $input -o $out_file1 -cols $column -order $order -style $style</command>
<inputs>
1
0
26 Jun '09
details: http://www.bx.psu.edu/hg/galaxy/rev/8dfe971fcc27
changeset: 2457:8dfe971fcc27
user: guru
date: Thu Jun 25 15:29:24 2009 -0400
description:
Extract genomic DNA tool now uses only alignseq.loc to access sequence files. The twobit.loc file will henceforth be unnecessary, and all sequences from it should be moved to alignseq.loc.
1 file(s) affected in this change:
tools/extract/extract_genomic_dna.py
diffs (92 lines):
diff -r d3abf05d9272 -r 8dfe971fcc27 tools/extract/extract_genomic_dna.py
--- a/tools/extract/extract_genomic_dna.py Fri Jun 19 11:21:13 2009 -0400
+++ b/tools/extract/extract_genomic_dna.py Thu Jun 25 15:29:24 2009 -0400
@@ -4,7 +4,7 @@
-1, --cols=N,N,N,N: Columns for start, end, strand in input file
-d, --dbkey=N: Genome build of input file
-o, --output_format=N: the data type of the output file
- -g, --GALAXY_DATA_INDEX_DIR=N: the directory containing alignseq.loc and twobit.loc
+ -g, --GALAXY_DATA_INDEX_DIR=N: the directory containing alignseq.loc
"""
from galaxy import eggs
import pkg_resources
@@ -29,33 +29,20 @@
reversed_s.reverse()
return "".join( reversed_s )
-def check_nib_file( dbkey, GALAXY_DATA_INDEX_DIR ):
- nib_file = "%s/alignseq.loc" % GALAXY_DATA_INDEX_DIR
- nib_path = ''
- for line in open( nib_file ):
+def check_seq_file( dbkey, GALAXY_DATA_INDEX_DIR ):
+ seq_file = "%s/alignseq.loc" % GALAXY_DATA_INDEX_DIR
+ seq_path = ''
+ for line in open( seq_file ):
line = line.rstrip( '\r\n' )
if line and not line.startswith( "#" ) and line.startswith( 'seq' ):
fields = line.split( '\t' )
if len( fields ) < 3:
continue
if fields[1] == dbkey:
- nib_path = fields[2].strip()
+ seq_path = fields[2].strip()
break
- return nib_path
+ return seq_path
-def check_twobit_file( dbkey, GALAXY_DATA_INDEX_DIR ):
- twobit_file = "%s/twobit.loc" % GALAXY_DATA_INDEX_DIR
- twobit_path = ''
- for line in open( twobit_file ):
- line = line.rstrip( '\r\n' )
- if line and not line.startswith( "#" ):
- fields = line.split( '\t' )
- if len( fields ) < 2:
- continue
- if fields[0] == dbkey:
- twobit_path = fields[1].strip()
- break
- return twobit_path
def __main__():
options, args = doc_optparse.parse( __doc__ )
@@ -72,9 +59,8 @@
strand = None
nibs = {}
twobits = {}
- nib_path = check_nib_file( dbkey, GALAXY_DATA_INDEX_DIR )
- twobit_path = check_twobit_file( dbkey, GALAXY_DATA_INDEX_DIR )
- if not os.path.exists( nib_path ) and not os.path.exists( twobit_path ):
+ seq_path = check_seq_file( dbkey, GALAXY_DATA_INDEX_DIR )
+ if not os.path.exists( seq_path ):
# If this occurs, we need to fix the metadata validator.
stop_err( "No sequences are available for '%s', request them by reporting this error." % dbkey )
@@ -116,11 +102,11 @@
strand = '+'
sequence = ''
- if nib_path and os.path.exists( "%s/%s.nib" % ( nib_path, chrom ) ):
+ if seq_path and os.path.exists( "%s/%s.nib" % ( seq_path, chrom ) ):
if chrom in nibs:
nib = nibs[chrom]
else:
- nibs[chrom] = nib = bx.seq.nib.NibFile( file( "%s/%s.nib" % ( nib_path, chrom ) ) )
+ nibs[chrom] = nib = bx.seq.nib.NibFile( file( "%s/%s.nib" % ( seq_path, chrom ) ) )
try:
sequence = nib.get( start, end-start )
except:
@@ -131,11 +117,11 @@
first_invalid_line = i + 1
invalid_line = line
continue
- elif twobit_path and os.path.exists( twobit_path ):
+ elif seq_path and os.path.exists( seq_path ):
if chrom in twobits:
t = twobits[chrom]
else:
- twobits[chrom] = t = bx.seq.twobit.TwoBitFile( file( twobit_path ) )
+ twobits[chrom] = t = bx.seq.twobit.TwoBitFile( file( seq_path ) )
try:
sequence = t[chrom][start:end]
except:
1
0
Hello,
I recently had to add a tool which generates a variable number of output
files, and I used the HTML / output directory trick as (very concisely)
explained here: http://g2.trac.bx.psu.edu/wiki/AddingToolsFAQ
This is a very cool feature!
It took some time to get right, so I'm attaching a complete working
example for future reference (you can add it to the wiki, if you'd like).
Using an <IFRAME> inside the output HTML, I can even show the multiple
output files in the main HTML page, in a report-like way.
Thanks, and keep up the good work.
Gordon.
1
0
Hello,
Here's a tip for connecting galaxy to mysql on localhost
(if it didn't work for you out of the box):
## Get a clean copy of galaxy
$ hg clone http://www.bx.psu.edu/hg/galaxy galaxy
## Create dedicated MySQL database for galaxy,
## with user 'galaxy' and password '12345'
$ mysql -u root -p
password:
mysql> create database galaxy ;
mysql> use galaxy ;
mysql> grant all on galaxy.* to 'galaxy'@'localhost' identified by '12345';
## Setup Galaxy
$ sh setup.sh
## Change universe_wsgi.ini, to use the new database
database_connection = mysql://galaxy:12345@localhost/galaxy
## Run Galaxy
$ sh run.sh
-----------------
But here's the catch:
On a standard debian system, mysql puts the unix socket file in
/var/run/mysqld/mysqld.sock
However, SqlAlchemy assumes the socket is in /tmp/mysql.sock.
So when I run galaxy, I get the following exception:
OperationalError: (OperationalError) (2002, "Can't connect to local
MySQL server through socket '/tmp/mysql.sock' (2)") None None
Two possible solutions for that:
1. Change the socket file in /etc/mysql/my.cnf (not recommended)
or
2. Add the following incantation to the SQLAlchemy string in
universe_wsgi.ini:
database_connection =
mysql://galaxy:12345@localhost/galaxy?unix_socket=/var/run/mysqld/mysqld.sock
With this change, Galaxy runs fine with mysql on localhost.
-Gordon.
1
0