/* ---------------------------------------------------------------------------------------- *\
|*	slideshow.js																			*|
|*	slideshow javascript object																*|
|*	© Copyright 2008, 2009 Mynor Ramos.  All rights reserved.								*|
\* ---------------------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------------------- *\
|* Function / object name:	slideshow														*|
|* Status:	finished																		*|
|* Description:	this function creates an object that contains a slideshow presentation		*|
|*				with a collection of images, each with its respective link and target		*|
|* ---------------------------------------------------------------------------------------- *|
|* Argument name			Description														*|
|* name	.	.	.	.	.	name of the slideshow control to be used as ID					*|
|* width, height	.	.	slideshow width and height										*|
|* domain (optional).	.	domain name in which the image is stored, ie. http://www.ie.com	*|
|* path	(optional)	.	.	path on the domain that the images are stored, ie. /path/path	*|
|* imageList	.	.	.	should be a string with the following format:					*|
|*							image1|link1|target1, ... imagen|linkn|targetn					*|
|*							where pipe (|) is the image properties separator and comma (,)	*|
|*							is the image separator. link and target are optional and don't	*|
|*							need to be pided												*|
|* *delay (1 sec def)	.	how long should the image stay on screen						*|
|* *pause (true def)	.	should we pause on mouseover									*|
|* *controls (true def)	.	display next, back controls										*|
|* ---------------------------------------------------------------------------------------- *|
|*  * optional arguments that, in their absence, default values will be used				*|
|* ---------------------------------------------------------------------------------------- *|
|* Furture upgrade ------------------------------------------------------------------------ *|
|* imageList	.	.	.	should be a string with the following format:					*|
|*							image1|link1|target1|delay1|tag1, ...							*|
|*							imagen|linkn|targetn|delayn|tagn								*|
|*							where pipe (|) is the image properties separator and comma (,)	*|
|*							is the image separator. link, target, delay and comment are		*|
|*							optional, and don't need to be piped							*|
\* ---------------------------------------------------------------------------------------- */
function slideshow(name, width, height, domain, path, imageList, delay, pause, controls)
{
	
	try {

/* [+] check ------------------------------------------------------------------------------ */
		
		// mandatory argument checking, if any of these is not available, an exception will be thrown
		if (!name)
			throw "You need to provide an element name for the slideshow";
		
		if (!width)
			throw "You need to provide an element width for the slideshow";
		
		if (!height)
			throw "You need to provide an element height for the slideshow";
		
		if (!imageList)
			throw "You need to provide an image list for the slideshow";
		
/* [-] check ------------------------------------------------------------------------------ */

/* [+] properties ------------------------------------------------------------------------- */

		// object properties assignment
		this.name = name;
		this.width = width;
		this.height = height;
		this.images = new Array(); // and empty array to contain all the image sets
		
		// parse the domain argument
		if (domain.replace(/\s*/g, "").length > 0) {
			domain = domain.toLowerCase(); // make all name low caps
			
			if (domain.indexOf("http://") == -1) // add http:// if necessary
				domain = "http://" + domain;
			
			if (domain.substring(domain.length - 1, domain.length) != "/") // add final "/" if necessary
				domain += "/";
		} else {
			domain = ""; // make domain an empty string as it may contain spaces
		}

		// parse the path argument
		if (path.replace(/\s*/g, "").length > 0) {
			path = path.toLowerCase(); // make path to lower caps
			
			// remove the first forward slash if present if domain is available since domain will
			// have the slash.  if domain is not present, leave the path as it is
			if (domain.replace(/\s*/g, "").length > 0 && path.substring(0, 1) == "/")
				path = path.substring(1, path.length);
			
			// add a forward slash at the end of path
			if (path.substring(path.length - 1, path.length) != "/")
				path += "/";
			
			// escape the path in case it contains invalid characters
			path = escape(path);
		} else {
			path = "";
		}
		
		if (imageList.replace(/\s*/g, "").length == 0)
			throw "You need to provide an image list for the slideshow";

		// get each image set, each separated by a comma
		var imageSets = imageList.split(",");
		
		// why would anyone summon a slideshow with only one image?
		if (imageSets.length < 2)
			throw "You need at least 2 image sets to run a slideshow";
		
		// check and ready each image
		for (i = 0; i < imageSets.length; i++) {
			imageSet = imageSets[i].split("|");
			
			if (imageSet.length == 0)
				throw "Each image set must consist of at least one image or any of these three items: (image|link|target)\r\nCheck set " + (i + 1);
			
			if (imageSet[0].replace(/\s*/g, "").length == 0)
				throw "You must specify an image name for set " + (i + 1);
			
			// remove the first forward slash if present if path or domain are available since either will
			// have the slash.  if neither is not present, leave the image as it is
			if ((path.replace(/\s*/g, "").length > 0 || domain.replace(/\s*/g, "").length > 0) && imageSet[0].substring(0, 1) == "/")
				imageSet[0] = imageSet[0].substring(1, imageSet[0].length);
			
			// create an array in the last image row to hold the values of the set
			this.images[i] = new Array();
			
			this.images[i][0] = new Image(); // first we'll have an image
			this.images[i][0].src = domain + path + imageSet[0]; // so they are loaded as soon as this is done
			
			// the image link, if it was provided
			if (imageSet.length > 1) {
				if (imageSet[1].replace(/\s*/g, "").length > 0)
					this.images[i][1] = imageSet[1];
				else
					this.images[i][1] = "";

				// use only valid targets, if provided
				if (imageSet[1].replace(/\s*/g, "").length > 0 && imageSet.length > 2) {
					if (imageSet[2] == "blank" || imageSet[2] == "parent" || imageSet[2] == "self" || imageSet[2] == "top")
						this.images[i][2] = imageSet[2];
					else // otherwise use nothing
						this.images[i][2] = "";
				} else {
					this.images[i][2] = "";
				}
			} else {
				this.images[i][1] = "";
				this.images[i][2] = "";
			}
		}
		
		// assign optional parameters, if undefined, assign a default value
		this.delay = (delay == null) ? 1000 : delay;
		this.pause = (pause == null) ? true : pause;
		this.controls = (controls == null) ? true : controls;
		
		this.paused = false; // if pause is available
		this.currentSlide = 0; // current image
		this.faded = false; // has the front slide been faded?
		this.fading = false; // are we in the middle of fading the image?
		this.opacity = 100; // current element opacity
		this.fadeIntervalId = 0; // the interval to clear
		this.runTimeoutId = 0; // the timeout between fades
		
/* [-] properties ------------------------------------------------------------------------- */

/* [+] methods ---------------------------------------------------------------------------- */

		// this will populate the given div element with the current slide, most likely used to
		// populate a hiden or not visible layer, or the first one upon first use
		this.prepareSlide = function(divSlide) {
			var innerHTML = "";
			
			// add the anchor element if we have an active link, with it's respective target
			if (this.images[this.currentSlide][1].length > 0)
				innerHTML = "<a href=\"" + this.images[this.currentSlide][1] + "\"" + ((this.images[this.currentSlide][2].length > 0) ? " target=\"_" + this.images[this.currentSlide][2] + "\"" : "") + ">";
			
			// add the image element
			innerHTML += "<img border=\"0\" width=\"" + this.width + "\" height=\"" + this.height + "\" src=\"" + this.images[this.currentSlide][0].src + "\" />";

			// close the anchor if we have an active link
			if (this.images[this.currentSlide][1].length > 0)
				innerHTML += "</a>";
			
			divSlide.innerHTML = innerHTML;
		}
		
		// this function will fade the first layer to make it visible or invisible
		this.fade = function() {
			// get both elements
			var divFront = document.getElementById(this.name + "Front");
			var divBack = document.getElementById(this.name + "Back");
			
			// the front image is currently displayed
			if (!this.faded) {
				if (this.opacity > 0) { // we have not faded the element completely
					if (!this.fading)
						this.fading = true; // we are fading the elements
					
					this.opacity -= 10; // decrease opacity
					
					// set opacity to front element to fade it away
					if (divFront.style.opacity)
						divFront.style.opacity = this.opacity / 100;
					
					if (divFront.style.MozOpacity)
						divFront.style.MozOpacity = this.opacity / 100;
					
					if (divFront.style.filter)
						divFront.style.filter = "alpha(opacity=" + this.opacity + ")";
					
					// set opacity to back element to fade it in
					if (divBack.style.opacity)
						divBack.style.opacity = (100 - this.opacity) / 100;
					
					if (divBack.style.MozOpacity)
						divBack.style.MozOpacity = (100 - this.opacity) / 100;
					
					if (divBack.style.filter)
						divBack.style.filter = "alpha(opacity=" + (100 - this.opacity) + ")";
				} else {
					divFront.style.visibility = "hidden"; // hide the layer completely to use the back layer link
					this.faded = true; // acknowledge the fade
					this.fading = false; // we are no longer fading the elements
					
					// now, onto change the image
					this.currentSlide++;
					
					if (this.currentSlide > this.images.length - 1)
						this.currentSlide = 0; // return to the first image if we've passed the end of the list
					
					// prepare the next slide
					this.prepareSlide(divFront);
					
					window.clearInterval(this.fadeIntervalId); // stop this function from being called any further
					
					var obj = this;
					this.runTimeoutId = window.setTimeout(function() { obj.run(); }, this.delay); // run the slide show again
				}
			} else {
				if (divFront.style.visibility == "hidden")
					divFront.style.visibility = "visible"; // hide the layer completely to use the back layer link

				if (this.opacity < 100) { // we have not faded the element completely
					if (!this.fading)
						this.fading = true; // we are fading the elements
						
					this.opacity += 10; // decrease opacity
					
					// set opacity to front element to fade it away
					if (divFront.style.opacity)
						divFront.style.opacity = this.opacity / 100;
					
					if (divFront.style.MozOpacity)
						divFront.style.MozOpacity = this.opacity / 100;
					
					if (divFront.style.filter)
						divFront.style.filter = "alpha(opacity=" + this.opacity + ")";
					
					// set opacity to back element to fade it in
					if (divBack.style.opacity)
						divBack.style.opacity = (100 - this.opacity) / 100;
					
					if (divBack.style.MozOpacity)
						divBack.style.MozOpacity = (100 - this.opacity) / 100;
					
					if (divBack.style.filter)
						divBack.style.filter = "alpha(opacity=" + (100 - this.opacity) + ")";
				} else {
					this.faded = false; // acknowledge the fade
					this.fading = false; // we are no longer fading the elements
					
					// now, onto change the image
					this.currentSlide++;
					
					if (this.currentSlide > this.images.length - 1)
						this.currentSlide = 0; // return to the first image if we've passed the end of the list
					
					// prepare the next slide
					this.prepareSlide(divBack);
					
					window.clearInterval(this.fadeIntervalId); // stop this function from being called any further
					
					var obj = this;
					this.runTimeoutId = window.setTimeout(function() { obj.run(); }, this.delay); // run the slide show again
				}
			}
		}
		
		// this function will check if the elements need to be faded
		this.run = function() {
			if (this.paused) {
				// if we are paused, wait 1/10th of a second to try again
				var obj = this;
				this.runTimeoutId = window.setTimeout(function() { obj.run(); }, 100);
			} else {
				var obj = this;
				this.fadeIntervalId = window.setInterval(function() { obj.fade(); }, 40); // otherwise, fade the image
			}
		}
		
/* [-] methods ---------------------------------------------------------------------------- */

/* [+] code ------------------------------------------------------------------------------- */

		// write the main element of the slideshow, which will contain both other elements
		// and will check for mouse over for pause
		document.write("<div id=\"" + this.name + "Main\" style=\"position:relative;overflow:hidden;width:" + this.width + "px;height:" + this.height + "px;\">");

		// write the back element of the slideshow, obscured by the next div, with opacity = 0
		document.write("<div id=\"" + this.name + "Back\" style=\"position:absolute;opacity:0;filter:alpha(opacity=0);-moz-opacity:0;width:" + this.width + "px;height:" + this.height + "px;\"></div>");
		
		// write the first visible slideshow element
		document.write("<div id=\"" + this.name + "Front\" style=\"position:absolute;opacity:1;filter:alpha(opacity=100);-moz-opacity:1;width:" + this.width + "px;height:" + this.height + "px;\"></div>");
		
		if (this.controls) {
			document.write("<div id=\"" + this.name + "Prev\" align=\"center\" style=\"opacity:0.50;filter:alpha(opacity=50);-moz-opacity:0.50;visibility:hidden;cursor:pointer;background-color:#bfbfbf;border-left:1px solid white;border-top:1px solid white;border-right:1px solid gray;border-bottom:1px solid gray;font:14pt verdana;position:absolute;margin-top:" + ((this.height / 2) - 13) + "px;width:25px;height:25px;\">&lt;</div>");

			document.write("<div id=\"" + this.name + "Next\" align=\"center\" style=\"opacity:0.50;filter:alpha(opacity=50);-moz-opacity:0.50;visibility:hidden;cursor:pointer;background-color:#bfbfbf;border-left:1px solid white;border-top:1px solid white;border-right:1px solid gray;border-bottom:1px solid gray;font:14pt verdana;position:absolute;margin-top:" + ((this.height / 2) - 13) + "px;margin-left:" + (this.width - 27) + "px;width:25px;height:25px;\">&gt;</div>");
		}
		
		document.write("</div>"); // end of the main slide show element
		
		// set mouseover, mouseout handlers for pause if necessary
		if (this.pause || this.controls) {
			var obj = this;
			
			document.getElementById(this.name + "Main").onmouseover = function() {
				if (obj.pause)
					obj.paused = true;
				
				if (obj.controls) {
					document.getElementById(obj.name + "Prev").style.visibility = "visible";
					document.getElementById(obj.name + "Next").style.visibility = "visible";
				}
			}
			
			document.getElementById(this.name + "Main").onmouseout = function() {
				if (obj.pause)
					obj.paused = false;
				
				if (obj.controls) {
					document.getElementById(obj.name + "Prev").style.visibility = "hidden";
					document.getElementById(obj.name + "Next").style.visibility = "hidden";
				}
			}
		}
		
		if (this.controls) {
			var obj = this;
			
			// this function will move to the previous slide, it needs to go back two slides since the fade
			// event makes the slideshow to be one slide ahead
			document.getElementById(this.name + "Prev").onclick = function() {
				if (!obj.fading) {
					window.clearTimeout(obj.runTimeoutId); // make sure the slide show is stopped before switching
				
					// move to the correct slide, if by moving back two slides we have a negative value, use that
					// value from the end of the array to get the correct slide
					if (obj.currentSlide - 2 < 0)
						obj.currentSlide = obj.images.length + (obj.currentSlide - 2);
					else
						obj.currentSlide -= 2;
					
					// we are currently on the front element
					if (!obj.faded)
						obj.prepareSlide(document.getElementById(obj.name + "Back")); // prepare the back one
					else // otherwise we are on the back element
						obj.prepareSlide(document.getElementById(obj.name + "Front")); // prepare the front one

					obj.fadeIntervalId = window.setInterval(function() { obj.fade(); }, 30); // fade the image
				}
			}

			document.getElementById(this.name + "Next").onclick = function() {
				if (!obj.fading) {
					window.clearTimeout(obj.runTimeoutId); // make sure the slide show is stopped before switching
				
					obj.fadeIntervalId = window.setInterval(function() { obj.fade(); }, 30); // fade the image
				}
			}
		}
		
		// prepare the first slide
		this.prepareSlide(document.getElementById(this.name + "Front"));
		
		this.currentSlide++; // change to the next image
		
		// prepare the back slide
		this.prepareSlide(document.getElementById(this.name + "Back"));
		
		// run the slide show
		var obj = this;
		this.runTimeoutId = window.setTimeout(function() { obj.run(); }, this.delay);
		
/* [-] code ------------------------------------------------------------------------------- */

	}
	
/* [+] exception handling ----------------------------------------------------------------- */

	catch (e) {
		if (e.message) {
			alert("Exception in function / object \"slideshow\"\r\n\r\n" + e.message);
		} else if (e.description) {
			alert("Exception in function / object \"slideshow\"\r\n\r\n" + e.description);
		} else {
			// most likely our custom error message
			alert("Exception in function / object \"slideshow\"\r\n\r\n" + e);
		}
	}

/* [-] exception handling ----------------------------------------------------------------- */

}