/* ---------------------------------------------------------------------------------------- *\
|*	gui-tools.js																			*|
|*	test classes and objects																*|
|*	© Copyright Mynor Ramos.  All rights reserved.											*|
\* ---------------------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------------------- *\
|* function / class fader																	*|
|*																							*|
|* fades an element into the screen and then fades it out									*|
|*																							*|
|* argument list:																			*|
|*	element:	the element id																*|
|*	speed: 		the speed with which the element will be faded, in miliseconds				*|
|*	fadeout: 	the time that the element will be visible on screen in miliseconds before	*|
|*				desappearing, -1 means the element will have to be faded out by calling the	*|
|*				function fadeout method of this class										*|
|*	limit:		opacity limit, from 1 to 100 (default)										*|
\* ---------------------------------------------------------------------------------------- */
function fader(element, speed, fadeout, limit)
{
	try {
		
/* [+] check and initialization ----------------------------------------------------------- */

		// if the element id has not been provided or id is any other than a string
		if (!element || typeof(element) != "string")
			throw "You need to provide a valid string element Id";
		else
			this.element = document.getElementById(element);
		
		// get the document element
		if (!this.element)
			throw "There are no elements by the id '" + element.toUpperCase() + "' in the current document";
		
		if (!speed)
			this.speed = 50; // 100 miliseconds default speed
		else
			this.speed = speed;
		
		if (!fadeout)
			this.fadeout = 5000; // 5 seconds default timeout
		else
			this.fadeout = fadeout;
		
		// if limit is not set or limit is not in between 1 to 100
		if (!limit || !(limit >=1 && limit <=100))
			this.limit = 100; // completely opaque
		else
			this.limit = limit;
		
		this.opacity = 0; // initialize opacity
		
		// set some element properties that might still not be assigned and therefore this class will not be able to change at run time
		this.element.style.opacity = 0.0;
		this.element.style.MozOpacity = 0.0;
		this.element.style.filter = "alpha(opacity=0)";
		this.element.style.visibility = "hidden";
		
		// control variables
		this.cancelFadeIn = false; // request for cancelling fade in
		this.cancelFadeOut = false; // request for cancelling fade out
		this.fadingIn = false; // fade in is in progress
		this.fadingOut = false; // fade out in is in progress
		this.timeoutId = false; // current timeout Id, none if false
		
/* [-] check and initialization ----------------------------------------------------------- */

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

		/* -------------------------------------------------------------------- *\
		|* this function will fade the element according to the current opacity	*|
		|* it shoud not be called directly										*|
		\* -------------------------------------------------------------------- */
		
		this.fade = function() {
			if (this.opacity > 100) // fix opacity if beyond totally opaque
				this.opacity = 100;
			else if (this.opacity < 0) // fix opacity if under totally transparent
				this.opacity = 0;
			
			// CSS3, firefox and ... *sigh* ie compatible
			this.element.style.opacity = this.opacity / 100;
			this.element.style.MozOpacity = this.opacity / 100;
			this.element.style.filter = "alpha(opacity=" + this.opacity + ")";
		}
		
		/* -------------------------------------------------------------------- *\
		|* this function will fade the element in calling itself recursively	*|
		|* for as many times as necessary and triggering the fadeout method if	*|
		|* requested.  called from fadeIn(), it should not be called directly	*|
		\* -------------------------------------------------------------------- */
		
		this.runFadeIn = function() {
			// handle fading in cancellation
			if (this.cancelFadeIn) {
				this.cancelFadeIn = false; // acknowledge
				this.fadingIn = false;
				
				// cancelled can only occur due to an early fade out, so clear the timeout, check if it's available
				if (this.timeoutId) {
					window.clearTimeout(this.timeoutId); // clear from queue
					this.timeoutId = false; // unset the timeout Id
				}
				
				return; // do nothing more
			}
			
			// otherwise proceed with the fading in process
			if (this.opacity < this.limit) {
				this.fadingIn = true; // acknowledge that we are currently fading in
				
				this.opacity += 5; // make a little bit more opaque
				this.fade(); // call the function that will set the opacity on the element
				
				var obj = this; // call itself quasy-recusively until opacity reaches the limit
				window.setTimeout(function () { obj.runFadeIn() }, this.speed);
				
			} else if (this.fadeout >= 0) {
				this.fadingIn = false; // no more fading in, all done
				
				var obj = this; // schedule the fade out process if requested
				this.timeoutId = window.setTimeout(function () { obj.fadeOut() }, this.fadeout);
				
			}
			
		}

		/* -------------------------------------------------------------------- *\
		|* this function will control the actual fade in calls and will 		*|
		|* determine if to run the fade in or not it will also control if a		*|
		|* fade out should be stopped and clear the corresponding timeout id	*|
		\* -------------------------------------------------------------------- */
		
		this.fadeIn = function() {
			// if the function is called again to fade in while fading, just return
			if (this.fadingIn)
				return;
			
			// the element is not visible, this is the first time the function is called
			if (this.opacity == 0) {
				this.element.style.visibility = "visible"; // make element visible
				this.runFadeIn(); // start fading
				
			} else { // fading out in may be in progress or the fade in process is complete
				if (this.fadingOut) // if fading out, cancel the process
					this.cancelFadeOut = true;
				
				if (this.timeoutId) {
					window.clearInterval(this.timeoutId);
					this.timeoutId = false;
				}
				
				this.runFadeIn(); // call the function to fade the element in once again, no need to set visibility

			}
			
		}
		
		/* -------------------------------------------------------------------- *\
		|* this function will fade the element out calling itself recursively	*|
		|* for as many times as necessary.  called from fadeOut()				*|
		\* -------------------------------------------------------------------- */
		
		this.runFadeOut = function() {
			// handle fading out cancellation
			if (this.cancelFadeOut) {
				this.cancelFadeOut = false; // acknowledge
				this.fadingOut = false;
				return; // do nothing more
			}
			
			// do the fadeout process
			if (this.opacity > 0) {
				this.fadingOut = true; // we are currently fading out
				
				this.opacity -= 5;
				this.fade();
				
				var obj = this; // call function to keep fading out
				window.setTimeout(function () { obj.runFadeOut() }, this.speed);
				
			} else {
				this.fadingOut = false; // no more fading
				
				// hide the element when fading out finishes
				this.element.style.visibility = "hidden";
				
			}
		}

		this.fadeOut = function() {
			// if we are in the middle of a fade out, do nothing
			if (this.fadingOut)
				return;
			
			// the fade in process was complete
			if (this.opacity == this.limit) {
				if (this.timeoutId) { // clear the time out if set
					window.clearTimeout(this.timeoutId);
					this.timeoutId = false; // reset it
				}
				
				this.runFadeOut(); // run the fade out normally
				
			} else { // the fade in process is still executing
				this.cancelFadeIn = true; // cancel it
				
				this.runFadeOut();
				
			}
			
		}
			
/* [-] methods ---------------------------------------------------------------------------- */

/* [+] exception -------------------------------------------------------------------------- */

	} catch (ex) {
		if (ex.message) {
			alert("Exception in class \"fader\"\r\n\r\n" + ex.message);
		} else if (ex.description) {
			alert("Exception in class \"fader\"\r\n\r\n" + ex.description);
		} else {
			// most likely our custom error message
			alert("Exception in class \"fader\"\r\n\r\n" + ex);
		}
	}
	
/* [-] exception -------------------------------------------------------------------------- */

}
