/****************************************************************************
 * 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
 *
 *  jsHelper.js
 *
 *  Description:
 *    A bunch of nice helper functions mostly to have cross browser support
 *
 *
 *  Requires:
 *    prototype.js - http://www.prototypejs.org/
 *
 *  Development History:
 *    2-22-2007       - created
 *
 *******************************************************************************/

/**
 * For cross browser support, returns the event object given the event function parameter
 */
function jshGetEvent( e )
{
  if( ( ! e ) && window.event )
  {
    return window.event;
  }

  return e;
}


/**
 * Returns the element which generated the given event.
 */
function jshGetSourceElement( e )
{
  var sourceElement = null;

  // grab event
  if( ! e )
  {
    e = jshGetEvent( e );
  }

  if( e && e.srcElement )
  {
    sourceElement = e.srcElement;
  }
  else if( e && e.target )
  {
    sourceElement = e.target;
  }
  else
  {
    sourceElement = null;
  }

  return sourceElement;
}

/**
 * Returns false iff given value is not null and length greater than zero.  Also value can not equal 'undefined'
 */
function jshIsEmpty( s )
{
  if( s )
  {
    if( ( s.length > 0 ) && ( s != 'undefined' ) )
    {
      return false;
    }
  }

  return true;
}

/**
 * Returns true if the given string is empty once it has been trimmed of spaces
 */
function jshIsEmptyTrim( s )
{
  if( s )
  {
    s = jshTrim( s );
  }

  return jshIsEmpty( s );
}

/**
 * Returns true if the string is a number
 */
function jshIsNumeric( sText )

{
  var ValidChars = "0123456789.";
  var IsNumber = true;
  var Char;
 
  for (i = 0; i < sText.length && IsNumber == true; i++) 
  { 
    Char = sText.charAt(i); 
    if ( ValidChars.indexOf(Char) == -1 ) 
    {
      IsNumber = false;
    }
  }
  return IsNumber;   
}
   
/**
 * Same as !jshIsEmpty()
 */
function jshIsNonEmpty( s )
{
  return ( ! jshIsEmpty( s ) );
}

function jshIsNonEmptyTrim( s )
{
  return (! jshIsEmptyTrim( s ) );
}

/**
 * Removes spaces
 */
function jshTrim( s )
{
  if( s.replace )
  {
    s = s.replace(/^\s+|\s+$/g,"");
  }

  return s;
}

/**
 * Returns array of all elements with the given tag name
 */
function jshGetElementBy( element, tagName )
{
  if( element && tagName )
  {
    return $(element).getElementsBySelector( tagName.toLowerCase() );
  }

  return null;
}

/**
 * Returns the first element with the given tagName
 */
function jshGetFirstElementBy( element, tagName )
{
  if( element && tagName )
  {
    var children = jshGetElementBy( element, tagName );
    if( children && children.length > 0 )
    {
      return children[0];
    }
  }

  return null;
}

/**
 * Returns the first form elements within the given element
 */
function jshGetFirstFormElement( element )
{
  return jshGetFirstElementBy( element, 'form' );
}

/**
 * Removes all elements of the given type from the given element
 */
function jshRemoveAllElementsBy( element, tagName )
{
  var elements = jshGetElementBy( element, tagName );
  if( elements )
  {
    for( var i = 0; i < elements.length; i++ )
    {
      element.removeChild( elements[ i ] );
    }
  }
}
/**
 * Returns the selected option's value from the given select form element.
 */
function jshGetSelectedValue( select )
{
  if( select && select.options )
  {
    return select.options[ select.selectedIndex ].value
  }

  return '';
}

/**
 * Sets the given opactiy to the given element where opactiy is a value between 0 and 1
 */
function jshSetOpacity( element, opacity )
{
  if( element )
  {
    if( opacity > 1 )
      opacity = 1;
    else if( opacity < 0 )
      opacity = 0;

    element.style.opacity = opacity;
    element.style.MozOpacity = opacity;
    element.style.KhtmlOpacity = opacity;
    element.style.filter = 'alpha(opacity=' + Math.round( opacity * 100 ) + ')';
    element.jshOpacity = opacity;
  }
}

function jshClearOpacity( element )
{
  if( element )
  {
    element.style.opacity = '';
    element.style.MozOpacity = '';
    element.style.KhtmlOpacity = '';
    element.style.filter = '';
    element.jshOpacity = null;
  }
}

function jshSetFloat( element, direction )
{
  if( element )
  {
    element.style.cssFloat = direction;
    element.style.styleFloat = direction;
  }
}

/**
 * Internal Static method used by jshFadeElement.
 * changes the opacity by a small amount and calls itself
 * again if necessary
 */
function Static_jshFadeElement( element, grain, fadeIn, next_step_time )
{
  // only do anything if the current direction matches
  // the direction we wish to fade
  if( element && grain && (fadeIn == element.jshFadeDir))
  {
    var opactity = element.jshOpacity;
    if( fadeIn )
    {
      opactity += grain;
    }
    else
    {
      opactity -= grain;
    }

    // set to Opacity
    jshSetOpacity( element, opactity );

    // check to see if we've reached the target
    // and call again if it hasn't
    if(fadeIn == element.jshFadeDir)
    {
      if( ( fadeIn && ( element.jshOpacity < element.jshFinalyOpacity ) ) ||
          ( ! fadeIn && ( element.jshOpacity > element.jshFinalyOpacity ) ) )
      {
        // boo, more work to do
        setTimeout(
          function(){ Static_jshFadeElement( element, grain, fadeIn, next_step_time ) },
          next_step_time );
      }
      else
      {
        // make sure we always end up at our final opacity value
        jshSetOpacity( element, element.jshFinalyOpacity );
      }
    }
  }
}

/**
 * Cross browser method for fading a given element
 * steps the number of iterations over the course
 * of the total fade
 * note: use different 'steps' for fading in and
 * fading out
 */
function jshFadeElement( element, total_time, steps, fadeIn, finalOpacity )
{
  if( element )
  {
    // default to the standard full or no ending opacity
    if( ! finalOpacity )
    {
      finalOpacity = (fadeIn)? 1 : 0;
    }

    if (element.jshOpacity == null)
    {
      element.jshOpacity = (fadeIn)? 0 : 1;
    }
    element.jshFinalyOpacity = finalOpacity;
    element.jshFadeDir = fadeIn;
    var el = element;
    var i = 0;
    setTimeout( function()
      {
        Static_jshFadeElement( el, 1.0 / steps, fadeIn, total_time * 1.0 / steps);
      },
      total_time / steps );
  }
}

/**
 * Cross browser method for supporting setting the background postion
 */
function jshSetBackgroundPosition( element, x, y )
{
  if( element && element.style )
  {
    if( !x || ( x == NaN ) )
      x = 0;

    if( !y || ( y == NaN ) )
      y = 0;

    if( element.style.backgroundPositionX )
    {
      element.style.backgroundPositionX = x + 'px';
      element.style.backgroundPositionY = y + 'px';
    }
    else
    {
      element.style.backgroundPosition = x + 'px ' + y + 'px';
    }
  }
}

/**
 * Returns an array with the background postion of the given element where index 0 is x and 1 is y.
 */
function jshGetBackgroundPosition( element )
{
  var toReturn = new Array();
  if( element && element.style )
  {
    if( element.style.backgroundPositionX )
    {
      toReturn[ 0 ] = parseInt( element.style.backgroundPositionX );
      toReturn[ 1 ] = parseInt( element.style.backgroundPositionY );
    }
    else
    {
      var split = element.style.backgroundPosition.split( ' ' );
      toReturn[ 0 ] = parseInt( split[ 0 ] );
      toReturn[ 1 ] = parseInt( split[ 1 ] );
    }
  }

  if( ( ! toReturn[ 0 ] ) || ( toReturn[ 0 ] == NaN ) )
    toReturn[ 0 ] = 0;

  if( ( ! toReturn[ 1 ] ) || ( toReturn[ 1 ] == NaN ) )
    toReturn[ 1 ] = 0;


  return toReturn;
}

/**
 * Returns the current windows dimensions as an array where index 0 is the widht and 1 is the height
 */
function jshGetWindowSize()
{
  var toReturn = $( new Array() );

  if( document.clientHeight )
  {
    toReturn[0] = document.clientWidth;
    toReturn[1] = document.clientHeight;
  }
  else if( document.documentElement && document.documentElement.clientHeight )
  {
    toReturn[0] = document.documentElement.clientWidth;
    toReturn[1] = document.documentElement.clientHeight;
  }
  else if( document.body && document.body.clientHeight )
  {
    toReturn[0] = document.body.clientWidth;
    toReturn[1] = document.body.clientHeight;
  }


  return toReturn;
}

/**
 * Converts the give object into a string of
 * uri parameters where teh first element has no & or ?
 */
function jshToParamString( someObject, prefix )
{
  var toReturn = '';
  if( ! prefix )
    prefix = '';

  if( someObject )
  {
    for( var key in someObject )
    {
      // skip objects
      if( typeof( someObject[key] ) != 'object' )
      {
        if( toReturn.length > 0  )
          toReturn += '&';

        toReturn += prefix + key.gsub(' ', '') + '=' + someObject[key];
      }
    }
  }

  return toReturn;
}

// strips a parameter from a url
function jshStripParameter( url, paramName )
{
  // strip out old param value if it already exists
  if ( url.indexOf( "?" + paramName + "=" ) > -1 || url.indexOf( "&" + paramName + "=" ) > -1 )
  {
    var delim = ( url.indexOf( "?" + paramName + "=" ) > -1 ) ? "?" : "&";

    var s1 = url.substring( 0, url.indexOf( delim + paramName + "=" )+1 );
    var s2 = url.substring( url.indexOf( delim + paramName + "=" ) + 1 );

    if ( s2.indexOf( "&" ) > -1 )
    {
      // if there are more params, concatenate first part of url with second part (minus stripped param)
      return s1 + s2.substring( s2.indexOf( "&" )+1 );
    }
    else
    {
      // if this was the last param, strip punctuation off
      return s1.substring( 0, s1.length-1 );
    }
  }

  else
    return url;
}

// appends or replaces supplied parameter (if it already exists in url)
function jshAppendParameter( url, paramName, paramValue, includeRandomParam )
{
  if( url )
  {
    // strip out old param value if it already exists
    url = jshStripParameter( url, paramName );

    if( url.indexOf( '?' ) >= 0 )
    {
      url += '&';
    }
    else
    {
      url += '?';
    }

    url += paramName + '=' + paramValue

    if( includeRandomParam == true )
    {
      url = jshAppendParameter( url, 'rand', jshRound(Math.random(), 4) );
    }
  }

  return url;
}

/**
 * converts an array of objects into url prameters with the given prefix
 * added to the attribute name.
 *
 * @param arrayOfObjects an array of js objects to be converted into url parameters name value pairs
 * @param prefix the prefix to use before each attribute name.
 */
function jshObjectArrayToParamString( arrayOfObjects, prefix )
{
  var toReturn = '';
  if( arrayOfObjects && ( arrayOfObjects.length > 0 ) )
  {
    var someObject = null;
    for( var i = 0; i < arrayOfObjects.length; i++ )
    {
      someObject = arrayOfObjects[i];
      if( someObject )
      {
        if( i > 0 )
          toReturn += '&';

        toReturn += jshToParamString( someObject, prefix + i );
      }
    }
  }

  return toReturn;
}

/**
 * evaluates teh given string as json using protoype's evalJSON but also goes through
 * each attribute of the evaluated object and attempts to jsonfy each json attribute
 *
 * @author cruiz
 * @param str the string to be evaluated as json
 * @param maxRecursion the number of levels deep it should attemp to jsonfy each attribute
 */
function jshEvalJSON( str, maxRecursion )
{
  var obj = null;
  try
  {
    obj = str.toString().evalJSON(true);
  }
  catch(e)
  {
    //debug( e );
  }

  //var obj = eval( '( ' + str + ' )' );
  if( ( maxRecursion >= 0 ) && obj )
  {
    // must be an array
    if( obj.length )
    {
      // loop through all elements
      for( var i = 0; i < obj.length; i++ )
      {
        // jsonfy each attribute that is json
        for( var key in obj[i] )
        {
          if( obj[i][key].toString().isJSON() )
          {
            try
            {
              obj[i][key] = jshEvalJSON( obj[i][key], maxRecursion - 1 );
            }
            catch(e)
            {
              // eat it!
            }
          }
        }
      }
    }
    else
    {
      // jsonfy each attribute that is json
      for( var key in obj )
      {
        if( obj[key].toString().isJSON() )
        {
          obj[key] = jshEvalJSON( obj[key], maxRecursion - 1 );
        }
      }
    }
  }

  return obj;
}

/**
 * Constructs a mock event with the given source element.
 * Used to simulate an js event object
 */
function jshConstructMockEvent( sourceElement )
{
  var obj = new Object();
  obj.srcElement = sourceElement;

  return obj;
}

/**
 * Constrcuts a mock event with the given message and status code
 */
function jshConstrcutMockResponse( message, statusCode )
{
  if( ! statusCode )
    statusCode = -1;

  if( ! message )
    message = 'Default Mock Response message!';

  var obj = new Object();
  obj.message = message;
  obj.statusCode = statusCode;

  return obj;
}

function jshBorderOffset( element, defaultValues )
{
  var toReturn = $( [ 0, 0 ] );

  if( element )
  {
    toReturn[0] = parseInt( element.getStyle( 'borderLeftWidth' ) ) + parseInt( element.getStyle( 'borderRightWidth' ) );
    toReturn[1] = parseInt( element.getStyle( 'borderBottomWidth' ) ) + parseInt( element.getStyle( 'borderTopWidth' ) );

    if( (!( ( toReturn[0] < 0 ) || ( toReturn[0] >= 0 ) ) && (defaultValues != null)) )
    {
      toReturn[0] = defaultValues[0]
    }

    if( (!( ( toReturn[1] < 0 ) || ( toReturn[1] >= 0 ) ) && (defaultValues != null)) )
    {
      toReturn[1] = defaultValues[1]
    }
  }

  return toReturn;
}

function jshBorderOffsets( elements, defaultValues )
{
  var toReturn = $( [ 0, 0 ] );

  if( elements && elements.length )
  {
    var bo;
    for( var i = 0; i < elements.length; i++ )
    {
      bo = jshBorderOffset( elements[i], defaultValues );
      toReturn[0] += bo[0];
      toReturn[1] += bo[1];
    }
  }

  return toReturn;
}

function jshMarginOffset( element, defaultValues )
{
  var toReturn = $( [ 0, 0 ] );

  if( element )
  {
    toReturn[0] = parseInt( element.getStyle( 'marginLeft' ) ) + parseInt( element.getStyle( 'marginRight' ) );
    toReturn[1] = parseInt( element.getStyle( 'marginBottom' ) ) + parseInt( element.getStyle( 'marginTop' ) );

    if( ( toReturn[0] == null ) || ( isNaN( toReturn[0] ) ) )
    {
      if( defaultValues && ( defaultValues[0] >= 0 ) )
        toReturn[0] = defaultValues[0];
    }

    if( ( toReturn[1] == null ) || ( isNaN( toReturn[1] ) ) )
    {
      if( defaultValues && ( defaultValues[1] >= 0 ) )
        toReturn[1] = defaultValues[1];
    }
  }

  return toReturn;
}

/**
 * place a given element on a corner of another given
 * element.
 */
var jshOverlayPosition = {TopLeft : 0, TopRight : 1, BottomLeft : 2, BottomRight : 3, Center : 4};
function jshOverlay(elemToOverlay, sourceElem, pos, offsetX, offsetY)
{
  if( elemToOverlay && sourceElem )
  {
    var sourcePos = Position.cumulativeOffset(sourceElem);
    var sourceDim = $(sourceElem).getDimensions();
    var overlayDim = $(elemToOverlay).getDimensions();
    var borderOffset = jshBorderOffset( sourceElem );
    // if the element has no borderOffset
    if( borderOffset[0] && borderOffset[1] )
    {
  	  borderOffset[0] /= 2;
  	  borderOffset[1] /= 2;
  	}
  	else
  	{
  	  borderOffset[0] = 0;
  	  borderOffset[1] = 0;
  	}

    switch (pos)
    {
    case jshOverlayPosition.TopLeft:
      elemToOverlay.style.top = (sourcePos[1] + offsetY + borderOffset[1]) + "px";
      elemToOverlay.style.left = (sourcePos[0] + offsetX + borderOffset[0]) + "px";
      break;
    case jshOverlayPosition.TopRight:
    	elemToOverlay.style.top = (sourcePos[1] + offsetY + borderOffset[1]) + "px";
      elemToOverlay.style.left = (sourcePos[0] + sourceDim.width - overlayDim.width + offsetX -  borderOffset[0]) + "px";
      break;
    case jshOverlayPosition.BottomLeft:
      elemToOverlay.style.top = (sourcePos[1] + sourceDim.height - overlayDim.height + offsetY + borderOffset[1]) + "px";
      elemToOverlay.style.left = (sourcePos[0] + offsetX + borderOffset[0]) + "px";
      break;
    case jshOverlayPosition.BottomRight:
      elemToOverlay.style.top = (sourcePos[1] + sourceDim.height - overlayDim.height + offsetY - borderOffset[1]) + "px";
      elemToOverlay.style.left = (sourcePos[0] + sourceDim.width - overlayDim.width + offsetX - borderOffset[0]) + "px";
    default:
      break;
    }
  }
}

/**
 * Returns true if visibility is not hidden AND display is not none
 */
function jshIsVisible( element )
{
  var toReturn = false;

  if( element && element.style )
  {
    if( element.style.visibility == 'hidden' || element.style.display == 'none' )
    {
      toReturn = false;
    }
    else
    {
      toReturn = true;
    }
  }

  return toReturn;
}

function jshSetOptionSelected( select, optionValue )
{
  if( select && select.options && optionValue )
  {
    for( var i = 0; i < select.options.length; i++ )
    {
      if( select.options[i].value == optionValue )
      {
        select.options[i].selected = true;
      }
      else
      {
        select.options[i].selected = false;
      }
    }
  }
}

/**
 * Retuns the index of the the child node whose x and y are within it skipping
 * any elements whos classnmae matches the given one.
 *
 * @param parent element used to loop through its child nodes
 * @param x the x corrdinate that lies withing the child node
 * @param y the y corrdinate that lies withing the child node
 * @param skipChildWithClassName the class name of the child nodes to skip when countint
 *                               up the index to return
 *
 */
function jshGetChildIndexWithin( parent, x, y )
{
  if( parent && x && y )
  {
    var child;
    var index = 0;
    for( var i = 0; i < parent.childNodes.length; i++ )
    {
      child = parent.childNodes[i];
      if( child )
      {
        if( jshIsVisible( child ) )
        {
          if( Position.within( child, x, y ) )
          {
            return index;
          }
          else
          {
            index++;
          }
        }
      }
    }
  }

  return -1;
}

/**
 * Returns the closes visible child node starting at the given index.
 * If the child at the given index is not visible then the next sibling, right or left,
 * of the given index is checked until eithe the end is reached or a visible sibling is found.
 *
 * @author carlos
 * @param parent the parent containing the child nodes
 * @param index that starting index of the child to be checked for visiblity
 * @param right iff true the next sibling to be check will be the one to the right of it otherwise the left will be checked
 */
function jshGetClosestVisbleChildNode( parent, index, right )
{
  var toReturn = null;
  if( parent && index && parent.childNodes &&
    ( parent.childNodes.length > index ) )
  {
    if( jshIsVisible( parent.childNodes[ index ] ) )
    {
      toReturn = parent.childNodes[ index ];
    }
    else
    {
      //recursivly call our selves
      if( right )
      {
        toReturn = jshGetClosestVisbleChildNode( parent, index + 1, right );
      }
      else
      {
        toReturn = jshGetClosestVisbleChildNode( parent, index - 1, right );
      }
    }
  }

  return toReturn;
}

/**
 *  Returns the xBrowserWindowSize
 */
function jshGetBrowserWindowSize( w )
{
  w = w ? w : window;
  var width = w.innerWidth || (w.document.documentElement.clientWidth || w.document.body.clientWidth);
  var height = w.innerHeight || (w.document.documentElement.clientHeight || w.document.body.clientHeight);
  return [width, height]
}

/**
 * Adds a scroll wheel event to the given element and assigns it to the give function
 */
function jshRegisterScrollWheelEvent( element, theFunction )
{

  if( element && theFunction )
  {
    element.onmousewheel = theFunction;
    if( element.addEventListener )
    {
      element.addEventListener( 'DOMMouseScroll', theFunction, false );
    }
  }
}

/**
 * Gets the mouse coordiantes given a scroll wheen event
 */
function jshGetMousePositionFromScrollEvent( e, referenceElement )
{
  var toReturn = $( new Array() );
  if( e )
  {
    if( e.clientX )
    {
      toReturn[0] = e.clientX;
      toReturn[1] = e.clientY;
    }
    else if( e.screenX )
    {
      toReturn[0] = e.screenX;
      toReturn[1] = e.screenY;
    }
    else if( e.x )
    {
      toReturn[0] = e.x;
      toReturn[1] = e.y;
    }
  }

  // ie always returns x,y with respects to the window verses other browsers
  // which returns x,y with respects to the source element.
  // for our purposes, its easier to work with IE than the other way around.
  if( ! jshIsIE6Or7() )
  {
    var sourceElement = referenceElement;
    if( ! sourceElement )
    {
      sourceElement = jshGetSourceElement( e );
    }

    var offset = Position.page( sourceElement );

    toReturn[0] += offset[0];
    toReturn[1] += offset[1];
  }


  return toReturn;
}

function jshIsFF3()
{
  //test for Firefox/x.x or Firefox x.x (ignoring remaining digits);
  if( /Firefox[\/\s](\d+\.\d+)/.test( navigator.userAgent ) )
  {
    // capture x.x portion and store as a number
    var ffversion = new Number( RegExp.$1 )
    if( ffversion >= 3 )
    {
      return true;
    }
  }

  return false;
}

/**
 * Returns true iff the scroll wheel was scrolled up
 */
function jshScrolledUp( e )
{
  var toReturn = false;
  if( e )
  {
    var delta = 0;
    if( e.wheelDelta )
    {
      delta = e.wheelDelta / 120;
    }
    else if( e.detail )
    {
      delta = e.detail / -3;
    }

    if( delta > 0 )
    {
      toReturn = true;
    }
  }

  return toReturn;
}

/**
 * Same as ! jshScrolledUp( e )
 */
function jshScrolledDown( e )
{
  return (! jshScrolledUp( e ) );
}

function jshIsIE6Or7()
{
  if( navigator && navigator.userAgent )
  {
    if( ( navigator.userAgent.indexOf( 'MSIE 6' ) >= 0 ) ||
        ( navigator.userAgent.indexOf( 'MSIE 7' ) >= 0 ) )
    {
      return true;
    }
  }

  return false;
}

/**
 * returns the largest possible dimensions that meet the given ratio.
 */
function jshCalculateProportionalDimensions( ratio, dimensions )
{
  var newSize = $(new Array());
  var largeSide = ( dimensions[0] > dimensions[1] )? 0 : 1;
  var smallSide = ( largeSide == 0 )? 1 : 0;
  var ratio = ratio[smallSide] / ratio[largeSide];

  newSize[largeSide] = dimensions[largeSide];
  newSize[smallSide] = Math.round( newSize[largeSide] * ratio );

  // the small side is still to large to fit within the container
  // so lets flip it
  if( newSize[smallSide] > dimensions[smallSide] )
  {
    ratio = newSize[largeSide] / newSize[smallSide];
    newSize[smallSide] = dimensions[smallSide];
    newSize[largeSide] = Math.round( newSize[smallSide] * ratio );
  }

  return newSize;
}

/**
 * Rounds the given double to the specified number of decimal places
 */
function jshRound( value, numberOfDecimalPlaces )
{
  if( value )
  {
    if( !numberOfDecimalPlaces )
      numberOfDecimalPlaces = 1;

    var precision = Math.pow( 10, numberOfDecimalPlaces );
    value = Math.round( value *  precision ) / precision;
  }

  return value;
}

function jshTruncate( string, maxLength, suffix )
{
  var toReturn = '';

  if( !suffix )
    suffix = '&#133;';

  if( string )
  {
    toReturn = string;
    if( string.length > maxLength )
    {
      toReturn = string.substring( 0, maxLength );
      toReturn += suffix;
    }
  }

  return toReturn;
}

function jshEqualsAny( string, matchOnArray )
{
  if( string && matchOnArray )
  {
    for( var i = 0; i < matchOnArray.length; i++ )
    {
      if( matchOnArray[i] )
      {
        if( matchOnArray[i].toLowerCase() == string.toLowerCase() )
        {
          return true;
        }
      }
    }
  }

  return false;
}

function jshDefaultEmpty( testValue, defaultValue )
{
  if( ! defaultValue )
      defaultValue = '';

  if( ! testValue )
    testValue = defaultValue;

  return testValue;
}


function jshForceSpaces( text, segmentLength )
{
  var toReturn = '';
  if( text )
  {
    if( ! segmentLength || ( segmentLength < 1 ) )
      segmentLength = 20;

    var index = 0;
    var segment;
    var end;
    while( index < text.length )
    {
      end = ( ( index + segmentLength ) > text.length )? text.length : ( index + segmentLength );
      segment = text.substring( index, end );
      if( segment.indexOf( ' ' ) < 0 )
      {
        segment += ' ';
      }

      toReturn += segment;
      index = end;
    }
  }

  return toReturn;
}

function jshGetExtensionFromUrl( url, knownExtensions, defaultExtension )
{
  var toReturn = '';
  if( url )
  {
    var extension = '';
    var lastIndex = url.lastIndexOf( '.' );
    if( lastIndex >= 0 )
    {
      extension = url.substring( lastIndex + 1 );
    }

  	var index1 = extension.indexOf( '&' );
  	var index2 = extension.indexOf( '?' );
  	if( ( index1 >= 0 ) || (  index2 >= 0 ) )
    {
      if( ( index2 < index1 ) && ( index2 >= 0 ) )
        index1 = index2;

      extension = extension.substring( 0, index1 );
    }

    extension = jshTrim( extension );

    // validate the foudn extension with the given know ones.
    if( ! jshIsEmpty( extension ) && knownExtensions && knownExtensions.length > 0 )
    {
      for( var ext = 0; ext < knownExtensions.length; ext++ )
      {
        if( extension == knownExtensions[ext] )
        {
          toReturn = extension;
          break;
        }
      }
    }
    else
    {
      // our best guess
      toReturn = extension
    }

    if( jshIsEmpty( toReturn ) )
      toReturn = defaultExtension;
  }

  return toReturn;
}

function jshGetAfterLastSlashFromUrl( url )
{
  var toReturn = '';
  url = jshGetCleanUrl( url );
  if( url )
  {
    var lastIndex = url.lastIndexOf( '/' );
    if( lastIndex >= 0 )
    {
      last = url.substring( lastIndex + 1 );
    }

    last = jshTrim( last );

    if( ! jshIsEmpty( last ) )
    {
      toReturn = last;
    }
  }
  return toReturn;
}

/**
 * Returns the index of the give child node or -1 if not found.
 */
function jshIndexOfNode( node )
{
  var parent = node.up();
  for( var i = 0; i < parent.childNodes.length; i++ )
  {
    if( parent.childNodes[i] == node )
    {
      return i;
    }
  }

  return -1;
}


function jshGetCleanUrl( url )
{
  var cleanUrl = url + '';
  if( cleanUrl.indexOf( '?' ) > 0 )
  {
    cleanUrl = cleanUrl.substring( 0, cleanUrl.indexOf( '?' ) );
  }
  if( cleanUrl.indexOf( ';jsessionid' ) > 0 )
  {
    cleanUrl = cleanUrl.substring( 0, cleanUrl.indexOf( ';jsessionid' ) );
  }

  return cleanUrl;
}


