/*******************************************************************
*
* File    : animate.js
*
* Created : 2000/05/16
*
* Author  : Roy Whittle  (Roy@Whittle.com) www.Roy.Whittle.com
*
* Purpose : To create animated rollovers
*
* History
* Date         Version        Description
*
* 2000-05-16	2.0		I have been doing JavaScript for over 8 months
*					and have decided to start this project from
*					scratch using what I have learned.
*					Knowing now what is required for animated rollovers
*					I have re-done the state transition diagram to
*					come up with a more robust design.
***********************************************************************/
/*** Create some global variables ***/
var AnimationRunning = false; /*** Global state of animation ***/
var FrameInterval    = 30;   /*** Time between frames in milliseconds   ***/

var AniImage = new Array();
var AniFrame = new Array();

var BaseHref="images/";
var Sep     = "/";
var Timer   = null;

/***********************************************************
* Function   : ImageError
*
* Parameters : 
*              
* Description : If the image being loaded does not exist then
*               this function will report an error giving the
*               full URL of the image we are trying to load.
*              
***********************************************************/
function ImageError()
{
	alert("animate.js has detected an error\nImage not found\n" + this.src);
}
/***********************************************************
* Function   : CreateAnimationFrames
*
* Parameters : aniName - the name of the animation.
*              n       - number of frames in animation
*              ext     - the type of image (".GIF", ".JPG")
*              
* Description : Creates an object that can hold the current
*               images for the animation.
*               There must be 1 ".ext" file for every frame
*               of animation  and they must reside in a 
*               the directory "images/name/x.ext". 
*               E.g.
*                 "images/email/0.gif"
*                 "images/email/1.gif"
*                 ....
*                 "images/email/x.gif" //where x=(n-1);
***********************************************************/
function CreateAnimationFrames(aniName, n, ext)
{
	this.num_frames = n;
	for(var i=0 ; i<n ; i++)
	{
		this[i]=new Image();
		this[i].src = BaseHref + aniName + Sep + i + ext;

		this[i].onerror=ImageError;
	}
}
/***********************************************************
* Function   : CreateAnimatedImage
*
* Parameters : imgNname - the name of the image.
*              aniName  - the name of the animation effect
*                         to use with this image
*              
* Description : Creates an object that can hold the current
*               state of the animation for a particular image
* NOTE: imgName must match an image defined in the document
*	  BODY (e.g. <IMG SRC="xxx.ext" NAME="imgName">)
***********************************************************/
function CreateAnimatedImage(imgName, aniName)
{
	if(document.images)
	{
		this.img_name   = imgName;
		this.ani_name   = aniName;
		this.next_on    = null;
		this.next_off   = null;
		this.index      = 0;
		this.target_frame= 0;
		this.state      = "CLOSED";
		this.img	    = null;
	}
	
}

/*****************************************************************
* Function    : getImage
*
* Parameters : n - the name of the image to find
*		   d - the (window/layer) document
*
* Description : In ie - just get the doucument.image.
*		    In NS, if there are layers we recursively
*		    search them for the image.
*
*****************************************************************/
function getImage(n, d) 
{
	var img = d.images[n];
	if(!img && d.layers)  
		for(var i=0 ; !img && i<d.layers.length ; i++) 
			img=getImage(n,d.layers[i].document); 
	return img;
}

/*****************************************************************
* Function    : startAnimation
*
* Description : Set a timeout which will call the animate routine
*               and start the animation running
*****************************************************************/
function startAnimation()
{
	if(!AnimationRunning)
		Animate();
}
/*******************************************************************
*
* Function    : Animate
*
* Description : Each animation object has a state.
*               The states normally go as follows
*                   CLOSED->OPENING->OPEN
*                   OPEN->CLOSING->CLOSED.
*               When a turn_on() event is received, an image in the
*               CLOSED state is switched to the OPENING state until OPEN
*               is reached. When the turn_off() event is received an image
*               in the OPEN state is switched to the CLOSING state until
*               the CLOSED state is reached. 
*
*               The special cases are what happens when we get turn_off() when
*               in the middle of opening. In this case the path is :-
*               CLOSED->OPENING->OPEN_CLOSE->CLOSING->CLOSED.
*               in this way the image will fully "open" before it starts 
*               closing. This can be changed by always setting the state
*               to "CLOSING" when the turn_off() event is received.
*
*               If the button is "CLOSING" and the turn_on() event is
*               received and the new open animation is null or the same
*               then the state is set back to "OPENING and the
*               button will start opening again immediately.
*                 Otherwise the state is set to CLOSE_OPEN so the image
*               will get to the CLOSED state and start opening with the
*               new animation.
*
*******************************************************************/
function Animate()
{	
	AnimationRunning = false; /*** Are there more frames that need displaying? ***/

	for(var i in AniImage)
	{
		var b=AniImage[i];
		var a=AniFrame[b.ani_name];

		if(b.state == "OPENING")
		{
			/*** Increment the frame index - display the next frame ***/
			/*** when fully open, set state to "OPEN"               ***/
			if(++b.index < a.num_frames)
			{
				b.img.src=a[b.index].src;
				AnimationRunning = true;
			}
			else
			{
				b.index=a.num_frames-1;
				b.state = "OPEN";
			}
		}
		else if(b.state == "OPEN_CLOSE")
		{
			/*** Increment the frame index - display the next frame ***/
			/*** when fully open, set state to "CLOSING"            ***/
			if(++b.index < a.num_frames)
			{
				b.img.src=a[b.index].src;
			}
			else
			{
				if(b.next_off)
				{
					b.ani_name=b.next_off;
					a=AniFrame[b.ani_name];
					b.next_off=null;
				}
				b.index=a.num_frames-1;
				b.state = "CLOSING";
			}
			AnimationRunning = true;
		}
		else if(b.state == "CLOSING")
		{
			/*** Decrement the frame index - display the next frame ***/
			/*** when fully closed, set state to "CLOSED"           ***/
			if(--b.index >= 0)
			{
				b.img.src=a[b.index].src;
				AnimationRunning = true;
			}
			else
			{
				b.index=0;
				b.state = "CLOSED";
			}
		}
		else if(b.state == "CLOSE_OPEN")
		{
			/*** Decrement the frame index - display the next frame ***/
			/*** when fully closed, set state to "OPENING"           ***/
			if(--b.index >= 0)
			{
				b.img.src=a[b.index].src;
			}
			else
			{
				b.index=0;
				b.ani_name=b.next_on;
				b.state = "OPENING";
			}
			AnimationRunning = true;
		}
		else if(b.state == "ROTATE_UP")
		{
			/*** Increment the frame index - display the next frame ***/
			/*** when target reached, set state to "CLOSED"        ***/
			if(b.index != b.target_frame)
			{
				if(++b.index == a.num_frames)
					b.index = 0;
				b.img.src=a[b.index].src;
				AnimationRunning = true;
			}
			else
				b.state = "CLOSED";
		}
		else if(b.state == "ROTATE_DOWN")
		{
			/*** Decrement the frame index - display the next frame ***/
			/*** when target reached, set state to "CLOSED"        ***/
			if(b.index != b.target_frame)
			{
				if(--b.index < 0)
					b.index = a.num_frames-1;
				b.img.src=a[b.index].src;
				AnimationRunning = true;
			}
			else
				b.state = "CLOSED";
		}
	}
	/*** Check to see if we need to animate any more frames. ***/
	if(AnimationRunning)
	{
		if(!Timer)
			Timer=setInterval("Animate()",FrameInterval);
	}
	else
	{
		clearInterval(Timer);
		Timer=null;
	}
}
/***********************************************************
* Function   : ErrorCheck
*
* Parameters : funcName - the name of the function that called this one
*              imgName  - The name of the image being animated
*              aniName  - (optional) the animation being used.
*              
* Description : This function checks that all the required
*               objects that make up animated RollOvers
*               have been defined. It will report any errors
*               detected. This function will also search for
*		    the corresponding document image by calling getImage().
*              
***********************************************************/
function ErrorCheck(funcName, imgName, aniName)
{
	var err_str="";

	if(AniImage[imgName]==null)
		err_str += "Error	: AnimatedImage \"" + imgName + "\" not defined\n";
	else
	{
		var b=AniImage[imgName];
		if(b.img == null)
			b.img=getImage(imgName, document);

		if(b.img==null)
			err_str += "Error	: Document <IMG NAME=\"" + imgName + "\"> not defined\n";

		/*** Check the AnimationFrames(b.ani_name, n, ext) has been defined ***/
		if(AniFrame[b.ani_name]==null)
			err_str += "Error	: AnimationFrames \"" + b.ani_name + "\" not defined\n";

	}

	if(aniName)
		/*** Check the AnimationFrames(aniName, n, ext) has been defined ***/
		if(AniFrame[aniName]==null)
			err_str += "Error	: AnimationFrames \"" + aniName + "\" not defined\n";

	if(err_str)
	{
		var extra = aniName ? ("\",\"" + aniName + "\")") : ("\")");
		alert("animate.js has detected an error\n       --------------------------------\n"
		    + err_str
		    + "Function	: " + funcName + "(\"" + imgName + extra);
	
		return true;
	}

	return false;
}
/************* OBJECT CONSTRUCTORS ******************************/
/***********************************************************
* Function   : AnimatedImage
*
* Parameters : imgName - the name of the image.
*              aniName - the name of the animation effect
*                     to use with this image
*              
* Description : Creates an object to hold the current state of
*		    the animation and stores it in the AniImage array.
***********************************************************/
function AnimatedImage(imgName, aniName)
{
	AniImage[ imgName ] = new CreateAnimatedImage( imgName, aniName);

	if(AniFrame[aniName]==null)
		alert("animate.js has detected a possible error\n       --------------------------------\n"
		    + "Error	: AnimationFrames \"" + aniName + "\" not defined\n"
		    + "Function	: AnimatedImage(\"" + imgName + "\",\"" + aniName + "\")");

}
/***********************************************************
* Function   : AnimationFrames
*
* Parameters : aniName - the name of the animation effect
*              n    - number of frames in animation
*              ext  - the type of image (".GIF", ".JPG")
*              
* Description : Creates an object to hold all the frames
*               for an animation and stores it in the AniFrames array.
***********************************************************/
function AnimationFrames(aniName, n, ext)
{
	/*** Only download this animation if we don't already have it ***/
	if(AniFrame[aniName] == null)
		AniFrame[ aniName ]= new CreateAnimationFrames(aniName, n, ext);
	else
//		alert(aniName + " already defined");
		;
}
/***********************************************************
* Function   : AnimatedGif AnimatedJpg
*
* Parameters : name - the name of the image.
*              n    - number of frames in animation
*              
* Description : These are a couple of helper functions to
*               help create simple animations.
*
***********************************************************/
function AnimatedGif(name, n)
{
	AnimationFrames(name, n, ".gif");
	AnimatedImage( name, name);
}
function AnimatedJpg(name, n)
{
	AnimationFrames(name, n, ".jpg");
	AnimatedImage( name, name);
}
/*************** ANIMATION METHODS **************************************/
/*****************************************************************
*
* Function   : turn_on
*
* Parameters : ingName - string containing the name of the
*                        image to start animating.
*		   aniName - optional, animation to use to open.
*
* Description: Checks that the imgName is in a valid state to
*              start "OPENING". If it is it sets the state to
*              "OPENING" and calls startAnimation.             
*
*****************************************************************/
function turn_on(imgName, aniName)
{
	if(!ErrorCheck("turn_on", imgName, aniName))
	{
		var b=AniImage[ imgName ];

		if(b.state == "CLOSED" )
		{
			b.state = "OPENING";
			if(aniName)
				b.ani_name=aniName;
			startAnimation();
		}
		else if ( b.state == "OPEN_CLOSE"
			||  b.state == "CLOSING" 
			||  b.state == "CLOSE_OPEN") 
		{
			if(!aniName || b.ani_name==aniName)
				b.state = "OPENING";
			else
			{
				b.next_on=aniName;
				b.state = "CLOSE_OPEN";
			}
		}
		/*** Special effect, can only happen in forced situations ***/
		/*** Hopefully this is described in the manual ***/
		else if( b.state == "OPENING"
			|| b.state == "OPEN")
		{
			if(aniName && b.ani_name != aniName)
			{
				b.ani_name=aniName;
				b.index=0;
				b.state="OPENING";
				startAnimation();
			}
		}
	}
}
/*****************************************************************
*
* Function   : turn_off
*
* Parameters : imgName - string containing the name of the
*                        image to start reverse animating.
*
* Description: Checks that the imgName is in a valid state to
*              start "CLOSING". If it is it sets the state to
*              "CLOSING" and calls startAnimation.             
*
*****************************************************************/
function turn_off(imgName, aniName)
{
	if(!ErrorCheck("turn_off", imgName, aniName))
	{
		var b=AniImage[ imgName ];

		if( b.state == "OPEN")		
		{
			if(aniName)
			{
				b.ani_name=aniName;
				b.index=AniFrame[aniName].num_frames-1;
			}
			b.state = "CLOSING";
			startAnimation();
		}
		else if(b.state == "CLOSE_OPEN")
		{
			b.next_off=null;
			b.state="CLOSING"
		}
		else if( b.state == "OPENING" )
		{
			b.next_off = aniName;
			b.state = "OPEN_CLOSE";
		}
	}
}
/******************************* LOOPING ANIMATION METHODS ****************/
/*****************************************************************
*
* Function   : animate_to
*
* Parameters : imgName - string containing the name of the
*                        image to start animating.
*              frameNo - the target frame
*
* Description: Determines the shortest animation path to the
*		   target frame. sets the state and calls startAnimation.             
*
*****************************************************************/
function animate_to(frameNo, imgName)
{
	if(!ErrorCheck("animate_to", imgName) )
	{
		var b = AniImage[ imgName  ];
		var a = AniFrame[ b.ani_name];
		var s = frameNo - b.index;

		b.target_frame = frameNo;

		if(Math.abs(s) > (a.num_frames/2))
		{
			if(s < 0)
				b.state = "ROTATE_UP";
			else
				b.state = "ROTATE_DOWN";
		}
		else
		{
			if(s > 0)
				b.state = "ROTATE_UP";
			else
				b.state = "ROTATE_DOWN";
		}
		startAnimation();
	}
}
/*****************************************************************
*
* Function   : animate_upto
*
* Parameters : imgName - string containing the name of the
*                        image to start animating.
*              frameNo - the target frame
*
* Description: Sets the state to ROTATE_UP calls startAnimation.             
*
*****************************************************************/
function animate_upto(frameNo, imgName)
{
	if(!ErrorCheck("animate_upto", imgName) )
	{
		var b=AniImage[ imgName ];

		b.target_frame = frameNo;
		b.state       = "ROTATE_UP";

		startAnimation();
	}
}
/*****************************************************************
*
* Function   : animate_downto
*
* Parameters : imgName - string containing the name of the
*                        image to start animating.
*              frameNo - the target frame
*
* Description: Sets the state to ROTATE_DOWN calls startAnimation.             
*
*****************************************************************/
function animate_downto(frameNo, imgName)
{
	if(!ErrorCheck("animate_downto", imgName) )
	{
		var b=AniImage[ imgName ];

		b.target_frame = frameNo;
		b.state       = "ROTATE_DOWN";
		startAnimation();
	}
}
