/*
*	ocImageRotator
*
*	This script allows you to add an image rotation to 
*	any image element on a webpage. The script will create 
*	an additional image on top of the image that the rotation 
*	is attached to. This requires the parent node to have a
*	styling of position:relative and the original image needs
*	to be set to position:absolute. Otherwise the overlay will
*	not work.
*
*	The Image Rotator object is called on window.onload via
*	unobtrusive javascript. Therefor you dont need any event
*	handlers in your html markup. Simply tell window.onload to
*	execute the function addImageRotator() below.
*
*	This code is copyright 2006 by Omnia Computing and published
*	under a Creative Commons License. You are free to to copy, 
*	distribute, display, and perform the work. You must give 
*	the original author and Omnia Computing credit. You may not
*	use this code for commercial purposes. If you alter, transform, 
*	or build upon this work, you may distribute the resulting work 
*	only under a licence identical to this one.
*
*	This code has been tested and works in InternetExplorer 6 and 
*	Firefox 1.5.0.4. It should work from Internet Explorer 5.5+ and
*	Firefox 1.0+ and all browsers that support the style object and
*	the node functions.
*
*	Send bug reports, inquiries and comments to go@omnia-computing.de
*
*	@copyright 	http://creativecommons.org/licenses/by-nc-sa/2.0/de/
*	@url 		www.omnia-computing.de
*   @author		Gordon Oheim, go@omnia-computing.de		
*	@version	1.0.0
*/


	/*
	*	Initializes and starts the ImageRotator object
	*
	*	The following function is essential to make the Image
	*	Rotator run. You will need to reference this function
	*	on window.onload. For a more comfortable window.onload
	*	method, see Simon Willisons addLoadEvent.
	*
	*	@url 		http://simon.incutio.com/archive/2004/05/26/addLoadEvent
	*	@version	1.0.0
	*	@param 		string elImgId - the id of the img element on your page
	*	@param		string[] imgList - the list of filenames to display at runtime
	*/
	function addImageRotator(elImgId, imgList) {
		
		try {
			// create new ImageRotator object
			objImgRotator = new ImageRotator();
	
			// tell the new Image Rotator object what to do
			with(objImgRotator) {
	
				// reference the HTML Image Object in the Rotator
				orgImage = document.getElementById(elImgId);
	
				// create the image on top of the original image
				dynImage = createOverlay();
	
				// preload your images from the string array
				images = preloadImages(imgList);
	
				// start the rotation
				start();
			}
		}
		catch(error) {
			// do nothing
		}
	}	
	
	/*
	*	class definition for the ImageRotator object
	*
	*	@version 1.0.0
	*/
	function ImageRotator() {

		/*
		*	the original img element in your markup
		*	
		*	@var HTMLImageObject
		*/
		this.orgImage = '';

		/*
		*	the img element that will overlay this.orgImage
		*	
		*	@var HTMLImageObject
		*/
		this.dynImage = '';
		
		/*
		*	the list of images
		*
		*	the array is empty if no preloading was executed
		*	in addImageRotator. If preloading was executed this
		*	array will hold HTMLImageObjects of the image files
		*	passed to addImageRotator at startup.
		*	
		*	@var HTMLImageObject[] 
		*/		
		this.images = new Array();

		/*
		*	the time for fades and between images
		*
		*	this number is a factor to various code segments, 
		*	such as calculating maximum fade time and timeouts
		*	an image rotation. changing it might work unexpected.
		*	setting the value above 100 will break the image
		*	rotation because the image change will take place
		*	before the fade effect is done.
		*
		*	timeout between image cycle: ms = delay*100
		*	time given for fade effect: (ms += n*delay); 1 to delay
		*	
		*	@var int - default 50
		*/		
		this.delay = 50;
		
		/*
		*	make functions available in ImageRotator object
		*/
		this.createOverlay = createOverlay;
		this.preloadImages = preloadImages;
		this.cycleImage = cycleImage;
		this.fade = fade;
		this.setOpacity = setOpacity;
		this.start = start;
	}

	// function reference following now

	/*
	*	creates an image on top of the original image
	*	
	*	this function will clone the original HTMLImageObject
	*	the ImageRotator is attached to and append it to the
	*	original HTMLImageObjects parent node.
	*
	*	You will need a div around your original image set to
	*	position:relative for this to work. You will also need to
	*	to set the img elements within that div to position:absolute
	*	to allow for the overlay to render correctly.
	*	The new image will have a class attribute of "dynImageOverlay",
	*	an alt attribute of "dynamically loaded image" and an id
	*	attribute "dyn" plus the original image tag id.
	*
	*	@version 	1.0.0
	*	@returns	HTMLImageObject
	*/
	function createOverlay() {

		// clones the original HTMLImageObject
		dynImage = this.orgImage.cloneNode(true);

		// setting some element attributes
		with (dynImage) {
			setAttribute('id', 'dyn' + this.orgImage.id);
			setAttribute('class', 'dynImageOverlay');
			setAttribute('alt', 'dynamically loaded image');
		}

		// appending the new HTMLImageObject to parent node
		this.orgImage.parentNode.appendChild(dynImage);

		// returning the object for further reference
		return dynImage;
	}

	/*
	*	loads the to-be-rotated images into client cache
	*	
	*	this is a simply preloader that takes a list of strings
	*	from an array and converts them into HTMLImageObjects.
	*	The return value is an array of HTMLImageObjects.
	*
	*	@version 	1.0.0
	*	@returns	HTMLImageObject[]
	*	@param		string[]
	*/
	function preloadImages(imgList) {
	
		// read all strings from the passed array
		for(n = 0; n < imgList.length; n++) {

			// create a new HTMLImageObject object
			elImg = new Image();
			
			// set the src to the current filename
			elImg.src = imgList[n];
			
			// add the new object to ImageRotation.images[]
			this.images[n] = elImg;
		}
		// return the array of HTMLImageObjects
		return this.images;
	}

	/*
	*	starts the image rotation
	*
	*	@version	1.0.0
	*/
	function start() {
		this.cycleImage(0, this, true);
	}

	// ----------- now the trickier functions -----------

	/*
	*	changes the src of either the original or overlay image.
	*
	*	the trickiest function in the setup due to the multiple
	*	setTimeout calls. setTimeout does not halt any code that
	*	is executed after it and it executes in Window scope.
	*	This leads to two problems:
	*	Problem A: the fade effect will start, but since any 
	*	following code is not halted until the fade is done, the
	*	image will be rotated before it is done.
	*	Problem B: we lose all reference to our ImageRotation object
	*	when we try to work with the 'this' keyword.
	*
	*	Through a lot of trial and error, we got around this.
	*	
	*	The logic behind this function is as follows:
	*	1. change the overlay image
	*	2. fade the overlay image
	*	3. call the function again in due time for the fade to end
	*	4. overlay overlays original now
	*	5. timeout starts second execution
	*	6. original image is changed under overlay
	*	7. overlay is made invisible
	*	8. wait some time again
	*	9. the original image is visible for the timeout duration
	*	10. on the third execution we start over at step 1.
	*
	*	@version 1.0.0
	*	@param	int i - index in ImageRotator.images
	*	@param 	ImageRotator obj - self reference for keeping the scope
	*	@param	boolean isDyn - boolean that determines which image to change
	*/
	function cycleImage(i, obj, isDyn) {
		
	// if block 1 - image determination
		
		// check if the overlay image needs to be rotated
		if (isDyn == true) {

			// setting to be rotated image to the overlay image
			elImg = obj.dynImage;

			// overlay images are always ahead of the base image
			i = i+1;
		}
		
		// if its not the overlay image it must be the original
		else {
			
			// setting to be rotated image to the original image
			elImg = obj.orgImage;
			
			// make the overlay image invisible to prevent flickering
			obj.setOpacity(0, obj);
		}

	// if block 2 - index reset test
		
		// check if param i exists in ImageRotator.images
		if (obj.images[i]){
		
			// if i exists then change the determined image to it
			elImg.src = obj.images[i].src;
		}
		// if param i does no exist in ImageRotator.images
		else {
			
			// we must be at the end of the image list.
			i = 0;
			
			//  start over with the first image in the list.
			elImg.src = obj.images[i].src;
		}
	
	// if block 3 - fade on every other execution
		
		// we only fade if the overlay was changed
		if (isDyn == true) {
			
			// start the fading effect
			obj.fade(0, obj);
			
			// set isDyn to false so the original will be 
			// changed on the next execution
			isDyn = false;
		}
		// this will happen when the original was changed
		else {
			// so next in line is the overlay
			isDyn = true;
		}
	
	// done cycling one image. now prepare for next execution
		
		// stupid setTimeout is always executed in window scope
		setTimeout(

			// if we would work with the 'this' keyword, we would
			// lose the reference to the ImageRotator object and
			// have 'this' reference Window instead on next execution.
			// getting around this only seemed to work properly
			// when the closure is used and the obj is passed in
			// the recursion as well.
			function() {

				// if the overlay was changed i was increased 
				// and isDyn is set to false.
				// on next execution of this function the original
				// image will be set to what overlay is now and
				// overlay will be made invisible.
				// but if the original was changed the overlay
				// will be changed to the next image in the list
				// and the fade-in will make it solid again. 
				obj.cycleImage(i, obj, isDyn);

			// we are still in a setTimeout, so we need some time
			// the delay makes sure we dont get hectic image cycles
			}, obj.delay*100
		);
	}

	/*
	*	creates a fade effect
	*
	*	the function creates a fade effect on the overlay image
	*	by setting the opacity from full to null or other way
	*	round depending on the params given. The functions calls
	*	itself again through multiple setTimeout calls, passing
	*	the current opacity value to achieve the fade effect.
	*
	*	@version	1.0.0
	*	@param		int i - the current or start opacity
	*	@param		ImageRotator obj - self reference for keeping the scope
	*/
	function fade(i, obj) {
		
		// all we do is call ImageRotator::setOpacity()
		setTimeout(

			// but since this is setTimeout() with the same
			// problems as in ImageRotator::cycleImages()
			// we need to do this via a closure
			function() {

				// now we set the opacity to the current i
				this.setOpacity(i, obj);

			// and tell it to wait so we can see the effect
			}, i*obj.delay);
		
		// the image is solid if i is the same as delay
		// so do this function again
		if (i < obj.delay) {

			// call the fade again with i increased by one
			this.fade(++i, obj);
		}
	}

	/*
	*	sets the opacity of the overlay image
	*	
	*	this function sets the opacity of the overlay to the value
	*	specified in val. if rev is passed to the function as well,
	*	val will be reversed. this will also reverse the fade effect
	*	from in to out, although this will look weird due to the
	*	anatomy of the cycle function.
	*
	*	The function uses two different style objects to achieve 
	*	the opacity effect. The filter object is only understood
	*	by IE and the opacity style object only by Firefox, but
	*	fortunately both browsers dont throw an error but simply
	*	ignore the other object.
	*
	*	@param	int val - the opacity value the overlay is set to
	*	@param	ImageRotator obj - self reference for keeping the scope
	*	@param	boolean rev - optional, switches fade-in to fade-out
	*/
	function setOpacity(val, obj, rev) {

		// check if the fade effect should be reversed
		// false = fade-in, true = fade-out.
		if (rev == true) {

			// simply reverse the current value of val
			val = obj.delay-val;
		}
		
		// this will be interpreted by Firefox but ignored by
		// Internet Explorer. Maybe IE7 can do this.
		obj.dynImage.style.opacity = val/obj.delay;

		// Microsofts alpha filter take a percentage value
		// we need to calculate that from our own values
		percent = val * (100/obj.delay);

		// this will be interpreted by Internet Explorer
		// but will be ignored by Firefox. Nifty.
		obj.dynImage.style.filter = 'alpha(opacity=' + percent + ')';
	}




