var gAnimationStack 				= new Array();
var isWindows						= (navigator.userAgent.indexOf("Windows") >= 0);
var isLinux							= (navigator.userAgent.indexOf("Linux") >= 0);
var isMac							= (navigator.userAgent.indexOf("Mac") >= 0);
var isIE 							= (document.all && !window.opera ? true : false);
var isFirefox						= (navigator.userAgent.indexOf("Firefox") >= 0);
var isOpera 						= (window.opera ? true : false);
var isSafari						= (navigator.userAgent.indexOf("Safari") >= 0);
var isCamino						= (navigator.userAgent.indexOf("Camino") >= 0);
var isKonquerer						= (navigator.appName == "Konquerer");
var ieVersion						= -1;

if (isIE){
	var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
	
	if (re.exec(navigator.userAgent) != null){
		ieVersion = parseFloat(RegExp.$1);
	}
}


/* *********** Form Functions *********** */
// IfEmpty
function errorIfEmpty(id, errorMsg){
	return errorIfEmptyV(document.getElementById(id).value, errorMsg);
}


function errorIfEmptyV(value, errorMsg){
	if (isEmptyV(value)){
		return errorMsg;
	} else {
		return "";
	}
}


function isEmpty(id){
	return isEmptyV(document.getElementById(id).value);
}


function isEmptyV(value){
	if (value.length == 0){
		return true;
	} else {
		return false;
	}
}


// IfLessThen
function errorIfLessThan(id, length, errorMsg){
	return errorIfLessThanV(document.getElementById(id).value, length, errorMsg);
}


function errorIfLessThanV(value, length, errorMsg){
	if (isLessThanV(value, length)){
		return errorMsg;
	} else {
		return "";
	}
}


function isLessThan(id, length){
	return isLessThanV(document.getElementById(id).value, length);
}


function isLessThanV(value, length){
	if (value.length < length){
		return true;
	} else {
		return false;
	}
}


// IfNotEqualTo
function errorIfNotEqual(id1, id2, errorMsg){
	return errorIfNotEqualV(document.getElementById(id1).value, document.getElementById(id2).value, errorMsg);
}


function errorIfNotEqualV(value1, value2, errorMsg){
	if (value1 == value2){
		return "";
	} else {
		return errorMsg;
	}
}


// IfEqualTo
function errorIfEqual(id1, id2, errorMsg){
	return errorIfEqualV(document.getElementById(id1).value, document.getElementById(id2).value, errorMsg);
}


function errorIfEqualV(value1, value2, errorMsg){
	if (value1 == value2){
		return errorMsg;
	} else {
		return "";
	}
}


// IfEqual
function isEqual(id1, id2){
	return isEqualV(document.getElementById(id1).value, document.getElementById(id2).value);
}


function isEqualV(value1, value2){
	if (value1 == value2){
		return true;
	} else {
		return false;
	}
}


// IfNotValidEmail
function errorIfNotValidEmail(id, errorMsg){
	return errorIfNotValidEmailV(document.getElementById(id).value, errorMsg);
}


function errorIfNotValidEmailV(value, errorMsg){
	if (isValidEmail(value)){
		return "";
	} else {
		return errorMsg;
	}
}


function isChecked(id){
	return document.getElementById(id).checked;
}


function formatErrorMessage(errorMsg){
	var errors = errorMsg.split("|");
	var output = "<ul class=\"error-list\">";
	
	for (var i = 0; i < errors.length - 1; i++){
		output += "<li>" + errors[i] + "</li>";
	}
	
	output += "</ul>";
	
	if (errors.length == 0){
		return "";
	} else {
		return output;
	}
}


// ---------- Misc functions ----------
function isValidEmail(email){
	var re = new RegExp("^([-!#\$%&'*+./0-9=?A-Z^_`a-z{|}~])+@([-!#\$%&'*+/0-9=?A-Z^_`a-z{|}~]+\\.)+[a-zA-Z]{2,6}\$","i");  
	
	return re.test(email);
}


function selectOption(list){
	document.getElementById(list).checked = true;
}


function getSelectValue(element){
	if (element.selectedIndex != -1){
		return element.options[element.selectedIndex].value;
	} else {
		return null;	// Nothing selected
	}
}


// Returns a URL encoded string of the select's selected values
function getSelectValuesEncodedForURL(id){
	var select = document.getElementById(id);
	var encodedName = encodeURIComponent(select.name);
	var querystring = "";
	
	if (select.selectedIndex != -1){
		for (var i = 0; i < select.options.length; i++){
			if (select.options[i].selected){
				querystring += encodedName + "=" + encodeURIComponent(select.options[i].value) + "&";
			}
		}
		
		querystring = querystring.substr(0, querystring.length - 1);
	}
	
	return querystring;
}


function setListboxValue(listId, selectedValue){
	var select = document.getElementById(listId);
	
	for (var i = 0; i < select.options.length; i++){
		if (select.options[i].value == selectedValue){
			select.options[i].selected = true;
		} else {
			select.options[i].selected = false;
		}
	}
}


// Returns a URL encoded string of the textbox's value
function getTextboxValueEncodedForURL(id){
	var querystring = "";
	var textbox = document.getElementById(id);
	
	querystring = encodeURIComponent(textbox.name) + "=" + encodeURIComponent(textbox.value);
	
	return querystring;
}


function getCheckboxValueEncodedForURL(id){
	var querystring = "";
	var checkbox = document.getElementById(id);
	
	if (checkbox.checked){
		querystring = encodeURIComponent(checkbox.name) + "=" + encodeURIComponent(checkbox.value);
	} else {
		querystring = "";
	}
	
	return querystring;
}


// Animates the opening/closing of the error message box
// If animate is false, it does the fallback animation (instant)
// callback is optional, if provided, callback will be invoked when the animation has ended
// duration is optional, defaults to 350
function animateBox(element, expanding, animate, duration, callback){
	// Browser FX whitelist
	if ((
		(isWindows && ((isIE && ieVersion >= 6.0) || isFirefox || isOpera)) ||
		(isLinux && (isFirefox)) ||
		(isMac && (isOpera))
		) && animate
	){
		// Do fancy animation
		if (gAnimationStack[element.id] != null){
			// Reverse the direction of the current animation
			var animate 			= gAnimationStack[element.id];
			var time 				= new Date().getTime();
			
			animate.percent			= 1 - animate.percent;
			animate.endTime			= time + animate.duration;
			animate.startTime 		= (animate.percent * animate.endTime - time) / (animate.percent - 1);
			animate.expanding 		= expanding;
			animate.currentFrame 	= 1;
		} else {
			// Starting a new animation
			element.style.display = "block";
			
			var animate 			= new animateStackItem();
			animate.element 		= element;
			animate.expanding 		= expanding;
			animate.startTime 		= new Date().getTime();
			animate.duration		= (typeof(duration) == "number" ? duration : 350);
			animate.height			= getElementHeight(element);
			animate.callback		= (typeof(callback) == "function" ? callback : null);
			animate.intervalID 		= window.setInterval("animateBoxStep('" + element.id + "')", 33);	// Limits animation to 30 fps
	
			gAnimationStack[element.id] = animate;
		}
	} else {
		// Do simple animation
		element.style.display = (expanding ? "block" : "none");
		element.style.overflow = "visible";
		element.style.height = "auto";

		if (typeof(callback) == "function"){
			callback();
		}
	}
}


// This is called on every loop of the animation by window.setInterval()
function animateBoxStep(elementID){
	var time 	= new Date().getTime();
	var animate = gAnimationStack[elementID];
	
	if (animate.currentFrame == 0){
		animate.frameLength = 1000 / animate.fps;
		animate.lastTime 	= time - animate.frameLength;
		animate.endTime 	= animate.startTime + animate.duration;
	}
	
	if (time >= animate.endTime){
		// End animation
		animate.element.style.height = (animate.expanding ? animate.height : 0) + "px";
		animate.element.style.overflow = (animate.expanding ? "visible" : "hidden");
	    setElementOpacity(animate.element, (animate.expanding ? 1 : 0));
		
		window.clearInterval(animate.intervalID);
		delete gAnimationStack[elementID];

		if (typeof(animate.callback) == "function"){
			animate.callback();
		}
	} else {
		// Check if it time to render another frame
		if (animate.lastTime + animate.frameLength <= time){
			animate.percent = (time - animate.startTime) / (animate.endTime - animate.startTime);	// Percent of animation progress

			var percent = (animate.expanding ? animate.percent : (1 - animate.percent));			// Reverses percent if we are going in the other direction

			animate.element.style.overflow = "hidden";
			animate.element.style.height = (Math.sin(percent * Math.PI / 2) * animate.height) + "px";	// Smoothly animate motion
			setElementOpacity(animate.element, percent);
			animate.lastTime = time;
			animate.currentFrame++;
		}
	}
}


// element is a DOM node
function getElementHeight(element){
	if (getCurrentStyle(element, "overflow") == "visible"){
		// The item is already at full height
		return element.offsetHeight;
	} else {
		// The item is currently collapsed, need to temporarily expand it to get its full height
		element.style.visibility 	= "hidden";
		element.style.position 		= "absolute";
		element.style.overflow		= "visible";
		element.style.height		= "auto";
		
		var offsetHeight = element.offsetHeight;
	
		element.style.height		= "0";
		element.style.overflow		= "hidden";
		element.style.position 		= "relative";
		element.style.visibility 	= "visible";

		return offsetHeight;
	}
}


function animateStackItem(){
	this.element 		= null;
	this.expanding 		= null;
	this.callback		= null;
	this.intervalID 	= 0;
	this.startTime 		= 0;	// Time animation started
	this.endTime 		= 0;	// Time animation will end
	this.lastTime		= 0;	// The last time a frame was actually rendered
	this.percent		= 0;	// Percent of animation complete (time-wise)
	this.duration		= 0;	// Length of time the animation will last
	this.currentFrame 	= 0;	// Counts frames rendered
	this.height			= 0;
	this.fps			= 30;	// Frames per second
	this.frameLength	= 0;	// Milliseconds per frame
}


// element is a DOM element, opacity is a 0 to 1 float value
function setElementOpacity(element, opacity){
	if (isIE){
	    element.style.filter 		= "alpha(opacity=" + (opacity * 100) + ")";	// Internet Explorer
	} else {
	    element.style.opacity 		= opacity;	// CSS3 compliant browsers (Opera 9)
	    element.style.MozOpacity 	= opacity;	// Mozilla/FF
	    element.style.KhtmlOpacity 	= opacity;	// Konquerer/Safari
	}
}


// Returns the style of an element not explicit set via CSS or javascript
// element is a DOM element
// property is a string, with the name of the CSS property to return
// Supported by IE, Opera 7+, Mozilla, iCab 3+ and Konqueror 3.5+
function getCurrentStyle(element, property){
	if (element.currentStyle){
		return eval("element.currentStyle." + property);
	} else if (window.getComputedStyle){
		return eval("window.getComputedStyle(element, null)." + property);
	}
	
	return "";
}


// Closes the box if needed, then changes it's class, and re-opens it
function setFormMessage(boxId, messageId, expanded, animate, className, content, callback){
	var boxElement = document.getElementById(boxId);
	var messageElement = document.getElementById(messageId);
	
	if (boxElement.style.display == "block" && animate){
		animateBox(boxElement, false, animate, null, function (){
			boxElement.className = className
			messageElement.innerHTML = content;
			animateBox(boxElement, expanded, true, null, callback);
		});
	} else {
		boxElement.className = className
		messageElement.innerHTML = content;
		animateBox(boxElement, expanded, animate, null, callback);
	}
}


// content should be URL encoded and look like a querystring
// callbacks are all optional, the xmlhttprequest object is provided as a parameter when they are called
// onFailureCallback is when a comm/server error has occured, not with the form validation (since it doesn't do any)
function ajaxPost(url, content, onSuccessCallback, onFailureCallback, onReadyStateChange){
	var xmlhttp = new XMLHttpRequest();		// XMLHttpRequest is browser neutralized by Sarissa

	xmlhttp.open("POST", url, true);
	xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	xmlhttp.onreadystatechange = function() {
									if (typeof(onReadyStateChange) == "function"){
										onReadyStateChange(xmlhttp);
									}

									if (xmlhttp.readyState == 4) {
										if (xmlhttp.status == 200){
											if (typeof(onSuccessCallback) == "function"){
												onSuccessCallback(xmlhttp);
											}
										} else {
											if (typeof(onFailureCallback) == "function"){
												onFailureCallback(xmlhttp);
											}
										}
									}
	}
	
	xmlhttp.send('ajax=1' + (content ? '&' +content : ''));
//	delete xmlhttp;
}