function hoverLink(div) {
	addClassName(div, 'hoverMainLink');
}
function leaveLink(div) {
	removeClassName(div, 'hoverMainLink');
}
function hoverSideLink(li) {
	addClassName(li, 'hoverSideLink');
}
function leaveSideLink(li) {
	removeClassName(li, 'hoverSideLink');
}

// Add a class to an element (if it hasn't already been added)
function addClassName(el, className) {
	if( el.className && el.className != '' ) {
		if( el.className.indexOf(className) == -1 ) {
			el.className += ' ' + className;
		}
	} else {
		el.className = className;
	}
}
// Remove a class to an element (if it exists)
function removeClassName(el, className) {
	if( el.className && el.className != '' ) {
		var index = el.className.indexOf(className);
		if( index != -1 ) {
			el.className = el.className.substr(0, index) + el.className.substr(index+className.length);
		}
	}
}

var ERROR_CLASS = 'errorInput';

// Automatically validate form data by checking specific hidden fields
function validate(form) {
	var inputs = form.elements;
	var errorStr = "";
	for( var i=0; i<inputs.length; i++ ) {
		var thisinput = inputs[i];
		var thisname = inputs[i].name;
		var thisval = inputs[i].value;
		removeClassName(thisinput, ERROR_CLASS);
		
		// Check for hidden inputs that add constraints to the data
		while( (i+1 < inputs.length) && (inputs[i+1].name.indexOf(thisname) == 0) && (inputs[i+1].name != thisname) ) {
			i++;
			var constraint = inputs[i].name.substr(thisname.length+1);
			switch( constraint ) {
				case 'req':
					// Check that the input is non-empty
					if( thisval == "" ) {
						errorStr += inputs[i].title + "\n";
						addClassName(thisinput, ERROR_CLASS);
					}
					break;
				case 'type':
					// Check that the input matches the specified type
					var type = inputs[i].value;
					switch( type ) {
						case 'int':
							var intval = parseInt(thisval);
							if( (thisval != "") && (isNaN(intval) || (intval != thisval)) ) {
								errorStr += inputs[i].title + "\n";
								addClassName(thisinput, ERROR_CLASS);
							}
							break;
						case 'float':
							var floatval = parseFloat(thisval);
							if( (thisval != "") && (isNaN(floatval) || (floatval != thisval)) ) {
								errorStr += inputs[i].title + "\n";
								addClassName(thisinput, ERROR_CLASS);
							}
							break;
						case 'alphanum':
							var ok = true;
							if( (thisval != "") && !thisval.match(/^[a-zA-Z0-9 ]+$/) ) {
								errorStr += inputs[i].title + "\n";
								addClassName(thisinput, ERROR_CLASS);
							}
							break;
						case 'email':
							// regexp taken from http://www.quirksmode.org/js/mailcheck.html
							var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
							if( (thisval != "") && !filter.test(thisval) ) {
								errorStr += inputs[i].title + "\n";
								addClassName(thisinput, ERROR_CLASS);
							}
							break;
						case 'date':
							var dateOk = false;
							if( thisval == "" )
								dateOk = true;
							else if( thisval.match(/^\d{1,2}[\.\/-]\d{1,2}[\.\/-](\d\d){1,2}$/) )
								dateOk = true;
							else if( thisval.match(/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\. \d{1,2}, (\d\d){1,2}$/i) )
								dateOk = true;
							else if( thisval.match(/^((jan|febr)uary|march|april|may|ju(ne|ly)|august|(sept|nov|dec)ember|october) \d{1,2}, (\d\d){1,2}$/i) )
								dateOk = true;
							if( !dateOk ) {
								errorStr += inputs[i].title + "\n";
								addClassName(thisinput, ERROR_CLASS);
							}
							break;
						case 'time':
							if( (thisval != "") && !thisval.match(/^\d{1,2}(:\d{2}){1,2}( [apAP][mM])?$/) ) {
								errorStr += inputs[i].title + "\n";
								addClassName(thisinput, ERROR_CLASS);
							}
							break;
						case 'datetime':
							var dateOk = false;
							if( thisval == "" )
								dateOk = true;
							else if( thisval.match(/^\d{1,2}[\.\/-]\d{1,2}[\.\/-](\d\d){1,2}([ -]+\d{1,2}(:\d{2}){1,2}( [apAP][mM])?)?$/) )
								dateOk = true;
							else if( thisval.match(/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\. \d{1,2}, (\d\d){1,2}([ -]+\d{1,2}(:\d{2}){1,2}( [ap]m)?)?$/i) )
								dateOk = true;
							else if( thisval.match(/^((jan|febr)uary|march|april|may|ju(ne|ly)|august|(sept|nov|dec)ember|october) \d{1,2}, (\d\d){1,2}([ -]+\d{1,2}(:\d{2}){1,2}( [ap]m)?)?$/i) )
								dateOk = true;
							if( !dateOk ) {
								errorStr += inputs[i].title + "\n";
								addClassName(thisinput, ERROR_CLASS);
							}
							break;
						case 'currency':
							if( (thisval != "") && !thisval.match(/^[$£]?[\d][\d\.\,]*$/) ) {
								errorStr += inputs[i].title + "\n";
								addClassName(thisinput, ERROR_CLASS);
							}
							break;
					}
					break;
				case 'match':
					// Check that the input value matches another input's value
					var other = inputs[inputs[i].value];
					if( other ) {
						if( thisval != other.value ) {
							errorStr += inputs[i].title + "\n";
							addClassName(thisinput, ERROR_CLASS);
							addClassName(inputs[inputs[i].value], ERROR_CLASS);
						} else {
							removeClassName(inputs[inputs[i].value], ERROR_CLASS);
						}
					} else
						alert("bad form data");
					break;
			}
		}
	}
	if( errorStr != "" ) {
		alert("Please fix the following errors:\n" + errorStr);
		return false;
	} else
		return true;
}