/*******************************************************************
*
* File    : JSFX_AnimatedRollovers.js © JavaScript-FX.com
*
* Created : 2000/05/16
*
* Author  : Roy Whittle www.Roy.Whittle.com
*           
* Purpose : To create animated rollovers
*
* History
* Date         Version        Description
*
* 2001-09-23	3.0		Completely re-structured to the JSFX naming
*					space and to use a more similar interface
*					to SimpleRollovers and FadingRollovers
* 2001-01-29	3.1		Make "ani" a seperate object of the "img"
* 2002-02-21	3.2		Correct a bug in JSFX.findImg
* 2002-02-21	3.3		When creating vers 3.1 I broke NS4 nested layers
*					This version fixes it by recursing the AnimateRollover function.
***********************************************************************/
var FrameInterval    = 30;	/*** Time between frames in milliseconds   ***/
var BaseHref="images/";		/*** This is now only required for AnimagedGif ***/

/*** Create some global variables ***/
if(!window.JSFX)
	JSFX=new Object();
JSFX.AnimationRunning = false; /*** Global state of animation ***/
JSFX.aniTimer = null;
JSFX.AniRollovers = new Array();

/***********************************************************
* Function   : AniImageError
*
* 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.
*              
***********************************************************/
JSFX.aniImageError = function()
{
	alert("JSFX_AnimatedRollover.js has detected an error\nImage not found\n" + this.src);
}
/***********************************************************
* Function   : CreateAnimationFrames
*
* Parameters : imgName - the path to the animation frames.
*              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 "imgName" + x + ".ext"
*               E.g.
*                 "images/email/0.gif"
*                 "images/email/1.gif"
*                 ....
*                 "images/email/x.gif" //where x=(n-1);
***********************************************************/
JSFX.CreateAnimationFrames = function(imgName, n, ext)
{
	this.num_frames = n;
	for(var i=0 ; i<n ; i++)
	{
		this[i]=new Image();
		this[i].src = imgName + i + ext;

		this[i].onerror=JSFX.aniImageError;
	}
}
/***********************************************************
* Function   : initAnimatedImage
*
* Parameters : img      - the image in the document.
*              aniName  - the name of the animation effect
*                         to use with this image
*              
* Description : Initialises an image object so that it can hold the current
*               state of the animation for a particular the image
* NOTE: imgName must match an image defined in the document
*	  BODY (e.g. <IMG SRC="xxx.ext" NAME="imgName">)
***********************************************************/
JSFX.initAnimatedImage = function(img, ani)
{
	img.ani = new Object();
	img.ani.name  	 = ani;
	img.ani.next_on    = ani;
	img.ani.next_off   = ani;
	img.ani.index      = 0;
	img.ani.target_frame= 0;
	img.ani.state      = "CLOSED";
}
/***********************************************************
* Function   : AnimatedRollover
*
* Parameters : aniName - the name of the animation effect
*              imgName - the path to the images
*              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.
***********************************************************/
JSFX.AnimatedRollover = function(aniName, imgName, n, ext)
{
	/*** Only download this animation if we don't already have it ***/
	if(JSFX.AniRollovers[aniName] == null)
		JSFX.AniRollovers[ aniName ]= new JSFX.CreateAnimationFrames(imgName, n, ext);
}
/***********************************************************
* 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.
*
***********************************************************/
JSFX.AnimatedGif = function(name, n)
{
	JSFX.AnimatedRollover(name, BaseHref + name + "/", n, ".gif");
}
JSFX.AnimatedJpg = function(name, n)
{
	JSFX.AnimatedRollover(name, BaseHref + name + "/", n, ".jpg");
}

/*******************************************************************
*
* Function    : getImg
*
* Description : In Netscape 4 images could be in layers so we might
*		    have to recurse the layers to find the image
*
*****************************************************************/
JSFX.getImg = function(n, d) 
{
	var img = d.images[n];
	if(!img && d.layers)
		for(var i=0 ; !img && i<d.layers.length ; i++)
			img=JSFX.getImg(n,d.layers[i].document);
	return img;
}
/*******************************************************************
*
* Function    : findImg
*
* Description : gets the image from the document and reports an
*		    error if it cannot find it.
*
*****************************************************************/
JSFX.findImg = function(n, d) 
{
	var img = JSFX.getImg(n, d);

	/*** Stop emails because the image was named incorrectly ***/
	if(!img)
	{
		alert("JSFX.findImg - An Error has been detected\n"
			+ "----------------------------------\n"
			+ "You must define an image in your document\n"
			+ "<IMG SRC=\"your_image.ext\" NAME=\""+n+"\">\n"
			+ "(check the NAME= attribute of your images)");

		return(new Image());
	}

	return img;
}
/*******************************************************************
*
* Function    : aniRolloverExists
*
* Description : A lot of users make mistakes like creating a rollover
*			AnimatedRollover("home", "images/home", 6, ".gif"
*		    and then try to us it like this
*			JSFX.aniOn("hom");
*
*****************************************************************/
JSFX.aniRolloverError = function(aniName)
{
	if(JSFX.AniRollovers[aniName])
		return false;
	else
		alert("JSFX.AnimatedRollovers - An Error has been detected\n"
			+ "----------------------------------\n"
			+ "You must define a JSFX.AnimatedRollover in your document\n"
			+ "JSFX.AnimatedRollover(\""+aniName+"\",\"your_img\", n,\".gif\")\n"
			+ "(check the spelling of your JSFX.AnimatedRollovers)");

	return true;
}

/*****************************************************************
* Function    : startAnimation
*
* Description : Set a timeout which will call the animate routine
*               and start the animation running
*****************************************************************/
JSFX.startAnimation = function()
{
	if(!JSFX.AnimationRunning)
		JSFX.AnimateRollovers();
}
/*****************************************************************
*
* Function   : aniOn
*
* Parameters : imgName - 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.             
*
*****************************************************************/
JSFX.aniOn = function(imgName, aniName)
{
	if(aniName == null)
		aniName = imgName;

	var img=JSFX.findImg(imgName, document);

	if(JSFX.aniRolloverError(aniName))
		return;

	if(img.ani == null)
		JSFX.initAnimatedImage(img, aniName);

	if(img.ani.state == "CLOSED" )
	{
		img.ani.state = "OPENING";
		img.ani.name  = aniName;
		JSFX.startAnimation();
	}
	else if ( img.ani.state == "OPEN_CLOSE"
		||  img.ani.state == "CLOSING" 
		||  img.ani.state == "CLOSE_OPEN") 
	{
		if(img.ani.name==aniName)
			img.ani.state = "OPENING";
		else
		{
			img.ani.next_on=aniName;
			img.ani.state = "CLOSE_OPEN";
		}
	}
	/*** Normally you can only set an opening image to OPEN ***/
	/*** However, here we force an already OPEN image to OPENING if we supply a different animation ***/
	else if( img.ani.state == "OPENING"
		|| img.ani.state == "OPEN")
	{
		if(aniName && img.ani.name != aniName)
		{
			img.ani.name=aniName;
			img.ani.index=0;
			img.ani.state="OPENING";
			JSFX.startAnimation();
		}
	}
}
/*****************************************************************
*
* Function   : aniOff
*
* Parameters : imgName - string containing the name of the
*                        image to start reverse animating.
*		   aniName - If you really want you can use a completely
*				 different "off" animation.
* 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.             
*
*****************************************************************/
JSFX.aniOff = function(imgName, aniName)
{
	var img=JSFX.findImg(imgName, document);

	if(aniName == null)
		aniName = img.ani.name;

	if(JSFX.aniRolloverError(aniName))
		return;

	if( img.ani.state == "OPEN")		
	{
		img.ani.name=aniName;
		img.ani.index=JSFX.AniRollovers[aniName].num_frames-1;
		img.ani.state = "CLOSING";
		JSFX.startAnimation();
	}
	else if(img.ani.state == "CLOSE_OPEN")
	{
		img.ani.next_off=null;
		img.ani.state="CLOSING"
	}
	else if( img.ani.state == "OPENING" )
	{
		img.ani.next_off = aniName;
		img.ani.state = "OPEN_CLOSE";
	}
}
/*******************************************************************
*
* Function    : AnimateRollovers
*
* Description : Each image object is given 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.
*
*******************************************************************/
JSFX.AnimateRollovers = function(d)
{	
	if(d==null)
	{
		d=document;
		JSFX.AnimationRunning = false; /*** Are there more frames that need displaying? ***/
	}

	for(var i=0 ; i<d.images.length ; i++)
	{
		var img = d.images[i];
		if(img.ani)
		{
			var ani=JSFX.AniRollovers[img.ani.name];
			if(img.ani.state == "OPENING")
			{
				/*** Increment the frame index - display the next frame ***/
				/*** when fully open, set state to "OPEN"               ***/
				if(++img.ani.index < ani.num_frames)
				{
					img.src=ani[img.ani.index].src;
					JSFX.AnimationRunning = true;
				}
				else
				{
					img.ani.index=ani.num_frames-1;
					img.ani.state = "OPEN";
				}
			}
			else if(img.ani.state == "OPEN_CLOSE")
			{
				/*** Increment the frame index - display the next frame ***/
				/*** when fully open, set state to "CLOSING"            ***/
				if(++img.ani.index < ani.num_frames)
				{
					img.src=ani[img.ani.index].src;
				}
				else
				{
					if(img.ani.next_off)
					{
						img.ani.name=img.ani.next_off;
						ani=JSFX.AniRollovers[img.ani.name];
						img.ani.next_off=null;
					}
					img.ani.index=ani.num_frames-1;
					img.ani.state = "CLOSING";
				}
				JSFX.AnimationRunning = true;
			}
			else if(img.ani.state == "CLOSING")
			{
				/*** Decrement the frame index - display the next frame ***/
				/*** when fully closed, set state to "CLOSED"           ***/
				if(--img.ani.index >= 0)
				{
					img.src=ani[img.ani.index].src;
					JSFX.AnimationRunning = true;
				}
				else
				{
					img.ani.index=0;
					img.ani.state = "CLOSED";
				}
			}
			else if(img.ani.state == "CLOSE_OPEN")
			{
				/*** Decrement the frame index - display the next frame ***/
				/*** when fully closed, set state to "OPENING"           ***/
				if(--img.ani.index >= 0)
				{
					img.src=ani[img.ani.index].src;
				}
				else
				{
					img.ani.index=0;
					img.ani.name=img.ani.next_on;
					img.ani.state = "OPENING";
				}
				JSFX.AnimationRunning = true;
			}
			else if(img.ani.state == "ROTATE_UP")
			{
				/*** Increment the frame index - display the next frame ***/
				/*** when target reached, set state to "CLOSED"        ***/
				if(img.ani.index != img.ani.target_frame)
				{
					if(++img.ani.index == ani.num_frames)
						img.ani.index = 0;
					img.src=ani[img.ani.index].src;
					JSFX.AnimationRunning = true;
				}
				else
					img.ani.state = "CLOSED";
			}
			else if(img.ani.state == "ROTATE_DOWN")
			{
				/*** Decrement the frame index - display the next frame ***/
				/*** when target reached, set state to "CLOSED"        ***/
				if(img.ani.index != img.ani.target_frame)
				{
					if(--img.ani.index < 0)
						img.ani.index = ani.num_frames-1;
					img.src=ani[img.ani.index].src;
					JSFX.AnimationRunning = true;
				}
				else
					img.ani.state = "CLOSED";
			}
		}
	}

	//NS4 recurse all layers
	if(document.layers)
	{
		for(var l=0 ; l<d.layers.length ; l++)
			JSFX.AnimateRollovers(d.layers[l].document);

		//If we are in a sub layer - no need to check the timer
		if(d!=document)
			return;
	}

	/*** Check to see if we need to animate any more frames. ***/
	if(JSFX.AnimationRunning)
	{
		if(!JSFX.aniTimer)
			JSFX.aniTimer=setInterval("JSFX.AnimateRollovers()",FrameInterval);
	}
	else
	{
		clearInterval(JSFX.aniTimer);
		JSFX.aniTimer=null;
	}
}
/*****************************************************************
*
* Function   : aniTo
*
* 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.             
*
*****************************************************************/
JSFX.aniTo = function(frameNo, imgName, aniName)
{
	var img=JSFX.findImg(imgName, document);

	if(aniName == null)
		aniName = imgName;

	if(JSFX.aniRolloverError(aniName))
		return;

	if(img.ani == null)
		JSFX.initAnimatedImage(img, aniName);

	var diff = frameNo - img.ani.index;

	img.ani.target_frame = frameNo;
	var ani=JSFX.AniRollovers[img.ani.name];

	if(Math.abs(diff) > (ani.num_frames/2))
	{
		if(diff < 0)
			img.ani.state = "ROTATE_UP";
		else
			img.ani.state = "ROTATE_DOWN";
	}
	else
	{
		if(diff > 0)
			img.ani.state = "ROTATE_UP";
		else
			img.ani.state = "ROTATE_DOWN";
	}
	JSFX.startAnimation();
}
/*****************************************************************
*
* Function   : aniUpto
*
* 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.             
*
*****************************************************************/
JSFX.aniUpto = function(frameNo, imgName, aniName)
{
	var img=JSFX.findImg(imgName, document);

	if(aniName == null)
		aniName = imgName;

	if(JSFX.aniRolloverError(aniName))
		return;

	if(img.ani == null)
		JSFX.initAnimatedImage(img, aniName);

	img.ani.target_frame = frameNo;
	img.ani.state       = "ROTATE_UP";
	JSFX.startAnimation();
}
/*****************************************************************
*
* Function   : aniDownto
*
* 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.             
*
*****************************************************************/
JSFX.aniDownto = function(frameNo, imgName, aniName)
{
	var img=JSFX.findImg(imgName, document);

	if(aniName == null)
		aniName = imgName;

	if(JSFX.aniRolloverError(aniName))
		return;

	var img=JSFX.findImg(imgName, document);

	if(img.ani == null)
		JSFX.initAnimatedImage(img, aniName);

	img.ani.target_frame = frameNo;
	img.ani.state       = "ROTATE_DOWN";
	JSFX.startAnimation();
}
/*****************************************************************
*
* Function   : aniStop
*
* Parameters : imgName - string containing the name of the
*                        image to stop animating.
*
* Description: Sets the state to CLOSED thus stopping the animation
*
*****************************************************************/
JSFX.aniStop = function(imgName)
{
	var img=JSFX.findImg(imgName, document);
	img.ani.state = "CLOSED";
}


