/****************************************************************************
 * 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
 *
 *  ElementEffects.js
 *
 *  Description:
 *    Common element level effects.
 *
 *  Structure:
 *
 *  Requires:
 *    prototype.js - http://www.prototypejs.org/
 *    rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
 *
 *  Development History:
 *    9-18-2007       - created
 *
 *******************************************************************************/


var ElementEffects =
{
  /**
   * Blinks the element by causing its border to change color.  The color
   * it chanes to is the opposite of the one currently set on the element
   *
   * Example:
   *
   * ElementEffects.blinkBorder( $( 'myDiv' ), 3, 120 )
   */
  blinkBorder: function( element, numberOfBlinks, msIntervalSpeed )
  {
    if( element )
    {
      if( ! msIntervalSpeed )
        msIntervalSpeed = 130;

      if( ! numberOfBlinks )
        numberOfBlinks = 3;

      var attributes = ['border-top-color', 'border-bottom-color', 'border-left-color', 'border-right-color'];
      this.doBlinkWork( element, numberOfBlinks * 2, msIntervalSpeed, attributes );
    }
  },

  /**
   * Blinks the element by causing its border to change color.  The color
   * it chanes to is the opposite of the one currently set on the element
   *
   * Example:
   *
   * ElementEffects.blinkColor( $( 'myDiv' ), 3, 120 )
   *
   */
  blinkColor: function( element, numberOfBlinks, msIntervalSpeed )
  {
    if( element )
    {
      if( ! msIntervalSpeed )
        msIntervalSpeed = 130;

      if( ! numberOfBlinks )
        numberOfBlinks = 3;

      this.doBlinkWork( element, numberOfBlinks * 2, msIntervalSpeed, ['color'] );
    }
  },

  /**
   * Blinks the elements border and font color by changing each elements color to its opposite.
   *
   * Example:
   *
   * ElementEffects.blinkBorderAndColor( $( 'myDiv' ), 3, 120 )
   */
  blinkBorderAndColor: function( element, numberOfBlinks, msIntervalSpeed )
  {
    if( element )
    {
      if( ! msIntervalSpeed )
        msIntervalSpeed = 130;

      if( ! numberOfBlinks )
        numberOfBlinks = 3;

      var attributes = ['border-top-color', 'border-bottom-color', 'border-left-color', 'border-right-color', 'color'];
      this.doBlinkWork( element, numberOfBlinks * 2, msIntervalSpeed, attributes );
    }
  },

  /**
   * Blinks the elements border and font color by changing each elements color to its opposite.
   *
   * Example:
   *
   * ElementEffects.blinkBorderAndColor( $( 'myDiv' ), 3, 120 )
   */
  blinkBackgroundColor: function( element, numberOfBlinks, msIntervalSpeed )
  {
    if( element )
    {
      if( ! msIntervalSpeed )
        msIntervalSpeed = 130;

      if( ! numberOfBlinks )
        numberOfBlinks = 3;

      var attributes = ['backgroundColor'];
      this.doBlinkWork( element, numberOfBlinks * 2, msIntervalSpeed, attributes );
    }
  },

  /**
   * Private method that actually does the work of blinking the element.
   */
  doBlinkWork: function( element, numberOfTimes, msIntervalSpeed, styleAttribute )
  {
    if( ( numberOfTimes > 0 ) && styleAttribute && ( styleAttribute.length > 0 ) )
    {
      // constrcut the styles we are going to change and their opposite color
      var styles = new Object();
      var currentColor;
      var newColor;
      for( var i = 0; i < styleAttribute.length; i++ )
      {
        // figure out the opposite color
        currentColor = element.getStyle( styleAttribute[i] );
        newColor = this.oppositeColor( currentColor );

        // add it if we have one.
        if( newColor )
        {
          styles[ styleAttribute[ i ] ] = newColor;
        }
      }

      element.setStyle( styles );

      var count = numberOfTimes - 1;
      setTimeout( function(){ ElementEffects.doBlinkWork( element, count, msIntervalSpeed, styleAttribute ); }, msIntervalSpeed );
    }
  },

  animateScroll: function( element, endingSize, interval, velocity, postFunction )
  {
    if( element && endingSize )
    {
      if( ! interval )
        interval = 100;

      if( ( velocity > 1 ) || ( velocity < 0 ) )
       velcoity = 0.35;

      element.mEndingSize = endingSize;
      element.mGrowing = $( [ ( endingSize[0] < element.scrollLeft ), ( endingSize[1] < element.scrollTop ) ] );
      element.mSoFar = $( [ ( element.scrollLeft ), ( element.scrollTop ) ] );
      element.mVelocity = velocity;
      element.mPostFunction = postFunction;
      element.mMaximumSets = 50;
      element.mStepsCount = 0;

      //alert('1: ' + element.scrollLeft );

      this.doAnimateScroll( element, interval );
    }
  },

  doAnimateScroll: function( element, interval )
  {
    if( element && element.mEndingSize && element.mGrowing )
    {
      element.mStepsCount++;

      if( ( element.mVelocity > 1 ) || ( element.mVelocity < 0 ) )
       element.mVelocity = 0.35;

      //alert('2:: ' + element.mStepsCount );

      var done = true;
      if( ( element.mEndingSize[0] != ( element.scrollLeft ) ) ||
          ( element.mEndingSize[1] != ( element.scrollTop ) ) )
      {
        var done0 = true;
        if( element.mEndingSize[0] != ( element.scrollLeft ) )
        {
          done0 = this.doAnimateElementScrollWork( element, true );
        }

        var done1 = true;
        if( element.mEndingSize[1] != ( element.scrollTop ) )
        {
          done1 = this.doAnimateElementScrollWork( element, false );
        }

        // were we told that we are done?
        // mostly dude to size variations due stylings such as padding, margin, border.
        done = ( ( done0 == true ) && ( done1 == true ) );
      }


      if( done == true || element.mStepsCount == 30 )
      {
        // must be done do post function, if any
        if( element.mPostFunction )
        {
          element.mPostFunction( element );
        }
      }
      else
      {
        // more work to do, call ourselves in a bit
        setTimeout( function(){ ElementEffects.doAnimateScroll( element, interval ) }, interval );
      }
    }
  },

  doAnimateElementScrollWork: function( element, left )
  {
    var reachedEndValue = false;
    if( element )
    {
      var direction = ( left == true )? 0 : 1;
      var howMuchToGo = ( ( left == true )? ( element.scrollLeft ) : ( element.scrollTop ) );
      //alert( '3a:: ' + element.mGrowing + ' ; ' +  howMuchToGo + ' ; ' + element.mEndingSize + ' ; ' + direction );
      //alert( 'howMuchToGo: ' + howMuchToGo + ' ; ' + element.mEndingSize[direction] );
      //var bb1 = ( ( !( element.mGrowing[direction] ) ) );
      //var bb2 = ( howMuchToGo < element.mEndingSize[direction] );
      //alert( 'bb: ' + bb1 + ' ; ' + bb2 )
      if( ( ( element.mGrowing[direction] ) && ( howMuchToGo > element.mEndingSize[direction] ) ) ||
          ( !( element.mGrowing[direction] ) && ( howMuchToGo < element.mEndingSize[direction] ) ) )
      {

        newPosition = this.calcuateNextValue( element, direction, howMuchToGo, 0 );
        //alert( '3:: ' + newPosition );
        if( left == true )
        {
          if( newPosition >= 0 )
          {
            element.scrollLeft = newPosition;
          }
        }
        else
        {
          if( newPosition >= 0 )
          {
            element.scrollRight = newPosition;
          }
        }

        // are we done
        if( newPosition == element.mEndingSize[direction] )
          reachedEndValue = true;
      }
    }

    return reachedEndValue;
  },

  /**
   * Animates the growth ( expand/contract of the width/height ) of the given element
   *
   * Example:
   *
   * var myElement = $( 'myDiv' );
   * var postFunction = function(element){ alert( 'done animating!' ); };
   * ElementEffects.animateElementGrowth( myElement, [ 300, 400 ], 55, .45 );
   *
   * @param element the element whose dimensions will changes
   * @param endingSize array of ending dimensions
   * @param interval optional value that defines in ms how soon to call the next size
   * @param velocity the rate at which the element will expand or contract to the ending size
   */
  animateElementGrowth: function( element, endingSize, interval, velocity, postFunction )
  {
    if( element && endingSize )
    {
      if( ! interval )
        interval = 100;

      if( ( velocity > 1 ) || ( velocity < 0 ) )
       velcoity = 0.35;

      var borderOffset = jshBorderOffset( element, [0,0] );
      element.mEndingSize = endingSize;
      element.mGrowing = $( [ ( endingSize[0] < element.getWidth() ), ( endingSize[1] < element.getHeight() ) ] );
      element.mSoFar = $( [ ( element.getWidth() + borderOffset[0] ), ( element.getHeight() + borderOffset[1] ) ] );
      element.mVelocity = velocity;
      element.mPostFunction = postFunction;
      element.mMaximumSets = 50;
      element.mStepsCount = 0;

      this.doAnimateElementGrowth( element, interval );
    }
  },

  /**
   * Private method that controls the animation of the element being grown/shurnk
   */
  doAnimateElementGrowth: function( element, interval )
  {
    if( element && element.mEndingSize && element.mGrowing )
    {
      element.mStepsCount++;

      if( ( element.mVelocity > 1 ) || ( element.mVelocity < 0 ) )
       element.mVelocity = 0.35;

      var done = true;
      var borderOffset = jshBorderOffset( element, [0,0] );
      if( ( element.mEndingSize[0] != ( element.getWidth() - borderOffset[0] ) ) ||
          ( element.mEndingSize[1] != ( element.getHeight() - borderOffset[1] ) ) )
      {
        var done0 = true;
        if( element.mEndingSize[0] != ( element.getWidth() - borderOffset[0] ) )
        {
          done0 = this.doAnimateElementGrowthWork( element, true );
        }

        var done1 = true;
        if( element.mEndingSize[1] != ( element.getHeight() - borderOffset[1] ) )
        {
          done1 = this.doAnimateElementGrowthWork( element, false );
        }

        // were we told that we are done?
        // mostly dude to size variations due stylings such as padding, margin, border.
        done = ( ( done0 == true ) && ( done1 == true ) );
      }


      if( done == true || element.mStepsCount == 30 )
      {
        // must be done do post function, if any
        if( element.mPostFunction )
        {
          element.mPostFunction( element );
        }
      }
      else
      {
        // more work to do, call ourselves in a bit
        setTimeout( function(){ ElementEffects.doAnimateElementGrowth( element, interval ) }, interval );
      }
    }
  },

  /**
   * Private method that does the actual work of changing the elemts dimensions
   */
  doAnimateElementGrowthWork: function( element, width )
  {
    var reachedEndValue = false;
    if( element )
    {
      var borderOffset = jshBorderOffset( element, [0,0] );
      var direction = ( width == true )? 0 : 1;
      var howMuchToGo = ( ( width == true )? ( element.getWidth() - borderOffset[0] ) : ( element.getHeight() - borderOffset[1] ) );
      if( ( ( element.mGrowing[direction] ) && ( howMuchToGo > element.mEndingSize[direction] ) ) ||
          ( !( element.mGrowing[direction] ) && ( howMuchToGo < element.mEndingSize[direction] ) ) )
      {

        newPosition = this.calcuateNextValue( element, direction, howMuchToGo, 0 );
        if( width == true )
        {
          if( newPosition >= 0 )
          {
            element.style.width = newPosition + 'px';
          }
        }
        else
        {
          if( newPosition >= 0 )
          {
            element.style.height = newPosition + 'px';
          }
        }

        // are we done
        if( newPosition == element.mEndingSize[direction] )
          reachedEndValue = true;
      }
    }

    return reachedEndValue;
  },

  /**
   * Animates the position of the given element. Assumes the elemtn is already preped
   * for absolute positioning
   *
   * Example:
   *
   * var myElement = $( 'myDiv' );
   * var postFunction = function(element){ alert( 'done animating!' ); };
   * ElementEffects.animateElementPosition( myElement, [ 10, 29 ], 55, .45, postFunction );
   *
   * @param element the element whose dimensions will changes
   * @param endingSize array of ending dimensions
   * @param interval optional value that defines in ms how soon to call the next size
   * @param velocity the rate at which the element will expand or contract to the ending size
   */
  animateElementPosition: function( element, endingSize, interval, velocity, postFunction )
  {
    if( element && endingSize )
    {
      if( ! interval )
        interval = 100;

      if( ( velocity > 1 ) || ( velocity < 0 ) )
       velcoity = 0.35;

      var elementX = ( element.style.left )? element.style.left : element.style.right;
      var elementY = ( element.style.top )? element.style.top : element.style.bottom;
      var position = [ parseInt( elementX ), parseInt( elementY ) ];
      element.mEndingSize = endingSize;
      element.mSoFar = $( [ ( position[0] ), ( position[1]) ] );
      element.mVelocity = velocity;
      element.mPostFunction = postFunction;
      element.mGrowing = $( new Array() );
      element.mGrowing[0] = ( ( element.style.left )? ( endingSize[0] < position[0] ) : ( endingSize[0] > position[0] )  );
      element.mGrowing[1] = ( ( element.style.top ) ? ( endingSize[1] < position[1] ) : ( ( endingSize[1] < position[1] ) ) );

      //alert( endingSize + ' ; ' + element.mGrowing + ' ; ' + position );
      this.doAnimateElementPosition( element, interval );
    }
  },

  /**
   * Private method that controls the animation of the element being grown/shurnk
   */
  doAnimateElementPosition: function( element, interval )
  {
    if( element && element.mEndingSize && element.mGrowing )
    {
      if( ( element.mVelocity > 1 ) || ( element.mVelocity < 0 ) )
       element.mVelocity = 0.35;

      var elementX = ( element.style.left )? element.style.left : element.style.right;
      var elementY = ( element.style.top )? element.style.top : element.style.bottom;
      var position = [ parseInt( elementX ), parseInt( elementY ) ];
      if( ( element.mEndingSize[0] != position[0] ) ||
          ( element.mEndingSize[1] != position[1] ) )
      {
        if( element.mEndingSize[0] != position[0] )
        {
          this.doAnimateElementPositionWork( element, 0 );
        }

        if( element.mEndingSize[1] != position[1] )
        {
          this.doAnimateElementPositionWork( element, 1 );
        }

        // call ourselves in a bit
        setTimeout( function(){ ElementEffects.doAnimateElementPosition( element, interval ) }, interval );
      }
      else
      {
        // must be done do post function, if any
        if( element.mPostFunction )
        {
          element.mPostFunction( element );
        }
      }
    }
  },

  /**
   * Private method that does the actual work of changing the elemts dimensions
   */
  doAnimateElementPositionWork: function( element, direction )
  {
    if( element )
    {
      var elementX = ( element.style.left )? element.style.left : element.style.right;
      var elementY = ( element.style.top )? element.style.top : element.style.bottom;
      var position = [ parseInt( elementX ), parseInt( elementY ) ];
      var howMuchToGo = position[direction];
      //alert( '22asdasd: ' + element.mGrowing[direction] + ' ; ' + howMuchToGo + ' ; ' + element.mEndingSize + ' ; direction: ' + direction );
      if( ( ( element.mGrowing[direction] ) && ( howMuchToGo > element.mEndingSize[direction] ) ) ||
          ( !( element.mGrowing[direction] ) && ( howMuchToGo < element.mEndingSize[direction] ) ) )
      {
        //alert( '11asdasd: ' );
        newPosition = this.calcuateNextValue( element, direction, howMuchToGo, 0, false );
        if( direction == 0 )
        {
          if( element.style.left )
          {
            element.style.left = newPosition + 'px';
          }
          else
          {
            element.style.right = newPosition + 'px';
          }
        }
        else
        {
          //alert( 'asdasd: ' + newPosition );
          if( element.style.top )
          {
            element.style.top = newPosition + 'px';
          }
          else
          {
            element.style.bottom = newPosition + 'px';
          }
        }
      }
    }
  },

  /**
   * A linear motion for producing the next value for an animation.
   *
   * Is this really linear?  works ok for fading.
   */
  linearNextValue: function( element, direction, howMuchToGo, defaultValue, dontRound )
  {
    var toReturn = defaultValue;
    if( element )
    {
      if( ( ( element.mGrowing[direction] ) && ( howMuchToGo < element.mEndingSize[direction] ) ) ||
          ( !( element.mGrowing[direction] ) && ( howMuchToGo > element.mEndingSize[direction] ) ) )
      {
        // calc next chunk
        var diff = element.mVelocity; // does this count as liner?
        if( ! element.mGrowing[direction] )
        {
          diff *= -1;
        }

        // store how far we have moved
        element.mSoFar[direction] += diff;

        // calc new position
        var nextValue = howMuchToGo + diff;
        if( dontRound != true )
        {
          nextValue = Math.round( nextValue );
        }

        // make sure we move a bit
        if( ( ( element.mGrowing[direction] ) && ( howMuchToGo > element.mEndingSize[direction] ) ) ||
          ( !( element.mGrowing[direction] ) && ( howMuchToGo < element.mEndingSize[direction] ) ) ||
           ( diff == 0 ) )
        {
          nextValue = element.mEndingSize[direction];
        }

        toReturn = nextValue;
      }
    }

    return toReturn;
  },

  /**
   * Default method for getting the next value in any kind of animation based on the current state.
   * The motion produced starts with a nice accleration and slowly decreases.
   * Something reminiscent of a opening/closing drawer.
   */
  calcuateNextValue: function( element, direction, howMuchToGo, defaultValue, dontRound )
  {
    var toReturn = defaultValue;
    if( element )
    {
      if( ( ( element.mGrowing[direction] ) && ( howMuchToGo > element.mEndingSize[direction] ) ) ||
          ( !( element.mGrowing[direction] ) && ( howMuchToGo < element.mEndingSize[direction] ) ) )
      {
        // calc next chunk
        var diff = element.mEndingSize[direction] - element.mSoFar[direction];

        // scale by velocity
        if( ( diff > 1 ) || ( diff < -1 ) )
        {
          diff *= element.mVelocity;
        }

        if( diff == 0 )
        {
          //alert( 'diff == 0' + element.mGrowing[direction] + ' ; ' + element.mEndingSize[direction] + ' ; ' + element.mSoFar[direction] + ' ; ' + element.getWidth() + ' ; '  + element.getHeight() );
          nextValue = element.mEndingSize[direction];
        }
        else
        {
          // store how far we have moved
          element.mSoFar[direction] += diff;

          // calc new position
          var nextValue = howMuchToGo + diff;
          if( dontRound != true )
          {
            nextValue = Math.round( nextValue );
          }
        }

        toReturn = nextValue;
      }
    }

    return toReturn;
  },

  /**
   * Fades the given element from its current opacity to the specified ending opacity.
   * When done and if a post funciton was provided the post funciton is called.
   *
   * Example:
   *
   * var myElement = $( 'myDiv' );
   * var postFunction = function(element){ alert( 'done fading!' ); };
   * ElementEffects.fadeElement( myElement, 0, 55, .25, postFunction );
   */
  fadeElement: function( element, endingOpacity, interval, velocity, postFunction )
  {
    if( element )
    {
      if( ! interval )
        interval = 100;

      if( ( velocity > 1 ) || ( velocity < 0 ) )
       velcoity = 0.35;

      var opacity = (element.mOpacity >= 0)? element.mOpacity : 1;
      element.mOpacity = opacity;
      element.mEndingSize = [ endingOpacity ];
      element.mSoFar = $( [ opacity ] );
      element.mVelocity = velocity;
      element.mPostFunction = postFunction;
      element.mGrowing = [ ( opacity < endingOpacity ) ];

      ElementEffects.doFadeElement( element, interval );
    }
  },

  /**
   * Private medthod used by fadeElement.  Should not be called directly.
   */
  doFadeElement: function( element, interval )
  {
    if( ( element.mGrowing[0] && ( element.mOpacity < element.mEndingSize[0] ) ) ||
        ( !element.mGrowing[0] && ( element.mOpacity > element.mEndingSize[0] ) ) )
    {
      // not done yet
      var nextOpacity = ElementEffects.linearNextValue( element, 0, element.mOpacity, 0, true );
      ElementEffects.setOpacity( element, nextOpacity );

      // call our selves in a bit
      setTimeout( function(){ ElementEffects.doFadeElement( element, interval ) }, interval );
    }
    else
    {
      ElementEffects.setOpacity( element, element.mEndingSize[0] );
      if( element.mPostFunction )
      {
        element.mPostFunction( element );
      }
    }
  },

  /**
   * Sets the opacity for the given element.
   * Supported across IE, FF, Safari.  Maybe more.
   */
  setOpacity: function( element, opacity )
  {
    if( element )
    {
      // keep in between 0 and 1
      opacity = Math.min( opacity, 1 );
      opacity = Math.max( opacity, 0 );

      element.style.opacity = opacity;
      element.style.MozOpacity = opacity;
      element.style.KhtmlOpacity = opacity;
      element.style.filter = 'alpha(opacity=' + ( opacity * 100 ) + ')';
      element.mOpacity = opacity;
    }
  },

  /**
   * Helper method that returns the opposite color of the given one.
   * Does it best to parse the color string
   */
  oppositeColor: function( colorString )
  {
    var newColor = colorString;
    if( colorString )
    {
      var color = new RGBColor( colorString );
      if( color.ok )
      {
        colorString = color.toRGB();
        var split = colorString.split( ',' );
        if( split.length == 3 )
        {
          for( var i = 0; i < split.length; i++ )
          {
            split[i] = split[i].gsub( '[a-z,A-Z,\(,\)]+', '' );
            split[i] = 255 - parseInt( split[i] );
          }

          newColor = 'rgb( ' + split.join( ', ' ) + ')';
        }
      }
    }

    return newColor;
  }

}














