//==================================================================
// Animations.js
// Library to animate web elements
//==================================================================
// DESCRIPTION:
// Javascript library that allows animate web elments easily.
// Animations are treated as objects that can be instaciated.
// Timelines can be configured by concatenating animations.
// COMPANY: Tecosoft
// AUTHOR: Joaquín Almansa Sáez
// DATE: May 2002
//
// Future enhancements:
// - Investigate if it is possible to implement inheritance
// - Recalculate las movement step so the final coordenate is exact  
//==================================================================


//------------------------------------------------------------------
// CLASS: makeLinearAnimation
//------------------------------------------------------------------
// DESCRIPTION:
// Animation class. After proper initialization it can be used to
// move web elements from one point to another (linear animation).
// It is very important that web elements have postion:absolute.
// If not, they will not move at all !!
// 
// METHODS:
//  - makeLinearAnimation: class constructor
//    Parameters:
//     element           -> html element (its id or name, depending on the elment)
//     xInitial          -> initial x coordinate (left html attribute)
//     yInitial          -> initial y coordinate (top html attribute)
//     xFinal            -> final x coordinate (left html attribute)
//     yFinal            -> final y coordinate (top html attribute)
//     pixelsPerStep     -> amount of steps in the animation (the more steps the more smooth)
//     delayBetweenSteps -> miliseconds between every step
//     hideWhenFinished  -> if true, animation hides when finished
//     delayBeforeHide   -> miliseconds before hiding animation. Only applies if hideWhenFinished is true
//     objectName        -> it must be the same name of the instanciated element. If not, animation will not work.
//   - startAnimation: starts the animation. Element is automatically made visible when animation is started.
//     Parameters: none
//   - hideElment: hides the element (changes its visibility to hidden)
//
// REQUIRES: no other libraries are requiered
//
// USSAGE SAMPLE:
// Next sample moves an image from (0,0) to (100,100) by steps of one pixel and 1 milisecond delay
// between pixels. The image remains visible after animation.
// ...
//  <img id="myImage" src="image.gif" style="position:absolute">
//  <script>
//  myAnimation = makeLinearAnimation(myImage, 0, 0, 100, 100, 1, 1, false, 0, "myAnimation");
//  myAnimation.startAnimation();
//  </script>
//
// COMPANY: Tecosoft
// AUTHOR: Joaquín Almansa Sáez
// DATE: May 2002
//------------------------------------------------------------------

function makeLinearAnimation(element, xInitial, yInitial, xFinal, yFinal, pixelsPerStep, delayBetweenSteps, hideWhenFinished, delayBeforeHide, objectName){

	// Animation parametes are copied inside object attribute variables
	this.element = element;
	this.xInitial = xInitial;
	this.yInitial = yInitial;
	this.xFinal = xFinal;
	this.yFinal = yFinal;
	this.pixelsPerStep = pixelsPerStep;
	this.delayBetweenSteps = delayBetweenSteps;
	this.hideWhenFinished = hideWhenFinished;	
	this.delayBeforeHide = delayBeforeHide;
	this.objectName = objectName;
	
	// Linear animation parameters are calculated for further ussage
	var distance = Math.sqrt( Math.pow(xInitial - xFinal, 2) + Math.pow(yInitial - yFinal, 2) );
	this.totalSteps = Math.round(distance / pixelsPerStep);
	this.xPixelsPerStep = (xFinal - xInitial) / this.totalSteps;
	this.yPixelsPerStep = (yFinal - yInitial) / this.totalSteps;

	// Current step is initialized to zero
	this.currentStep = null;
	
	// Animation control parameters are also set
	this.animationRunning = false;
	this.animationFinished = false;
	this.currentStep = 0;
	this.timerId = null;
	
	this.updatePositition = function(){

		this.element.style.visibility = "visible";
		if(this.currentStep == this.totalSteps){
			clearInterval(this.timerId);
			this.animationFinished = true;
			this.animationRunning = false;
			if(this.hideWhenFinished)
				setInterval(this.objectName + "." + "hideElement()", this.delayBeforeHide);
		}
		else{
			this.element.style.top  = Math.round(this.xInitial + this.currentStep * this.xPixelsPerStep);
			this.element.style.left = Math.round(this.yInitial + this.currentStep * this.yPixelsPerStep);
			this.currentStep++;
		}
	};

	this.startAnimation = function(){
		this.currentStep = 0;
		this.animationRunning = true;
		this.animationFinished = false;
		this.timerId = setInterval(this.objectName + "." + "updatePositition()", this.delayBetweenSteps);
	};
	
	this.resetAnimation = function(){
		this.currentStep = 0;
		this.animationRunning = false;
		this.animationFinished = false;
		this.timerId = null;
	};
	
	this.hideElement = function(){
		this.element.style.visibility = 'hidden';
	};
}

//------------------------------------------------------------------
// CLASS: makeGrowingSizeAndLinearMovementAnimation
//------------------------------------------------------------------
// DESCRIPTION:
// Animation class. After proper initialization it can be used to
// move web elements from one point to another (linear animation)
// and change its size at the same time.
// 
// METHODS:
//  - makeLinearAnimation: class constructor
//    Parameters:
//     element           -> html element (its id or name, depending on the elment)
//     xInitial          -> initial x coordinate (left html attribute)
//     yInitial          -> initial y coordinate (top html attribute)
//     xInitial          -> initial x coordinate (left html attribute)
//     yInitial          -> initial y coordinate (top html attribute)
//     initialWidth      -> initial element width
//     initialHeight     -> initial element heigth
//     finalWidth        -> final element width
//     finalHeight       -> final element height
//     pixelsPerStep     -> amount of steps in the animation (the more steps the more smooth)
//     delayBetweenSteps -> miliseconds between every step
//     hideWhenFinished  -> if true, animation hides when finished
//     delayBeforeHide   -> miliseconds before hiding animation. Only applies if hideWhenFinished is true
//     objectName        -> it must be the same name of the instanciated element. If not, animation will not work.
//   - startAnimation: starts the animation. Element is automatically made visible when animation is started.
//     Parameters: none
//   - hideElment: hides the element (changes its visibility to hidden)
//
// COMPANY: Tecosoft
// AUTHOR: Joaquín Almansa Sáez
// DATE: May 2002
// Ussage sample:
// Next sample moves an image from (0,0) to (100,100) by steps of one pixel and 1 milisecond delay
// between pixels. The image remains visible after animation. Its size changes from 10x20 to 100x200.
// ...
// <img id="myImage" src="image.gif" style="position:absolute">
// <script>
// myAnimation = makeGrowingSizeAndLinearMovementAnimation(myImage, 0, 0, 100, 100, 10, 20, 100, 200, 1, 1, false, 0, "myAnimation");
// myAnimation.startAnimation();
// </script>
//
//------------------------------------------------------------------

function makeGrowingSizeAndLinearMovementAnimation(element, xInitial, yInitial, xFinal, yFinal, initialWidth, initialHeight, finalWidth, finalHeight, pixelsPerStep, delayBetweenSteps, hideWhenFinished, delayBeforeHide, objectName){

	// Animation parametes are copied inside object attribute variables
	this.element = element;
	this.xInitial = xInitial;
	this.yInitial = yInitial;
	this.xFinal = xFinal;
	this.yFinal = yFinal;
	this.initialWidth = initialWidth;
	this.finalWidth = finalWidth;	
	this.initialHeight = initialHeight;
	this.finalHeight = finalHeight;	
	this.pixelsPerStep = pixelsPerStep;
	this.delayBetweenSteps = delayBetweenSteps;
	this.hideWhenFinished = hideWhenFinished;	
	this.delayBeforeHide = delayBeforeHide;
	this.objectName = objectName;
	
	// Linear animation parameters are calculated for further ussage
	var distance = Math.sqrt( Math.pow(xInitial - xFinal, 2) + Math.pow(yInitial - yFinal, 2) );
	this.totalSteps = Math.round(distance / pixelsPerStep);
	this.xPixelsPerStep = (xFinal - xInitial) / this.totalSteps;
	this.yPixelsPerStep = (yFinal - yInitial) / this.totalSteps;
	this.widthIncrementPerStep = (finalWidth - initialWidth) / this.totalSteps;
	this.heightIncrementPerStep = (finalHeight - initialHeight) / this.totalSteps;	

	// Current step is initialized to zero
	this.currentStep = null;
	
	// Animation control parameters are also set
	this.animationRunning = false;
	this.animationFinished = false;
	this.currentStep = 0;
	this.timerId = null;
	
	this.updatePositition = function(){

		this.element.style.visibility = "visible";
		if(this.currentStep == this.totalSteps){
			clearInterval(this.timerId);
			this.animationFinished = true;
			this.animationRunning = false;
			if(this.hideWhenFinished)
				setInterval(this.objectName + "." + "hideElement()", this.delayBeforeHide);
		}
		else{
			this.element.style.top  = Math.round(this.xInitial + this.currentStep * this.xPixelsPerStep);
			this.element.style.left = Math.round(this.yInitial + this.currentStep * this.yPixelsPerStep);

			this.element.style.width = Math.round(this.initialWidth + this.currentStep * this.widthIncrementPerStep);
			this.element.style.height = Math.round(this.initialHeight + this.currentStep * this.heightIncrementPerStep);			
			this.currentStep++;
		}
	};

	this.startAnimation = function(){
		this.currentStep = 0;
		this.animationRunning = true;
		this.animationFinished = false;
		this.timerId = setInterval(this.objectName + "." + "updatePositition()", this.delayBetweenSteps);
	};
	
	this.resetAnimation = function(){
		this.currentStep = 0;
		this.animationRunning = false;
		this.animationFinished = false;
		this.timerId = null;
	};
	
	this.hideElement = function(){
		this.element.style.visibility = 'hidden';
	};
}

//------------------------------------------------------------------
// CLASS: makeAnimationController
//------------------------------------------------------------------
// DESCRIPTION:
// Animation controller object. Receives an array of animations as
// initialization parametes and exectues one animationafter the other. Looping
// is allowed for non-stop animations.
// 
// METHODS:
//  - makeAnimationController: class constructor
//    Parameters:
//     arrayOfAnimations -> array of objects that implement methods startAnimation and resetAnimation. Objects
//                          can be whichever as long as they implement this methods.
//     loop              -> if true, animation goes on and on ...
//     objectName        -> it must be the same name of the instanciated element. If not, animation will not work.
//   - startAnimation: starts the animation. Elements are automatically made visible when their animation is started.
//     Parameters: none
//
// COMPANY: Tecosoft
// AUTHOR: Joaquín Almansa Sáez
// DATE: May 2002
// Ussage sample:
// Next sample moves an image from (0,0) to (100,100) by steps of one pixel and 1 milisecond delay
// between pixels. Then, the element moves back from (100, 100) to (0,0).
// The animation goes on and on.
// ...
// <img id="myImage" src="image.gif" style="position:absolute">
// <script>
// myAnimation1 = makeLinearAnimation(myImage, 0, 0, 100, 100, 1, 1, false, 0, "myAnimation1");
// myAnimation2 = makeLinearAnimation(myImage, 100, 100, 0, 0, 1, 1, false, 0, "myAnimation2");
// myAnimationsArray = new Array();
// myAnimationsArray[0] = myAnimation1;
// myAnimationsArray[1] = myAnimation2;
// myAnimationController = new AnimationCotroller(myAnimation, true, "myAnimationController");
// myAnimationControlloer.startAnimation();
// </script>
//
//------------------------------------------------------------------

function makeAnimationController(arrayOfAnimations, loop, objectName){

	this.arrayOfAnimations = arrayOfAnimations;
	this.loop       = loop;
	this.objectName = objectName;
	this.timerId    = null;	

	this.controlAnimation = function(){
		
		for(var i=0; i < this.arrayOfAnimations.length; i++){
			if(this.arrayOfAnimations[i].animationRunning == true)
				// Whenever we find an animation running we do nothing
				return;
			else if(this.arrayOfAnimations[i].animationFinished == false){
				// If animation has not yet been started we start it and quit
				this.arrayOfAnimations[i].startAnimation();
				return;
			}
			// If animation is not running and has finished we look for next animation in the array
		}
		
		if(i == this.arrayOfAnimations.length){
			// If all animations have stopped and loop is enabled, we restart animation
			if(this.loop){
				// Control variables in all animations must be reset. Timer is stopped during
				// this process to be on the safe side.
				clearInterval(this.timerId);
				for(var j=0; j < this.arrayOfAnimations.length; j++)
					this.arrayOfAnimations[j].resetAnimation();
				this.arrayOfAnimations[0].startAnimation();
				this.timerId = setInterval(this.objectName + "." + "controlAnimation()", 10);
			}
			// If loop is not on we just stop the timer so animation is no longer executed
			// We also reset control variables so animation can be reexecuted
			else{
				clearInterval(this.timerId);
				for(var j=0; j < this.arrayOfAnimations.length; j++)
					this.arrayOfAnimations[j].resetAnimation();				
			}
		}
	};
	
	this.startAnimation = function(){
		this.arrayOfAnimations[0].startAnimation();
		this.timerId = setInterval(this.objectName + "." + "controlAnimation()", 10);
	};
}
