// <![CDATA[

// GLOBAL VARIABLES //
var isDOM = (document.getElementById ? true : false); // does the browser support the DOM?
var isIE4 = ((document.all && !isDOM) ? true : false); // is this IE 4?
var isNS4 = (document.layers ? true : false); // is this Netscape 4?
var isDyn = (isDOM || isIE4 || isNS4); // a summary variable that let's us know if we're using a browser that doesn't support dynamic content... like some mobile browsers

// GLOBAL METHODS //

/*  METHOD: getElementsByClassName
	SUMMARY: a DOM tree search that brings back arrays of Elements by both HTML element type and buy CSS class name.
	PARAMS:
		ref - A begining reference point in the DOM to start the search through the tree. To search the whole tree, set ref to document.body .
		tag - The HTML element type that you are searching for (e.g. DIV, SPAN, H1...H5, P, OL, UL, LI, TABLE, etc.).
		cl - The CSS class name that you are searching for.
*/
function getElementsByClassName(ref, tag, cl) {
	var retnode = [];
	var myclass = new RegExp('\\b'+cl.replace('/\-/g', '\\-')+'\\b'); // fix for hyphens
	var elem = ref.getElementsByTagName(tag);
	for (var i = 0; i < elem.length; i++)
	if (myclass.test(elem[i].className)) retnode.push(elem[i]);
	return retnode;
};

/* 	METHOD: isFunction
	SUMMARY: test if the passed refernce is a javascript function.
	PARAMS:
		a - any javascript reference.
*/
function isFunction(a) { return (typeof a == 'function'); }

/* 	METHOD: isObject
	SUMMARY: test if the passed refernce is a javascript object (i.e. not a primative).
	PARAMS:
		a - any javascript reference.
*/
function isObject(a) { return (a && typeof a == 'object') || isFunction(a); }

/* 	METHOD: gR - getReference
	SUMMARY: the magic method! find the element reference of any unique string DOM element id. It also passes through objects that are already DOM elements.
	PARAMS:
		id - any javascript reference or a string id of any unique DOM element.
*/
function gR(id) { // getReference
	if (isObject(id)) return id; //need to check for is in the DOM, but not really... not checking saves a lot of cycles, so write good code instead!
	if (isDOM) return document.getElementById(id);
	if (isIE4) return document.all[id];
	if (isNS4) return document.layers[id];
}

/* 	METHOD: gChd - getChild
	SUMMARY: retrieves child elements of the passed DOM element by number while ignoring TEXT nodes.
	PARAMS:
		ref - any DOM element.
		num - the zero based child index.
*/
function gChd(ref,num) { //getChild
	var r = ref.firstChild;
	while (r && r.nodeType!=1 && r.nextSibling!=null) r = r.nextSibling;
	for (var i=0; i<num; i++) r = gNS(r);
	if (r) return (r.nodeType==1) ? r : false;
	else return false;
}

/* 	METHOD: gNS - getNextSibling
	SUMMARY: retrieves the next sibling element of the passed DOM element while ignoring TEXT nodes.
	PARAMS:
		ref - any DOM element.
*/
function gNS(ref) { // getNextSibling
	var r = ref.nextSibling;
	while(r && r.nodeType!=1 && r.nextSibling!=null) r = r.nextSibling;
	if (r) return (r.nodeType==1) ? r : false;
	else return false;
}

/* 	METHOD: gS - getStyle
	SUMMARY: access to the 'style' control of a DOM element, where you make CSS changes.
	PARAMS:
		ref - any DOM element.
*/
function gS(ref) { // getStyle
	return (isNS4 ? ref : ref.style);
}

/* 	METHOD: hD - hideDiv
	SUMMARY: controls the visibilty attribute of a DOM elements. 'Visibility' changes if the element is visible or invisibile. Invisible elements still take up space on the page.
	PARAMS:
		id - any DOM element that supports the 'visibility' attribute.
		hide - a boolean to hide or show the element.
*/
function hD(id,hide) { // hideDiv
	var s = gS(gR(id));
	if (s) s.visibility = (hide) ? 'hidden' : '';
}

/* 	METHOD: dD - displayDiv
	SUMMARY: controls the display attribute of a DOM elements. 'Display' changes if the element is rendered or not. Not rendered elements take up no space on the page.
	PARAMS:
		id - any DOM element that supports the 'display' attribute.
		display - a boolean to display the element or not.
*/
function dD(id,display) { //displayDiv
	var s = gS(gR(id));
	if (s) s.display = (display) ? '' : 'none';
}

/* 	METHOD: aC - Add CSS Class
	SUMMARY: adds a CSS class to the passed DOM element (e.g. Add class: aC('myDiv', 'myclass').).
	PARAMS:
		div - any DOM element that supports the 'class' attribute.
		cl - a CSS class
*/
function aC(div, cl) { // add CSS Class
	var c = gR(div);
	if (c.className.indexOf(cl) == -1) c.className += c.className ? ' '+cl : cl;
}

/* 	METHOD: rC - Remove CSS Class
	SUMMARY: removes a CSS class from the passed DOM element (e.g. Remove class: rC('myDiv', 'myclass').).
	PARAMS:
		div - any DOM element that supports the 'class' attribute.
		cl - a CSS class
*/
function rC(div, cl) { // remove CSS Class
	var c = gR(div);
	var rep = c.className.match(' '+cl) ? ' '+cl : cl;
	c.className = c.className.replace(rep, '');
}

/* 	METHOD: cEB - Cancel Event Bubble
	SUMMARY: There are two different models of event propegation on the web: Event Bubbling and Event Capturing. This method stops both, preventing a lot extra processing from being done.
	PARAMS:
		e - the event
*/
function cEB(e) { // cancel event bubble
	var e = e || window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
}

/* 	METHOD: ro - Rollover
	SUMMARY: Simple Image Rollovers (NOTE: No preloading, preload your separately images if you want)
	PARAMS:
		el - a IMAGE element.
		img - a URL to the rollover
*/
function ro(el,img) { // rollover
	if (el.defaultSrc) {
		el.src = el.defaultSrc;
		el.defaultSrc = null;
	} else {
		el.defaultSrc = el.src;
		el.src = img;
	}
}

/* 	METHOD: Traverse
	SUMMARY: a function that recursivly traverses the DOM tree, invoking the callback function on every node.
	PARAMS:
		node - a DOM element
		func - a callback function that accepts a DOM node as input
*/
function traverse(node, func) {
	if (func) func(node);
		var i=0;
		while (node.childNodes[i]) {
		if (node.childNodes[i].nodeName!="#text") {
			if (func) func(node.childNodes[i]);
			traverse(node.childNodes[i], func)
		}
		i++;
	}
}

/* 	METHOD: sO - setOpacity
	SUMMARY: sets the opacity of a DOM element cross-browser style
	PARAMS:
		id - a DOM element
		value - a numeric representation of the opacity from 0-100. 0 = invisible , 100 = fully opaque
*/
function sO(id,value) { // setOpacity
	var s = gS(gR(id));
	if (s) {
		if (value >= 100) value = 99.999; // fix for Mozilla < 1.5b2
		if (value < 0) value = 0;
		s.opacity = value/100;
		s.MozOpacity = value/100;
		s.KhtmlOpacity = value/100;
		s.filter = 'alpha(opacity=' + value + ')'; // note: doesn't protect exisiting filters
	}
	return true;
}

/* 	METHOD: gO - getOpacity
	SUMMARY: gets the opacity of a DOM element cross-browser style
	PARAMS:
		id - a DOM element
	RETURNS: a numeric representation of the opacity from 0-100. 0 = invisible , 100 = fully opaque
*/
function gO(id) { // getOpacity
	var s = gS(gR(id));
	if (s) {
		if (s.opacity) return (s.opacity * 100);
		if (s.MozOpacity) return (s.MozOpacity * 100);
		if (s.KhtmlOpacity) return (s.KhtmlOpacity * 100);
		if (s.filters) return s.filters.alpha.opacity;
	}
	return 100;
}

/* 	METHOD: gDim - get Dimensions
	SUMMARY: gets computed dimensions of a rendered DOM element, caching it for future lookups
	PARAMS:
		id - a DOM element
	RETURNS: a numeric representation of the dimensions of the element
*/
function gDim(div) { // get Dimensions
	var r = gR(div);
	if (!r._ht) { // check for already computed
		if (r.offsetHeight) {
			r._ht = r.offsetHeight;
			r._wt = r.offsetWidth;
		} else {
			r._ht = parseInt(document.defaultView.getComputedStyle(r, "").getPropertyValue("height"));
			r._wt = parseInt(document.defaultView.getComputedStyle(r, "").getPropertyValue("width"));
	    }
	}
	return [r._ht, r._wt];
}

/* 	METHOD: rgDim - re-get Dimensions
	SUMMARY: gets computed dimensions of a rendered DOM element, bypassing the current cached value
	PARAMS:
		id - a DOM element
	RETURNS: a numeric representation of the dimensions of the element
*/
function rgDim(div) { // re-get (recompute) Dimensions
	var r = gR(div);
	r._ht = r._wt = null;
	return gDim(div);
}

/* 	METHOD: arrayPrint
	SUMMARY: print out the values of a javascript array
	PARAMS:
		arr - a javascript array
	RETURNS: a string representation of the array
*/
function arrayPrint(arr) { return "['" + arr.join("', '") + "']"; }

/* 	METHOD: createRequest
	SUMMARY: if the browser supports it, this function returns an XMLHTTPRequest like object
	RETURNS: a XMLHTTPRequest object or null
*/
function createRequest() {
	var factory = [ function() {return new XMLHttpRequest()},
					function() {return new ActiveXObject("Msxml2.XMLHTTP")},
					function() {return new ActiveXObject("Msxml3.XMLHTTP")},
					function() {return new ActiveXObject("Microsoft.XMLHTTP")}];
	var ret = false;
	for (var i=0; i<factory.length; i++) {
		try {
			ret = factory[i]();
		} catch (e) {
			continue;
		}
		break;
	}
	return ret;
}

/* 	METHOD: mD - modifiy Div
	SUMMARY: this is the core animation function. It modifies pixel CSS attributes (e.g. height, width, top, left, right, bottom, opacity, etc.)
	PARAMS:
		div - a DOM element
		dims - an array of pixel CSS properties to modify
		start - an array of starting pixel positions
		stop - an array of ending pixel positions
		lastPos - an array of current pixel positions
		duration - the time duration for the animation
		after - a callback function to invoke after the animation is complete
*/
function mD(div, dims, start, stop, lastPos, duration, after) { // Modify Div
	var frame_speed = 40; // 40ms per step = 25fps = fluid motion
	var total_steps = Math.round(duration / frame_speed);
	if (total_steps<1) total_steps = 1;
	var s = gS(gR(div));
	for (var i=0; i<dims.length; i++) {
		var ending = 'px';
		var p1 = start[i];
		var p2 = stop[i];
		var nextmove;
		if (start[i].toString().substring(0,1)=='#') { // animate colors
			ending = '';
			p1 = pC(start[i]);
			p2 = pC(stop[i]);
			nextmove = p1.fadeTo(p2, lastPos/total_steps);
		} else {
			if (isNaN(p1)) p1 = 0;
			if (isNaN(p2)) p2 = 0;
			nextmove = parseInt(p1) + Math.ceil((p2 - p1) * Math.sin(Math.PI/2*(lastPos/total_steps)));
		}
		if (dims[i]=='opacity') sO(gR(div), nextmove);
		else {
			var prop = s[dims[i]];
			if (!prop) prop = s[dims[i]] = p1 + ending;
			if (s) s[dims[i]] = nextmove + ending;
   		}
	}
	if (lastPos < total_steps)
		setTimeout("mD('"+div+"',"+arrayPrint(dims)+","+arrayPrint(start)+","+arrayPrint(stop)+","+(lastPos+1)+","+duration+","+after+")", frame_speed);
	else if (after) after(div);
}

/* 	METHOD: aLE - Add Load Event
	SUMMARY: attaches a callback function for when the page finishes loading
	PARAMS:
		func - a callback function to call on window.load
*/
function aLE(func) { //addLoadEvent
	var old = window.onload;
	if (typeof window.onload != 'function') window.onload = func;
	else window.onload = function() { old(); func(); }
}

/* 	METHOD: bC - Bake Cookie
	SUMMARY: sets a cookie
	PARAMS:
		name - the name for the cookie value
		value - the value of the cookie name
		days - how many days the cookie is active for
*/
function bC(name, value, days) {
	var expires = "";
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		expires = "; expires="+date.toGMTString();
	}
	document.cookie = name+"="+value+expires+"; path=/";
}

/* 	METHOD: eC - Eat Cookie
	SUMMARY: reads a cookie
	PARAMS:
		name - the name for the cookie value to be returned
	REUTRNS: a string value or null	
*/
function eC(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0; i < ca.length; i++) {
		var c = ca[i];
		while (c.charAt(0) == ' ') c = c.substring(1, c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
	}
	return null;
}

/* 	METHOD: getPos - get Position
	SUMMARY: returns the position of the top left corner of the element
	REUTRNS: 
		x - horizontal position of the element in pixels
		y - vertical position of the element in pixels
		
    TODO: make it work in IE.		
*/
function getPos(ev) {
	var left = 0;
	var top =  0;
	if (ev.offsetParent) { // Does IE "Have Layout"?
		while (ev) {
			left += parseInt(ev.offsetLeft).NaN0() + (ev.currentStyle?(parseInt(ev.currentStyle.borderLeftWidth)).NaN0():0);
			top += parseInt(ev.offsetTop).NaN0()  + (ev.currentStyle?(parseInt(ev.currentStyle.borderTopWidth)).NaN0():0);
			ev = ev.offsetParent;
		}
	}
	return {x:left, y:top};
}

function getPosition(e) {
	var ev = e || window.event;
	if (ev.target) ev = ev.target;
	var left = 0;
	var top =  0;
	while (ev) {
		left += ev.offsetLeft + (ev.currentStyle?(parseInt(ev.currentStyle.borderLeftWidth)).NaN0():0);
		top += ev.offsetTop  + (ev.currentStyle?(parseInt(ev.currentStyle.borderTopWidth)).NaN0():0);
		ev = ev.offsetParent;
	}
	return {x:left, y:top};
}

function cd(el) { // clearDefault
	if (el.value && el.defaultValue && el.defaultValue == el.value) el.value = '';
} 

/* 	METHOD: screenDims - Screen Dimensions
	SUMMARY: returns the dimensions of the browser window / parent frame
	REUTRNS: 
		x - width of the window in pixels
		y - height of the window in pixels
*/
function screenDims() {
	var x,y;
	if (self.innerHeight) {
		x = self.innerWidth;
		y = self.innerHeight;
	} else if (document.documentElement && document.documentElement.clientHeight) {
		x = document.documentElement.clientWidth;
		y = document.documentElement.clientHeight;
	} else if (document.body) {
		x = document.body.clientWidth;
		y = document.body.clientHeight;
	}
	return {x:x, y:y};
}

Number.prototype.NaN0 = function() { return isNaN(this) ? 0 : this; }

/* 	METHOD: pC - parseColor
	SUMMARY: returns the dimensions of the browser window / parent frame
	REUTRNS: 
		x - width of the window in pixels
		y - height of the window in pixels
*/
function pC(color_string) { // parseColor
	var t = new Object();
    color_string = color_string.replace(/[ #]/g,'').toLowerCase();
    var color_defs = [ { // array of color definition objects 
            re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
            process: function (bits) { return [ parseInt(bits[1]), parseInt(bits[2]), parseInt(bits[3])]; }
        }, {   
			re: /^(\w{2})(\w{2})(\w{2})$/,
            process: function (bits) { return [parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16)]; }
        }, {   
			re: /^(\w{1})(\w{1})(\w{1})$/,
            process: function (bits) { return [ parseInt(bits[1] + bits[1], 16), parseInt(bits[2] + bits[2], 16), parseInt(bits[3] + bits[3], 16)]; }
        }
    ];

    for (var i = 0; i < color_defs.length; i++) { // search through the definitions to find a match
		var bits = color_defs[i].re.exec(color_string)
        if (bits) {
			channels = color_defs[i].process(bits);
			t.r = parseInt(channels[0]);
            t.g = parseInt(channels[1]);
            t.b = parseInt(channels[2]);
        }
    }

    // validate/cleanup values
    t.r = (t.r < 0 || isNaN(t.r)) ? 0 : ((t.r > 255) ? 255 : t.r);
    t.g = (t.g < 0 || isNaN(t.g)) ? 0 : ((t.g > 255) ? 255 : t.g);
    t.b = (t.b < 0 || isNaN(t.b)) ? 0 : ((t.b > 255) ? 255 : t.b);

    t.toRGB = function () { return 'rgb(' + t.r + ', ' + t.g + ', ' + t.b + ')'; }

    t.toHex = function () {
        var r = t.r.toString(16);
        var g = t.g.toString(16);
        var b = t.b.toString(16);
        if (r.length == 1) r = '0' + r;
        if (g.length == 1) g = '0' + g;
        if (b.length == 1) b = '0' + b;
        return '#' + r + g + b;
    }

	t.toString = function() { return t.toHex(); }

	t.fadeTo = function (color, percentage) {
		t.r = t.r + Math.round((color.r - t.r) * percentage);
		t.g = t.g + Math.round((color.g - t.g) * percentage);
		t.b = t.b + Math.round((color.b - t.b) * percentage);
		return t.toHex();
	}

	return t;
}

/* ]]> */
