/****************************************************************************
 * Copyright (c) 1998-2007 Luna Imaging, Inc.  All Rights Reserved.
 *
 * This software is confidential and proprietary information of
 * Luna Imaging, Inc.  ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Luna Imaging, Inc.
 *
 * The software may not be copied, reproduced, translated or reduced to
 * any electronic medium or machine-readable form without
 * the prior written consent of Luna Imaging.
 *
 * You are not allowed to distribute the binary and source code
 * (if released) to third parties, without the prior written consent from
 * Luna Imaging.
 *
 * You are not allowed to reverse engineer, disassemble or decompile
 * code, or make any modifications of the binary or source code, remove
 * or alter any trademark, logo, copyright or other proprietary notices,
 * legends, symbols, or labels in the Software.
 *
 * You are not allowed to sub-license the Software or any derivative
 * work based on or derived from the Software.
 *
 * LUNA IMAGING MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
 * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT
 * NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
 * A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, LUNA IMAGING SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, 
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 * 
 *   cruiz10020@yahoo.com
 *
 *   CollapsibleContainer.js
 *
 *   Description:
 *     Dynamically renders a collapisble container.
 *
 *   Structure:
 *
 *     
 *
 *   Requires:
 *     prototype.js - http://www.prototypejs.org/
 *
 *   Development History:
 *     2-12-2007       - created
 *
 *******************************************************************************/

function CollapsibleContainer( title, options )
{
  //
  // Member variables
  //
  this.mTitle = title;
  this.mContainer = null;
  this.mHeader = null;
  this.mBody = null;
  this.mFooter = null;
  this.mCollapseButton = null;
  this.mMiddleViewButton = null;
  this.mExpandButton = null;
  this.mAnimating = false;
  this.mOptions = options;
  this.mPostToggleFunction;
  this.mState = '1'; // states can be 0:closed; 1:open; 2:extended
  
  this.mOldOverflowValue = null;
  this.mOldHeightValue = null;
  this.mOldWidthValue = null;
  
  //
  // Constants
  //
  this.HEADER_CLASSNAME = 'header';
  this.BODY_CLASSNAME = 'body';
  this.FOOTER_CLASSNAME = 'footer';
  this.COLLAPSED_CLASSNAME = 'collapsed';
  this.TABLE_CLASSNAME = 'mediaInfoCollapsed';
  this.FULLY_EXPANDED_CLASSNAME = 'fullExpanded';
  this.BUTTON_COLLAPSED = 'collapseButton';
  this.BUTTON_MIDDLE_STATE = 'middleStateButton';
  this.BUTTON_EXPANDED = 'expandedButton';
  this.ANIMATION_GRAIN = 15;
  this.ANIMATION_PERCENTAGE_GRAIN = 0.15;
  this.ANIMATION_SLEEP = 50; //milliseconds 10ms
  
  this.DEFAULT_CLASS_NAME = 'collapsibleContainer';
  this.DEFAULT_BODY_CONTENT = 'Loading...'
  
  this.OPTION_CLASS_NAME = 'className';
  this.OPTION_INITIAL_BODY_CONTENT = 'initialBodyContent';
  this.OPTION_NO_ANIMATE_COLLAPSE = 'noAnimateCollapse';
  this.OPTION_COLLAPSE_VERTICALLY = 'collapseVertically';
  this.OPTION_OVERFLOW_EXPAND = 'overflowExpand';
  this.OPTION_INITIAL_STATE = 'initialState';
  this.OPTION_MAX_STATES = 'maxStates';
  this.OPTION_NO_INFO = 'noInfo';
  this.COLLAPSED_WIDTH = 17;
  
  //
  // Map Methods
  //
  this.init = CollapsibleContainer_Init;
  this.render = CollapsibleContainer_Render;
  this.associate = CollapsibleContainer_Associate;
  this.wrap = CollapsibleContainer_Wrap;
  this.toggleState = CollapsibleContainer_ToggleState;
  this.collapse = CollapsibleContainer_Collapse;
  this.isCollapsed = CollapsibleContainer_IsCollapsed;
  this.isVerticalCollapse = CollapsibleContainer_IsVerticalCollapse;
  this.animateCollapse = CollapsibleContainer_AnimateCollapse;
  this.animateHorizontalCollapse = CollapsibleContainer_AnimateHorizontalCollapse;
  this.animateVerticalCollapse = CollapsibleContainer_AnimateVerticalCollapse;  
  this.addContent = CollapsibleContainer_AddContent;
  this.append = CollapsibleContainer_Append;
  this.insert = CollapsibleContainer_Insert;
  this.set = CollapsibleContainer_Set;
  this.clearContent = CollapsibleContainer_ClearContent;
}


/**
 *
 *  Initialize the Collapsible Container
 *  @param createElements if true the struture is created before initializing
 *
 */
function CollapsibleContainer_Init( createElements )
{
  // create structure only if told to.
  if( createElements )
  {
    // there should a container already set, create it if not
    if( ! this.mContainer )
      this.mContainer = document.createElement( 'div' );

    if( ! this.mHeader )
      this.mHeader = document.createElement( 'div' );
    
    if( ! this.mBody )
      this.mBody = document.createElement( 'div' );

    // href javascript is just so it doesn't change url to #
    var collapseButton = '<a href="javascript:var collapseOrExpand;" title="Click to Expand or Collapse">&nbsp;</a>';

    // create header content
    this.mHeader.innerHTML = '<table cellpadding="0" cellspacing="0" border="0" width="100%"><tr><td><h1>' + this.mTitle + '</h1><  d><td align="right">' + collapseButton + '<  d><  r><  able>';

    // default body content, if none already set
    if( ! this.mBody.innerHTML )
      this.mBody.innerHTML = ( ( this.mOptions[this.OPTION_INITIAL_BODY_CONTENT] == null )? this.DEFAULT_BODY_CONTENT :this.mOptions[this.OPTION_INITIAL_BODY_CONTENT] );

    // add sections to main container
    this.mContainer.appendChild( this.mHeader );
    this.mContainer.appendChild( this.mBody );  
  }
  // grab the initial state
  this.mState = this.mOptions[this.OPTION_INITIAL_STATE];
  
  // set className
  this.mContainer.className = ( ( this.mOptions[this.OPTION_CLASS_NAME] == null )? this.DEFAULT_CLASS_NAME  :this.mOptions[this.OPTION_CLASS_NAME] );
  this.mHeader.className = this.HEADER_CLASSNAME;
  this.mBody.className = this.BODY_CLASSNAME;
  
  // add classes if collapsed state
  if( this.mState == '0' )
  {
    $(this.mContainer).addClassName( 'containerCollapsed' );
    $(this.mHeader).addClassName( 'headerCollapsed' );
    $(this.mBody).addClassName( 'bodyCollapsed' );
    $(this.mFooter).addClassName( 'footerCollapsed' );
    
    if( this.mOptions[this.OPTION_MAX_STATES] == '2' )
    {
    	$(this.mContainer).addClassName( 'twoStateContainerCollapsed' );
      $(this.mHeader).addClassName( 'headerCollapseTwoState' );
    }
  }
  
  // grab the collapse button  
  if( this.mCollapseButton = $(this.mHeader).getElementsBySelector('td')[1] )
  {
    this.mCollapseButton.collapsibleContainer = this;  
    this.mCollapseButton.onclick = Static_CollapsibleContainer_Toggle;
  }
  if( this.mMiddleViewButton = $(this.mHeader).getElementsBySelector('td')[2] )
  {
    this.mMiddleViewButton.collapsibleContainer = this; 
    this.mMiddleViewButton.onclick = Static_CollapsibleContainer_Toggle;
  }
  if( this.mExpandButton = $(this.mHeader).getElementsBySelector('td')[3] )
  {
    this.mExpandButton.collapsibleContainer = this; 
    this.mExpandButton.onclick = Static_CollapsibleContainer_Toggle;
  }
  
  // give elements a reference to this object for later use
  //  this.mCollapseButton.collapsibleContainer = this;  
  //  this.mMiddleViewButton.collapsibleContainer = this;  
  //  this.mExpandButton.collapsibleContainer = this;  
  this.mHeader.collapsibleContainer = this;
  this.mBody.collapsibleContainer = this;
  this.mContainer.collapsibleContainer = this;
   
  // set the listeners for collapsing
  //  this.mCollapseButton.onclick = Static_CollapsibleContainer_Toggle;
  //  this.mMiddleViewButton.onclick = Static_CollapsibleContainer_Toggle;
  //  this.mExpandButton.onclick = Static_CollapsibleContainer_Toggle;
  
  // use the initial state to determine if it should be open/close/etc
  // check for 2 states and 3 states
  if( this.mOptions[this.OPTION_MAX_STATES] == '2' )
  {
    switch( this.mOptions[this.OPTION_INITIAL_STATE] )
    {
      case '0':
        this.collapse( true, this.mOptions[this.OPTION_INITIAL_STATE] );
        break;
      case '1':
        this.collapse( false, this.mOptions[this.OPTION_INITIAL_STATE] );
        break;
      default:
        this.collapse( false, this.mOptions[this.OPTION_INITIAL_STATE] );
        break;
    }
  }
  else if( this.mOptions[this.OPTION_MAX_STATES] == '3' )
  {
    switch( this.mOptions[this.OPTION_INITIAL_STATE] )
    {
      case '0':
        this.collapse( true, this.mOptions[this.OPTION_INITIAL_STATE] );
        break;
      case '1':
        this.collapse( false, this.mOptions[this.OPTION_INITIAL_STATE] );
        break;
      case '2':
        this.collapse( false, this.mOptions[this.OPTION_INITIAL_STATE] );
        break;
      default:
        this.collapse( false, this.mOptions[this.OPTION_INITIAL_STATE] );
        break;
    }
  }
}

/**
 *  Renders the collapisble container using the given element as the container to collapse.
 *  Does nothing if no element is provided
 */
function CollapsibleContainer_Render( element )
{
  // render if an element is provided
  if( element )
  {
    this.mContainer = element;
    this.init( true )
  }
}

/**  
 *  Alternative to rendering.  
 *  Assumes the appropriate structure has already been rendered with the header and body defined.
 */
function CollapsibleContainer_Associate( element )
{
  if( element )
  {
    // already associated, only assosiate once.
    if( ! ( element.mCollapseButton && element.mCollapseButton.collapsibleContainer ) )
    {
      this.mContainer = element;
      this.mHeader = $(this.mContainer).getElementsBySelector('.' + this.HEADER_CLASSNAME )[0];
      this.mBody = $(this.mContainer).getElementsBySelector('.' + this.BODY_CLASSNAME )[0];
      this.mFooter = $(this.mContainer).getElementsBySelector('.' + this.FOOTER_CLASSNAME )[0];
      
      this.init( false );
    }
  }
}


/**
 *  Alternative to rendering which allows wrapping the given element with the collapsible container 
 *  turning the given element into the body of the CC.
 *  Assumes the given element is the body and from there a container div is created and append to the parent of the given body.
 * 
 */
function CollapsibleContainer_Wrap( body )
{
  if( body && ( ! body.collapsibleContainer ) )
  {
    body = $( body );
    var parent = $( body.up() );
    
    // create a div and add it to the parent of the given body
    this.mContainer = document.createElement( 'div' );
    this.mBody = body;
    parent.appendChild( this.mContainer );
    parent.removeChild( body );
    
    this.init( true );
  }
}

/**
 * Clears the contents of the body
 */
function CollapsibleContainer_ClearContent()
{
  this.mBody.innerHTML = '';
}

/**
 * Apends the given content to the end of the body
 */
function CollapsibleContainer_Append( content )
{
  this.addContent( content, false );
}

/**
 * Inserts the given content at top of the body
 */
function CollapsibleContainer_Insert( content )
{
  this.addContent( content, true );
}

/**
 * Clears the current contents of the body then appends the given content
 */
function CollapsibleContainer_Set( content )
{
  if( content )
  {
    this.clearContent();
    this.addContent( content, false );
  }
}

/**
 *  Either inserts or append the given content to the body of the CC.
 *  @param content eiter html element object to string to be added
 *  @param insert if true the content will be inserted at the begining otherwise it is append to the end
 */
function CollapsibleContainer_AddContent( content, insert )
{
  if( content )
  {
    if( content.innerHTML )
    {
      // we are looking at an html element object
      if( insert && ( this.mBody.childNodes.length > 0 ) )
      {
        // insert at the begining
        this.mBody.insertBefore( content, this.mBody.childNodes[0] );
      }
      else
      {
        //append to the end
        this.mBody.appendChild( content );
      }
    }
    else
    {
      // looking at some text
      if( insert )
      {
        this.mBody.innerHTML = content + this.mBody.innerHTML;
      }
      else
      {
        this.mBody.innerHTML += content;
      }
    }
  }
}

/**
 *  Returns true if the container is currently collapsed otherwise false.
 */
function CollapsibleContainer_IsCollapsed()
{
  return ( ( this.mOldOverflowValue == null ) && ( this.mOldWidthValue == null ) && ( this.mOldHeightValue == null )  );
}

function CollapsibleContainer_IsVerticalCollapse()
{
  return this.mOptions[ this.OPTION_COLLAPSE_VERTICALLY ];
}

/**
 *  Toggle state from collapse to expand or vise versa.
 */
function CollapsibleContainer_ToggleState( toggleState )
{
  // Assuming sequential ordering, but if that changes, here's the place to change the order
  this.mState = toggleState;
  
  this.collapse( this.isCollapsed() );
}

/**
 *  Collapse or Expands the container
 *  @param collapse if true the container is collapse otherwise it is expanded
 */
function CollapsibleContainer_Collapse( collapse )
{  
  this.mHeader.blur();

  //debug( 'CollapsibleContainer_Collapse:: ' + collapse);
  this.yOrginalContainer = $(this.mContainer).getHeight();

  // subtracting off the px amount of the top and bottom borders from the orginal y height
  if(jshIsNonEmptyTrim($(this.mContainer).getStyle('border-top-width')))
  {
     this.yOrginalContainer -=  parseInt($(this.mContainer).getStyle('border-top-width').indexOf('p'));
  }
  if(jshIsNonEmptyTrim($(this.mContainer).getStyle('border-bottom-width')))
  {
     this.yOrginalContainer -=  parseInt($(this.mContainer).getStyle('border-bottom-width').indexOf('p'));
  }

  if( this.mState == '0' )
  {
    // backup values
    this.mOldOverflowValue = this.mContainer.style.overflow;
    this.mOldHeightValue = this.mContainer.style.height;
    this.mOldWidthValue = this.mContainer.style.width;
    
    // collapse the container
    this.mContainer.style.overflow = "hidden";
    
    // add single click expand to hearder
    this.mHeader.onclick = Static_CollapsibleContainer_Toggle;
    
    // animation? or no animation?
    if( this.mOptions[ this.OPTION_NO_ANIMATE_COLLAPSE ] )
    {
      // vertical or horizontal?
      if( this.isVerticalCollapse() )
      {              
        // resize stuff, a bit more complex than horizontal
        this.mContainer.style.width = '32px';
        this.mHeader.style.margin = '0 0 0 -119px';
        
        this.mHeader.style.height = (this.yOrginalContainer)  + 'px';
        this.mContainer.style.height = (this.yOrginalContainer)  + 'px';
        
        this.mBody.style.width = '1px';
      }
      else
      {
        this.mContainer.style.height = $(this.mHeader).getHeight() + 'px';
      }
      
      // finally, set the collapse state to the parent of the button for non animation
      $(this.mHeader).addClassName(this.COLLAPSED_CLASSNAME);
      
      if( this.mOptions[this.OPTION_MAX_STATES] == '2' )
      {
      	$(this.mContainer).addClassName('twoStateContainerCollapsed');
      	$(this.mHeader).addClassName('headerCollapseTwoState');
      }
      
      // show the container
      $(this.mContainer).show();
      
      // hide/show the necessary button
      if( this.mMiddleViewButton != null )
        $(this.mMiddleViewButton).show();
      if( this.mCollapseButton != null )
        $(this.mCollapseButton).hide();
      if( this.mExpandButton != null )
        $(this.mExpandButton).show();
      
    }
    else
    {
      // collapse state set after animation is complete. from within recursive method
      this.animateCollapse();
    }
  }
  // if the state is 1:open
  else if( this.mState == '1' )
  { 
    // remove the classes
    $(this.mContainer).removeClassName( 'containerCollapsed' );
    $(this.mHeader).removeClassName( 'headerCollapsed' );
    $(this.mHeader).removeClassName(this.FULLY_EXPANDED_CLASSNAME);
    $(this.mBody).removeClassName( 'bodyCollapsed' );
    $(this.mFooter).removeClassName( 'footerCollapsed' );
    
    if( this.mOptions[this.OPTION_MAX_STATES] == '2' )
    {
      $(this.mContainer).removeClassName('twoStateContainerCollapsed');
      $(this.mHeader).removeClassName('headerCollapseTwoState');
    }
      
    // show the container
    $(this.mContainer).show();
    
    // hide/show the necessary button
    if( this.mMiddleViewButton != null )
      $(this.mMiddleViewButton).hide();
    if( this.mCollapseButton != null )
      $(this.mCollapseButton).show();
    if( this.mExpandButton != null )
      $(this.mExpandButton).show();
  
    // backup values
    this.mOldOverflowValue = this.mContainer.style.overflow;
    this.mOldHeightValue = this.mContainer.style.height;
    this.mOldWidthValue = this.mContainer.style.width;
    this.mHeader.className = this.HEADER_CLASSNAME;

    // remove single click expand
    this.mHeader.onclick = null;
    
    if( this.isVerticalCollapse() )
    {
      this.mContainer.style.overflow = this.mOldOverflowValue;
      this.mContainer.style.overflow = 'visible';      
      this.mBody.style.display = '';
      this.mBody.style.visibility= 'visible'; 
      
      // hard coded
      this.mHeader.style.height = '20px';
      this.mHeader.style.width = '157px';
      this.mHeader.style.margin = '0 0 0 0';
      
      // change the width of the meta-data table valueValue cells' <div>
      /*var divsToChange = new Array();
      divsToChange = $(this.mContainer).getElementsByClassName('valueValue');
      for( var i = 0; i < divsToChange.size(); i++ )
      {
        divsToChange[i].style.width = '100%';
      }
      */

      this.mBody.style.width = '';

      // grab the larger of the values - header vs body (FF vs IE)
      if( $(this.mBody).getWidth() > 157 )
      {
        this.mContainer.style.width = '157px';
        this.mHeader.style.width = '157px';
      }
      else
      {
        this.mContainer.style.width = '157px';
        this.mHeader.style.width = '157px';
      }
      
      this.mContainer.style.height = '100%';
    }
    else
    {
      this.mContainer.style.height = this.mOldHeightValue;
      this.mContainer.style.width = this.mOldWidthValue;
    }
    
    // restore to expand button
    if( this.mCollapseButton != null )
      this.mCollapseButton.className = '';
    
    // clear them
    this.mOldOverflowValue = null;
    this.mOldHeightValue = null;
    this.mOldWidthValue = null;
  }
  // the state is 2:expanded
  else if( this.mState == '2' )
  {
  	// reset the state of '1' if the user initially goes from '0' to '2'
    if( this.isVerticalCollapse() )
    {           
      this.mContainer.style.height = '100%';
      this.mBody.style.width = '';
    }
    else
    {
      this.mContainer.style.height = this.mOldHeightValue;
      this.mContainer.style.width = this.mOldWidthValue;
    }
    
    // remove the classes
    $(this.mContainer).removeClassName( 'containerCollapsed' );
    $(this.mHeader).removeClassName( 'headerCollapsed' );
    $(this.mBody).removeClassName( 'bodyCollapsed' );
    $(this.mFooter).removeClassName( 'footerCollapsed' );
    
    // show the container
    $(this.mContainer).show();
      
    // hide/show the necessary button
    
    if( this.mMiddleViewButton != null )
      $(this.mMiddleViewButton).show();
    if( this.mCollapseButton != null )
      $(this.mCollapseButton).show();
    if( this.mExpandButton != null )
      $(this.mExpandButton).hide();  
  
    // backup values
    this.mOldOverflowValue = this.mContainer.style.overflow;
    this.mOldHeightValue = this.mContainer.style.height;
    this.mOldWidthValue = this.mContainer.style.width;

    this.mHeader.className = this.HEADER_CLASSNAME;
    $(this.mHeader).addClassName(this.FULLY_EXPANDED_CLASSNAME);
    
    this.mContainer.style.overflow = this.mOldOverflowValue;
    this.mBody.style.display = '';
    this.mBody.style.visibility= 'visible'; 
    
    // get the width of the parent element
    // there's 2 tables, the 7th element in the array is the available space <table>
    var ancestor = new Array();
    ancestor = $(this.mContainer).ancestors();
    var availableSpace = '350';
    if( ancestor[7] != null )
      availableSpace = ancestor[7].getWidth();
      
      
    //alert( '111: ' + ancestor[7].tagName + ' :: ' + availableSpace );
    //alert( this.mContainer.id );

    //this.mBody.style.width = (availableSpace / 2) + 'px';
    this.mBody.style.width = '100%';

    // remove single click expand
    this.mHeader.onclick = null;
    
    if( this.isVerticalCollapse() )
    {
      // back up the height
      this.yStarting = $(this.mHeader).getHeight() + 'px';
      this.mContainer.style.width = this.mOldWidthValue;
      this.mContainer.style.overflow = 'visible';
      
      // hard coded
      if( this.mOptions[ this.OPTION_NO_INFO ] == 'true' )
      {
        this.mContainer.style.width = '200px';
        this.mHeader.style.height = '20px';
        this.mHeader.style.width = '200px';
      }
      else
      { 
        this.mContainer.style.width = ( (availableSpace / 2) - 15 ) + 'px';
        this.mHeader.style.height = '20px';
        this.mHeader.style.width = '100%';
      }
      
      // change the width of the meta-data table cells' <div>
      var divTitle = new Array();
      divTitle = $(this.mContainer).getElementsByClassName('valueFieldDisplayName');
      var divsToChange = new Array();
      divsToChange = $(this.mContainer).getElementsByClassName('valueValue');
      for( var i = 0; i < divsToChange.size(); i++ )
      {
        // get half the available size, subtract the space for the title, subtract 3 to sync the right border up
        divsToChange[i].style.width = ( (availableSpace / 2) - $(divTitle[0]).getWidth() - 3 ) + 'px';
      }
      
      this.mContainer.style.width = ( (availableSpace / 2) - 15 ) + 'px';
      this.mHeader.style.margin = '0 0 0 0';   
      this.mContainer.style.height = '100%';
      this.mBody.style.width = '100%';
    }
    else
    {
      this.mContainer.style.height = this.mOldHeightValue;
      this.mContainer.style.width = this.mOldWidthValue;
    }
    
    // restore to expand button
    this.mCollapseButton.className = '';
    
    // clear them
    this.mOldOverflowValue = null;
    this.mOldHeightValue = null;
    this.mOldWidthValue = null;
  }
}


/**
 *  Static Call to collapse the container.  
 *  Assumes source element defined by the event has a reference to the collapsibleContainer.
 *  If no collapsibleContainer is found, nothing is done.
 */
function Static_CollapsibleContainer_Toggle( e )
{
  //debug('Static_CollapsibleContainer_Toggle::');
  var sourceElement = jshGetSourceElement( e );
  
  // keep going up until we find container reference or we go all the way up
  while( ( ! sourceElement ) || (! sourceElement.collapsibleContainer ) )
  {
    sourceElement = $(sourceElement).up();
  }
  
  // try to toggle
  if( sourceElement && sourceElement.collapsibleContainer )
  {  
    // stop propegation of the event
    Event.stop( jshGetEvent( e ) );
    
    // toggle if not animating
    if( ! sourceElement.collapsibleContainer.mAnimating )
    {
      var toggleState = 1;
      var childElement = $(sourceElement).childElements();
      
      if( childElement[0].id == sourceElement.collapsibleContainer.BUTTON_COLLAPSED )
        toggleState = 0;
      if( childElement[0].id == sourceElement.collapsibleContainer.BUTTON_MIDDLE_STATE )
        toggleState = 1;
      if( childElement[0].id == sourceElement.collapsibleContainer.BUTTON_EXPANDED )
        toggleState = 2;
        
      // set the collapse state to the next state
      var sUrl = setCollapseStateUrl;
      sUrl = jshAppendParameter( sUrl, 'ccType', sourceElement.collapsibleContainer.mOptions[sourceElement.collapsibleContainer.OPTION_MAX_STATES], true );
      sUrl = jshAppendParameter( sUrl, 'ccState', toggleState, true );

      var callback =
      {
        success: function( o ) { },
        failure: function( o ) { }
      };
      
      this.mTransaction = YAHOO.util.Connect.asyncRequest( 'GET', sUrl, callback, null );
      
      sourceElement.collapsibleContainer.toggleState( toggleState );
      
      if( sourceElement.collapsibleContainer.mPostToggleFunction )
      {
        sourceElement.collapsibleContainer.mPostToggleFunction( sourceElement.collapsibleContainer );
      }
    }
  }
}

/**
 *  Call to animate the collapsing of the container, either vertical or horizontal.
 */
function CollapsibleContainer_AnimateCollapse()
{
  if( this.isVerticalCollapse() )
  {
    // backup some values before we animate
    this.xStarting = $(this.mContainer).getWidth();
    this.yStarting = $(this.mHeader).getHeight();
    this.xEndingSize = $(this.mHeader).getHeight();
    this.yEndingSize = $(this.mContainer).getHeight();
    this.headerWidth = $(this.mHeader).getWidth();
    this.yOrginalContainer = $(this.mContainer).getHeight();

    // subtracting off the px amount of the top and bottom borders from the orginal y height
    if(jshIsNonEmptyTrim($(this.mContainer).getStyle('border-top-width')))
    {
        this.yOrginalContainer -=  parseInt($(this.mContainer).getStyle('border-top-width'));
    }
    if(jshIsNonEmptyTrim($(this.mContainer).getStyle('border-bottom-width')))
    {
        this.yOrginalContainer -=  parseInt($(this.mContainer).getStyle('border-bottom-width'));
    }
  
    this.animateVerticalCollapse();
  }
  else
  {
    // backup some values before we animate
    this.yEndingSize = $(this.mHeader).getHeight();
    this.yStarting = $(this.mContainer).getHeight();
    
    this.animateHorizontalCollapse();  
  }

  return;
}

/**
 *  Recursive call to animate the collapsing of a vertical container.
 *  This is accomplished by continuously shrinking the main container's ( this.mContainer ) width
 *  as well as continuously expanding the header's height until the appropriate final dimensions are reached.
 */
function CollapsibleContainer_AnimateVerticalCollapse()
{
  this.mHeader.blur();

  // set the animating flag
  this.mAnimating = true;
  
  var title = $(this.mHeader).getElementsBySelector('h1')[0];
  title.style.display = 'none'
  this.mBody.style.visibility= 'hidden'; 
  this.mCollapseButton.style.visibility = 'hidden';
  this.mContainer.overflow='hidden';
  
  // current size of the 2 elements we are animating
  var xCurrentSize = $(this.mContainer).getWidth();
  var yCurrentSize = $(this.mHeader).getHeight();
  
  var startContainerHeight = $(this.mContainer).getHeight();
  
  // shrinking via percentage that way both elements grow at similar rate
  var xGrain = ( Math.abs( this.xEndingSize - this.xStarting ) * this.ANIMATION_PERCENTAGE_GRAIN  );
  var yGrain = ( Math.abs( this.yEndingSize - this.yStarting ) * this.ANIMATION_PERCENTAGE_GRAIN  );

  // x is shrinking
  var xSize = Math.round( Math.abs( xCurrentSize - xGrain ) );
  
  // y is growing
  var ySize = Math.round( yCurrentSize + yGrain );

  if( ( xSize < this.xEndingSize ) || 
      ( ySize > this.yEndingSize ) )
  {
    // woohoo! we are done.
    xSize = this.xEndingSize;
    ySize = this.yEndingSize;
  }


  // set the height
  this.mContainer.style.width = xSize + 'px';
  this.mHeader.style.height = ySize + 'px';
  this.mContainer.style.height = startContainerHeight;
  
  // scroll to the side where the 
  this.mContainer.scrollLeft  = this.mContainer.scrollWidth;

  // are we done animating?
  if( ( xSize == this.xEndingSize ) ||
      ( ySize == this.yEndingSize ) )
  {
    // bring back the title, even though its not visible
    title.style.display = ''
    this.mCollapseButton.style.visibility = '';
    
    // backup different height for vertical
    this.mOldHeightValue = $(this.mHeader).getHeight() + 'px';

    // resize stuff, a bit more complex than horizontal
    this.mHeader.style.width = this.xStarting + 'px';
    this.mHeader.style.height = ySize + 'px';
    this.mContainer.style.height = (this.yOrginalContainer)  + 'px';
    //  this.mHeader.style.height = '100%';
    
    // scroll to the side where the 
    this.mContainer.scrollLeft  = this.mContainer.scrollWidth;
    
    // all done animating
    this.mAnimating = false;      
    
    // set collapse state to header
    $(this.mHeader).addClassName(this.COLLAPSED_CLASSNAME);
    
  }
  else
  {
    // more animating to do
    var cc = this;
    setTimeout( function(){ cc.animateVerticalCollapse(); }, this.ANIMATION_SLEEP );
  }
}

/**
 *  Recursive call animate the collapsing of the container.
 *  This is accomplished by continuously shrinking the size of the main container ( this.mContainer )
 */
function CollapsibleContainer_AnimateHorizontalCollapse()
{
  // set the animating flag
  this.mAnimating = true;
  
  // hide the collapse button
  this.mCollapseButton.style.visibility = 'hidden';
  this.mBody.style.display = 'none';
  
  // current size
  var yCurrentSize = $(this.mContainer).getHeight();
  
  // grain based on percentage
  var yGrain = Math.abs( (this.yEndingSize - this.yStarting ) * this.ANIMATION_PERCENTAGE_GRAIN );

  // y is shrinking
  var ySize = Math.round( Math.abs( yCurrentSize - yGrain ) );
  if( ySize < this.yEndingSize )
  {
    // woohoo! we are done.
    ySize = this.yEndingSize;
  }

  // set the height
  this.mContainer.style.height = ySize + 'px'; 

  if( ySize == this.yEndingSize )
  {
    // all done animating
    this.mAnimating = false;
  
    // bring back the collapse button
    this.mCollapseButton.style.visibility = '';
    
    // set collapse state on header
    $(this.mHeader).addClassName(this.COLLAPSED_CLASSNAME);
  }
  else
  {
    // more animating to do
    var cc = this;
    setTimeout( function(){cc.animateHorizontalCollapse();}, this.ANIMATION_SLEEP );
  }
}