galaxy-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions

galaxy-dist commit 6497a8cfd12e: More miscellaneous bug fixes in Sample Tracking, all due to the recent UI changes. Changes the name of the test_forms_and_requests.py functional test script to be test_sample_tracking.py to more clearly reeflect what's being tested.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1288723785 14400
# Node ID 6497a8cfd12e477e9c92dd183c4abb81d469ba51
# Parent b7ac22fab1588a565a07acc4972564c2b9198489
More miscellaneous bug fixes in Sample Tracking, all due to the recent UI changes. Changes the name of the test_forms_and_requests.py functional test script to be test_sample_tracking.py to more clearly reeflect what's being tested.
--- a/templates/requests/common/edit_samples.mako
+++ b/templates/requests/common/edit_samples.mako
@@ -12,58 +12,6 @@
<%def name="javascripts()">
${parent.javascripts()}
${common_javascripts()}
- ${local_javascripts()}
-</%def>
-
-<%def name="local_javascripts()">
- <script type="text/javascript">
- // Looks for changes in sample states using an async request. Keeps
- // calling itself (via setTimeout) until all samples are in a terminal
- // state.
- var updater = function ( sample_states ) {
- // Check if there are any items left to track
- var empty = true;
- for ( i in sample_states ) {
- empty = false;
- break;
- }
- if ( ! empty ) {
- setTimeout( function() { updater_callback( sample_states ) }, 1000 );
- }
- };
-
- var updater_callback = function ( sample_states ) {
- // Build request data
- var ids = []
- var states = []
- $.each( sample_states, function ( id, state ) {
- ids.push( id );
- states.push( state );
- });
- // Make ajax call
- $.ajax( {
- type: "POST",
- url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}",
- dataType: "json",
- data: { ids: ids.join( "," ), states: states.join( "," ) },
- success : function ( data ) {
- $.each( data, function( id, val ) {
- // Replace HTML
- var cell1 = $("#sampleState-" + id);
- cell1.html( val.html_state );
- var cell2 = $("#sampleDatasets-" + id);
- cell2.html( val.html_datasets );
- sample_states[ parseInt( id ) ] = val.state;
- });
- updater( sample_states );
- },
- error: function() {
- // Just retry, like the old method, should try to be smarter
- updater( sample_states );
- }
- });
- };
- </script></%def><%
@@ -73,7 +21,8 @@
is_complete = request.is_complete
is_unsubmitted = request.is_unsubmitted
can_add_samples = is_unsubmitted
- can_edit_or_delete_samples = request.samples and not is_complete
+ can_delete_samples = request.samples and not is_complete
+ can_edit_samples = request.samples and ( is_admin or not is_complete )
can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
can_reject_or_transfer = is_admin and request.is_submitted
can_submit = request.samples and is_unsubmitted
@@ -82,7 +31,7 @@
<br/><br/><ul class="manage-table-actions">
- %if not editing_samples and can_edit_or_delete_samples:
+ %if not editing_samples and can_edit_samples:
<li><a class="action-button" href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples='True' )}">Edit samples</a></li>
%endif
%if editing_samples and can_add_samples:
@@ -131,13 +80,13 @@
grid_header = '<h3>Add Samples to Request "%s"</h3>' % request.name
%>
${render_samples_grid( cntrller, request, current_samples, action='edit_samples', editing_samples=editing_samples, encoded_selected_sample_ids=encoded_selected_sample_ids, render_buttons=False, grid_header=grid_header )}
- %if editing_samples and len( sample_operation_select_field.options ) > 1 and not ( is_unsubmitted or is_complete ):
+ %if editing_samples and len( sample_operation_select_field.options ) > 1 and not is_unsubmitted:
<div class="form-row" style="background-color:#FAFAFA;">
For selected samples:
${sample_operation_select_field.get_html()}
</div><% sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True ) %>
- %if sample_operation_selected_value != 'none' and encoded_selected_sample_ids:
+ %if ( is_admin or not is_complete ) and sample_operation_selected_value != 'none' and encoded_selected_sample_ids:
<div class="form-row" style="background-color:#FAFAFA;">
%if sample_operation_selected_value == trans.model.Sample.bulk_operations.CHANGE_STATE:
## sample_operation_selected_value == 'Change state'
@@ -150,7 +99,7 @@
Optional
</div></div>
- %elif sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY:
+ %elif not is_complete and sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY:
<% libraries_selected_value = libraries_select_field.get_selected( return_value=True ) %><div class="form-row"><label>Select data library:</label>
@@ -210,12 +159,12 @@
Click the <b>Save</b> button when you have finished editing the samples
</div>
%endif
- ##%if request.samples and request.is_submitted:
- ## <script type="text/javascript">
- ## // Updater
- ## updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
- ## </script>
- ##%endif
+ %if request.samples and request.is_submitted:
+ <script type="text/javascript">
+ // Updater
+ updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
+ </script>
+ %endif
</form></div>
%if is_unsubmitted and not editing_samples:
--- a/test/functional/test_forms_and_requests.py
+++ /dev/null
@@ -1,436 +0,0 @@
-import galaxy.model
-from galaxy.model.orm import *
-from base.twilltestcase import *
-from base.test_db_util import *
-
-sample_states = [ ( 'New', 'Sample entered into the system' ),
- ( 'Received', 'Sample tube received' ),
- ( 'Done', 'Sequence run complete' ) ]
-address_dict = dict( short_desc="Office",
- name="James Bond",
- institution="MI6" ,
- address="MI6 Headquarters",
- city="London",
- state="London",
- postal_code="007",
- country="United Kingdom",
- phone="007-007-0007" )
-
-class TestFormsAndRequests( TwillTestCase ):
- def test_000_initiate_users( self ):
- """Ensuring all required user accounts exist"""
- self.logout()
- self.login( email='test1(a)bx.psu.edu', username='regular-user1' )
- global regular_user1
- regular_user1 = get_user( 'test1(a)bx.psu.edu' )
- assert regular_user1 is not None, 'Problem retrieving user with email "test1(a)bx.psu.edu" from the database'
- global regular_user1_private_role
- regular_user1_private_role = get_private_role( regular_user1 )
- self.logout()
- self.login( email='test2(a)bx.psu.edu', username='regular-user2' )
- global regular_user2
- regular_user2 = get_user( 'test2(a)bx.psu.edu' )
- assert regular_user2 is not None, 'Problem retrieving user with email "test2(a)bx.psu.edu" from the database'
- global regular_user2_private_role
- regular_user2_private_role = get_private_role( regular_user2 )
- self.logout()
- self.login( email='test3(a)bx.psu.edu', username='regular-user3' )
- global regular_user3
- regular_user3 = get_user( 'test3(a)bx.psu.edu' )
- assert regular_user3 is not None, 'Problem retrieving user with email "test3(a)bx.psu.edu" from the database'
- global regular_user3_private_role
- regular_user3_private_role = get_private_role( regular_user3 )
- self.logout()
- self.login( email='test(a)bx.psu.edu', username='admin-user' )
- global admin_user
- admin_user = get_user( 'test(a)bx.psu.edu' )
- assert admin_user is not None, 'Problem retrieving user with email "test(a)bx.psu.edu" from the database'
- global admin_user_private_role
- admin_user_private_role = get_private_role( admin_user )
- def test_005_create_required_groups_and_roles( self ):
- """Testing creating all required groups and roles for this script"""
- # Logged in as admin_user
- # Create role_one
- name = 'Role One'
- description = "This is Role One's description"
- user_ids = [ str( admin_user.id ), str( regular_user1.id ), str( regular_user3.id ) ]
- self.create_role( name=name,
- description=description,
- in_user_ids=user_ids,
- in_group_ids=[],
- create_group_for_role='no',
- private_role=admin_user.email )
- # Get the role object for later tests
- global role_one
- role_one = get_role_by_name( name )
- # Create group_one
- name = 'Group One'
- self.create_group( name=name, in_user_ids=[ str( regular_user1.id ) ], in_role_ids=[ str( role_one.id ) ] )
- # Get the group object for later tests
- global group_one
- group_one = get_group_by_name( name )
- assert group_one is not None, 'Problem retrieving group named "Group One" from the database'
- # NOTE: To get this to work with twill, all select lists on the ~/admin/role page must contain at least
- # 1 option value or twill throws an exception, which is: ParseError: OPTION outside of SELECT
- # Due to this bug in twill, we create the role, we bypass the page and visit the URL in the
- # associate_users_and_groups_with_role() method.
- #
- #create role_two
- name = 'Role Two'
- description = 'This is Role Two'
- user_ids = [ str( admin_user.id ) ]
- group_ids = [ str( group_one.id ) ]
- private_role = admin_user.email
- self.create_role( name=name,
- description=description,
- in_user_ids=user_ids,
- in_group_ids=group_ids,
- private_role=private_role )
- # Get the role object for later tests
- global role_two
- role_two = get_role_by_name( name )
- assert role_two is not None, 'Problem retrieving role named "Role Two" from the database'
- def test_010_create_request_form( self ):
- """Testing creating a request form definition, editing the name and description and adding fields"""
- # Logged in as admin_user
- # Create a form definition
- tmp_name = "Temp form"
- tmp_desc = "Temp form description"
- form_type = galaxy.model.FormDefinition.types.REQUEST
- self.create_form( name=tmp_name,
- desc=tmp_desc,
- form_type=form_type,
- num_fields=0,
- strings_displayed=[ 'Create a new form definition' ],
- strings_displayed_after_submit=[ tmp_name, tmp_desc, form_type ] )
- tmp_form = get_form( tmp_name )
- # Edit the name and description of the form definition, and add 3 fields.
- new_name = "Request Form"
- new_desc = "Request Form description"
- global test_field_name1
- test_field_name1 = 'Test field name one'
- global test_field_name2
- test_field_name2 = 'Test field name two'
- global test_field_name3
- test_field_name3 = 'Test field name three'
- field_dicts = [ dict( name=test_field_name1,
- desc='Test field description one',
- type='SelectField',
- required='optional',
- selectlist=[ 'option1', 'option2' ] ),
- dict( name=test_field_name2,
- desc='Test field description two',
- type='AddressField',
- required='optional' ),
- dict( name=test_field_name3,
- desc='Test field description three',
- type='TextField',
- required='required' ) ]
- self.edit_form( id=self.security.encode_id( tmp_form.current.id ),
- new_form_name=new_name,
- new_form_desc=new_desc,
- field_dicts=field_dicts,
- field_index=len( tmp_form.fields ),
- strings_displayed=[ 'Edit form definition "%s"' % tmp_name ],
- strings_displayed_after_submit=[ "The form '%s' has been updated with the changes." % new_name ] )
- # Get the form_definition object for later tests
- global form_one
- form_one = get_form( new_name )
- assert form_one is not None, 'Problem retrieving form named "%s" from the database' % new_name
- assert len( form_one.fields ) == len( tmp_form.fields ) + len( field_dicts )
- def test_015_create_sample_form( self ):
- """Testing creating sample form definition"""
- name = "Sample Form"
- desc = "This is Form Two's description"
- form_type = galaxy.model.FormDefinition.types.SAMPLE
- form_layout_name = 'Layout Grid One'
- self.create_form( name=name,
- desc=desc,
- form_type=form_type,
- form_layout_name=form_layout_name,
- strings_displayed=[ 'Create a new form definition' ],
- strings_displayed_after_submit=[ "The form '%s' has been updated with the changes." % name ] )
- global form_two
- form_two = get_form( name )
- assert form_two is not None, "Error retrieving form %s from db" % name
- def test_020_create_request_type( self ):
- """Testing creating a request_type"""
- request_form = get_form( form_one.name )
- sample_form = get_form( form_two.name )
- name = 'Test Requestype'
- self.create_request_type( name,
- "test sequencer configuration",
- self.security.encode_id( request_form.id ),
- self.security.encode_id( sample_form.id ),
- sample_states,
- strings_displayed=[ 'Create a new sequencer configuration' ],
- strings_displayed_after_submit=[ "Sequencer configuration (%s) has been created" % name ] )
- global request_type1
- request_type1 = get_request_type_by_name( name )
- assert request_type1 is not None, 'Problem retrieving sequencer configuration named "%s" from the database' % name
- # Set permissions
- permissions_in = [ k for k, v in galaxy.model.RequestType.permitted_actions.items() ]
- permissions_out = []
- # Role one members are: admin_user, regular_user1, regular_user3. Each of these users will be permitted for
- # REQUEST_TYPE_ACCESS on this request_type
- self.request_type_permissions( self.security.encode_id( request_type1.id ),
- request_type1.name,
- str( role_one.id ),
- permissions_in,
- permissions_out )
- # Make sure the request_type1 is not accessible by regular_user2 since regular_user2 does not have Role1.
- self.logout()
- self.login( email=regular_user2.email )
- self.visit_url( '%s/requests_common/create_request?cntrller=requests&request_type=True' % self.url )
- try:
- self.check_page_for_string( 'There are no sequencer configurations created for a new request.' )
- raise AssertionError, 'The request_type %s is accessible by %s when it should be restricted' % ( request_type1.name, regular_user2.email )
- except:
- pass
- self.logout()
- self.login( email=admin_user.email )
- def test_025_create_request( self ):
- """Testing creating a sequence run request"""
- # logged in as admin_user
- # Create a user_address
- self.logout()
- self.login( email=regular_user1.email )
- self.add_user_address( regular_user1.id, address_dict )
- global user_address1
- user_address1 = get_user_address( regular_user1, address_dict[ 'short_desc' ] )
- # Set field values - the tuples in the field_values list include the field_value, and True if refresh_on_change
- # is required for that field.
- field_value_tuples = [ ( 'option1', False ), ( str( user_address1.id ), True ), ( 'field three value', False ) ]
- # Create the request
- name = 'Request One'
- desc = 'Request One Description'
- self.create_request( cntrller='requests',
- request_type_id=self.security.encode_id( request_type1.id ),
- name=name,
- desc=desc,
- field_value_tuples=field_value_tuples,
- strings_displayed=[ 'Create a new sequencing request',
- test_field_name1,
- test_field_name2,
- test_field_name3 ],
- strings_displayed_after_submit=[ name, desc ] )
- global request_one
- request_one = get_request_by_name( name )
- # Make sure the request's state is now set to NEW
- assert request_one.state is not request_one.states.NEW, "The state of the request '%s' should be set to '%s'" \
- % ( request_one.name, request_one.states.NEW )
- # Sample fields - the tuple represents a sample name and a list of sample form field values
- sample_value_tuples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ),
- ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ]
- strings_displayed_after_submit = [ 'Unsubmitted' ]
- for sample_name, field_values in sample_value_tuples:
- strings_displayed_after_submit.append( sample_name )
- # Add samples to the request
- self.add_samples( cntrller='requests',
- request_id=self.security.encode_id( request_one.id ),
- request_name=request_one.name,
- sample_value_tuples=sample_value_tuples,
- strings_displayed=[ 'There are no samples.' ],
- strings_displayed_after_submit=strings_displayed_after_submit )
- def test_030_edit_basic_request_info( self ):
- """Testing editing the basic information of a sequence run request"""
- # logged in as regular_user1
- fields = [ 'option2', str( user_address1.id ), 'field three value (edited)' ]
- new_name=request_one.name + ' (Renamed)'
- new_desc=request_one.desc + ' (Re-described)'
- self.edit_basic_request_info( request_id=self.security.encode_id( request_one.id ),
- cntrller='requests',
- name=request_one.name,
- new_name=new_name,
- new_desc=new_desc,
- new_fields=fields,
- strings_displayed=[ 'Edit sequencing request "%s"' % request_one.name ],
- strings_displayed_after_submit=[ new_name, new_desc ] )
- refresh( request_one )
- # check if the request is showing in the 'new' filter
- self.check_request_grid( cntrller='requests',
- state=request_one.states.NEW,
- strings_displayed=[ request_one.name ] )
- def test_035_submit_request( self ):
- """Testing editing a sequence run request"""
- # logged in as regular_user1
- self.submit_request( cntrller='requests',
- request_id=self.security.encode_id( request_one.id ),
- request_name=request_one.name,
- strings_displayed_after_submit=[ 'The request has been submitted.' ] )
- refresh( request_one )
- # Make sure the request is showing in the 'submitted' filter
- self.check_request_grid( cntrller='requests',
- state=request_one.states.SUBMITTED,
- strings_displayed=[ request_one.name ] )
- # Make sure the request's state is now set to 'submitted'
- assert request_one.state is not request_one.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" \
- % ( request_one.name, request_one.states.SUBMITTED )
- def test_040_request_lifecycle( self ):
- """Testing request life-cycle as it goes through all the states"""
- # logged in as regular_user1
- self.logout()
- self.login( email=admin_user.email )
- self.check_request_grid( cntrller='requests_admin',
- state=request_one.states.SUBMITTED,
- strings_displayed=[ request_one.name ] )
- self.visit_url( "%s/requests_common/view_request?cntrller=requests&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ) )
- # TODO: add some string for checking on the page above...
- # Set bar codes for the samples
- bar_codes = [ '1234567890', '0987654321' ]
- strings_displayed_after_submit=[ 'Changes made to the samples have been saved.' ]
- for bar_code in bar_codes:
- strings_displayed_after_submit.append( bar_code )
- self.add_bar_codes( request_id=self.security.encode_id( request_one.id ),
- request_name=request_one.name,
- bar_codes=bar_codes,
- samples=request_one.samples,
- strings_displayed_after_submit=strings_displayed_after_submit )
- # Change the states of all the samples of this request to ultimately be COMPLETE
- self.change_sample_state( request_id=self.security.encode_id( request_one.id ),
- request_name=request_one.name,
- sample_names=[ sample.name for sample in request_one.samples ],
- sample_ids=[ sample.id for sample in request_one.samples ],
- new_sample_state_id=request_type1.states[1].id,
- new_state_name=request_type1.states[1].name )
- self.change_sample_state( request_id=self.security.encode_id( request_one.id ),
- request_name=request_one.name,
- sample_names=[ sample.name for sample in request_one.samples ],
- sample_ids=[ sample.id for sample in request_one.samples ],
- new_sample_state_id=request_type1.states[2].id,
- new_state_name=request_type1.states[2].name )
- refresh( request_one )
- self.logout()
- self.login( email=regular_user1.email )
- # check if the request's state is now set to 'complete'
- self.check_request_grid( cntrller='requests',
- state='Complete',
- strings_displayed=[ request_one.name ] )
- assert request_one.state is not request_one.states.COMPLETE, "The state of the request '%s' should be set to '%s'" \
- % ( request_one.name, request_one.states.COMPLETE )
-
- def test_045_admin_create_request_on_behalf_of_regular_user( self ):
- """Testing creating and submitting a request as an admin on behalf of a regular user"""
- # Logged in as regular_user1
- self.logout()
- self.login( email=admin_user.email )
- # Create the request
- name = "RequestTwo"
- desc = 'Request Two Description'
- # Set field values - the tuples in the field_values list include the field_value, and True if refresh_on_change
- # is required for that field.
- field_value_tuples = [ ( 'option2', False ), ( str( user_address1.id ), True ), ( 'field_2_value', False ) ]
- self.create_request( cntrller='requests_admin',
- request_type_id=self.security.encode_id( request_type1.id ),
- other_users_id=self.security.encode_id( regular_user1.id ),
- name=name,
- desc=desc,
- field_value_tuples=field_value_tuples,
- strings_displayed=[ 'Create a new sequencing request',
- test_field_name1,
- test_field_name2,
- test_field_name3 ],
- strings_displayed_after_submit=[ "The request has been created." ] )
- global request_two
- request_two = get_request_by_name( name )
- # Make sure the request is showing in the 'new' filter
- self.check_request_grid( cntrller='requests_admin',
- state=request_two.states.NEW,
- strings_displayed=[ request_two.name ] )
- # Make sure the request's state is now set to 'new'
- assert request_two.state is not request_two.states.NEW, "The state of the request '%s' should be set to '%s'" \
- % ( request_two.name, request_two.states.NEW )
- # Sample fields - the tuple represents a sample name and a list of sample form field values
- sample_value_tuples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ),
- ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ]
- strings_displayed_after_submit = [ 'Unsubmitted' ]
- for sample_name, field_values in sample_value_tuples:
- strings_displayed_after_submit.append( sample_name )
- # Add samples to the request
- self.add_samples( cntrller='requests_admin',
- request_id=self.security.encode_id( request_two.id ),
- request_name=request_two.name,
- sample_value_tuples=sample_value_tuples,
- strings_displayed=[ 'There are no samples.' ],
- strings_displayed_after_submit=strings_displayed_after_submit )
- # Submit the request
- self.submit_request( cntrller='requests_admin',
- request_id=self.security.encode_id( request_two.id ),
- request_name=request_two.name,
- strings_displayed_after_submit=[ 'The request has been submitted.' ] )
- refresh( request_two )
- # Make sure the request is showing in the 'submitted' filter
- self.check_request_grid( cntrller='requests_admin',
- state=request_two.states.SUBMITTED,
- strings_displayed=[ request_two.name ] )
- # Make sure the request's state is now set to 'submitted'
- assert request_two.state is not request_two.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" \
- % ( request_two.name, request_two.states.SUBMITTED )
- # Make sure both requests are showing in the 'All' filter
- self.check_request_grid( cntrller='requests_admin',
- state='All',
- strings_displayed=[ request_one.name, request_two.name ] )
- def test_050_reject_request( self ):
- """Testing rejecting a request"""
- # Logged in as admin_user
- self.reject_request( request_id=self.security.encode_id( request_two.id ),
- request_name=request_two.name,
- comment="Rejection test comment",
- strings_displayed=[ 'Reject Sequencing Request "%s"' % request_two.name ],
- strings_displayed_after_submit=[ 'Request (%s) has been rejected.' % request_two.name ] )
- refresh( request_two )
- # Make sure the request is showing in the 'rejected' filter
- self.check_request_grid( cntrller='requests_admin',
- state=request_two.states.REJECTED,
- strings_displayed=[ request_two.name ] )
- # Make sure the request's state is now set to REJECTED
- assert request_two.state is not request_two.states.REJECTED, "The state of the request '%s' should be set to '%s'" \
- % ( request_two.name, request_two.states.REJECTED )
- def test_055_reset_data_for_later_test_runs( self ):
- """Reseting data to enable later test runs to pass"""
- """
- # Logged in as admin_user
- ##################
- # Delete request_type permissions
- ##################
- for request_type in [ request_type1 ]:
- delete_request_type_permissions( request_type.id )
- ##################
- # Mark all request_types deleted
- ##################
- for request_type in [ request_type1 ]:
- mark_obj_deleted( request_type )
- ##################
- # Mark all requests deleted
- ##################
- for request in [ request_one, request_two ]:
- mark_obj_deleted( request )
- ##################
- # Mark all forms deleted
- ##################
- for form in [ form_one, form_two ]:
- self.mark_form_deleted( self.security.encode_id( form.current.id ) )
- ##################
- # Mark all user_addresses deleted
- ##################
- for user_address in [ user_address1 ]:
- mark_obj_deleted( user_address )
- ##################
- # Delete all non-private roles
- ##################
- for role in [ role_one, role_two ]:
- self.mark_role_deleted( self.security.encode_id( role.id ), role.name )
- self.purge_role( self.security.encode_id( role.id ), role.name )
- # Manually delete the role from the database
- refresh( role )
- delete( role )
- ##################
- # Delete all groups
- ##################
- for group in [ group_one ]:
- self.mark_group_deleted( self.security.encode_id( group.id ), group.name )
- self.purge_group( self.security.encode_id( group.id ), group.name )
- # Manually delete the group from the database
- refresh( group )
- delete( group )
- """
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -1047,10 +1047,15 @@ class RequestsCommon( BaseController, Us
action='update_request_state',
request_id=trans.security.encode_id( request.id ) ) )
elif sample_operation == 'Select data library and folder':
+ # TODO: fix the code so that the sample_operation_select_field does not use
+ # sample_0_library_id as it's name. it should use something like sample_operation_library_id
+ # and sample_operation-folder_id because the name sample_0_library_id should belong to the
+ # first sample since all other form field values are named like this. The library and folder
+ # are skewed to be named +1 resulting in the forced use of id_index everywhere...
library_id = params.get( 'sample_0_library_id', 'none' )
folder_id = params.get( 'sample_0_folder_id', 'none' )
library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
- self.__update_samples( trans, request, samples, **kwd )
+ self.__update_samples( trans, cntrller, request, samples, **kwd )
# Samples will not have an associated SampleState until the request is submitted, at which
# time all samples of the request will be set to the first SampleState configured for the
# request's RequestType defined by the admin.
@@ -1092,7 +1097,7 @@ class RequestsCommon( BaseController, Us
editing_samples=editing_samples,
status=status,
message=message ) )
- def __update_samples( self, trans, request, sample_widgets, **kwd ):
+ def __update_samples( self, trans, cntrller, request, sample_widgets, **kwd ):
# Determine if the values in kwd require updating the request's samples. The list of
# sample_widgets must have the same number of objects as request.samples, but some of
# the objects can be None. Those that are not None correspond to samples selected by
--- /dev/null
+++ b/test/functional/test_sample_tracking.py
@@ -0,0 +1,434 @@
+import galaxy.model
+from galaxy.model.orm import *
+from base.twilltestcase import *
+from base.test_db_util import *
+
+sample_states = [ ( 'New', 'Sample entered into the system' ),
+ ( 'Received', 'Sample tube received' ),
+ ( 'Done', 'Sequence run complete' ) ]
+address_dict = dict( short_desc="Office",
+ name="James Bond",
+ institution="MI6" ,
+ address="MI6 Headquarters",
+ city="London",
+ state="London",
+ postal_code="007",
+ country="United Kingdom",
+ phone="007-007-0007" )
+
+class TestFormsAndRequests( TwillTestCase ):
+ def test_000_initiate_users( self ):
+ """Ensuring all required user accounts exist"""
+ self.logout()
+ self.login( email='test1(a)bx.psu.edu', username='regular-user1' )
+ global regular_user1
+ regular_user1 = get_user( 'test1(a)bx.psu.edu' )
+ assert regular_user1 is not None, 'Problem retrieving user with email "test1(a)bx.psu.edu" from the database'
+ global regular_user1_private_role
+ regular_user1_private_role = get_private_role( regular_user1 )
+ self.logout()
+ self.login( email='test2(a)bx.psu.edu', username='regular-user2' )
+ global regular_user2
+ regular_user2 = get_user( 'test2(a)bx.psu.edu' )
+ assert regular_user2 is not None, 'Problem retrieving user with email "test2(a)bx.psu.edu" from the database'
+ global regular_user2_private_role
+ regular_user2_private_role = get_private_role( regular_user2 )
+ self.logout()
+ self.login( email='test3(a)bx.psu.edu', username='regular-user3' )
+ global regular_user3
+ regular_user3 = get_user( 'test3(a)bx.psu.edu' )
+ assert regular_user3 is not None, 'Problem retrieving user with email "test3(a)bx.psu.edu" from the database'
+ global regular_user3_private_role
+ regular_user3_private_role = get_private_role( regular_user3 )
+ self.logout()
+ self.login( email='test(a)bx.psu.edu', username='admin-user' )
+ global admin_user
+ admin_user = get_user( 'test(a)bx.psu.edu' )
+ assert admin_user is not None, 'Problem retrieving user with email "test(a)bx.psu.edu" from the database'
+ global admin_user_private_role
+ admin_user_private_role = get_private_role( admin_user )
+ def test_005_create_required_groups_and_roles( self ):
+ """Testing creating all required groups and roles for this script"""
+ # Logged in as admin_user
+ # Create role_one
+ name = 'Role One'
+ description = "This is Role One's description"
+ user_ids = [ str( admin_user.id ), str( regular_user1.id ), str( regular_user3.id ) ]
+ self.create_role( name=name,
+ description=description,
+ in_user_ids=user_ids,
+ in_group_ids=[],
+ create_group_for_role='no',
+ private_role=admin_user.email )
+ # Get the role object for later tests
+ global role_one
+ role_one = get_role_by_name( name )
+ # Create group_one
+ name = 'Group One'
+ self.create_group( name=name, in_user_ids=[ str( regular_user1.id ) ], in_role_ids=[ str( role_one.id ) ] )
+ # Get the group object for later tests
+ global group_one
+ group_one = get_group_by_name( name )
+ assert group_one is not None, 'Problem retrieving group named "Group One" from the database'
+ # NOTE: To get this to work with twill, all select lists on the ~/admin/role page must contain at least
+ # 1 option value or twill throws an exception, which is: ParseError: OPTION outside of SELECT
+ # Due to this bug in twill, we create the role, we bypass the page and visit the URL in the
+ # associate_users_and_groups_with_role() method.
+ #
+ #create role_two
+ name = 'Role Two'
+ description = 'This is Role Two'
+ user_ids = [ str( admin_user.id ) ]
+ group_ids = [ str( group_one.id ) ]
+ private_role = admin_user.email
+ self.create_role( name=name,
+ description=description,
+ in_user_ids=user_ids,
+ in_group_ids=group_ids,
+ private_role=private_role )
+ # Get the role object for later tests
+ global role_two
+ role_two = get_role_by_name( name )
+ assert role_two is not None, 'Problem retrieving role named "Role Two" from the database'
+ def test_010_create_request_form( self ):
+ """Testing creating a request form definition, editing the name and description and adding fields"""
+ # Logged in as admin_user
+ # Create a form definition
+ tmp_name = "Temp form"
+ tmp_desc = "Temp form description"
+ form_type = galaxy.model.FormDefinition.types.REQUEST
+ self.create_form( name=tmp_name,
+ desc=tmp_desc,
+ form_type=form_type,
+ num_fields=0,
+ strings_displayed=[ 'Create a new form definition' ],
+ strings_displayed_after_submit=[ tmp_name, tmp_desc, form_type ] )
+ tmp_form = get_form( tmp_name )
+ # Edit the name and description of the form definition, and add 3 fields.
+ new_name = "Request Form"
+ new_desc = "Request Form description"
+ global test_field_name1
+ test_field_name1 = 'Test field name one'
+ global test_field_name2
+ test_field_name2 = 'Test field name two'
+ global test_field_name3
+ test_field_name3 = 'Test field name three'
+ field_dicts = [ dict( name=test_field_name1,
+ desc='Test field description one',
+ type='SelectField',
+ required='optional',
+ selectlist=[ 'option1', 'option2' ] ),
+ dict( name=test_field_name2,
+ desc='Test field description two',
+ type='AddressField',
+ required='optional' ),
+ dict( name=test_field_name3,
+ desc='Test field description three',
+ type='TextField',
+ required='required' ) ]
+ self.edit_form( id=self.security.encode_id( tmp_form.current.id ),
+ new_form_name=new_name,
+ new_form_desc=new_desc,
+ field_dicts=field_dicts,
+ field_index=len( tmp_form.fields ),
+ strings_displayed=[ 'Edit form definition "%s"' % tmp_name ],
+ strings_displayed_after_submit=[ "The form '%s' has been updated with the changes." % new_name ] )
+ # Get the form_definition object for later tests
+ global form_one
+ form_one = get_form( new_name )
+ assert form_one is not None, 'Problem retrieving form named "%s" from the database' % new_name
+ assert len( form_one.fields ) == len( tmp_form.fields ) + len( field_dicts )
+ def test_015_create_sample_form( self ):
+ """Testing creating sample form definition"""
+ name = "Sample Form"
+ desc = "This is Form Two's description"
+ form_type = galaxy.model.FormDefinition.types.SAMPLE
+ form_layout_name = 'Layout Grid One'
+ self.create_form( name=name,
+ desc=desc,
+ form_type=form_type,
+ form_layout_name=form_layout_name,
+ strings_displayed=[ 'Create a new form definition' ],
+ strings_displayed_after_submit=[ "The form '%s' has been updated with the changes." % name ] )
+ global form_two
+ form_two = get_form( name )
+ assert form_two is not None, "Error retrieving form %s from db" % name
+ def test_020_create_request_type( self ):
+ """Testing creating a request_type"""
+ request_form = get_form( form_one.name )
+ sample_form = get_form( form_two.name )
+ name = 'Test Requestype'
+ self.create_request_type( name,
+ "test sequencer configuration",
+ self.security.encode_id( request_form.id ),
+ self.security.encode_id( sample_form.id ),
+ sample_states,
+ strings_displayed=[ 'Create a new sequencer configuration' ],
+ strings_displayed_after_submit=[ "Sequencer configuration (%s) has been created" % name ] )
+ global request_type1
+ request_type1 = get_request_type_by_name( name )
+ assert request_type1 is not None, 'Problem retrieving sequencer configuration named "%s" from the database' % name
+ # Set permissions
+ permissions_in = [ k for k, v in galaxy.model.RequestType.permitted_actions.items() ]
+ permissions_out = []
+ # Role one members are: admin_user, regular_user1, regular_user3. Each of these users will be permitted for
+ # REQUEST_TYPE_ACCESS on this request_type
+ self.request_type_permissions( self.security.encode_id( request_type1.id ),
+ request_type1.name,
+ str( role_one.id ),
+ permissions_in,
+ permissions_out )
+ # Make sure the request_type1 is not accessible by regular_user2 since regular_user2 does not have Role1.
+ self.logout()
+ self.login( email=regular_user2.email )
+ self.visit_url( '%s/requests_common/create_request?cntrller=requests&request_type=True' % self.url )
+ try:
+ self.check_page_for_string( 'There are no sequencer configurations created for a new request.' )
+ raise AssertionError, 'The request_type %s is accessible by %s when it should be restricted' % ( request_type1.name, regular_user2.email )
+ except:
+ pass
+ self.logout()
+ self.login( email=admin_user.email )
+ def test_025_create_request( self ):
+ """Testing creating a sequence run request"""
+ # logged in as admin_user
+ # Create a user_address
+ self.logout()
+ self.login( email=regular_user1.email )
+ self.add_user_address( regular_user1.id, address_dict )
+ global user_address1
+ user_address1 = get_user_address( regular_user1, address_dict[ 'short_desc' ] )
+ # Set field values - the tuples in the field_values list include the field_value, and True if refresh_on_change
+ # is required for that field.
+ field_value_tuples = [ ( 'option1', False ), ( str( user_address1.id ), True ), ( 'field three value', False ) ]
+ # Create the request
+ name = 'Request One'
+ desc = 'Request One Description'
+ self.create_request( cntrller='requests',
+ request_type_id=self.security.encode_id( request_type1.id ),
+ name=name,
+ desc=desc,
+ field_value_tuples=field_value_tuples,
+ strings_displayed=[ 'Create a new sequencing request',
+ test_field_name1,
+ test_field_name2,
+ test_field_name3 ],
+ strings_displayed_after_submit=[ name, desc ] )
+ global request_one
+ request_one = get_request_by_name( name )
+ # Make sure the request's state is now set to NEW
+ assert request_one.state is not request_one.states.NEW, "The state of the request '%s' should be set to '%s'" \
+ % ( request_one.name, request_one.states.NEW )
+ # Sample fields - the tuple represents a sample name and a list of sample form field values
+ sample_value_tuples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ),
+ ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ]
+ strings_displayed_after_submit = [ 'Unsubmitted' ]
+ for sample_name, field_values in sample_value_tuples:
+ strings_displayed_after_submit.append( sample_name )
+ # Add samples to the request
+ self.add_samples( cntrller='requests',
+ request_id=self.security.encode_id( request_one.id ),
+ request_name=request_one.name,
+ sample_value_tuples=sample_value_tuples,
+ strings_displayed=[ 'There are no samples.' ],
+ strings_displayed_after_submit=strings_displayed_after_submit )
+ def test_030_edit_basic_request_info( self ):
+ """Testing editing the basic information of a sequence run request"""
+ # logged in as regular_user1
+ fields = [ 'option2', str( user_address1.id ), 'field three value (edited)' ]
+ new_name=request_one.name + ' (Renamed)'
+ new_desc=request_one.desc + ' (Re-described)'
+ self.edit_basic_request_info( request_id=self.security.encode_id( request_one.id ),
+ cntrller='requests',
+ name=request_one.name,
+ new_name=new_name,
+ new_desc=new_desc,
+ new_fields=fields,
+ strings_displayed=[ 'Edit sequencing request "%s"' % request_one.name ],
+ strings_displayed_after_submit=[ new_name, new_desc ] )
+ refresh( request_one )
+ # check if the request is showing in the 'new' filter
+ self.check_request_grid( cntrller='requests',
+ state=request_one.states.NEW,
+ strings_displayed=[ request_one.name ] )
+ def test_035_submit_request( self ):
+ """Testing editing a sequence run request"""
+ # logged in as regular_user1
+ self.submit_request( cntrller='requests',
+ request_id=self.security.encode_id( request_one.id ),
+ request_name=request_one.name,
+ strings_displayed_after_submit=[ 'The request has been submitted.' ] )
+ refresh( request_one )
+ # Make sure the request is showing in the 'submitted' filter
+ self.check_request_grid( cntrller='requests',
+ state=request_one.states.SUBMITTED,
+ strings_displayed=[ request_one.name ] )
+ # Make sure the request's state is now set to 'submitted'
+ assert request_one.state is not request_one.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" \
+ % ( request_one.name, request_one.states.SUBMITTED )
+ def test_040_request_lifecycle( self ):
+ """Testing request life-cycle as it goes through all the states"""
+ # logged in as regular_user1
+ self.logout()
+ self.login( email=admin_user.email )
+ self.check_request_grid( cntrller='requests_admin',
+ state=request_one.states.SUBMITTED,
+ strings_displayed=[ request_one.name ] )
+ self.visit_url( "%s/requests_common/view_request?cntrller=requests&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ) )
+ # TODO: add some string for checking on the page above...
+ # Set bar codes for the samples
+ bar_codes = [ '1234567890', '0987654321' ]
+ strings_displayed_after_submit=[ 'Changes made to the samples have been saved.' ]
+ for bar_code in bar_codes:
+ strings_displayed_after_submit.append( bar_code )
+ self.add_bar_codes( request_id=self.security.encode_id( request_one.id ),
+ request_name=request_one.name,
+ bar_codes=bar_codes,
+ samples=request_one.samples,
+ strings_displayed_after_submit=strings_displayed_after_submit )
+ # Change the states of all the samples of this request to ultimately be COMPLETE
+ self.change_sample_state( request_id=self.security.encode_id( request_one.id ),
+ request_name=request_one.name,
+ sample_names=[ sample.name for sample in request_one.samples ],
+ sample_ids=[ sample.id for sample in request_one.samples ],
+ new_sample_state_id=request_type1.states[1].id,
+ new_state_name=request_type1.states[1].name )
+ self.change_sample_state( request_id=self.security.encode_id( request_one.id ),
+ request_name=request_one.name,
+ sample_names=[ sample.name for sample in request_one.samples ],
+ sample_ids=[ sample.id for sample in request_one.samples ],
+ new_sample_state_id=request_type1.states[2].id,
+ new_state_name=request_type1.states[2].name )
+ refresh( request_one )
+ self.logout()
+ self.login( email=regular_user1.email )
+ # check if the request's state is now set to 'complete'
+ self.check_request_grid( cntrller='requests',
+ state='Complete',
+ strings_displayed=[ request_one.name ] )
+ assert request_one.state is not request_one.states.COMPLETE, "The state of the request '%s' should be set to '%s'" \
+ % ( request_one.name, request_one.states.COMPLETE )
+
+ def test_045_admin_create_request_on_behalf_of_regular_user( self ):
+ """Testing creating and submitting a request as an admin on behalf of a regular user"""
+ # Logged in as regular_user1
+ self.logout()
+ self.login( email=admin_user.email )
+ # Create the request
+ name = "RequestTwo"
+ desc = 'Request Two Description'
+ # Set field values - the tuples in the field_values list include the field_value, and True if refresh_on_change
+ # is required for that field.
+ field_value_tuples = [ ( 'option2', False ), ( str( user_address1.id ), True ), ( 'field_2_value', False ) ]
+ self.create_request( cntrller='requests_admin',
+ request_type_id=self.security.encode_id( request_type1.id ),
+ other_users_id=self.security.encode_id( regular_user1.id ),
+ name=name,
+ desc=desc,
+ field_value_tuples=field_value_tuples,
+ strings_displayed=[ 'Create a new sequencing request',
+ test_field_name1,
+ test_field_name2,
+ test_field_name3 ],
+ strings_displayed_after_submit=[ "The request has been created." ] )
+ global request_two
+ request_two = get_request_by_name( name )
+ # Make sure the request is showing in the 'new' filter
+ self.check_request_grid( cntrller='requests_admin',
+ state=request_two.states.NEW,
+ strings_displayed=[ request_two.name ] )
+ # Make sure the request's state is now set to 'new'
+ assert request_two.state is not request_two.states.NEW, "The state of the request '%s' should be set to '%s'" \
+ % ( request_two.name, request_two.states.NEW )
+ # Sample fields - the tuple represents a sample name and a list of sample form field values
+ sample_value_tuples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ),
+ ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ]
+ strings_displayed_after_submit = [ 'Unsubmitted' ]
+ for sample_name, field_values in sample_value_tuples:
+ strings_displayed_after_submit.append( sample_name )
+ # Add samples to the request
+ self.add_samples( cntrller='requests_admin',
+ request_id=self.security.encode_id( request_two.id ),
+ request_name=request_two.name,
+ sample_value_tuples=sample_value_tuples,
+ strings_displayed=[ 'There are no samples.' ],
+ strings_displayed_after_submit=strings_displayed_after_submit )
+ # Submit the request
+ self.submit_request( cntrller='requests_admin',
+ request_id=self.security.encode_id( request_two.id ),
+ request_name=request_two.name,
+ strings_displayed_after_submit=[ 'The request has been submitted.' ] )
+ refresh( request_two )
+ # Make sure the request is showing in the 'submitted' filter
+ self.check_request_grid( cntrller='requests_admin',
+ state=request_two.states.SUBMITTED,
+ strings_displayed=[ request_two.name ] )
+ # Make sure the request's state is now set to 'submitted'
+ assert request_two.state is not request_two.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" \
+ % ( request_two.name, request_two.states.SUBMITTED )
+ # Make sure both requests are showing in the 'All' filter
+ self.check_request_grid( cntrller='requests_admin',
+ state='All',
+ strings_displayed=[ request_one.name, request_two.name ] )
+ def test_050_reject_request( self ):
+ """Testing rejecting a request"""
+ # Logged in as admin_user
+ self.reject_request( request_id=self.security.encode_id( request_two.id ),
+ request_name=request_two.name,
+ comment="Rejection test comment",
+ strings_displayed=[ 'Reject Sequencing Request "%s"' % request_two.name ],
+ strings_displayed_after_submit=[ 'Request (%s) has been rejected.' % request_two.name ] )
+ refresh( request_two )
+ # Make sure the request is showing in the 'rejected' filter
+ self.check_request_grid( cntrller='requests_admin',
+ state=request_two.states.REJECTED,
+ strings_displayed=[ request_two.name ] )
+ # Make sure the request's state is now set to REJECTED
+ assert request_two.state is not request_two.states.REJECTED, "The state of the request '%s' should be set to '%s'" \
+ % ( request_two.name, request_two.states.REJECTED )
+ def test_055_reset_data_for_later_test_runs( self ):
+ """Reseting data to enable later test runs to pass"""
+ # Logged in as admin_user
+ ##################
+ # Delete request_type permissions
+ ##################
+ for request_type in [ request_type1 ]:
+ delete_request_type_permissions( request_type.id )
+ ##################
+ # Mark all request_types deleted
+ ##################
+ for request_type in [ request_type1 ]:
+ mark_obj_deleted( request_type )
+ ##################
+ # Mark all requests deleted
+ ##################
+ for request in [ request_one, request_two ]:
+ mark_obj_deleted( request )
+ ##################
+ # Mark all forms deleted
+ ##################
+ for form in [ form_one, form_two ]:
+ self.mark_form_deleted( self.security.encode_id( form.current.id ) )
+ ##################
+ # Mark all user_addresses deleted
+ ##################
+ for user_address in [ user_address1 ]:
+ mark_obj_deleted( user_address )
+ ##################
+ # Delete all non-private roles
+ ##################
+ for role in [ role_one, role_two ]:
+ self.mark_role_deleted( self.security.encode_id( role.id ), role.name )
+ self.purge_role( self.security.encode_id( role.id ), role.name )
+ # Manually delete the role from the database
+ refresh( role )
+ delete( role )
+ ##################
+ # Delete all groups
+ ##################
+ for group in [ group_one ]:
+ self.mark_group_deleted( self.security.encode_id( group.id ), group.name )
+ self.purge_group( self.security.encode_id( group.id ), group.name )
+ # Manually delete the group from the database
+ refresh( group )
+ delete( group )
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -65,12 +65,60 @@
}
}
}
+
+ // Looks for changes in sample states using an async request. Keeps
+ // calling itself (via setTimeout) until all samples are in a terminal
+ // state.
+ var updater = function ( sample_states ) {
+ // Check if there are any items left to track
+ var empty = true;
+ for ( i in sample_states ) {
+ empty = false;
+ break;
+ }
+ if ( ! empty ) {
+ setTimeout( function() { updater_callback( sample_states ) }, 1000 );
+ }
+ };
+
+ var updater_callback = function ( sample_states ) {
+ // Build request data
+ var ids = []
+ var states = []
+ $.each( sample_states, function ( id, state ) {
+ ids.push( id );
+ states.push( state );
+ });
+ // Make ajax call
+ $.ajax( {
+ type: "POST",
+ url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}",
+ dataType: "json",
+ data: { ids: ids.join( "," ), states: states.join( "," ) },
+ success : function ( data ) {
+ $.each( data, function( id, val ) {
+ // Replace HTML
+ var cell1 = $("#sampleState-" + id);
+ cell1.html( val.html_state );
+ var cell2 = $("#sampleDatasets-" + id);
+ cell2.html( val.html_datasets );
+ sample_states[ parseInt( id ) ] = val.state;
+ });
+ updater( sample_states );
+ },
+ error: function() {
+ // Just retry, like the old method, should try to be smarter
+ updater( sample_states );
+ }
+ });
+ };
</script></%def><%def name="render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )"><%
if sample:
+ trans.sa_session.refresh( sample.request )
is_complete = sample.request.is_complete
is_submitted = sample.request.is_submitted
is_unsubmitted = sample.request.is_unsubmitted
@@ -80,12 +128,12 @@
is_unsubmitted = False
%><%
- if is_admin and is_submitted and editing_samples and trans.security.encode_id( sample.id ) in encoded_selected_sample_ids:
+ if is_submitted and editing_samples and trans.security.encode_id( sample.id ) in encoded_selected_sample_ids:
checked_str = "checked"
else:
checked_str = ""
%>
- %if is_admin and is_submitted and editing_samples:
+ %if is_submitted and editing_samples:
<td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" ${checked_str}/><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"/></td>
%endif
<td valign="top">
@@ -95,7 +143,11 @@
</div></td>
%if sample and is_submitted or is_complete:
- <td valign="top"><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
+ %if is_admin:
+ <td valign="top"><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
+ %else:
+ ${current_sample['barcode']}
+ %endif
%endif
%if sample:
%if is_unsubmitted:
@@ -118,7 +170,7 @@
<td valign="top"><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td><td valign="top"><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
%endif
- %if sample and ( is_admin or is_unsubmitted ):
+ %if sample and ( is_admin or is_unsubmitted ) and not is_complete:
## Delete button
<td valign="top"><a class="action-button" href="${h.url_for( controller='requests_common', action='delete_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=current_sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a></td>
%endif
@@ -127,20 +179,22 @@
<%def name="render_samples_grid( cntrller, request, current_samples, action, editing_samples=False, encoded_selected_sample_ids=[], render_buttons=False, grid_header='<h3>Samples</h3>' )">
## Displays the "Samples" grid
<%
+ trans.sa_session.refresh( request )
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
is_complete = request.is_complete
is_submitted = request.is_submitted
is_unsubmitted = request.is_unsubmitted
can_add_samples = request.is_unsubmitted
- can_edit_or_delete_samples = request.samples and not is_complete
+ can_delete_samples = request.samples and not is_complete
+ can_edit_samples = request.samples and ( is_admin or not is_complete )
%>
${grid_header}
- %if render_buttons and ( can_add_samples or can_edit_or_delete_samples ):
+ %if render_buttons and ( can_add_samples or can_edit_samples ):
<ul class="manage-table-actions">
%if can_add_samples:
<li><a class="action-button" href="${h.url_for( controller='requests_common', action='add_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), add_sample_button='Add sample' )}">Add sample</a></li>
%endif
- %if can_edit_or_delete_samples:
+ %if can_edit_samples:
<li><a class="action-button" href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples='True' )}">Edit samples</a></li>
%endif
</ul>
@@ -148,7 +202,7 @@
<table class="grid"><thead><tr>
- %if is_admin and is_submitted and editing_samples:
+ %if is_submitted and editing_samples:
<th><input type="checkbox" id="checkAll" name=select_all_samples_checkbox value="true" onclick='checkAllFields(1);'/><input type="hidden" name=select_all_samples_checkbox value="true"/></th>
%endif
<th>Name</th>
@@ -191,7 +245,7 @@
except:
sample = None
%>
- %if editing_samples:
+ %if not is_complete and editing_samples:
<tr>${render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
%elif sample:
<tr>
@@ -220,6 +274,7 @@
%endif
</tr>
%else:
+ ## The Add sample button was clicked for this sample_widget
<tr>${render_editable_sample_row( is_admin, None, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
%endif
%endfor
@@ -231,9 +286,7 @@
<tr><td>${sample_name}</td>
%for field_index, field in fields_dict.items():
- <%
- field_type = field[ 'type' ]
- %>
+ <% field_type = field[ 'type' ] %><td>
%if display_only:
%if sample_values[field_index]:
--- a/templates/requests/common/view_request.mako
+++ b/templates/requests/common/view_request.mako
@@ -22,7 +22,8 @@
is_unsubmitted = request.is_unsubmitted
can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
can_add_samples = is_unsubmitted
- can_edit_or_delete_samples = request.samples and not is_complete
+ can_delete_samples = request.samples and not is_complete
+ can_edit_samples = request.samples and ( is_admin or not is_complete )
can_submit = request.samples and is_unsubmitted
%>
@@ -131,14 +132,23 @@
${states}
<div style="clear: both"></div></div>
+ %if request.samples and request.is_submitted:
+ <script type="text/javascript">
+ // Updater
+ updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
+ </script>
+ %endif
</div></div></div></div><p/>
%if current_samples:
- <% grid_header = '<h3>Samples</h3>' %>
- ${render_samples_grid( cntrller, request, current_samples=current_samples, action='view_request', editing_samples=False, encoded_selected_sample_ids=[], render_buttons=can_edit_or_delete_samples, grid_header=grid_header )}
+ <%
+ grid_header = '<h3>Samples</h3>'
+ render_buttons = can_edit_samples
+ %>
+ ${render_samples_grid( cntrller, request, current_samples=current_samples, action='view_request', editing_samples=False, encoded_selected_sample_ids=[], render_buttons=render_buttons, grid_header=grid_header )}
%else:
There are no samples.
%if can_add_samples:
1
0

galaxy-dist commit e000a6800e1c: trackster: Fix summary_tree data provider to reflect bottom level not being indexed anymore.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Kanwei Li <kanwei(a)gmail.com>
# Date 1288748135 14400
# Node ID e000a6800e1cbfa15a190bb2aff90a5956882a43
# Parent 56948309109207f932a113e464409432c7a38df7
trackster: Fix summary_tree data provider to reflect bottom level not being indexed anymore.
- Fix a bug causing no display modes to appear.
- Update "Visualize in Trackster" init code in root/history.mako
Fix small typo in root/tool_menu.mako
--- a/templates/root/tool_menu.mako
+++ b/templates/root/tool_menu.mako
@@ -32,7 +32,7 @@
<div class="toolTitleNoSection">
%endif
<% encoded_id = key.lstrip( 'workflow_' ) %>
- <a id="link-${workflow.id}" href="${ h.url_for( controller='workflow', action='run', id=encoded_id, check_user=False )}" target="_parent"}">${_(workflow.name)}</a>
+ <a id="link-${workflow.id}" href="${ h.url_for( controller='workflow', action='run', id=encoded_id, check_user=False )}" target="_parent">${_(workflow.name)}</a></div></%def>
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -677,7 +677,7 @@ var TiledTrack = function() {
track.draw();
};
var mode_mapping = {};
- for (var i, len = track.display_modes.length; i < len; i++) {
+ for (var i = 0, len = track.display_modes.length; i < len; i++) {
var mode = track.display_modes[i];
mode_mapping[mode] = function(mode) {
return function() { change_mode(mode); };
--- a/lib/galaxy/visualization/tracks/data_providers.py
+++ b/lib/galaxy/visualization/tracks/data_providers.py
@@ -102,14 +102,14 @@ class SummaryTreeDataProvider( TracksDat
level = ceil( log( resolution, st.block_size ) ) - 1
level = int(max( level, 0 ))
- if level <= 0:
+ if level <= 1:
return None
stats = st.chrom_stats[chrom]
results = st.query(chrom, int(start), int(end), level)
if results == "detail":
return None
- elif results == "draw" or level <= 1:
+ elif results == "draw":
return "no_detail"
else:
return results, stats[level]["max"], stats[level]["avg"], stats[level]["delta"]
--- a/templates/root/history.mako
+++ b/templates/root/history.mako
@@ -157,29 +157,33 @@ TERMINAL_STATES = ${ h.to_json_string(TE
// Trackster links
function init_trackster_links() {
- $("a.trackster").live( "click", function() {
- var link = $(this),
- hid = link.attr("id").split("_")[1]; // visualize_{id}
-
+ // Add to trackster browser functionality
+ $(".trackster-add").live("click", function() {
+ var dataset = this,
+ dataset_jquery = $(this);
$.ajax({
- url: "${h.url_for( controller='tracks', action='list_tracks' )}",
- data: {'hid': hid},
- error: function() { alert( "Visualization error" ); },
- success: function(html) {
- show_modal("Add Track — Select Dataset(s)", html, {
- "New Browser": function() {
- hide_modal();
+ url: dataset_jquery.attr("data-url"),
+ dataType: "html",
+ error: function() { alert( "Could not add this dataset to browser." ); },
+ success: function(table_html) {
+ var parent = window.parent;
+ parent.show_modal("Add to Browser:", table_html, {
+ "Cancel": function() {
+ parent.hide_modal();
},
- "Insert": function() {
- hide_modal();
+ "Insert into selected": function() {
+ $(parent.document).find('input[name=id]:checked').each(function() {
+ var vis_id = $(this).val();
+ parent.location = dataset_jquery.attr("action-url") + "&id=" + vis_id;
+ });
},
- "Cancel": function() {
- hide_modal();
+ "Insert into new browser": function() {
+ parent.location = dataset_jquery.attr("new-url");
}
});
}
});
- });
+ });
}
init_trackster_links();
--- a/static/scripts/packed/trackster.js
+++ b/static/scripts/packed/trackster.js
@@ -1,1 +1,1 @@
-var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=50,CONNECTOR_COLOR="#ccc",DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",FILTERABLE_CLASS="filterable",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=5,CACHED_DATA=5,DUMMY_CANVAS=document.createElement("canvas"),RIGHT_STRAND,LEFT_STRAND;if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(DUMMY_CANVAS)}CONTEXT=DUMMY_CANVAS.getContext("2d");PX_PER_CHAR=CONTEXT.measureText("A").width;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPa
ttern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src=image_path+"/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function round_1000(a){return Math.round(a*1000)/1000}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var View=function(a,d,c,b){this.container=a;this.vis_id=c;this.dbkey=b;this.ti
tle=d;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide();this.nav_container=$("<div/>").addClass("nav-container").appendTo(c);this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.nav_container);this.nav=$("<div/>").addClass("nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.nav);this.overview_viewport=$("<div/>").addClas
s("overview-viewport").appendTo(this.overview);this.overview_close=$("<a href='javascript:void(0);'>Close Overview</a>").addClass("overview-close").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div />").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_form=$("<form/>").attr("action",function(){}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.wh
ich)!==27){a.go_to($(this).val())}$(this).hide();a.location_span.show();a.chrom_select.show();return false}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keypress focusout",b).appendTo(this.chrom_form);this.location_span=$("<span/>").addClass("location").appendTo(this.chrom_form);this.location_span.bind("click",function(){a.location_span.hide();a.chrom_select.hide();a.nav_input.css("display","inline-block");a.nav_input.select();a.nav_input.focus()});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.chrom_form)}this.zo_link=$("<a/>").click(function(){a.zoom_out();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom-out.png" />').appendTo(this.chrom_form);this.zi_link=$("<a/>").click(function(){a.zoom_in();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom.png" />').appendTo(this.chrom_form);$.ajax({url:chrom_url,data:(this.vis_id!==undefined?{vis_id:this.vis_id}:{dbkey:
this.dbkey}),dataType:"json",success:function(e){if(e.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=e.chrom_info;var h='<option value="">Select Chrom/Contig</option>';for(var g=0,d=a.chrom_data.length;g<d;g++){var f=a.chrom_data[g].chrom;h+='<option value="'+f+'">'+f+"</option>"}a.chrom_select.html(h);a.intro_div.show();a.chrom_select.bind("change",function(){a.change_chrom(a.chrom_select.val())})},error:function(){alert("Could not load chroms for this dbkey:",a.dbkey)}});this.content_div.bind("dblclick",function(d){a.zoom_in(d.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(d){this.current_x=d.offsetX}).bind("drag",function(d){var g=d.offsetX-this.current_x;this.current_x=d.offsetX;var f=Math.round(g/a.viewport_container.width()*(a.max_high-a.max_low));a.move_delta(-f)});this.overview_close.bind("click",function(){for(var e=0,d=a.tracks.length;e<d;e++){a.tracks[e].is_overview=false}$(this).siblings().filter("canvas").remove();
$(this).parent().css("height",a.overview_box.height());a.overview_highlight.hide();$(this).hide()});this.viewport_container.bind("dragstart",function(d){this.original_low=a.low;this.current_height=d.clientY;this.current_x=d.offsetX;this.enable_pan=(d.clientX<a.viewport_container.width()-16)?true:false}).bind("drag",function(g){if(!this.enable_pan||this.in_reordering){return}var d=$(this);var i=g.offsetX-this.current_x;var f=d.scrollTop()-(g.clientY-this.current_height);d.scrollTop(f);this.current_height=g.clientY;this.current_x=g.offsetX;var h=Math.round(i/a.viewport_container.width()*(a.high-a.low));a.move_delta(h)});this.top_labeltrack.bind("dragstart",function(d){this.drag_origin_x=d.clientX;this.drag_origin_pos=d.clientX/a.viewport_container.width()*(a.high-a.low)+a.low;this.drag_div=$("<div />").css({height:a.content_div.height()+30,top:"0px",position:"absolute","background-color":"#cfc",border:"1px solid #6a6",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag
",function(i){var f=Math.min(i.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(i.clientX,this.drag_origin_x)-a.container.offset().left,h=(a.high-a.low),g=a.viewport_container.width();a.update_location(Math.round(f/g*h)+a.low,Math.round(d/g*h)+a.low);this.drag_div.css({left:f+"px",width:(d-f)+"px"})}).bind("dragend",function(j){var f=Math.min(j.clientX,this.drag_origin_x),d=Math.max(j.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),i=a.low;a.low=Math.round(f/g*h)+i;a.high=Math.round(d/g*h)+i;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(this,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack));$(window).bind("resize",function(){a.resize_window()});$(document).bind("redraw",function(){a.redraw()});this.reset();$(window).trigger("resize")},update_location:function(a,b){this.location_span.text(commatize(a)+" - "+commatize(b));this.nav_input.val(this.chrom+":"+commatize(a)+"-"+c
ommatize(b))},change_chrom:function(e,b,g){var d=this;var f=$.grep(d.chrom_data,function(j,k){return j.chrom===e})[0];if(f===undefined){return}if(e!==d.chrom){d.chrom=e;if(!d.chrom){d.intro_div.show()}else{d.intro_div.hide()}d.chrom_select.val(d.chrom);d.max_high=f.len;d.reset();d.redraw(true);for(var h=0,a=d.tracks.length;h<a;h++){var c=d.tracks[h];if(c.init){c.init()}}}if(b!==undefined&&g!==undefined){d.low=Math.max(b,0);d.high=Math.min(g,d.max_high)}d.reset_overview();d.redraw()},go_to:function(f){var j=this,a,d,b=f.split(":"),h=b[0],i=b[1];if(i!==undefined){try{var g=i.split("-");a=parseInt(g[0].replace(/,/g,""),10);d=parseInt(g[1].replace(/,/g,""),10)}catch(c){return false}}j.change_chrom(h,a,d)},move_delta:function(c){var a=this;var b=a.high-a.low;if(a.low-c<a.max_low){a.low=a.max_low;a.high=a.max_low+b}else{if(a.high-c>a.max_high){a.high=a.max_high;a.low=a.max_high-b}else{a.high-=c;a.low-=c}}a.redraw()},add_track:function(a){a.view=this;a.track_id=this.track_id_counte
r;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1;this.num_tracks+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)];this.num_tracks-=1},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},redraw:function(h){var g=this.high-this.low,f=this.low,b=this.high;if(f<this.max_low){f=this.max_low}if(b>this.max_high){b=this.max_high}if(this.high!==0&&g<this.min_separation){b=f+this.min_separation}this.low=Math.floor(f);this.high=Math.ceil(b);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));var a=(this.low/(this.max_high-this.max
_low)*this.overview_viewport.width())||0;var e=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var j=13;this.overview_box.css({left:a,width:Math.max(j,e)}).show();if(e<j){this.overview_box.css("left",a-(j-e)/2)}if(this.overview_highlight){this.overview_highlight.css({left:a,width:e})}this.update_location(this.low,this.high);if(!h){for(var c=0,d=this.tracks.length;c<d;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(c=0,d=this.label_tracks.length;c<d;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);th
is.high=Math.round(c+a);this.redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.nav_container.height()-45);this.nav_container.width(this.container.width());this.redraw()},reset_overview:function(){this.overview_viewport.find("canvas").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide()}});var Filter=function(b,a,c){this.name=b;this.index=a;this.value=c};var NumberFilter=function(b,a){this.name=b;this.index=a;this.low=-Number.MAX_VALUE;this.high=Number.MAX_VALUE;this.slider_min=Number.MAX_VALUE;this.slider_max=-Number.MAX_VALUE;this.slider=null;this.slider_label=null};$.extend(NumberFilter.prototype,{applies_to:function(a){if(a.length>this.index){return true}return false},keep:function(a){if(!this.applies_to(a)){return true}return(a[this.index]>=this.low&&a[this.index]<=this.high)},update_attrs:function(b
){var a=false;if(!this.applies_to(b)){return a}if(b[this.index]<this.slider_min){this.slider_min=b[this.index];a=true}if(b[this.index]>this.slider_max){this.slider_max=b[this.index];a=false}return a},update_ui_elt:function(){var b=this.slider.slider("option","min"),a=this.slider.slider("option","max");if(this.slider_min<b||this.slider_max>a){this.slider.slider("option","min",this.slider_min);this.slider.slider("option","max",this.slider_max);this.slider.slider("option","values",[this.slider_min,this.slider_max])}}});var get_filters=function(a){var g=[];for(var d=0;d<a.length;d++){var f=a[d];var c=f.name,e=f.type,b=f.index;if(e=="int"||e=="float"){g[d]=new NumberFilter(c,b)}else{g[d]=new Filter(c,b,e)}}return g};var Track=function(b,a,d,c){this.name=b;this.view=a;this.parent_element=d;this.filters=(c!==undefined?get_filters(c):[]);this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track").css("position","relative");i
f(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);if(this.view.editor){this.drag_div=$("<div class='draghandle' />").appendTo(this.header_div)}this.name_div=$("<div class='menubutton popup' />").appendTo(this.header_div);this.name_div.text(this.name);this.name_div.attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase())}this.filtering_div=$("<div class='track-filters'>").appendTo(this.container_div);this.filtering_div.hide();this.filtering_div.bind("drag",function(i){i.stopPropagation()});var b=$("<table class='filters'>").appendTo(this.filtering_div);var c=this;for(var e=0;e<this.filters.length;e++){var a=this.filters[e];var f=$("<tr>").appendTo(b);var g=$("<th class='filter-info'>").appendTo(f);var j=$("<span class='name'>").appendTo(g);j.text(a.name+" ");var d=$("<span class='values'>").appendTo(g);var h=$("<td>").appendTo(f);a.control_element=$("<div id='"+a.name+"-filter-control' style='width: 200
px; position: relative'>").appendTo(h);a.control_element.slider({range:true,min:Number.MAX_VALUE,max:-Number.MIN_VALUE,values:[0,0],slide:function(k,l){var i=l.values;d.text("["+i[0]+"-"+i[1]+"]");a.low=i[0];a.high=i[1];c.draw(true)},change:function(i,k){a.control_element.slider("option","slide").call(a.control_element,i,k)}});a.slider=a.control_element;a.slider_label=d}this.content_div=$("<div class='track-content'>").appendTo(this.container_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.initial_canvas=undefined;a.content_div.css("height","auto");if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"||d.kind==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR);if(d.message){var f=a.view.tracks.indexOf(a);var e=$("
<a href='javascript:void(0);'></a>").attr("id",f+"_error");e.text("Click to view error");$("#"+f+"_error").live("click",function(){show_modal("Trackster Error","<pre>"+d.message+"</pre>",{Close:hide_modal})});a.content_div.append(e)}}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if(d.data!==undefined&&(d.data===null||d.data.length===0)){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){var b=this,j=b.view;if(b.hidden){return}if(b.display_modes!==undefined){if(b.mode_div===undefined){b.mode_div=$("<div class='right-float menubutton popup' />").appendTo(b.header_div);var e=
b.display_modes[0];b.mode=e;b.mode_div.text(e);var c=function(i){b.mode_div.text(i);b.mode=i;b.tile_cache.clear();b.draw()};var a={};for(var f,h=b.display_modes.length;f<h;f++){var g=b.display_modes[f];a[g]=function(i){return function(){c(i)}}(g)}make_popupmenu(b.mode_div,a)}else{b.mode_div.hide()}}var d={};d["Set as overview"]=function(){j.overview_viewport.find("canvas").remove();b.is_overview=true;b.set_overview();for(var i in j.tracks){if(j.tracks[i]!==b){j.tracks[i].is_overview=false}}};d["Edit configuration"]=function(){var l=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},i=function(){b.update_options(b.track_id);hide_modal();$(window).unbind("keypress.check_enter_esc")},k=function(m){if((m.keyCode||m.which)===27){l()}else{if((m.keyCode||m.which)===13){i()}}};$(window).bind("keypress.check_enter_esc",k);show_modal("Configure Track",b.gen_options(b.track_id),{Cancel:l,OK:i})};if(b.filters.length>0){d["Show filters"]=function(){var i;if(!b.filterin
g_div.is(":visible")){i="Hide filters";b.filters_visible=true}else{i="Show filters";b.filters_visible=false}$("#"+b.name_div.attr("id")+"-menu").find("li").eq(2).text(i);b.filtering_div.toggle()}}d.Remove=function(){j.remove_track(b);if(j.num_tracks===0){$("#no-tracks").show()}};b.popup_menu=make_popupmenu(b.name_div,d);show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters",false)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(a){var k=this.view.low,g=this.view.high,h=g-k,f=this.view.resolution;var n=$("<div style='position: relative;'></div>"),o=this.content_div.width()/h;this.content_div.append(n);this.max_height=0;var b=Math.floor(k/f/DENSITY);var j={};while((b*DENSITY*f)<g){var l=this.content_div.width()+"_"+o+"_"+b;var e=this.tile_cache.get(l);if(!a&&e){var i=b*DENSITY*f;var d=(i-k)*o;if(this.left_offset){d-=this.left_offset}e.css({left:d});this.show_tile(e,n)}else{this.delayed_draw(this,l,k,g,b,f,n,o,j)}b+=1}var c=this;var m=setInterval(function(
){if(obj_length(j)===0){if(c.content_div.children().length>1){c.content_div.children(":first").remove()}for(var p=0;p<c.filters.length;p++){c.filters[p].update_ui_elt()}clearInterval(m)}},50)},delayed_draw:function(c,h,g,e,b,d,i,j,f){var a=setTimeout(function(){if(g<=c.view.high&&e>=c.view.low){var k=c.draw_tile(d,b,i,j);if(k){if(!c.initial_canvas&&!window.G_vmlCanvasManager){c.initial_canvas=$(k).clone();var n=k.get(0).getContext("2d");var l=c.initial_canvas.get(0).getContext("2d");var m=n.getImageData(0,0,n.canvas.width,n.canvas.height);l.putImageData(m,0,0);c.set_overview()}c.tile_cache.set(h,k);c.show_tile(k,i)}}delete f[a]},50);f[a]=true},show_tile:function(a,c){var b=this;c.append(a);b.max_height=Math.max(b.max_height,a.height());b.content_div.css("height",b.max_height+"px");if(a.hasClass(FILTERABLE_CLASS)){show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters");if(b.filters_visible){b.filtering_div.show()}}else{show_hide_popupmenu_options(b.popup_menu,"(Show|H
ide) filters",false);b.filtering_div.hide()}},set_overview:function(){var a=this.view;if(this.initial_canvas&&this.is_overview){a.overview_close.show();a.overview_viewport.append(this.initial_canvas);a.overview_highlight.show().height(this.initial_canvas.height());a.overview_viewport.height(this.initial_canvas.height()+a.overview_box.height())}$(window).trigger("resize")}});var LabelTrack=function(a,b){this.track_type="LabelTrack";this.hidden=true;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(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'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.
append(b)}});var ReferenceTrack=function(a){this.track_type="ReferenceTrack";this.hidden=true;Track.call(this,null,a,a.top_labeltrack);TiledTrack.call(this);this.left_offset=200;this.height_px=12;this.container_div.addClass("reference-track");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,j=f+"_"+b;var e=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(e)}e=$(e);var n=e.get(0).getContext("2d");if(o>PX_PER_CHAR){if(this.data_
cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o),i=Math.round(o/2);n.fillText(m[h],a+this.left_offset+i,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=80;this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={color:"black",min_value:undefined,max_value:undefined,mode:this.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.verti
cal_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");var e=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=e.min;a.prefs.max_value=e.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=e.total_frequency;a.container_div.find(".yaxislabel").remove();var f=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(round_1000(a.prefs.min_value));var d=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(round_1000(a.prefs.max_value));d.css({position:"absolute",top:"22px",left:"10px"});d.prependTo(a.container_div);f.css({position:"absolute",top:a.height_px+11+"px",left:"10px"});f.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b
+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){var h=g.data;c.data_cache.set(e,h);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*o,a=DENSITY*o,w=o+"_"+r;var b=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(b)}b=$(b);if(this.data_cache.get(w)===undefined){this.get_data(o,r);return}var v=this.data_cache.get(w);if(!v){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),j=false,k=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,t=this.total_frequency,d=this.height_px,l=this.mode;n.beginPath();n.fillStyle=this.prefs.colo
r;var u,h,f;if(v.length>1){f=Math.ceil((v[1][0]-v[0][0])*e)}else{f=10}for(var p=0,q=v.length;p<q;p++){u=Math.round((v[p][0]-s)*e);h=v[p][1];if(h===null){if(j&&l==="Filled"){n.lineTo(u,d)}j=false;continue}if(h<k){h=k}else{if(h>g){h=g}}if(l==="Histogram"){h=Math.round(d-(h-k)/m*d);n.fillRect(u,h,f,d-h)}else{if(l==="Intensity"){h=255-Math.floor((h-k)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(u,0,f,d)}else{h=Math.round(d-(h-k)/m*d);if(j){n.lineTo(u,h)}else{j=true;if(l==="Filled"){n.moveTo(u,d);n.lineTo(u,h)}else{n.moveTo(u,h)}}}}}if(l==="Filled"){if(j){n.lineTo(u,d)}n.fill()}else{n.stroke()}c.append(b);return b},gen_options:function(m){var a=$("<div />").addClass("form-row");var e="track_"+m+"_color",b=$("<label />").attr("for",e).text("Color:"),c=$("<input />").attr("id",e).attr("name",e).val(this.prefs.color),h="track_"+m+"_minval",l=$("<label></label>").attr("for",h).text("Min value:"),d=(this.prefs.min_value===undefined?"":this.prefs.min_value),k=$("<input></inp
ut>").attr("id",h).val(d),j="track_"+m+"_maxval",g=$("<label></label>").attr("for",j).text("Max value:"),i=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",j).val(i);return a.append(l).append(k).append(g).append(f).append(b).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_color").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!==this.prefs.color){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.color=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,e,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container,e);TiledTrack.call(this);this.height_px=
0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=30;this.default_font="9px Monaco, Lucida Console, monospace";this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.left_offset=200;this.prefs={block_color:"#444",label_color:"black",show_counts:true}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b="initial";this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution,mode:a.mode},function(c){a.mode_div.show();a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.
resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,b,q){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=q;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,y=[],h=0,n=this.view.max_low;var A=[];if(this.inc_slots[a].mode!==q){delete this.inc_slots[a];this.inc_slots[a]={mode:q,w_scale:m};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var v=0,w=g.length;v<w;v++){var f=g[v],l=f[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);A.push(this.inc_slots[a][l])}else{y.push(v)}}for(var v=0,w=y.length;v<w;v++){var f=g[y[v]],l=f[0],r=f[1],c=f[2],p=f[3],d=Math.floor((r-n)*m),e=Math.ceil((c-n)*m);if(p!==undefined&&!b){var s=CONTEXT.measureText(p).width;if(d-s<0){e+=s}else{d-=s}}var u=0;while(u<=MAX_FEATURE_DEPTH){var o=true;if(this.s_e_by_tile[a][u]!==undefined){for(var t=0,z=this.s_e_by_tile[a][u].length;t<z;t++){var x=this.s_e_by
_tile[a][u][t];if(e>x[0]&&d<x[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][u]===undefined){this.s_e_by_tile[a][u]=[]}this.s_e_by_tile[a][u].push([d,e]);this.inc_slots[a][l]=u;h=Math.max(h,u);break}u++}}return h},rect_or_text:function(r,l,t,b,q,f,i,e){r.textAlign="center";var k=0,p=Math.round(l/2);for(var m=0,s=i.length;m<s;m++){var j=i[m],d="MIDNSHP"[j[0]],n=j[1];if(d==="H"||d==="S"){k-=n}var g=q+k,w=Math.floor(Math.max(0,(g-t)*l)),h=Math.floor(Math.max(0,(g+n-t)*l));switch(d){case"S":case"H":case"M":var o=f.slice(k,n);if((this.mode==="Pack"||this.mode==="Auto")&&f!==undefined&&l>PX_PER_CHAR){r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+1,h-w,9);r.fillStyle=CONNECTOR_COLOR;for(var u=0,a=o.length;u<a;u++){if(g+u>=t&&g+u<=b){var v=Math.floor(Math.max(0,(g+u-t)*l));r.fillText(o[u],v+this.left_offset+p,e+9)}}}else{r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+4,h-w,3)}break;case"N":r.fillStyle=CONNECTOR_COLOR;r.fillRect(w+this.left_
offset,e+5,h-w,1);break;case"D":r.fillStyle="red";r.fillRect(w+this.left_offset,e+4,h-w,3);break;case"P":case"I":break}k+=n}},draw_tile:function(ag,o,s,av){var N=o*DENSITY*ag,al=(o+1)*DENSITY*ag,M=al-N;var an=(!this.initial_canvas?"initial":N+"_"+al);var I=this.data_cache.get(an);var e;if(I===undefined||(this.mode!=="Auto"&&I.dataset_type==="summary_tree")){this.data_queue[[N,al]]=true;this.get_data(N,al);return}var a=Math.ceil(M*av),ai=this.prefs.label_color,l=this.prefs.block_color,r=this.mode,z=25,ae=(r==="Squish")||(r==="Dense")&&(r!=="Pack")||(r==="Auto"&&(I.extra_info==="no_detail")),W=this.left_offset,au,D,aw;var q=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(q)}q=$(q);if(I.dataset_type==="summary_tree"){D=this.summary_draw_height}else{if(r==="Dense"){D=z;aw=10}else{aw=(ae?this.vertical_nodetail_px:this.vertical_detail_px);var A=(av<0.0001?1/this.view.zoom_res:av);D=this.incremental_slots(A,I.data,ae,r)*aw+z;au=this.inc
_slots[A]}}q.css({position:"absolute",top:0,left:(N-this.view.low)*av-W});q.get(0).width=a+W;q.get(0).height=D;s.parent().css("height",Math.max(this.height_px,D)+"px");var J=q.get(0).getContext("2d");J.fillStyle=l;J.font=this.default_font;J.textAlign="right";this.container_div.find(".yaxislabel").remove();if(I.dataset_type=="summary_tree"){var Y=I.data,L=I.max,b=Math.ceil(I.delta*av);var p=$("<div />").addClass("yaxislabel").text(L);p.css({position:"absolute",top:"22px",left:"10px"});p.prependTo(this.container_div);for(var ap=0,H=Y.length;ap<H;ap++){var aa=Math.floor((Y[ap][0]-N)*av);var Z=Y[ap][1];if(!Z){continue}var am=Z/L*this.summary_draw_height;J.fillStyle="black";J.fillRect(aa+W,this.summary_draw_height-am,b,am);if(this.prefs.show_counts&&J.measureText(Z).width<b){J.fillStyle="#bbb";J.textAlign="center";J.fillText(Z,aa+W+(b/2),this.summary_draw_height-5)}}e="Summary";s.append(q);return q}if(I.message){q.css({border:"solid red","border-width":"2px 2px 2px 0px"});J.fillS
tyle="red";J.textAlign="left";J.fillText(I.message,100+W,aw)}var ad=false;if(I.data){ad=true;for(var ar=0;ar<this.filters.length;ar++){if(!this.filters[ar].applies_to(I.data[0])){ad=false}}}if(ad){q.addClass(FILTERABLE_CLASS)}var at=I.data;var ao=0;for(var ap=0,H=at.length;ap<H;ap++){var S=at[ap],R=S[0],aq=S[1],ac=S[2],O=S[3];if(au[R]===undefined){continue}var ab=false;var U;for(var ar=0;ar<this.filters.length;ar++){U=this.filters[ar];U.update_attrs(S);if(!U.keep(S)){ab=true;break}}if(ab){continue}if(aq<=al&&ac>=N){var af=Math.floor(Math.max(0,(aq-N)*av)),K=Math.ceil(Math.min(a,Math.max(0,(ac-N)*av))),X=(r==="Dense"?1:(1+au[R]))*aw;var G,aj,P=null,ax=null;if(I.dataset_type==="bai"){var v=S[4];J.fillStyle=l;if(S[5] instanceof Array){var E=Math.floor(Math.max(0,(S[5][0]-N)*av)),Q=Math.ceil(Math.min(a,Math.max(0,(S[5][1]-N)*av))),C=Math.floor(Math.max(0,(S[6][0]-N)*av)),w=Math.ceil(Math.min(a,Math.max(0,(S[6][1]-N)*av)));if(S[5][1]>=N&&S[5][0]<=al){this.rect_or_text(J,av,N,al,S
[5][0],S[5][2],v,X)}if(S[6][1]>=N&&S[6][0]<=al){this.rect_or_text(J,av,N,al,S[6][0],S[6][2],v,X)}if(C>Q){J.fillStyle=CONNECTOR_COLOR;J.fillRect(Q+W,X+5,C-Q,1)}}else{J.fillStyle=l;this.rect_or_text(J,av,N,al,aq,O,v,X)}if(r!=="Dense"&&!ae&&aq>N){J.fillStyle=this.prefs.label_color;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(R,K+2+W,X+8)}else{J.textAlign="right";J.fillText(R,af-2+W,X+8)}J.fillStyle=l}}else{if(I.dataset_type==="interval_index"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var F=S[4],V=S[5],ah=S[6],h=S[7];if(V&&ah){P=Math.floor(Math.max(0,(V-N)*av));ax=Math.ceil(Math.min(a,Math.max(0,(ah-N)*av)))}if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}if(h){if(F){if(F=="+"){J.fillStyle=RIGHT_STRAND}else{if(F=="-"){J.fillStyle=LEFT_STRAND}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}for(var an=0
,g=h.length;an<g;an++){var u=h[an],d=Math.floor(Math.max(0,(u[0]-N)*av)),T=Math.ceil(Math.min(a,Math.max((u[1]-N)*av)));if(d>T){continue}G=5;aj=3;J.fillRect(d+W,X+aj,T-d,G);if(P!==undefined&&!(d>ax||T<P)){G=9;aj=1;var ak=Math.max(d,P),B=Math.min(T,ax);J.fillRect(ak+W,X+aj,B-ak,G)}}}else{G=9;aj=1;J.fillRect(af+W,X+aj,K-af,G);if(S.strand){if(S.strand=="+"){J.fillStyle=RIGHT_STRAND_INV}else{if(S.strand=="-"){J.fillStyle=LEFT_STRAND_INV}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}}}}else{if(I.dataset_type==="vcf"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var t=S[4],n=S[5],c=S[6];G=9;aj=1;J.fillRect(af+W,X,K-af,G);if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}var m=t+" / "+n;if(aq>N&&J.measureText(m).width<(K-af)){J.fillStyle="white";J.textAlign="center";J.fillText(m,W+af+(K-af)/2,X+8);J.fillStyle=l}}}}}ao++}}return q},
gen_options:function(i){var a=$("<div />").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label />").attr("for",e).text("Block color:"),l=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label />").attr("for",j).text("Text color:"),h=$("<input />").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(k).append(l).append(g).append(h).append(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_co
unts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,e,c){FeatureTrack.call(this,d,b,a,e,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
+var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=50,CONNECTOR_COLOR="#ccc",DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",FILTERABLE_CLASS="filterable",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=5,CACHED_DATA=5,DUMMY_CANVAS=document.createElement("canvas"),RIGHT_STRAND,LEFT_STRAND;if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(DUMMY_CANVAS)}CONTEXT=DUMMY_CANVAS.getContext("2d");PX_PER_CHAR=CONTEXT.measureText("A").width;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPa
ttern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src=image_path+"/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function round_1000(a){return Math.round(a*1000)/1000}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var View=function(a,d,c,b){this.container=a;this.vis_id=c;this.dbkey=b;this.ti
tle=d;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide();this.nav_container=$("<div/>").addClass("nav-container").appendTo(c);this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.nav_container);this.nav=$("<div/>").addClass("nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.nav);this.overview_viewport=$("<div/>").addClas
s("overview-viewport").appendTo(this.overview);this.overview_close=$("<a href='javascript:void(0);'>Close Overview</a>").addClass("overview-close").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div />").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_form=$("<form/>").attr("action",function(){}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.wh
ich)!==27){a.go_to($(this).val())}$(this).hide();a.location_span.show();a.chrom_select.show();return false}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keypress focusout",b).appendTo(this.chrom_form);this.location_span=$("<span/>").addClass("location").appendTo(this.chrom_form);this.location_span.bind("click",function(){a.location_span.hide();a.chrom_select.hide();a.nav_input.css("display","inline-block");a.nav_input.select();a.nav_input.focus()});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.chrom_form)}this.zo_link=$("<a/>").click(function(){a.zoom_out();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom-out.png" />').appendTo(this.chrom_form);this.zi_link=$("<a/>").click(function(){a.zoom_in();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom.png" />').appendTo(this.chrom_form);$.ajax({url:chrom_url,data:(this.vis_id!==undefined?{vis_id:this.vis_id}:{dbkey:
this.dbkey}),dataType:"json",success:function(e){if(e.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=e.chrom_info;var h='<option value="">Select Chrom/Contig</option>';for(var g=0,d=a.chrom_data.length;g<d;g++){var f=a.chrom_data[g].chrom;h+='<option value="'+f+'">'+f+"</option>"}a.chrom_select.html(h);a.intro_div.show();a.chrom_select.bind("change",function(){a.change_chrom(a.chrom_select.val())})},error:function(){alert("Could not load chroms for this dbkey:",a.dbkey)}});this.content_div.bind("dblclick",function(d){a.zoom_in(d.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(d){this.current_x=d.offsetX}).bind("drag",function(d){var g=d.offsetX-this.current_x;this.current_x=d.offsetX;var f=Math.round(g/a.viewport_container.width()*(a.max_high-a.max_low));a.move_delta(-f)});this.overview_close.bind("click",function(){for(var e=0,d=a.tracks.length;e<d;e++){a.tracks[e].is_overview=false}$(this).siblings().filter("canvas").remove();
$(this).parent().css("height",a.overview_box.height());a.overview_highlight.hide();$(this).hide()});this.viewport_container.bind("dragstart",function(d){this.original_low=a.low;this.current_height=d.clientY;this.current_x=d.offsetX;this.enable_pan=(d.clientX<a.viewport_container.width()-16)?true:false}).bind("drag",function(g){if(!this.enable_pan||this.in_reordering){return}var d=$(this);var i=g.offsetX-this.current_x;var f=d.scrollTop()-(g.clientY-this.current_height);d.scrollTop(f);this.current_height=g.clientY;this.current_x=g.offsetX;var h=Math.round(i/a.viewport_container.width()*(a.high-a.low));a.move_delta(h)});this.top_labeltrack.bind("dragstart",function(d){this.drag_origin_x=d.clientX;this.drag_origin_pos=d.clientX/a.viewport_container.width()*(a.high-a.low)+a.low;this.drag_div=$("<div />").css({height:a.content_div.height()+30,top:"0px",position:"absolute","background-color":"#cfc",border:"1px solid #6a6",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag
",function(i){var f=Math.min(i.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(i.clientX,this.drag_origin_x)-a.container.offset().left,h=(a.high-a.low),g=a.viewport_container.width();a.update_location(Math.round(f/g*h)+a.low,Math.round(d/g*h)+a.low);this.drag_div.css({left:f+"px",width:(d-f)+"px"})}).bind("dragend",function(j){var f=Math.min(j.clientX,this.drag_origin_x),d=Math.max(j.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),i=a.low;a.low=Math.round(f/g*h)+i;a.high=Math.round(d/g*h)+i;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(this,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack));$(window).bind("resize",function(){a.resize_window()});$(document).bind("redraw",function(){a.redraw()});this.reset();$(window).trigger("resize")},update_location:function(a,b){this.location_span.text(commatize(a)+" - "+commatize(b));this.nav_input.val(this.chrom+":"+commatize(a)+"-"+c
ommatize(b))},change_chrom:function(e,b,g){var d=this;var f=$.grep(d.chrom_data,function(j,k){return j.chrom===e})[0];if(f===undefined){return}if(e!==d.chrom){d.chrom=e;if(!d.chrom){d.intro_div.show()}else{d.intro_div.hide()}d.chrom_select.val(d.chrom);d.max_high=f.len;d.reset();d.redraw(true);for(var h=0,a=d.tracks.length;h<a;h++){var c=d.tracks[h];if(c.init){c.init()}}}if(b!==undefined&&g!==undefined){d.low=Math.max(b,0);d.high=Math.min(g,d.max_high)}d.reset_overview();d.redraw()},go_to:function(f){var j=this,a,d,b=f.split(":"),h=b[0],i=b[1];if(i!==undefined){try{var g=i.split("-");a=parseInt(g[0].replace(/,/g,""),10);d=parseInt(g[1].replace(/,/g,""),10)}catch(c){return false}}j.change_chrom(h,a,d)},move_delta:function(c){var a=this;var b=a.high-a.low;if(a.low-c<a.max_low){a.low=a.max_low;a.high=a.max_low+b}else{if(a.high-c>a.max_high){a.high=a.max_high;a.low=a.max_high-b}else{a.high-=c;a.low-=c}}a.redraw()},add_track:function(a){a.view=this;a.track_id=this.track_id_counte
r;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1;this.num_tracks+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)];this.num_tracks-=1},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},redraw:function(h){var g=this.high-this.low,f=this.low,b=this.high;if(f<this.max_low){f=this.max_low}if(b>this.max_high){b=this.max_high}if(this.high!==0&&g<this.min_separation){b=f+this.min_separation}this.low=Math.floor(f);this.high=Math.ceil(b);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));var a=(this.low/(this.max_high-this.max
_low)*this.overview_viewport.width())||0;var e=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var j=13;this.overview_box.css({left:a,width:Math.max(j,e)}).show();if(e<j){this.overview_box.css("left",a-(j-e)/2)}if(this.overview_highlight){this.overview_highlight.css({left:a,width:e})}this.update_location(this.low,this.high);if(!h){for(var c=0,d=this.tracks.length;c<d;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(c=0,d=this.label_tracks.length;c<d;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);th
is.high=Math.round(c+a);this.redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.nav_container.height()-45);this.nav_container.width(this.container.width());this.redraw()},reset_overview:function(){this.overview_viewport.find("canvas").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide()}});var Filter=function(b,a,c){this.name=b;this.index=a;this.value=c};var NumberFilter=function(b,a){this.name=b;this.index=a;this.low=-Number.MAX_VALUE;this.high=Number.MAX_VALUE;this.slider_min=Number.MAX_VALUE;this.slider_max=-Number.MAX_VALUE;this.slider=null;this.slider_label=null};$.extend(NumberFilter.prototype,{applies_to:function(a){if(a.length>this.index){return true}return false},keep:function(a){if(!this.applies_to(a)){return true}return(a[this.index]>=this.low&&a[this.index]<=this.high)},update_attrs:function(b
){var a=false;if(!this.applies_to(b)){return a}if(b[this.index]<this.slider_min){this.slider_min=b[this.index];a=true}if(b[this.index]>this.slider_max){this.slider_max=b[this.index];a=false}return a},update_ui_elt:function(){var b=this.slider.slider("option","min"),a=this.slider.slider("option","max");if(this.slider_min<b||this.slider_max>a){this.slider.slider("option","min",this.slider_min);this.slider.slider("option","max",this.slider_max);this.slider.slider("option","values",[this.slider_min,this.slider_max])}}});var get_filters=function(a){var g=[];for(var d=0;d<a.length;d++){var f=a[d];var c=f.name,e=f.type,b=f.index;if(e=="int"||e=="float"){g[d]=new NumberFilter(c,b)}else{g[d]=new Filter(c,b,e)}}return g};var Track=function(b,a,d,c){this.name=b;this.view=a;this.parent_element=d;this.filters=(c!==undefined?get_filters(c):[]);this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track").css("position","relative");i
f(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);if(this.view.editor){this.drag_div=$("<div class='draghandle' />").appendTo(this.header_div)}this.name_div=$("<div class='menubutton popup' />").appendTo(this.header_div);this.name_div.text(this.name);this.name_div.attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase())}this.filtering_div=$("<div class='track-filters'>").appendTo(this.container_div);this.filtering_div.hide();this.filtering_div.bind("drag",function(i){i.stopPropagation()});var b=$("<table class='filters'>").appendTo(this.filtering_div);var c=this;for(var e=0;e<this.filters.length;e++){var a=this.filters[e];var f=$("<tr>").appendTo(b);var g=$("<th class='filter-info'>").appendTo(f);var j=$("<span class='name'>").appendTo(g);j.text(a.name+" ");var d=$("<span class='values'>").appendTo(g);var h=$("<td>").appendTo(f);a.control_element=$("<div id='"+a.name+"-filter-control' style='width: 200
px; position: relative'>").appendTo(h);a.control_element.slider({range:true,min:Number.MAX_VALUE,max:-Number.MIN_VALUE,values:[0,0],slide:function(k,l){var i=l.values;d.text("["+i[0]+"-"+i[1]+"]");a.low=i[0];a.high=i[1];c.draw(true)},change:function(i,k){a.control_element.slider("option","slide").call(a.control_element,i,k)}});a.slider=a.control_element;a.slider_label=d}this.content_div=$("<div class='track-content'>").appendTo(this.container_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.initial_canvas=undefined;a.content_div.css("height","auto");if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"||d.kind==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR);if(d.message){var f=a.view.tracks.indexOf(a);var e=$("
<a href='javascript:void(0);'></a>").attr("id",f+"_error");e.text("Click to view error");$("#"+f+"_error").live("click",function(){show_modal("Trackster Error","<pre>"+d.message+"</pre>",{Close:hide_modal})});a.content_div.append(e)}}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if(d.data!==undefined&&(d.data===null||d.data.length===0)){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){var b=this,j=b.view;if(b.hidden){return}if(b.display_modes!==undefined){if(b.mode_div===undefined){b.mode_div=$("<div class='right-float menubutton popup' />").appendTo(b.header_div);var e=
b.display_modes[0];b.mode=e;b.mode_div.text(e);var c=function(i){b.mode_div.text(i);b.mode=i;b.tile_cache.clear();b.draw()};var a={};for(var f=0,h=b.display_modes.length;f<h;f++){var g=b.display_modes[f];a[g]=function(i){return function(){c(i)}}(g)}make_popupmenu(b.mode_div,a)}else{b.mode_div.hide()}}var d={};d["Set as overview"]=function(){j.overview_viewport.find("canvas").remove();b.is_overview=true;b.set_overview();for(var i in j.tracks){if(j.tracks[i]!==b){j.tracks[i].is_overview=false}}};d["Edit configuration"]=function(){var l=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},i=function(){b.update_options(b.track_id);hide_modal();$(window).unbind("keypress.check_enter_esc")},k=function(m){if((m.keyCode||m.which)===27){l()}else{if((m.keyCode||m.which)===13){i()}}};$(window).bind("keypress.check_enter_esc",k);show_modal("Configure Track",b.gen_options(b.track_id),{Cancel:l,OK:i})};if(b.filters.length>0){d["Show filters"]=function(){var i;if(!b.filter
ing_div.is(":visible")){i="Hide filters";b.filters_visible=true}else{i="Show filters";b.filters_visible=false}$("#"+b.name_div.attr("id")+"-menu").find("li").eq(2).text(i);b.filtering_div.toggle()}}d.Remove=function(){j.remove_track(b);if(j.num_tracks===0){$("#no-tracks").show()}};b.popup_menu=make_popupmenu(b.name_div,d);show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters",false)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(a){var k=this.view.low,g=this.view.high,h=g-k,f=this.view.resolution;var n=$("<div style='position: relative;'></div>"),o=this.content_div.width()/h;this.content_div.append(n);this.max_height=0;var b=Math.floor(k/f/DENSITY);var j={};while((b*DENSITY*f)<g){var l=this.content_div.width()+"_"+o+"_"+b;var e=this.tile_cache.get(l);if(!a&&e){var i=b*DENSITY*f;var d=(i-k)*o;if(this.left_offset){d-=this.left_offset}e.css({left:d});this.show_tile(e,n)}else{this.delayed_draw(this,l,k,g,b,f,n,o,j)}b+=1}var c=this;var m=setInterval(functio
n(){if(obj_length(j)===0){if(c.content_div.children().length>1){c.content_div.children(":first").remove()}for(var p=0;p<c.filters.length;p++){c.filters[p].update_ui_elt()}clearInterval(m)}},50)},delayed_draw:function(c,h,g,e,b,d,i,j,f){var a=setTimeout(function(){if(g<=c.view.high&&e>=c.view.low){var k=c.draw_tile(d,b,i,j);if(k){if(!c.initial_canvas&&!window.G_vmlCanvasManager){c.initial_canvas=$(k).clone();var n=k.get(0).getContext("2d");var l=c.initial_canvas.get(0).getContext("2d");var m=n.getImageData(0,0,n.canvas.width,n.canvas.height);l.putImageData(m,0,0);c.set_overview()}c.tile_cache.set(h,k);c.show_tile(k,i)}}delete f[a]},50);f[a]=true},show_tile:function(a,c){var b=this;c.append(a);b.max_height=Math.max(b.max_height,a.height());b.content_div.css("height",b.max_height+"px");if(a.hasClass(FILTERABLE_CLASS)){show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters");if(b.filters_visible){b.filtering_div.show()}}else{show_hide_popupmenu_options(b.popup_menu,"(Show
|Hide) filters",false);b.filtering_div.hide()}},set_overview:function(){var a=this.view;if(this.initial_canvas&&this.is_overview){a.overview_close.show();a.overview_viewport.append(this.initial_canvas);a.overview_highlight.show().height(this.initial_canvas.height());a.overview_viewport.height(this.initial_canvas.height()+a.overview_box.height())}$(window).trigger("resize")}});var LabelTrack=function(a,b){this.track_type="LabelTrack";this.hidden=true;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(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'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_di
v.append(b)}});var ReferenceTrack=function(a){this.track_type="ReferenceTrack";this.hidden=true;Track.call(this,null,a,a.top_labeltrack);TiledTrack.call(this);this.left_offset=200;this.height_px=12;this.container_div.addClass("reference-track");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,j=f+"_"+b;var e=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(e)}e=$(e);var n=e.get(0).getContext("2d");if(o>PX_PER_CHAR){if(this.dat
a_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o),i=Math.round(o/2);n.fillText(m[h],a+this.left_offset+i,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=80;this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={color:"black",min_value:undefined,max_value:undefined,mode:this.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.ver
tical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");var e=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=e.min;a.prefs.max_value=e.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=e.total_frequency;a.container_div.find(".yaxislabel").remove();var f=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(round_1000(a.prefs.min_value));var d=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(round_1000(a.prefs.max_value));d.css({position:"absolute",top:"22px",left:"10px"});d.prependTo(a.container_div);f.css({position:"absolute",top:a.height_px+11+"px",left:"10px"});f.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=
(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){var h=g.data;c.data_cache.set(e,h);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*o,a=DENSITY*o,w=o+"_"+r;var b=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(b)}b=$(b);if(this.data_cache.get(w)===undefined){this.get_data(o,r);return}var v=this.data_cache.get(w);if(!v){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),j=false,k=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,t=this.total_frequency,d=this.height_px,l=this.mode;n.beginPath();n.fillStyle=this.prefs.co
lor;var u,h,f;if(v.length>1){f=Math.ceil((v[1][0]-v[0][0])*e)}else{f=10}for(var p=0,q=v.length;p<q;p++){u=Math.round((v[p][0]-s)*e);h=v[p][1];if(h===null){if(j&&l==="Filled"){n.lineTo(u,d)}j=false;continue}if(h<k){h=k}else{if(h>g){h=g}}if(l==="Histogram"){h=Math.round(d-(h-k)/m*d);n.fillRect(u,h,f,d-h)}else{if(l==="Intensity"){h=255-Math.floor((h-k)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(u,0,f,d)}else{h=Math.round(d-(h-k)/m*d);if(j){n.lineTo(u,h)}else{j=true;if(l==="Filled"){n.moveTo(u,d);n.lineTo(u,h)}else{n.moveTo(u,h)}}}}}if(l==="Filled"){if(j){n.lineTo(u,d)}n.fill()}else{n.stroke()}c.append(b);return b},gen_options:function(m){var a=$("<div />").addClass("form-row");var e="track_"+m+"_color",b=$("<label />").attr("for",e).text("Color:"),c=$("<input />").attr("id",e).attr("name",e).val(this.prefs.color),h="track_"+m+"_minval",l=$("<label></label>").attr("for",h).text("Min value:"),d=(this.prefs.min_value===undefined?"":this.prefs.min_value),k=$("<input></i
nput>").attr("id",h).val(d),j="track_"+m+"_maxval",g=$("<label></label>").attr("for",j).text("Max value:"),i=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",j).val(i);return a.append(l).append(k).append(g).append(f).append(b).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_color").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!==this.prefs.color){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.color=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,e,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container,e);TiledTrack.call(this);this.height_p
x=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=30;this.default_font="9px Monaco, Lucida Console, monospace";this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.left_offset=200;this.prefs={block_color:"#444",label_color:"black",show_counts:true}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b="initial";this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution,mode:a.mode},function(c){a.mode_div.show();a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.vie
w.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,b,q){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=q;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,y=[],h=0,n=this.view.max_low;var A=[];if(this.inc_slots[a].mode!==q){delete this.inc_slots[a];this.inc_slots[a]={mode:q,w_scale:m};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var v=0,w=g.length;v<w;v++){var f=g[v],l=f[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);A.push(this.inc_slots[a][l])}else{y.push(v)}}for(var v=0,w=y.length;v<w;v++){var f=g[y[v]],l=f[0],r=f[1],c=f[2],p=f[3],d=Math.floor((r-n)*m),e=Math.ceil((c-n)*m);if(p!==undefined&&!b){var s=CONTEXT.measureText(p).width;if(d-s<0){e+=s}else{d-=s}}var u=0;while(u<=MAX_FEATURE_DEPTH){var o=true;if(this.s_e_by_tile[a][u]!==undefined){for(var t=0,z=this.s_e_by_tile[a][u].length;t<z;t++){var x=this.s_e_
by_tile[a][u][t];if(e>x[0]&&d<x[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][u]===undefined){this.s_e_by_tile[a][u]=[]}this.s_e_by_tile[a][u].push([d,e]);this.inc_slots[a][l]=u;h=Math.max(h,u);break}u++}}return h},rect_or_text:function(r,l,t,b,q,f,i,e){r.textAlign="center";var k=0,p=Math.round(l/2);for(var m=0,s=i.length;m<s;m++){var j=i[m],d="MIDNSHP"[j[0]],n=j[1];if(d==="H"||d==="S"){k-=n}var g=q+k,w=Math.floor(Math.max(0,(g-t)*l)),h=Math.floor(Math.max(0,(g+n-t)*l));switch(d){case"S":case"H":case"M":var o=f.slice(k,n);if((this.mode==="Pack"||this.mode==="Auto")&&f!==undefined&&l>PX_PER_CHAR){r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+1,h-w,9);r.fillStyle=CONNECTOR_COLOR;for(var u=0,a=o.length;u<a;u++){if(g+u>=t&&g+u<=b){var v=Math.floor(Math.max(0,(g+u-t)*l));r.fillText(o[u],v+this.left_offset+p,e+9)}}}else{r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+4,h-w,3)}break;case"N":r.fillStyle=CONNECTOR_COLOR;r.fillRect(w+this.lef
t_offset,e+5,h-w,1);break;case"D":r.fillStyle="red";r.fillRect(w+this.left_offset,e+4,h-w,3);break;case"P":case"I":break}k+=n}},draw_tile:function(ag,o,s,av){var N=o*DENSITY*ag,al=(o+1)*DENSITY*ag,M=al-N;var an=(!this.initial_canvas?"initial":N+"_"+al);var I=this.data_cache.get(an);var e;if(I===undefined||(this.mode!=="Auto"&&I.dataset_type==="summary_tree")){this.data_queue[[N,al]]=true;this.get_data(N,al);return}var a=Math.ceil(M*av),ai=this.prefs.label_color,l=this.prefs.block_color,r=this.mode,z=25,ae=(r==="Squish")||(r==="Dense")&&(r!=="Pack")||(r==="Auto"&&(I.extra_info==="no_detail")),W=this.left_offset,au,D,aw;var q=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(q)}q=$(q);if(I.dataset_type==="summary_tree"){D=this.summary_draw_height}else{if(r==="Dense"){D=z;aw=10}else{aw=(ae?this.vertical_nodetail_px:this.vertical_detail_px);var A=(av<0.0001?1/this.view.zoom_res:av);D=this.incremental_slots(A,I.data,ae,r)*aw+z;au=this.i
nc_slots[A]}}q.css({position:"absolute",top:0,left:(N-this.view.low)*av-W});q.get(0).width=a+W;q.get(0).height=D;s.parent().css("height",Math.max(this.height_px,D)+"px");var J=q.get(0).getContext("2d");J.fillStyle=l;J.font=this.default_font;J.textAlign="right";this.container_div.find(".yaxislabel").remove();if(I.dataset_type=="summary_tree"){var Y=I.data,L=I.max,b=Math.ceil(I.delta*av);var p=$("<div />").addClass("yaxislabel").text(L);p.css({position:"absolute",top:"22px",left:"10px"});p.prependTo(this.container_div);for(var ap=0,H=Y.length;ap<H;ap++){var aa=Math.floor((Y[ap][0]-N)*av);var Z=Y[ap][1];if(!Z){continue}var am=Z/L*this.summary_draw_height;J.fillStyle="black";J.fillRect(aa+W,this.summary_draw_height-am,b,am);if(this.prefs.show_counts&&J.measureText(Z).width<b){J.fillStyle="#bbb";J.textAlign="center";J.fillText(Z,aa+W+(b/2),this.summary_draw_height-5)}}e="Summary";s.append(q);return q}if(I.message){q.css({border:"solid red","border-width":"2px 2px 2px 0px"});J.fil
lStyle="red";J.textAlign="left";J.fillText(I.message,100+W,aw)}var ad=false;if(I.data){ad=true;for(var ar=0;ar<this.filters.length;ar++){if(!this.filters[ar].applies_to(I.data[0])){ad=false}}}if(ad){q.addClass(FILTERABLE_CLASS)}var at=I.data;var ao=0;for(var ap=0,H=at.length;ap<H;ap++){var S=at[ap],R=S[0],aq=S[1],ac=S[2],O=S[3];if(au[R]===undefined){continue}var ab=false;var U;for(var ar=0;ar<this.filters.length;ar++){U=this.filters[ar];U.update_attrs(S);if(!U.keep(S)){ab=true;break}}if(ab){continue}if(aq<=al&&ac>=N){var af=Math.floor(Math.max(0,(aq-N)*av)),K=Math.ceil(Math.min(a,Math.max(0,(ac-N)*av))),X=(r==="Dense"?1:(1+au[R]))*aw;var G,aj,P=null,ax=null;if(I.dataset_type==="bai"){var v=S[4];J.fillStyle=l;if(S[5] instanceof Array){var E=Math.floor(Math.max(0,(S[5][0]-N)*av)),Q=Math.ceil(Math.min(a,Math.max(0,(S[5][1]-N)*av))),C=Math.floor(Math.max(0,(S[6][0]-N)*av)),w=Math.ceil(Math.min(a,Math.max(0,(S[6][1]-N)*av)));if(S[5][1]>=N&&S[5][0]<=al){this.rect_or_text(J,av,N,al
,S[5][0],S[5][2],v,X)}if(S[6][1]>=N&&S[6][0]<=al){this.rect_or_text(J,av,N,al,S[6][0],S[6][2],v,X)}if(C>Q){J.fillStyle=CONNECTOR_COLOR;J.fillRect(Q+W,X+5,C-Q,1)}}else{J.fillStyle=l;this.rect_or_text(J,av,N,al,aq,O,v,X)}if(r!=="Dense"&&!ae&&aq>N){J.fillStyle=this.prefs.label_color;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(R,K+2+W,X+8)}else{J.textAlign="right";J.fillText(R,af-2+W,X+8)}J.fillStyle=l}}else{if(I.dataset_type==="interval_index"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var F=S[4],V=S[5],ah=S[6],h=S[7];if(V&&ah){P=Math.floor(Math.max(0,(V-N)*av));ax=Math.ceil(Math.min(a,Math.max(0,(ah-N)*av)))}if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}if(h){if(F){if(F=="+"){J.fillStyle=RIGHT_STRAND}else{if(F=="-"){J.fillStyle=LEFT_STRAND}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}for(var an
=0,g=h.length;an<g;an++){var u=h[an],d=Math.floor(Math.max(0,(u[0]-N)*av)),T=Math.ceil(Math.min(a,Math.max((u[1]-N)*av)));if(d>T){continue}G=5;aj=3;J.fillRect(d+W,X+aj,T-d,G);if(P!==undefined&&!(d>ax||T<P)){G=9;aj=1;var ak=Math.max(d,P),B=Math.min(T,ax);J.fillRect(ak+W,X+aj,B-ak,G)}}}else{G=9;aj=1;J.fillRect(af+W,X+aj,K-af,G);if(S.strand){if(S.strand=="+"){J.fillStyle=RIGHT_STRAND_INV}else{if(S.strand=="-"){J.fillStyle=LEFT_STRAND_INV}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}}}}else{if(I.dataset_type==="vcf"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var t=S[4],n=S[5],c=S[6];G=9;aj=1;J.fillRect(af+W,X,K-af,G);if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}var m=t+" / "+n;if(aq>N&&J.measureText(m).width<(K-af)){J.fillStyle="white";J.textAlign="center";J.fillText(m,W+af+(K-af)/2,X+8);J.fillStyle=l}}}}}ao++}}return q
},gen_options:function(i){var a=$("<div />").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label />").attr("for",e).text("Block color:"),l=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label />").attr("for",j).text("Text color:"),h=$("<input />").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(k).append(l).append(g).append(h).append(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_
counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,e,c){FeatureTrack.call(this,d,b,a,e,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
1
0

20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1288760547 14400
# Node ID 49f0e8441a4da6b1ec03250448ab84854f07aa77
# Parent 37477f7d10e7407186bd189fdec9e114e4b8f3d2
sample tracking
- new file browser for selecting datasets in the sequencer
--- a/templates/admin/requests/get_data.mako
+++ b/templates/admin/requests/get_data.mako
@@ -1,71 +1,71 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
+${h.js( "ui.core", "jquery.cookie" )}
+<link href='/static/june_2007_style/blue/dynatree_skin/ui.dynatree.css' rel='stylesheet' type='text/css'>
+${h.js( "jquery.dynatree" )}
<script type="text/javascript">
- $(document).ready(function(){
- //hide the all of the element with class msg_body
- $(".msg_body").hide();
- //toggle the component with class msg_body
- $(".msg_head").click(function(){
- $(this).next(".msg_body").slideToggle(450);
- });
+ $(function(){
+ $("#tree").ajaxComplete(function(event, XMLHttpRequest, ajaxOptions) {
+ _log("debug", "ajaxComplete: %o", this); // dom element listening
+ });
+ // --- Initialize sample trees
+ $("#tree").dynatree({
+ title: "${request.type.datatx_info['data_dir']}",
+ rootVisible: true,
+ minExpandLevel: 0, // 1: root node is not collapsible
+ persist: false,
+ checkbox: true,
+ selectMode: 3,
+ onPostInit: function(isReloading, isError) {
+// alert("reloading: "+isReloading+", error:"+isError);
+ logMsg("onPostInit(%o, %o) - %o", isReloading, isError, this);
+ // Re-fire onActivate, so the text is updated
+ this.reactivate();
+ },
+ fx: { height: "toggle", duration: 200 },
+ // initAjax is hard to fake, so we pass the children as object array:
+ initAjax: {url: "${h.url_for( controller='requests_admin', action='open_folder' )}",
+ dataType: "json",
+ data: { id: "${request.id}", key: "${request.type.datatx_info['data_dir']}" },
+ },
+ onLazyRead: function(dtnode){
+ dtnode.appendAjax({
+ url: "${h.url_for( controller='requests_admin', action='open_folder' )}",
+ dataType: "json",
+ data: { id: "${request.id}", key: dtnode.data.key },
+ });
+ },
+ onSelect: function(select, dtnode) {
+ // Display list of selected nodes
+ var selNodes = dtnode.tree.getSelectedNodes();
+ // convert to title/key array
+ var selKeys = $.map(selNodes, function(node){
+ return node.data.key;
+ });
+ document.get_data.selected_files.value = selKeys.join(",")
+ },
+ onActivate: function(dtnode) {
+ var cell = $("#file_details");
+ var selected_value = dtnode.data.key
+ if(selected_value.charAt(selected_value.length-1) != '/') {
+ // Make ajax call
+ $.ajax( {
+ type: "POST",
+ url: "${h.url_for( controller='requests_admin', action='get_file_details' )}",
+ dataType: "json",
+ data: { id: "${request.id}", folder_path: dtnode.data.key },
+ success : function ( data ) {
+ cell.html( '<label>'+data+'</label>' )
+ }
+ });
+ } else {
+ cell.html( '' )
+ }
+ },
+ });
});
- function display_file_details(request_id, folder_path)
- {
- var w = document.get_data.files_list.selectedIndex;
- var selected_value = document.get_data.files_list.options[w].value;
- var cell = $("#file_details");
- if(selected_value.charAt(selected_value.length-1) != '/')
- {
- // Make ajax call
- $.ajax( {
- type: "POST",
- url: "${h.url_for( controller='requests_admin', action='get_file_details' )}",
- dataType: "json",
- data: { id: request_id, folder_path: document.get_data.folder_path.value + selected_value },
- success : function ( data ) {
- cell.html( '<label>'+data+'</label>' )
- }
- });
- }
- else
- {
- cell.html( '' )
- }
- }
-
- function open_folder1( request_id, folder_path )
- {
- var w = document.get_data.files_list.selectedIndex;
- var selected_value = document.get_data.files_list.options[w].value;
- var cell = $("#file_details");
- if(selected_value.charAt(selected_value.length-1) == '/')
- {
- document.get_data.folder_path.value = document.get_data.folder_path.value+selected_value
- // Make ajax call
- $.ajax( {
- type: "POST",
- url: "${h.url_for( controller='requests_admin', action='open_folder' )}",
- dataType: "json",
- data: { id: request_id, folder_path: document.get_data.folder_path.value },
- success : function ( data ) {
- document.get_data.files_list.options.length = 0
- for(i=0; i<data.length; i++)
- {
- var newOpt = new Option(data[i], data[i]);
- document.get_data.files_list.options[i] = newOpt;
- }
- //cell.html( '<label>'+data+'</label>' )
-
- }
- });
- }
- else
- {
- cell.html( '' )
- }
- }
</script><br/>
@@ -92,18 +92,16 @@
<div class="toolParamHelp" style="clear: both;">
Select the sample with which you want to associate the datasets
</div>
- <br/>
- <label>Folder path on the sequencer:</label>
- <input type="text" name="folder_path" value="${folder_path}" size="100"/>
- <input type="submit" name="browse_button" value="List contents"/>
- <input type="submit" name="folder_up" value="Up"/></div>
- <div class="form-row">
- <select name="files_list" id="files_list" style="max-width: 60%; width: 98%; height: 150px; font-size: 100%;" ondblclick="open_folder1(${request.id}, '${folder_path}')" onChange="display_file_details(${request.id}, '${folder_path}')" multiple>
- %for index, f in enumerate( files ):
- <option value="${f}">${f}</option>
- %endfor
- </select>
+ <div class="form-row" >
+ <label>Select dataset files in the sequencer:</label>
+ <div id="tree" >
+ Loading...
+ </div>
+ <input id="selected_files" name="selected_files" type="hidden" size=40"/>
+ <div class="toolParamHelp" style="clear: both;">
+ To select a folder, select all the individual files in that folder.
+ </div></div><div class="form-row"><div id="file_details" class="toolParamHelp" style="clear: both;background-color:#FAFAFA;"></div>
Binary file static/june_2007_style/blue/dynatree_skin/rbUnchecked_hover.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/cbChecked.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltError.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/cbIntermediate.gif has changed
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -316,7 +316,6 @@ class RequestsAdmin( BaseController, Use
dict( controller='requests_admin',
action='get_data',
request_id=request_id,
- folder_path=sample.request.type.datatx_info[ 'data_dir' ],
sample_id=sample_id ) ),
grids.GridAction( "Browse target data library",
dict( controller='library_common',
@@ -383,67 +382,61 @@ class RequestsAdmin( BaseController, Use
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', request_id )
- selected_files = util.listify( params.get( 'files_list', [] ) )
- folder_path = util.restore_text( params.get( 'folder_path', request.type.datatx_info[ 'data_dir' ] ) )
+ selected_files = util.restore_text( params.get( 'selected_files', '' ) )
+ if len( selected_files ):
+ selected_files = selected_files.split(',')
+ else:
+ selected_files = []
selected_sample_id = kwd.get( 'sample_id', 'none' )
sample_id_select_field = self.__build_sample_id_select_field( trans, request, selected_sample_id )
# The __get_files() method redirects here with a status of 'error' and a message if there
# was a problem retrieving the files.
- if folder_path and status != 'error':
- folder_path = self.__check_path( folder_path )
- if params.get( 'folder_up', False ):
- if folder_path[-1] == os.sep:
- folder_path = os.path.dirname( folder_path[:-1] )
- folder_path = self.__check_path( folder_path )
- elif params.get( 'open_folder', False ):
- if len(selected_files) == 1:
- folder_path = os.path.join(folder_path, selected_files[0])
- folder_path = self.__check_path( folder_path )
- elif params.get( 'select_show_datasets_button', False ) or params.get( 'select_more_button', False ):
- # get the sample these datasets are associated with
- try:
- sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( selected_sample_id ) )
- except:
- return invalid_id_redirect( trans, 'requests_admin', selected_sample_id )
- if sample in sample.request.samples_without_library_destinations:
- # Display an error if a sample has been selected that
- # has not yet been associated with a destination library.
- status = 'error'
- message = 'Select a sample with associated data library and folder before selecting the datasets.'
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='get_data',
- request_id=request_id,
- folder_path=folder_path,
- status=status,
- message=message ) )
- # Save the sample datasets
- sample_dataset_file_names = self.__save_sample_datasets( trans, sample, selected_files, folder_path )
- if sample_dataset_file_names:
- message = 'Datasets (%s) have been selected for sample (%s)' % \
- ( str( sample_dataset_file_names )[1:-1].replace( "'", "" ), sample.name )
- if params.get( 'select_show_datasets_button', False ):
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='manage_datasets',
- request_id=request_id,
- sample_id=selected_sample_id,
- message=message,
- status=status ) )
- else: # 'select_more_button' was clicked
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='get_data',
- request_id=request_id,
- folder_path=folder_path,
- sample_id=sample.id,
- message=message,
- status=status ) )
- # Get the filenames from the remote host
- files = self.__get_files( trans, request, folder_path )
+ if params.get( 'select_show_datasets_button', False ) or params.get( 'select_more_button', False ):
+ # get the sample these datasets are associated with
+ try:
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( selected_sample_id ) )
+ except:
+ message = 'Select a sample before selecting its associated datasets.'
+ return trans.fill_template( '/admin/requests/get_data.mako',
+ cntrller='requests_admin',
+ request=request,
+ sample_id_select_field=sample_id_select_field,
+ status='error',
+ message=message )
+ if sample in sample.request.samples_without_library_destinations:
+ # Display an error if a sample has been selected that
+ # has not yet been associated with a destination library.
+ status = 'error'
+ message = 'Select a sample with associated data library and folder before selecting the datasets.'
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='get_data',
+ request_id=request_id,
+ sample_id=sample.id,
+ status=status,
+ message=message ) )
+ # Save the sample datasets
+ sample_dataset_file_names = self.__save_sample_datasets( trans, sample, selected_files )
+ if sample_dataset_file_names:
+ message = 'Datasets (%s) have been selected for sample (%s)' % \
+ ( str( sample_dataset_file_names )[1:-1].replace( "'", "" ), sample.name )
+ if params.get( 'select_show_datasets_button', False ):
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='manage_datasets',
+ request_id=request_id,
+ sample_id=selected_sample_id,
+ message=message,
+ status=status ) )
+ else: # 'select_more_button' was clicked
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='get_data',
+ request_id=request_id,
+ sample_id=sample.id,
+ message=message,
+ status=status ) )
return trans.fill_template( '/admin/requests/get_data.mako',
cntrller='requests_admin',
request=request,
sample_id_select_field=sample_id_select_field,
- files=files,
- folder_path=folder_path,
status=status,
message=message )
@web.json
@@ -464,15 +457,27 @@ class RequestsAdmin( BaseController, Use
timeout=10 )
return unicode( output.replace( '\n', '<br/>' ) )
@web.json
- def open_folder( self, trans, id, folder_path ):
- def print_ticks( d ):
- # pexpect timeout method
- pass
+ def open_folder( self, trans, id, key ):
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
request = trans.sa_session.query( trans.model.Request ).get( int( id ) )
- return self.__get_files( trans, request, folder_path )
+ folder_path = key
+ files_list = self.__get_files( trans, request, folder_path )
+ folder_contents = []
+ for filename in files_list:
+ is_folder = False
+ if filename[-1] == os.sep:
+ is_folder = True
+ full_path = os.path.join(folder_path, filename)
+ node = {"title": filename,
+ "isFolder": is_folder,
+ "isLazy": is_folder,
+ "tooltip": full_path,
+ "key": full_path
+ }
+ folder_contents.append(node)
+ return folder_contents
def __get_files( self, trans, request, folder_path ):
# Retrieves the filenames to be transferred from the remote host.
ok = True
@@ -496,7 +501,6 @@ class RequestsAdmin( BaseController, Use
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='get_data',
request_id=trans.security.encode_id( request.id ),
- folder_path=folder_path,
status=status,
message=message ) )
def __check_path( self, a_path ):
@@ -504,20 +508,13 @@ class RequestsAdmin( BaseController, Use
if a_path and not a_path.endswith( os.sep ):
a_path += os.sep
return a_path
- def __save_sample_datasets( self, trans, sample, selected_files, folder_path ):
+ def __save_sample_datasets( self, trans, sample, selected_files ):
sample_dataset_file_names = []
if selected_files:
- for f in selected_files:
- filepath = os.path.join( folder_path, f )
- if f[-1] == os.sep:
- # FIXME: The selected item is a folder so transfer all the folder contents
- request_id = trans.security.ecnode_id( sample.request.id )
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='get_data',
- request_id=request_id,
- folder_path=folder_path,
- open_folder=True ) )
- else:
+ for filepath in selected_files:
+ # FIXME: handle folder selection
+ # ignore folders for now
+ if filepath[-1] != os.sep:
name = self.__dataset_name( sample, filepath.split( '/' )[-1] )
sample_dataset = trans.model.SampleDataset( sample=sample,
file_path=filepath,
Binary file static/june_2007_style/blue/dynatree_skin/ltP_nes.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltFld.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/rbIntermediate.gif has changed
--- /dev/null
+++ b/static/scripts/packed/ui.core.js
@@ -0,0 +1,1 @@
+jQuery.ui||(function(c){var i=c.fn.remove,d=c.browser.mozilla&&(parseFloat(c.browser.version)<1.9);c.ui={version:"1.7.1",plugin:{add:function(k,l,n){var m=c.ui[k].prototype;for(var j in n){m.plugins[j]=m.plugins[j]||[];m.plugins[j].push([l,n[j]])}},call:function(j,l,k){var n=j.plugins[l];if(!n||!j.element[0].parentNode){return}for(var m=0;m<n.length;m++){if(j.options[n[m][0]]){n[m][1].apply(j.element,k)}}}},contains:function(k,j){return document.compareDocumentPosition?k.compareDocumentPosition(j)&16:k!==j&&k.contains(j)},hasScroll:function(m,k){if(c(m).css("overflow")=="hidden"){return false}var j=(k&&k=="left")?"scrollLeft":"scrollTop",l=false;if(m[j]>0){return true}m[j]=1;l=(m[j]>0);m[j]=0;return l},isOverAxis:function(k,j,l){return(k>j)&&(k<(j+l))},isOver:function(o,k,n,m,j,l){return c.ui.isOverAxis(o,n,j)&&c.ui.isOverAxis(k,m,l)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NU
MPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(d){var f=c.attr,e=c.fn.removeAttr,h="http://www.w3.org/2005/07/aaa",a=/^aria-/,b=/^wairole:/;c.attr=function(k,j,l){var m=l!==undefined;return(j=="role"?(m?f.call(this,k,j,"wairole:"+l):(f.apply(this,arguments)||"").replace(b,"")):(a.test(j)?(m?k.setAttributeNS(h,j.replace(a,"aaa:"),l):f.call(this,k,j.replace(a,"aaa:"))):f.apply(this,arguments)))};c.fn.removeAttr=function(j){return(a.test(j)?this.each(function(){this.removeAttributeNS(h,j.replace(a,""))}):e.call(this,j))}}c.fn.extend({remove:function(){c("*",this).add(this).each(function(){c(this).triggerHandler("remove")});return i.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","no
ne").bind("selectstart.ui",function(){return false})},scrollParent:function(){var j;if((c.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){j=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(c.curCSS(this,"position",1))&&(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}else{j=this.parents().filter(function(){return(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!j.length?c(document):j}});c.extend(c.expr[":"],{data:function(l,k,j){return !!c.data(l,j[3])},focusable:function(k){var l=k.nodeName.toLowerCase(),j=c.attr(k,"tabindex");return(/input|select|textarea|button|object/.test(l)?!k.disabled:"a"==l||"area"==l?k.href||!isNaN(j):!isNaN(j))&&!c(k)["area"==l?"parents":"closest"](":hidden").length},tabbable:function
(k){var j=c.attr(k,"tabindex");return(isNaN(j)||j>=0)&&c(k).is(":focusable")}});function g(m,n,o,l){function k(q){var p=c[m][n][q]||[];return(typeof p=="string"?p.split(/,?\s+/):p)}var j=k("getter");if(l.length==1&&typeof l[0]=="string"){j=j.concat(k("getterSetter"))}return(c.inArray(o,j)!=-1)}c.widget=function(k,j){var l=k.split(".")[0];k=k.split(".")[1];c.fn[k]=function(p){var n=(typeof p=="string"),o=Array.prototype.slice.call(arguments,1);if(n&&p.substring(0,1)=="_"){return this}if(n&&g(l,k,p,o)){var m=c.data(this[0],k);return(m?m[p].apply(m,o):undefined)}return this.each(function(){var q=c.data(this,k);(!q&&!n&&c.data(this,k,new c[l][k](this,p))._init());(q&&n&&c.isFunction(q[p])&&q[p].apply(q,o))})};c[l]=c[l]||{};c[l][k]=function(o,n){var m=this;this.namespace=l;this.widgetName=k;this.widgetEventPrefix=c[l][k].eventPrefix||k;this.widgetBaseClass=l+"-"+k;this.options=c.extend({},c.widget.defaults,c[l][k].defaults,c.metadata&&c.metadata.get(o)[k],n);this.element=c(o).bin
d("setData."+k,function(q,p,r){if(q.target==o){return m._setData(p,r)}}).bind("getData."+k,function(q,p){if(q.target==o){return m._getData(p)}}).bind("remove",function(){return m.destroy()})};c[l][k].prototype=c.extend({},c.widget.prototype,j);c[l][k].getterSetter="option"};c.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(l,m){var k=l,j=this;if(typeof l=="string"){if(m===undefined){return this._getData(l)}k={};k[l]=m}c.each(k,function(n,o){j._setData(n,o)})},_getData:function(j){return this.options[j]},_setData:function(j,k){this.options[j]=k;if(j=="disabled"){this.element[k?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",k)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:f
unction(l,m,n){var p=this.options[l],j=(l==this.widgetEventPrefix?l:this.widgetEventPrefix+l);m=c.Event(m);m.type=j;if(m.originalEvent){for(var k=c.event.props.length,o;k;){o=c.event.props[--k];m[o]=m.originalEvent[o]}}this.element.trigger(m,n);return !(c.isFunction(p)&&p.call(this.element[0],m,n)===false||m.isDefaultPrevented())}};c.widget.defaults={disabled:false};c.ui.mouse={_mouseInit:function(){var j=this;this.element.bind("mousedown."+this.widgetName,function(k){return j._mouseDown(k)}).bind("click."+this.widgetName,function(k){if(j._preventClickEvent){j._preventClickEvent=false;k.stopImmediatePropagation();return false}});if(c.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);(c.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(l){l.originalEvent=l.originalEvent||{};if(l.originalE
vent.mouseHandled){return}(this._mouseStarted&&this._mouseUp(l));this._mouseDownEvent=l;var k=this,m=(l.which==1),j=(typeof this.options.cancel=="string"?c(l.target).parents().add(l.target).filter(this.options.cancel).length:false);if(!m||j||!this._mouseCapture(l)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){k.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(l)&&this._mouseDelayMet(l)){this._mouseStarted=(this._mouseStart(l)!==false);if(!this._mouseStarted){l.preventDefault();return true}}this._mouseMoveDelegate=function(n){return k._mouseMove(n)};this._mouseUpDelegate=function(n){return k._mouseUp(n)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);(c.browser.safari||l.preventDefault());l.originalEvent.mouseHandled=true;return true},_mouseMove:function(j){if(c.browser.msie&&!j.button){return this._mouseUp(j
)}if(this._mouseStarted){this._mouseDrag(j);return j.preventDefault()}if(this._mouseDistanceMet(j)&&this._mouseDelayMet(j)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,j)!==false);(this._mouseStarted?this._mouseDrag(j):this._mouseUp(j))}return !this._mouseStarted},_mouseUp:function(j){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=(j.target==this._mouseDownEvent.target);this._mouseStop(j)}return false},_mouseDistanceMet:function(j){return(Math.max(Math.abs(this._mouseDownEvent.pageX-j.pageX),Math.abs(this._mouseDownEvent.pageY-j.pageY))>=this.options.distance)},_mouseDelayMet:function(j){return this.mouseDelayMet},_mouseStart:function(j){},_mouseDrag:function(j){},_mouseStop:function(j){},_mouseCapture:function(j){return true}};c.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);
Binary file static/june_2007_style/blue/dynatree_skin/ltP_ne.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/cbIntermediate_hover.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltDoc.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/rbUnchecked.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltWait.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/cbChecked_hover.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/customDoc1.gif has changed
--- /dev/null
+++ b/static/scripts/ui.core.js
@@ -0,0 +1,519 @@
+/*
+ * jQuery UI 1.7.1
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI
+ */
+;jQuery.ui || (function($) {
+
+var _remove = $.fn.remove,
+ isFF2 = $.browser.mozilla && (parseFloat($.browser.version) < 1.9);
+
+//Helper functions and ui object
+$.ui = {
+ version: "1.7.1",
+
+ // $.ui.plugin is deprecated. Use the proxy pattern instead.
+ plugin: {
+ add: function(module, option, set) {
+ var proto = $.ui[module].prototype;
+ for(var i in set) {
+ proto.plugins[i] = proto.plugins[i] || [];
+ proto.plugins[i].push([option, set[i]]);
+ }
+ },
+ call: function(instance, name, args) {
+ var set = instance.plugins[name];
+ if(!set || !instance.element[0].parentNode) { return; }
+
+ for (var i = 0; i < set.length; i++) {
+ if (instance.options[set[i][0]]) {
+ set[i][1].apply(instance.element, args);
+ }
+ }
+ }
+ },
+
+ contains: function(a, b) {
+ return document.compareDocumentPosition
+ ? a.compareDocumentPosition(b) & 16
+ : a !== b && a.contains(b);
+ },
+
+ hasScroll: function(el, a) {
+
+ //If overflow is hidden, the element might have extra content, but the user wants to hide it
+ if ($(el).css('overflow') == 'hidden') { return false; }
+
+ var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop',
+ has = false;
+
+ if (el[scroll] > 0) { return true; }
+
+ // TODO: determine which cases actually cause this to happen
+ // if the element doesn't have the scroll set, see if it's possible to
+ // set the scroll
+ el[scroll] = 1;
+ has = (el[scroll] > 0);
+ el[scroll] = 0;
+ return has;
+ },
+
+ isOverAxis: function(x, reference, size) {
+ //Determines when x coordinate is over "b" element axis
+ return (x > reference) && (x < (reference + size));
+ },
+
+ isOver: function(y, x, top, left, height, width) {
+ //Determines when x, y coordinates is over "b" element
+ return $.ui.isOverAxis(y, top, height) && $.ui.isOverAxis(x, left, width);
+ },
+
+ keyCode: {
+ BACKSPACE: 8,
+ CAPS_LOCK: 20,
+ COMMA: 188,
+ CONTROL: 17,
+ DELETE: 46,
+ DOWN: 40,
+ END: 35,
+ ENTER: 13,
+ ESCAPE: 27,
+ HOME: 36,
+ INSERT: 45,
+ LEFT: 37,
+ NUMPAD_ADD: 107,
+ NUMPAD_DECIMAL: 110,
+ NUMPAD_DIVIDE: 111,
+ NUMPAD_ENTER: 108,
+ NUMPAD_MULTIPLY: 106,
+ NUMPAD_SUBTRACT: 109,
+ PAGE_DOWN: 34,
+ PAGE_UP: 33,
+ PERIOD: 190,
+ RIGHT: 39,
+ SHIFT: 16,
+ SPACE: 32,
+ TAB: 9,
+ UP: 38
+ }
+};
+
+// WAI-ARIA normalization
+if (isFF2) {
+ var attr = $.attr,
+ removeAttr = $.fn.removeAttr,
+ ariaNS = "http://www.w3.org/2005/07/aaa",
+ ariaState = /^aria-/,
+ ariaRole = /^wairole:/;
+
+ $.attr = function(elem, name, value) {
+ var set = value !== undefined;
+
+ return (name == 'role'
+ ? (set
+ ? attr.call(this, elem, name, "wairole:" + value)
+ : (attr.apply(this, arguments) || "").replace(ariaRole, ""))
+ : (ariaState.test(name)
+ ? (set
+ ? elem.setAttributeNS(ariaNS,
+ name.replace(ariaState, "aaa:"), value)
+ : attr.call(this, elem, name.replace(ariaState, "aaa:")))
+ : attr.apply(this, arguments)));
+ };
+
+ $.fn.removeAttr = function(name) {
+ return (ariaState.test(name)
+ ? this.each(function() {
+ this.removeAttributeNS(ariaNS, name.replace(ariaState, ""));
+ }) : removeAttr.call(this, name));
+ };
+}
+
+//jQuery plugins
+$.fn.extend({
+ remove: function() {
+ // Safari has a native remove event which actually removes DOM elements,
+ // so we have to use triggerHandler instead of trigger (#3037).
+ $("*", this).add(this).each(function() {
+ $(this).triggerHandler("remove");
+ });
+ return _remove.apply(this, arguments );
+ },
+
+ enableSelection: function() {
+ return this
+ .attr('unselectable', 'off')
+ .css('MozUserSelect', '')
+ .unbind('selectstart.ui');
+ },
+
+ disableSelection: function() {
+ return this
+ .attr('unselectable', 'on')
+ .css('MozUserSelect', 'none')
+ .bind('selectstart.ui', function() { return false; });
+ },
+
+ scrollParent: function() {
+ var scrollParent;
+ if(($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
+ scrollParent = this.parents().filter(function() {
+ return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+ }).eq(0);
+ } else {
+ scrollParent = this.parents().filter(function() {
+ return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+ }).eq(0);
+ }
+
+ return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
+ }
+});
+
+
+//Additional selectors
+$.extend($.expr[':'], {
+ data: function(elem, i, match) {
+ return !!$.data(elem, match[3]);
+ },
+
+ focusable: function(element) {
+ var nodeName = element.nodeName.toLowerCase(),
+ tabIndex = $.attr(element, 'tabindex');
+ return (/input|select|textarea|button|object/.test(nodeName)
+ ? !element.disabled
+ : 'a' == nodeName || 'area' == nodeName
+ ? element.href || !isNaN(tabIndex)
+ : !isNaN(tabIndex))
+ // the element and all of its ancestors must be visible
+ // the browser may report that the area is hidden
+ && !$(element)['area' == nodeName ? 'parents' : 'closest'](':hidden').length;
+ },
+
+ tabbable: function(element) {
+ var tabIndex = $.attr(element, 'tabindex');
+ return (isNaN(tabIndex) || tabIndex >= 0) && $(element).is(':focusable');
+ }
+});
+
+
+// $.widget is a factory to create jQuery plugins
+// taking some boilerplate code out of the plugin code
+function getter(namespace, plugin, method, args) {
+ function getMethods(type) {
+ var methods = $[namespace][plugin][type] || [];
+ return (typeof methods == 'string' ? methods.split(/,?\s+/) : methods);
+ }
+
+ var methods = getMethods('getter');
+ if (args.length == 1 && typeof args[0] == 'string') {
+ methods = methods.concat(getMethods('getterSetter'));
+ }
+ return ($.inArray(method, methods) != -1);
+}
+
+$.widget = function(name, prototype) {
+ var namespace = name.split(".")[0];
+ name = name.split(".")[1];
+
+ // create plugin method
+ $.fn[name] = function(options) {
+ var isMethodCall = (typeof options == 'string'),
+ args = Array.prototype.slice.call(arguments, 1);
+
+ // prevent calls to internal methods
+ if (isMethodCall && options.substring(0, 1) == '_') {
+ return this;
+ }
+
+ // handle getter methods
+ if (isMethodCall && getter(namespace, name, options, args)) {
+ var instance = $.data(this[0], name);
+ return (instance ? instance[options].apply(instance, args)
+ : undefined);
+ }
+
+ // handle initialization and non-getter methods
+ return this.each(function() {
+ var instance = $.data(this, name);
+
+ // constructor
+ (!instance && !isMethodCall &&
+ $.data(this, name, new $[namespace][name](this, options))._init());
+
+ // method call
+ (instance && isMethodCall && $.isFunction(instance[options]) &&
+ instance[options].apply(instance, args));
+ });
+ };
+
+ // create widget constructor
+ $[namespace] = $[namespace] || {};
+ $[namespace][name] = function(element, options) {
+ var self = this;
+
+ this.namespace = namespace;
+ this.widgetName = name;
+ this.widgetEventPrefix = $[namespace][name].eventPrefix || name;
+ this.widgetBaseClass = namespace + '-' + name;
+
+ this.options = $.extend({},
+ $.widget.defaults,
+ $[namespace][name].defaults,
+ $.metadata && $.metadata.get(element)[name],
+ options);
+
+ this.element = $(element)
+ .bind('setData.' + name, function(event, key, value) {
+ if (event.target == element) {
+ return self._setData(key, value);
+ }
+ })
+ .bind('getData.' + name, function(event, key) {
+ if (event.target == element) {
+ return self._getData(key);
+ }
+ })
+ .bind('remove', function() {
+ return self.destroy();
+ });
+ };
+
+ // add widget prototype
+ $[namespace][name].prototype = $.extend({}, $.widget.prototype, prototype);
+
+ // TODO: merge getter and getterSetter properties from widget prototype
+ // and plugin prototype
+ $[namespace][name].getterSetter = 'option';
+};
+
+$.widget.prototype = {
+ _init: function() {},
+ destroy: function() {
+ this.element.removeData(this.widgetName)
+ .removeClass(this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled')
+ .removeAttr('aria-disabled');
+ },
+
+ option: function(key, value) {
+ var options = key,
+ self = this;
+
+ if (typeof key == "string") {
+ if (value === undefined) {
+ return this._getData(key);
+ }
+ options = {};
+ options[key] = value;
+ }
+
+ $.each(options, function(key, value) {
+ self._setData(key, value);
+ });
+ },
+ _getData: function(key) {
+ return this.options[key];
+ },
+ _setData: function(key, value) {
+ this.options[key] = value;
+
+ if (key == 'disabled') {
+ this.element
+ [value ? 'addClass' : 'removeClass'](
+ this.widgetBaseClass + '-disabled' + ' ' +
+ this.namespace + '-state-disabled')
+ .attr("aria-disabled", value);
+ }
+ },
+
+ enable: function() {
+ this._setData('disabled', false);
+ },
+ disable: function() {
+ this._setData('disabled', true);
+ },
+
+ _trigger: function(type, event, data) {
+ var callback = this.options[type],
+ eventName = (type == this.widgetEventPrefix
+ ? type : this.widgetEventPrefix + type);
+
+ event = $.Event(event);
+ event.type = eventName;
+
+ // copy original event properties over to the new event
+ // this would happen if we could call $.event.fix instead of $.Event
+ // but we don't have a way to force an event to be fixed multiple times
+ if (event.originalEvent) {
+ for (var i = $.event.props.length, prop; i;) {
+ prop = $.event.props[--i];
+ event[prop] = event.originalEvent[prop];
+ }
+ }
+
+ this.element.trigger(event, data);
+
+ return !($.isFunction(callback) && callback.call(this.element[0], event, data) === false
+ || event.isDefaultPrevented());
+ }
+};
+
+$.widget.defaults = {
+ disabled: false
+};
+
+
+/** Mouse Interaction Plugin **/
+
+$.ui.mouse = {
+ _mouseInit: function() {
+ var self = this;
+
+ this.element
+ .bind('mousedown.'+this.widgetName, function(event) {
+ return self._mouseDown(event);
+ })
+ .bind('click.'+this.widgetName, function(event) {
+ if(self._preventClickEvent) {
+ self._preventClickEvent = false;
+ event.stopImmediatePropagation();
+ return false;
+ }
+ });
+
+ // Prevent text selection in IE
+ if ($.browser.msie) {
+ this._mouseUnselectable = this.element.attr('unselectable');
+ this.element.attr('unselectable', 'on');
+ }
+
+ this.started = false;
+ },
+
+ // TODO: make sure destroying one instance of mouse doesn't mess with
+ // other instances of mouse
+ _mouseDestroy: function() {
+ this.element.unbind('.'+this.widgetName);
+
+ // Restore text selection in IE
+ ($.browser.msie
+ && this.element.attr('unselectable', this._mouseUnselectable));
+ },
+
+ _mouseDown: function(event) {
+ // don't let more than one widget handle mouseStart
+ // TODO: figure out why we have to use originalEvent
+ event.originalEvent = event.originalEvent || {};
+ if (event.originalEvent.mouseHandled) { return; }
+
+ // we may have missed mouseup (out of window)
+ (this._mouseStarted && this._mouseUp(event));
+
+ this._mouseDownEvent = event;
+
+ var self = this,
+ btnIsLeft = (event.which == 1),
+ elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
+ if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
+ return true;
+ }
+
+ this.mouseDelayMet = !this.options.delay;
+ if (!this.mouseDelayMet) {
+ this._mouseDelayTimer = setTimeout(function() {
+ self.mouseDelayMet = true;
+ }, this.options.delay);
+ }
+
+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+ this._mouseStarted = (this._mouseStart(event) !== false);
+ if (!this._mouseStarted) {
+ event.preventDefault();
+ return true;
+ }
+ }
+
+ // these delegates are required to keep context
+ this._mouseMoveDelegate = function(event) {
+ return self._mouseMove(event);
+ };
+ this._mouseUpDelegate = function(event) {
+ return self._mouseUp(event);
+ };
+ $(document)
+ .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+ .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+ // preventDefault() is used to prevent the selection of text here -
+ // however, in Safari, this causes select boxes not to be selectable
+ // anymore, so this fix is needed
+ ($.browser.safari || event.preventDefault());
+
+ event.originalEvent.mouseHandled = true;
+ return true;
+ },
+
+ _mouseMove: function(event) {
+ // IE mouseup check - mouseup happened when mouse was out of window
+ if ($.browser.msie && !event.button) {
+ return this._mouseUp(event);
+ }
+
+ if (this._mouseStarted) {
+ this._mouseDrag(event);
+ return event.preventDefault();
+ }
+
+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+ this._mouseStarted =
+ (this._mouseStart(this._mouseDownEvent, event) !== false);
+ (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
+ }
+
+ return !this._mouseStarted;
+ },
+
+ _mouseUp: function(event) {
+ $(document)
+ .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+ .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+ if (this._mouseStarted) {
+ this._mouseStarted = false;
+ this._preventClickEvent = (event.target == this._mouseDownEvent.target);
+ this._mouseStop(event);
+ }
+
+ return false;
+ },
+
+ _mouseDistanceMet: function(event) {
+ return (Math.max(
+ Math.abs(this._mouseDownEvent.pageX - event.pageX),
+ Math.abs(this._mouseDownEvent.pageY - event.pageY)
+ ) >= this.options.distance
+ );
+ },
+
+ _mouseDelayMet: function(event) {
+ return this.mouseDelayMet;
+ },
+
+ // These are placeholder methods, to be overriden by extending plugin
+ _mouseStart: function(event) {},
+ _mouseDrag: function(event) {},
+ _mouseStop: function(event) {},
+ _mouseCapture: function(event) { return true; }
+};
+
+$.ui.mouse.defaults = {
+ cancel: null,
+ distance: 1,
+ delay: 0
+};
+
+})(jQuery);
--- /dev/null
+++ b/static/scripts/jquery.cookie.js
@@ -0,0 +1,97 @@
+/**
+ * Cookie plugin
+ *
+ * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ */
+
+/**
+ * Create a cookie with the given name and value and other optional parameters.
+ *
+ * @example $.cookie('the_cookie', 'the_value');
+ * @desc Set the value of a cookie.
+ * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
+ * @desc Create a cookie with all available options.
+ * @example $.cookie('the_cookie', 'the_value');
+ * @desc Create a session cookie.
+ * @example $.cookie('the_cookie', null);
+ * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
+ * used when the cookie was set.
+ *
+ * @param String name The name of the cookie.
+ * @param String value The value of the cookie.
+ * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
+ * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
+ * If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
+ * If set to null or omitted, the cookie will be a session cookie and will not be retained
+ * when the the browser exits.
+ * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
+ * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
+ * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
+ * require a secure protocol (like HTTPS).
+ * @type undefined
+ *
+ * @name $.cookie
+ * @cat Plugins/Cookie
+ * @author Klaus Hartl/klaus.hartl(a)stilbuero.de
+ */
+
+/**
+ * Get the value of a cookie with the given name.
+ *
+ * @example $.cookie('the_cookie');
+ * @desc Get the value of a cookie.
+ *
+ * @param String name The name of the cookie.
+ * @return The value of the cookie.
+ * @type String
+ *
+ * @name $.cookie
+ * @cat Plugins/Cookie
+ * @author Klaus Hartl/klaus.hartl(a)stilbuero.de
+ */
+jQuery.cookie = function(name, value, options) {
+ if (typeof value != 'undefined') { // name and value given, set cookie
+ options = options || {};
+ if (value === null) {
+ value = '';
+ options = $.extend({}, options); // clone object since it's unexpected behavior if the expired property were changed
+ options.expires = -1;
+ }
+ var expires = '';
+ if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
+ var date;
+ if (typeof options.expires == 'number') {
+ date = new Date();
+ date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
+ } else {
+ date = options.expires;
+ }
+ expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
+ }
+ // NOTE Needed to parenthesize options.path and options.domain
+ // in the following expressions, otherwise they evaluate to undefined
+ // in the packed version for some reason...
+ var path = options.path ? '; path=' + (options.path) : '';
+ var domain = options.domain ? '; domain=' + (options.domain) : '';
+ var secure = options.secure ? '; secure' : '';
+ document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
+ } else { // only name given, get cookie
+ var cookieValue = null;
+ if (document.cookie && document.cookie != '') {
+ var cookies = document.cookie.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = jQuery.trim(cookies[i]);
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+ break;
+ }
+ }
+ }
+ return cookieValue;
+ }
+};
Binary file static/june_2007_style/blue/dynatree_skin/ltM_ne.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltD_nes.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltFld_o.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltM_nes.gif has changed
--- /dev/null
+++ b/static/scripts/packed/jquery.dynatree.js
@@ -0,0 +1,1 @@
+var _canLog=true;function _log(g,f){if(!_canLog){return}var b=Array.prototype.slice.apply(arguments,[1]);var c=new Date();var a=c.getHours()+":"+c.getMinutes()+":"+c.getSeconds()+"."+c.getMilliseconds();b[0]=a+" - "+b[0];try{switch(g){case"info":window.console.info.apply(window.console,b);break;case"warn":window.console.warn.apply(window.console,b);break;default:window.console.log.apply(window.console,b)}}catch(d){if(!window.console){_canLog=false}}}function logMsg(a){Array.prototype.unshift.apply(arguments,["debug"]);_log.apply(this,arguments)}var getDynaTreePersistData=undefined;var DTNodeStatus_Error=-1;var DTNodeStatus_Loading=1;var DTNodeStatus_Ok=0;(function($){var Class={create:function(){return function(){this.initialize.apply(this,arguments)}}};var DynaTreeNode=Class.create();DynaTreeNode.prototype={initialize:function(parent,tree,data){this.parent=parent;this.tree=tree;if(typeof data=="string"){data={title:data}}if(data.key==undefined){data.key="_"+tree._nodeCount+
+}this.data=$.extend({},$.ui.dynatree.nodedatadefaults,data);this.div=null;this.span=null;this.childList=null;this.isLoading=false;this.hasSubSel=false},toString:function(){return"dtnode<"+this.data.key+">: '"+this.data.title+"'"},toDict:function(recursive,callback){var dict=$.extend({},this.data);dict.activate=(this.tree.activeNode===this);dict.focus=(this.tree.focusNode===this);dict.expand=this.bExpanded;dict.select=this.bSelected;if(callback){callback(dict)}if(recursive&&this.childList){dict.children=[];for(var i=0;i<this.childList.length;i++){dict.children.push(this.childList[i].toDict(true,callback))}}else{delete dict.children}return dict},_getInnerHtml:function(){var opts=this.tree.options;var cache=this.tree.cache;var rootParent=opts.rootVisible?null:this.tree.tnRoot;var bHideFirstExpander=(opts.rootVisible&&opts.minExpandLevel>0)||opts.minExpandLevel>1;var bHideFirstConnector=opts.rootVisible||opts.minExpandLevel>0;var res="";var p=this.parent;while(p){if(bHideFirstC
onnector&&p==rootParent){break}res=(p.isLastSibling()?cache.tagEmpty:cache.tagVline)+res;p=p.parent}if(bHideFirstExpander&&this.parent==rootParent){}else{if(this.childList||this.data.isLazy){res+=cache.tagExpander}else{res+=cache.tagConnector}}if(opts.checkbox&&this.data.hideCheckbox!=true&&!this.data.isStatusNode){res+=cache.tagCheckbox}if(this.data.icon){res+="<img src='"+opts.imagePath+this.data.icon+"' alt='' />"}else{if(this.data.icon==false){}else{res+=cache.tagNodeIcon}}var tooltip=(this.data&&typeof this.data.tooltip=="string")?" title='"+this.data.tooltip+"'":"";res+="<a href='#' class='"+opts.classNames.title+"'"+tooltip+">"+this.data.title+"</a>";return res},_fixOrder:function(){var cl=this.childList;if(!cl){return}var childDiv=this.div.firstChild.nextSibling;for(var i=0;i<cl.length-1;i++){var childNode1=cl[i];var childNode2=childDiv.firstChild.dtnode;if(childNode1!==childNode2){this.tree.logDebug("_fixOrder: mismatch at index "+i+": "+childNode1+" != "+childNode2
);this.div.insertBefore(childNode1.div,childNode2.div)}else{childDiv=childDiv.nextSibling}}},render:function(bDeep,bHidden){var opts=this.tree.options;var cn=opts.classNames;var isLastSib=this.isLastSibling();if(!this.div){this.span=document.createElement("span");this.span.dtnode=this;if(this.data.key){this.span.id=this.tree.options.idPrefix+this.data.key}this.div=document.createElement("div");this.div.appendChild(this.span);if(this.parent){this.parent.div.appendChild(this.div)}if(this.parent==null&&!this.tree.options.rootVisible){this.span.style.display="none"}}this.span.innerHTML=this._getInnerHtml();this.div.style.display=(this.parent==null||this.parent.bExpanded?"":"none");var cnList=[];cnList.push((this.data.isFolder)?cn.folder:cn.document);if(this.bExpanded){cnList.push(cn.expanded)}if(this.childList!=null){cnList.push(cn.hasChildren)}if(this.data.isLazy&&this.childList==null){cnList.push(cn.lazy)}if(isLastSib){cnList.push(cn.lastsib)}if(this.bSelected){cnList.push(cn.
selected)}if(this.hasSubSel){cnList.push(cn.partsel)}if(this.tree.activeNode===this){cnList.push(cn.active)}if(this.data.addClass){cnList.push(this.data.addClass)}cnList.push(cn.combinedExpanderPrefix+(this.bExpanded?"e":"c")+(this.data.isLazy&&this.childList==null?"d":"")+(isLastSib?"l":""));cnList.push(cn.combinedIconPrefix+(this.bExpanded?"e":"c")+(this.data.isFolder?"f":""));this.span.className=cnList.join(" ");if(bDeep&&this.childList&&(bHidden||this.bExpanded)){for(var i=0;i<this.childList.length;i++){this.childList[i].render(bDeep,bHidden)}this._fixOrder()}},hasChildren:function(){return this.childList!=null},isLastSibling:function(){var p=this.parent;if(!p){return true}return p.childList[p.childList.length-1]===this},prevSibling:function(){if(!this.parent){return null}var ac=this.parent.childList;for(var i=1;i<ac.length;i++){if(ac[i]===this){return ac[i-1]}}return null},nextSibling:function(){if(!this.parent){return null}var ac=this.parent.childList;for(var i=0;i<ac.
length-1;i++){if(ac[i]===this){return ac[i+1]}}return null},_setStatusNode:function(data){var firstChild=(this.childList?this.childList[0]:null);if(!data){if(firstChild){this.div.removeChild(firstChild.div);if(this.childList.length==1){this.childList=null}else{this.childList.shift()}}}else{if(firstChild){data.isStatusNode=true;firstChild.data=data;firstChild.render(false,false)}else{data.isStatusNode=true;firstChild=this.addChild(data)}}},setLazyNodeStatus:function(lts,opts){var tooltip=(opts&&opts.tooltip)?opts.tooltip:null;var info=(opts&&opts.info)?" ("+opts.info+")":"";switch(lts){case DTNodeStatus_Ok:this._setStatusNode(null);this.isLoading=false;this.render(false,false);if(this.tree.options.autoFocus){if(this===this.tree.tnRoot&&!this.tree.options.rootVisible&&this.childList){this.childList[0].focus()}else{this.focus()}}break;case DTNodeStatus_Loading:this.isLoading=true;this._setStatusNode({title:this.tree.options.strings.loading+info,tooltip:tooltip,addClass:this.tre
e.options.classNames.nodeWait});break;case DTNodeStatus_Error:this.isLoading=false;this._setStatusNode({title:this.tree.options.strings.loadError+info,tooltip:tooltip,addClass:this.tree.options.classNames.nodeError});break;default:throw"Bad LazyNodeStatus: '"+lts+"'."}},_parentList:function(includeRoot,includeSelf){var l=[];var dtn=includeSelf?this:this.parent;while(dtn){if(includeRoot||dtn.parent){l.unshift(dtn)}dtn=dtn.parent}return l},getLevel:function(){var level=0;var dtn=this.parent;while(dtn){level++;dtn=dtn.parent}return level},_getTypeForOuterNodeEvent:function(event){var cns=this.tree.options.classNames;var target=event.target;if(target.className.indexOf(cns.folder)<0&&target.className.indexOf(cns.document)<0){return null}var eventX=event.pageX-target.offsetLeft;var eventY=event.pageY-target.offsetTop;for(var i=0;i<target.childNodes.length;i++){var cn=target.childNodes[i];var x=cn.offsetLeft-target.offsetLeft;var y=cn.offsetTop-target.offsetTop;var nx=cn.clientWidt
h,ny=cn.clientHeight;if(eventX>=x&&eventX<=(x+nx)&&eventY>=y&&eventY<=(y+ny)){if(cn.className==cns.title){return"title"}else{if(cn.className==cns.expander){return"expander"}else{if(cn.className==cns.checkbox){return"checkbox"}else{if(cn.className==cns.nodeIcon){return"icon"}}}}}}return"prefix"},getEventTargetType:function(event){var tcn=event&&event.target?event.target.className:"";var cns=this.tree.options.classNames;if(tcn==cns.title){return"title"}else{if(tcn==cns.expander){return"expander"}else{if(tcn==cns.checkbox){return"checkbox"}else{if(tcn==cns.nodeIcon){return"icon"}else{if(tcn==cns.empty||tcn==cns.vline||tcn==cns.connector){return"prefix"}else{if(tcn.indexOf(cns.folder)>=0||tcn.indexOf(cns.document)>=0){return this._getTypeForOuterNodeEvent(event)}}}}}}return null},isVisible:function(){var parents=this._parentList(true,false);for(var i=0;i<parents.length;i++){if(!parents[i].bExpanded){return false}}return true},makeVisible:function(){var parents=this._parentList(t
rue,false);for(var i=0;i<parents.length;i++){parents[i]._expand(true)}},focus:function(){this.makeVisible();try{$(this.span).find(">a").focus()}catch(e){}},_activate:function(flag,fireEvents){this.tree.logDebug("dtnode._activate(%o, fireEvents=%o) - %o",flag,fireEvents,this);var opts=this.tree.options;if(this.data.isStatusNode){return}if(fireEvents&&opts.onQueryActivate&&opts.onQueryActivate.call(this.span,flag,this)==false){return}if(flag){if(this.tree.activeNode){if(this.tree.activeNode===this){return}this.tree.activeNode.deactivate()}if(opts.activeVisible){this.makeVisible()}this.tree.activeNode=this;if(opts.persist){$.cookie(opts.cookieId+"-active",this.data.key,opts.cookie)}this.tree.persistence.activeKey=this.data.key;$(this.span).addClass(opts.classNames.active);if(fireEvents&&opts.onActivate){opts.onActivate.call(this.span,this)}}else{if(this.tree.activeNode===this){var opts=this.tree.options;if(opts.onQueryActivate&&opts.onQueryActivate.call(this.span,false,this)==f
alse){return}$(this.span).removeClass(opts.classNames.active);if(opts.persist){$.cookie(opts.cookieId+"-active","",opts.cookie)}this.tree.persistence.activeKey=null;this.tree.activeNode=null;if(fireEvents&&opts.onDeactivate){opts.onDeactivate.call(this.span,this)}}}},activate:function(){this._activate(true,true)},deactivate:function(){this._activate(false,true)},isActive:function(){return(this.tree.activeNode===this)},_userActivate:function(){var activate=true;var expand=false;if(this.data.isFolder){switch(this.tree.options.clickFolderMode){case 2:activate=false;expand=true;break;case 3:activate=expand=true;break}}if(this.parent==null&&this.tree.options.minExpandLevel>0){expand=false}if(expand){this.toggleExpand();this.focus()}if(activate){this.activate()}},_setSubSel:function(hasSubSel){if(hasSubSel){this.hasSubSel=true;$(this.span).addClass(this.tree.options.classNames.partsel)}else{this.hasSubSel=false;$(this.span).removeClass(this.tree.options.classNames.partsel)}},_fixS
electionState:function(){if(this.bSelected){this.visit(function(dtnode){dtnode.parent._setSubSel(true);dtnode._select(true,false,false)});var p=this.parent;while(p){p._setSubSel(true);var allChildsSelected=true;for(var i=0;i<p.childList.length;i++){var n=p.childList[i];if(!n.bSelected&&!n.data.isStatusNode){allChildsSelected=false;break}}if(allChildsSelected){p._select(true,false,false)}p=p.parent}}else{this._setSubSel(false);this.visit(function(dtnode){dtnode._setSubSel(false);dtnode._select(false,false,false)});var p=this.parent;while(p){p._select(false,false,false);var isPartSel=false;for(var i=0;i<p.childList.length;i++){if(p.childList[i].bSelected||p.childList[i].hasSubSel){isPartSel=true;break}}p._setSubSel(isPartSel);p=p.parent}}},_select:function(sel,fireEvents,deep){var opts=this.tree.options;if(this.data.isStatusNode){return}if(this.bSelected==sel){return}if(fireEvents&&opts.onQuerySelect&&opts.onQuerySelect.call(this.span,sel,this)==false){return}if(opts.selectMod
e==1&&sel){this.tree.visit(function(dtnode){if(dtnode.bSelected){dtnode._select(false,false,false);return false}})}this.bSelected=sel;if(sel){if(opts.persist){this.tree.persistence.addSelect(this.data.key)}$(this.span).addClass(opts.classNames.selected);if(deep&&opts.selectMode==3){this._fixSelectionState()}if(fireEvents&&opts.onSelect){opts.onSelect.call(this.span,true,this)}}else{if(opts.persist){this.tree.persistence.clearSelect(this.data.key)}$(this.span).removeClass(opts.classNames.selected);if(deep&&opts.selectMode==3){this._fixSelectionState()}if(fireEvents&&opts.onSelect){opts.onSelect.call(this.span,false,this)}}},select:function(sel){if(this.data.unselectable){return this.bSelected}return this._select(sel!=false,true,true)},toggleSelect:function(){return this.select(!this.bSelected)},isSelected:function(){return this.bSelected},_loadContent:function(){try{var opts=this.tree.options;this.tree.logDebug("_loadContent: start - %o",this);this.setLazyNodeStatus(DTNodeSta
tus_Loading);if(true==opts.onLazyRead.call(this.span,this)){this.setLazyNodeStatus(DTNodeStatus_Ok);this.tree.logDebug("_loadContent: succeeded - %o",this)}}catch(e){this.setLazyNodeStatus(DTNodeStatus_Error);this.tree.logWarning("_loadContent: failed - %o",e)}},_expand:function(bExpand){if(this.bExpanded==bExpand){return}var opts=this.tree.options;if(!bExpand&&this.getLevel()<opts.minExpandLevel){this.tree.logDebug("dtnode._expand(%o) forced expand - %o",bExpand,this);return}if(opts.onQueryExpand&&opts.onQueryExpand.call(this.span,bExpand,this)==false){return}this.bExpanded=bExpand;if(opts.persist){if(bExpand){this.tree.persistence.addExpand(this.data.key)}else{this.tree.persistence.clearExpand(this.data.key)}}this.render(false);if(this.bExpanded&&this.parent&&opts.autoCollapse){var parents=this._parentList(false,true);for(var i=0;i<parents.length;i++){parents[i].collapseSiblings()}}if(opts.activeVisible&&this.tree.activeNode&&!this.tree.activeNode.isVisible()){this.tree.ac
tiveNode.deactivate()}if(bExpand&&this.data.isLazy&&this.childList==null&&!this.isLoading){this._loadContent();return}var fxDuration=opts.fx?(opts.fx.duration||200):0;if(this.childList){for(var i=0;i<this.childList.length;i++){var $child=$(this.childList[i].div);if(fxDuration){if(bExpand!=$child.is(":visible")){$child.animate(opts.fx,fxDuration)}}else{if(bExpand){$child.show()}else{$child.hide()}}}}if(opts.onExpand){opts.onExpand.call(this.span,bExpand,this)}},expand:function(flag){if(!this.childList&&!this.data.isLazy&&flag){return}if(this.parent==null&&this.tree.options.minExpandLevel>0&&!flag){return}this._expand(flag)},toggleExpand:function(){this.expand(!this.bExpanded)},collapseSiblings:function(){if(this.parent==null){return}var ac=this.parent.childList;for(var i=0;i<ac.length;i++){if(ac[i]!==this&&ac[i].bExpanded){ac[i]._expand(false)}}},onClick:function(event){var targetType=this.getEventTargetType(event);if(targetType=="expander"){this.toggleExpand();this.focus()}e
lse{if(targetType=="checkbox"){this.toggleSelect();this.focus()}else{this._userActivate();this.span.getElementsByTagName("a")[0].focus()}}return false},onDblClick:function(event){},onKeydown:function(event){var handled=true;switch(event.which){case 107:case 187:if(!this.bExpanded){this.toggleExpand()}break;case 109:case 189:if(this.bExpanded){this.toggleExpand()}break;case 32:this._userActivate();break;case 8:if(this.parent){this.parent.focus()}break;case 37:if(this.bExpanded){this.toggleExpand();this.focus()}else{if(this.parent&&(this.tree.options.rootVisible||this.parent.parent)){this.parent.focus()}}break;case 39:if(!this.bExpanded&&(this.childList||this.data.isLazy)){this.toggleExpand();this.focus()}else{if(this.childList){this.childList[0].focus()}}break;case 38:var sib=this.prevSibling();while(sib&&sib.bExpanded&&sib.childList){sib=sib.childList[sib.childList.length-1]}if(!sib&&this.parent&&(this.tree.options.rootVisible||this.parent.parent)){sib=this.parent}if(sib){si
b.focus()}break;case 40:var sib;if(this.bExpanded&&this.childList){sib=this.childList[0]}else{var parents=this._parentList(false,true);for(var i=parents.length-1;i>=0;i--){sib=parents[i].nextSibling();if(sib){break}}}if(sib){sib.focus()}break;default:handled=false}return !handled},onKeypress:function(event){},onFocus:function(event){var opts=this.tree.options;if(event.type=="blur"||event.type=="focusout"){if(opts.onBlur){opts.onBlur.call(this.span,this)}if(this.tree.tnFocused){$(this.tree.tnFocused.span).removeClass(opts.classNames.focused)}this.tree.tnFocused=null;if(opts.persist){$.cookie(opts.cookieId+"-focus","",opts.cookie)}}else{if(event.type=="focus"||event.type=="focusin"){if(this.tree.tnFocused&&this.tree.tnFocused!==this){this.tree.logDebug("dtnode.onFocus: out of sync: curFocus: %o",this.tree.tnFocused);$(this.tree.tnFocused.span).removeClass(opts.classNames.focused)}this.tree.tnFocused=this;if(opts.onFocus){opts.onFocus.call(this.span,this)}$(this.tree.tnFocused.
span).addClass(opts.classNames.focused);if(opts.persist){$.cookie(opts.cookieId+"-focus",this.data.key,opts.cookie)}}}},visit:function(fn,data,includeSelf){var n=0;if(includeSelf==true){if(fn(this,data)==false){return 1}n++}if(this.childList){for(var i=0;i<this.childList.length;i++){n+=this.childList[i].visit(fn,data,true)}}return n},remove:function(){if(this===this.tree.root){return false}return this.parent.removeChild(this)},removeChild:function(tn){var ac=this.childList;if(ac.length==1){if(tn!==ac[0]){throw"removeChild: invalid child"}return this.removeChildren()}if(tn===this.tree.activeNode){tn.deactivate()}if(this.tree.options.persist){if(tn.bSelected){this.tree.persistence.clearSelect(tn.data.key)}if(tn.bExpanded){this.tree.persistence.clearExpand(tn.data.key)}}tn.removeChildren(true);this.div.removeChild(tn.div);for(var i=0;i<ac.length;i++){if(ac[i]===tn){this.childList.splice(i,1);delete tn;break}}},removeChildren:function(isRecursiveCall,retainPersistence){var tree=
this.tree;var ac=this.childList;if(ac){for(var i=0;i<ac.length;i++){var tn=ac[i];if(tn===tree.activeNode&&!retainPersistence){tn.deactivate()}if(this.tree.options.persist&&!retainPersistence){if(tn.bSelected){this.tree.persistence.clearSelect(tn.data.key)}if(tn.bExpanded){this.tree.persistence.clearExpand(tn.data.key)}}tn.removeChildren(true,retainPersistence);this.div.removeChild(tn.div);delete tn}this.childList=null}if(!isRecursiveCall){this.isLoading=false;this.render(false,false)}},reload:function(force){if(this.parent==null){return this.tree.reload()}if(!this.data.isLazy){throw"node.reload() requires lazy nodes."}if(this.bExpanded){this.expand(false);this.removeChildren();this.expand(true)}else{this.removeChildren();if(force){this._loadContent()}}},_addChildNode:function(dtnode,beforeNode){var tree=this.tree;var opts=tree.options;var pers=tree.persistence;dtnode.parent=this;if(this.childList==null){this.childList=[]}else{if(!beforeNode){$(this.childList[this.childList.l
ength-1].span).removeClass(opts.classNames.lastsib)}}if(beforeNode){var iBefore=$.inArray(beforeNode,this.childList);if(iBefore<0){throw"<beforeNode> must be a child of <this>"}this.childList.splice(iBefore,0,dtnode)}else{this.childList.push(dtnode)}var isInitializing=tree.isInitializing();if(opts.persist&&pers.cookiesFound&&isInitializing){if(pers.activeKey==dtnode.data.key){tree.activeNode=dtnode}if(pers.focusedKey==dtnode.data.key){tree.focusNode=dtnode}dtnode.bExpanded=($.inArray(dtnode.data.key,pers.expandedKeyList)>=0);dtnode.bSelected=($.inArray(dtnode.data.key,pers.selectedKeyList)>=0)}else{if(dtnode.data.activate){tree.activeNode=dtnode;if(opts.persist){pers.activeKey=dtnode.data.key}}if(dtnode.data.focus){tree.focusNode=dtnode;if(opts.persist){pers.focusedKey=dtnode.data.key}}dtnode.bExpanded=(dtnode.data.expand==true);if(dtnode.bExpanded&&opts.persist){pers.addExpand(dtnode.data.key)}dtnode.bSelected=(dtnode.data.select==true);if(dtnode.bSelected&&opts.persist){pe
rs.addSelect(dtnode.data.key)}}if(opts.minExpandLevel>=dtnode.getLevel()){this.bExpanded=true}if(dtnode.bSelected&&opts.selectMode==3){var p=this;while(p){if(!p.hasSubSel){p._setSubSel(true)}p=p.parent}}if(tree.bEnableUpdate){this.render(true,true)}return dtnode},addChild:function(obj,beforeNode){if(!obj||obj.length==0){return}if(obj instanceof DynaTreeNode){return this._addChildNode(obj,beforeNode)}if(!obj.length){obj=[obj]}var prevFlag=this.tree.enableUpdate(false);var tnFirst=null;for(var i=0;i<obj.length;i++){var data=obj[i];var dtnode=this._addChildNode(new DynaTreeNode(this,this.tree,data),beforeNode);if(!tnFirst){tnFirst=dtnode}if(data.children){dtnode.addChild(data.children,null)}}this.tree.enableUpdate(prevFlag);return tnFirst},append:function(obj){this.tree.logWarning("node.append() is deprecated (use node.addChild() instead).");return this.addChild(obj,null)},appendAjax:function(ajaxOptions){this.removeChildren(false,true);this.setLazyNodeStatus(DTNodeStatus_Loadi
ng);var self=this;var orgSuccess=ajaxOptions.success;var orgError=ajaxOptions.error;var options=$.extend({},this.tree.options.ajaxDefaults,ajaxOptions,{success:function(data,textStatus){var prevPhase=self.tree.phase;self.tree.phase="init";self.addChild(data,null);self.tree.phase="postInit";self.setLazyNodeStatus(DTNodeStatus_Ok);if(orgSuccess){orgSuccess.call(options,self)}self.tree.phase=prevPhase},error:function(XMLHttpRequest,textStatus,errorThrown){self.tree.logWarning("appendAjax failed:",textStatus,":\n",XMLHttpRequest,"\n",errorThrown);self.setLazyNodeStatus(DTNodeStatus_Error,{info:textStatus,tooltip:""+errorThrown});if(orgError){orgError.call(options,self,XMLHttpRequest,textStatus,errorThrown)}}});$.ajax(options)},lastentry:undefined};var DynaTreeStatus=Class.create();DynaTreeStatus._getTreePersistData=function(cookieId,cookieOpts){var ts=new DynaTreeStatus(cookieId,cookieOpts);ts.read();return ts.toDict()};getDynaTreePersistData=DynaTreeStatus._getTreePersistData;D
ynaTreeStatus.prototype={initialize:function(cookieId,cookieOpts){this._log("DynaTreeStatus: initialize");if(cookieId===undefined){cookieId=$.ui.dynatree.defaults.cookieId}cookieOpts=$.extend({},$.ui.dynatree.defaults.cookie,cookieOpts);this.cookieId=cookieId;this.cookieOpts=cookieOpts;this.cookiesFound=undefined;this.activeKey=null;this.focusedKey=null;this.expandedKeyList=null;this.selectedKeyList=null},_log:function(msg){Array.prototype.unshift.apply(arguments,["debug"]);_log.apply(this,arguments)},read:function(){this._log("DynaTreeStatus: read");this.cookiesFound=false;var cookie=$.cookie(this.cookieId+"-active");this.activeKey=(cookie==null)?"":cookie;if(cookie!=null){this.cookiesFound=true}cookie=$.cookie(this.cookieId+"-focus");this.focusedKey=(cookie==null)?"":cookie;if(cookie!=null){this.cookiesFound=true}cookie=$.cookie(this.cookieId+"-expand");this.expandedKeyList=(cookie==null)?[]:cookie.split(",");if(cookie!=null){this.cookiesFound=true}cookie=$.cookie(this.coo
kieId+"-select");this.selectedKeyList=(cookie==null)?[]:cookie.split(",");if(cookie!=null){this.cookiesFound=true}},write:function(){this._log("DynaTreeStatus: write");$.cookie(this.cookieId+"-active",(this.activeKey==null)?"":this.activeKey,this.cookieOpts);$.cookie(this.cookieId+"-focus",(this.focusedKey==null)?"":this.focusedKey,this.cookieOpts);$.cookie(this.cookieId+"-expand",(this.expandedKeyList==null)?"":this.expandedKeyList.join(","),this.cookieOpts);$.cookie(this.cookieId+"-select",(this.selectedKeyList==null)?"":this.selectedKeyList.join(","),this.cookieOpts)},addExpand:function(key){this._log("addExpand(%o)",key);if($.inArray(key,this.expandedKeyList)<0){this.expandedKeyList.push(key);$.cookie(this.cookieId+"-expand",this.expandedKeyList.join(","),this.cookieOpts)}},clearExpand:function(key){this._log("clearExpand(%o)",key);var idx=$.inArray(key,this.expandedKeyList);if(idx>=0){this.expandedKeyList.splice(idx,1);$.cookie(this.cookieId+"-expand",this.expandedKeyLi
st.join(","),this.cookieOpts)}},addSelect:function(key){this._log("addSelect(%o)",key);if($.inArray(key,this.selectedKeyList)<0){this.selectedKeyList.push(key);$.cookie(this.cookieId+"-select",this.selectedKeyList.join(","),this.cookieOpts)}},clearSelect:function(key){this._log("clearSelect(%o)",key);var idx=$.inArray(key,this.selectedKeyList);if(idx>=0){this.selectedKeyList.splice(idx,1);$.cookie(this.cookieId+"-select",this.selectedKeyList.join(","),this.cookieOpts)}},isReloading:function(){return this.cookiesFound==true},toDict:function(){return{cookiesFound:this.cookiesFound,activeKey:this.activeKey,focusedKey:this.activeKey,expandedKeyList:this.expandedKeyList,selectedKeyList:this.selectedKeyList}},lastentry:undefined};var DynaTree=Class.create();DynaTree.version="$Version: 0.5.4$";DynaTree.prototype={initialize:function($widget){this.phase="init";this.$widget=$widget;this.options=$widget.options;this.$tree=$widget.element;this.divTree=this.$tree.get(0)},_load:function(
){var $widget=this.$widget;var opts=this.options;this.bEnableUpdate=true;this._nodeCount=1;this.activeNode=null;this.focusNode=null;if(opts.classNames!==$.ui.dynatree.defaults.classNames){opts.classNames=$.extend({},$.ui.dynatree.defaults.classNames,opts.classNames)}if(!opts.imagePath){$("script").each(function(){if(this.src.search(_rexDtLibName)>=0){if(this.src.indexOf("/")>=0){opts.imagePath=this.src.slice(0,this.src.lastIndexOf("/"))+"/skin/"}else{opts.imagePath="skin/"}return false}})}this.persistence=new DynaTreeStatus(opts.cookieId,opts.cookie);if(opts.persist){if(!$.cookie){_log("warn","Please include jquery.cookie.js to use persistence.")}this.persistence.read()}this.logDebug("DynaTree.persistence: %o",this.persistence.toDict());this.cache={tagEmpty:"<span class='"+opts.classNames.empty+"'></span>",tagVline:"<span class='"+opts.classNames.vline+"'></span>",tagExpander:"<span class='"+opts.classNames.expander+"'></span>",tagConnector:"<span class='"+opts.classNames.co
nnector+"'></span>",tagNodeIcon:"<span class='"+opts.classNames.nodeIcon+"'></span>",tagCheckbox:"<span class='"+opts.classNames.checkbox+"'></span>",lastentry:undefined};if(opts.children||(opts.initAjax&&opts.initAjax.url)||opts.initId){$(this.divTree).empty()}else{if(this.divRoot){$(this.divRoot).remove()}}this.tnRoot=new DynaTreeNode(null,this,{title:opts.title,key:"root"});this.tnRoot.data.isFolder=true;this.tnRoot.render(false,false);this.divRoot=this.tnRoot.div;this.divRoot.className=opts.classNames.container;this.divTree.appendChild(this.divRoot);var root=this.tnRoot;var isReloading=(opts.persist&&this.persistence.isReloading());var isLazy=false;var prevFlag=this.enableUpdate(false);this.logDebug("Dynatree._load(): read tree structure...");if(opts.children){root.addChild(opts.children)}else{if(opts.initAjax&&opts.initAjax.url){isLazy=true;root.data.isLazy=true;this._reloadAjax()}else{if(opts.initId){this._createFromTag(root,$("#"+opts.initId))}else{var $ul=this.$tree.
find(">ul").hide();this._createFromTag(root,$ul);$ul.remove()}}}this._checkConsistency();this.logDebug("Dynatree._load(): render nodes...");this.enableUpdate(prevFlag);this.logDebug("Dynatree._load(): bind events...");this.$widget.bind();this.logDebug("Dynatree._load(): postInit...");this.phase="postInit";if(opts.persist){this.persistence.write()}if(this.focusNode&&this.focusNode.isVisible()){this.logDebug("Focus on init: %o",this.focusNode);this.focusNode.focus()}if(!isLazy&&opts.onPostInit){opts.onPostInit.call(this,isReloading,false)}this.phase="idle"},_reloadAjax:function(){var opts=this.options;if(!opts.initAjax||!opts.initAjax.url){throw"tree.reload() requires 'initAjax' mode."}var pers=this.persistence;var ajaxOpts=$.extend({},opts.initAjax);if(ajaxOpts.addActiveKey){ajaxOpts.data.activeKey=pers.activeKey}if(ajaxOpts.addFocusedKey){ajaxOpts.data.focusedKey=pers.focusedKey}if(ajaxOpts.addExpandedKeyList){ajaxOpts.data.expandedKeyList=pers.expandedKeyList.join(",")}if(a
jaxOpts.addSelectedKeyList){ajaxOpts.data.selectedKeyList=pers.selectedKeyList.join(",")}if(opts.onPostInit){if(ajaxOpts.success){this.logWarning("initAjax: success callback is ignored when onPostInit was specified.")}if(ajaxOpts.error){this.logWarning("initAjax: error callback is ignored when onPostInit was specified.")}var isReloading=pers.isReloading();ajaxOpts.success=function(dtnode){opts.onPostInit.call(dtnode.tree,isReloading,false)};ajaxOpts.error=function(dtnode){opts.onPostInit.call(dtnode.tree,isReloading,true)}}this.logDebug("Dynatree._init(): send Ajax request...");this.tnRoot.appendAjax(ajaxOpts)},toString:function(){return"DynaTree '"+this.options.title+"'"},toDict:function(){return this.tnRoot.toDict(true)},getPersistData:function(){return this.persistence.toDict()},logDebug:function(msg){if(this.options.debugLevel>=2){Array.prototype.unshift.apply(arguments,["debug"]);_log.apply(this,arguments)}},logInfo:function(msg){if(this.options.debugLevel>=1){Array.pro
totype.unshift.apply(arguments,["info"]);_log.apply(this,arguments)}},logWarning:function(msg){Array.prototype.unshift.apply(arguments,["warn"]);_log.apply(this,arguments)},isInitializing:function(){return(this.phase=="init"||this.phase=="postInit")},isReloading:function(){return(this.phase=="init"||this.phase=="postInit")&&this.options.persist&&this.persistence.cookiesFound},isUserEvent:function(){return(this.phase=="userEvent")},redraw:function(){this.logDebug("dynatree.redraw()...");this.tnRoot.render(true,true);this.logDebug("dynatree.redraw() done.")},reloadAjax:function(){this.logWarning("tree.reloadAjax() is deprecated since v0.5.2 (use reload() instead).")},reload:function(){this._load()},getRoot:function(){return this.tnRoot},getNodeByKey:function(key){var el=document.getElementById(this.options.idPrefix+key);return(el&&el.dtnode)?el.dtnode:null},getActiveNode:function(){return this.activeNode},reactivate:function(setFocus){var node=this.activeNode;if(node){this.act
iveNode=null;node.activate();if(setFocus){node.focus()}}},getSelectedNodes:function(stopOnParents){var nodeList=[];this.tnRoot.visit(function(dtnode){if(dtnode.bSelected){nodeList.push(dtnode);if(stopOnParents==true){return false}}});return nodeList},activateKey:function(key){var dtnode=(key===null)?null:this.getNodeByKey(key);if(!dtnode){if(this.activeNode){this.activeNode.deactivate()}this.activeNode=null;return null}dtnode.focus();dtnode.activate();return dtnode},selectKey:function(key,select){var dtnode=this.getNodeByKey(key);if(!dtnode){return null}dtnode.select(select);return dtnode},enableUpdate:function(bEnable){if(this.bEnableUpdate==bEnable){return bEnable}this.bEnableUpdate=bEnable;if(bEnable){this.redraw()}return !bEnable},visit:function(fn,data,includeRoot){return this.tnRoot.visit(fn,data,includeRoot)},_createFromTag:function(parentTreeNode,$ulParent){var self=this;$ulParent.find(">li").each(function(){var $li=$(this);var $liSpan=$li.find(">span:first");var tit
le;if($liSpan.length){title=$liSpan.html()}else{title=$li.html();var iPos=title.search(/<ul/i);if(iPos>=0){title=$.trim(title.substring(0,iPos))}else{title=$.trim(title)}}var data={title:title,isFolder:$li.hasClass("folder"),isLazy:$li.hasClass("lazy"),expand:$li.hasClass("expanded"),select:$li.hasClass("selected"),activate:$li.hasClass("active"),focus:$li.hasClass("focused")};if($li.attr("title")){data.tooltip=$li.attr("title")}if($li.attr("id")){data.key=$li.attr("id")}if($li.attr("data")){var dataAttr=$.trim($li.attr("data"));if(dataAttr){if(dataAttr.charAt(0)!="{"){dataAttr="{"+dataAttr+"}"}try{$.extend(data,eval("("+dataAttr+")"))}catch(e){throw ("Error parsing node data: "+e+"\ndata:\n'"+dataAttr+"'")}}}childNode=parentTreeNode.addChild(data);var $ul=$li.find(">ul:first");if($ul.length){self._createFromTag(childNode,$ul)}})},_checkConsistency:function(){},lastentry:undefined};$.widget("ui.dynatree",{init:function(){_log("warn","ui.dynatree.init() was called; you should
upgrade to ui.core.js v1.6 or higher.");return this._init()},_init:function(){if(parseFloat($.ui.version)<1.8){_log("info","ui.dynatree._init() was called; consider upgrading to jquery.ui.core.js v1.8 or higher.");return this._create()}_log("debug","ui.dynatree._init() was called; no current default functionality.")},_create:function(){if(parseFloat($.ui.version)>=1.8){this.options=$.extend(true,{},$[this.namespace][this.widgetName].defaults,this.options)}logMsg("Dynatree._create(): version='%s', debugLevel=%o.",DynaTree.version,this.options.debugLevel);var opts=this.options;this.options.event+=".dynatree";var divTree=this.element.get(0);this.tree=new DynaTree(this);this.tree._load();this.tree.logDebug("Dynatree._create(): done.")},bind:function(){var $this=this.element;var o=this.options;this.unbind();function __getNodeFromElement(el){var iMax=5;while(el&&iMax--){if(el.dtnode){return el.dtnode}el=el.parentNode}return null}var eventNames="click.dynatree dblclick.dynatree";i
f(o.keyboard){eventNames+=" keypress.dynatree keydown.dynatree"}$this.bind(eventNames,function(event){var dtnode=__getNodeFromElement(event.target);if(!dtnode){return true}var prevPhase=dtnode.tree.phase;dtnode.tree.phase="userEvent";try{dtnode.tree.logDebug("bind(%o): dtnode: %o",event,dtnode);switch(event.type){case"click":return(o.onClick&&o.onClick(dtnode,event)===false)?false:dtnode.onClick(event);case"dblclick":return(o.onDblClick&&o.onDblClick(dtnode,event)===false)?false:dtnode.onDblClick(event);case"keydown":return(o.onKeydown&&o.onKeydown(dtnode,event)===false)?false:dtnode.onKeydown(event);case"keypress":return(o.onKeypress&&o.onKeypress(dtnode,event)===false)?false:dtnode.onKeypress(event)}}catch(e){var _=null}finally{dtnode.tree.phase=prevPhase}});function __focusHandler(event){event=arguments[0]=$.event.fix(event||window.event);var dtnode=__getNodeFromElement(event.target);return dtnode?dtnode.onFocus(event):false}var div=this.tree.divTree;if(div.addEventListen
er){div.addEventListener("focus",__focusHandler,true);div.addEventListener("blur",__focusHandler,true)}else{div.onfocusin=div.onfocusout=__focusHandler}},unbind:function(){this.element.unbind(".dynatree")},enable:function(){this.bind();$.widget.prototype.enable.apply(this,arguments)},disable:function(){this.unbind();$.widget.prototype.disable.apply(this,arguments)},getTree:function(){return this.tree},getRoot:function(){return this.tree.getRoot()},getActiveNode:function(){return this.tree.getActiveNode()},getSelectedNodes:function(){return this.tree.getSelectedNodes()},lastentry:undefined});$.ui.dynatree.getter="getTree getRoot getActiveNode getSelectedNodes";$.ui.dynatree.defaults={title:"Dynatree root",rootVisible:false,minExpandLevel:1,imagePath:null,children:null,initId:null,initAjax:null,autoFocus:true,keyboard:true,persist:false,autoCollapse:false,clickFolderMode:3,activeVisible:true,checkbox:false,selectMode:2,fx:null,onClick:null,onDblClick:null,onKeydown:null,onKeyp
ress:null,onFocus:null,onBlur:null,onQueryActivate:null,onQuerySelect:null,onQueryExpand:null,onPostInit:null,onActivate:null,onDeactivate:null,onSelect:null,onExpand:null,onLazyRead:null,ajaxDefaults:{cache:false,dataType:"json"},strings:{loading:"Loading…",loadError:"Load error!"},idPrefix:"ui-dynatree-id-",cookieId:"dynatree",cookie:{expires:null},classNames:{container:"ui-dynatree-container",folder:"ui-dynatree-folder",document:"ui-dynatree-document",empty:"ui-dynatree-empty",vline:"ui-dynatree-vline",expander:"ui-dynatree-expander",connector:"ui-dynatree-connector",checkbox:"ui-dynatree-checkbox",nodeIcon:"ui-dynatree-icon",title:"ui-dynatree-title",nodeError:"ui-dynatree-statusnode-error",nodeWait:"ui-dynatree-statusnode-wait",hidden:"ui-dynatree-hidden",combinedExpanderPrefix:"ui-dynatree-exp-",combinedIconPrefix:"ui-dynatree-ico-",hasChildren:"ui-dynatree-has-children",active:"ui-dynatree-active",selected:"ui-dynatree-selected",expanded:"ui-dynatree-expanded",l
azy:"ui-dynatree-lazy",focused:"ui-dynatree-focused",partsel:"ui-dynatree-partsel",lastsib:"ui-dynatree-lastsib"},debugLevel:1,lastentry:undefined};$.ui.dynatree.nodedatadefaults={title:null,key:null,isFolder:false,isLazy:false,tooltip:null,icon:null,addClass:null,activate:false,focus:false,expand:false,select:false,hideCheckbox:false,unselectable:false,children:null,lastentry:undefined}})(jQuery);var _rexDtLibName=/.*dynatree[^/]*\.js$/i;
Binary file static/june_2007_style/blue/dynatree_skin/rbChecked.gif has changed
--- /dev/null
+++ b/static/scripts/jquery.dynatree.js
@@ -0,0 +1,2034 @@
+/*************************************************************************
+ jquery.dynatree.js
+ Dynamic tree view control, with support for lazy loading of branches.
+
+ Copyright (c) 2008-2010, Martin Wendt (http://wwWendt.de)
+ Dual licensed under the MIT or GPL Version 2 licenses.
+ http://code.google.com/p/dynatree/wiki/LicenseInfo
+
+ A current version and some documentation is available at
+ http://dynatree.googlecode.com/
+
+ $Version: 0.5.4$
+ $Revision: 329, 2010-05-05 08:04:39$
+
+ @depends: jquery.js
+ @depends: ui.core.js
+ @depends: jquery.cookie.js
+*************************************************************************/
+
+
+/*************************************************************************
+ * Debug functions
+ */
+
+var _canLog = true;
+
+function _log(mode, msg) {
+ /**
+ * Usage: logMsg("%o was toggled", this);
+ */
+ if( !_canLog )
+ return;
+ // Remove first argument
+ var args = Array.prototype.slice.apply(arguments, [1]);
+ // Prepend timestamp
+ var dt = new Date();
+ var tag = dt.getHours()+":"+dt.getMinutes()+":"+dt.getSeconds()+"."+dt.getMilliseconds();
+ args[0] = tag + " - " + args[0];
+
+ try {
+ switch( mode ) {
+ case "info":
+ window.console.info.apply(window.console, args);
+ break;
+ case "warn":
+ window.console.warn.apply(window.console, args);
+ break;
+ default:
+ window.console.log.apply(window.console, args);
+ }
+ } catch(e) {
+ if( !window.console )
+ _canLog = false; // Permanently disable, when logging is not supported by the browser
+ }
+}
+
+function logMsg(msg) {
+ Array.prototype.unshift.apply(arguments, ["debug"]);
+ _log.apply(this, arguments);
+}
+
+
+// Forward declaration
+var getDynaTreePersistData = undefined;
+
+
+
+/*************************************************************************
+ * Constants
+ */
+var DTNodeStatus_Error = -1;
+var DTNodeStatus_Loading = 1;
+var DTNodeStatus_Ok = 0;
+
+
+// Start of local namespace
+;(function($) {
+
+/*************************************************************************
+ * Common tool functions.
+ */
+
+var Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+}
+
+/*************************************************************************
+ * Class DynaTreeNode
+ */
+var DynaTreeNode = Class.create();
+
+DynaTreeNode.prototype = {
+ initialize: function(parent, tree, data) {
+ /**
+ * @constructor
+ */
+ this.parent = parent;
+ this.tree = tree;
+ if ( typeof data == "string" )
+ data = { title: data };
+ if( data.key == undefined )
+ data.key = "_" + tree._nodeCount++;
+ this.data = $.extend({}, $.ui.dynatree.nodedatadefaults, data);
+ this.div = null; // not yet created
+ this.span = null; // not yet created
+ this.childList = null; // no subnodes yet
+// this.isRead = false; // Lazy content not yet read
+ this.isLoading = false; // Lazy content is being loaded
+ this.hasSubSel = false;
+ },
+
+ toString: function() {
+ return "dtnode<" + this.data.key + ">: '" + this.data.title + "'";
+ },
+
+ toDict: function(recursive, callback) {
+ var dict = $.extend({}, this.data);
+ dict.activate = ( this.tree.activeNode === this );
+ dict.focus = ( this.tree.focusNode === this );
+ dict.expand = this.bExpanded;
+ dict.select = this.bSelected;
+ if( callback )
+ callback(dict);
+ if( recursive && this.childList ) {
+ dict.children = [];
+ for(var i=0; i<this.childList.length; i++ )
+ dict.children.push(this.childList[i].toDict(true, callback));
+ } else {
+ delete dict.children;
+ }
+ return dict;
+ },
+
+ _getInnerHtml: function() {
+ var opts = this.tree.options;
+ var cache = this.tree.cache;
+ // parent connectors
+ var rootParent = opts.rootVisible ? null : this.tree.tnRoot;
+ var bHideFirstExpander = (opts.rootVisible && opts.minExpandLevel>0) || opts.minExpandLevel>1;
+ var bHideFirstConnector = opts.rootVisible || opts.minExpandLevel>0;
+
+ var res = "";
+ var p = this.parent;
+ while( p ) {
+ // Suppress first connector column, if visible top level is always expanded
+ if ( bHideFirstConnector && p==rootParent )
+ break;
+ res = ( p.isLastSibling() ? cache.tagEmpty : cache.tagVline) + res;
+ p = p.parent;
+ }
+
+ // connector (expanded, expandable or simple)
+ if( bHideFirstExpander && this.parent==rootParent ) {
+ // skip connector
+ } else if ( this.childList || this.data.isLazy ) {
+ res += cache.tagExpander;
+ } else {
+ res += cache.tagConnector;
+ }
+
+ // Checkbox mode
+ if( opts.checkbox && this.data.hideCheckbox!=true && !this.data.isStatusNode ) {
+ res += cache.tagCheckbox;
+ }
+
+ // folder or doctype icon
+ if ( this.data.icon ) {
+ res += "<img src='" + opts.imagePath + this.data.icon + "' alt='' />";
+ } else if ( this.data.icon == false ) {
+ // icon == false means 'no icon'
+ } else {
+ // icon == null means 'default icon'
+ res += cache.tagNodeIcon;
+ }
+
+ // node name
+ var tooltip = ( this.data && typeof this.data.tooltip == "string" ) ? " title='" + this.data.tooltip + "'" : "";
+ res += "<a href='#' class='" + opts.classNames.title + "'" + tooltip + ">" + this.data.title + "</a>";
+ return res;
+ },
+
+ _fixOrder: function() {
+ /**
+ * Make sure, that <div> order matches childList order.
+ */
+ var cl = this.childList;
+ if( !cl )
+ return;
+ var childDiv = this.div.firstChild.nextSibling;
+ for(var i=0; i<cl.length-1; i++) {
+ var childNode1 = cl[i];
+ var childNode2 = childDiv.firstChild.dtnode;
+ if( childNode1 !== childNode2 ) {
+ this.tree.logDebug("_fixOrder: mismatch at index " + i + ": " + childNode1 + " != " + childNode2);
+ this.div.insertBefore(childNode1.div, childNode2.div);
+ } else {
+ childDiv = childDiv.nextSibling;
+ }
+ }
+ },
+
+ render: function(bDeep, bHidden) {
+ /**
+ * Create HTML markup for this node.
+ *
+ * <div> // This div contains the node's span and list of child div's.
+ * <span id='key'>S S S A</span> // Span contains graphic spans and title <a> tag
+ * <div>child1</div>
+ * <div>child2</div>
+ * </div>
+ */
+// this.tree.logDebug("%o.render()", this);
+ var opts = this.tree.options;
+ var cn = opts.classNames;
+ var isLastSib = this.isLastSibling();
+ // ---
+ if( ! this.div ) {
+ this.span = document.createElement("span");
+ this.span.dtnode = this;
+ if( this.data.key )
+ this.span.id = this.tree.options.idPrefix + this.data.key;
+ this.div = document.createElement("div");
+ this.div.appendChild(this.span);
+
+ if ( this.parent ) {
+ this.parent.div.appendChild(this.div);
+ }
+
+ if( this.parent==null && !this.tree.options.rootVisible )
+ this.span.style.display = "none";
+ }
+ // set node connector images, links and text
+ this.span.innerHTML = this._getInnerHtml();
+
+ // hide this node, if parent is collapsed
+ this.div.style.display = ( this.parent==null || this.parent.bExpanded ? "" : "none");
+
+ // Set classes for current status
+ var cnList = [];
+ cnList.push( ( this.data.isFolder ) ? cn.folder : cn.document );
+ if( this.bExpanded )
+ cnList.push(cn.expanded);
+ if( this.childList != null )
+ cnList.push(cn.hasChildren);
+ if( this.data.isLazy && this.childList==null )
+ cnList.push(cn.lazy);
+ if( isLastSib )
+ cnList.push(cn.lastsib);
+ if( this.bSelected )
+ cnList.push(cn.selected);
+ if( this.hasSubSel )
+ cnList.push(cn.partsel);
+ if( this.tree.activeNode === this )
+ cnList.push(cn.active);
+ if( this.data.addClass )
+ cnList.push(this.data.addClass);
+ // IE6 doesn't correctly evaluate multiple class names,
+ // so we create combined class names that can be used in the CSS
+ cnList.push(cn.combinedExpanderPrefix
+ + (this.bExpanded ? "e" : "c")
+ + (this.data.isLazy && this.childList==null ? "d" : "")
+ + (isLastSib ? "l" : "")
+ );
+ cnList.push(cn.combinedIconPrefix
+ + (this.bExpanded ? "e" : "c")
+ + (this.data.isFolder ? "f" : "")
+ );
+ this.span.className = cnList.join(" ");
+
+ if( bDeep && this.childList && (bHidden || this.bExpanded) ) {
+ for(var i=0; i<this.childList.length; i++) {
+ this.childList[i].render(bDeep, bHidden)
+ }
+ this._fixOrder();
+ }
+ },
+
+ hasChildren: function() {
+ return this.childList != null;
+ },
+
+ isLastSibling: function() {
+ var p = this.parent;
+ if ( !p ) return true;
+ return p.childList[p.childList.length-1] === this;
+ },
+
+ prevSibling: function() {
+ if( !this.parent ) return null;
+ var ac = this.parent.childList;
+ for(var i=1; i<ac.length; i++) // start with 1, so prev(first) = null
+ if( ac[i] === this )
+ return ac[i-1];
+ return null;
+ },
+
+ nextSibling: function() {
+ if( !this.parent ) return null;
+ var ac = this.parent.childList;
+ for(var i=0; i<ac.length-1; i++) // up to length-2, so next(last) = null
+ if( ac[i] === this )
+ return ac[i+1];
+ return null;
+ },
+
+ _setStatusNode: function(data) {
+ // Create, modify or remove the status child node (pass 'null', to remove it).
+ var firstChild = ( this.childList ? this.childList[0] : null );
+ if( !data ) {
+ if ( firstChild ) {
+ this.div.removeChild(firstChild.div);
+ if( this.childList.length == 1 )
+ this.childList = null;
+ else
+ this.childList.shift();
+ }
+ } else if ( firstChild ) {
+ data.isStatusNode = true;
+ firstChild.data = data;
+ firstChild.render(false, false);
+ } else {
+ data.isStatusNode = true;
+ firstChild = this.addChild(data);
+ }
+ },
+
+ setLazyNodeStatus: function(lts, opts) {
+ var tooltip = (opts && opts.tooltip) ? opts.tooltip : null;
+ var info = (opts && opts.info) ? " (" + opts.info + ")" : "";
+ switch( lts ) {
+ case DTNodeStatus_Ok:
+ this._setStatusNode(null);
+// this.isRead = true;
+ this.isLoading = false;
+ this.render(false, false);
+ if( this.tree.options.autoFocus ) {
+ if( this === this.tree.tnRoot && !this.tree.options.rootVisible && this.childList ) {
+ // special case: using ajaxInit
+ this.childList[0].focus();
+ } else {
+ this.focus();
+ }
+ }
+ break;
+ case DTNodeStatus_Loading:
+ this.isLoading = true;
+ this._setStatusNode({
+ title: this.tree.options.strings.loading + info,
+ tooltip: tooltip,
+ addClass: this.tree.options.classNames.nodeWait
+ });
+ break;
+ case DTNodeStatus_Error:
+ this.isLoading = false;
+ this._setStatusNode({
+ title: this.tree.options.strings.loadError + info,
+ tooltip: tooltip,
+ addClass: this.tree.options.classNames.nodeError
+ });
+ break;
+ default:
+ throw "Bad LazyNodeStatus: '" + lts + "'.";
+ }
+ },
+
+ _parentList: function(includeRoot, includeSelf) {
+ var l = [];
+ var dtn = includeSelf ? this : this.parent;
+ while( dtn ) {
+ if( includeRoot || dtn.parent )
+ l.unshift(dtn);
+ dtn = dtn.parent;
+ };
+ return l;
+ },
+
+ getLevel: function() {
+ var level = 0;
+ var dtn = this.parent;
+ while( dtn ) {
+ level++;
+ dtn = dtn.parent;
+ };
+ return level;
+ },
+
+ _getTypeForOuterNodeEvent: function(event) {
+ /** Return the inner node span (title, checkbox or expander) if
+ * event.target points to the outer span.
+ * This function should fix issue #93:
+ * FF2 ignores empty spans, when generating events (returning the parent instead).
+ */
+ var cns = this.tree.options.classNames;
+ var target = event.target;
+ // Only process clicks on an outer node span (probably due to a FF2 event handling bug)
+ if( target.className.indexOf(cns.folder)<0
+ && target.className.indexOf(cns.document)<0 ) {
+ return null
+ }
+ // Event coordinates, relative to outer node span:
+ var eventX = event.pageX - target.offsetLeft;
+ var eventY = event.pageY - target.offsetTop;
+
+ for(var i=0; i<target.childNodes.length; i++) {
+ var cn = target.childNodes[i];
+ var x = cn.offsetLeft - target.offsetLeft;
+ var y = cn.offsetTop - target.offsetTop;
+ var nx = cn.clientWidth, ny = cn.clientHeight;
+// alert (cn.className + ": " + x + ", " + y + ", s:" + nx + ", " + ny);
+ if( eventX>=x && eventX<=(x+nx) && eventY>=y && eventY<=(y+ny) ) {
+// alert("HIT "+ cn.className);
+ if( cn.className==cns.title )
+ return "title";
+ else if( cn.className==cns.expander )
+ return "expander";
+ else if( cn.className==cns.checkbox )
+ return "checkbox";
+ else if( cn.className==cns.nodeIcon )
+ return "icon";
+ }
+ }
+ return "prefix";
+ },
+
+ getEventTargetType: function(event) {
+ // Return the part of a node, that a click event occured on.
+ // Note: there is no check, if the was fired on TIHS node.
+ var tcn = event && event.target ? event.target.className : "";
+ var cns = this.tree.options.classNames;
+
+ if( tcn == cns.title )
+ return "title";
+ else if( tcn==cns.expander )
+ return "expander";
+ else if( tcn==cns.checkbox )
+ return "checkbox";
+ else if( tcn==cns.nodeIcon )
+ return "icon";
+ else if( tcn==cns.empty || tcn==cns.vline || tcn==cns.connector )
+ return "prefix";
+ else if( tcn.indexOf(cns.folder)>=0 || tcn.indexOf(cns.document)>=0 )
+ // FIX issue #93
+ return this._getTypeForOuterNodeEvent(event);
+ return null;
+ },
+
+ isVisible: function() {
+ // Return true, if all parents are expanded.
+ var parents = this._parentList(true, false);
+ for(var i=0; i<parents.length; i++)
+ if( ! parents[i].bExpanded ) return false;
+ return true;
+ },
+
+ makeVisible: function() {
+ // Make sure, all parents are expanded
+ var parents = this._parentList(true, false);
+ for(var i=0; i<parents.length; i++)
+ parents[i]._expand(true);
+ },
+
+ focus: function() {
+ // TODO: check, if we already have focus
+// this.tree.logDebug("dtnode.focus(): %o", this);
+ this.makeVisible();
+ try {
+ $(this.span).find(">a").focus();
+ } catch(e) { }
+ },
+
+ _activate: function(flag, fireEvents) {
+ // (De)Activate - but not focus - this node.
+ this.tree.logDebug("dtnode._activate(%o, fireEvents=%o) - %o", flag, fireEvents, this);
+ var opts = this.tree.options;
+ if( this.data.isStatusNode )
+ return;
+ if ( fireEvents && opts.onQueryActivate && opts.onQueryActivate.call(this.span, flag, this) == false )
+ return; // Callback returned false
+
+ if( flag ) {
+ // Activate
+ if( this.tree.activeNode ) {
+ if( this.tree.activeNode === this )
+ return;
+ this.tree.activeNode.deactivate();
+ }
+ if( opts.activeVisible )
+ this.makeVisible();
+ this.tree.activeNode = this;
+ if( opts.persist )
+ $.cookie(opts.cookieId+"-active", this.data.key, opts.cookie);
+ this.tree.persistence.activeKey = this.data.key;
+ $(this.span).addClass(opts.classNames.active);
+ if ( fireEvents && opts.onActivate ) // Pass element as 'this' (jQuery convention)
+ opts.onActivate.call(this.span, this);
+ } else {
+ // Deactivate
+ if( this.tree.activeNode === this ) {
+ var opts = this.tree.options;
+ if ( opts.onQueryActivate && opts.onQueryActivate.call(this.span, false, this) == false )
+ return; // Callback returned false
+ $(this.span).removeClass(opts.classNames.active);
+ if( opts.persist ) {
+ // Note: we don't pass null, but ''. So the cookie is not deleted.
+ // If we pass null, we also have to pass a COPY of opts, because $cookie will override opts.expires (issue 84)
+ $.cookie(opts.cookieId+"-active", "", opts.cookie);
+ }
+ this.tree.persistence.activeKey = null;
+ this.tree.activeNode = null;
+ if ( fireEvents && opts.onDeactivate )
+ opts.onDeactivate.call(this.span, this);
+ }
+ }
+ },
+
+ activate: function() {
+ // Select - but not focus - this node.
+// this.tree.logDebug("dtnode.activate(): %o", this);
+ this._activate(true, true);
+ },
+
+ deactivate: function() {
+// this.tree.logDebug("dtnode.deactivate(): %o", this);
+ this._activate(false, true);
+ },
+
+ isActive: function() {
+ return (this.tree.activeNode === this);
+ },
+
+ _userActivate: function() {
+ // Handle user click / [space] / [enter], according to clickFolderMode.
+ var activate = true;
+ var expand = false;
+ if ( this.data.isFolder ) {
+ switch( this.tree.options.clickFolderMode ) {
+ case 2:
+ activate = false;
+ expand = true;
+ break;
+ case 3:
+ activate = expand = true;
+ break;
+ }
+ }
+ if( this.parent == null && this.tree.options.minExpandLevel>0 ) {
+ expand = false;
+ }
+ if( expand ) {
+ this.toggleExpand();
+ this.focus();
+ }
+ if( activate ) {
+ this.activate();
+ }
+ },
+
+ _setSubSel: function(hasSubSel) {
+ if( hasSubSel ) {
+ this.hasSubSel = true;
+ $(this.span).addClass(this.tree.options.classNames.partsel);
+ } else {
+ this.hasSubSel = false;
+ $(this.span).removeClass(this.tree.options.classNames.partsel);
+ }
+ },
+
+ _fixSelectionState: function() {
+ // fix selection status, for multi-hier mode
+// this.tree.logDebug("_fixSelectionState(%o) - %o", this.bSelected, this);
+ if( this.bSelected ) {
+ // Select all children
+ this.visit(function(dtnode){
+ dtnode.parent._setSubSel(true);
+ dtnode._select(true, false, false);
+ });
+ // Select parents, if all children are selected
+ var p = this.parent;
+ while( p ) {
+ p._setSubSel(true);
+ var allChildsSelected = true;
+ for(var i=0; i<p.childList.length; i++) {
+ var n = p.childList[i];
+ if( !n.bSelected && !n.data.isStatusNode ) {
+ allChildsSelected = false;
+ break;
+ }
+ }
+ if( allChildsSelected )
+ p._select(true, false, false);
+ p = p.parent;
+ }
+ } else {
+ // Deselect all children
+ this._setSubSel(false);
+ this.visit(function(dtnode){
+ dtnode._setSubSel(false);
+ dtnode._select(false, false, false);
+ });
+ // Deselect parents, and recalc hasSubSel
+ var p = this.parent;
+ while( p ) {
+ p._select(false, false, false);
+ var isPartSel = false;
+ for(var i=0; i<p.childList.length; i++) {
+ if( p.childList[i].bSelected || p.childList[i].hasSubSel ) {
+ isPartSel = true;
+ break;
+ }
+ }
+ p._setSubSel(isPartSel);
+ p = p.parent;
+ }
+ }
+ },
+
+ _select: function(sel, fireEvents, deep) {
+ // Select - but not focus - this node.
+// this.tree.logDebug("dtnode._select(%o) - %o", sel, this);
+ var opts = this.tree.options;
+ if( this.data.isStatusNode )
+ return;
+ //
+ if( this.bSelected == sel ) {
+// this.tree.logDebug("dtnode._select(%o) IGNORED - %o", sel, this);
+ return;
+ }
+ // Allow event listener to abort selection
+ if ( fireEvents && opts.onQuerySelect && opts.onQuerySelect.call(this.span, sel, this) == false )
+ return; // Callback returned false
+
+ // Force single-selection
+ if( opts.selectMode==1 && sel ) {
+ this.tree.visit(function(dtnode){
+ if( dtnode.bSelected ) {
+ // Deselect; assuming that in selectMode:1 there's max. one other selected node
+ dtnode._select(false, false, false);
+ return false;
+ }
+ });
+ }
+
+ this.bSelected = sel;
+// this.tree._changeNodeList("select", this, sel);
+
+ if( sel ) {
+ if( opts.persist )
+ this.tree.persistence.addSelect(this.data.key);
+
+ $(this.span).addClass(opts.classNames.selected);
+
+ if( deep && opts.selectMode==3 )
+ this._fixSelectionState();
+
+ if ( fireEvents && opts.onSelect )
+ opts.onSelect.call(this.span, true, this);
+
+ } else {
+ if( opts.persist )
+ this.tree.persistence.clearSelect(this.data.key);
+
+ $(this.span).removeClass(opts.classNames.selected);
+
+ if( deep && opts.selectMode==3 )
+ this._fixSelectionState();
+
+ if ( fireEvents && opts.onSelect )
+ opts.onSelect.call(this.span, false, this);
+ }
+ },
+
+ select: function(sel) {
+ // Select - but not focus - this node.
+// this.tree.logDebug("dtnode.select(%o) - %o", sel, this);
+ if( this.data.unselectable )
+ return this.bSelected;
+ return this._select(sel!=false, true, true);
+ },
+
+ toggleSelect: function() {
+// this.tree.logDebug("dtnode.toggleSelect() - %o", this);
+ return this.select(!this.bSelected);
+ },
+
+ isSelected: function() {
+ return this.bSelected;
+ },
+
+ _loadContent: function() {
+ try {
+ var opts = this.tree.options;
+ this.tree.logDebug("_loadContent: start - %o", this);
+ this.setLazyNodeStatus(DTNodeStatus_Loading);
+ if( true == opts.onLazyRead.call(this.span, this) ) {
+ // If function returns 'true', we assume that the loading is done:
+ this.setLazyNodeStatus(DTNodeStatus_Ok);
+ // Otherwise (i.e. if the loading was started as an asynchronous process)
+ // the onLazyRead(dtnode) handler is expected to call dtnode.setLazyNodeStatus(DTNodeStatus_Ok/_Error) when done.
+ this.tree.logDebug("_loadContent: succeeded - %o", this);
+ }
+ } catch(e) {
+// alert(e);
+ this.setLazyNodeStatus(DTNodeStatus_Error);
+ this.tree.logWarning("_loadContent: failed - %o", e);
+ }
+ },
+
+ _expand: function(bExpand) {
+// this.tree.logDebug("dtnode._expand(%o) - %o", bExpand, this);
+ if( this.bExpanded == bExpand ) {
+// this.tree.logDebug("dtnode._expand(%o) IGNORED - %o", bExpand, this);
+ return;
+ }
+ var opts = this.tree.options;
+ if( !bExpand && this.getLevel()<opts.minExpandLevel ) {
+ this.tree.logDebug("dtnode._expand(%o) forced expand - %o", bExpand, this);
+ return;
+ }
+ if ( opts.onQueryExpand && opts.onQueryExpand.call(this.span, bExpand, this) == false )
+ return; // Callback returned false
+ this.bExpanded = bExpand;
+
+ // Persist expand state
+ if( opts.persist ) {
+ if( bExpand )
+ this.tree.persistence.addExpand(this.data.key);
+ else
+ this.tree.persistence.clearExpand(this.data.key);
+ }
+
+ this.render(false);
+
+ // Auto-collapse mode: collapse all siblings
+ if( this.bExpanded && this.parent && opts.autoCollapse ) {
+ var parents = this._parentList(false, true);
+ for(var i=0; i<parents.length; i++)
+ parents[i].collapseSiblings();
+ }
+
+ // If the currently active node is now hidden, deactivate it
+ if( opts.activeVisible && this.tree.activeNode && ! this.tree.activeNode.isVisible() ) {
+ this.tree.activeNode.deactivate();
+ }
+ // Expanding a lazy node: set 'loading...' and call callback
+ if( bExpand && this.data.isLazy && this.childList==null && !this.isLoading ) {
+ this._loadContent();
+ return;
+ }
+// this.tree.logDebug("_expand: start div toggle - %o", this);
+
+ var fxDuration = opts.fx ? (opts.fx.duration || 200) : 0;
+ if( this.childList ) {
+ for(var i=0; i<this.childList.length; i++ ) {
+ var $child = $(this.childList[i].div);
+ if( fxDuration ) {
+ // This is a toggle, so only do it, if not already rendered (in)visible (issue 98)
+ if( bExpand != $child.is(':visible') )
+ $child.animate(opts.fx, fxDuration);
+ } else {
+ if( bExpand )
+ $child.show();
+ else
+ $child.hide(); // TODO: this seems to be slow, when called the first time for an element
+ }
+ }
+ }
+
+/* issue 109: using selector filter is really SLOW.
+ // issue 98: only toggle, if render hasn't set visibility already:
+ var filter = ">DIV" + (bExpand ? ":hidden" : ":visible");
+
+ if( opts.fx ) {
+ var duration = opts.fx.duration || 200;
+// $(">DIV", this.div).animate(opts.fx, duration);
+ $(filter, this.div).animate(opts.fx, duration);
+ } else {
+ $(filter, this.div).toggle();
+// var $d = $(">DIV", this.div);
+// this.tree.logDebug("_expand: got div, start toggle - %o", this);
+// $d.toggle();
+ }
+//*/
+// this.tree.logDebug("_expand: end div toggle - %o", this);
+
+ if ( opts.onExpand )
+ opts.onExpand.call(this.span, bExpand, this);
+ },
+
+ expand: function(flag) {
+ if( !this.childList && !this.data.isLazy && flag )
+ return; // Prevent expanding empty nodes
+ if( this.parent == null && this.tree.options.minExpandLevel>0 && !flag )
+ return; // Prevent collapsing the root
+ this._expand(flag);
+ },
+
+ toggleExpand: function() {
+ this.expand(!this.bExpanded);
+ },
+
+ collapseSiblings: function() {
+ if( this.parent == null )
+ return;
+ var ac = this.parent.childList;
+ for (var i=0; i<ac.length; i++) {
+ if ( ac[i] !== this && ac[i].bExpanded )
+ ac[i]._expand(false);
+ }
+ },
+
+ onClick: function(event) {
+// this.tree.logDebug("dtnode.onClick(" + event.type + "): dtnode:" + this + ", button:" + event.button + ", which: " + event.which);
+ var targetType = this.getEventTargetType(event);
+ if( targetType == "expander" ) {
+ // Clicking the expander icon always expands/collapses
+ this.toggleExpand();
+ this.focus(); // issue 95
+ } else if( targetType == "checkbox" ) {
+ // Clicking the checkbox always (de)selects
+ this.toggleSelect();
+ this.focus(); // issue 95
+ } else {
+ this._userActivate();
+ // Chrome and Safari don't focus the a-tag on click
+ this.span.getElementsByTagName("a")[0].focus();
+ }
+ // Make sure that clicks stop, otherwise <a href='#'> jumps to the top
+ return false;
+ },
+
+ onDblClick: function(event) {
+// this.tree.logDebug("dtnode.onDblClick(" + event.type + "): dtnode:" + this + ", button:" + event.button + ", which: " + event.which);
+ },
+
+ onKeydown: function(event) {
+// this.tree.logDebug("dtnode.onKeydown(" + event.type + "): dtnode:" + this + ", charCode:" + event.charCode + ", keyCode: " + event.keyCode + ", which: " + event.which);
+ var handled = true;
+// alert("keyDown" + event.which);
+
+ switch( event.which ) {
+ // charCodes:
+// case 43: // '+'
+ case 107: // '+'
+ case 187: // '+' @ Chrome, Safari
+ if( !this.bExpanded ) this.toggleExpand();
+ break;
+// case 45: // '-'
+ case 109: // '-'
+ case 189: // '+' @ Chrome, Safari
+ if( this.bExpanded ) this.toggleExpand();
+ break;
+ //~ case 42: // '*'
+ //~ break;
+ //~ case 47: // '/'
+ //~ break;
+ // case 13: // <enter>
+ // <enter> on a focused <a> tag seems to generate a click-event.
+ // this._userActivate();
+ // break;
+ case 32: // <space>
+ this._userActivate();
+ break;
+ case 8: // <backspace>
+ if( this.parent )
+ this.parent.focus();
+ break;
+ case 37: // <left>
+ if( this.bExpanded ) {
+ this.toggleExpand();
+ this.focus();
+ } else if( this.parent && (this.tree.options.rootVisible || this.parent.parent) ) {
+ this.parent.focus();
+ }
+ break;
+ case 39: // <right>
+ if( !this.bExpanded && (this.childList || this.data.isLazy) ) {
+ this.toggleExpand();
+ this.focus();
+ } else if( this.childList ) {
+ this.childList[0].focus();
+ }
+ break;
+ case 38: // <up>
+ var sib = this.prevSibling();
+ while( sib && sib.bExpanded && sib.childList )
+ sib = sib.childList[sib.childList.length-1];
+ if( !sib && this.parent && (this.tree.options.rootVisible || this.parent.parent) )
+ sib = this.parent;
+ if( sib ) sib.focus();
+ break;
+ case 40: // <down>
+ var sib;
+ if( this.bExpanded && this.childList ) {
+ sib = this.childList[0];
+ } else {
+ var parents = this._parentList(false, true);
+ for(var i=parents.length-1; i>=0; i--) {
+ sib = parents[i].nextSibling();
+ if( sib ) break;
+ }
+ }
+ if( sib ) sib.focus();
+ break;
+ default:
+ handled = false;
+ }
+ // Return false, if handled, to prevent default processing
+ return !handled;
+ },
+
+ onKeypress: function(event) {
+ // onKeypress is only hooked to allow user callbacks.
+ // We don't process it, because IE and Safari don't fire keypress for cursor keys.
+// this.tree.logDebug("dtnode.onKeypress(" + event.type + "): dtnode:" + this + ", charCode:" + event.charCode + ", keyCode: " + event.keyCode + ", which: " + event.which);
+ },
+
+ onFocus: function(event) {
+ // Handles blur and focus events.
+// this.tree.logDebug("dtnode.onFocus(%o): %o", event, this);
+ var opts = this.tree.options;
+ if ( event.type=="blur" || event.type=="focusout" ) {
+ if ( opts.onBlur ) // Pass element as 'this' (jQuery convention)
+ opts.onBlur.call(this.span, this);
+ if( this.tree.tnFocused )
+ $(this.tree.tnFocused.span).removeClass(opts.classNames.focused);
+ this.tree.tnFocused = null;
+ if( opts.persist )
+ $.cookie(opts.cookieId+"-focus", "", opts.cookie);
+ } else if ( event.type=="focus" || event.type=="focusin") {
+ // Fix: sometimes the blur event is not generated
+ if( this.tree.tnFocused && this.tree.tnFocused !== this ) {
+ this.tree.logDebug("dtnode.onFocus: out of sync: curFocus: %o", this.tree.tnFocused);
+ $(this.tree.tnFocused.span).removeClass(opts.classNames.focused);
+ }
+ this.tree.tnFocused = this;
+ if ( opts.onFocus ) // Pass element as 'this' (jQuery convention)
+ opts.onFocus.call(this.span, this);
+ $(this.tree.tnFocused.span).addClass(opts.classNames.focused);
+ if( opts.persist )
+ $.cookie(opts.cookieId+"-focus", this.data.key, opts.cookie);
+ }
+ // TODO: return anything?
+// return false;
+ },
+
+ visit: function(fn, data, includeSelf) {
+ // Call fn(dtnode, data) for all child nodes. Stop iteration, if fn() returns false.
+ var n = 0;
+ if( includeSelf == true ) {
+ if( fn(this, data) == false )
+ return 1;
+ n++;
+ }
+ if ( this.childList )
+ for (var i=0; i<this.childList.length; i++)
+ n += this.childList[i].visit(fn, data, true);
+ return n;
+ },
+
+ remove: function() {
+ // Remove this node
+// this.tree.logDebug ("%o.remove()", this);
+ if ( this === this.tree.root )
+ return false;
+ return this.parent.removeChild(this);
+ },
+
+ removeChild: function(tn) {
+ // Remove tn from list of direct children.
+ var ac = this.childList;
+ if( ac.length == 1 ) {
+ if( tn !== ac[0] )
+ throw "removeChild: invalid child";
+ return this.removeChildren();
+ }
+ if( tn === this.tree.activeNode )
+ tn.deactivate();
+ if( this.tree.options.persist ) {
+ if( tn.bSelected )
+ this.tree.persistence.clearSelect(tn.data.key);
+ if ( tn.bExpanded )
+ this.tree.persistence.clearExpand(tn.data.key);
+ }
+ tn.removeChildren(true);
+ this.div.removeChild(tn.div);
+ for(var i=0; i<ac.length; i++) {
+ if( ac[i] === tn ) {
+ this.childList.splice(i, 1);
+ delete tn;
+ break;
+ }
+ }
+ },
+
+ removeChildren: function(isRecursiveCall, retainPersistence) {
+ // Remove all child nodes (more efficiently than recursive remove())
+// this.tree.logDebug ("%o.removeChildren(%o)", this, isRecursiveCall);
+ var tree = this.tree;
+ var ac = this.childList;
+ if( ac ) {
+ for(var i=0; i<ac.length; i++) {
+ var tn=ac[i];
+// this.tree.logDebug ("del %o", tn);
+ if ( tn === tree.activeNode && !retainPersistence )
+ tn.deactivate();
+ if( this.tree.options.persist && !retainPersistence ) {
+ if( tn.bSelected )
+ this.tree.persistence.clearSelect(tn.data.key);
+ if ( tn.bExpanded )
+ this.tree.persistence.clearExpand(tn.data.key);
+ }
+ tn.removeChildren(true, retainPersistence);
+ this.div.removeChild(tn.div);
+ delete tn;
+ }
+ this.childList = null;
+ }
+ if( ! isRecursiveCall ) {
+// this._expand(false);
+// this.isRead = false;
+ this.isLoading = false;
+ this.render(false, false);
+ }
+ },
+
+ reload: function(force) {
+ // Discard lazy content (and reload, if node was expanded).
+ if( this.parent == null )
+ return this.tree.reload();
+
+ if( ! this.data.isLazy )
+ throw "node.reload() requires lazy nodes.";
+ if( this.bExpanded ) {
+ this.expand(false);
+ this.removeChildren();
+ this.expand(true);
+ } else {
+ this.removeChildren();
+ if( force )
+ this._loadContent();
+ }
+ },
+
+ _addChildNode: function(dtnode, beforeNode) {
+ /**
+ * Internal function to add one single DynatreeNode as a child.
+ *
+ */
+ var tree = this.tree;
+ var opts = tree.options;
+ var pers = tree.persistence;
+
+// tree.logDebug("%o._addChildNode(%o)", this, dtnode);
+
+ // --- Update and fix dtnode attributes if necessary
+ dtnode.parent = this;
+// if( beforeNode && (beforeNode.parent !== this || beforeNode === dtnode ) )
+// throw "<beforeNode> must be another child of <this>";
+
+ // --- Add dtnode as a child
+ if ( this.childList==null ) {
+ this.childList = [];
+ } else if( ! beforeNode ) {
+ // Fix 'lastsib'
+ $(this.childList[this.childList.length-1].span).removeClass(opts.classNames.lastsib);
+ }
+ if( beforeNode ) {
+ var iBefore = $.inArray(beforeNode, this.childList);
+ if( iBefore < 0 )
+ throw "<beforeNode> must be a child of <this>";
+ this.childList.splice(iBefore, 0, dtnode);
+// alert(this.childList);
+ } else {
+ // Append node
+ this.childList.push(dtnode);
+ }
+
+ // --- Handle persistence
+ // Initial status is read from cookies, if persistence is active and
+ // cookies are already present.
+ // Otherwise the status is read from the data attributes and then persisted.
+ var isInitializing = tree.isInitializing();
+ if( opts.persist && pers.cookiesFound && isInitializing ) {
+ // Init status from cookies
+// tree.logDebug("init from cookie, pa=%o, dk=%o", pers.activeKey, dtnode.data.key);
+ if( pers.activeKey == dtnode.data.key )
+ tree.activeNode = dtnode;
+ if( pers.focusedKey == dtnode.data.key )
+ tree.focusNode = dtnode;
+ dtnode.bExpanded = ($.inArray(dtnode.data.key, pers.expandedKeyList) >= 0);
+ dtnode.bSelected = ($.inArray(dtnode.data.key, pers.selectedKeyList) >= 0);
+// tree.logDebug(" key=%o, bSelected=%o", dtnode.data.key, dtnode.bSelected);
+ } else {
+ // Init status from data (Note: we write the cookies after the init phase)
+// tree.logDebug("init from data");
+ if( dtnode.data.activate ) {
+ tree.activeNode = dtnode;
+ if( opts.persist )
+ pers.activeKey = dtnode.data.key;
+ }
+ if( dtnode.data.focus ) {
+ tree.focusNode = dtnode;
+ if( opts.persist )
+ pers.focusedKey = dtnode.data.key;
+ }
+ dtnode.bExpanded = ( dtnode.data.expand == true ); // Collapsed by default
+ if( dtnode.bExpanded && opts.persist )
+ pers.addExpand(dtnode.data.key);
+ dtnode.bSelected = ( dtnode.data.select == true ); // Deselected by default
+/*
+ Doesn't work, cause pers.selectedKeyList may be null
+ if( dtnode.bSelected && opts.selectMode==1
+ && pers.selectedKeyList && pers.selectedKeyList.length>0 ) {
+ tree.logWarning("Ignored multi-selection in single-mode for %o", dtnode);
+ dtnode.bSelected = false; // Fixing bad input data (multi selection for mode:1)
+ }
+*/
+ if( dtnode.bSelected && opts.persist )
+ pers.addSelect(dtnode.data.key);
+ }
+
+ // Always expand, if it's below minExpandLevel
+// tree.logDebug ("%o._addChildNode(%o), l=%o", this, dtnode, dtnode.getLevel());
+ if ( opts.minExpandLevel >= dtnode.getLevel() ) {
+// tree.logDebug ("Force expand for %o", dtnode);
+ this.bExpanded = true;
+ }
+
+ // In multi-hier mode, update the parents selection state
+ // issue #82: only if not initializing, because the children may not exist yet
+// if( !dtnode.data.isStatusNode && opts.selectMode==3 && !isInitializing )
+// dtnode._fixSelectionState();
+
+ // In multi-hier mode, update the parents selection state
+ if( dtnode.bSelected && opts.selectMode==3 ) {
+ var p = this;
+ while( p ) {
+ if( !p.hasSubSel )
+ p._setSubSel(true);
+ p = p.parent;
+ }
+ }
+ // render this node and the new child
+ if ( tree.bEnableUpdate )
+ this.render(true, true);
+
+ return dtnode;
+ },
+
+ addChild: function(obj, beforeNode) {
+ /**
+ * Add a node object as child.
+ *
+ * This should be the only place, where a DynaTreeNode is constructed!
+ * (Except for the root node creation in the tree constructor)
+ *
+ * @param obj A JS object (may be recursive) or an array of those.
+ * @param {DynaTreeNode} beforeNode (optional) sibling node.
+ *
+ * Data format: array of node objects, with optional 'children' attributes.
+ * [
+ * { title: "t1", isFolder: true, ... }
+ * { title: "t2", isFolder: true, ...,
+ * children: [
+ * {title: "t2.1", ..},
+ * {..}
+ * ]
+ * }
+ * ]
+ * A simple object is also accepted instead of an array.
+ *
+ */
+// this.tree.logDebug("%o.addChild(%o, %o)", this, obj, beforeNode);
+ if( !obj || obj.length==0 ) // Passed null or undefined or empty array
+ return;
+ if( obj instanceof DynaTreeNode )
+ return this._addChildNode(obj, beforeNode);
+ if( !obj.length ) // Passed a single data object
+ obj = [ obj ];
+
+ var prevFlag = this.tree.enableUpdate(false);
+
+ var tnFirst = null;
+ for (var i=0; i<obj.length; i++) {
+ var data = obj[i];
+ var dtnode = this._addChildNode(new DynaTreeNode(this, this.tree, data), beforeNode);
+ if( !tnFirst ) tnFirst = dtnode;
+ // Add child nodes recursively
+ if( data.children )
+ dtnode.addChild(data.children, null);
+ }
+ this.tree.enableUpdate(prevFlag);
+ return tnFirst;
+ },
+
+ append: function(obj) {
+ this.tree.logWarning("node.append() is deprecated (use node.addChild() instead).");
+ return this.addChild(obj, null);
+ },
+
+ appendAjax: function(ajaxOptions) {
+ this.removeChildren(false, true);
+ this.setLazyNodeStatus(DTNodeStatus_Loading);
+ // Ajax option inheritance: $.ajaxSetup < $.ui.dynatree.defaults.ajaxDefaults < tree.options.ajaxDefaults < ajaxOptions
+ var self = this;
+ var orgSuccess = ajaxOptions.success;
+ var orgError = ajaxOptions.error;
+ var options = $.extend({}, this.tree.options.ajaxDefaults, ajaxOptions, {
+/*
+ complete: function(req, textStatus){
+ alert("ajax complete");
+ },
+ timeout: 5000, // 5 sec
+*/
+ success: function(data, textStatus){
+ // <this> is the request options
+// self.tree.logDebug("appendAjax().success");
+ var prevPhase = self.tree.phase;
+ self.tree.phase = "init";
+// self.append(data);
+ self.addChild(data, null);
+ self.tree.phase = "postInit";
+ self.setLazyNodeStatus(DTNodeStatus_Ok);
+ if( orgSuccess )
+ orgSuccess.call(options, self);
+ self.tree.phase = prevPhase;
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown){
+ // <this> is the request options
+// self.tree.logWarning("appendAjax failed: %o:\n%o\n%o", textStatus, XMLHttpRequest, errorThrown);
+ self.tree.logWarning("appendAjax failed:", textStatus, ":\n", XMLHttpRequest, "\n", errorThrown);
+ self.setLazyNodeStatus(DTNodeStatus_Error, {info: textStatus, tooltip: ""+errorThrown});
+ if( orgError )
+ orgError.call(options, self, XMLHttpRequest, textStatus, errorThrown);
+ }
+ });
+ $.ajax(options);
+ },
+ // --- end of class
+ lastentry: undefined
+}
+
+/*************************************************************************
+ * class DynaTreeStatus
+ */
+
+var DynaTreeStatus = Class.create();
+
+
+DynaTreeStatus._getTreePersistData = function(cookieId, cookieOpts) {
+ // Static member: Return persistence information from cookies
+ var ts = new DynaTreeStatus(cookieId, cookieOpts);
+ ts.read();
+ return ts.toDict();
+}
+// Make available in global scope
+getDynaTreePersistData = DynaTreeStatus._getTreePersistData;
+
+
+DynaTreeStatus.prototype = {
+ // Constructor
+ initialize: function(cookieId, cookieOpts) {
+ this._log("DynaTreeStatus: initialize");
+ if( cookieId === undefined )
+ cookieId = $.ui.dynatree.defaults.cookieId;
+ cookieOpts = $.extend({}, $.ui.dynatree.defaults.cookie, cookieOpts);
+
+ this.cookieId = cookieId;
+ this.cookieOpts = cookieOpts;
+ this.cookiesFound = undefined;
+ this.activeKey = null;
+ this.focusedKey = null;
+ this.expandedKeyList = null;
+ this.selectedKeyList = null;
+ },
+ // member functions
+ _log: function(msg) {
+ // this.logDebug("_changeNodeList(%o): nodeList:%o, idx:%o", mode, nodeList, idx);
+ Array.prototype.unshift.apply(arguments, ["debug"]);
+ _log.apply(this, arguments);
+ },
+ read: function() {
+ this._log("DynaTreeStatus: read");
+ // Read or init cookies.
+ this.cookiesFound = false;
+
+ var cookie = $.cookie(this.cookieId + "-active");
+ this.activeKey = ( cookie == null ) ? "" : cookie;
+ if( cookie != null ) this.cookiesFound = true;
+
+ cookie = $.cookie(this.cookieId + "-focus");
+ this.focusedKey = ( cookie == null ) ? "" : cookie;
+ if( cookie != null ) this.cookiesFound = true;
+
+ cookie = $.cookie(this.cookieId + "-expand");
+ this.expandedKeyList = ( cookie == null ) ? [] : cookie.split(",");
+ if( cookie != null ) this.cookiesFound = true;
+
+ cookie = $.cookie(this.cookieId + "-select");
+ this.selectedKeyList = ( cookie == null ) ? [] : cookie.split(",");
+ if( cookie != null ) this.cookiesFound = true;
+ },
+ write: function() {
+ this._log("DynaTreeStatus: write");
+ $.cookie(this.cookieId + "-active", ( this.activeKey == null ) ? "" : this.activeKey, this.cookieOpts);
+ $.cookie(this.cookieId + "-focus", ( this.focusedKey == null ) ? "" : this.focusedKey, this.cookieOpts);
+ $.cookie(this.cookieId + "-expand", ( this.expandedKeyList == null ) ? "" : this.expandedKeyList.join(","), this.cookieOpts);
+ $.cookie(this.cookieId + "-select", ( this.selectedKeyList == null ) ? "" : this.selectedKeyList.join(","), this.cookieOpts);
+ },
+ addExpand: function(key) {
+ this._log("addExpand(%o)", key);
+ if( $.inArray(key, this.expandedKeyList) < 0 ) {
+ this.expandedKeyList.push(key);
+ $.cookie(this.cookieId + "-expand", this.expandedKeyList.join(","), this.cookieOpts);
+ }
+ },
+ clearExpand: function(key) {
+ this._log("clearExpand(%o)", key);
+ var idx = $.inArray(key, this.expandedKeyList);
+ if( idx >= 0 ) {
+ this.expandedKeyList.splice(idx, 1);
+ $.cookie(this.cookieId + "-expand", this.expandedKeyList.join(","), this.cookieOpts);
+ }
+ },
+ addSelect: function(key) {
+ this._log("addSelect(%o)", key);
+ if( $.inArray(key, this.selectedKeyList) < 0 ) {
+ this.selectedKeyList.push(key);
+ $.cookie(this.cookieId + "-select", this.selectedKeyList.join(","), this.cookieOpts);
+ }
+ },
+ clearSelect: function(key) {
+ this._log("clearSelect(%o)", key);
+ var idx = $.inArray(key, this.selectedKeyList);
+ if( idx >= 0 ) {
+ this.selectedKeyList.splice(idx, 1);
+ $.cookie(this.cookieId + "-select", this.selectedKeyList.join(","), this.cookieOpts);
+ }
+ },
+ isReloading: function() {
+ return this.cookiesFound == true;
+ },
+ toDict: function() {
+ return {
+ cookiesFound: this.cookiesFound,
+ activeKey: this.activeKey,
+ focusedKey: this.activeKey,
+ expandedKeyList: this.expandedKeyList,
+ selectedKeyList: this.selectedKeyList
+ };
+ },
+ // --- end of class
+ lastentry: undefined
+};
+
+
+/*************************************************************************
+ * class DynaTree
+ */
+
+var DynaTree = Class.create();
+
+// --- Static members ----------------------------------------------------------
+
+DynaTree.version = "$Version: 0.5.4$";
+/*
+DynaTree._initTree = function() {
+};
+
+DynaTree._bind = function() {
+};
+*/
+//--- Class members ------------------------------------------------------------
+
+DynaTree.prototype = {
+ // Constructor
+// initialize: function(divContainer, options) {
+ initialize: function($widget) {
+ // instance members
+ this.phase = "init";
+ this.$widget = $widget;
+ this.options = $widget.options;
+ this.$tree = $widget.element;
+ // find container element
+ this.divTree = this.$tree.get(0);
+ },
+
+ // member functions
+
+ _load: function() {
+ var $widget = this.$widget;
+ var opts = this.options;
+ this.bEnableUpdate = true;
+ this._nodeCount = 1;
+ this.activeNode = null;
+ this.focusNode = null;
+
+ // If a 'options.classNames' dictionary was passed, still use defaults
+ // for undefined classes:
+ if( opts.classNames !== $.ui.dynatree.defaults.classNames ) {
+ opts.classNames = $.extend({}, $.ui.dynatree.defaults.classNames, opts.classNames);
+ }
+ // Guess skin path, if not specified
+ if(!opts.imagePath) {
+ $("script").each( function () {
+ // Eclipse syntax parser breaks on this expression, so put it at the bottom:
+ if( this.src.search(_rexDtLibName) >= 0 ) {
+ if( this.src.indexOf("/")>=0 ) // issue #47
+ opts.imagePath = this.src.slice(0, this.src.lastIndexOf("/")) + "/skin/";
+ else
+ opts.imagePath = "skin/";
+// logMsg("Guessing imagePath from '%s': '%s'", this.src, opts.imagePath);
+ return false; // first match
+ }
+ });
+ }
+
+ this.persistence = new DynaTreeStatus(opts.cookieId, opts.cookie);
+ if( opts.persist ) {
+ if( !$.cookie )
+ _log("warn", "Please include jquery.cookie.js to use persistence.");
+ this.persistence.read();
+ }
+ this.logDebug("DynaTree.persistence: %o", this.persistence.toDict());
+
+ // Cached tag strings
+ this.cache = {
+ tagEmpty: "<span class='" + opts.classNames.empty + "'></span>",
+ tagVline: "<span class='" + opts.classNames.vline + "'></span>",
+ tagExpander: "<span class='" + opts.classNames.expander + "'></span>",
+ tagConnector: "<span class='" + opts.classNames.connector + "'></span>",
+ tagNodeIcon: "<span class='" + opts.classNames.nodeIcon + "'></span>",
+ tagCheckbox: "<span class='" + opts.classNames.checkbox + "'></span>",
+ lastentry: undefined
+ };
+
+ // Clear container, in case it contained some 'waiting' or 'error' text
+ // for clients that don't support JS.
+ // We don't do this however, if we try to load from an embedded UL element.
+ if( opts.children || (opts.initAjax && opts.initAjax.url) || opts.initId )
+ $(this.divTree).empty();
+ else if( this.divRoot )
+ $(this.divRoot).remove();
+
+ // create the root element
+ this.tnRoot = new DynaTreeNode(null, this, {title: opts.title, key: "root"});
+ this.tnRoot.data.isFolder = true;
+ this.tnRoot.render(false, false);
+ this.divRoot = this.tnRoot.div;
+ this.divRoot.className = opts.classNames.container;
+ // add root to container
+ // TODO: this should be delayed until all children have been created for performance reasons
+ this.divTree.appendChild(this.divRoot);
+
+ var root = this.tnRoot;
+ var isReloading = ( opts.persist && this.persistence.isReloading() );
+ var isLazy = false;
+ var prevFlag = this.enableUpdate(false);
+
+ this.logDebug("Dynatree._load(): read tree structure...");
+
+ // Init tree structure
+ if( opts.children ) {
+ // Read structure from node array
+ root.addChild(opts.children);
+
+ } else if( opts.initAjax && opts.initAjax.url ) {
+ // Init tree from AJAX request
+ isLazy = true;
+ root.data.isLazy = true;
+ this._reloadAjax();
+
+ } else if( opts.initId ) {
+ // Init tree from another UL element
+ this._createFromTag(root, $("#"+opts.initId));
+
+ } else {
+ // Init tree from the first UL element inside the container <div>
+ var $ul = this.$tree.find(">ul").hide();
+ this._createFromTag(root, $ul);
+ $ul.remove();
+ }
+
+ this._checkConsistency();
+ // Render html markup
+ this.logDebug("Dynatree._load(): render nodes...");
+ this.enableUpdate(prevFlag);
+
+ // bind event handlers
+ this.logDebug("Dynatree._load(): bind events...");
+ this.$widget.bind();
+
+ // --- Post-load processing
+ this.logDebug("Dynatree._load(): postInit...");
+ this.phase = "postInit";
+
+ // In persist mode, make sure that cookies are written, even if they are empty
+ if( opts.persist ) {
+ this.persistence.write();
+ }
+
+ // Set focus, if possible (this will also fire an event and write a cookie)
+ if( this.focusNode && this.focusNode.isVisible() ) {
+ this.logDebug("Focus on init: %o", this.focusNode);
+ this.focusNode.focus();
+ }
+
+ if( !isLazy && opts.onPostInit ) {
+ opts.onPostInit.call(this, isReloading, false);
+ }
+
+ this.phase = "idle";
+ },
+
+ _reloadAjax: function() {
+ // Reload
+ var opts = this.options;
+ if( ! opts.initAjax || ! opts.initAjax.url )
+ throw "tree.reload() requires 'initAjax' mode.";
+ var pers = this.persistence;
+ var ajaxOpts = $.extend({}, opts.initAjax);
+ // Append cookie info to the request
+// this.logDebug("reloadAjax: key=%o, an.key:%o", pers.activeKey, this.activeNode?this.activeNode.data.key:"?");
+ if( ajaxOpts.addActiveKey )
+ ajaxOpts.data.activeKey = pers.activeKey;
+ if( ajaxOpts.addFocusedKey )
+ ajaxOpts.data.focusedKey = pers.focusedKey;
+ if( ajaxOpts.addExpandedKeyList )
+ ajaxOpts.data.expandedKeyList = pers.expandedKeyList.join(",");
+ if( ajaxOpts.addSelectedKeyList )
+ ajaxOpts.data.selectedKeyList = pers.selectedKeyList.join(",");
+
+ // Set up onPostInit callback to be called when Ajax returns
+ if( opts.onPostInit ) {
+ if( ajaxOpts.success )
+ this.logWarning("initAjax: success callback is ignored when onPostInit was specified.");
+ if( ajaxOpts.error )
+ this.logWarning("initAjax: error callback is ignored when onPostInit was specified.");
+ var isReloading = pers.isReloading();
+ ajaxOpts["success"] = function(dtnode) { opts.onPostInit.call(dtnode.tree, isReloading, false); };
+ ajaxOpts["error"] = function(dtnode) { opts.onPostInit.call(dtnode.tree, isReloading, true); };
+ }
+ this.logDebug("Dynatree._init(): send Ajax request...");
+ this.tnRoot.appendAjax(ajaxOpts);
+ },
+
+ toString: function() {
+ return "DynaTree '" + this.options.title + "'";
+ },
+
+ toDict: function() {
+ return this.tnRoot.toDict(true);
+ },
+
+ getPersistData: function() {
+ return this.persistence.toDict();
+ },
+
+ logDebug: function(msg) {
+ if( this.options.debugLevel >= 2 ) {
+ Array.prototype.unshift.apply(arguments, ["debug"]);
+ _log.apply(this, arguments);
+ }
+ },
+
+ logInfo: function(msg) {
+ if( this.options.debugLevel >= 1 ) {
+ Array.prototype.unshift.apply(arguments, ["info"]);
+ _log.apply(this, arguments);
+ }
+ },
+
+ logWarning: function(msg) {
+ Array.prototype.unshift.apply(arguments, ["warn"]);
+ _log.apply(this, arguments);
+ },
+
+ isInitializing: function() {
+ return ( this.phase=="init" || this.phase=="postInit" );
+ },
+ isReloading: function() {
+ return ( this.phase=="init" || this.phase=="postInit" ) && this.options.persist && this.persistence.cookiesFound;
+ },
+ isUserEvent: function() {
+ return ( this.phase=="userEvent" );
+ },
+
+ redraw: function() {
+ this.logDebug("dynatree.redraw()...");
+ this.tnRoot.render(true, true);
+ this.logDebug("dynatree.redraw() done.");
+ },
+
+ reloadAjax: function() {
+ this.logWarning("tree.reloadAjax() is deprecated since v0.5.2 (use reload() instead).");
+ },
+
+ reload: function() {
+ this._load();
+ },
+
+ getRoot: function() {
+ return this.tnRoot;
+ },
+
+ getNodeByKey: function(key) {
+ // $("#...") has problems, if the key contains '.', so we use getElementById()
+// return $("#" + this.options.idPrefix + key).attr("dtnode");
+ var el = document.getElementById(this.options.idPrefix + key);
+ return ( el && el.dtnode ) ? el.dtnode : null;
+ },
+
+ getActiveNode: function() {
+ return this.activeNode;
+ },
+
+ reactivate: function(setFocus) {
+ // Re-fire onQueryActivate and onActivate events.
+ var node = this.activeNode;
+// this.logDebug("reactivate %o", node);
+ if( node ) {
+ this.activeNode = null; // Force re-activating
+ node.activate();
+ if( setFocus )
+ node.focus();
+ }
+ },
+
+ getSelectedNodes: function(stopOnParents) {
+ var nodeList = [];
+ this.tnRoot.visit(function(dtnode){
+ if( dtnode.bSelected ) {
+ nodeList.push(dtnode);
+ if( stopOnParents == true )
+ return false; // stop processing this branch
+ }
+ });
+ return nodeList;
+ },
+
+ activateKey: function(key) {
+ var dtnode = (key === null) ? null : this.getNodeByKey(key);
+ if( !dtnode ) {
+ if( this.activeNode )
+ this.activeNode.deactivate();
+ this.activeNode = null;
+ return null;
+ }
+ dtnode.focus();
+ dtnode.activate();
+ return dtnode;
+ },
+
+ selectKey: function(key, select) {
+ var dtnode = this.getNodeByKey(key);
+ if( !dtnode )
+ return null;
+ dtnode.select(select);
+ return dtnode;
+ },
+
+ enableUpdate: function(bEnable) {
+ if ( this.bEnableUpdate==bEnable )
+ return bEnable;
+ this.bEnableUpdate = bEnable;
+ if ( bEnable )
+ this.redraw();
+ return !bEnable; // return previous value
+ },
+
+ visit: function(fn, data, includeRoot) {
+ return this.tnRoot.visit(fn, data, includeRoot);
+ },
+
+ _createFromTag: function(parentTreeNode, $ulParent) {
+ // Convert a <UL>...</UL> list into children of the parent tree node.
+ var self = this;
+/*
+TODO: better?
+ this.$lis = $("li:has(a[href])", this.element);
+ this.$tabs = this.$lis.map(function() { return $("a", this)[0]; });
+ */
+ $ulParent.find(">li").each(function() {
+ var $li = $(this);
+ var $liSpan = $li.find(">span:first");
+ var title;
+ if( $liSpan.length ) {
+ // If a <li><span> tag is specified, use it literally.
+ title = $liSpan.html();
+ } else {
+ // If only a <li> tag is specified, use the trimmed string up to the next child <ul> tag.
+ title = $li.html();
+ var iPos = title.search(/<ul/i);
+ if( iPos>=0 )
+ title = $.trim(title.substring(0, iPos));
+ else
+ title = $.trim(title);
+// self.logDebug("%o", title);
+ }
+ // Parse node options from ID, title and class attributes
+ var data = {
+ title: title,
+ isFolder: $li.hasClass("folder"),
+ isLazy: $li.hasClass("lazy"),
+ expand: $li.hasClass("expanded"),
+ select: $li.hasClass("selected"),
+ activate: $li.hasClass("active"),
+ focus: $li.hasClass("focused")
+ };
+ if( $li.attr("title") )
+ data.tooltip = $li.attr("title");
+ if( $li.attr("id") )
+ data.key = $li.attr("id");
+ // If a data attribute is present, evaluate as a JavaScript object
+ if( $li.attr("data") ) {
+ var dataAttr = $.trim($li.attr("data"));
+ if( dataAttr ) {
+ if( dataAttr.charAt(0) != "{" )
+ dataAttr = "{" + dataAttr + "}"
+ try {
+ $.extend(data, eval("(" + dataAttr + ")"));
+ } catch(e) {
+ throw ("Error parsing node data: " + e + "\ndata:\n'" + dataAttr + "'");
+ }
+ }
+ }
+ childNode = parentTreeNode.addChild(data);
+ // Recursive reading of child nodes, if LI tag contains an UL tag
+ var $ul = $li.find(">ul:first");
+ if( $ul.length ) {
+ self._createFromTag(childNode, $ul); // must use 'self', because 'this' is the each() context
+ }
+ });
+ },
+
+ _checkConsistency: function() {
+// this.logDebug("tree._checkConsistency() NOT IMPLEMENTED - %o", this);
+ },
+
+ // --- end of class
+ lastentry: undefined
+};
+
+/*************************************************************************
+ * widget $(..).dynatree
+ */
+
+$.widget("ui.dynatree", {
+ init: function() {
+ // ui.core 1.6 renamed init() to _init(): this stub assures backward compatibility
+ _log("warn", "ui.dynatree.init() was called; you should upgrade to ui.core.js v1.6 or higher.");
+ return this._init();
+ },
+
+ _init: function() {
+ if( parseFloat($.ui.version) < 1.8 ) {
+ // jquery.ui.core 1.8 renamed _init() to _create(): this stub assures backward compatibility
+ _log("info", "ui.dynatree._init() was called; consider upgrading to jquery.ui.core.js v1.8 or higher.");
+ return this._create();
+ }
+ // jquery.ui.core 1.8 still uses _init() to perform "default functionality"
+ _log("debug", "ui.dynatree._init() was called; no current default functionality.");
+ },
+
+ _create: function() {
+ if( parseFloat($.ui.version) >= 1.8 ) {
+ this.options = $.extend(true, {}, $[this.namespace][this.widgetName].defaults, this.options);
+ }
+ logMsg("Dynatree._create(): version='%s', debugLevel=%o.", DynaTree.version, this.options.debugLevel);
+ var opts = this.options;
+ // The widget framework supplies this.element and this.options.
+ this.options.event += ".dynatree"; // namespace event
+
+ var divTree = this.element.get(0);
+/* // Clear container, in case it contained some 'waiting' or 'error' text
+ // for clients that don't support JS
+ if( opts.children || (opts.initAjax && opts.initAjax.url) || opts.initId )
+ $(divTree).empty();
+*/
+ // Create the DynaTree object
+ this.tree = new DynaTree(this);
+ this.tree._load();
+ this.tree.logDebug("Dynatree._create(): done.");
+ },
+
+ bind: function() {
+ var $this = this.element;
+ var o = this.options;
+
+ // Prevent duplicate binding
+ this.unbind();
+
+ // Tool function to get dtnode from the event target:
+ function __getNodeFromElement(el) {
+ var iMax = 5;
+ while( el && iMax-- ) {
+ if( el.dtnode ) return el.dtnode;
+ el = el.parentNode;
+ };
+ return null;
+ }
+
+ var eventNames = "click.dynatree dblclick.dynatree";
+ if( o.keyboard ) // Note: leading ' '!
+ eventNames += " keypress.dynatree keydown.dynatree";
+ $this.bind(eventNames, function(event){
+ var dtnode = __getNodeFromElement(event.target);
+ if( !dtnode )
+ return true; // Allow bubbling of other events
+ var prevPhase = dtnode.tree.phase;
+ dtnode.tree.phase = "userEvent";
+ try {
+ dtnode.tree.logDebug("bind(%o): dtnode: %o", event, dtnode);
+
+ switch(event.type) {
+ case "click":
+ return ( o.onClick && o.onClick(dtnode, event)===false ) ? false : dtnode.onClick(event);
+ case "dblclick":
+ return ( o.onDblClick && o.onDblClick(dtnode, event)===false ) ? false : dtnode.onDblClick(event);
+ case "keydown":
+ return ( o.onKeydown && o.onKeydown(dtnode, event)===false ) ? false : dtnode.onKeydown(event);
+ case "keypress":
+ return ( o.onKeypress && o.onKeypress(dtnode, event)===false ) ? false : dtnode.onKeypress(event);
+ };
+ } catch(e) {
+ var _ = null; // issue 117
+// dtnode.tree.logError("bind(%o): dtnode: %o", event, dtnode);
+ } finally {
+ dtnode.tree.phase = prevPhase;
+ }
+ });
+
+ // focus/blur don't bubble, i.e. are not delegated to parent <div> tags,
+ // so we use the addEventListener capturing phase.
+ // See http://www.howtocreate.co.uk/tutorials/javascript/domevents
+ function __focusHandler(event) {
+ // Handles blur and focus.
+ // Fix event for IE:
+ event = arguments[0] = $.event.fix( event || window.event );
+ var dtnode = __getNodeFromElement(event.target);
+ return dtnode ? dtnode.onFocus(event) : false;
+ }
+ var div = this.tree.divTree;
+ if( div.addEventListener ) {
+ div.addEventListener("focus", __focusHandler, true);
+ div.addEventListener("blur", __focusHandler, true);
+ } else {
+ div.onfocusin = div.onfocusout = __focusHandler;
+ }
+ // EVENTS
+ // disable click if event is configured to something else
+// if (!(/^click/).test(o.event))
+// this.$tabs.bind("click.tabs", function() { return false; });
+
+ },
+
+ unbind: function() {
+ this.element.unbind(".dynatree");
+ },
+
+/* TODO: we could handle option changes during runtime here (maybe to re-render, ...)
+ setData: function(key, value) {
+ this.tree.logDebug("dynatree.setData('" + key + "', '" + value + "')");
+ },
+*/
+ enable: function() {
+ this.bind();
+ // Call default disable(): remove -disabled from css:
+ $.widget.prototype.enable.apply(this, arguments);
+ },
+
+ disable: function() {
+ this.unbind();
+ // Call default disable(): add -disabled to css:
+ $.widget.prototype.disable.apply(this, arguments);
+ },
+
+ // --- getter methods (i.e. NOT returning a reference to $)
+ getTree: function() {
+ return this.tree;
+ },
+
+ getRoot: function() {
+ return this.tree.getRoot();
+ },
+
+ getActiveNode: function() {
+ return this.tree.getActiveNode();
+ },
+
+ getSelectedNodes: function() {
+ return this.tree.getSelectedNodes();
+ },
+
+ // ------------------------------------------------------------------------
+ lastentry: undefined
+});
+
+
+// The following methods return a value (thus breaking the jQuery call chain):
+
+$.ui.dynatree.getter = "getTree getRoot getActiveNode getSelectedNodes";
+
+
+// Plugin default options:
+
+$.ui.dynatree.defaults = {
+ title: "Dynatree root", // Name of the root node.
+ rootVisible: false, // Set to true, to make the root node visible.
+ minExpandLevel: 1, // 1: root node is not collapsible
+ imagePath: null, // Path to a folder containing icons. Defaults to 'skin/' subdirectory.
+ children: null, // Init tree structure from this object array.
+ initId: null, // Init tree structure from a <ul> element with this ID.
+ initAjax: null, // Ajax options used to initialize the tree strucuture.
+ autoFocus: true, // Set focus to first child, when expanding or lazy-loading.
+ keyboard: true, // Support keyboard navigation.
+ persist: false, // Persist expand-status to a cookie
+ autoCollapse: false, // Automatically collapse all siblings, when a node is expanded.
+ clickFolderMode: 3, // 1:activate, 2:expand, 3:activate and expand
+ activeVisible: true, // Make sure, active nodes are visible (expanded).
+ checkbox: false, // Show checkboxes.
+ selectMode: 2, // 1:single, 2:multi, 3:multi-hier
+ fx: null, // Animations, e.g. null or { height: "toggle", duration: 200 }
+
+ // Low level event handlers: onEvent(dtnode, event): return false, to stop default processing
+ onClick: null, // null: generate focus, expand, activate, select events.
+ onDblClick: null, // (No default actions.)
+ onKeydown: null, // null: generate keyboard navigation (focus, expand, activate).
+ onKeypress: null, // (No default actions.)
+ onFocus: null, // null: set focus to node.
+ onBlur: null, // null: remove focus from node.
+
+ // Pre-event handlers onQueryEvent(flag, dtnode): return false, to stop processing
+ onQueryActivate: null, // Callback(flag, dtnode) before a node is (de)activated.
+ onQuerySelect: null, // Callback(flag, dtnode) before a node is (de)selected.
+ onQueryExpand: null, // Callback(flag, dtnode) before a node is expanded/collpsed.
+
+ // High level event handlers
+ onPostInit: null, // Callback(isReloading, isError) when tree was (re)loaded.
+ onActivate: null, // Callback(dtnode) when a node is activated.
+ onDeactivate: null, // Callback(dtnode) when a node is deactivated.
+ onSelect: null, // Callback(flag, dtnode) when a node is (de)selected.
+ onExpand: null, // Callback(dtnode) when a node is expanded/collapsed.
+ onLazyRead: null, // Callback(dtnode) when a lazy node is expanded for the first time.
+
+ ajaxDefaults: { // Used by initAjax option
+ cache: false, // false: Append random '_' argument to the request url to prevent caching.
+ dataType: "json" // Expect json format and pass json object to callbacks.
+ },
+ strings: {
+ loading: "Loading…",
+ loadError: "Load error!"
+ },
+ idPrefix: "ui-dynatree-id-", // Used to generate node id's like <span id="ui-dynatree-id-<key>">.
+// cookieId: "ui-dynatree-cookie", // Choose a more unique name, to allow multiple trees.
+ cookieId: "dynatree", // Choose a more unique name, to allow multiple trees.
+ cookie: {
+ expires: null //7, // Days or Date; null: session cookie
+// path: "/", // Defaults to current page
+// domain: "jquery.com",
+// secure: true
+ },
+ // Class names used, when rendering the HTML markup.
+ // Note: if only single entries are passed for options.classNames, all other
+ // values are still set to default.
+ classNames: {
+ container: "ui-dynatree-container",
+ folder: "ui-dynatree-folder",
+ document: "ui-dynatree-document",
+
+ empty: "ui-dynatree-empty",
+ vline: "ui-dynatree-vline",
+ expander: "ui-dynatree-expander",
+ connector: "ui-dynatree-connector",
+ checkbox: "ui-dynatree-checkbox",
+ nodeIcon: "ui-dynatree-icon",
+ title: "ui-dynatree-title",
+
+ nodeError: "ui-dynatree-statusnode-error",
+ nodeWait: "ui-dynatree-statusnode-wait",
+ hidden: "ui-dynatree-hidden",
+ combinedExpanderPrefix: "ui-dynatree-exp-",
+ combinedIconPrefix: "ui-dynatree-ico-",
+// disabled: "ui-dynatree-disabled",
+ hasChildren: "ui-dynatree-has-children",
+ active: "ui-dynatree-active",
+ selected: "ui-dynatree-selected",
+ expanded: "ui-dynatree-expanded",
+ lazy: "ui-dynatree-lazy",
+ focused: "ui-dynatree-focused",
+ partsel: "ui-dynatree-partsel",
+ lastsib: "ui-dynatree-lastsib"
+ },
+ debugLevel: 1,
+
+ // ------------------------------------------------------------------------
+ lastentry: undefined
+};
+
+/**
+ * Reserved data attributes for a tree node.
+ */
+$.ui.dynatree.nodedatadefaults = {
+ title: null, // (required) Displayed name of the node (html is allowed here)
+ key: null, // May be used with activate(), select(), find(), ...
+ isFolder: false, // Use a folder icon. Also the node is expandable but not selectable.
+ isLazy: false, // Call onLazyRead(), when the node is expanded for the first time to allow for delayed creation of children.
+ tooltip: null, // Show this popup text.
+ icon: null, // Use a custom image (filename relative to tree.options.imagePath). 'null' for default icon, 'false' for no icon.
+ addClass: null, // Class name added to the node's span tag.
+ activate: false, // Initial active status.
+ focus: false, // Initial focused status.
+ expand: false, // Initial expanded status.
+ select: false, // Initial selected status.
+ hideCheckbox: false, // Suppress checkbox display for this node.
+ unselectable: false, // Prevent selection.
+// disabled: false,
+ // The following attributes are only valid if passed to some functions:
+ children: null, // Array of child nodes.
+ // NOTE: we can also add custom attributes here.
+ // This may then also be used in the onActivate(), onSelect() or onLazyTree() callbacks.
+ // ------------------------------------------------------------------------
+ lastentry: undefined
+};
+
+// ---------------------------------------------------------------------------
+})(jQuery);
+
+// Eclipse syntax parser breaks on this expression, so we put it at the bottom.
+var _rexDtLibName = /.*dynatree[^/]*\.js$/i;
Binary file static/june_2007_style/blue/dynatree_skin/customFolder1.gif has changed
--- /dev/null
+++ b/static/june_2007_style/blue/dynatree_skin/ui.dynatree.css
@@ -0,0 +1,295 @@
+/*******************************************************************************
+ * Tree container
+ */
+div.ui-dynatree-container
+{
+ font-family: tahoma, arial, helvetica;
+ font-size: 10pt; /* font size should not be too big */
+ white-space: nowrap;
+ padding: 3px;
+
+ background-color: white;
+ border: none;
+}
+
+/* Style, when control is disabled */
+.ui-dynatree-disabled div.ui-dynatree-container
+{
+ opacity: 0.5;
+/* filter: alpha(opacity=50); /* Yields a css warning */
+ background-color: silver;
+}
+
+
+/*******************************************************************************
+ * Vertical line image
+ */
+div.ui-dynatree-container img
+{
+ width: 16px;
+ height: 16px;
+ margin-left: 3px;
+ vertical-align: top;
+ border-style: none;
+}
+
+/*******************************************************************************
+ * Common icon definitions
+ */
+span.ui-dynatree-empty,
+span.ui-dynatree-vline,
+span.ui-dynatree-connector,
+span.ui-dynatree-expander,
+span.ui-dynatree-icon,
+span.ui-dynatree-checkbox,
+span.ui-dynatree-radio
+{
+ width: 16px;
+ height: 16px;
+ display: -moz-inline-box; /* @ FF 1+2 */
+ display: inline-block; /* Required to make a span sizeable */
+ vertical-align: top;
+ background-repeat: no-repeat;
+ background-position: left;
+}
+
+/*******************************************************************************
+ * Lines and connectors
+ */
+span.ui-dynatree-empty
+{
+}
+span.ui-dynatree-vline
+{
+ background-image: url("ltL_ns.gif");
+}
+span.ui-dynatree-connector
+{
+ background-image: url("ltL_nes.gif");
+}
+.ui-dynatree-lastsib span.ui-dynatree-connector
+{
+ background-image: url("ltL_ne.gif");
+}
+
+/*******************************************************************************
+ * Expander icon
+ * Note: IE6 doesn't correctly evaluate multiples class names,
+ * so we create combined class names that can be used in the CSS.
+ *
+ * Prefix: ui-dynatree-exp-
+ * 1st character: 'e': expanded, 'c': collapsed
+ * 2nd character (optional): 'd': lazy (Delayed)
+ * 3rd character (optional): 'l': Last sibling
+ */
+
+span.ui-dynatree-expander
+{
+ background-image: url("ltP_nes.gif");
+ cursor: pointer;
+}
+.ui-dynatree-exp-cl span.ui-dynatree-expander /* Collapsed, not delayed, last sibling */
+{
+ background-image: url("ltP_ne.gif");
+}
+.ui-dynatree-exp-cd span.ui-dynatree-expander /* Collapsed, delayed, not last sibling */
+{
+ background-image: url("ltD_nes.gif");
+}
+.ui-dynatree-exp-cdl span.ui-dynatree-expander /* Collapsed, delayed, last sibling */
+{
+ background-image: url("ltD_ne.gif");
+}
+.ui-dynatree-exp-e span.ui-dynatree-expander, /* Expanded, not delayed, not last sibling */
+.ui-dynatree-exp-ed span.ui-dynatree-expander /* Expanded, delayed, not last sibling */
+{
+ background-image: url("ltM_nes.gif");
+}
+.ui-dynatree-exp-el span.ui-dynatree-expander, /* Expanded, not delayed, last sibling */
+.ui-dynatree-exp-edl span.ui-dynatree-expander /* Expanded, delayed, last sibling */
+{
+ background-image: url("ltM_ne.gif");
+}
+
+
+/*******************************************************************************
+ * Checkbox icon
+ */
+span.ui-dynatree-checkbox
+{
+ margin-left: 3px;
+ background-image: url("cbUnchecked.gif");
+}
+span.ui-dynatree-checkbox:hover
+{
+ background-image: url("cbUnchecked_hover.gif");
+}
+
+.ui-dynatree-partsel span.ui-dynatree-checkbox
+{
+ background-image: url("cbIntermediate.gif");
+}
+.ui-dynatree-partsel span.ui-dynatree-checkbox:hover
+{
+ background-image: url("cbIntermediate_hover.gif");
+}
+
+.ui-dynatree-selected span.ui-dynatree-checkbox
+{
+ background-image: url("cbChecked.gif");
+}
+.ui-dynatree-selected span.ui-dynatree-checkbox:hover
+{
+ background-image: url("cbChecked_hover.gif");
+}
+
+/*******************************************************************************
+ * Radiobutton icon
+ * This is a customization, that may be activated by overriding the 'checkbox'
+ * class name as 'ui-dynatree-radio' in the tree options.
+ */
+span.ui-dynatree-radio
+{
+ margin-left: 3px;
+ background-image: url("rbUnchecked.gif");
+}
+span.ui-dynatree-radio:hover
+{
+ background-image: url("rbUnchecked_hover.gif");
+}
+
+.ui-dynatree-partsel span.ui-dynatree-radio
+{
+ background-image: url("rbIntermediate.gif");
+}
+.ui-dynatree-partsel span.ui-dynatree-radio:hover
+{
+ background-image: url("rbIntermediate_hover.gif");
+}
+
+.ui-dynatree-selected span.ui-dynatree-radio
+{
+ background-image: url("rbChecked.gif");
+}
+.ui-dynatree-selected span.ui-dynatree-radio:hover
+{
+ background-image: url("rbChecked_hover.gif");
+}
+
+/*******************************************************************************
+ * Node type icon
+ * Note: IE6 doesn't correctly evaluate multiples class names,
+ * so we create combined class names that can be used in the CSS.
+ *
+ * Prefix: ui-dynatree-ico-
+ * 1st character: 'e': expanded, 'c': collapsed
+ * 2nd character (optional): 'f': folder
+ */
+
+span.ui-dynatree-icon /* Default icon */
+{
+ margin-left: 3px;
+ background-image: url("ltDoc.gif");
+}
+
+.ui-dynatree-ico-cf span.ui-dynatree-icon /* Collapsed Folder */
+{
+ background-image: url("ltFld.gif");
+}
+
+.ui-dynatree-ico-ef span.ui-dynatree-icon /* Expanded Folder */
+{
+ background-image: url("ltFld_o.gif");
+}
+
+/* Status node icons */
+
+.ui-dynatree-statusnode-wait span.ui-dynatree-icon
+{
+ background-image: url("ltWait.gif");
+}
+
+.ui-dynatree-statusnode-error span.ui-dynatree-icon
+{
+ background-image: url("ltError.gif");
+}
+
+/*******************************************************************************
+ * Node titles
+ */
+
+ /* @Chrome: fix broken hit area of node titles (issue 133) */
+span.ui-dynatree-document,
+span.ui-dynatree-folder
+{
+ display: -moz-inline-box; /* @ FF 1+2 */
+ display: inline-block; /* Required to make a span sizeable */
+}
+
+/* Remove blue color and underline from title links */
+div.ui-dynatree-container a
+/*, div.ui-dynatree-container a:visited*/
+{
+ color: black; /* inherit doesn't work on IE */
+ text-decoration: none;
+ vertical-align: top;
+ margin: 0px;
+ margin-left: 3px;
+/* outline: 0; /* @ Firefox, prevent dotted border after click */
+}
+
+div.ui-dynatree-container a:hover
+{
+ text-decoration: underline;
+}
+
+span.ui-dynatree-document a,
+span.ui-dynatree-folder a
+{
+ display: inline-block; /* Better alignment, when title contains <br> */
+/* vertical-align: top;*/
+ padding-left: 3px;
+ padding-right: 3px; /* Otherwise italic font will be outside bounds */
+ /* line-height: 16px; /* should be the same as img height, in case 16 px */
+}
+span.ui-dynatree-folder a
+{
+ font-weight: normal;
+}
+
+div.ui-dynatree-container a:focus,
+span.ui-dynatree-focused a:link /* @IE */
+{
+ background-color: #EFEBDE; /* gray */
+}
+
+
+span.ui-dynatree-has-children a
+{
+}
+
+span.ui-dynatree-expanded a
+{
+}
+
+span.ui-dynatree-selected a
+{
+ color: black;
+ font-style: italic;
+}
+
+span.ui-dynatree-active a
+{
+ background-color: #3169C6 !important;
+ color: white !important; /* @ IE6 */
+}
+
+/*******************************************************************************
+ * Custom node classes (sample)
+ */
+
+span.custom1 a
+{
+ background-color: maroon;
+ color: yellow;
+}
--- /dev/null
+++ b/static/scripts/packed/jquery.cookie.js
@@ -0,0 +1,1 @@
+jQuery.cookie=function(b,j,m){if(typeof j!="undefined"){m=m||{};if(j===null){j="";m=$.extend({},m);m.expires=-1}var e="";if(m.expires&&(typeof m.expires=="number"||m.expires.toUTCString)){var f;if(typeof m.expires=="number"){f=new Date();f.setTime(f.getTime()+(m.expires*24*60*60*1000))}else{f=m.expires}e="; expires="+f.toUTCString()}var l=m.path?"; path="+(m.path):"";var g=m.domain?"; domain="+(m.domain):"";var a=m.secure?"; secure":"";document.cookie=[b,"=",encodeURIComponent(j),e,l,g,a].join("")}else{var d=null;if(document.cookie&&document.cookie!=""){var k=document.cookie.split(";");for(var h=0;h<k.length;h++){var c=jQuery.trim(k[h]);if(c.substring(0,b.length+1)==(b+"=")){d=decodeURIComponent(c.substring(b.length+1));break}}}return d}};
Binary file static/june_2007_style/blue/dynatree_skin/ltL_nes.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltL_ns.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltL_ne.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltD_ne.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/rbIntermediate_hover.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/cbUnchecked.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/cbUnchecked_hover.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/ltL_.gif has changed
Binary file static/june_2007_style/blue/dynatree_skin/rbChecked_hover.gif has changed
1
0

galaxy-dist commit 667043341e81: Significant cleanup for the Sample Tracking UI. Things are more streamlined and Galaxy UI standards are now followed.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1288649810 14400
# Node ID 667043341e81261a7fe587daa475110486b15292
# Parent c4d8ffb3109e8cfb8143dc87c88dee3337bb7569
Significant cleanup for the Sample Tracking UI. Things are more streamlined and Galaxy UI standards are now followed.
--- a/templates/requests/common/sample_events.mako
+++ b/templates/requests/common/sample_events.mako
@@ -5,10 +5,7 @@
<h2>Events for Sample "${sample.name}"</h2><ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">
- <span>Browse this request</span></a>
- </li>
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li></ul><h3>Sequencing Request "${sample.request.name}"</h3>
--- /dev/null
+++ b/templates/requests/common/edit_samples.mako
@@ -0,0 +1,240 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/requests/common/common.mako" import="common_javascripts" />
+<%namespace file="/requests/common/common.mako" import="render_samples_grid" />
+<%namespace file="/requests/common/common.mako" import="render_request_type_sample_form_grids" />
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "library" )}
+</%def>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${common_javascripts()}
+ ${local_javascripts()}
+</%def>
+
+<%def name="local_javascripts()">
+ <script type="text/javascript">
+ // Looks for changes in sample states using an async request. Keeps
+ // calling itself (via setTimeout) until all samples are in a terminal
+ // state.
+ var updater = function ( sample_states ) {
+ // Check if there are any items left to track
+ var empty = true;
+ for ( i in sample_states ) {
+ empty = false;
+ break;
+ }
+ if ( ! empty ) {
+ setTimeout( function() { updater_callback( sample_states ) }, 1000 );
+ }
+ };
+
+ var updater_callback = function ( sample_states ) {
+ // Build request data
+ var ids = []
+ var states = []
+ $.each( sample_states, function ( id, state ) {
+ ids.push( id );
+ states.push( state );
+ });
+ // Make ajax call
+ $.ajax( {
+ type: "POST",
+ url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}",
+ dataType: "json",
+ data: { ids: ids.join( "," ), states: states.join( "," ) },
+ success : function ( data ) {
+ $.each( data, function( id, val ) {
+ // Replace HTML
+ var cell1 = $("#sampleState-" + id);
+ cell1.html( val.html_state );
+ var cell2 = $("#sampleDatasets-" + id);
+ cell2.html( val.html_datasets );
+ sample_states[ parseInt( id ) ] = val.state;
+ });
+ updater( sample_states );
+ },
+ error: function() {
+ // Just retry, like the old method, should try to be smarter
+ updater( sample_states );
+ }
+ });
+ };
+ </script>
+</%def>
+
+<%
+ from galaxy.web.framework.helpers import time_ago
+
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_complete = request.is_complete
+ is_unsubmitted = request.is_unsubmitted
+ can_add_samples = is_unsubmitted
+ can_edit_or_delete_samples = is_unsubmitted and request.samples
+ can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
+ can_reject_or_transfer = is_admin and request.is_submitted
+%>
+
+<br/><br/>
+
+<ul class="manage-table-actions">
+ %if not editing_samples and can_edit_or_delete_samples:
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples='True' )}">Edit samples</a></li>
+ %endif
+ %if editing_samples and can_add_samples:
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='add_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), add_sample_button='Add sample' )}">Add sample</a></li>
+ %endif
+ %if is_unsubmitted:
+ <li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
+ %endif
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ %if can_edit_request:
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
+ %if can_reject_or_transfer:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div>
+</ul>
+
+%if request.samples_without_library_destinations:
+ <br/>
+ <font color="red"><b><i>Select a target data library and folder for all samples before starting the sequence run</i></b></font>
+ <br/>
+%endif
+
+%if request.is_rejected:
+ <br/>
+ <font color="red"><b><i>Reason for rejection: </i></b></font><b>${request.last_comment}</b>
+ <br/>
+%endif
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolFormBody">
+ <form id="edit_samples" name="edit_samples" action="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples=editing_samples )}" method="post">
+ %if current_samples:
+ <%
+ if editing_samples:
+ grid_header = '<h3>Edit Current Samples of Request "%s"</h3>' % request.name
+ else:
+ grid_header = '<h3>Add Samples to Request "%s"</h3>' % request.name
+ %>
+ ${render_samples_grid( cntrller, request, current_samples, action='edit_samples', editing_samples=editing_samples, encoded_selected_sample_ids=encoded_selected_sample_ids, render_buttons=False, grid_header=grid_header )}
+ %if editing_samples and len( sample_operation_select_field.options ) > 1 and not ( is_unsubmitted or is_complete ):
+ <div class="form-row" style="background-color:#FAFAFA;">
+ For selected samples:
+ ${sample_operation_select_field.get_html()}
+ </div>
+ <% sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True ) %>
+ %if sample_operation_selected_value != 'none' and encoded_selected_sample_ids:
+ <div class="form-row" style="background-color:#FAFAFA;">
+ %if sample_operation_selected_value == trans.model.Sample.bulk_operations.CHANGE_STATE:
+ ## sample_operation_selected_value == 'Change state'
+ <div class="form-row">
+ <label>Change current state</label>
+ ${sample_state_id_select_field.get_html()}
+ <label>Comments</label>
+ <input type="text" name="sample_event_comment" value=""/>
+ <div class="toolParamHelp" style="clear: both;">
+ Optional
+ </div>
+ </div>
+ %elif sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY:
+ <% libraries_selected_value = libraries_select_field.get_selected( return_value=True ) %>
+ <div class="form-row">
+ <label>Select data library:</label>
+ ${libraries_select_field.get_html()}
+ </div>
+ %if libraries_selected_value != 'none':
+ <div class="form-row">
+ <label>Select folder:</label>
+ ${folders_select_field.get_html()}
+ </div>
+ %endif
+ %endif
+ </div>
+ %endif
+ %endif
+ ## Render the other grids
+ <% trans.sa_session.refresh( request.type.sample_form ) %>
+ %for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
+ ${render_request_type_sample_form_grids( grid_index, grid_name, request.type.sample_form.grid_fields( grid_index ), editing_samples=editing_samples )}
+ %endfor
+ %else:
+ <label>There are no samples.</label>
+ %endif
+ %if not editing_samples and is_unsubmitted:
+ ## The user is adding a new sample
+ %if current_samples:
+ <p/>
+ <div class="form-row">
+ <label> Copy <input type="text" name="num_sample_to_copy" value="1" size="3"/> samples from sample ${sample_copy.get_html()}</label>
+ <div class="toolParamHelp" style="clear: both;">
+ Select the sample from which the new sample should be copied or leave selection as <b>None</b> to add a new "generic" sample.
+ </div>
+ </div>
+ %endif
+ <p/>
+ <div class="form-row">
+ %if ( request.samples or current_samples ) and ( editing_samples or len( current_samples ) > len( request.samples ) ):
+ <input type="submit" name="add_sample_button" value="Add sample"/>
+ <input type="submit" name="save_samples_button" value="Save"/>
+ <input type="submit" name="cancel_changes_button" value="Cancel"/>
+ <div class="toolParamHelp" style="clear: both;">
+ Click the <b>Add sample</b> button for each new sample and click the <b>Save</b> button when you have finished adding samples.
+ </div>
+ %else:
+ <input type="submit" name="add_sample_button" value="Add sample"/>
+ <div class="toolParamHelp" style="clear: both;">
+ Click the <b>Add sample</b> button for each new sample.
+ </div>
+ %endif
+ </div>
+ %elif editing_samples:
+ <p/>
+ <div class="form-row">
+ <input type="submit" name="save_samples_button" value="Save"/>
+ <input type="submit" name="cancel_changes_button" value="Cancel"/>
+ <div class="toolParamHelp" style="clear: both;">
+ Click the <b>Save</b> button when you have finished editing the samples
+ </div>
+ %endif
+ %if request.samples and request.is_submitted:
+ <script type="text/javascript">
+ // Updater
+ updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
+ </script>
+ %endif
+ </form>
+</div>
+%if is_unsubmitted and not editing_samples:
+ <p/>
+ ##<div class="toolForm">
+ ##<div class="toolFormTitle">Import samples from csv file</div>
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> Import samples from csv file</h4>
+ <div style="display:none;">
+ <div class="toolFormBody">
+ <form id="import" name="import" action="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples=editing_samples )}" enctype="multipart/form-data" method="post" >
+ <div class="form-row">
+ <input type="file" name="file_data" />
+ <input type="submit" name="import_samples_button" value="Import samples"/>
+ <div class="toolParamHelp" style="clear: both;">
+ The csv file must be in the following format:<br/>
+ SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ ##</div>
+%endif
--- a/test/functional/test_forms_and_requests.py
+++ b/test/functional/test_forms_and_requests.py
@@ -225,15 +225,12 @@ class TestFormsAndRequests( TwillTestCas
strings_displayed_after_submit = [ 'Unsubmitted' ]
for sample_name, field_values in sample_value_tuples:
strings_displayed_after_submit.append( sample_name )
- for field_value in field_values:
- strings_displayed_after_submit.append( field_value )
# Add samples to the request
self.add_samples( cntrller='requests',
request_id=self.security.encode_id( request_one.id ),
request_name=request_one.name,
sample_value_tuples=sample_value_tuples,
- strings_displayed=[ 'Sequencing Request "%s"' % request_one.name,
- 'There are no samples.' ],
+ strings_displayed=[ 'There are no samples.' ],
strings_displayed_after_submit=strings_displayed_after_submit )
def test_030_edit_basic_request_info( self ):
"""Testing editing the basic information of a sequence run request"""
@@ -277,11 +274,11 @@ class TestFormsAndRequests( TwillTestCas
self.check_request_grid( cntrller='requests_admin',
state=request_one.states.SUBMITTED,
strings_displayed=[ request_one.name ] )
- self.visit_url( "%s/requests_common/manage_request?cntrller=requests&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ) )
- self.check_page_for_string( 'Sequencing Request "%s"' % request_one.name )
+ self.visit_url( "%s/requests_common/view_request?cntrller=requests&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ) )
+ # TODO: add some string for checking on the page above...
# Set bar codes for the samples
bar_codes = [ '1234567890', '0987654321' ]
- strings_displayed_after_submit=[ 'Changes made to the samples are saved.' ]
+ strings_displayed_after_submit=[ 'Changes made to the samples have been saved.' ]
for bar_code in bar_codes:
strings_displayed_after_submit.append( bar_code )
self.add_bar_codes( request_id=self.security.encode_id( request_one.id ),
@@ -333,7 +330,7 @@ class TestFormsAndRequests( TwillTestCas
test_field_name1,
test_field_name2,
test_field_name3 ],
- strings_displayed_after_submit=[ "The request has been created" ] )
+ strings_displayed_after_submit=[ "The request has been created." ] )
global request_two
request_two = get_request_by_name( name )
# Make sure the request is showing in the 'new' filter
@@ -349,15 +346,12 @@ class TestFormsAndRequests( TwillTestCas
strings_displayed_after_submit = [ 'Unsubmitted' ]
for sample_name, field_values in sample_value_tuples:
strings_displayed_after_submit.append( sample_name )
- for field_value in field_values:
- strings_displayed_after_submit.append( field_value )
# Add samples to the request
self.add_samples( cntrller='requests_admin',
request_id=self.security.encode_id( request_two.id ),
request_name=request_two.name,
sample_value_tuples=sample_value_tuples,
- strings_displayed=[ 'Sequencing Request "%s"' % request_two.name,
- 'There are no samples.' ],
+ strings_displayed=[ 'There are no samples.' ],
strings_displayed_after_submit=strings_displayed_after_submit )
# Submit the request
self.submit_request( cntrller='requests_admin',
@@ -394,6 +388,7 @@ class TestFormsAndRequests( TwillTestCas
% ( request_two.name, request_two.states.REJECTED )
def test_055_reset_data_for_later_test_runs( self ):
"""Reseting data to enable later test runs to pass"""
+ """
# Logged in as admin_user
##################
# Delete request_type permissions
@@ -438,3 +433,4 @@ class TestFormsAndRequests( TwillTestCas
# Manually delete the group from the database
refresh( group )
delete( group )
+ """
--- a/templates/admin/requests/rename_datasets.mako
+++ b/templates/admin/requests/rename_datasets.mako
@@ -10,7 +10,7 @@
<a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', sample_id=trans.security.encode_id( sample.id ) )}">Browse datasets</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller='requests_admin', id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller='requests_admin', id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li></ul>
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -158,9 +158,9 @@ class RequestsAdmin( BaseController, Use
action='edit_basic_request_info',
cntrller='requests_admin',
**kwd ) )
- if operation == "manage_request":
+ if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests_admin',
**kwd ) )
if operation == "request_events":
@@ -186,14 +186,14 @@ class RequestsAdmin( BaseController, Use
return self.request_grid( trans, **kwd )
@web.expose
@web.require_admin
- def reject( self, trans, **kwd ):
+ def reject_request( self, trans, **kwd ):
params = util.Params( kwd )
request_id = params.get( 'id', '' )
status = params.get( 'status', 'done' )
message = params.get( 'message', 'done' )
if params.get( 'cancel_reject_button', False ):
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests_admin',
id=request_id ) )
try:
@@ -292,7 +292,6 @@ class RequestsAdmin( BaseController, Use
return invalid_id_redirect( trans, 'requests_admin', sample_id )
request_id = trans.security.encode_id( sample.request.id )
library_id = trans.security.encode_id( sample.library.id )
- self.datatx_grid.title = 'Datasets of sample "%s"' % sample.name
self.datatx_grid.global_actions = [ grids.GridAction( "Refresh",
dict( controller='requests_admin',
action='manage_datasets',
@@ -303,14 +302,14 @@ class RequestsAdmin( BaseController, Use
request_id=request_id,
folder_path=sample.request.type.datatx_info[ 'data_dir' ],
sample_id=sample_id ) ),
- grids.GridAction( 'Data library "%s"' % sample.library.name,
- dict( controller='library_common',
- action='browse_library',
- cntrller='library_admin',
- id=library_id ) ),
+ #grids.GridAction( 'Data library "%s"' % sample.library.name,
+ # dict( controller='library_common',
+ # action='browse_library',
+ # cntrller='library_admin',
+ # id=library_id ) ),
grids.GridAction( "Browse this request",
dict( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests_admin',
id=request_id ) ) ]
return self.datatx_grid( trans, **kwd )
--- a/templates/requests/common/edit_basic_request_info.mako
+++ b/templates/requests/common/edit_basic_request_info.mako
@@ -1,14 +1,25 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
+<%
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ can_add_samples = request.is_unsubmitted
+%>
+
<br/><br/><ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse all requests</a>
- </li>
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request Actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ %if can_add_samples:
+ <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
+ %if is_admin and request.is_submitted:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div></ul>
%if message:
--- /dev/null
+++ b/templates/requests/common/view_request.mako
@@ -0,0 +1,157 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/requests/common/common.mako" import="common_javascripts" />
+<%namespace file="/requests/common/common.mako" import="render_samples_grid" />
+<%namespace file="/requests/common/common.mako" import="render_request_type_sample_form_grids" />
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "library" )}
+</%def>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${common_javascripts()}
+</%def>
+
+<%
+ from galaxy.web.framework.helpers import time_ago
+
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_unsubmitted = request.is_unsubmitted
+ can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
+ can_add_samples = is_unsubmitted
+%>
+
+<br/><br/>
+
+<ul class="manage-table-actions">
+ %if is_unsubmitted:
+ <li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
+ %endif
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ %if can_edit_request:
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
+ %if is_admin and request.is_submitted:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div>
+</ul>
+
+%if request.is_rejected:
+ <font color="red"><b><i>Reason for rejection: </i></b></font><b>${request.last_comment}</b>
+ <br/><br/>
+%endif
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">Sequencing request "${request.name}"</div>
+ <div class="toolFormBody">
+ <div class="form-row">
+ <label>Current state:</label>
+ ${request.state}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Description:</label>
+ ${request.desc}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>User:</label>
+ %if is_admin:
+ ${request.user.email}
+ %elif request.user.username:
+ ${request.user.username}
+ %else:
+ Unknown
+ %endif
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> More</h4>
+ <div style="display:none;">
+ %for index, rd in enumerate( request_widgets ):
+ <%
+ field_label = rd[ 'label' ]
+ field_value = rd[ 'value' ]
+ %>
+ <div class="form-row">
+ <label>${field_label}:</label>
+ %if field_label == 'State':
+ <a href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">${field_value}</a>
+ %else:
+ ${field_value}
+ %endif
+ </div>
+ <div style="clear: both"></div>
+ %endfor
+ <div class="form-row">
+ <label>Date created:</label>
+ ${request.create_time}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Last updated:</label>
+ ${time_ago( request.update_time )}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Email recipients:</label>
+ <%
+ if request.notification:
+ emails = ', '.join( request.notification[ 'email' ] )
+ else:
+ emails = ''
+ %>
+ ${emails}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Send email when state changes to:</label>
+ <%
+ if request.notification:
+ states = []
+ for ss in request.type.states:
+ if ss.id in request.notification[ 'sample_states' ]:
+ states.append( ss.name )
+ states = ', '.join( states )
+ else:
+ states = ''
+ %>
+ ${states}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Sequencer configuration:</label>
+ ${request.type.name}
+ <div style="clear: both"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<p/>
+%if current_samples:
+ <% grid_header = '<h3>Samples</h3>' %>
+ ${render_samples_grid( cntrller, request, current_samples=current_samples, action='view_request', editing_samples=False, encoded_selected_sample_ids=[], render_buttons=can_edit_request, grid_header=grid_header )}
+%else:
+ There are no samples.
+ %if can_add_samples:
+ <ul class="manage-table-actions">
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='add_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), add_sample_button='Add sample' )}">Add sample</a></li>
+ </ul>
+ %endif
+%endif
+## Render the other grids
+<% trans.sa_session.refresh( request.type.sample_form ) %>
+%for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
+ ${render_request_type_sample_form_grids( grid_index, grid_name, request.type.sample_form.grid_fields( grid_index ), editing_samples=False )}
+%endfor
--- a/lib/galaxy/web/controllers/library.py
+++ b/lib/galaxy/web/controllers/library.py
@@ -24,13 +24,11 @@ class LibraryListGrid( grids.Grid ):
columns = [
NameColumn( "Name",
key="name",
- model_class=model.Library,
link=( lambda library: dict( operation="browse", id=library.id ) ),
attach_popup=False,
filterable="advanced" ),
DescriptionColumn( "Description",
key="description",
- model_class=model.Library,
attach_popup=False,
filterable="advanced" ),
]
--- a/templates/admin/forms/show_form_read_only.mako
+++ b/templates/admin/forms/show_form_read_only.mako
@@ -81,10 +81,10 @@
<form name="library" action="${h.url_for( controller='forms', action='manage' )}" method="post" >
%if form_definition.type == trans.app.model.FormDefinition.types.SAMPLE:
%if not len(form_definition.layout):
- ${render_grid( 0, '', form_definition.fields_of_grid( None ) )}
+ ${render_grid( 0, '', form_definition.grid_fields( None ) )}
%else:
%for grid_index, grid_name in enumerate(form_definition.layout):
- ${render_grid( grid_index, grid_name, form_definition.fields_of_grid( grid_index ) )}
+ ${render_grid( grid_index, grid_name, form_definition.grid_fields( grid_index ) )}
%endfor
%endif
%else:
--- a/templates/library/common/browse_library.mako
+++ b/templates/library/common/browse_library.mako
@@ -346,7 +346,6 @@
><td style="padding-left: ${folder_pad}px;"><input type="checkbox" class="folderCheckbox"/>
-
%if folder.deleted:
<span class="libraryItem-error">
%endif
@@ -354,7 +353,6 @@
<div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup"><a href="javascript:void(0);">${folder.name}</a></div>
-
%if folder.deleted:
</span>
%endif
--- a/templates/admin/requests/reject.mako
+++ b/templates/admin/requests/reject.mako
@@ -8,16 +8,16 @@
<h2>Reject Sequencing Request "${request.name}"</h2><ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">Events</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">View history</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">Browse this request</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">Browse this request</a></li></ul><div class="toolForm"><div class="toolFormTitle">Reject request</div>
- <form name="event" action="${h.url_for( controller='requests_admin', action='reject', id=trans.security.encode_id( request.id ) )}" method="post" >
+ <form name="event" action="${h.url_for( controller='requests_admin', action='reject_request', id=trans.security.encode_id( request.id ) )}" method="post" ><div class="form-row">
Rejecting this request will move the request state to <b>Rejected</b>.
</div>
--- a/templates/admin/requests/get_data.mako
+++ b/templates/admin/requests/get_data.mako
@@ -75,7 +75,7 @@
<a class="action-button" href="${h.url_for( controller='requests_admin', action='view_request_type', id=trans.security.encode_id( request.type.id ) )}">Sequencer configuration "${request.type.name}"</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a></li></ul>
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1488,25 +1488,23 @@ class MetadataFile( object ):
return os.path.abspath( os.path.join( path, "metadata_%d.dat" % self.id ) )
class FormDefinition( object ):
- types = Bunch( REQUEST = 'Sequencing Request Form',
- SAMPLE = 'Sequencing Sample Form',
- LIBRARY_INFO_TEMPLATE = 'Library information template',
- USER_INFO = 'User Information' )
- def __init__(self, name=None, desc=None, fields=[],
- form_definition_current=None, form_type=None, layout=None):
+ types = Bunch( REQUEST = 'Sequencing Request Form',
+ SAMPLE = 'Sequencing Sample Form',
+ LIBRARY_INFO_TEMPLATE = 'Library information template',
+ USER_INFO = 'User Information' )
+ def __init__( self, name=None, desc=None, fields=[], form_definition_current=None, form_type=None, layout=None ):
self.name = name
self.desc = desc
self.fields = fields
self.form_definition_current = form_definition_current
self.type = form_type
self.layout = layout
- def fields_of_grid(self, grid_index):
- '''
- This method returns the list of fields belonging to the given grid.
- '''
+ def grid_fields( self, grid_index ):
+ # Returns a dictionary whose keys are integers corresponding to field positions
+ # on the grid and whose values are the field.
gridfields = {}
- for i, f in enumerate(self.fields):
- if str(f['layout']) == str(grid_index):
+ for i, f in enumerate( self.fields ):
+ if str( f[ 'layout' ] ) == str( grid_index ):
gridfields[i] = f
return gridfields
def get_widgets( self, user, contents=[], **kwd ):
--- a/lib/galaxy/web/controllers/requests.py
+++ b/lib/galaxy/web/controllers/requests.py
@@ -10,11 +10,10 @@ log = logging.getLogger( __name__ )
class UserRequestsGrid( RequestsGrid ):
operations = [ operation for operation in RequestsGrid.operations ]
- operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted and item.is_unsubmitted ) ) )
- operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted and item.is_new ) ) )
+ operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: item.is_unsubmitted and not item.deleted ) ) )
+ operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.is_new and not item.deleted ) ) )
operations.append( grids.GridOperation( "Undelete", allow_multiple=True, condition=( lambda item: item.deleted ) ) )
def apply_query_filter( self, trans, query, **kwd ):
- # gvk ( 9/28/10 ) TODO: is this method needed?
return query.filter_by( user=trans.user )
class Requests( BaseController ):
@@ -37,9 +36,9 @@ class Requests( BaseController ):
action='edit_basic_request_info',
cntrller='requests',
**kwd ) )
- if operation == "manage_request":
+ if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests',
**kwd ) )
if operation == "delete":
@@ -70,12 +69,12 @@ class Requests( BaseController ):
message = "%d requests (highlighted in red) were rejected. Click on the request name for details." % rejected
kwd[ 'status' ] = status
kwd[ 'message' ] = message
- # show the create request button to the user, only when the user has permissions
- # to at least one request_type (sequencer configuration)
+ # Allow the user to create a new request only if they have permission to access a
+ # (sequencer configuration) request type.
if len( trans.user.accessible_request_types( trans ) ):
self.request_grid.global_actions = [ grids.GridAction( "Create new request", dict( controller='requests_common',
- action='create_request',
- cntrller='requests' ) ) ]
+ action='create_request',
+ cntrller='requests' ) ) ]
else:
self.request_grid.global_actions = []
# Render the list view
--- a/templates/requests/common/find_samples.mako
+++ b/templates/requests/common/find_samples.mako
@@ -76,7 +76,7 @@
<i>User: ${sample.request.user.email}</i>
%endif
<div class="toolParamHelp" style="clear: both;">
- <a href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Sequencing request: ${sample.request.name} | Type: ${sample.request.type.name} | State: ${sample.request.state}</a>
+ <a href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Sequencing request: ${sample.request.name} | Type: ${sample.request.type.name} | State: ${sample.request.state}</a></div></div><br/>
--- a/lib/galaxy/web/controllers/forms.py
+++ b/lib/galaxy/web/controllers/forms.py
@@ -86,7 +86,7 @@ class Forms( BaseController ):
status='error',
message="Invalid form ID") )
if operation == "view":
- return self.__view( trans, **kwd )
+ return self.view_form_definition( trans, **kwd )
elif operation == "delete":
return self.__delete( trans, **kwd )
elif operation == "undelete":
@@ -94,10 +94,12 @@ class Forms( BaseController ):
elif operation == "edit":
return self.edit( trans, **kwd )
return self.forms_grid( trans, **kwd )
- def __view(self, trans, **kwd):
+ @web.expose
+ def view_form_definition( self, trans, **kwd ):
+ form_definition_current_id = kwd.get( 'id', None )
try:
- fdc = trans.sa_session.query( trans.app.model.FormDefinitionCurrent )\
- .get( trans.security.decode_id(kwd['id']) )
+ fdc = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ) \
+ .get( trans.security.decode_id( form_definition_current_id ) )
except:
return trans.response.send_redirect( web.url_for( controller='forms',
action='manage',
--- /dev/null
+++ b/templates/requests/common/common.mako
@@ -0,0 +1,332 @@
+<%namespace file="/requests/common/sample_state.mako" import="render_sample_state" />
+
+<%def name="javascripts()">
+ ${self.common_javascripts()}
+</%def>
+
+<%def name="common_javascripts()">
+ <script type="text/javascript">
+ function showContent(vThis)
+ {
+ // http://www.javascriptjunkie.com
+ // alert(vSibling.className + " " + vDef_Key);
+ vParent = vThis.parentNode;
+ vSibling = vParent.nextSibling;
+ while (vSibling.nodeType==3) {
+ // Fix for Mozilla/FireFox Empty Space becomes a TextNode or Something
+ vSibling = vSibling.nextSibling;
+ };
+ if(vSibling.style.display == "none")
+ {
+ vThis.src="/static/images/fugue/toggle.png";
+ vThis.alt = "Hide";
+ vSibling.style.display = "block";
+ } else {
+ vSibling.style.display = "none";
+ vThis.src="/static/images/fugue/toggle-expand.png";
+ vThis.alt = "Show";
+ }
+ return;
+ }
+ $(document).ready(function(){
+ //hide the all of the element with class msg_body
+ $(".msg_body").hide();
+ //toggle the component with class msg_body
+ $(".msg_head").click(function(){
+ $(this).next(".msg_body").slideToggle(0);
+ });
+ });
+
+ function checkAllFields()
+ {
+ var chkAll = document.getElementById('checkAll');
+ var checks = document.getElementsByTagName('input');
+ var boxLength = checks.length;
+ var allChecked = false;
+ var totalChecked = 0;
+ if ( chkAll.checked == true )
+ {
+ for ( i=0; i < boxLength; i++ )
+ {
+ if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
+ {
+ checks[i].checked = true;
+ }
+ }
+ }
+ else
+ {
+ for ( i=0; i < boxLength; i++ )
+ {
+ if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
+ {
+ checks[i].checked = false
+ }
+ }
+ }
+ }
+ </script>
+</%def>
+
+<%def name="render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )">
+ <%
+ if sample:
+ is_complete = sample.request.is_complete
+ is_submitted = sample.request.is_submitted
+ is_unsubmitted = sample.request.is_unsubmitted
+ else:
+ is_complete = False
+ is_submitted = False
+ is_unsubmitted = False
+ %>
+ <%
+ if is_admin and is_submitted and editing_samples and trans.security.encode_id( sample.id ) in encoded_selected_sample_ids:
+ checked_str = "checked"
+ else:
+ checked_str = ""
+ %>
+ %if is_admin and is_submitted and editing_samples:
+ <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" ${checked_str}/><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"/></td>
+ %endif
+ <td>
+ <input type="text" name="sample_${current_sample_index}_name" value="${current_sample['name']}" size="10"/>
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${' (required)' }</i>
+ </div>
+ </td>
+ %if sample and is_submitted or is_complete:
+ <td><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
+ %endif
+ %if sample:
+ %if is_unsubmitted:
+ <td>Unsubmitted</td>
+ %else:
+ <td><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
+ %endif
+ %else:
+ <td></td>
+ %endif
+ <td>${current_sample['library_select_field'].get_html()}</td>
+ <td>${current_sample['folder_select_field'].get_html()}</td>
+ %if is_submitted or is_complete:
+ <%
+ if sample:
+ label = str( len( sample.datasets ) )
+ else:
+ label = 'add'
+ %>
+ <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ %endif
+ %if sample and ( is_admin or is_unsubmitted ):
+ ## Delete button
+ <td><a class="action-button" href="${h.url_for( controller='requests_common', action='delete_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=current_sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a></td>
+ %endif
+</%def>
+
+<%def name="render_samples_grid( cntrller, request, current_samples, action, editing_samples=False, encoded_selected_sample_ids=[], render_buttons=False, grid_header='<h3>Samples</h3>' )">
+ ## Displays the "Samples" grid
+ <%
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_complete = request.is_complete
+ is_submitted = request.is_submitted
+ is_unsubmitted = request.is_unsubmitted
+ can_add_samples = request.is_unsubmitted
+ can_edit_or_delete_samples = request.samples and ( is_admin or request.is_unsubmitted )
+ %>
+ ${grid_header}
+ %if render_buttons and ( can_add_samples or can_edit_or_delete_samples ):
+ <ul class="manage-table-actions">
+ %if can_add_samples:
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='add_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), add_sample_button='Add sample' )}">Add sample</a></li>
+ %endif
+ %if can_edit_or_delete_samples:
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples='True' )}">Edit samples</a></li>
+ %endif
+ </ul>
+ %endif
+ <table class="grid">
+ <thead>
+ <tr>
+ %if is_admin and is_submitted and editing_samples:
+ <th><input type="checkbox" id="checkAll" name=select_all_samples_checkbox value="true" onclick='checkAllFields(1);'/><input type="hidden" name=select_all_samples_checkbox value="true"/></th>
+ %endif
+ <th>Name</th>
+ %if is_submitted or is_complete:
+ <th>Barcode</th>
+ %endif
+ <th>State</th>
+ <th>Data Library</th>
+ <th>Folder</th>
+ %if is_submitted or is_complete:
+ <th>Datasets Selected</th>
+ <th>Datasets Transferred</th>
+ %endif
+ <th>
+ %if editing_samples:
+ Delete
+ %endif
+ </th>
+ </tr>
+ <thead>
+ <tbody>
+ <% trans.sa_session.refresh( request ) %>
+ ## current_samples is a dictionary whose keys are:
+ ## name, barcode, library, folder, field_values, library_select_field, folder_select_field
+ %for current_sample_index, current_sample in enumerate( current_samples ):
+ <%
+ current_sample_name = current_sample[ 'name' ]
+ current_sample_barcode = current_sample[ 'barcode' ]
+ current_sample_library = current_sample[ 'library' ]
+ if current_sample_library:
+ if cntrller == 'requests':
+ library_cntrller = 'library'
+ elif is_admin:
+ library_cntrller = 'library_admin'
+ else:
+ library_cntrller = None
+ current_sample_folder = current_sample[ 'folder' ]
+ try:
+ sample = request.samples[ current_sample_index ]
+ except:
+ sample = None
+ %>
+ %if editing_samples:
+ <tr>${render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
+ %elif sample:
+ <tr>
+ <td>${current_sample_name}</td>
+ %if is_submitted or is_complete:
+ <td>${current_sample_barcode}</td>
+ %endif
+ %if is_unsubmitted:
+ <td>Unsubmitted</td>
+ %else:
+ <td><a id="sampleState-${sample.id}" href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${render_sample_state( sample )}</a></td>
+ %endif
+ %if current_sample_library and library_cntrller is not None:
+ <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller=library_cntrller, id=trans.security.encode_id( current_sample_library.id ) )}">${current_sample_library.name}</a></td>
+ %else:
+ <td></td>
+ %endif
+ %if current_sample_folder:
+ <td>${current_sample_folder.name}</td>
+ %else:
+ <td></td>
+ %endif
+ %if is_submitted or is_complete:
+ <td><a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a></td>
+ <td><a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a></td>
+ %endif
+ </tr>
+ %else:
+ <tr>${render_editable_sample_row( is_admin, None, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
+ %endif
+ %endfor
+ </tbody>
+ </table>
+</%def>
+
+<%def name="render_sample_form( index, sample_name, sample_values, fields_dict, display_only )">
+ <tr>
+ <td>${sample_name}</td>
+ %for field_index, field in fields_dict.items():
+ <%
+ field_type = field[ 'type' ]
+ %>
+ <td>
+ %if display_only:
+ %if sample_values[field_index]:
+ %if field_type == 'WorkflowField':
+ %if str(sample_values[field_index]) != 'none':
+ <% workflow = trans.sa_session.query( trans.app.model.StoredWorkflow ).get( int( sample_values[ field_index ] ) ) %>
+ <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id( workflow.id ) )}">${workflow.name}</a>
+ %endif
+ %else:
+ ${sample_values[ field_index ]}
+ %endif
+ %else:
+ <i>None</i>
+ %endif
+ %else:
+ %if field_type == 'TextField':
+ <input type="text" name="sample_${index}_field_${field_index}" value="${sample_values[field_index]}" size="7"/>
+ %elif field_type == 'SelectField':
+ <select name="sample_${index}_field_${field_index}" last_selected_value="2">
+ %for option_index, option in enumerate(field['selectlist']):
+ %if option == sample_values[field_index]:
+ <option value="${option}" selected>${option}</option>
+ %else:
+ <option value="${option}">${option}</option>
+ %endif
+ %endfor
+ </select>
+ %elif field_type == 'WorkflowField':
+ <select name="sample_${index}_field_${field_index}">
+ %if str(sample_values[field_index]) == 'none':
+ <option value="none" selected>Select one</option>
+ %else:
+ <option value="none">Select one</option>
+ %endif
+ %for option_index, option in enumerate(request.user.stored_workflows):
+ %if not option.deleted:
+ %if str(option.id) == str(sample_values[field_index]):
+ <option value="${option.id}" selected>${option.name}</option>
+ %else:
+ <option value="${option.id}">${option.name}</option>
+ %endif
+ %endif
+ %endfor
+ </select>
+ %elif field_type == 'CheckboxField':
+ <input type="checkbox" name="sample_${index}_field_${field_index}" value="Yes"/>
+ %endif
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${'('+field['required']+')' }</i>
+ </div>
+ %endif
+ </td>
+ %endfor
+ </tr>
+</%def>
+
+<%def name="render_request_type_sample_form_grids( grid_index, grid_name, fields_dict, editing_samples )">
+ <%
+ if not grid_name:
+ grid_name = "Sample form layout " + grid_index
+ %>
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
+ <div style="display:none;">
+ <table class="grid">
+ <thead>
+ <tr>
+ <th>Name</th>
+ %for index, field in fields_dict.items():
+ <th>
+ ${field['label']}
+ ## TODO: help comments in the grid header are UGLY!
+ ## If they are needed display them more appropriately,
+ ## if they are not, delete this commented code.
+ ##<div class="toolParamHelp" style="clear: both;">
+ ## <i>${field['helptext']}</i>
+ ##</div>
+ </th>
+ %endfor
+ <th></th>
+ </tr>
+ <thead>
+ <tbody>
+ <% trans.sa_session.refresh( request ) %>
+ %for sample_index, sample in enumerate( current_samples ):
+ <%
+ if editing_samples or sample_index >= len( request.samples ):
+ display_only = False
+ else:
+ display_only = True
+ %>
+ ${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict, display_only )}
+ %endfor
+ </tbody>
+ </table>
+ </div>
+</%def>
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -1489,17 +1489,17 @@ class TwillTestCase( unittest.TestCase )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
def add_samples( self, cntrller, request_id, request_name, sample_value_tuples, strings_displayed=[], strings_displayed_after_submit=[] ):
- self.visit_url( "%s/requests_common/manage_request?cntrller=%s&id=%s" % ( self.url, cntrller, request_id ) )
+ self.visit_url( "%s/requests_common/edit_samples?cntrller=%s&id=%s&editing_samples=False" % ( self.url, cntrller, request_id ) )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
# Simulate clicking the add-sample_button on the form. (gvk: 9/21/10 - TODO : There must be a bug in the mako template
# because twill cannot find any forms on the page, but I cannot find it although I've spent time cleaning up the
# template code and looking for any problems.
- url = "%s/requests_common/manage_request?cntrller=%s&id=%s" % ( self.url, cntrller, request_id )
+ url = "%s/requests_common/edit_samples?cntrller=%s&id=%s&editing_samples=False" % ( self.url, cntrller, request_id )
# This should work, but although twill does not thorw any exceptions, the button click never occurs
- # There are multiple forms on this page, and we'll only be using the form named manage_request.
+ # There are multiple forms on this page, and we'll only be using the form named edit_samples.
# for sample_index, sample_value_tuple in enumerate( sample_value_tuples ):
- # # Add the following form value to the already populated hidden field so that the manage_request
+ # # Add the following form value to the already populated hidden field so that the edit_samples
# # form is the current form
# tc.fv( "1", "id", request_id )
# tc.submit( 'add_sample_button' )
@@ -1530,7 +1530,7 @@ class TwillTestCase( unittest.TestCase )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
def reject_request( self, request_id, request_name, comment, strings_displayed=[], strings_displayed_after_submit=[] ):
- self.visit_url( "%s/requests_admin/reject?id=%s" % ( self.url, request_id ) )
+ self.visit_url( "%s/requests_admin/reject_request?id=%s" % ( self.url, request_id ) )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
tc.fv( "1", "comment", comment )
@@ -1540,7 +1540,7 @@ class TwillTestCase( unittest.TestCase )
def add_bar_codes( self, request_id, request_name, bar_codes, samples, strings_displayed_after_submit=[] ):
# We have to simulate the form submission here since twill barfs on the page
# gvk - 9/22/10 - TODO: make sure the mako template produces valid html
- url = "%s/requests_common/manage_request?cntrller=requests_admin&id=%s&managing_samples=True" % ( self.url, request_id )
+ url = "%s/requests_common/edit_samples?cntrller=requests_admin&id=%s&editing_samples=True" % ( self.url, request_id )
for index, field_value in enumerate( bar_codes ):
sample_field_name = "sample_%i_name" % index
sample_field_value = samples[ index ].name.replace( ' ', '+' )
@@ -1555,15 +1555,15 @@ class TwillTestCase( unittest.TestCase )
strings_displayed=[], strings_displayed_after_submit=[] ):
# We have to simulate the form submission here since twill barfs on the page
# gvk - 9/22/10 - TODO: make sure the mako template produces valid html
- url = "%s/requests_common/manage_request?cntrller=requests_admin&id=%s" % ( self.url, request_id )
+ url = "%s/requests_common/edit_samples?cntrller=requests_admin&id=%s" % ( self.url, request_id )
url += "&comment=%s&sample_state_id=%s" % ( comment, self.security.encode_id( new_sample_state_id ) )
# select_sample_%i=true must be included twice for each sample to simulate a CheckboxField checked setting.
for sample_id in sample_ids:
url += "&select_sample_%i=true&select_sample_%i=true" % ( sample_id, sample_id )
url += "&sample_operation=Change%20state&refresh=true"
- url += "&change_state_button=Save"
+ url += "&save_changes_button=Save&editing_samples=True"
self.visit_url( url )
- self.check_page_for_string( 'Sequencing Request "%s"' % request_name )
+ self.check_page_for_string( 'Edit Current Samples of Request "%s"' % request_name )
for sample_id, sample_name in zip( sample_ids, sample_names ):
self.visit_url( "%s/requests_common/sample_events?cntrller=requests_admin&sample_id=%s" % ( self.url, self.security.encode_id( sample_id ) ) )
self.check_page_for_string( 'Events for Sample "%s"' % sample_name )
--- a/templates/requests/common/events.mako
+++ b/templates/requests/common/events.mako
@@ -1,20 +1,36 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
-<h2>History of Sequencing Request "${request.name}"</h2>
+<%
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
+ can_add_samples = request.is_unsubmitted
+%>
+
+<br/><br/><ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse all requests</a>
- </li>
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request Actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ %if can_edit_request:
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ %endif
+ %if can_add_samples:
+ <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
+ %endif
+ %if is_admin and request.is_submitted:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div></ul>
%if message:
${render_msg( message, status )}
%endif
+<h2>History of Sequencing Request "${request.name}"</h2>
+
<div class="toolForm"><table class="grid"><thead>
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -70,14 +70,14 @@ class RequestsGrid( grids.Grid ):
columns = [
NameColumn( "Name",
key="name",
- link=( lambda item: iff( item.deleted, None, dict( operation="manage_request", id=item.id ) ) ),
+ link=( lambda item: iff( item.deleted, None, dict( operation="view_request", id=item.id ) ) ),
attach_popup=True,
filterable="advanced" ),
DescriptionColumn( "Description",
key='desc',
filterable="advanced" ),
SamplesColumn( "Samples",
- link=( lambda item: iff( item.deleted, None, dict( operation="manage_request", id=item.id ) ) ) ),
+ link=( lambda item: iff( item.deleted, None, dict( operation="edit_samples", id=item.id ) ) ) ),
TypeColumn( "Sequencer",
link=( lambda item: iff( item.deleted, None, dict( operation="view_type", id=item.type.id ) ) ) ),
grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
@@ -148,7 +148,7 @@ class RequestsCommon( BaseController, Us
except TypeError, e:
# We must have an email address rather than an encoded user id
# This is because the galaxy.base.js creates a search+select box
- # when there are more than 20 items in a selectfield
+ # when there are more than 20 items in a SelectField.
user = trans.sa_session.query( trans.model.User ) \
.filter( trans.model.User.table.c.email==util.restore_text( user_id ) ) \
.first()
@@ -178,7 +178,8 @@ class RequestsCommon( BaseController, Us
message=message ,
status='done' ) )
elif params.get( 'add_sample_button', False ):
- return self.__add_sample( trans, cntrller, request, **kwd )
+ request_id = trans.security.encode_id( request.id )
+ return self.add_sample( trans, cntrller, request_id, **kwd )
request_type_select_field = self.__build_request_type_id_select_field( trans, selected_value=request_type_id )
# Widgets to be rendered on the request form
widgets = []
@@ -194,7 +195,7 @@ class RequestsCommon( BaseController, Us
widgets += request_type.request_form.get_widgets( user, **kwd )
# In case there is an error on the form, make sure to populate widget fields with anything the user
# may have already entered.
- self.populate_widgets_from_kwd( trans, widgets, **kwd )
+ widgets = self.populate_widgets_from_kwd( trans, widgets, **kwd )
if request_type is not None or status == 'error':
# Either the user selected a request_type or an error exists on the form.
if is_admin:
@@ -214,6 +215,29 @@ class RequestsCommon( BaseController, Us
message=message,
status=status )
@web.expose
+ @web.require_login( "view request" )
+ def view_request( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ request_id = params.get( 'id', None )
+ try:
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
+ except:
+ return invalid_id_redirect( trans, cntrller, request_id )
+ sample_state_id = params.get( 'sample_state_id', None )
+ # Get the user entered sample information
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
+ request_widgets = self.__get_request_widgets( trans, request.id )
+ return trans.fill_template( '/requests/common/view_request.mako',
+ cntrller=cntrller,
+ request=request,
+ request_widgets=request_widgets,
+ current_samples=current_samples,
+ status=status,
+ message=message )
+ @web.expose
@web.require_login( "edit sequencing requests" )
def edit_basic_request_info( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
@@ -226,7 +250,7 @@ class RequestsCommon( BaseController, Us
return invalid_id_redirect( trans, cntrller, request_id )
name = util.restore_text( params.get( 'name', '' ) )
desc = util.restore_text( params.get( 'desc', '' ) )
- if params.get( 'edit_basic_request_info_button', False ) or params.get( 'edit_samples_button', False ):
+ if params.get( 'edit_basic_request_info_button', False ):
if not name:
status = 'error'
message = 'Enter the name of the request'
@@ -244,7 +268,7 @@ class RequestsCommon( BaseController, Us
widgets = widgets + request.type.request_form.get_widgets( request.user, request.values.content, **kwd )
# In case there is an error on the form, make sure to populate widget fields with anything the user
# may have already entered.
- self.populate_widgets_from_kwd( trans, widgets, **kwd )
+ widgets = self.populate_widgets_from_kwd( trans, widgets, **kwd )
return trans.fill_template( 'requests/common/edit_basic_request_info.mako',
cntrller=cntrller,
request_type=request.type,
@@ -371,8 +395,8 @@ class RequestsCommon( BaseController, Us
status=status,
message=message ) )
@web.expose
- @web.require_login( "sequencing request page" )
- def manage_request( self, trans, cntrller, **kwd ):
+ @web.require_login( "manage samples" )
+ def edit_samples( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
message = util.restore_text( params.get( 'message', '' ) )
@@ -382,80 +406,64 @@ class RequestsCommon( BaseController, Us
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
return invalid_id_redirect( trans, cntrller, request_id )
- sample_state_id = params.get( 'sample_state_id', None )
+ # This method is called when the user is adding new samples as well as
+ # editing existing samples, so we use the editing_samples flag to keep
+ # track of what's occurring.
+ # TODO: CRITICAL: We need another round of code fixes to abstract out
+ # adding samples vs editing samples. We need to eliminate the need for
+ # this editing_samples flag since it is not maintainable. Greg will do
+ # this work as soon as possible.
+ editing_samples = util.string_as_bool( params.get( 'editing_samples', False ) )
+ if params.get( 'cancel_changes_button', False ):
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ id=request_id,
+ editing_samples=editing_samples ) )
+ # Get all libraries for which the current user has permission to add items.
+ libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
# Get the user entered sample information
- current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
- selected_samples = self.__get_selected_samples( trans, request, **kwd )
- selected_value = params.get( 'sample_operation', 'none' )
- if selected_value != 'none' and not selected_samples:
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
+ encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ def handle_error( **kwd ):
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
+ if not encoded_selected_sample_ids and sample_operation != 'none':
+ # Probably occurred due to refresh_on_change...is there a better approach?
+ kwd[ 'sample_operation' ] = 'none'
message = 'Select at least one sample before selecting an operation.'
- status = 'error'
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id,
- status=status,
- message=message ) )
- sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
- sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True )
+ kwd[ 'message' ] = message
+ handle_error( **kwd )
if params.get( 'import_samples_button', False ):
# Import sample field values from a csv file
return self.__import_samples( trans, cntrller, request, current_samples, libraries, **kwd )
elif params.get( 'add_sample_button', False ):
- return self.__add_sample( trans, cntrller, request, **kwd )
+ return self.add_sample( trans, cntrller, request_id, **kwd )
elif params.get( 'save_samples_button', False ):
- return self.__save_sample( trans, cntrller, request, current_samples, **kwd )
- elif params.get( 'edit_samples_button', False ):
- managing_samples = True
- elif params.get( 'cancel_changes_button', False ):
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id ) )
- pass
- elif params.get( 'change_state_button', False ):
- sample_event_comment = util.restore_text( params.get( 'sample_event_comment', '' ) )
- new_state = trans.sa_session.query( trans.model.SampleState ).get( trans.security.decode_id( sample_state_id ) )
- self.update_sample_state(trans, cntrller, selected_samples, new_state, comment=sample_event_comment )
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller=cntrller,
- action='update_request_state',
- request_id=request_id ) )
- elif params.get( 'cancel_change_state_button', False ):
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id ) )
- elif params.get( 'change_lib_button', False ):
- library_id = params.get( 'sample_0_library_id', None )
- try:
- library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
- except:
- invalid_id_redirect( trans, cntrller, library_id )
- folder_id = params.get( 'sample_0_folder_id', None )
- try:
- folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
- except:
- invalid_id_redirect( trans, cntrller, folder_id )
- for sample_id in selected_samples:
- sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
- sample.library = library
- sample.folder = folder
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- trans.sa_session.refresh( request )
- message = 'Changes made to the selected samples have been saved. '
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id,
- status=status,
- message=message ) )
- elif params.get( 'cancel_change_lib_button', False ):
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=trans.security.encode_id( request.id ) ) )
+ if encoded_selected_sample_ids:
+ # This gets tricky because we need the list of samples to include the same number
+ # of objects that that current_samples ( i.e., request.samples ) has. We'll first
+ # get the set of samples corresponding to the checked sample ids.
+ samples = []
+ selected_samples = []
+ for encoded_sample_id in encoded_selected_sample_ids:
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( encoded_sample_id ) )
+ selected_samples.append( sample )
+ # Now build the list of samples, inserting None for samples that have not been checked.
+ for sample in request.samples:
+ if sample in selected_samples:
+ samples.append( sample )
+ else:
+ samples.append( None )
+ # The __save_samples method requires samples widgets, not sample objects
+ samples = self.__get_sample_widgets( trans, request, samples, **kwd )
+ else:
+ samples = current_samples
+ return self.__save_samples( trans, cntrller, request, samples, **kwd )
request_widgets = self.__get_request_widgets( trans, request.id )
sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
libraries_select_field, folders_select_field = self.__build_library_and_folder_select_fields( trans,
@@ -464,12 +472,13 @@ class RequestsCommon( BaseController, Us
libraries,
None,
**kwd )
- # Build the sample_state_id_select_field SelectField
+ sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, sample_operation )
+ sample_state_id = params.get( 'sample_state_id', None )
sample_state_id_select_field = self.__build_sample_state_id_select_field( trans, request, sample_state_id )
- return trans.fill_template( '/requests/common/manage_request.mako',
+ return trans.fill_template( '/requests/common/edit_samples.mako',
cntrller=cntrller,
request=request,
- selected_samples=selected_samples,
+ encoded_selected_sample_ids=encoded_selected_sample_ids,
request_widgets=request_widgets,
current_samples=current_samples,
sample_copy=sample_copy,
@@ -478,7 +487,7 @@ class RequestsCommon( BaseController, Us
libraries_select_field=libraries_select_field,
folders_select_field=folders_select_field,
sample_state_id_select_field=sample_state_id_select_field,
- managing_samples=managing_samples,
+ editing_samples=editing_samples,
status=status,
message=message )
@web.expose
@@ -489,7 +498,7 @@ class RequestsCommon( BaseController, Us
except:
if cntrller == 'api':
trans.response.status = 400
- return "Malformed sample id ( %s ) specified, unable to decode." % str( sample_id )
+ return "Invalid sample id ( %s ) specified, unable to decode." % str( sample_id )
else:
return invalid_id_redirect( trans, cntrller, sample_id )
event = trans.model.SampleEvent( sample, new_state, comment )
@@ -516,7 +525,7 @@ class RequestsCommon( BaseController, Us
if ok_for_now:
request.deleted = True
trans.sa_session.add( request )
- # delete all the samples belonging to this request
+ # Delete all the samples belonging to this request
for s in request.samples:
s.deleted = True
trans.sa_session.add( s )
@@ -546,7 +555,7 @@ class RequestsCommon( BaseController, Us
if ok_for_now:
request.deleted = False
trans.sa_session.add( request )
- # undelete all the samples belonging to this request
+ # Undelete all the samples belonging to this request
for s in request.samples:
s.deleted = False
trans.sa_session.add( s )
@@ -655,9 +664,10 @@ class RequestsCommon( BaseController, Us
if cntrller == 'api':
return 200, message
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
id=request_id,
+ editing_samples=True,
status=status,
message=message ) )
final_state = False
@@ -673,8 +683,8 @@ class RequestsCommon( BaseController, Us
event = trans.model.RequestEvent( request, state, comments )
trans.sa_session.add( event )
trans.sa_session.flush()
- # check if an email notification is configured to be sent when the samples
- # are in this state
+ # See if an email notification is configured to be sent when the samples
+ # are in this state.
retval = request.send_email_notification( trans, common_state, final_state )
if retval:
message = comments + retval
@@ -683,113 +693,10 @@ class RequestsCommon( BaseController, Us
if cntrller == 'api':
return 200, message
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
- id=trans.security.encode_id(request.id),
- status='done',
- message=message ) )
- def __save_sample( self, trans, cntrller, request, current_samples, **kwd ):
- # Save all the new/unsaved samples entered by the user
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
- is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
- selected_value = params.get( 'sample_operation', 'none' )
- # Check for duplicate sample names
- message = ''
- for index in range( len( current_samples ) - len( request.samples ) ):
- sample_index = index + len( request.samples )
- current_sample = current_samples[ sample_index ]
- sample_name = current_sample[ 'name' ]
- if not sample_name.strip():
- message = 'Enter the name of sample number %i' % sample_index
- break
- count = 0
- for i in range( len( current_samples ) ):
- if sample_name == current_samples[ i ][ 'name' ]:
- count += 1
- if count > 1:
- message = "This request has %i samples with the name (%s). Samples belonging to a request must have unique names." % ( count, sample_name )
- break
- if message:
- selected_samples = self.__get_selected_samples( trans, request, **kwd )
- request_widgets = self.__get_request_widgets( trans, request.id )
- sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
- sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
- status = 'error'
- return trans.fill_template( '/requests/common/manage_request.mako',
- cntrller=cntrller,
- request=request,
- selected_samples=selected_samples,
- request_widgets=request_widgets,
- current_samples=current_samples,
- sample_copy=sample_copy,
- managing_samples=managing_samples,
- sample_operation_select_field=sample_operation_select_field,
- status=status,
- message=message )
- if not managing_samples:
- for index in range( len( current_samples ) - len( request.samples ) ):
- sample_index = len( request.samples )
- current_sample = current_samples[ sample_index ]
- form_values = trans.model.FormValues( request.type.sample_form, current_sample[ 'field_values' ] )
- trans.sa_session.add( form_values )
- trans.sa_session.flush()
- s = trans.model.Sample( current_sample[ 'name' ],
- '',
- request,
- form_values,
- current_sample[ 'barcode' ],
- current_sample[ 'library' ],
- current_sample[ 'folder' ] )
- trans.sa_session.add( s )
- trans.sa_session.flush()
- else:
- message = 'Changes made to the samples are saved. '
- for sample_index in range( len( current_samples ) ):
- sample = request.samples[ sample_index ]
- current_sample = current_samples[ sample_index ]
- sample.name = current_sample[ 'name' ]
- sample.library = current_sample[ 'library' ]
- sample.folder = current_sample[ 'folder' ]
- if request.is_submitted:
- bc_message = self.__validate_barcode( trans, sample, current_sample[ 'barcode' ] )
- if bc_message:
- status = 'error'
- message += bc_message
- else:
- if not sample.bar_code:
- # If this is a 'new' (still in its first state) sample
- # change the state to the next
- if sample.state.id == request.type.states[0].id:
- event = trans.model.SampleEvent( sample,
- request.type.states[1],
- 'Sample added to the system' )
- trans.sa_session.add( event )
- trans.sa_session.flush()
- # Now check if all the samples' barcode has been entered.
- # If yes then send notification email if configured
- common_state = request.samples_have_common_state
- if common_state:
- if common_state.id == request.type.states[1].id:
- event = trans.model.RequestEvent( request,
- request.states.SUBMITTED,
- "All samples are in %s state." % common_state.name )
- trans.sa_session.add( event )
- trans.sa_session.flush()
- request.send_email_notification( trans, request.type.states[1] )
- sample.bar_code = current_samples[sample_index]['barcode']
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
- form_values.content = current_sample[ 'field_values' ]
- trans.sa_session.add( form_values )
- trans.sa_session.flush()
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=trans.security.encode_id( request.id ),
+ id=request_id,
+ editing_samples=True,
status=status,
message=message ) )
@web.expose
@@ -876,25 +783,33 @@ class RequestsCommon( BaseController, Us
cntrller=cntrller,
events_list=events_list,
sample=sample )
- def __add_sample( self, trans, cntrller, request, **kwd ):
+ @web.expose
+ @web.require_login( "add sample" )
+ def add_sample( self, trans, cntrller, request_id, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
+ try:
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
+ except:
+ return invalid_id_redirect( trans, cntrller, request_id )
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
# Get the widgets for rendering the request form
request_widgets = self.__get_request_widgets( trans, request.id )
- current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
if not current_samples:
# Form field names are zero-based.
sample_index = 0
else:
sample_index = len( current_samples )
if params.get( 'add_sample_button', False ):
+ # Get all libraries for which the current user has permission to add items
+ libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
num_samples_to_add = int( params.get( 'num_sample_to_copy', 1 ) )
# See if the user has selected a sample to copy.
copy_sample_index = int( params.get( 'copy_sample_index', -1 ) )
for index in range( num_samples_to_add ):
+ id_index = len( current_samples ) + 1
if copy_sample_index != -1:
# The user has selected a sample to copy.
library_id = current_samples[ copy_sample_index][ 'library_select_field' ].get_selected( return_value=True )
@@ -902,7 +817,7 @@ class RequestsCommon( BaseController, Us
name = current_samples[ copy_sample_index ][ 'name' ] + '_%i' % ( len( current_samples ) + 1 )
field_values = [ val for val in current_samples[ copy_sample_index ][ 'field_values' ] ]
else:
- # The user has not selected a sample to copy (may just be adding a sample).
+ # The user has not selected a sample to copy, just adding a new generic sample.
library_id = None
folder_id = None
name = 'Sample_%i' % ( len( current_samples ) + 1 )
@@ -910,7 +825,7 @@ class RequestsCommon( BaseController, Us
# Build the library_select_field and folder_select_field for the new sample being added.
library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
user=request.user,
- sample_index=len( current_samples ),
+ sample_index=id_index,
libraries=libraries,
sample=None,
library_id=library_id,
@@ -926,101 +841,21 @@ class RequestsCommon( BaseController, Us
field_values=field_values,
library_select_field=library_select_field,
folder_select_field=folder_select_field ) )
- selected_samples = self.__get_selected_samples( trans, request, **kwd )
- selected_value = params.get( 'sample_operation', 'none' )
- sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
+ encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, sample_operation )
sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
- return trans.fill_template( '/requests/common/manage_request.mako',
+ return trans.fill_template( '/requests/common/edit_samples.mako',
cntrller=cntrller,
request=request,
- selected_samples=selected_samples,
+ encoded_selected_sample_ids=encoded_selected_sample_ids,
request_widgets=request_widgets,
current_samples=current_samples,
sample_operation_select_field=sample_operation_select_field,
sample_copy=sample_copy,
- managing_samples=managing_samples,
+ editing_samples=False,
message=message,
status=status )
- def __get_sample_info( self, trans, request, **kwd ):
- """
- Retrieves all user entered sample information and returns a
- list of all the samples and their field values.
- """
- params = util.Params( kwd )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
- # Bet all data libraries accessible to this user
- libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
- # Build the list of widgets which will be used to render each sample row on the request page
- current_samples = []
- for index, sample in enumerate( request.samples ):
- library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
- request.user,
- index,
- libraries,
- sample,
- **kwd )
- current_samples.append( dict( name=sample.name,
- barcode=sample.bar_code,
- library=sample.library,
- folder=sample.folder,
- field_values=sample.values.content,
- library_select_field=library_select_field,
- folder_select_field=folder_select_field ) )
- if not managing_samples:
- sample_index = len( request.samples )
- else:
- sample_index = 0
- while True:
- library_id = params.get( 'sample_%i_library_id' % sample_index, None )
- folder_id = params.get( 'sample_%i_folder_id' % sample_index, None )
- if params.get( 'sample_%i_name' % sample_index, False ):
- # Data library
- try:
- library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
- #library_id = library.id
- except:
- library = None
- if library is not None:
- # Folder
- try:
- folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
- #folder_id = folder.id
- except:
- if library:
- folder = library.root_folder
- else:
- folder = None
- else:
- folder = None
- sample_info = dict( name=util.restore_text( params.get( 'sample_%i_name' % sample_index, '' ) ),
- barcode=util.restore_text( params.get( 'sample_%i_barcode' % sample_index, '' ) ),
- library=library,
- folder=folder)
- sample_info[ 'field_values' ] = []
- for field_index in range( len( request.type.sample_form.fields ) ):
- sample_info[ 'field_values' ].append( util.restore_text( params.get( 'sample_%i_field_%i' % ( sample_index, field_index ), '' ) ) )
- if not managing_samples:
- sample_info[ 'library_select_field' ], sample_info[ 'folder_select_field' ] = self.__build_library_and_folder_select_fields( trans,
- request.user,
- sample_index,
- libraries,
- None,
- library_id,
- folder_id,
- **kwd )
- current_samples.append( sample_info )
- else:
- sample_info[ 'library_select_field' ], sample_info[ 'folder_select_field' ] = self.__build_library_and_folder_select_fields( trans,
- request.user,
- sample_index,
- libraries,
- request.samples[ sample_index ],
- **kwd )
- current_samples[ sample_index ] = sample_info
- sample_index += 1
- else:
- break
- return current_samples, managing_samples, libraries
@web.expose
@web.require_login( "delete sample from sequencing request" )
def delete_sample( self, trans, cntrller, **kwd ):
@@ -1032,7 +867,7 @@ class RequestsCommon( BaseController, Us
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
return invalid_id_redirect( trans, cntrller, request_id )
- current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
sample_index = int( params.get( 'sample_id', 0 ) )
sample_name = current_samples[sample_index]['name']
sample = request.has_sample( sample_name )
@@ -1042,9 +877,10 @@ class RequestsCommon( BaseController, Us
trans.sa_session.flush()
message = 'Sample (%s) has been deleted.' % sample_name
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
id=trans.security.encode_id( request.id ),
+ editing_samples=True,
status=status,
message=message ) )
@web.expose
@@ -1059,12 +895,12 @@ class RequestsCommon( BaseController, Us
sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
except:
return invalid_id_redirect( trans, cntrller, sample_id )
- # check if a library and folder has been set for this sample yet.
+ # See if a library and folder have been set for this sample.
if not sample.library or not sample.folder:
status = 'error'
message = "Set a data library and folder for sequencing request (%s) to transfer datasets." % sample.name
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
id=trans.security.encode_id( sample.request.id ),
status=status,
@@ -1101,7 +937,6 @@ class RequestsCommon( BaseController, Us
SampleName,DataLibrary,DataLibraryFolder,Field1,Field2....
"""
params = util.Params( kwd )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
file_obj = params.get( 'file_data', '' )
try:
reader = csv.reader( file_obj.file )
@@ -1137,23 +972,211 @@ class RequestsCommon( BaseController, Us
folder_select_field=folder_select_field,
field_values=row[3:] ) )
except Exception, e:
- status = 'error'
- message = 'Error thrown when importing samples file: %s' % str( e )
+ if str( e ) == "'unicode' object has no attribute 'file'":
+ message = "Select a file"
+ else:
+ message = 'Error attempting to create samples from selected file: %s.' % str( e )
+ message += ' Make sure the selected csv file uses the format: SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...'
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='add_sample',
cntrller=cntrller,
- id=trans.security.encode_id( request.id ),
- status=status,
+ request_id=trans.security.encode_id( request.id ),
+ add_sample_button='Add sample',
+ status='error',
message=message ) )
request_widgets = self.__get_request_widgets( trans, request.id )
sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
- return trans.fill_template( '/requests/common/manage_request.mako',
+ return trans.fill_template( '/requests/common/edit_samples.mako',
cntrller=cntrller,
request=request,
request_widgets=request_widgets,
current_samples=current_samples,
sample_copy=sample_copy,
- managing_samples=managing_samples )
+ editing_samples=False )
+ def __save_samples( self, trans, cntrller, request, samples, **kwd ):
+ # Here we handle saving all new samples added by the user as well as saving
+ # changes to any subset of the request's samples.
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ editing_samples = util.string_as_bool( params.get( 'editing_samples', False ) )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ sample_operation = params.get( 'sample_operation', 'none' )
+ # Check for duplicate sample names within the request
+ self.__validate_sample_names( trans, cntrller, request, samples, **kwd )
+ if editing_samples:
+ library = None
+ folder = None
+ def handle_error( **kwd ):
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
+ # Here we handle saving changes to single samples as well as saving changes to
+ # selected sets of samples. If samples are selected, the sample_operation param
+ # will have a value other than 'none', and the samples param will be a list of
+ # encoded sample ids. There are currently only 2 multi-select operations;
+ # 'Change state' and 'Select data library and folder'. If sample_operation is
+ # 'none, then the samples param will be a list of sample objects.
+ if sample_operation == 'Change state':
+ sample_state_id = params.get( 'sample_state_id', None )
+ if sample_state_id in [ None, 'none' ]:
+ message = "Select a new state from the <b>Change current state</b> list before clicking the <b>Save</b> button."
+ kwd[ 'message' ] = message
+ del kwd[ 'save_changes_button' ]
+ handle_error( **kwd )
+ sample_event_comment = util.restore_text( params.get( 'sample_event_comment', '' ) )
+ new_state = trans.sa_session.query( trans.model.SampleState ).get( trans.security.decode_id( sample_state_id ) )
+ # Send the encoded sample_ids to update_sample_state.
+ # TODO: make changes necessary to just send the samples...
+ encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
+ # Make sure all samples have a unique barcode if the state is changing
+ for sample_index in range( len( samples ) ):
+ current_sample = samples[ sample_index ]
+ if current_sample is None:
+ # We have a None value because the user did not select this sample
+ # on which to perform the action.
+ continue
+ request_sample = request.samples[ sample_index ]
+ bc_message = self.__validate_barcode( trans, request_sample, current_sample[ 'barcode' ] )
+ if bc_message:
+ #status = 'error'
+ message += bc_message
+ kwd[ 'message' ] = message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
+ self.update_sample_state( trans, cntrller, encoded_selected_sample_ids, new_state, comment=sample_event_comment )
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ cntrller=cntrller,
+ action='update_request_state',
+ request_id=trans.security.encode_id( request.id ) ) )
+ elif sample_operation == 'Select data library and folder':
+ library_id = params.get( 'sample_0_library_id', 'none' )
+ folder_id = params.get( 'sample_0_folder_id', 'none' )
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ self.__update_samples( trans, request, samples, **kwd )
+ # See if all the samples' barcodes are in the same state,
+ # and if so send email if configured to.
+ common_state = request.samples_have_common_state
+ if common_state and common_state.id == request.type.states[1].id:
+ event = trans.model.RequestEvent( request,
+ request.states.SUBMITTED,
+ "All samples are in %s state." % common_state.name )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ request.send_email_notification( trans, request.type.states[1] )
+ message = 'Changes made to the samples have been saved. '
+ else:
+ # Saving a newly created sample.
+ for index in range( len( samples ) - len( request.samples ) ):
+ sample_index = len( request.samples )
+ current_sample = samples[ sample_index ]
+ form_values = trans.model.FormValues( request.type.sample_form, current_sample[ 'field_values' ] )
+ trans.sa_session.add( form_values )
+ trans.sa_session.flush()
+ s = trans.model.Sample( current_sample[ 'name' ],
+ '',
+ request,
+ form_values,
+ current_sample[ 'barcode' ],
+ current_sample[ 'library' ],
+ current_sample[ 'folder' ] )
+ trans.sa_session.add( s )
+ trans.sa_session.flush()
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ id=trans.security.encode_id( request.id ),
+ editing_samples=editing_samples,
+ status=status,
+ message=message ) )
+ def __update_samples( self, trans, request, sample_widgets, **kwd ):
+ # Determine if the values in kwd require updating the request's samples. The list of
+ # sample_widgets must have the same number of objects as request.samples, but some of
+ # the objects can be None. Those that are not None correspond to samples selected by
+ # the user for performing an action on multiple samples simultaneously.
+ def handle_error( **kwd ):
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
+ params = util.Params( kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ if sample_operation != 'none':
+ # These values will be in kwd if the user checked 1 or more checkboxes for performing this action
+ # on a set of samples.
+ library_id = params.get( 'sample_0_library_id', 'none' )
+ folder_id = params.get( 'sample_0_folder_id', 'none' )
+ for index, obj in enumerate( sample_widgets ):
+ if obj is not None:
+ # obj will be None if the user checked sample check boxes and selected an action
+ # to perform on multiple samples, but did not select certain samples.
+ sample_updated = False
+ # If this sample has values in kwd, then kwd will include a
+ # key whose value is this sample's ( possibly changed ) name. An
+ # example of this key is 'sample_0_name'.
+ for k, v in kwd.items():
+ name_key = 'sample_%i_name' % index
+ if k == name_key:
+ sample_updated = True
+ break
+ if sample_updated:
+ id_index = index + 1
+ if sample_operation == 'none':
+ # We are handling changes to a single sample.
+ library_id = params.get( 'sample_%i_library_id' % id_index, 'none' )
+ folder_id = params.get( 'sample_%i_folder_id' % id_index, 'none' )
+ # Update the corresponding sample's values as well as the sample_widget.
+ sample = request.samples[ index ]
+ sample.name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
+ # The bar_code field requires special handling because after a request is submitted, the
+ # state of a sample cannot be changed without a bar_code assocaited with the sample.
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
+ if not bar_code and not sample.bar_code:
+ # If this is a 'new' (still in its first state) sample, create an event
+ if sample.state.id == request.states[0].id:
+ event = trans.model.SampleEvent( sample,
+ request.type.states[1],
+ 'Sample added to the system' )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ elif bar_code:
+ bc_message = self.__validate_barcode( trans, sample, bar_code )
+ if bc_message:
+ kwd[ 'message' ] = bc_message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
+ sample.bar_code = bar_code
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ sample.library = library
+ sample.folder = folder
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
+ form_values.content = field_values
+ trans.sa_session.add_all( ( sample, form_values ) )
+ trans.sa_session.flush()
+ def __get_library_and_folder( self, trans, library_id, folder_id ):
+ try:
+ library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
+ except:
+ library = None
+ if library and folder_id == 'none':
+ folder = library.root_folder
+ elif library and folder_id != 'none':
+ try:
+ folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
+ except:
+ if library:
+ folder = library.root_folder
+ else:
+ folder = None
+ else:
+ folder = None
+ return library, folder
# ===== Methods for handling form definition widgets =====
def __get_request_widgets( self, trans, id ):
"""Get the widgets for the request"""
@@ -1179,27 +1202,104 @@ class RequestsCommon( BaseController, Us
value=request.values.content[ index ],
helptext=field[ 'helptext' ] + ' (' + required_label + ')' ) )
return request_widgets
- def __get_samples_widgets( self, trans, request, libraries, **kwd ):
- """Get the widgets for all of the samples currently associated with the request"""
- # The current_samples_widgets list is a list of dictionaries
- current_samples_widgets = []
- for index, sample in enumerate( request.samples ):
- # Build the library_select_field and folder_select_field for each existing sample
- library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
+ def __get_sample_widgets( self, trans, request, samples, **kwd ):
+ """
+ Returns a list of dictionaries, each representing the widgets that define a sample on a form.
+ The widgets are populated from kwd based on the set of samples received. The set of samples
+ corresponds to a reques.samples list, but if the user checked specific check boxes on the form,
+ those samples that were not check will have None objects in the list of samples. In this case,
+ the corresponding sample_widget is populated from the db rather than kwd.
+ """
+ params = util.Params( kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ # This method is called when the user is adding new samples as well as
+ # editing existing samples, so we use the editing_samples flag to keep
+ # track of what's occurring.
+ editing_samples = util.string_as_bool( params.get( 'editing_samples', False ) )
+ sample_widgets = []
+ if sample_operation != 'none':
+ # The sample_operatin param has a value other than 'none', and a specified
+ # set of samples was received.
+ library_id = util.restore_text( params.get( 'sample_0_library_id', 'none' ) )
+ folder_id = util.restore_text( params.get( 'sample_0_folder_id', 'none' ) )
+ # Build the list of widgets which will be used to render each sample row on the request page
+ if not request:
+ return sample_widgets
+ # Get the list of libraries for which the current user has permission to add items.
+ libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
+ # Build the list if sample widgets, populating the values from kwd.
+ for index, sample in enumerate( samples ):
+ id_index = index + 1
+ if sample is None:
+ # Id sample is None, then we'll use the sample from the request object since it will
+ # not have updated =values from kwd.
+ sample = request.samples[ index ]
+ name = sample.name
+ bar_code = sample.bar_code
+ library = sample.library
+ folder = sample.folder
+ field_values = sample.values.content,
+ else:
+ # Update the sample attributes from kwd
+ name = util.restore_text( params.get( 'sample_%i_name' % index, sample.name ) )
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, sample.bar_code ) )
+ library_id = util.restore_text( params.get( 'sample_%i_library_id' % id_index, '' ) )
+ if not library_id and sample.library:
+ library_id = trans.security.encode_id( sample.library.id )
+ folder_id = util.restore_text( params.get( 'sample_%i_folder_id' % id_index, '' ) )
+ if not folder_id and sample.folder:
+ folder_id = trans.security.encode_id( sample.folder.id )
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans=trans,
user=request.user,
- sample_index=index,
+ sample_index=id_index,
libraries=libraries,
sample=sample,
+ library_id=library_id,
+ folder_id=folder_id,
**kwd )
- # Append the dictionary for the current sample to the current_samples_widgets list
- current_samples_widgets.append( dict( name=sample.name,
- barcode=sample.bar_code,
- library=sample.library,
- folder=sample.folder,
- field_values=sample.values.content,
- library_select_field=library_select_field,
- folder_select_field=folder_select_field ) )
- return current_samples_widgets
+ sample_widgets.append( dict( name=name,
+ barcode=bar_code,
+ library=library,
+ folder=folder,
+ field_values=field_values,
+ library_select_field=library_select_field,
+ folder_select_field=folder_select_field ) )
+ # There may be additional new samples on the form that have not yet been associated with the request.
+ # TODO: factor this code so it is not duplicating what's above.
+ index = len( samples )
+ while True:
+ name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
+ if not name:
+ break
+ id_index = index + 1
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
+ library_id = util.restore_text( params.get( 'sample_%i_library_id' % id_index, '' ) )
+ folder_id = util.restore_text( params.get( 'sample_%i_folder_id' % id_index, '' ) )
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans=trans,
+ user=request.user,
+ sample_index=id_index,
+ libraries=libraries,
+ sample=None,
+ library_id=library_id,
+ folder_id=folder_id,
+ **kwd )
+ sample_widgets.append( dict( name=name,
+ barcode=bar_code,
+ library=library,
+ folder=folder,
+ field_values=field_values,
+ library_select_field=library_select_field,
+ folder_select_field=folder_select_field ) )
+ index += 1
+ return sample_widgets
# ===== Methods for building SelectFields used on various request forms =====
def __build_copy_sample_select_field( self, trans, current_samples ):
copy_sample_index_select_field = SelectField( 'copy_sample_index' )
@@ -1240,26 +1340,30 @@ class RequestsCommon( BaseController, Us
# The libraries dictionary looks like: { library : '1,2' }, library : '3' }. Its keys are the libraries that
# should be displayed for the current user and its values are strings of comma-separated folder ids that should
# NOT be displayed.
- #
- # TODO: all object ids received in the params must be encoded.
params = util.Params( kwd )
library_select_field_name= "sample_%i_library_id" % sample_index
folder_select_field_name = "sample_%i_folder_id" % sample_index
if not library_id:
- library_id = params.get( library_select_field_name, 'none' )
+ library_id = params.get( library_select_field_name, None )
+ if not folder_id:
+ folder_id = params.get( folder_select_field_name, None )
selected_library = None
selected_hidden_folder_ids = []
showable_folders = []
- if sample and sample.library and library_id == 'none':
- library_id = str( sample.library.id )
+ if library_id not in [ None, 'none' ]:
+ # If we have a selected library, get the list of it's folders that are not accessible to the current user
+ for library, hidden_folder_ids in libraries.items():
+ encoded_id = trans.security.encode_id( library.id )
+ if encoded_id == str( library_id ):
+ selected_library = library
+ selected_hidden_folder_ids = hidden_folder_ids.split( ',' )
+ break
+ elif sample and sample.library and library_id == 'none':
+ # The user previously selected a library but is now resetting the selection to 'none'
+ selected_library = None
+ elif sample and sample.library:
+ library_id = trans.security.encode_id( sample.library.id )
selected_library = sample.library
- # If we have a selected library, get the list of it's folders that are not accessible to the current user
- for library, hidden_folder_ids in libraries.items():
- encoded_id = trans.security.encode_id( library.id )
- if encoded_id == str( library_id ):
- selected_library = library
- selected_hidden_folder_ids = hidden_folder_ids.split( ',' )
- break
# sample_%i_library_id SelectField with refresh on change enabled
library_select_field = build_select_field( trans,
libraries.keys(),
@@ -1275,20 +1379,12 @@ class RequestsCommon( BaseController, Us
selected_library,
[ trans.app.security_agent.permitted_actions.LIBRARY_ADD ],
selected_hidden_folder_ids )
- if sample:
- # The user is editing the request, and may have previously selected a folder
- if sample.folder:
- selected_folder_id = sample.folder.id
+ if folder_id:
+ selected_folder_id = folder_id
+ elif sample and sample.folder:
+ selected_folder_id = trans.security.encode_id( sample.folder.id )
else:
- # If a library is selected but not a folder, use the library's root folder
- if sample.library:
- selected_folder_id = sample.library.root_folder.id
- else:
- # The user just selected a folder
- selected_folder_id = params.get( folder_select_field_name, 'none' )
- elif folder_id:
- # TODO: not sure when this would be passed
- selected_folder_id = folder_id
+ selected_folder_id = trans.security.encode_id( selected_library.root_folder.id )
else:
selected_folder_id = 'none'
# TODO: Change the name of the library root folder to "Library root" to clarify to the
@@ -1328,9 +1424,35 @@ class RequestsCommon( BaseController, Us
message += '<b>' + ef + '</b> '
return message
return None
+ def __validate_sample_names( self, trans, cntrller, request, current_samples, **kwd ):
+ # Check for duplicate sample names for all samples of the request.
+ editing_samples = util.string_as_bool( kwd.get( 'editing_samples', False ) )
+ message = ''
+ for index in range( len( current_samples ) - len( request.samples ) ):
+ sample_index = index + len( request.samples )
+ current_sample = current_samples[ sample_index ]
+ sample_name = current_sample[ 'name' ]
+ if not sample_name.strip():
+ message = 'Enter the name of sample number %i' % sample_index
+ break
+ count = 0
+ for i in range( len( current_samples ) ):
+ if sample_name == current_samples[ i ][ 'name' ]:
+ count += 1
+ if count > 1:
+ message = "You tried to add %i samples with the name (%s). Samples belonging to a request must have unique names." % ( count, sample_name )
+ break
+ if message:
+ del kwd[ 'save_samples_button' ]
+ kwd[ 'message' ] = message
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
def __validate_barcode( self, trans, sample, barcode ):
"""
- Makes sure that the barcode about to be assigned to a sample is gobally unique.
+ Makes sure that the barcode about to be assigned to a sample is globally unique.
That is, barcodes must be unique across requests in Galaxy sample tracking.
"""
message = ''
@@ -1338,13 +1460,18 @@ class RequestsCommon( BaseController, Us
for index in range( len( sample.request.samples ) ):
# Check for empty bar code
if not barcode.strip():
- message = 'Fill in the barcode for sample (%s).' % sample.name
- break
+ if sample.state.id == sample.request.type.states[0].id:
+ # The user has not yet filled in the barcode value, but the sample is
+ # 'new', so all is well.
+ break
+ else:
+ message = "Fill in the barcode for sample (%s) before changing it's state." % sample.name
+ break
# TODO: Add a unique constraint to sample.bar_code table column
# Make sure bar code is unique
- for sample_has_bar_code in trans.sa_session.query( trans.model.Sample ) \
- .filter( trans.model.Sample.table.c.bar_code == barcode ):
- if sample_has_bar_code and sample_has_bar_code.id != sample.id:
+ for sample_with_barcode in trans.sa_session.query( trans.model.Sample ) \
+ .filter( trans.model.Sample.table.c.bar_code == barcode ):
+ if sample_with_barcode and sample_with_barcode.id != sample.id:
message = '''The bar code (%s) associated with the sample (%s) belongs to another sample.
Bar codes must be unique across all samples, so use a different bar code
for this sample.''' % ( barcode, sample.name )
@@ -1360,15 +1487,15 @@ class RequestsCommon( BaseController, Us
elif len( email ) > 255:
error = "(%s) exceeds maximum allowable length. " % str( email )
return error
- # ===== Other miscellaneoud utility methods =====
- def __get_selected_samples( self, trans, request, **kwd ):
- selected_samples = []
+ # ===== Other miscellaneous utility methods =====
+ def __get_encoded_selected_sample_ids( self, trans, request, **kwd ):
+ encoded_selected_sample_ids = []
for sample in request.samples:
if CheckboxField.is_checked( kwd.get( 'select_sample_%i' % sample.id, '' ) ):
- selected_samples.append( trans.security.encode_id( sample.id ) )
- return selected_samples
+ encoded_selected_sample_ids.append( trans.security.encode_id( sample.id ) )
+ return encoded_selected_sample_ids
-# ===== Miscellaneoud utility methods outside of the RequestsCommon class =====
+# ===== Miscellaneous utility methods outside of the RequestsCommon class =====
def invalid_id_redirect( trans, cntrller, obj_id, action='browse_requests' ):
status = 'error'
message = "Invalid request id (%s)" % str( obj_id )
--- a/templates/requests/common/dataset_transfer.mako
+++ b/templates/requests/common/dataset_transfer.mako
@@ -1,47 +1,46 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
+<br/><br/>
+
+<ul class="manage-table-actions">
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">Refresh</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( sample.library.id ) )}">Target Data Library</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li>
+</ul>
+
%if message:
${render_msg( message, status )}
%endif
-<h2>Datasets of Sample "${sample.name}"</h2>
-
-<ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">Refresh</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( sample.library.id ) )}">${sample.library.name} Data Library</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a>
- </li>
-</ul>
-
-%if dataset_files:
- <div class="form-row">
- <table class="grid">
- <thead>
- <tr>
- <th>Name</th>
- <th>Size</th>
- <th>Status</th>
- </tr>
- <thead>
- <tbody>
- %for dataset_file in dataset_files:
- <tr>
- <td>${dataset_file.name}</td>
- <td>${dataset_file.size}</td>
- <td>${dataset_file.status}</td>
- </tr>
- %endfor
- </tbody>
- </table>
+<div class="toolForm">
+ <div class="toolFormTitle">Sample "${sample.name}"</div>
+ <div class="toolFormBody">
+ %if dataset_files:
+ <div class="form-row">
+ <table class="grid">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Size</th>
+ <th>Status</th>
+ </tr>
+ <thead>
+ <tbody>
+ %for dataset_file in dataset_files:
+ <tr>
+ <td>${dataset_file.name}</td>
+ <td>${dataset_file.size}</td>
+ <td>${dataset_file.status}</td>
+ </tr>
+ %endfor
+ </tbody>
+ </table>
+ </div>
+ %else:
+ <div class="form-row">
+ There are no datasets associated with this sample.
+ </div>
+ %endif
</div>
-%else:
- <div class="form-row">
- There are no datasets associated with this sample.
- </div>
-%endif
+</div>
--- a/templates/requests/common/manage_request.mako
+++ /dev/null
@@ -1,615 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-<%namespace file="/requests/common/sample_state.mako" import="render_sample_state" />
-<%namespace file="/requests/common/sample_datasets.mako" import="render_sample_datasets" />
-
-<%def name="stylesheets()">
- ${parent.stylesheets()}
- ${h.css( "library" )}
-</%def>
-
-<%def name="javascripts()">
- ${parent.javascripts()}
- <script type="text/javascript">
- function showContent(vThis)
- {
- // http://www.javascriptjunkie.com
- // alert(vSibling.className + " " + vDef_Key);
- vParent = vThis.parentNode;
- vSibling = vParent.nextSibling;
- while (vSibling.nodeType==3) {
- // Fix for Mozilla/FireFox Empty Space becomes a TextNode or Something
- vSibling = vSibling.nextSibling;
- };
- if(vSibling.style.display == "none")
- {
- vThis.src="/static/images/fugue/toggle.png";
- vThis.alt = "Hide";
- vSibling.style.display = "block";
- } else {
- vSibling.style.display = "none";
- vThis.src="/static/images/fugue/toggle-expand.png";
- vThis.alt = "Show";
- }
- return;
- }
-
- $(document).ready(function(){
- //hide the all of the element with class msg_body
- $(".msg_body").hide();
- //toggle the component with class msg_body
- $(".msg_head").click(function(){
- $(this).next(".msg_body").slideToggle(0);
- });
- });
-
- // Looks for changes in sample states using an async request. Keeps
- // calling itself (via setTimeout) until all samples are in a terminal
- // state.
- var updater = function ( sample_states ) {
- // Check if there are any items left to track
- var empty = true;
- for ( i in sample_states ) {
- empty = false;
- break;
- }
- if ( ! empty ) {
- setTimeout( function() { updater_callback( sample_states ) }, 1000 );
- }
- };
-
- var updater_callback = function ( sample_states ) {
- // Build request data
- var ids = []
- var states = []
- $.each( sample_states, function ( id, state ) {
- ids.push( id );
- states.push( state );
- });
- // Make ajax call
- $.ajax( {
- type: "POST",
- url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}",
- dataType: "json",
- data: { ids: ids.join( "," ), states: states.join( "," ) },
- success : function ( data ) {
- $.each( data, function( id, val ) {
- // Replace HTML
- var cell1 = $("#sampleState-" + id);
- cell1.html( val.html_state );
- var cell2 = $("#sampleDatasets-" + id);
- cell2.html( val.html_datasets );
- sample_states[ parseInt( id ) ] = val.state;
- });
- updater( sample_states );
- },
- error: function() {
- // Just retry, like the old method, should try to be smarter
- updater( sample_states );
- }
- });
- };
-
- function checkAllFields()
- {
- var chkAll = document.getElementById('checkAll');
- var checks = document.getElementsByTagName('input');
- var boxLength = checks.length;
- var allChecked = false;
- var totalChecked = 0;
- if ( chkAll.checked == true )
- {
- for ( i=0; i < boxLength; i++ )
- {
- if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
- {
- checks[i].checked = true;
- }
- }
- }
- else
- {
- for ( i=0; i < boxLength; i++ )
- {
- if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
- {
- checks[i].checked = false
- }
- }
- }
- }
-
- function stopRKey(evt) {
- var evt = (evt) ? evt : ((event) ? event : null);
- var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
- if ((evt.keyCode == 13) && (node.type=="text")) {return false;}
- }
- document.onkeypress = stopRKey
- </script>
-</%def>
-
-<% is_admin = cntrller == 'requests_admin' and trans.user_is_admin() %>
-
-<div class="grid-header">
- <h2>Sequencing Request "${request.name}"</h2>
-
- <ul class="manage-table-actions">
- <li><a class="action-button" id="seqreq-${request.id}-popup" class="menubutton">Sequencing Request Actions</a></li>
- <div popupmenu="seqreq-${request.id}-popup">
- %if request.is_unsubmitted and request.samples:
- <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
- %endif
- <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">History</a>
- %if is_admin:
- %if request.is_submitted:
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
- %endif
- %endif
- </div>
- <li><a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse requests</a></li>
- </ul>
-
- <div class="toolParamHelp" style="clear: both;">
- <b>Sequencer</b>: ${request.type.name}
- %if is_admin:
- | <b>User</b>: ${request.user.email}
- %endif
- %if request.is_submitted:
- | <b>State</b>: <i>${request.state}</i>
- %else:
- | <b>State</b>: ${request.state}
- %endif
- </div>
-</div>
-
-%if request.samples_without_library_destinations:
- ${render_msg( "Select a target data library and folder for all the samples before starting the sequence run", "warning" )}
-%endif
-
-%if request.is_rejected:
- ${render_msg( "Reason for rejection: " + request.last_comment, "warning" )}
-%endif
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> Request Information</h4>
-<div style="display:none;">
- <div class="form-row">
- <ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit request informaton</a>
- </li>
- </ul>
- </div>
- <table class="grid" border="0">
- <tbody>
- <tr>
- <td valign="top" width="50%">
- <div class="form-row">
- <label>Description:</label>
- ${request.desc}
- </div>
- <div style="clear: both"></div>
- %for index, rd in enumerate( request_widgets ):
- <%
- field_label = rd[ 'label' ]
- field_value = rd[ 'value' ]
- %>
- <div class="form-row">
- <label>${field_label}:</label>
- %if field_label == 'State':
- <a href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">${field_value}</a>
- %else:
- ${field_value}
- %endif
- </div>
- <div style="clear: both"></div>
- %endfor
- </td>
- <td valign="top" width="50%">
- <div class="form-row">
- <label>Date created:</label>
- ${request.create_time}
- </div>
- <div class="form-row">
- <label>Date updated:</label>
- ${request.update_time}
- </div>
- <div class="form-row">
- <label>Email notification recipients:</label>
- <%
- if request.notification:
- emails = ', '.join( request.notification[ 'email' ] )
- else:
- emails = ''
- %>
- ${emails}
- </div>
- <div style="clear: both"></div>
- <div class="form-row">
- <label>Email notification on sample states:</label>
- <%
- if request.notification:
- states = []
- for ss in request.type.states:
- if ss.id in request.notification[ 'sample_states' ]:
- states.append( ss.name )
- states = ', '.join( states )
- else:
- states = ''
- %>
- ${states}
- </div>
- <div style="clear: both"></div>
- </td>
- </tr>
- </tbody>
- </table>
-</div>
-<br/>
-<form id="manage_request" name="manage_request" action="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ), managing_samples=managing_samples )}" method="post">
- %if current_samples:
- <% sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True ) %>
- ## first render the basic info grid
- ${render_basic_info_grid()}
- %if not request.is_new and not managing_samples and len( sample_operation_select_field.options ) > 1:
- <div class="form-row" style="background-color:#FAFAFA;">
- For selected samples:
- ${sample_operation_select_field.get_html()}
- </div>
- %if sample_operation_selected_value != 'none' and selected_samples:
- <div class="form-row" style="background-color:#FAFAFA;">
- %if sample_operation_selected_value == trans.model.Sample.bulk_operations.CHANGE_STATE:
- ## sample_operation_selected_value == 'Change state'
- <div class="form-row">
- <label>Change current state</label>
- ${sample_state_id_select_field.get_html()}
- <label>Comments</label>
- <input type="text" name="sample_event_comment" value=""/>
- <div class="toolParamHelp" style="clear: both;">
- Optional
- </div>
- </div>
- <div class="form-row">
- <input type="submit" name="change_state_button" value="Save"/>
- <input type="submit" name="cancel_change_state_button" value="Cancel"/>
- </div>
- %elif sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY:
- <% libraries_selected_value = libraries_select_field.get_selected( return_value=True ) %>
- <div class="form-row">
- <label>Select data library:</label>
- ${libraries_select_field.get_html()}
- </div>
- %if libraries_selected_value != 'none':
- <div class="form-row">
- <label>Select folder:</label>
- ${folders_select_field.get_html()}
- </div>
- <div class="form-row">
- <input type="submit" name="change_lib_button" value="Save"/>
- <input type="submit" name="cancel_change_lib_button" value="Cancel"/>
- </div>
- %endif
- %endif
- </div>
- %endif
- %endif
- ## Render the other grids
- <% trans.sa_session.refresh( request.type.sample_form ) %>
- %for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
- ${render_grid( grid_index, grid_name, request.type.sample_form.fields_of_grid( grid_index ) )}
- %endfor
- %else:
- <label>There are no samples.</label>
- %endif
- %if request.samples and request.is_submitted:
- <script type="text/javascript">
- // Updater
- updater({${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
- </script>
- %endif
- %if not managing_samples:
- <table class="grid">
- <tbody>
- <tr>
- <div class="form-row">
- %if request.is_unsubmitted:
- <td>
- %if current_samples:
- <label>Copy </label>
- <input type="integer" name="num_sample_to_copy" value="1" size="3"/>
- <label>samples from sample</label>
- ${sample_copy.get_html()}
- %endif
- <input type="submit" name="add_sample_button" value="Add New"/>
- </td>
- %endif
- <td>
- %if current_samples and len( current_samples ) <= len( request.samples ):
- <input type="submit" name="edit_samples_button" value="Edit samples"/>
- %endif
- </td>
- </div>
- </tr>
- </tbody>
- </table>
- %endif
- %if request.samples or current_samples:
- %if managing_samples:
- <div class="form-row">
- <input type="submit" name="save_samples_button" value="Save"/>
- <input type="submit" name="cancel_changes_button" value="Cancel"/>
- </div>
- %elif len( current_samples ) > len( request.samples ):
- <div class="form-row">
- <input type="submit" name="save_samples_button" value="Save"/>
- <input type="submit" name="cancel_changes_button" value="Cancel"/>
- </div>
- %endif
- %endif
-</form>
-<br/>
-%if request.is_unsubmitted:
- <form id="import" name="import" action="${h.url_for( controller='requests_common', action='manage_request', managing_samples=managing_samples, id=trans.security.encode_id( request.id ) )}" enctype="multipart/form-data" method="post" >
- <h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> Import samples</h4>
- <div style="display:none;">
- <input type="file" name="file_data" />
- <input type="submit" name="import_samples_button" value="Import samples"/>
- <br/>
- <div class="toolParamHelp" style="clear: both;">
- The csv file must be in the following format:<br/>
- SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...
- </div>
- </div>
- </form>
-%endif
-
-<%def name="render_grid( grid_index, grid_name, fields_dict )">
- <br/>
- <% if not grid_name:
- grid_name = "Grid "+ grid_index
- %>
- <div>
- %if managing_samples or len( current_samples ) > len( request.samples ):
- <h4><img src="/static/images/fugue/toggle.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
- <div>
- %else:
- <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
- <div style="display:none;">
- %endif
- <table class="grid">
- <thead>
- <tr>
- <th>Name</th>
- %for index, field in fields_dict.items():
- <th>
- ${field['label']}
- <div class="toolParamHelp" style="clear: both;">
- <i>${field['helptext']}</i>
- </div>
- </th>
- %endfor
- <th></th>
- </tr>
- <thead>
- <tbody>
- <% trans.sa_session.refresh( request ) %>
- %for sample_index, sample in enumerate( current_samples ):
- %if managing_samples:
- <tr>${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict)}</tr>
- %else:
- <tr>
- %if sample_index in range( len( request.samples ) ):
- ${render_sample( sample_index, sample['name'], sample['field_values'], fields_dict )}
- %else:
- ${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict)}
- %endif
- </tr>
- %endif
- %endfor
- </tbody>
- </table>
- </div>
- </div>
-</%def>
-
-## This function displays the "Basic Information" grid
-<%def name="render_basic_info_grid()">
- <h3>Sample Information</h3>
- <table class="grid">
- <thead>
- <tr>
- <th><input type="checkbox" id="checkAll" name=select_all_samples_checkbox value="true" onclick='checkAllFields(1);'><input type="hidden" name=select_all_samples_checkbox value="true"></th>
- <th>Name</th>
- <th>Barcode</th>
- <th>State</th>
- <th>Data Library</th>
- <th>Folder</th>
- %if request.is_submitted or request.is_complete:
- <th>Datasets Transferred</th>
- %endif
- <th></th>
- </tr>
- <thead>
- <tbody>
- <% trans.sa_session.refresh( request ) %>
- %for sample_index, info in enumerate( current_samples ):
- <%
- if sample_index in range( len(request.samples ) ):
- sample = request.samples[sample_index]
- else:
- sample = None
- %>
- %if managing_samples:
- <tr>${show_basic_info_form( sample_index, sample, info )}</tr>
- %else:
- <tr>
- %if sample_index in range( len( request.samples ) ):
- %if trans.security.encode_id( sample.id ) in selected_samples:
- <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" checked><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"></td>
- %else:
- <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true"><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"></td>
- %endif
- <td>${info['name']}</td>
- <td>${info['barcode']}</td>
- %if sample.request.is_unsubmitted:
- <td>Unsubmitted</td>
- %else:
- <td><a id="sampleState-${sample.id}" href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${render_sample_state( sample )}</a></td>
- %endif
- %if info['library']:
- %if cntrller == 'requests':
- <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
- %elif is_admin:
- <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library_admin', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
- %endif
- %else:
- <td></td>
- %endif
- %if info['folder']:
- <td>${info['folder'].name}</td>
- %else:
- <td></td>
- %endif
- %if request.is_submitted or request.is_complete:
- <td><a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">
- ${render_sample_datasets( sample )}
- </a></td>
- %endif
- %else:
- ${show_basic_info_form( sample_index, sample, info )}
- %endif
- %if request.is_unsubmitted or request.is_rejected:
- <td>
- %if sample:
- %if sample.request.is_unsubmitted:
- <a class="action-button" href="${h.url_for( controller='requests_common', cntrller=cntrller, action='delete_sample', request_id=trans.security.encode_id( request.id ), sample_id=sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a>
- %endif
- %endif
- </td>
- %endif
- </tr>
- %endif
- %endfor
- </tbody>
- </table>
-</%def>
-
-<%def name="show_basic_info_form( sample_index, sample, info )">
- <td></td>
- <td>
- <input type="text" name="sample_${sample_index}_name" value="${info['name']}" size="10"/>
- <div class="toolParamHelp" style="clear: both;">
- <i>${' (required)' }</i>
- </div>
- </td>
- %if cntrller == 'requests':
- %if sample:
- %if sample.request.is_unsubmitted:
- <td></td>
- %else:
- <td><input type="text" name="sample_${sample_index}_barcode" value="${info['barcode']}" size="10"/></td>
- %endif
- %else:
- <td></td>
- %endif
- %elif is_admin:
- %if sample:
- %if sample.request.is_unsubmitted:
- <td></td>
- %else:
- <td><input type="text" name="sample_${sample_index}_barcode" value="${info['barcode']}" size="10"/></td>
- %endif
- %else:
- <td></td>
- %endif
- %endif
- %if sample:
- %if sample.request.is_unsubmitted:
- <td>Unsubmitted</td>
- %else:
- <td><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
- %endif
- %else:
- <td></td>
- %endif
- <td>${info['library_select_field'].get_html()}</td>
- <td>${info['folder_select_field'].get_html()}</td>
- %if request.is_submitted or request.is_complete:
- <%
- if sample:
- label = str( len( sample.datasets ) )
- else:
- label = 'Add'
- %>
- <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
- %endif
-</%def>
-
-<%def name="render_sample( index, sample_name, sample_values, fields_dict )">
- <td>
- ${sample_name}
- </td>
- %for field_index, field in fields_dict.items():
- <td>
- %if sample_values[field_index]:
- %if field['type'] == 'WorkflowField':
- %if str(sample_values[field_index]) != 'none':
- <% workflow = trans.sa_session.query( trans.app.model.StoredWorkflow ).get( int(sample_values[field_index]) ) %>
- <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
- %endif
- %else:
- ${sample_values[field_index]}
- %endif
- %else:
- <i>None</i>
- %endif
- </td>
- %endfor
-</%def>
-
-<%def name="render_sample_form( index, sample_name, sample_values, fields_dict )">
- <td>${sample_name}</td>
- %for field_index, field in fields_dict.items():
- <td>
- %if field['type'] == 'TextField':
- <input type="text" name="sample_${index}_field_${field_index}" value="${sample_values[field_index]}" size="7"/>
- %elif field['type'] == 'SelectField':
- <select name="sample_${index}_field_${field_index}" last_selected_value="2">
- %for option_index, option in enumerate(field['selectlist']):
- %if option == sample_values[field_index]:
- <option value="${option}" selected>${option}</option>
- %else:
- <option value="${option}">${option}</option>
- %endif
- %endfor
- </select>
- %elif field['type'] == 'WorkflowField':
- <select name="sample_${index}_field_${field_index}">
- %if str(sample_values[field_index]) == 'none':
- <option value="none" selected>Select one</option>
- %else:
- <option value="none">Select one</option>
- %endif
- %for option_index, option in enumerate(request.user.stored_workflows):
- %if not option.deleted:
- %if str(option.id) == str(sample_values[field_index]):
- <option value="${option.id}" selected>${option.name}</option>
- %else:
- <option value="${option.id}">${option.name}</option>
- %endif
- %endif
- %endfor
- </select>
- %elif field['type'] == 'CheckboxField':
- <input type="checkbox" name="sample_${index}_field_${field_index}" value="Yes"/>
- %endif
- <div class="toolParamHelp" style="clear: both;">
- <i>${'('+field['required']+')' }</i>
- </div>
- </td>
- %endfor
-</%def>
1
0

20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1288646507 14400
# Node ID 9285b39ae2f3dfd5017808b4d9b657094fec802b
# Parent 695c89309bcb2f5d31bfb8b9d8b0124ff3abf90f
Packing editor.
Tweak to reload.
--- a/static/scripts/packed/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/packed/galaxy.workflow_editor.canvas.js
@@ -1,1 +1,1 @@
-function Terminal(a){this.element=a;this.connectors=[]}$.extend(Terminal.prototype,{connect:function(a){this.connectors.push(a);if(this.node){this.node.changed()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.changed()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});function OutputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}OutputTerminal.prototype=new Terminal();function InputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}InputTerminal.prototype=new Terminal();$.extend(InputTerminal.prototype,{can_accept:function(a){if(this.connectors.length<1){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.act
ion_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){if(f[b]=="input"||issubtype(f[b],this.datatypes[c])){return true}}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;this.handle1.connect(this);this.handle2=a;this.handle2.connect(this)},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},redraw:function(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};var h=n(this.handle1.element)+5;var g=i(t
his.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*f;this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canvas.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});function Node(a){this.element=a;this.input_terminals={};this.output_terminals={};this.tool_errors={}}$.extend(Node.prototype,{enable_input_terminal:function(d,a,b){var c=this;$(d).each(function(){var f=this.terminal=new InputTerminal(this,b);f.node=c;f.name=a;$(this).bind
("dropstart",function(g){g.dragProxy.terminal.connectors[0].inner_color="#BBFFBB"}).bind("dropend",function(g){g.dragProxy.terminal.connectors[0].inner_color="#FFFFFF"}).bind("drop",function(g){(new Connector(g.dragTarget.terminal,g.dropTarget.terminal)).redraw()}).bind("hover",function(){if(f.connectors.length>0){var g=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='buttons'></div>").append($("<img/>").attr("src",image_path+"/delete_icon.png").click(function(){$.each(f.connectors,function(i,h){h.destroy()});g.remove()}))).bind("mouseleave",function(){$(this).remove()});g.css({top:$(this).offset().top-2,left:$(this).offset().left-g.width(),"padding-right":$(this).width()}).show()}});c.input_terminals[a]=f})},enable_output_terminal:function(d,a,b){var c=this;$(d).each(function(){var g=this;var f=this.terminal=new OutputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dragstart",function(j){workflow.check_changes_in_active_form();v
ar i=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);i.terminal=new OutputTerminal(i);var k=new Connector();k.dragging=true;k.connect(this.terminal,i.terminal);$.dropManage({filter:function(h){return this.terminal.can_accept(f)}}).addClass("input-terminal-active");return i}).bind("drag",function(i){var h=function(){var k=$(i.dragProxy).offsetParent().offset(),j=i.offsetX-k.left,l=i.offsetY-k.top;$(i.dragProxy).css({left:j,top:l});i.dragProxy.terminal.redraw();canvas_manager.update_viewport_overlay()};h();$("#canvas-container").get(0).scroll_panel.test(i,h)}).bind("dragend",function(h){h.dragProxy.terminal.connectors[0].destroy();$(h.dragProxy).remove();$.dropManage().removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()});c.output_terminals[a]=f})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:fun
ction(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(h){var g=this.element;if(h.type){this.type=h.type}this.name=h.name;this.form_html=h.form_html;this.tool_state=h.tool_state;this.tool_errors=h.tool_errors;this.tooltip=h.tooltip?h.tooltip:"";this.annotation=h.annotation;this.post_job_actions=h.post_job_actions;if(h.workflow_outputs){this.workflow_outputs=h.workflow_outputs}else{this.workflow_outputs=[]}if(this.tool_errors){g.addClass("tool-node-error")}else{g.removeClass("tool-node-error")}var d=this;var c=Math.max(150,g.width());var a=g.find(".toolFormBody");a.find("div").remove();var i=$("<div class='inputs'></div
>").appendTo(a);$.each(h.data_inputs,function(k,f){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,f.name,f.extensions);var b=$("<div class='form-row dataRow input-data-row' name='"+f.name+"'>"+f.label+"</div>");b.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(b);c=Math.max(c,b.outerWidth());b.css({position:"",left:"",top:"",display:""});$("body").remove(b);i.append(b.prepend(j))});if((h.data_inputs.length>0)&&(h.data_outputs.length>0)){a.append($("<div class='rule'></div>"))}$.each(h.data_outputs,function(k,b){var j=$("<div class='terminal output-terminal'></div>");d.enable_output_terminal(j,b.name,b.extensions);var f=b.name;if(b.extensions.indexOf("input")<0){f=f+" ("+b.extensions.join(", ")+")"}var m=$("<div class='form-row dataRow'>"+f+"</div>");if(d.type=="tool"){var l=$("<div class='callout'></div>").css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",image_path+"/
fugue/asterisk-small-outline.png").click(function(){if($.inArray(b.name,d.workflow_outputs)!=-1){d.workflow_outputs.splice($.inArray(b.name,d.workflow_outputs),1);l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{d.workflow_outputs.push(b.name);l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tipsy({delayIn:500,fallback:"Flag this as a workflow output. All non-flagged outputs will be hidden."});l.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});l.show();m.append(l);if($.inArray(b.name,d.workflow_outputs)==-1){l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}m.bind("hover",function(){l.find("img").attr("src",image_path+"/fugue/asterisk-small-yellow.png")});m.bind("mouseleave",function(){l.find("img").attr("src",image_path+"/fugue/asterisk-small.png");if($.inArray(b.name,d.workf
low_outputs)==-1){l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}})}m.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(m);c=Math.max(c,m.outerWidth()+17);m.css({position:"",left:"",top:"",display:""});$("body").remove(m);a.append(m.append(j))});g.css("width",Math.min(250,Math.max(g.width(),c)));workflow.node_changed(this)},update_field_data:function(f){var c=$(this.element),d=this;this.tool_state=f.tool_state;this.form_html=f.form_html;this.tool_errors=f.tool_errors;this.annotation=f.annotation;this.post_job_actions=$.parseJSON(f.post_job_actions);if(this.tool_errors){c.addClass("tool-node-error")}else{c.removeClass("tool-node-error")}var g=c.find("div.inputs");var b=$("<div class='inputs'></div>");var a=g.find("div.input-data-row");$.each(f.data_inputs,function(k,h){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,h.n
ame,h.extensions);g.find("div[name="+h.name+"]").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){j[0].terminal.connectors[0]=i;i.handle2=j[0].terminal}});$(this).remove()});b.append($("<div class='form-row dataRow input-data-row' name='"+h.name+"'>"+h.label+"</div>").prepend(j))});g.replaceWith(b);g.find("div.input-data-row > .terminal").each(function(){this.terminal.destroy()});this.changed();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},changed:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);th
is.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(h,i){g[i.name]=null;$.each(i.connectors,function(j,k){g[i.name]={id:k.handle1.node.id,output_name:k.handle1.name}})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={job_id:h.id,action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job
_actions:f.post_job_actions,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(a){wf=this;var b=0;wf.name=a.name;$.each(a.steps,function(f,d){var c=prebuild_node("tool",d.name,d.tool_id);c.init_field_data(d);if(d.position){c.element.css({top:d.position.top,left:d.position.left})}c.id=d.id;wf.nodes[c.id]=c;b=Math.max(b,parseInt(f))});wf.id_counter=b+1;$.each(a.steps,function(f,d){var c=wf.nodes[f];$.each(d.input_connections,function(h,g){if(g){var i=wf.nodes[g.id];var j=new Connector();j.connect(i.output_terminals[g.output_name],c.input_terminals[h]);j.redraw()}})})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_no
de!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30
;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math
.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node(i);g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+image_path+"/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<img/>").attr("src",image_path+"/delete_icon.png").c
lick(function(b){g.destroy()}).hover(function(){$(this).attr("src",image_path+"/delete_icon_dark.png")},function(){$(this).attr("src",image_path+"/delete_icon.png")}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o){var f=$(this).offsetParent().offset(),b=o.offsetX-f.left,p=o.offsetY-f.top;$(this).css({left:b,top:p});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];retur
n(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){thi
s.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(g){var h=$(this).offset();var f=b.cc.position();c=f.top-h.top;d=f.left-h.left}).bind("drag",function(f){a(f.offsetX+d,f.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k){var j=b.cc.width(),g=b.cc.height(),f=b.oc.width(),h=b.oc.height(),i=$(this).offsetParent().offset(),m=k.offsetX-i.left,l=k.offsetY-i.top;a(-(m/f*j),-(l/h*g))}).bind("dragend",function(){workflow.fit_canvas_to_nodes
();b.draw_overview()});$("#overview-border").bind("drag",function(g){var i=$(this).offsetParent();var h=i.offset();var f=Math.max(i.width()-(g.offsetX-h.left),i.height()-(g.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(f){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("hei
ght",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
+function Terminal(a){this.element=a;this.connectors=[]}$.extend(Terminal.prototype,{connect:function(a){this.connectors.push(a);if(this.node){this.node.changed()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.changed()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});function OutputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}OutputTerminal.prototype=new Terminal();function InputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}InputTerminal.prototype=new Terminal();$.extend(InputTerminal.prototype,{can_accept:function(a){if(this.connectors.length<1){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.act
ion_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){if(f[b]=="input"||issubtype(f[b],this.datatypes[c])){return true}}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;this.handle1.connect(this);this.handle2=a;this.handle2.connect(this)},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},redraw:function(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};var h=n(this.handle1.element)+5;var g=i(t
his.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*f;this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canvas.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});function Node(a){this.element=a;this.input_terminals={};this.output_terminals={};this.tool_errors={}}$.extend(Node.prototype,{enable_input_terminal:function(d,a,b){var c=this;$(d).each(function(){var f=this.terminal=new InputTerminal(this,b);f.node=c;f.name=a;$(this).bind
("dropstart",function(g){g.dragProxy.terminal.connectors[0].inner_color="#BBFFBB"}).bind("dropend",function(g){g.dragProxy.terminal.connectors[0].inner_color="#FFFFFF"}).bind("drop",function(g){(new Connector(g.dragTarget.terminal,g.dropTarget.terminal)).redraw()}).bind("hover",function(){if(f.connectors.length>0){var g=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='buttons'></div>").append($("<img/>").attr("src",image_path+"/delete_icon.png").click(function(){$.each(f.connectors,function(i,h){h.destroy()});g.remove()}))).bind("mouseleave",function(){$(this).remove()});g.css({top:$(this).offset().top-2,left:$(this).offset().left-g.width(),"padding-right":$(this).width()}).show()}});c.input_terminals[a]=f})},enable_output_terminal:function(d,a,b){var c=this;$(d).each(function(){var g=this;var f=this.terminal=new OutputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dragstart",function(j){workflow.check_changes_in_active_form();v
ar i=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);i.terminal=new OutputTerminal(i);var k=new Connector();k.dragging=true;k.connect(this.terminal,i.terminal);$.dropManage({filter:function(h){return this.terminal.can_accept(f)}}).addClass("input-terminal-active");return i}).bind("drag",function(i){var h=function(){var k=$(i.dragProxy).offsetParent().offset(),j=i.offsetX-k.left,l=i.offsetY-k.top;$(i.dragProxy).css({left:j,top:l});i.dragProxy.terminal.redraw();canvas_manager.update_viewport_overlay()};h();$("#canvas-container").get(0).scroll_panel.test(i,h)}).bind("dragend",function(h){h.dragProxy.terminal.connectors[0].destroy();$(h.dragProxy).remove();$.dropManage().removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()});c.output_terminals[a]=f})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:fun
ction(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(h){var g=this.element;if(h.type){this.type=h.type}this.name=h.name;this.form_html=h.form_html;this.tool_state=h.tool_state;this.tool_errors=h.tool_errors;this.tooltip=h.tooltip?h.tooltip:"";this.annotation=h.annotation;this.post_job_actions=h.post_job_actions;if(h.workflow_outputs){this.workflow_outputs=h.workflow_outputs}else{this.workflow_outputs=[]}if(this.tool_errors){g.addClass("tool-node-error")}else{g.removeClass("tool-node-error")}var d=this;var c=Math.max(150,g.width());var a=g.find(".toolFormBody");a.find("div").remove();var i=$("<div class='inputs'></div
>").appendTo(a);$.each(h.data_inputs,function(k,f){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,f.name,f.extensions);var b=$("<div class='form-row dataRow input-data-row' name='"+f.name+"'>"+f.label+"</div>");b.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(b);c=Math.max(c,b.outerWidth());b.css({position:"",left:"",top:"",display:""});$("body").remove(b);i.append(b.prepend(j))});if((h.data_inputs.length>0)&&(h.data_outputs.length>0)){a.append($("<div class='rule'></div>"))}$.each(h.data_outputs,function(k,b){var j=$("<div class='terminal output-terminal'></div>");d.enable_output_terminal(j,b.name,b.extensions);var f=b.name;if(b.extensions.indexOf("input")<0){f=f+" ("+b.extensions.join(", ")+")"}var m=$("<div class='form-row dataRow'>"+f+"</div>");if(d.type=="tool"){var l=$("<div class='callout'></div>").css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",image_path+"/
fugue/asterisk-small-outline.png").click(function(){if($.inArray(b.name,d.workflow_outputs)!=-1){d.workflow_outputs.splice($.inArray(b.name,d.workflow_outputs),1);l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{d.workflow_outputs.push(b.name);l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tipsy({delayIn:500,fallback:"Flag this as a workflow output. All non-flagged outputs will be hidden."});l.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});l.show();m.append(l);if($.inArray(b.name,d.workflow_outputs)==-1){l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}m.bind("hover",function(){l.find("img").attr("src",image_path+"/fugue/asterisk-small-yellow.png")});m.bind("mouseleave",function(){l.find("img").attr("src",image_path+"/fugue/asterisk-small.png");if($.inArray(b.name,d.workf
low_outputs)==-1){l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}})}m.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(m);c=Math.max(c,m.outerWidth()+17);m.css({position:"",left:"",top:"",display:""});$("body").remove(m);a.append(m.append(j))});g.css("width",Math.min(250,Math.max(g.width(),c)));workflow.node_changed(this)},update_field_data:function(f){var c=$(this.element),d=this;this.tool_state=f.tool_state;this.form_html=f.form_html;this.tool_errors=f.tool_errors;this.annotation=f.annotation;this.post_job_actions=$.parseJSON(f.post_job_actions);if(this.tool_errors){c.addClass("tool-node-error")}else{c.removeClass("tool-node-error")}var g=c.find("div.inputs");var b=$("<div class='inputs'></div>");var a=g.find("div.input-data-row");$.each(f.data_inputs,function(k,h){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,h.n
ame,h.extensions);g.find("div[name="+h.name+"]").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){j[0].terminal.connectors[0]=i;i.handle2=j[0].terminal}});$(this).remove()});b.append($("<div class='form-row dataRow input-data-row' name='"+h.name+"'>"+h.label+"</div>").prepend(j))});g.replaceWith(b);g.find("div.input-data-row > .terminal").each(function(){this.terminal.destroy()});this.changed();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},changed:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);th
is.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var a=false;$.each(this.nodes,function(b,c){if(c.workflow_outputs&&c.workflow_outputs.length>0){a=true}});if(a==false){return true}wf=this;$.each(this.nodes,function(b,f){if(f.type=="tool"){var d=false;if(f.post_job_actions==null){f.post_job_actions={}}var c=[];$.each(f.post_job_actions,function(h,g){if(g.action_type=="HideDatasetAction"){c.push(h)}});if(c.length>0&&f==workflow.active_node){$.each(c,function(g,h){d=true;delete f.post_job_actions[h]})}$.each(f.output_terminals,function(h,i){var g=true;$.each(f.workflow_outputs,function(k,l){if(i.name==l){g=false}});if(g==true){d=true;var j={action_type:"HideDatasetAction",output_name:i.name,action_arguments
:{}};f.post_job_actions["HideDatasetAction"+i.name]=null;f.post_job_actions["HideDatasetAction"+i.name]=j}});if(wf.active_node==f&&d==true){wf.reload_active_node()}}})},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(h,i){g[i.name]=null;$.each(i.connectors,function(j,k){g[i.name]={id:k.handle1.node.id,output_name:k.handle1.name}})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(a){wf=this;var b=
0;wf.name=a.name;$.each(a.steps,function(f,d){var c=prebuild_node("tool",d.name,d.tool_id);c.init_field_data(d);if(d.position){c.element.css({top:d.position.top,left:d.position.left})}c.id=d.id;wf.nodes[c.id]=c;b=Math.max(b,parseInt(f))});wf.id_counter=b+1;$.each(a.steps,function(f,d){var c=wf.nodes[f];$.each(d.input_connections,function(h,g){if(g){var i=wf.nodes[g.id];var j=new Connector();j.connect(i.output_terminals[g.output_name],c.input_terminals[h]);j.redraw()}})})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){th
is.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;
$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g
+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node(i);g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+image_path+"/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<img/>").attr("src",image_path+"/delete_icon.png").click(func
tion(b){g.destroy()}).hover(function(){$(this).attr("src",image_path+"/delete_icon_dark.png")},function(){$(this).attr("src",image_path+"/delete_icon.png")}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o){var f=$(this).offsetParent().offset(),b=o.offsetX-f.left,p=o.offsetY-f.top;$(this).css({left:b,top:p});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to
_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;th
is.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(g){var h=$(this).offset();var f=b.cc.position();c=f.top-h.top;d=f.left-h.left}).bind("drag",function(f){a(f.offsetX+d,f.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k){var j=b.cc.width(),g=b.cc.height(),f=b.oc.width(),h=b.oc.height(),i=$(this).offsetParent().offset(),m=k.offsetX-i.left,l=k.offsetY-i.top;a(-(m/f*j),-(l/h*g))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw
_overview()});$("#overview-border").bind("drag",function(g){var i=$(this).offsetParent();var h=i.offset();var f=Math.max(i.width()-(g.offsetX-h.left),i.height()-(g.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(f){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$
.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -614,8 +614,11 @@ function Workflow( canvas_container ) {
}
},
reload_active_node : function() {
- this.clear_active_node();
- this.activate_node(node);
+ if (this.active_node){
+ var node = this.active_node;
+ this.clear_active_node();
+ this.activate_node(node);
+ }
},
clear_active_node : function() {
if ( this.active_node ) {
1
0

20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1288645775 14400
# Node ID 695c89309bcb2f5d31bfb8b9d8b0124ff3abf90f
# Parent cf7ec71c561323940ce95c8148d2008508920092
Cleanup of debug info.
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -476,7 +476,6 @@ function Workflow( canvas_container ) {
});
},
rectify_workflow_outputs : function() {
- console.log("RECTIFICATION!");
// Find out if we're using workflow_outputs or not.
var using_workflow_outputs = false;
$.each( this.nodes, function ( k, node ) {
@@ -493,7 +492,6 @@ function Workflow( canvas_container ) {
if (node.type == 'tool'){
var node_changed = false;
if (node.post_job_actions == null){
- console.log("CREATED FOR NEW NODE");
node.post_job_actions = {};
}
var pjas_to_rem = [];
1
0

galaxy-dist commit c4d8ffb3109e: Increment js version to get around cached editors.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1288647270 14400
# Node ID c4d8ffb3109e8cfb8143dc87c88dee3337bb7569
# Parent 9285b39ae2f3dfd5017808b4d9b657094fec802b
Increment js version to get around cached editors.
--- a/lib/galaxy/web/framework/helpers/__init__.py
+++ b/lib/galaxy/web/framework/helpers/__init__.py
@@ -44,7 +44,7 @@ def js( *args ):
TODO: This has a hardcoded "?v=X" to defeat caching. This should be done
in a better way.
"""
- return "\n".join( [ javascript_include_tag( "/static/scripts/" + name + ".js?v=7" ) for name in args ] )
+ return "\n".join( [ javascript_include_tag( "/static/scripts/" + name + ".js?v=8" ) for name in args ] )
# Hashes
1
0

galaxy-dist commit b7ac22fab158: First pass of bug fixes, anbd additional UI cleanup for Sample Tracking.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1288713844 14400
# Node ID b7ac22fab1588a565a07acc4972564c2b9198489
# Parent 667043341e81261a7fe587daa475110486b15292
First pass of bug fixes, anbd additional UI cleanup for Sample Tracking.
--- a/templates/requests/common/edit_samples.mako
+++ b/templates/requests/common/edit_samples.mako
@@ -73,9 +73,10 @@
is_complete = request.is_complete
is_unsubmitted = request.is_unsubmitted
can_add_samples = is_unsubmitted
- can_edit_or_delete_samples = is_unsubmitted and request.samples
+ can_edit_or_delete_samples = request.samples and not is_complete
can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
can_reject_or_transfer = is_admin and request.is_submitted
+ can_submit = request.samples and is_unsubmitted
%><br/><br/>
@@ -87,7 +88,7 @@
%if editing_samples and can_add_samples:
<li><a class="action-button" href="${h.url_for( controller='requests_common', action='add_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), add_sample_button='Add sample' )}">Add sample</a></li>
%endif
- %if is_unsubmitted:
+ %if can_submit:
<li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
%endif
<li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
@@ -209,12 +210,12 @@
Click the <b>Save</b> button when you have finished editing the samples
</div>
%endif
- %if request.samples and request.is_submitted:
- <script type="text/javascript">
- // Updater
- updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
- </script>
- %endif
+ ##%if request.samples and request.is_submitted:
+ ## <script type="text/javascript">
+ ## // Updater
+ ## updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
+ ## </script>
+ ##%endif
</form></div>
%if is_unsubmitted and not editing_samples:
--- a/lib/galaxy/web/controllers/requests.py
+++ b/lib/galaxy/web/controllers/requests.py
@@ -36,6 +36,12 @@ class Requests( BaseController ):
action='edit_basic_request_info',
cntrller='requests',
**kwd ) )
+ if operation == "edit_samples":
+ kwd[ 'editing_samples' ] = True
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller='requests',
+ **kwd ) )
if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='view_request',
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -46,17 +46,6 @@ class RequestsGrid( grids.Grid ):
.filter( model.RequestEvent.table.c.id.in_( select( columns=[ func.max( model.RequestEvent.table.c.id ) ],
from_obj=model.RequestEvent.table,
group_by=model.RequestEvent.table.c.request_id ) ) )
- def get_accepted_filters( self ):
- """ Returns a list of accepted filters for this column. """
- # TODO: is this method necessary?
- accepted_filter_labels_and_vals = [ model.Request.states.get( state ) for state in model.Request.states ]
- accepted_filter_labels_and_vals.append( "All" )
- accepted_filters = []
- for val in accepted_filter_labels_and_vals:
- label = val.lower()
- args = { self.key: val }
- accepted_filters.append( grids.GridColumnFilter( label, args ) )
- return accepted_filters
# Grid definition
title = "Sequencing Requests"
@@ -64,7 +53,6 @@ class RequestsGrid( grids.Grid ):
model_class = model.Request
default_sort_key = "-update_time"
num_rows_per_page = 50
- preserve_state = True
use_paging = True
default_filter = dict( state="All", deleted="False" )
columns = [
@@ -379,14 +367,24 @@ class RequestsCommon( BaseController, Us
sample_event_comment = ""
event = trans.model.RequestEvent( request, request.states.SUBMITTED, sample_event_comment )
trans.sa_session.add( event )
- # change the state of each of the samples of thus request
- new_state = request.type.states[0]
+ # Change the state of each of the samples of this request
+ # request.type.states is the list of SampleState objects configured
+ # by the admin for this RequestType.
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ # Samples will not have an associated SampleState until the request is submitted, at which
+ # time all samples of the request will be set to the first SampleState configured for the
+ # request's RequestType configured by the admin.
+ initial_sample_state_after_request_submitted = request.type.states[0]
for sample in request.samples:
- event = trans.model.SampleEvent( sample, new_state, 'Samples created.' )
+ event_comment = 'Request submitted and sample state set to %s.' % request.type.states[0].name
+ event = trans.model.SampleEvent( sample,
+ initial_sample_state_after_request_submitted,
+ event_comment )
trans.sa_session.add( event )
trans.sa_session.add( request )
trans.sa_session.flush()
- request.send_email_notification( trans, new_state )
+ request.send_email_notification( trans, initial_sample_state_after_request_submitted )
message = 'The request has been submitted.'
return trans.response.send_redirect( web.url_for( controller=cntrller,
action='browse_requests',
@@ -519,8 +517,6 @@ class RequestsCommon( BaseController, Us
try:
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( id ) )
except:
- message += "Invalid request ID (%s). " % str( id )
- status = 'error'
ok_for_now = False
if ok_for_now:
request.deleted = True
@@ -549,8 +545,6 @@ class RequestsCommon( BaseController, Us
try:
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( id ) )
except:
- message += "Invalid request ID (%s). " % str( id )
- status = 'error'
ok_for_now = False
if ok_for_now:
request.deleted = False
@@ -903,6 +897,7 @@ class RequestsCommon( BaseController, Us
action='edit_samples',
cntrller=cntrller,
id=trans.security.encode_id( sample.request.id ),
+ editing_samples=True,
status=status,
message=message ) )
if is_admin:
@@ -1024,7 +1019,7 @@ class RequestsCommon( BaseController, Us
if sample_state_id in [ None, 'none' ]:
message = "Select a new state from the <b>Change current state</b> list before clicking the <b>Save</b> button."
kwd[ 'message' ] = message
- del kwd[ 'save_changes_button' ]
+ del kwd[ 'save_samples_button' ]
handle_error( **kwd )
sample_event_comment = util.restore_text( params.get( 'sample_event_comment', '' ) )
new_state = trans.sa_session.query( trans.model.SampleState ).get( trans.security.decode_id( sample_state_id ) )
@@ -1056,32 +1051,38 @@ class RequestsCommon( BaseController, Us
folder_id = params.get( 'sample_0_folder_id', 'none' )
library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
self.__update_samples( trans, request, samples, **kwd )
- # See if all the samples' barcodes are in the same state,
- # and if so send email if configured to.
- common_state = request.samples_have_common_state
- if common_state and common_state.id == request.type.states[1].id:
- event = trans.model.RequestEvent( request,
- request.states.SUBMITTED,
- "All samples are in %s state." % common_state.name )
- trans.sa_session.add( event )
- trans.sa_session.flush()
- request.send_email_notification( trans, request.type.states[1] )
+ # Samples will not have an associated SampleState until the request is submitted, at which
+ # time all samples of the request will be set to the first SampleState configured for the
+ # request's RequestType defined by the admin.
+ if request.is_submitted:
+ # See if all the samples' barcodes are in the same state, and if so send email if configured to.
+ common_state = request.samples_have_common_state
+ if common_state and common_state.id == request.type.states[1].id:
+ event = trans.model.RequestEvent( request,
+ request.states.SUBMITTED,
+ "All samples are in %s state." % common_state.name )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ request.send_email_notification( trans, request.type.states[1] )
message = 'Changes made to the samples have been saved. '
else:
- # Saving a newly created sample.
+ # Saving a newly created sample. The sample will not have an associated SampleState
+ # until the request is submitted, at which time all samples of the request will be
+ # set to the first SampleState configured for the request's RequestType configured
+ # by the admin ( i.e., the sample's SampleState would be set to request.type.states[0] ).
for index in range( len( samples ) - len( request.samples ) ):
sample_index = len( request.samples )
current_sample = samples[ sample_index ]
form_values = trans.model.FormValues( request.type.sample_form, current_sample[ 'field_values' ] )
trans.sa_session.add( form_values )
trans.sa_session.flush()
- s = trans.model.Sample( current_sample[ 'name' ],
- '',
- request,
- form_values,
- current_sample[ 'barcode' ],
- current_sample[ 'library' ],
- current_sample[ 'folder' ] )
+ s = trans.model.Sample( name=current_sample[ 'name' ],
+ desc='',
+ request=request,
+ form_values=form_values,
+ bar_code='',
+ library=current_sample[ 'library' ],
+ folder=current_sample[ 'folder' ] )
trans.sa_session.add( s )
trans.sa_session.flush()
return trans.response.send_redirect( web.url_for( controller='requests_common',
@@ -1113,49 +1114,55 @@ class RequestsCommon( BaseController, Us
if obj is not None:
# obj will be None if the user checked sample check boxes and selected an action
# to perform on multiple samples, but did not select certain samples.
- sample_updated = False
- # If this sample has values in kwd, then kwd will include a
- # key whose value is this sample's ( possibly changed ) name. An
- # example of this key is 'sample_0_name'.
- for k, v in kwd.items():
- name_key = 'sample_%i_name' % index
- if k == name_key:
- sample_updated = True
- break
- if sample_updated:
- id_index = index + 1
- if sample_operation == 'none':
- # We are handling changes to a single sample.
- library_id = params.get( 'sample_%i_library_id' % id_index, 'none' )
- folder_id = params.get( 'sample_%i_folder_id' % id_index, 'none' )
- # Update the corresponding sample's values as well as the sample_widget.
- sample = request.samples[ index ]
- sample.name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
- # The bar_code field requires special handling because after a request is submitted, the
- # state of a sample cannot be changed without a bar_code assocaited with the sample.
- bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
- if not bar_code and not sample.bar_code:
- # If this is a 'new' (still in its first state) sample, create an event
- if sample.state.id == request.states[0].id:
- event = trans.model.SampleEvent( sample,
- request.type.states[1],
- 'Sample added to the system' )
+ sample = request.samples[ index ]
+ # See if any values in kwd are different from the values already associated with this sample.
+ id_index = index + 1
+ if sample_operation == 'none':
+ # We are handling changes to a single sample.
+ library_id = params.get( 'sample_%i_library_id' % id_index, 'none' )
+ folder_id = params.get( 'sample_%i_folder_id' % id_index, 'none' )
+ # Update the corresponding sample's values as well as the sample_widget.
+ name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
+ # The bar_code field requires special handling because after a request is submitted, the
+ # state of a sample cannot be changed without a bar_code associated with the sample. Bar
+ # codes can only be added to a sample after the request is submitted. Also, a samples will
+ # not have an associated SampleState until the request is submitted, at which time the sample
+ # is automatically associated with the first SamplesState configured by the admin for the
+ # request's RequestType.
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
+ if bar_code:
+ bc_message = self.__validate_barcode( trans, sample, bar_code )
+ if bc_message:
+ kwd[ 'message' ] = bc_message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
+ if not sample.bar_code:
+ # If the sample's associated SampleState is still the initial state
+ # configured by the admin for the request's RequestType, this must be
+ # the first time a bar code was added to the sample, so change it's state
+ # to the next associated SampleState.
+ if sample.state.id == request.type.states[0].id:
+ event = trans.app.model.SampleEvent(sample,
+ request.type.states[1],
+ 'Bar code associated with the sample' )
trans.sa_session.add( event )
trans.sa_session.flush()
- elif bar_code:
- bc_message = self.__validate_barcode( trans, sample, bar_code )
- if bc_message:
- kwd[ 'message' ] = bc_message
- del kwd[ 'save_samples_button' ]
- handle_error( **kwd )
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
+ form_values.content = field_values
+ if sample.name != name or \
+ sample.bar_code != bar_code or \
+ sample.library != library or \
+ sample.folder != folder or \
+ form_values.content != field_values:
+ # Information about this sample has been changed.
+ sample.name = name
sample.bar_code = bar_code
- library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
sample.library = library
sample.folder = folder
- field_values = []
- for field_index in range( len( request.type.sample_form.fields ) ):
- field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
- form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
form_values.content = field_values
trans.sa_session.add_all( ( sample, form_values ) )
trans.sa_session.flush()
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -9,8 +9,6 @@ import logging, os, pexpect, ConfigParse
log = logging.getLogger( __name__ )
-
-
class AdminRequestsGrid( RequestsGrid ):
class UserColumn( grids.TextColumn ):
def get_value( self, trans, grid, request ):
@@ -51,6 +49,7 @@ class RequestTypeGrid( grids.Grid ):
return request_type.sample_form.name
# Grid definition
+ webapp = "galaxy"
title = "Sequencer Configurations"
template = "admin/requests/grid.mako"
model_class = model.RequestType
@@ -103,6 +102,7 @@ class DataTransferGrid( grids.Grid ):
def get_value( self, trans, grid, sample_dataset ):
return sample_dataset.status
# Grid definition
+ webapp = "galaxy"
title = "Sample Datasets"
template = "admin/requests/grid.mako"
model_class = model.SampleDataset
@@ -129,9 +129,19 @@ class DataTransferGrid( grids.Grid ):
visible=False,
filterable="standard" ) )
operations = [
- grids.GridOperation( "Start Transfer", allow_multiple=True, condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ) ),
- grids.GridOperation( "Rename", allow_multiple=True, allow_popup=False, condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ) ),
- grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ) ),
+ grids.GridOperation( "Transfer",
+ allow_multiple=True,
+ condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ),
+ url_args=dict( webapp="galaxy" ) ),
+ grids.GridOperation( "Rename",
+ allow_multiple=True,
+ allow_popup=False,
+ condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ),
+ url_args=dict( webapp="galaxy" ) ),
+ grids.GridOperation( "Delete",
+ allow_multiple=True,
+ condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ),
+ url_args=dict( webapp="galaxy" ) )
]
def apply_query_filter( self, trans, query, **kwd ):
sample_id = kwd.get( 'sample_id', None )
@@ -158,6 +168,12 @@ class RequestsAdmin( BaseController, Use
action='edit_basic_request_info',
cntrller='requests_admin',
**kwd ) )
+ if operation == "edit_samples":
+ kwd[ 'editing_samples' ] = True
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller='requests_admin',
+ **kwd ) )
if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='view_request',
@@ -273,7 +289,7 @@ class RequestsAdmin( BaseController, Use
break
if no_datasets_transferred:
status = 'error'
- message = 'A dataset can be renamed only if it is in the "Not Started" state.'
+ message = 'A dataset can be renamed only if it has been transferred.'
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
sample_id=trans.security.encode_id( selected_sample_datasets[0].sample.id ),
@@ -282,7 +298,7 @@ class RequestsAdmin( BaseController, Use
return trans.fill_template( '/admin/requests/rename_datasets.mako',
sample=selected_sample_datasets[0].sample,
id_list=id_list )
- elif operation == "start transfer":
+ elif operation == "transfer":
self.__start_datatx( trans, selected_sample_datasets[0].sample, selected_sample_datasets )
# Render the grid view
sample_id = params.get( 'sample_id', None )
@@ -292,7 +308,7 @@ class RequestsAdmin( BaseController, Use
return invalid_id_redirect( trans, 'requests_admin', sample_id )
request_id = trans.security.encode_id( sample.request.id )
library_id = trans.security.encode_id( sample.library.id )
- self.datatx_grid.global_actions = [ grids.GridAction( "Refresh",
+ self.datatx_grid.global_actions = [ grids.GridAction( "Refresh page",
dict( controller='requests_admin',
action='manage_datasets',
sample_id=sample_id ) ),
@@ -302,11 +318,11 @@ class RequestsAdmin( BaseController, Use
request_id=request_id,
folder_path=sample.request.type.datatx_info[ 'data_dir' ],
sample_id=sample_id ) ),
- #grids.GridAction( 'Data library "%s"' % sample.library.name,
- # dict( controller='library_common',
- # action='browse_library',
- # cntrller='library_admin',
- # id=library_id ) ),
+ grids.GridAction( "Browse target data library",
+ dict( controller='library_common',
+ action='browse_library',
+ cntrller='library_admin',
+ id=library_id ) ),
grids.GridAction( "Browse this request",
dict( controller='requests_common',
action='view_request',
--- a/templates/requests/common/view_request.mako
+++ b/templates/requests/common/view_request.mako
@@ -18,15 +18,18 @@
from galaxy.web.framework.helpers import time_ago
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_complete = request.is_complete
is_unsubmitted = request.is_unsubmitted
can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
can_add_samples = is_unsubmitted
+ can_edit_or_delete_samples = request.samples and not is_complete
+ can_submit = request.samples and is_unsubmitted
%><br/><br/><ul class="manage-table-actions">
- %if is_unsubmitted:
+ %if can_submit:
<li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
%endif
<li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
@@ -66,13 +69,12 @@
</div><div class="form-row"><label>User:</label>
- %if is_admin:
- ${request.user.email}
- %elif request.user.username:
- ${request.user.username}
- %else:
- Unknown
- %endif
+ ${request.user.email}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Sequencer configuration:</label>
+ ${request.type.name}
<div style="clear: both"></div></div><div class="form-row">
@@ -129,11 +131,6 @@
${states}
<div style="clear: both"></div></div>
- <div class="form-row">
- <label>Sequencer configuration:</label>
- ${request.type.name}
- <div style="clear: both"></div>
- </div></div></div></div>
@@ -141,7 +138,7 @@
<p/>
%if current_samples:
<% grid_header = '<h3>Samples</h3>' %>
- ${render_samples_grid( cntrller, request, current_samples=current_samples, action='view_request', editing_samples=False, encoded_selected_sample_ids=[], render_buttons=can_edit_request, grid_header=grid_header )}
+ ${render_samples_grid( cntrller, request, current_samples=current_samples, action='view_request', editing_samples=False, encoded_selected_sample_ids=[], render_buttons=can_edit_or_delete_samples, grid_header=grid_header )}
%else:
There are no samples.
%if can_add_samples:
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -752,11 +752,18 @@
%if grid.global_actions:
<ul class="manage-table-actions">
- %for action in grid.global_actions:
- <li>
- <a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a>
- </li>
- %endfor
+ %if len( grid.global_actions ) < 4:
+ %for action in grid.global_actions:
+ <li><a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a></li>
+ %endfor
+ %else:
+ <li><a class="action-button" id="action-8675309-popup" class="menubutton">Actions</a></li>
+ <div popupmenu="action-8675309-popup">
+ %for action in grid.global_actions:
+ <a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a>
+ %endfor
+ </div>
+ %endif
</ul>
%endif
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -88,26 +88,26 @@
%if is_admin and is_submitted and editing_samples:
<td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" ${checked_str}/><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"/></td>
%endif
- <td>
+ <td valign="top"><input type="text" name="sample_${current_sample_index}_name" value="${current_sample['name']}" size="10"/><div class="toolParamHelp" style="clear: both;"><i>${' (required)' }</i></div></td>
%if sample and is_submitted or is_complete:
- <td><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
+ <td valign="top"><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
%endif
%if sample:
%if is_unsubmitted:
<td>Unsubmitted</td>
%else:
- <td><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
+ <td valign="top"><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
%endif
%else:
<td></td>
%endif
- <td>${current_sample['library_select_field'].get_html()}</td>
- <td>${current_sample['folder_select_field'].get_html()}</td>
+ <td valign="top">${current_sample['library_select_field'].get_html()}</td>
+ <td valign="top">${current_sample['folder_select_field'].get_html()}</td>
%if is_submitted or is_complete:
<%
if sample:
@@ -115,12 +115,12 @@
else:
label = 'add'
%>
- <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
- <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ <td valign="top"><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ <td valign="top"><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
%endif
%if sample and ( is_admin or is_unsubmitted ):
## Delete button
- <td><a class="action-button" href="${h.url_for( controller='requests_common', action='delete_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=current_sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a></td>
+ <td valign="top"><a class="action-button" href="${h.url_for( controller='requests_common', action='delete_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=current_sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a></td>
%endif
</%def>
@@ -132,7 +132,7 @@
is_submitted = request.is_submitted
is_unsubmitted = request.is_unsubmitted
can_add_samples = request.is_unsubmitted
- can_edit_or_delete_samples = request.samples and ( is_admin or request.is_unsubmitted )
+ can_edit_or_delete_samples = request.samples and not is_complete
%>
${grid_header}
%if render_buttons and ( can_add_samples or can_edit_or_delete_samples ):
1
0

galaxy-dist commit cbb895e2b272: If a job's input fails to set metadata, fail the job rather than leaving it in the 'new' state indefinitely.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1288376593 14400
# Node ID cbb895e2b2729d324c1693882de7939360e683a4
# Parent 23844e86e6167c79737a7e0396c39e350e95d1c4
If a job's input fails to set metadata, fail the job rather than leaving it in the 'new' state indefinitely.
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -241,6 +241,9 @@ class JobQueue( object ):
elif idata.state == idata.states.ERROR:
JobWrapper( job, self ).fail( "input data %d is in error state" % ( idata.hid ) )
return JOB_INPUT_ERROR
+ elif idata.state == idata.states.FAILED_METADATA:
+ JobWrapper( job, self ).fail( "input data %d failed to properly set metadata" % ( idata.hid ) )
+ return JOB_INPUT_ERROR
elif idata.state != idata.states.OK and not ( idata.state == idata.states.SETTING_METADATA and job.tool_id is not None and job.tool_id == self.app.datatypes_registry.set_external_metadata_tool.id ):
# need to requeue
return JOB_WAIT
1
0

galaxy-dist commit cf7ec71c5613: Shift management of the interaction between workflow outputs and HideDatasetActions to the front end editor.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1288645730 14400
# Node ID cf7ec71c561323940ce95c8148d2008508920092
# Parent 1efe19f6f3d1a75bda9c198633dd8102d3063410
Shift management of the interaction between workflow outputs and HideDatasetActions to the front end editor.
This resolves the issue with multiple HideDatasetActions being created.
Existing workflows displaying multiple HideDatasetActions per step on the Run Workflow screen will persist.
These extra HideDatasetActions are harmless, but a simple edit workflow -> save will remove them.
--- a/lib/galaxy/web/controllers/workflow.py
+++ b/lib/galaxy/web/controllers/workflow.py
@@ -1270,13 +1270,6 @@ class WorkflowController( BaseController
workflow_invocation = model.WorkflowInvocation()
workflow_invocation.workflow = workflow
outputs = odict()
- # Find out if there are any workflow outputs defined, as that influences our actions.
- use_workflow_outputs = False
- for step in workflow.steps:
- if step.type == 'tool' or step.type is None:
- if step.workflow_outputs:
- use_workflow_outputs = True
- break
for i, step in enumerate( workflow.steps ):
# Execute module
job = None
@@ -1294,24 +1287,6 @@ class WorkflowController( BaseController
job, out_data = tool.execute( trans, step.state.inputs )
outputs[ step.id ] = out_data
# Create new PJA associations with the created job, to be run on completion.
- if use_workflow_outputs:
- # We're using outputs. Check the step for outputs to be displayed. Create PJAs to hide the rest upon completion.
- step_outputs = [s.output_name for s in step.workflow_outputs]
- for output in tool.outputs.keys():
- if output not in step_outputs:
- # Necessary, unfortunately, to clean up workflows that might have more than one at this point.
- for pja in step.post_job_actions:
- if pja.action_type == "HideDatasetAction" and pja.output_name == output:
- step.post_job_actions.remove(pja)
- trans.sa_session.delete(pja)
- # Create a PJA for hiding this output.
- n_pja = PostJobAction('HideDatasetAction', step, output, {})
- else:
- # Remove any HideDatasetActions, step is flagged for output.
- for pja in step.post_job_actions:
- if pja.action_type == "HideDatasetAction" and pja.output_name == output:
- step.post_job_actions.remove(pja)
- trans.sa_session.delete(pja)
for pja in step.post_job_actions:
if pja.action_type in ActionBox.immediate_actions:
ActionBox.execute(trans.app, trans.sa_session, pja, job)
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -475,6 +475,63 @@ function Workflow( canvas_container ) {
wf.remove_node( v );
});
},
+ rectify_workflow_outputs : function() {
+ console.log("RECTIFICATION!");
+ // Find out if we're using workflow_outputs or not.
+ var using_workflow_outputs = false;
+ $.each( this.nodes, function ( k, node ) {
+ if (node.workflow_outputs && node.workflow_outputs.length > 0){
+ using_workflow_outputs = true;
+ }
+ });
+ if (using_workflow_outputs == false){
+ //We're done, leave PJAs alone.
+ return true;
+ }
+ wf = this;
+ $.each(this.nodes, function (k, node ){
+ if (node.type == 'tool'){
+ var node_changed = false;
+ if (node.post_job_actions == null){
+ console.log("CREATED FOR NEW NODE");
+ node.post_job_actions = {};
+ }
+ var pjas_to_rem = [];
+ $.each(node.post_job_actions, function(pja_id, pja){
+ if (pja.action_type == "HideDatasetAction"){
+ pjas_to_rem.push(pja_id);
+ }
+ });
+ if (pjas_to_rem.length > 0 && node == workflow.active_node)
+ $.each(pjas_to_rem, function(i, pja_name){
+ node_changed = true;
+ delete node.post_job_actions[pja_name];
+ })
+ $.each(node.output_terminals, function(ot_id, ot){
+ var create_pja = true;
+ $.each(node.workflow_outputs, function(i, wo_name){
+ if (ot.name == wo_name){
+ create_pja = false;
+ }
+ });
+ if (create_pja == true){
+ node_changed = true;
+ var pja = {
+ action_type : "HideDatasetAction",
+ output_name : ot.name,
+ action_arguments : {}
+ }
+ node.post_job_actions['HideDatasetAction'+ot.name] = null;
+ node.post_job_actions['HideDatasetAction'+ot.name] = pja;
+ }
+ });
+ // lastly, if this is the active node, and we made changes, reload the display at right.
+ if (wf.active_node == node && node_changed == true) {
+ wf.reload_active_node();
+ }
+ }
+ });
+ },
to_simple : function () {
var nodes = {};
$.each( this.nodes, function ( i, node ) {
@@ -491,7 +548,6 @@ function Workflow( canvas_container ) {
if (node.post_job_actions){
$.each( node.post_job_actions, function ( i, act ) {
var pja = {
- job_id : act.id,
action_type : act.action_type,
output_name : act.output_name,
action_arguments : act.action_arguments
@@ -559,6 +615,10 @@ function Workflow( canvas_container ) {
this.active_form_has_changes = false;
}
},
+ reload_active_node : function() {
+ this.clear_active_node();
+ this.activate_node(node);
+ },
clear_active_node : function() {
if ( this.active_node ) {
this.active_node.make_inactive();
--- a/templates/workflow/editor.mako
+++ b/templates/workflow/editor.mako
@@ -627,6 +627,7 @@
}
return;
}
+ workflow.rectify_workflow_outputs();
var savefn = function(callback) {
$.ajax( {
url: "${h.url_for( action='save_workflow' )}",
1
0

galaxy-dist commit 9415f380f2e7: Added validation checks in the data transfer script for sample tracking
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1288380270 14400
# Node ID 9415f380f2e7d4aca44f74923b6984ab3bcc1dfb
# Parent 0518ce52d8d3ca15ec6070f2addb4bfe7608c23b
Added validation checks in the data transfer script for sample tracking
--- a/scripts/galaxy_messaging/server/data_transfer.py
+++ b/scripts/galaxy_messaging/server/data_transfer.py
@@ -3,12 +3,12 @@
Data Transfer Script: Sequencer to Galaxy
-This script is called from Galaxy LIMS once the lab admin starts the data
-transfer process using the user interface.
+This script is called from Galaxy RabbitMQ listener ( amqp_consumer.py ) once
+the lab admin starts the data transfer process using the user interface.
Usage:
-python data_transfer.py <data_transfer_xml><config_id_secret>
+python data_transfer.py <config_file>
"""
@@ -19,13 +19,13 @@ import urllib,urllib2, cookielib, shutil
import logging, time, datetime
import xml.dom.minidom
-log = logging.getLogger("datatx_"+str(os.getpid()))
-log.setLevel(logging.DEBUG)
-fh = logging.FileHandler("data_transfer.log")
-fh.setLevel(logging.DEBUG)
-formatter = logging.Formatter("%(asctime)s - %(name)s - %(message)s")
-fh.setFormatter(formatter)
-log.addHandler(fh)
+log = logging.getLogger( "datatx_" + str( os.getpid() ) )
+log.setLevel( logging.DEBUG )
+fh = logging.FileHandler( "data_transfer.log" )
+fh.setLevel( logging.DEBUG )
+formatter = logging.Formatter( "%(asctime)s - %(name)s - %(message)s" )
+fh.setFormatter( formatter )
+log.addHandler( fh )
api_path = [ os.path.join( os.getcwd(), "scripts/api" ) ]
sys.path.extend( api_path )
@@ -47,60 +47,76 @@ import pexpect
pkg_resources.require( "simplejson" )
import simplejson
-class DataTransfer(object):
-
- def __init__(self, msg, config_file):
- log.info(msg)
- self.dom = xml.dom.minidom.parseString(msg)
- self.sequencer_host = self.get_value(self.dom, 'data_host')
- self.sequencer_username = self.get_value(self.dom, 'data_user')
- self.sequencer_password = self.get_value(self.dom, 'data_password')
- self.request_id = self.get_value(self.dom, 'request_id')
- self.sample_id = self.get_value(self.dom, 'sample_id')
- self.library_id = self.get_value(self.dom, 'library_id')
- self.folder_id = self.get_value(self.dom, 'folder_id')
+class DataTransfer( object ):
+
+ def __init__( self, msg, config_file ):
+ log.info( msg )
+ self.dom = xml.dom.minidom.parseString( msg )
+ self.sequencer_host = self.get_value( self.dom, 'data_host' )
+ self.sequencer_username = self.get_value( self.dom, 'data_user' )
+ self.sequencer_password = self.get_value( self.dom, 'data_password' )
+ self.request_id = self.get_value( self.dom, 'request_id' )
+ self.sample_id = self.get_value( self.dom, 'sample_id' )
+ self.library_id = self.get_value( self.dom, 'library_id' )
+ self.folder_id = self.get_value( self.dom, 'folder_id' )
self.dataset_files = []
count=0
while True:
- dataset_id = self.get_value_index(self.dom, 'dataset_id', count)
- file = self.get_value_index(self.dom, 'file', count)
- name = self.get_value_index(self.dom, 'name', count)
- if file:
- self.dataset_files.append(dict(name=name,
- dataset_id=int(dataset_id),
- file=file))
- else:
- break
- count=count+1
+ dataset_id = self.get_value_index( self.dom, 'dataset_id', count )
+ file = self.get_value_index( self.dom, 'file', count )
+ name = self.get_value_index( self.dom, 'name', count )
+ if file:
+ self.dataset_files.append( dict( name=name,
+ dataset_id=int( dataset_id ),
+ file=file ) )
+ else:
+ break
+ count=count+1
try:
# Retrieve the upload user login information from the config file
- transfer_datasets_config = ConfigParser.ConfigParser()
- transfer_datasets_config.read('transfer_datasets.ini')
- self.data_transfer_user_email = transfer_datasets_config.get("data_transfer_user_login_info", "email")
- self.data_transfer_user_password = transfer_datasets_config.get("data_transfer_user_login_info", "password")
- self.api_key = transfer_datasets_config.get("data_transfer_user_login_info", "api_key")
- self.http_server_section = transfer_datasets_config.get("universe_wsgi_config", "http_server_section")
- config = ConfigParser.ConfigParser()
- config.read( config_file )
- self.server_host = config.get(self.http_server_section, "host")
- self.server_port = config.get(self.http_server_section, "port")
- self.database_connection = config.get("app:main", "database_connection")
- self.import_dir = config.get("app:main", "library_import_dir")
- self.config_id_secret = config.get("app:main", "id_secret")
-
- # create the destination directory within the import directory
- self.server_dir = os.path.join( self.import_dir, 'datatx_'+str(os.getpid())+'_'+datetime.date.today().strftime("%d%b%Y") )
- os.mkdir(self.server_dir)
- if not os.path.exists(self.server_dir):
- raise Exception
+ transfer_datasets_config = ConfigParser.ConfigParser( )
+ transfer_datasets_config.read( 'transfer_datasets.ini' )
+ self.data_transfer_user_email = transfer_datasets_config.get( "data_transfer_user_login_info", "email" )
+ self.data_transfer_user_password = transfer_datasets_config.get( "data_transfer_user_login_info", "password" )
+ self.api_key = transfer_datasets_config.get( "data_transfer_user_login_info", "api_key" )
+ self.http_server_section = transfer_datasets_config.get( "universe_wsgi_config", "http_server_section" )
except:
- log.error(traceback.format_exc())
- log.error('FATAL ERROR')
- if self.database_connection:
- self.error_and_exit('Error')
+ log.error( traceback.format_exc() )
+ log.error( 'ERROR reading config values from transfer_datasets.ini.' )
sys.exit(1)
+ # read config variables
+ config = ConfigParser.ConfigParser()
+ retval = config.read( config_file )
+ if not retval:
+ error_msg = 'FATAL ERROR: Unable to open config file %s.' % config_file
+ log.error( error_msg )
+ sys.exit(1)
+ try:
+ self.server_host = config.get( self.http_server_section, "host" )
+ except ConfigParser.NoOptionError,e:
+ self.server_host = '127.0.0.1'
+ try:
+ self.server_port = config.get( self.http_server_section, "port" )
+ except ConfigParser.NoOptionError,e:
+ self.server_port = '8080'
+ try:
+ self.config_id_secret = config.get( "app:main", "id_secret" )
+ except ConfigParser.NoOptionError,e:
+ self.config_id_secret = "USING THE DEFAULT IS NOT SECURE!"
+ try:
+ self.import_dir = config.get( "app:main", "library_import_dir" )
+ except ConfigParser.NoOptionError,e:
+ log.error( 'ERROR: library_import_dir config variable is not set in %s. ' % config_file )
+ sys.exit( 1 )
+ # create the destination directory within the import directory
+ self.server_dir = os.path.join( self.import_dir,
+ 'datatx_' + str( os.getpid() ) + '_' + datetime.date.today( ).strftime( "%d%b%Y" ) )
+ try:
+ os.mkdir( self.server_dir )
+ except Exception, e:
+ self.error_and_exit( str( e ) )
- def start(self):
+ def start( self ):
'''
This method executes the file transfer from the sequencer, adds the dataset
to the data library & finally updates the data transfer status in the db
@@ -110,72 +126,73 @@ class DataTransfer(object):
# add the dataset to the given library
self.add_to_library()
# update the data transfer status in the db
- self.update_status(SampleDataset.transfer_status.COMPLETE)
+ self.update_status( SampleDataset.transfer_status.COMPLETE )
# cleanup
#self.cleanup()
- sys.exit(0)
+ sys.exit( 0 )
- def cleanup(self):
+ def cleanup( self ):
'''
remove the directory created to store the dataset files temporarily
before adding the same to the data library
'''
try:
- time.sleep(60)
+ time.sleep( 60 )
shutil.rmtree( self.server_dir )
except:
self.error_and_exit()
- def error_and_exit(self, msg=''):
+ def error_and_exit( self, msg='' ):
'''
This method is called any exception is raised. This prints the traceback
and terminates this script
'''
- log.error(traceback.format_exc())
- log.error('FATAL ERROR.'+msg)
- self.update_status('Error', 'All', msg+"\n"+traceback.format_exc())
- sys.exit(1)
+ log.error( traceback.format_exc() )
+ log.error( 'FATAL ERROR.' + msg )
+ self.update_status( 'Error', 'All', msg )
+ sys.exit( 1 )
- def transfer_files(self):
+ def transfer_files( self ):
'''
This method executes a scp process using pexpect library to transfer
the dataset file from the remote sequencer to the Galaxy server
'''
- def print_ticks(d):
+ def print_ticks( d ):
pass
- for i, df in enumerate(self.dataset_files):
- self.update_status(SampleDataset.transfer_status.TRANSFERRING, df['dataset_id'])
+ for i, dataset_file in enumerate( self.dataset_files ):
+ self.update_status( SampleDataset.transfer_status.TRANSFERRING, dataset_file[ 'dataset_id' ] )
try:
cmd = "scp %s@%s:'%s' '%s/%s'" % ( self.sequencer_username,
self.sequencer_host,
- df['file'].replace(' ', '\ '),
- self.server_dir.replace(' ', '\ '),
- df['name'].replace(' ', '\ ') )
- log.debug(cmd)
- output = pexpect.run(cmd, events={'.ssword:*': self.sequencer_password+'\r\n',
- pexpect.TIMEOUT:print_ticks},
- timeout=10)
- log.debug(output)
- path = os.path.join(self.server_dir, os.path.basename(df['name']))
- if not os.path.exists(path):
- msg = 'Could not find the local file after transfer (%s)' % path
- log.error(msg)
- raise Exception(msg)
+ dataset_file[ 'file' ].replace( ' ', '\ ' ),
+ self.server_dir.replace( ' ', '\ ' ),
+ dataset_file[ 'name' ].replace( ' ', '\ ' ) )
+ log.debug( cmd )
+ output = pexpect.run( cmd,
+ events={ '.ssword:*': self.sequencer_password+'\r\n',
+ pexpect.TIMEOUT: print_ticks },
+ timeout=10 )
+ log.debug( output )
+ path = os.path.join( self.server_dir, os.path.basename( dataset_file[ 'name' ] ) )
+ if not os.path.exists( path ):
+ msg = 'Could not find the local file after transfer ( %s ).' % path
+ log.error( msg )
+ raise Exception( msg )
except Exception, e:
msg = traceback.format_exc()
- self.update_status('Error', df['dataset_id'], msg)
+ self.update_status( 'Error', dataset_file['dataset_id'], msg)
- def add_to_library(self):
+ def add_to_library( self ):
'''
This method adds the dataset file to the target data library & folder
by opening the corresponding url in Galaxy server running.
'''
- self.update_status(SampleDataset.transfer_status.ADD_TO_LIBRARY)
+ self.update_status( SampleDataset.transfer_status.ADD_TO_LIBRARY )
try:
data = {}
- data[ 'folder_id' ] = api.encode_id( self.config_id_secret, '%s.%s' % ( 'folder', self.folder_id ) )
+ data[ 'folder_id' ] = api.encode_id( self.config_id_secret, '%s.%s' % ( 'folder', self.folder_id ) )
data[ 'file_type' ] = 'auto'
data[ 'server_dir' ] = self.server_dir
data[ 'dbkey' ] = ''
@@ -183,23 +200,23 @@ class DataTransfer(object):
data[ 'create_type' ] = 'file'
url = "http://%s:%s/api/libraries/%s/contents" % ( self.server_host,
self.server_port,
- api.encode_id( self.config_id_secret, self.library_id ) )
- log.debug( str( ( self.api_key, url, data ) ) )
+ api.encode_id( self.config_id_secret, self.library_id ) )
+ log.debug( str( ( self.api_key, url, data ) ) )
retval = api.submit( self.api_key, url, data, return_formatted=False )
- log.debug( str( retval ) )
+ log.debug( str( retval ) )
except Exception, e:
- self.error_and_exit( str( e ) )
+ self.error_and_exit( str( e ) )
- def update_status(self, status, dataset_id='All', msg=''):
+ def update_status( self, status, dataset_id='All', msg='' ):
'''
Update the data transfer status for this dataset in the database
'''
try:
- log.debug('Setting status "%s" for dataset "%s" of sample "%s"' % ( status, str(dataset_id), str(self.sample_id) ) )
+ log.debug( 'Setting status "%s" for dataset "%s" of sample "%s"' % ( status, str( dataset_id ), str( self.sample_id) ) )
sample_dataset_ids = []
if dataset_id == 'All':
for dataset in self.dataset_files:
- sample_dataset_ids.append( api.encode_id( self.config_id_secret, dataset['dataset_id'] ) )
+ sample_dataset_ids.append( api.encode_id( self.config_id_secret, dataset[ 'dataset_id' ] ) )
else:
sample_dataset_ids.append( api.encode_id( self.config_id_secret, dataset_id ) )
# update the transfer status
@@ -208,37 +225,37 @@ class DataTransfer(object):
data[ 'sample_dataset_ids' ] = sample_dataset_ids
data[ 'new_status' ] = status
data[ 'error_msg' ] = msg
- url = "http://%s:%s/api/requests/%s" % ( self.server_host,
+ url = "http://%s:%s/api/requests/%s" % ( self.server_host,
self.server_port,
- api.encode_id( self.config_id_secret, self.request_id ) )
- log.debug(str((self.api_key, url, data)))
+ api.encode_id( self.config_id_secret, self.request_id ) )
+ log.debug( str( ( self.api_key, url, data)))
retval = api.update( self.api_key, url, data, return_formatted=False )
log.debug( str( retval ) )
except urllib2.URLError, e:
- log.debug( 'ERROR(sample_dataset_transfer_status (%s)): %s' % ( url, str(e) ) )
- log.error(traceback.format_exc())
+ log.debug( 'ERROR( sample_dataset_transfer_status ( %s ) ): %s' % ( url, str( e ) ) )
+ log.error( traceback.format_exc() )
except:
- log.error(traceback.format_exc())
- log.error('FATAL ERROR')
- sys.exit(1)
+ log.error( traceback.format_exc() )
+ log.error( 'FATAL ERROR' )
+ sys.exit( 1 )
- def get_value(self, dom, tag_name):
+ def get_value( self, dom, tag_name ):
'''
This method extracts the tag value from the xml message
'''
- nodelist = dom.getElementsByTagName(tag_name)[0].childNodes
+ nodelist = dom.getElementsByTagName( tag_name )[ 0 ].childNodes
rc = ""
for node in nodelist:
if node.nodeType == node.TEXT_NODE:
rc = rc + node.data
return rc
- def get_value_index(self, dom, tag_name, dataset_id):
+ def get_value_index( self, dom, tag_name, dataset_id ):
'''
This method extracts the tag value from the xml message
'''
try:
- nodelist = dom.getElementsByTagName(tag_name)[dataset_id].childNodes
+ nodelist = dom.getElementsByTagName( tag_name )[ dataset_id ].childNodes
except:
return None
rc = ""
@@ -248,11 +265,11 @@ class DataTransfer(object):
return rc
if __name__ == '__main__':
- log.info('STARTING %i %s' % (os.getpid(), str(sys.argv)))
+ log.info( 'STARTING %i %s' % ( os.getpid(), str( sys.argv ) ) )
#
# Start the daemon
#
- dt = DataTransfer(sys.argv[1], sys.argv[2])
+ dt = DataTransfer( sys.argv[1], sys.argv[2])
dt.start()
- sys.exit(0)
+ sys.exit( 0 )
1
0
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1288377687 14400
# Node ID a22917c97fbe89aa2c0393d30f51d2a2183fd2f8
# Parent 9f74170b7781122577b8cf7ffe38f5c986617844
# Parent cbb895e2b2729d324c1693882de7939360e683a4
Merge.
--- a/test-data/cuffdiff_out5.tracking
+++ /dev/null
@@ -1,2 +0,0 @@
-tracking_id class_code nearest_ref_id gene_short_name tss_id locus q0_FPKM q0_conf_lo q0_conf_hi q1_FPKM q1_conf_lo q1_conf_hi
-TCONS_00000001 c Xkr4 - - chr1:3204754-3204833 0 0 0 67745 0 163551
1
0

galaxy-dist commit 2c934a168af9: More tweaking of lines_diff for cuffdiff test.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1288633591 14400
# Node ID 2c934a168af968dc84300412220c2f2cb74bf676
# Parent 9415f380f2e7d4aca44f74923b6984ab3bcc1dfb
More tweaking of lines_diff for cuffdiff test.
--- a/tools/ngs_rna/cuffdiff_wrapper.xml
+++ b/tools/ngs_rna/cuffdiff_wrapper.xml
@@ -78,11 +78,11 @@
TODO: can we find datasets that lead to deterministic behavior?
--><output name="isoforms_exp" file="cuffdiff_out1.txt" lines_diff="14"/>
- <output name="genes_exp" file="cuffdiff_out2.txt" lines_diff="4"/>
+ <output name="genes_exp" file="cuffdiff_out2.txt" lines_diff="10"/><output name="tss_groups_exp" file="cuffdiff_out3.txt"/><output name="cds_exp_fpkm_tracking" file="cuffdiff_out4.txt"/>
- <output name="isoforms_fpkm_tracking" file="cuffdiff_out5.txt" lines_diff="8"/>
- <output name="genes_fpkm_tracking" file="cuffdiff_out6.txt" lines_diff="4"/>
+ <output name="isoforms_fpkm_tracking" file="cuffdiff_out5.txt" lines_diff="16"/>
+ <output name="genes_fpkm_tracking" file="cuffdiff_out6.txt" lines_diff="8"/><output name="tss_groups_fpkm_tracking" file="cuffdiff_out7.txt"/><output name="cds_fpkm_tracking" file="cuffdiff_out8.txt"/><output name="splicing_diff" file="cuffdiff_out9.txt"/>
1
0

galaxy-dist commit 1efe19f6f3d1: Fixes for rerun action to recurse grouping options when checking unvalidated values and cloned HDAs. Better selection of corresponding HDAs from cloned histories, when multiple copies exist.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Daniel Blankenberg <dan(a)bx.psu.edu>
# Date 1288640846 14400
# Node ID 1efe19f6f3d1a75bda9c198633dd8102d3063410
# Parent 2c934a168af968dc84300412220c2f2cb74bf676
Fixes for rerun action to recurse grouping options when checking unvalidated values and cloned HDAs. Better selection of corresponding HDAs from cloned histories, when multiple copies exist.
--- a/lib/galaxy/web/controllers/tool_runner.py
+++ b/lib/galaxy/web/controllers/tool_runner.py
@@ -123,16 +123,6 @@ class ToolRunner( BaseController ):
params_objects = job.get_param_values( trans.app )
except:
raise Exception( "Failed to get paramemeters for dataset id %d " % data.id )
- # Unpack unvalidated values to strings, they'll be validated when the
- # form is submitted (this happens when re-running a job that was
- # initially run by a workflow)
- validated_params = {}
- for name, value in params_objects.items():
- if isinstance( value, UnvalidatedValue ):
- validated_params [ str(name) ] = str(value)
- else:
- validated_params [ str(name) ] = value
- params_objects = validated_params
# Need to remap dataset parameters. Job parameters point to original
# dataset used; parameter should be the analygous dataset in the
# current history.
@@ -141,12 +131,22 @@ class ToolRunner( BaseController ):
for hda in history.datasets:
source_hda = hda.copied_from_history_dataset_association
while source_hda:#should this check library datasets as well?
- hda_source_dict[ source_hda ] = hda
+ #FIXME: could be multiple copies of a hda in a single history, this does a better job of matching on cloned histories,
+ #but is still less than perfect when eg individual datasets are copied between histories
+ if source_hda not in hda_source_dict or source_hda.hid == hda.hid:
+ hda_source_dict[ source_hda ] = hda
source_hda = source_hda.copied_from_history_dataset_association
- for name, value in validated_params.items():
- if isinstance( value, trans.app.model.HistoryDatasetAssociation ):
- if value not in history.datasets:
- validated_params[ name ] = hda_source_dict[ value ]
+ # Unpack unvalidated values to strings, they'll be validated when the
+ # form is submitted (this happens when re-running a job that was
+ # initially run by a workflow)
+ #This needs to be done recursively through grouping parameters
+ def rerun_callback( input, value, prefixed_name, prefixed_label ):
+ if isinstance( value, UnvalidatedValue ):
+ return str( value )
+ if isinstance( input, DataToolParameter ):
+ if value not in history.datasets and value in hda_source_dict:
+ return hda_source_dict[ value ]
+ visit_input_values( tool.inputs, params_objects, rerun_callback )
# Create a fake tool_state for the tool, with the parameters values
state = tool.new_state( trans )
state.inputs = params_objects
1
0

galaxy-dist commit 0518ce52d8d3: Allow jobs to be stopped when using track_jobs_in_database/enable_job_running (multiprocess config).
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1288379862 14400
# Node ID 0518ce52d8d3ca15ec6070f2addb4bfe7608c23b
# Parent a22917c97fbe89aa2c0393d30f51d2a2183fd2f8
Allow jobs to be stopped when using track_jobs_in_database/enable_job_running (multiprocess config).
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -813,6 +813,8 @@ class JobStopQueue( object ):
self.sa_session = app.model.context
self.dispatcher = dispatcher
+ self.track_jobs_in_database = app.config.get_bool( 'track_jobs_in_database', False )
+
# Keep track of the pid that started the job manager, only it
# has valid threads
self.parent_pid = os.getpid()
@@ -848,21 +850,29 @@ class JobStopQueue( object ):
Called repeatedly by `monitor` to stop jobs.
"""
# Pull all new jobs from the queue at once
- jobs = []
- try:
- while 1:
- ( job_id, error_msg ) = self.queue.get_nowait()
- if job_id is self.STOP_SIGNAL:
- return
- # Append to watch queue
- jobs.append( ( job_id, error_msg ) )
- except Empty:
- pass
-
- for job_id, error_msg in jobs:
- job = self.sa_session.query( model.Job ).get( job_id )
- self.sa_session.refresh( job )
- # if desired, error the job so we can inform the user.
+ jobs_to_check = []
+ if self.track_jobs_in_database:
+ # Clear the session so we get fresh states for job and all datasets
+ self.sa_session.expunge_all()
+ # Fetch all new jobs
+ newly_deleted_jobs = self.sa_session.query( model.Job ) \
+ .options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ) \
+ .filter( model.Job.state == model.Job.states.DELETED_NEW ).all()
+ for job in newly_deleted_jobs:
+ jobs_to_check.append( ( job, None ) )
+ else:
+ try:
+ while 1:
+ message = self.queue.get_nowait()
+ if message is self.STOP_SIGNAL:
+ return
+ # Unpack the message
+ job_id, error_msg = message
+ # Get the job object and append to watch queue
+ jobs_to_check.append( ( self.sa_session.query( model.Job ).get( job_id ), error_msg ) )
+ except Empty:
+ pass
+ for job, error_msg in jobs_to_check:
if error_msg is not None:
job.state = job.states.ERROR
job.info = error_msg
@@ -870,9 +880,6 @@ class JobStopQueue( object ):
job.state = job.states.DELETED
self.sa_session.add( job )
self.sa_session.flush()
- # if job is in JobQueue or FooJobRunner's put method,
- # job_runner_name will be unset and the job will be dequeued due to
- # state change above
if job.job_runner_name is not None:
# tell the dispatcher to stop the job
self.dispatcher.stop( job )
@@ -888,7 +895,8 @@ class JobStopQueue( object ):
else:
log.info( "sending stop signal to worker thread" )
self.running = False
- self.queue.put( ( self.STOP_SIGNAL, None ) )
+ if not self.track_jobs_in_database:
+ self.queue.put( self.STOP_SIGNAL )
self.sleeper.wake()
log.info( "job stopper stopped" )
--- a/lib/galaxy/web/controllers/root.py
+++ b/lib/galaxy/web/controllers/root.py
@@ -432,7 +432,8 @@ class RootController( BaseController, Us
if job.state in [ self.app.model.Job.states.QUEUED, self.app.model.Job.states.RUNNING, self.app.model.Job.states.NEW ]:
# Are *all* of the job's other output datasets deleted?
if job.check_if_output_datasets_deleted():
- job.mark_deleted()
+ job.mark_deleted( self.app.config.get_bool( 'enable_job_running', True ),
+ self.app.config.get_bool( 'track_jobs_in_database', False ) )
self.app.job_manager.job_stop_queue.put( job.id )
trans.sa_session.flush()
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -97,7 +97,8 @@ class Job( object ):
RUNNING = 'running',
OK = 'ok',
ERROR = 'error',
- DELETED = 'deleted' )
+ DELETED = 'deleted',
+ DELETED_NEW = 'deleted_new' )
def __init__( self ):
self.session_id = None
self.user_id = None
@@ -152,11 +153,17 @@ class Job( object ):
if not dataset.deleted:
return False
return True
- def mark_deleted( self ):
+ def mark_deleted( self, enable_job_running=True, track_jobs_in_database=False ):
"""
Mark this job as deleted, and mark any output datasets as discarded.
"""
- self.state = Job.states.DELETED
+ # This could be handled with *just* track_jobs_in_database, but I
+ # didn't want to make setting track_jobs_in_database required in
+ # non-runner configs.
+ if not enable_job_running or track_jobs_in_database:
+ self.state = Job.states.DELETED_NEW
+ else:
+ self.state = Job.states.DELETED
self.info = "Job output deleted by user before job completed."
for dataset_assoc in self.output_datasets:
dataset = dataset_assoc.dataset
1
0

galaxy-dist commit 9f74170b7781: Fix for multiple hide dataset actions when using workflow outputs.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1288377653 14400
# Node ID 9f74170b7781122577b8cf7ffe38f5c986617844
# Parent a2e4d36c4318bc4ce73af9df869ae4071af33d55
Fix for multiple hide dataset actions when using workflow outputs.
--- a/lib/galaxy/web/controllers/workflow.py
+++ b/lib/galaxy/web/controllers/workflow.py
@@ -1299,6 +1299,11 @@ class WorkflowController( BaseController
step_outputs = [s.output_name for s in step.workflow_outputs]
for output in tool.outputs.keys():
if output not in step_outputs:
+ # Necessary, unfortunately, to clean up workflows that might have more than one at this point.
+ for pja in step.post_job_actions:
+ if pja.action_type == "HideDatasetAction" and pja.output_name == output:
+ step.post_job_actions.remove(pja)
+ trans.sa_session.delete(pja)
# Create a PJA for hiding this output.
n_pja = PostJobAction('HideDatasetAction', step, output, {})
else:
1
0

galaxy-dist commit 247c19db7f89: fix for the Lab access bug caused by the last commit .
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1288299278 14400
# Node ID 247c19db7f8987d9da245bc750043188d4a7c373
# Parent 522c99a05163c1816e9fe3be4c0b4ad05bff3ea0
fix for the Lab access bug caused by the last commit .
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -84,7 +84,7 @@
[ 'Find Samples', h.url_for( controller='/requests', action='find_samples_index' ) ],
[ 'Help', app.config.get( "lims_doc_url", "http://main.g2.bx.psu.edu/u/rkchak/p/sts" ), "galaxy_main" ]
]
- tab( "lab", "Lab", None, menu_options=menu_options, visible=( trans.user and ( trans.user.requests or trans.user.accessible_request_types() ) ) )
+ tab( "lab", "Lab", None, menu_options=menu_options, visible=( trans.user and ( trans.user.requests or trans.user.accessible_request_types( trans ) ) ) )
%>
## Visualization menu.
1
0

galaxy-dist commit 0b278c24c19f: Tophat wrapper improvements: (a) from JJ and Ry4an Brase @ UMN: wire up parameters from wrapper to tool and (b) improve path joining.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1288217658 14400
# Node ID 0b278c24c19f0a0f7a57b8a33bd6d992461c04c3
# Parent d57ea326563cb4539e47aae49daa6c5dfc69efc5
Tophat wrapper improvements: (a) from JJ and Ry4an Brase @ UMN: wire up parameters from wrapper to tool and (b) improve path joining.
--- a/tools/ngs_rna/tophat_wrapper.py
+++ b/tools/ngs_rna/tophat_wrapper.py
@@ -52,8 +52,6 @@ def __main__():
(options, args) = parser.parse_args()
- #sys.stderr.write('*'*50+'\n'+str(options)+'\n'+'*'*50+'\n')
-
# Creat bowtie index if necessary.
tmp_index_dir = tempfile.mkdtemp()
if options.own_file != 'None':
@@ -121,6 +119,14 @@ def __main__():
opts += ' --microexon-search'
if options.single_paired == 'paired':
opts += ' --mate-std-dev %s' % options.mate_std_dev
+ if options.seg_mismatches != None:
+ opts += ' --segment-mismatches %d' % int(options.seg_mismatches)
+ if options.seg_length != None:
+ opts += ' --segment-length %d' % int(options.seg_length)
+ if options.min_segment_intron != None:
+ opts += ' --min-segment-intron %d' % int(options.min_segment_intron)
+ if options.max_segment_intron != None:
+ opts += ' --max-segment-intron %d' % int(options.max_segment_intron)
cmd = cmd % ( tmp_output_dir, opts, index_path, reads )
except Exception, e:
# Clean up temp dirs
@@ -166,12 +172,12 @@ def __main__():
# Postprocessing: copy output files from tmp directory to specified files. Also need to remove header lines from SAM file.
try:
try:
- shutil.copyfile( tmp_output_dir + "/coverage.wig", options.coverage_output_file )
- shutil.copyfile( tmp_output_dir + "/junctions.bed", options.junctions_output_file )
+ shutil.copyfile( os.path.join( tmp_output_dir, "coverage.wig" ), options.coverage_output_file )
+ shutil.copyfile( os.path.join( tmp_output_dir, "junctions.bed" ), options.junctions_output_file )
# Remove headers from SAM file in place.
in_header = True # Headers always at start of file.
- for line in fileinput.input( tmp_output_dir + "/accepted_hits.sam", inplace=1 ):
+ for line in fileinput.input( os.path.join( tmp_output_dir, "accepted_hits.sam" ), inplace=1 ):
if in_header and line.startswith("@"):
continue
else:
@@ -179,7 +185,7 @@ def __main__():
sys.stdout.write( line )
# Copy SAM File.
- shutil.copyfile( tmp_output_dir + "/accepted_hits.sam", options.accepted_hits_output_file )
+ shutil.copyfile( os.path.join( tmp_output_dir, "accepted_hits.sam" ), options.accepted_hits_output_file )
except Exception, e:
stop_err( 'Error in tophat:\n' + str( e ) )
finally:
1
0

galaxy-dist commit a2e4d36c4318: Make fastx_barcode_splitter_galaxy_wrapper.sh invoke bash rather than sh.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1288189891 14400
# Node ID a2e4d36c4318bc4ce73af9df869ae4071af33d55
# Parent 0185bb614c7721d6edf6822ad973e589620b91f5
Make fastx_barcode_splitter_galaxy_wrapper.sh invoke bash rather than sh.
--- a/tools/fastx_toolkit/fastx_barcode_splitter_galaxy_wrapper.sh
+++ b/tools/fastx_toolkit/fastx_barcode_splitter_galaxy_wrapper.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
# FASTX-toolkit - FASTA/FASTQ preprocessing tools.
# Copyright (C) 2009 A. Gordon (gordon(a)cshl.edu)
1
0

galaxy-dist commit 23844e86e616: Fix error on all uploads < 4b. An exception was raised in the bigwig sniffer, which does a binary unpack.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1288376296 14400
# Node ID 23844e86e6167c79737a7e0396c39e350e95d1c4
# Parent 247c19db7f8987d9da245bc750043188d4a7c373
Fix error on all uploads < 4b. An exception was raised in the bigwig sniffer, which does a binary unpack.
--- a/lib/galaxy/datatypes/binary.py
+++ b/lib/galaxy/datatypes/binary.py
@@ -213,8 +213,11 @@ class BigWig(Binary):
def _unpack( self, pattern, handle ):
return struct.unpack( pattern, handle.read( struct.calcsize( pattern ) ) )
def sniff( self, filename ):
- magic = self._unpack( "I", open( filename ) )
- return magic[0] == self._magic
+ try:
+ magic = self._unpack( "I", open( filename ) )
+ return magic[0] == self._magic
+ except:
+ return False
def set_peek( self, dataset, is_multi_byte=False ):
if not dataset.dataset.purged:
dataset.peek = "Binary UCSC %s file" % self._name
1
0

galaxy-dist commit 6a97d4fc4268: Update test data for GFF-to-BED to accomodate change made in 4475:aaacde471f03.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1288198736 14400
# Node ID 6a97d4fc4268493480a0a3e7462e91f48d926a72
# Parent 121be69ca30675593dc9fc247a4b10a118fed227
Update test data for GFF-to-BED to accomodate change made in 4475:aaacde471f03.
--- a/test-data/gff2bed_out2.bed
+++ b/test-data/gff2bed_out2.bed
@@ -1,3 +1,3 @@
-chr1 3204562 3661579 Xkr4 0 - 3204562 3204562 0 3 2487,200,947 0,207220,456070
-chr1 4481008 4486494 Sox17 0 - 4481008 4481008 0 5 1741,367,92,807,123 0,2172,2844,4208,5363
-chr1 4763278 4775807 Mrpl15 0 - 4763278 4763278 0 5 3604,124,166,155,154 0,4327,9370,10753,12375
+chr1 3204562 3661579 Xkr4 0 - 3204562 3661579 0 3 2487,200,947 0,207220,456070
+chr1 4481008 4486494 Sox17 0 - 4481008 4486494 0 5 1741,367,92,807,123 0,2172,2844,4208,5363
+chr1 4763278 4775807 Mrpl15 0 - 4763278 4775807 0 5 3604,124,166,155,154 0,4327,9370,10753,12375
1
0

galaxy-dist commit 522c99a05163: fixed Lab menu access bug
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1288298593 14400
# Node ID 522c99a05163c1816e9fe3be4c0b4ad05bff3ea0
# Parent 7327a60e33968cb067edf6541cc84643f970180d
fixed Lab menu access bug
fixed rabbitmq setup script bug
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -84,7 +84,7 @@
[ 'Find Samples', h.url_for( controller='/requests', action='find_samples_index' ) ],
[ 'Help', app.config.get( "lims_doc_url", "http://main.g2.bx.psu.edu/u/rkchak/p/sts" ), "galaxy_main" ]
]
- tab( "lab", "Lab", None, menu_options=menu_options, visible=( trans.user and trans.user.requests ) )
+ tab( "lab", "Lab", None, menu_options=menu_options, visible=( trans.user and ( trans.user.requests or trans.user.accessible_request_types() ) ) )
%>
## Visualization menu.
--- a/scripts/galaxy_messaging/server/setup_rabbitmq.py
+++ b/scripts/galaxy_messaging/server/setup_rabbitmq.py
@@ -25,7 +25,7 @@ def main( config_file ):
cmd_list = [
'add_user %s %s' % ( username, password ),
'add_vhost %s' % config.get( 'galaxy_amqp', 'virtual_host' ),
- 'set_permissions -p %s %s "%s.*" ".*" ".*"' % ( virtual_host, username, username )
+ 'set_permissions -p %s %s ".*" ".*" ".*"' % ( virtual_host, username )
]
for cmd in cmd_list:
1
0

galaxy-dist commit d57ea326563c: More tweaking to try to get Cuffdiff to pass functional tests. The problem is that Cuffdiff's output is not deterministic, so lines_diff needs to be large enough (but no larger than needed) to accomodate small differences in output.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1288216747 14400
# Node ID d57ea326563cb4539e47aae49daa6c5dfc69efc5
# Parent 6a97d4fc4268493480a0a3e7462e91f48d926a72
More tweaking to try to get Cuffdiff to pass functional tests. The problem is that Cuffdiff's output is not deterministic, so lines_diff needs to be large enough (but no larger than needed) to accomodate small differences in output.
--- a/tools/ngs_rna/cuffdiff_wrapper.xml
+++ b/tools/ngs_rna/cuffdiff_wrapper.xml
@@ -77,7 +77,7 @@
Line diffs are needed because cuffdiff does not produce deterministic output.
TODO: can we find datasets that lead to deterministic behavior?
-->
- <output name="isoforms_exp" file="cuffdiff_out1.txt" lines_diff="8"/>
+ <output name="isoforms_exp" file="cuffdiff_out1.txt" lines_diff="14"/><output name="genes_exp" file="cuffdiff_out2.txt" lines_diff="4"/><output name="tss_groups_exp" file="cuffdiff_out3.txt"/><output name="cds_exp_fpkm_tracking" file="cuffdiff_out4.txt"/>
1
0

galaxy-dist commit 243d80d9905a: adding setup_rabbitmq.sh script.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1288197416 14400
# Node ID 243d80d9905af9121452f1a0955b2b49c076d66e
# Parent 9c6040ab75a8397a5708cfec4edfe216927b8220
adding setup_rabbitmq.sh script.
web api minor code fixes
--- a/scripts/api/common.py
+++ b/scripts/api/common.py
@@ -91,7 +91,10 @@ def submit( api_key, url, data, return_f
except urllib2.HTTPError, e:
print e
print e.read( 1024 )
- sys.exit( 1 )
+ if return_formatted:
+ sys.exit( 1 )
+ else:
+ return 'Error. '+ str( e )
if not return_formatted:
return r
print 'Response'
--- /dev/null
+++ b/setup_rabbitmq.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd `dirname $0`
+python scripts/galaxy_messaging/server/setup_rabbitmq.py universe_wsgi.ini
--- a/scripts/galaxy_messaging/server/data_transfer.py
+++ b/scripts/galaxy_messaging/server/data_transfer.py
@@ -184,10 +184,11 @@ class DataTransfer(object):
url = "http://%s:%s/api/libraries/%s/contents" % ( self.server_host,
self.server_port,
api.encode_id( self.config_id_secret, self.library_id ) )
- log.debug(str((self.api_key, url, data)))
+ log.debug( str( ( self.api_key, url, data ) ) )
retval = api.submit( self.api_key, url, data, return_formatted=False )
- except:
- self.error_and_exit(str(e))
+ log.debug( str( retval ) )
+ except Exception, e:
+ self.error_and_exit( str( e ) )
def update_status(self, status, dataset_id='All', msg=''):
'''
@@ -212,7 +213,7 @@ class DataTransfer(object):
api.encode_id( self.config_id_secret, self.request_id ) )
log.debug(str((self.api_key, url, data)))
retval = api.update( self.api_key, url, data, return_formatted=False )
- log.debug('done.')
+ log.debug( str( retval ) )
except urllib2.URLError, e:
log.debug( 'ERROR(sample_dataset_transfer_status (%s)): %s' % ( url, str(e) ) )
log.error(traceback.format_exc())
--- a/scripts/galaxy_messaging/server/setup_rabbitmq.py
+++ b/scripts/galaxy_messaging/server/setup_rabbitmq.py
@@ -11,13 +11,17 @@
import os, sys, csv, ConfigParser
def main( config_file ):
- config = ConfigParser.ConfigParser()
- config.read( config_file )
- rabbitmqctl_path = config.get( 'galaxy_amqp', 'rabbitmqctl_path' )
- username = config.get( 'galaxy_amqp', 'userid' )
- password = config.get( 'galaxy_amqp', 'password' )
- virtual_host = config.get( 'galaxy_amqp', 'virtual_host' )
-
+ try:
+ config = ConfigParser.ConfigParser()
+ config.read( config_file )
+ rabbitmqctl_path = config.get( 'galaxy_amqp', 'rabbitmqctl_path' )
+ username = config.get( 'galaxy_amqp', 'userid' )
+ password = config.get( 'galaxy_amqp', 'password' )
+ virtual_host = config.get( 'galaxy_amqp', 'virtual_host' )
+ except Exception, e:
+ print 'Fatal error:', str(e)
+ sys.exit(1)
+
cmd_list = [
'add_user %s %s' % ( username, password ),
'add_vhost %s' % config.get( 'galaxy_amqp', 'virtual_host' ),
1
0

galaxy-dist commit 7327a60e3396: pbs_deljob bugfix from Glen Beane.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1288287803 14400
# Node ID 7327a60e33968cb067edf6541cc84643f970180d
# Parent 0b278c24c19f0a0f7a57b8a33bd6d992461c04c3
pbs_deljob bugfix from Glen Beane.
--- a/lib/galaxy/jobs/runners/pbs.py
+++ b/lib/galaxy/jobs/runners/pbs.py
@@ -563,7 +563,7 @@ class PBSJobRunner( object ):
if c <= 0:
log.debug("(%s/%s) Connection to PBS server for job delete failed" % ( job.id, job.job_runner_external_id ) )
return
- pbs.pbs_deljob( c, str( job.job_runner_external_id ), 'NULL' )
+ pbs.pbs_deljob( c, str( job.job_runner_external_id ), '' )
pbs.pbs_disconnect( c )
log.debug( "(%s/%s) Removed from PBS queue before job completion" % ( job.id, job.job_runner_external_id ) )
1
0