/*  jnwgz JavaScript toolbox

		Compiled by John Wigzell, November 2008.
		Published under a creative commons attribution license, please credit and share if you re-use or adapt any of this code.
		http://creativecommons.org/licenses/by-sa/3.0/

*/

/*  Add event

		Based on code by Scott Andrew and Dustin Diaz -
		http://www.scottandrew.com/weblog/jsjunk
		http://www.dustindiaz.com/top-ten-javascript/

*/

		function addEvent(element, type, listener, capture) {
			if (element.addEventListener) {
				element.addEventListener(type, listener, capture);
				return true;
			}
			else if (element.attachEvent) {
				var ev = element.attachEvent("on" + type, listener);
				return ev;
			}
			else {
				element["on" + type] = listener;
			}
		}


/*  Add load event

		Shorthand for addEvent(window, "load", [listener], false);

		Written by John Wigzell -
		http://jnwgz.co.uk/

*/

		function addLoadEvent(listener) {
			addEvent(window, "load", listener, false);
		}


/*  Get element(s) by class

		Takes a string to search for, optionally an existing object to search within (defaults to "document") and optionally a particular tag type to concentrate on (defaults to "*").
		Returns an array of objects.

		Based on code by Dustin Diaz -
		http://www.dustindiaz.com/top-ten-javascript/

*/

		function getElementsByClass(needle, haystack, tag) {
			var elements = new Array();
			if (haystack == null) {
				var haystack = document;
			}
			if (tag == null) {
				var tag = "*";
			}
			var nodes = haystack.getElementsByTagName(tag);
			var pattern = new RegExp("(^|\\\\s)" + needle + "(\\\\s|$)");
			for (i = 0; i < nodes.length; i ++) {
				if (pattern.test(nodes[i].className)) {
					elements.push(nodes[i]);
				}
			}
			return elements;
		}


/*  Get element(s)

		Takes one or more arguments, initially searching for each by id, then class.
		Returns an array of objects.

		Written by John Wigzell -
		http://jnwgz.co.uk/

*/

		function getElements() {
			var elements = new Array();
			for (var i = 0; i < arguments.length; i++) {
				var element = arguments[i];
				if (element = document.getElementById(element)) {
					elements.push(element)
				}
				if (element = getElementsByClass(element)) {
					elements = elements.concat(element);
				}
			}
			return elements;
		}


/*  Remove whitespace

		Gecko browsers consider whitespace characters used to format HTML/XML to be distinct nodes.
		This function strips out whitespace characters so that DOM manipulation functions behave as expected.
		
		Provenance unknown (get in touch to correct this). Could be based on code offered in response to this blog post -
		http://www.mikechambers.com/blog/2006/01/09/parsing-xml-in-javascript/
		
*/
		
		function removeWhitespace() {
		  for (var i = 0; i < arguments.length; i ++) {
				var element = arguments[i];
			  for (var j = 0; j < element.childNodes.length; j ++) {
		    	var node = element.childNodes[j];
		    	if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) {
			      element.removeChild(node);
					}
		  	}
			}
		}
		

/*  Insert after

		Inserts new DOM node (newNode) after reference node (refNode).
		
		Based on code published anonymously on Snipplr -
		http://snipplr.com/view/2107/insertafter-function-for-the-dom/
		
		Also some reference to code published by Dustin Diaz and credited to Jeremy Keith -
		http://www.dustindiaz.com/top-ten-javascript/
		http://adactio.com/

*/

		function insertAfter(newNode, refNode) {
			var parent = refNode.parentNode;
			if (parent.lastchild == refNode) {
				parent.appendChild(newNode);
			}
			else {
				parent.insertBefore(newNode, refNode.nextSibling);
			}
		}


/*  Discard element

		IE does not implement "element.removeChild" correctly (it continues to hold removed elements in memory); documented on MSDN -
		http://msdn.microsoft.com/en-us/library/bb250448.aspx
		
		This is a crude but effective way of properly removing DOM nodes in IE.
		Based on code adapted by Chris Robinson (aka "crob") from an article published by Scott Sweeney (aka "Avery_Novalis") at Scribd -
		http://www.outsidethediv.com/2008/10/removechild-vs-the-garbage-bin
		http://www.scribd.com/doc/2159768/Ajax-Part2

*/

		function discardElement(element) {
			var garbageBin = document.getElementById("garbage-bin");
			if (!garbageBin) {
				garbageBin = document.createElement("div");
				garbageBin.id = "garbage-bin";
				garbageBin.style.display = "none";
				document.body.appendChild(garbageBin);
			}
			garbageBin.appendChild(element);
			garbageBin.innerHTML = "";
		}


/*  Change opacity

		Sets the opacity of an object (object) to the given level (opacity).
		
		Based on code by Patrick H Lauke (aka "redux"), who was inspired by Steve at Slayeroffice -
		http://www.splintered.co.uk/experiments/
		http://slayeroffice.com/code/imageCrossFade/ 

*/

		function changeOpacity(object, opacity) {
			if (object.style) {
				if (object.style.MozOpacity != null) {  
					object.style.MozOpacity = (opacity / 100) - .001;
				}
				else if (object.style.opacity != null) {
					object.style.opacity = (opacity / 100) - .001;
				}
				else if (object.style.filter != null) {
					object.style.filter = "alpha(opacity=" + opacity + ")";
				}
			}
		}


/*  Fade class

*/

		var Fade = function () {

			this.object;
			this.opacityFrom = 0;
			this.opacityTo = 100;
			this.fadeIncrement = 10;
			this.fadeSpeed = 10;
			this.removeObject = false;

			this.opacityCurrent;
			this.timeout;

			this.fadeIn = function () {
				var self = this;
				if (this.opacityCurrent <= this.opacityTo) {
					changeOpacity(this.object, this.opacityCurrent);
					this.opacityCurrent += this.fadeIncrement;
					this.timeout = window.setTimeout(function () {self.fadeIn();}, this.fadeSpeed);
				}
			};

			this.fadeOut = function () {
				var self = this;
				if (this.opacityCurrent >= this.opacityTo) {
					changeOpacity(this.object, this.opacityCurrent);
					this.opacityCurrent -= this.fadeIncrement;
					this.timeout = window.setTimeout(function () {self.fadeOut();}, this.fadeSpeed);
				}
				else {
					if (this.removeObject == true) {
						discardElement(this.object);
					}
				}
			};

			this.start = function () {
				if (this.object.style) {
					this.opacityCurrent = this.opacityFrom;
					if (this.opacityFrom < this.opacityTo) {
						this.fadeIn();
					}
					if (this.opacityFrom > this.opacityTo) {
						this.fadeOut();
					}
				}
			};

		};
			
/*	Slideshow class

		Some elements based on code by Patrick H Lauke (aka "redux"), who was inspired by Steve at Slayeroffice -
		http://www.splintered.co.uk/experiments/
		http://slayeroffice.com/code/imageCrossFade/ 

		Some elements based on code by Michael Leigeber -
		http://www.leigeber.com/2008/05/ajax-image-gallery-slideshow/
 
*/

		var Slideshow = function () {

			this.target = "slideshow";
			this.loop = false;
			this.fadeIncrement = 10;
			this.fadeSpeed = 20;
			this.fadeInterval = 5000;
			this.slideClass = "slide";
			this.controlsShow = true;
			this.controlsClass = "slideshow-controls";
			this.controlsNextClass = "slideshow-controls-next";
			this.controlsNextImage = "images/next.png";
			this.controlsPlayClass = "slideshow-controls-play";
			this.controlsPlayImage = "images/play.png";
			this.controlsPauseClass = "slideshow-controls-pause";
			this.controlsPauseImage = "images/pause.png";
			this.controlsPreviousClass = "slideshow-controls-previous";
			this.controlsPreviousImage = "images/previous.png";
			this.indexShow = true;
			this.indexClass = "slideshow-index";
			this.indexJumpClass = "slideshow-index-jump";
			this.indexJumpImage = "images/index-outline.gif";
			this.indexJumpCurrentClass = "slideshow-index-jump-current";
			this.indexJumpCurrentImage = "images/index-solid.gif";

			this.slideContainer;
			this.slideStack;	
			this.slideStackSlide;	
			this.currentSlide;
			this.previousSlide;
			this.timeout;
			this.paused;

			this.start = function () {
				if (this.slideContainer = document.getElementById(this.slideContainer)) {
					this.slideContainer.style.position = "relative";
					while (this.slideContainer.firstChild) {
						discardElement(this.slideContainer.firstChild);
					}
					this.currentSlide = 0;
					this.previousSlide = this.slideStack.length - 1;
					this.changeSlide();
					if (this.loop == true) {
						var self = this;
						this.timeout = window.setTimeout(function () {self.playLoop();}, this.fadeInterval);
					}
				}
			};

			this.changeSlide = function () {
				var self = this;
				var previousNode = this.slideContainer.firstChild;
				while (previousNode) {
					if (previousNode.tagName == "IMG") {
					  previousNode.style.zIndex = "0";
					  this.fadeOut(previousNode);
					}
					previousNode = previousNode.nextSibling;
				}
				var image = new Image();
				image.onload = function () {
					var newNode = document.createElement("img");
				  newNode.className = self.slideClass;
				  newNode.src = self.slideStack[self.currentSlide];
				  newNode.style.top = "0px";
				  newNode.style.left = "0px";
				  newNode.style.position = "absolute";
				  newNode.style.zIndex = "100";
					changeOpacity(newNode, 0);
					self.slideContainer.appendChild(newNode);
					self.fadeIn(newNode);
				}
				image.src = this.slideStack[this.currentSlide];
				if (this.indexShow == true) {
					this.buildIndex();
				}
				if (this.controlsShow == true) {
					this.buildControls();
				}
			};
	
			this.fadeIn = function (object) {
				var fade = new Fade();
				fade.object = object;
				fade.opacityFrom = 0;
				fade.opacityTo = 100;
				fade.fadeSpeed = this.fadeSpeed;
				fade.fadeIncrement = this.fadeIncrement;
				fade.start();
			};

			this.fadeOut = function (object) {
				var fade = new Fade();
				fade.object = object;
				fade.opacityFrom = 100;
				fade.opacityTo = 0;
				fade.fadeSpeed = this.fadeSpeed;
				fade.fadeIncrement = this.fadeIncrement;
				fade.removeObject = true;
				fade.start();
			};

			this.jumpNext = function () {
				this.pause();
				this.previousSlide = this.currentSlide;
				if ((this.currentSlide + 1) >= this.slideStack.length) {
					this.currentSlide = 0;
				}
				else {
					this.currentSlide += 1;
				}
				this.changeSlide();
			};

			this.jumpPrevious = function () {
				this.pause();
				this.previousSlide = this.currentSlide;
				if ((this.currentSlide - 1) < 0) {
					this.currentSlide = (this.slideStack.length - 1);
				}
				else {
					this.currentSlide -= 1;
				}
				this.changeSlide();
			};

			this.jumpTo = function (slide) {
				this.pause();
				var slide = parseInt(slide, 10);
				if (slide != this.currentSlide) {
					this.previousSlide = this.currentSlide;
					this.currentSlide = slide;
					this.changeSlide();
				}
			};

			this.playLoop = function () {
				var self = this;
				this.loop = true;
				this.previousSlide = this.currentSlide;
				if ((this.currentSlide + 1) >= this.slideStack.length) {
					this.currentSlide = 0;
				}
				else {
					this.currentSlide += 1;
				}
				this.changeSlide();
				this.timeout = window.setTimeout(function () {self.playLoop();}, this.fadeInterval);
			};

			this.pause = function () {
				this.loop = false;
				window.clearTimeout(this.timeout);
				if (this.indexShow = true) {
					this.buildIndex();
				}
				if (this.controlsShow = true) {
					this.buildControls();
				}
			};

			this.buildControls = function () {
				var self = this;
				var oldControls = getElementsByClass(this.controlsClass, this.slideContainer, "div");
				for (var i = 0; i < oldControls.length; i ++) {
					this.slideContainer.removeChild(oldControls[i]);
				}
				var controlsContainer = document.createElement("div");
			  controlsContainer.className = this.controlsClass;
				var previousButton = document.createElement("img");
			  previousButton.className = this.controlsPreviousClass;
			  previousButton.src = this.controlsPreviousImage;
				previousButton.onclick = function () {self.jumpPrevious();};
				controlsContainer.appendChild(previousButton);
				if (this.loop == false) {
					var playButton = document.createElement("img");
				  playButton.className = this.controlsPlayClass;
				  playButton.src = this.controlsPlayImage;
					playButton.onclick = function () {self.playLoop();};
					controlsContainer.appendChild(playButton);
				}
				else {
					var pauseButton = document.createElement("img");
				  pauseButton.className = this.controlsPauseClass;
				  pauseButton.src = this.controlsPauseImage;
					pauseButton.onclick = function () {self.pause();};
					controlsContainer.appendChild(pauseButton);
				}
				var nextButton = document.createElement("img");
			  nextButton.className = this.controlsNextClass;
			  nextButton.src = this.controlsNextImage;
				nextButton.onclick = function () {self.jumpNext();};
				controlsContainer.appendChild(nextButton);		
				this.slideContainer.appendChild(controlsContainer);		
			};

			this.buildIndex = function () {
				var self = this;
				var oldIndex = getElementsByClass(this.indexClass, this.slideContainer, "div");
				for (var i = 0; i < oldIndex.length; i ++) {
					this.slideContainer.removeChild(oldIndex[i]);
				}
				var indexContainer = document.createElement("div");
			  indexContainer.className = this.indexClass;
				for (var i = 0; i < this.slideStack.length; i ++) {
					if (i == this.currentSlide) {
						var currentButton = document.createElement("img");
					  currentButton.className = this.indexJumpCurrentClass;
					  currentButton.src = this.indexJumpCurrentImage;
						indexContainer.appendChild(currentButton);		
					}
					else {
						var jumpButton = document.createElement("img");
					  jumpButton.className = this.indexJumpClass;
					  jumpButton.src = this.indexJumpImage;
						jumpButton.alt = i;
					  jumpButton.onclick = function () { var a = i; self.jumpTo(this.alt);};
						indexContainer.appendChild(jumpButton);		
					}
				}
				this.slideContainer.appendChild(indexContainer);		
			};

		};