
/*****************************************************************************\
* Events                                                                      *
\*****************************************************************************/



/*
 * (c)2006 Dean Edwards/Matthias Miller/John Resig
 * Special thanks to Dan Webb's domready.js Prototype extension
 * and Simon Willison's addLoadEvent
 *
 * For more info, see:
 * http://dean.edwards.name/weblog/2006/06/again/
 * http://www.vivabit.com/bollocks/2006/06/21/a-dom-ready-extension-for-prototype
 * http://simon.incutio.com/archive/2004/05/26/addLoadEvent
 *
 * Thrown together by Jesse Skinner (http://www.thefutureoftheweb.com/)
 *
 * Slightly modified by David "Dewi" Young:
 *     broadened KHTML regexp
 *     in IE hack, replaced void(0) with false to work better in a https context
 *     corrected IE hack markup
 *     amended window.onload fallback not to clobber any existing handler
 *     removed reference to Array.push() unsupported in IE5
 *
 * To use: call addDOMLoadEvent one or more times with functions, ie:
 *
 *    function something() {
 *       // do something
 *    }
 *    addDOMLoadEvent(something);
 *
 *    addDOMLoadEvent(function() {
 *        // do other stuff
 *    });
 *
 */

function addDOMLoadEvent(func) {
	if (!window.__load_events) {
		var init = function () {
			// quit if this function has already been called
			if (arguments.callee.done) return;

			// flag this function so we do not do the same thing twice
			arguments.callee.done = true;

			// kill the timer
			if (window.__load_timer) {
				clearInterval(window.__load_timer);
				window.__load_timer = null;
			}

			// execute each function in the stack in the order they were added
			for (var i=0;i < window.__load_events.length;i++) {
				window.__load_events[i]();
			}
			window.__load_events = null;
		};

		// for Mozilla/Opera9
		if (document.addEventListener) {
			document.addEventListener("DOMContentLoaded", init, false);
		}

		// for Internet Explorer
		/*@cc_on @*/
		/*@if (@_win32)
			document.write("<scr"+"ipt id='__ie_onload' defer src='javascript:false;'><\/scr"+"ipt>");
			var script = document.getElementById("__ie_onload");
			script.onreadystatechange = function() {
				if (this.readyState == "complete") {
					init(); // call the onload handler
				}
			};
		/*@end @*/

		// for Safari
		if (/KHTML|WebKit/i.test(navigator.userAgent)) { // sniff
			window.__load_timer = setInterval(function() {
				if (/loaded|complete/.test(document.readyState)) {
					init(); // call the onload handler
				}
			}, 10);
		}

		// for other browsers
		var oldonload = window.onload;
		if (typeof(window.onload) != 'function') {
			window.onload = init;
		} else {
			window.onload = function() {
				init();
				if (oldonload) {
					oldonload();
				}
			}
		}

		// create event function stack
		window.__load_events = [];
	}

	// add function to event stack
	window.__load_events[window.__load_events.length] = func;
}




/*
 * Based on:
 * code from "Crossbrowser DOM Scripting: Event Handlers"
 *   by Scott Andrew (http://www.scottandrew.com/weblog/articles/cbs-events)
 * code from "Executing JavaScript on page load"
 *   by S. Willison (http://simon.incutio.com/archive/2004/05/26/addLoadEvent)
 *
 * Modifications by David "Dewi" Young.
 *
 * Notes:
 * Take care with Capture mode; IE lacks support and Firefox is wrong.
 * On pre-version-5 browsers, event removal is likely to fail.
 *
 */

function addEvent(obj, evType, fn, useCapture){
	if (obj.addEventListener){
		// W3C Registration
		obj.addEventListener(evType, fn, useCapture);
		return true;
	} else if (obj.attachEvent){
		// IE Registration
		return obj.attachEvent('on' + evType, fn);
	} else {
		// Fall back to traditional
		var eventName = 'on' + evType;
		var oldEvent = obj[eventName];
		if (typeof(oldEvent) != 'function') {
			obj[eventName] = fn;
		} else {
			obj[eventName] = function() {
				if (oldEvent) {
					oldEvent();
				}
				fn();
			}
		}
		return true;
	}
}

function removeEvent(obj, evType, fn, useCapture){
	if (obj.removeEventListener){
		// W3C Deregistration
		obj.removeEventListener(evType, fn, useCapture);
		return true;
	} else if (obj.detachEvent){
		// IE Deregistration
		obj.detachEvent('on' + evType, fn);
		return true;
	} else {
		// Sorry, insufficient browser support.
		return false;
	}
}






/*****************************************************************************\
* Effects                                                                     *
\*****************************************************************************/



function textShadowsInit() {
	// Usage:
	// Controlled by attribute tobor-text-shadow="color x-offset y-offset"
	// * Element must have parent, which will end up with position: relative.
	// * Color must be numeric (in IE, anyway)
	// * Offsets must have unit.
	// Problems:
	// * Avoid margins and padding (particularly horizontal) on the
	//   element, as IE will double it on the shadow and selectively
	//   ignore the need for wrapping.
	// * Wrap points of original and shadow differ slightly due to the offset;
	//   avoid the possibility of wrapping where possible.

	// get live elements list
	var els = document.getElementsByTagName('*');

	// get dead elements list
	var dels = new Array(0);
	for (var ii = 0; ii < els.length; ii++) {
		dels[dels.length] = els[ii];
	}

	// iterate elements
	for (var ii = 0; ii < dels.length; ii++) {
		var el = dels[ii];
		var paramstr = el.getAttribute('tobor-text-shadow');

		// do only elements with attribute
		if (undefined != paramstr) {
			var params = paramstr.split(' ');

			// do only elements with valid attribute value
			if (params.length >= 3) {
				var color = params[0];
				var xx = params[1];
				var yy = params[2];

				// parent position relative (to act as container)
				el.parentNode.style.position = 'relative';


				// copy element without attribute
				var copy = el.cloneNode(true);
				copy.removeAttribute('tobor-text-shadow');

				// original comes forward
				el.style.position = 'relative';
				el.style.zIndex = 1;

				// copy becomes shadow
				copy.style.color = color;
				copy.style.position = 'absolute';
				copy.style.marginTop = '0';    // or FF applies twice
				copy.style.top = yy;
				copy.style.left = xx;

				// insert copy
				el.parentNode.insertBefore(copy, el);
			}
		}
	}
}







/*
 * Sets the opacity of an element. Known to work with Firefox 2, Opera 9, IE 6
 * and 7, and Safari (Windows). IE requires that the element style has an
 * opacity filter, which can be initialised with the following property...
 *
 *     filter:alpha(opacity=100);
 *
 * Fails silently if techniques are unavailable.
 *
 * Parameters:
 *     el       the element to manipulate
 *     opacity  the desired opacity, from 0 to 1.
 */
function setOpacity(el, opacity) {
	if ('undefined' != typeof(el.style.opacity)) {
		el.style.opacity = opacity;
	} else if ('undefined' != typeof(el.style.MozOpacity)) {
		el.style.MozOpacity = opacity;
	} else if ('undefined' != typeof(el.filters) && 'undefined' != typeof(el.filters.alpha) && 'undefined' != typeof(el.filters.alpha.opacity)) {
		el.filters.alpha.opacity = opacity * 100;
	}
}

/*
 * Transitions a value linearly from here to a target, executing actions
 * throughout and on completion.
 *
 * Properties:
 *     this.current       the current counter value
 *     this.target        the target counter value
 *     this.delta         the change in counter value per cycle
 *     this.fps           the number of cycles per second
 *     this.actionUpdate  the function to call each cycle with the new value
 *     this.actionPost    a function to call upon completion
 *     this.disabled      dictates whether this animation can continue
 */
function Animation() {

	/* properties */
	this.current = 0;
	this.target = 1;
	this.delta = 0.01;
	this.fps = 50;
	this.actionCatch = null;
	this.actionUpdate = null;
	this.actionPost = null;
	this.disabled = false;

	this.run = function() {
		try {
			if (!this.disabled) {
				/* validation */
				var togo = this.target - this.current;
				if (togo < 0 && this.delta > 0) {
					this.delta = -this.delta;
				}

				var changed = false;

				if (togo != 0) {
					if (Math.abs(togo) <= Math.abs(this.delta)) {
						this.current = this.target;
					} else {
						this.current += this.delta;
					}
					changed = true;
				}

				if (changed && 'function' == typeof(this.actionUpdate)) {

					/* perform action */
					this.actionUpdate(this.current);

					/* schedule next */
					var _lt_an = this;
					window.setTimeout(function() { _lt_an.run(); }, 1000 / this.fps);

				} else if ('function' == typeof(this.actionPost)) {

					/* clear and discharge post action */
					var _post = this.actionPost;
					this.actionPost = null;
					_post();
				}
			}
		} catch(err) {
			if ('function' == typeof(this.actionCatch)) {
				this.actionCatch(err);
			} else {
				throw err;
			}
		}
	}
}

