/**
 * This script fades/scrolls dom elements in and out of visibility for a nice
 * little 'hide' effect.  If the user does not have javascript enabled, then
 * the elements are never hidden and no fading is ever done.  One should _not_
 * hide the elements by default, they will fade away when the page loads.  This
 * ensures proper degredation, and also hints to the user that there is more
 * content on the page than what is displayed (so they know to click the 
 * controller). The cursor is set to a pointer to futher hint at the intended 
 * clicking action.
 * 
 * To put this script to use, first, include it in the page with a script 
 * tag in the header.  Then add the fadeControlClass (default is 'fading_controller')
 * to the element(s) you want to act as the controller(s).  You link the controller to
 * the fading element by giving them the same id except for the controller is
 * prefixed with the fadeControlPrefix (default is 'ctrl_') and the object to
 * actually fade in/out is prefixed with the fadeElementPrefix (default is 'elem_').
 * Then clicking on the controller will toggle the element to fade and scroll
 * in and out of existance.
 *
 * If you want to re-implement (override) any of the default variables, you can do
 * so simply be redefining whatever you want in a script that gets loaded after 
 * this script is loaded.  i.e. 
 *
 * <script type="text/javascript" src="/js/fader.js"></script>
 * <script type="text/javascript" src="/path/to/script/new_fade.js"></script>
 *
 * Since new_fade.js is loaded *after* this script, all the variables get
 * overridden for *your* needs, but remain default so somebody else's page
 * doesn't suddenly just break (or behave unpredictably).
 *
 * In addition to overriding variables, you may also override any of the
 * functions.  Here is a list of functions you can override to achieve
 * custom behavior (before overridding any function, make sure you read
 * its comment so you know what it is expected to do).
 *
 * 		fetchControllerElements
 * 		getFadingElement
 * 		computeHeight
 * 		getElementIndex
 * 		showElem
 * 		hideElem
 * 		fade
 * 		addElemFader
 * 		toggleElement
 *
 * To override a function, just redeclare it in your custom script, i.e. 
 *
 * computeHeight = function(idx_, opacity_) { // do something... }
 *
 * NOTE: If you set the fadeControlPrefix = fadeElementPrefix, then it is
 * possible that an element can be its own controller, (e.g. clicking the
 * object itself initiates fading).  Other work would then need to be
 * done in order to trigger the fading since you can't click an object that
 * has faded out of existance.  I'll leave that conundrum. up to you to solve.
 *
 * -----------------------------------------------------------------------------
 * @author Eric Martinez
 * @version 1.0
 * @date 5/22/2007
 * -----------------------------------------------------------------------------
 */

// Set fading properties [Customize the script here]
var maxOpacity = 0.99;        // The 'brightest' the element will be [0.0,1.0]
var minOpacity = 0.00;        // The 'dimmest' the element will get [0.0,1.0]
var increase   = 0.10;        // How much to 'brighten' with each fade
var decrease   = -1*increase; // How much to 'dim' with each fade
var timeout    = 2;           // Millisecond timeout between fades

// Set attributes to give elements you want to fade by default
var fadeControlClass  = "fading_controller"; // Class name for controllers
var fadeControlPrefix = "ctrl_";             // Id prefix for controllers
var fadeElementPrefix = "elem_";             // Id prefix for fading elements

// Set fading controllers (Probably a bad idea to change these)
var controllers   = new Array();  // Elements to control fading
var elements      = new Array();  // Elements to fade
var opacity       = new Array();  // Starting opacity of elements to fade
var increment     = new Array();  // How much to fade with each call
var targetOpacity = new Array();  // Destination opacity for the element
var fading        = new Array();  // Determines whether element is fading
var curHeights    = new Array();  // The current height (px) of the element
var fullHeight    = new Array();  // The default height of the object

var theElement;

/**
 * Sets which DOM elements you would like to fade. You can re-implement this
 * function in your own script if you want different criteria for which
 * elements to fade.
 *
 * Returns an array of DOM elements that you wish to have fade in and out.
 */
fader_default_fetchControllerElements = function() {
	var elems = new Array();
	var j = 0;
	var regex = new RegExp("(^|\\s)"+fadeControlClass+"(\\s|$)");
	// Load up the elements with the fader_class_name
	var divs = document.getElementsByTagName('div');
	for(var i = 0; i < divs.length; ++i) {
		if(regex.test(divs[i].className)) {
			elems[j++] = divs[i];
		}
	}
	return elems;
}

/**
 * Returns the fading element linked to this controller.
 */
fader_default_getFadingElement = function(ctrl_) {
	var prefix_len = fadeControlPrefix.length;
	
	// Swap out the prefixes
	var id = ctrl_.id;
	id = id.substring(prefix_len);
	id = fadeElementPrefix + id;

	// Get the proper element
	return document.getElementById(id);
}

/**
 * Computes the height that should be used for the element at
 */
fader_default_computeHeight = function(idx_, curOpacity_) {
	var range = maxOpacity - minOpacity;
	var complete = curOpacity_ / range;
	var newHeight = complete * fullHeight[idx_];
	newHeight = parseFloat(newHeight);
	return newHeight;
}

/**
 * Returns the index of the elem that activated the event
 */
fader_default_getElementIndex = function(elem_) {
	for(var i = 0; i < elements.length; ++i) {
	  if(elem_ == elements[i]) { return i; }
	}
	for(var i = 0; i < controllers.length; ++i) {
	  if(elem_ == controllers[i]) { return i; }
	}
	return -1;
}

/**
 * Fades the elem to its maxOpacity
 */
fader_default_showElem = function(elem_) {
	elem_.style.display = '';
	var idx = getElementIndex(elem_);
	targetOpacity[idx] = parseFloat(maxOpacity);
	increment[idx] = parseFloat(increase);
	if(fading[idx] == 0) { fade(idx); }
}

/**
 * Fades the elem to its minOpacity
 */
fader_default_hideElem = function(elem_) {
	var idx = getElementIndex(elem_);
	// In case user resized the window
	height = elem_.scrollHeight;
	fullHeight[idx] = parseFloat(height);
	targetOpacity[idx] = parseFloat(minOpacity);
	increment[idx] = parseFloat(decrease);
	if(fading[idx] == 0) { fade(idx); }
}

/**
 * Actually performs the fading.  This is controlled by the
 * targetOpacity/increment of the element located at elements[idx_].
 */
fader_default_fade = function(idx_) {
	// Atomic case
	if((targetOpacity[idx_] == maxOpacity && 
		  opacity[idx_] > targetOpacity[idx_]) ||
	   (targetOpacity[idx_] == minOpacity && 
		 opacity[idx_] < targetOpacity[idx_]) ) {
		
		opacity[idx_] = targetOpacity[idx_];
		//elements[idx_].style.MozOpacity = opacity[idx_];
		//elements[idx_].style.filters.alpha.opacity = parseInt(100*opacity[idx_]);
		$(elements[idx_]).setStyle({opacity:opacity[idx_]});
		
		// Do this step here so the fading effect is not lost
		if(increment[idx_] < 0) {
			elements[idx_].style.display = 'none';
		} else {
			// In case user resized the window
			elements[idx_].style.height = 'auto';
		}
		
		fading[idx_] = 0;
		return;
	}

	fading[idx_] = 1;
	
	curHeights[idx_] = computeHeight(idx_, opacity[idx_]);
	
	var height = parseInt(curHeights[idx_], 10) + 'px';
	elements[idx_].style.height = height;
	$(elements[idx_]).setStyle({opacity:opacity[idx_]});
	//elements[idx_].style.opacity = opacity[idx_];
	//elements[idx_].style.filter = "alpha(opacity="+opacity[idx_]+")";
	opacity[idx_] += increment[idx_];
	setTimeout('fade('+idx_+')', timeout);
}

/**
 * The function is attached to the window.onload event and adds the
 * fading logic to all the elements defined in fetchControllerElements()
 */
fader_default_addElemFader = function() {
	// Fetch the elements to control fadding
	controllers = fetchControllerElements();
	
	// Add the events/set defaults for each element
	for(var i = 0; i < controllers.length; ++i) {
		
		elements[i] = getFadingElement(controllers[i]);
		
		// Set the default controllers
		opacity[i] = parseFloat(maxOpacity);
		increment[i] = parseFloat(decrease);
		targetOpacity[i] = parseFloat(minOpacity);
		var height = elements[i].scrollHeight;
		curHeights[i] = parseFloat(height);
		fullHeight[i] = parseFloat(height);
		fading[i] = 0;
		
		// Attach the events
		var theFunc = createFunc(controllers[i]);
		appendEvent(controllers[i], 'click', createFunc(controllers[i]));
		
		// Set the overflow
		elements[i].style.overflow = 'hidden';
		
		// Hide elements by default
		hideElem(elements[i]);
		
		// This speeds the hiding process
		elements[i].style.display  = 'none';
	} // END: for loop
}


fader_default_createFunc = function(ctrl_) {
	return function() {toggleElement(ctrl_);}	
}

/**
 * Finds the fading element associated with the ctrl_
 * and then determines if the underlying fading element
 * is currently (or most recently) fading in or out,
 * and then inverts the fade direction.
 */
fader_default_toggleElement = function(ctrl_) {
	if(!ctrl_) {return;}
	var elem = getFadingElement(ctrl_);
	if(!elem) { return;}
	var idx = getElementIndex(elem);
	
	if(increment[idx] > 0) {
		hideElem(elem);
	} else {
		showElem(elem);
	}
}

/**
 * This function is called after the fading effect is added to each element.
 * By default this does nothing, but allows the user to define it on their own
 * so if they want something to fire immediately after all the elements get
 * loaded.
 */
fader_default_initCleanup = function() {
	
}

/**
 * Appends events for objects. 
 *
 * obj_ is a DOM object that can trigger javascript events
 * event_ is a string naming the event to attach
 * function_ is a function that is in scope
 */
appendEvent = function(obj_, event_, function_) {
	if(obj_.attachEvent) { // IE case
		obj_.attachEvent("on"+event_, function_);
	} else if (obj_.addEventListener) { // All others case
		obj_.addEventListener(event_, function_, false);
	} else { // Just in case
		event_ = "on"+event_;
		var old_event = obj_[event_];
		if(old_event) {
			obj_[event_] = old_event&&function_;
		} else {
			obj_[event_] = function() { old_event(); function_();}
		}
	}
}

/**
 * Starts up the process.  Do _NOT_ override this function.
 * Well, you *can*, but do so at your own risk.  And be ready
 * for a fun ride while you debug.
 */
fader_initialize = function() {
	addElemFader();
	initCleanup();
}

/**
 * Set the functions that we will use.  These can be overriden at any point 
 * before the document finishes loading.
 */
fetchControllerElements = fader_default_fetchControllerElements;
getFadingElement        = fader_default_getFadingElement;
computeHeight           = fader_default_computeHeight;
getElementIndex         = fader_default_getElementIndex;
showElem                = fader_default_showElem;
hideElem                = fader_default_hideElem;
fade                    = fader_default_fade;
addElemFader            = fader_default_addElemFader;
createFunc              = fader_default_createFunc;
toggleElement           = fader_default_toggleElement;
initCleanup             = fader_default_initCleanup;

/** Get the party started! */
appendEvent(window, 'load', fader_initialize);
