/* This isn't the place for this, but it's an easy add */
if (window != top) top.location.href = location.href;

/*
Functions by Steven Moss (steve@mossianic.com)
These functions validate form responses, there is one master function which should be called on submission and per-element functions which can be called independently
Note: This is back-to-front really, should start with element names, not IDs
 */

// bWillValidate turns true after an initial submission and then stays that way
var bWillValidate = false;

// Form validation: A list of specified form elements required
function vValidateRequiredElements(sFailureAlert, bPromptFirst) {
	var bError, bElementIsValid, bFormIsValid, i, sJumpTo, aRequiredIDs, sRequiredIDs, sElementID, oElement;
	bError = false;
	bElementIsValid = true;
	bFormIsValid = true;
	i = 0;
	bWillValidate = true;  // this is a global variable
	// Get list of required fields from hidden input tag in calling form
	// Required fields (where there is more than one element [i.e. radio selection], any id will cause all identically named elements to be validated! PASSING NAME WILL CAUSE AN ERROR!)
	aRequiredIDs = document.getElementsByName("aRequiredIDs");
	if ( ! aRequiredIDs) { // If required fields were not provided as discrete elements (tags)
		// Build required elements from a string
		sRequiredIDs = document.getElementById("sRequiredIDs");
		sRequiredIDs = sRequiredIDs.replace(/\s/g, ""); // Get rid of any nasty spaces
		aRequiredIDs = sRequiredIDs.split(",");
	}
	if (aRequiredIDs != "") {
		for (i = 0; i < aRequiredIDs.length; i ++) {
			sElementID = aRequiredIDs[i].value;
			//alert(i + " = " + sElementID);
			oElement = document.getElementById(sElementID);
			// Check validity
			bElementIsValid = bValidateElement(oElement);
			if ( ! bError) {
				if ( ! bElementIsValid) {
					bFormIsValid = false;
					bError = true;
				}
			}
		}
	}
	if ( ! bFormIsValid && ! bPromptFirst) {
		alert(sFailureAlert);
		return bFormIsValid;
	} else if ( ! bFormIsValid) {
		// Soft validation, choosing OK causes forced submit
		return confirm(sFailureAlert + " (select 'Cancel' to review or 'OK' to continue)");
	}
	return bFormIsValid;
}

// Delegate validation to the appropriate individual method given a specific element
function bValidateElement(oElement) {
	//alert('bValidateElement(' + oElement.name + ')');
	if ( ! bWillValidate) {
		return true;
	}
	else {
		// For customised behaviour questions get a 'case' of their own...
		switch (oElement.name) {
		case "sEmail" :
			bValidateEmailConfirmation(oElement); // Have to run this in order to update the screen after Email changes!
			return bValidateEmail(oElement);
			break;
		case "sEmailConfirmation" :
			return bValidateEmailConfirmation(oElement);
			break;
		case "sPhone" :
			return bValidatePhone(oElement);
			break;
		default :
			// ...everything else uses default behaviour defined below...
			switch (oElement.type) {
			case "checkbox" :
				// Define properties just for this case
				var iArrayLength, sElementName, sElementNameStem, sMinTagID, sMaxTagID, iMinTagValue, iMaxTagValue, iMinCheckable, iMaxCheckable;
				// Get the count of items in an element array (often null)
				iArrayLength = document.getElementsByName(oElement.name).length;
				if (iArrayLength > 1) {
					// Strip square-brackets from element name (which is an array)
					// Note: Must do the same thing in the HTML!!! Consider doing this for labels too
					sElementName = oElement.name;
					sElementNameStem = sElementName.replace("[]", ""); // Cut brackets (fit my naming convention)
					sMinTagID = "MIN_" + sElementNameStem;
					sMaxTagID = "MAX_" + sElementNameStem;
					try {
						// Get the customised minimum/maximum number of elements in the array which are checkable
						// This will fail unless there are correlating hidden input tags, that's ok
						iMinTagValue = document.getElementById(sMinTagID).value;
					} catch (oError) {}
					try {
						// Get the customised minimum/maximum number of elements in the array which are checkable
						// This will fail unless there are correlating hidden input tags, that's ok
						iMaxTagValue = document.getElementById(sMaxTagID).value;
					} catch (oError) {}
					//alert('iMinTagValue = ' + iMinTagValue);
					//alert('iMaxTagValue = ' + iMaxTagValue);
					if (iMinTagValue > 0) {
						iMinCheckable = iMinTagValue;
					} else {
						iMinCheckable = 0;
					}
					if (iMaxTagValue > 0) {
						iMaxCheckable = iMaxTagValue;
					} else {
						iMaxCheckable = iArrayLength;
					}
					return bValidateCheckboxArray(oElement, iMinCheckable, iMaxCheckable);
				} else {
					return bValidateCheckbox(oElement);
				}
				break;
			case "radio":
				return bValidateRadio(oElement);
				break;
			case "select-one":
				return bValidateSelectOne(oElement);
				break;
			case "text":
				var iArrayLength;
				iArrayLength = document.getElementsByName(oElement.name).length; // the count of items in an element array (often null)
				if (iArrayLength > 0) {
					return bValidateTextArray(oElement, 1, iArrayLength);
				} else {
					return bValidateText(oElement);
				}
				break;
			case 'textarea':
				var iArrayLength;
				iArrayLength = document.getElementsByName(oElement.name).length; // the count of items in an element array (often null)
				if (iArrayLength > 0) {
					return bValidateTextArray(oElement, 1, iArrayLength);
				} else {
					return bValidateText(oElement);
				}
				break;
			default: // This shouldn't happen
				return false;
			}
		}
	}
	return true; // This line will never be reached, but it stops the JS validator from complaining
}

// checkbox array check takes extra parameters iMin iMax for arrays of checkboxes
function bValidateCheckboxArray(oElement, iMin, iMax) {
	var iCount, i, aElements;
	iCount = 0;
	i = 0;
	// Use the individual element object to get an array of all the elements of the same name
	aElements = document.getElementsByName(oElement.name);
	// Note: Could have just returned the checked elements and obsoleted the below?
	// Loop through the element array of the same name (different IDs)
	for (i = 0; i < aElements.length; i ++) {
		if (aElements[i].checked) {
			iCount = iCount + 1;
		}
	}
	// If required # of elements were checked
	if (iCount >= iMin && iCount <= iMax) {
		vMarkComplete(oElement);
		return true;
	} else {
		vMarkIncomplete(oElement);
		return false;
	}
}

// single mandatory checkbox check
function bValidateCheckbox(oElement) {
	if (oElement.checked == false) {
		vMarkIncomplete(oElement);
		return false;
	} else {
		vMarkComplete(oElement);
		return true;
	}
}

// radio check
function bValidateRadio(oElement) {
	var iCount, i, aElements;
	iCount = 0;
	i = 0;
	// Use the individual element object to get an array of all the elements of the same name
	aElements = document.getElementsByName(oElement.name);
	// Note: Could have just returned the checked elements and obsoleted the below?
	// Loop through the element array of the same name (different IDs)
	for (i = 0; i < aElements.length; i ++) {
		if (aElements[i].checked == true) {
			iCount = iCount + 1;
		}
	}
	if (iCount > 0) {
		vMarkComplete(oElement);
		return true;
	} else {
		vMarkIncomplete(oElement);
		return false;
	}
}

// select-one check
function bValidateSelectOne(oElement) {
	if (oElement.value != "") {
		vMarkComplete(oElement);
		return true;
	} else {
		vMarkIncomplete(oElement);
		return false;
	}
}

// text array check takes extra parameters iMin iMax for arrays of text/textarea boxes
function bValidateTextArray(oElement, iMin, iMax) {
	var iCount, i, aElements;
	iCount = 0;
	i = 0;
	// I forget why this try/catch block is here now - dagnammit!
	try {
		// Use the individual element object to get an array of all the elements of the same name
		aElements = document.getElementsByName(oElement.name);
		// Loop through the element array of the same name (different IDs)
		for (i = 0; i < aElements.length; i ++) {
			if (aElements[i].value != "") {
				iCount ++;
			}
		}
		// If required # of elements were checked
		if (iCount >= iMin && iCount <= iMax) {
			vMarkComplete(oElement);
			return true;
		} else {
			vMarkIncomplete(oElement);
			return false;
		}
	} catch(e) {
		vMarkComplete(oElement);
		return true;
	}
}

// text and textarea boxes check
function bValidateText(oElement) {
	if (oElement.value == "") {
		vMarkIncomplete(oElement);
		return false;
	} else {
		vMarkComplete(oElement);
		return true;
	}
}

// Update the on-screen display
// These next two are where errors often occur due to mistakes in html, should put some checks in here to report on which element failed (didn't exist) at least
function vMarkIncomplete(oElement) {
	//alert("vMarkIncomplete(" + oElement.name + ")");
	var sLabelID, oLabel, sElementName, sElementNameStem;
	sElementName = oElement.name;
	sElementNameStem = sElementName.replace("[]", ""); // Labels for array elements must not include brackets
	sLabelID = "L_" + sElementNameStem;
	try {
		oLabel = document.getElementById(sLabelID);
		oLabel.className = "ErrorLabel"; // This class must exist in the CSS
/*		oLabel.style.color = "#f00";
		oLabel.style.borderWidth = "0em 0em 0.1em 0em";
		oLabel.style.borderColor = "#f00";
		oLabel.style.borderStyle = "solid";*/
	} catch(e) {
		// This happens if there's no label, do nothing
		// alert("Missing label: " + sLabelID);	
	}
}

function vMarkComplete(oElement) {
	//alert("vMarkComplete("+oElement.name+")");
	var sLabelID, oLabel, sElementName, sElementNameStem;
	sElementName = oElement.name;
	sElementNameStem = sElementName.replace("[]", ""); // Labels for array elements must not include brackets
	sLabelID = "L_" + sElementNameStem;
	try {
		oLabel = document.getElementById(sLabelID);
		oLabel.className = "StandardLabel"; // This class must exist in the CSS
		/*
		oLabel.style.color = document.body.style.color;
		oLabel.style.borderWidth = "0";
		*/
	} catch(e) {
		// This happens if there's no label, do nothing
		// alert("Missing label: " + sLabelID);
	}
}

// Validate an e-mail address to within an inch of it's life
// Possible improvements that could be made: check against full list of legal characters for e-mail addresses, check against list of valid domain suffixes, check that domain name is 3 or more characters (tricky); not sure about rules for sub-domains, if any
function bValidateEmailAddress(oElement) {
	var sEmail, iPositionOfLastDot, iLength, iDistanceOfLastDotFromEnd, aEmailParts, iPositionOfAtSymbol;
	sEmail = oElement.value;
	sEmail = sTrimWhitespace(sEmail);
	// This causes irritation for IE users (forces the cursor rightwards)
	//document.getElementById("sEmail").value=sEmail; // Update form field
	if (sEmail == "") { // There's nothing there
		vMarkIncomplete(oElement);
		return false;
	}
	// Check for illegal characters
	// Ideally this would use a list of allowed characters, but who knows what all of those are?
	// Might want to add a comma here, but are the old Compuserve e-mail addresses still an issue?
	if (sEmail.search(/[ \~`¬#\<\>\?\;\|\"\'\t\\\/\!£$\%\^\&\*\(\)\+\=\{\}\[\]]/g) != -1) {
		vMarkIncomplete(oElement);
		return false;
	}
	// Find the position of the first dot from the end of the string
	// ...checking against a full list of domain suffixes would be best, but they could change and I don't got forever!
	iPositionOfLastDot = sEmail.lastIndexOf(".");
	if (iPositionOfLastDot == -1) { // No dot
		vMarkIncomplete(oElement);
		return false;
	}
	iLength = sEmail.length;
	iDistanceOfLastDotFromEnd = iLength - iPositionOfLastDot;
	if (iDistanceOfLastDotFromEnd < 3) { // Dot is too close to the end to be part of a domain suffix
		vMarkIncomplete(oElement);
		return false;
	}
	// Count @s
	aEmailParts = sEmail.split("@");
	if (aEmailParts.length != 2) { // Either there is no @ or there's more than one
		vMarkIncomplete(oElement);
		return false;
	}
	// Find the position of the single @
	iPositionOfAtSymbol = sEmail.indexOf("@");
	// Check that it's not the first character
	if (iPositionOfAtSymbol == 0) { // @ is the first character
		vMarkIncomplete(oElement);
		return false;
	}
	// Check for dots next to the @ or other dots
	if (sEmail.indexOf("@.") != -1 || sEmail.indexOf(".@") != -1 || sEmail.indexOf("..") != -1) { // One of the illegal combo's matched
		vMarkIncomplete(oElement);
		return false;
	}
	// Check that the last dot appears after the @
	if (iPositionOfLastDot < iPositionOfAtSymbol) { // There is no domain suffix
		vMarkIncomplete(oElement);
		return false;
	}
	// Check for a dot at the start
	if (sEmail.charAt(0) == ".") { // So close and yet so far
		vMarkIncomplete(oElement);
		return false;
	}
	// Well yeehaaa! God bless you brother!
	vMarkComplete(oElement);
	return true;
}

function bValidateEmail(oElement) {
	var bValid;
	bValid = bValidateEmailAddress(oElement);
	if (bValid) {
		vHideElement("M_sEmail");
	} else {
		vShowElement("M_sEmail");
	}
	return bValid;
}

function bValidateEmailConfirmation(oElement) {
	// Need to re-check here that the first one is valid and that this one matches
	var oElement1, oElement2, bFirstValid;
	oElement1 = document.getElementById('sEmail');
	oElement2 = document.getElementById('sEmailConfirmation');
	bFirstValid = bValidateEmailAddress(oElement1);
	if (bFirstValid && (oElement1.value == oElement2.value)) {
		vMarkComplete(oElement2);
		vHideElement("M_sEmailConfirmation");
		return true;
	} else {
		vMarkIncomplete(oElement2);
		vShowElement("M_sEmailConfirmation");
		return false;
	}
}

// Validate a phone number (check that there are at least 3 numbers in it; wouldn't dare to be any stricter than that, consider: "1-800 INFOMERCIAL")
function bValidatePhone(oElement) {
	var sText, aNumbers, iNumberCount;
	sText = oElement.value;
	aNumbers = sText.match(/\d/g);
	if (aNumbers == null) {
		vMarkIncomplete(oElement);
		vShowElement("M_sPhone");
		return false;
	}
	iNumberCount = aNumbers.length;
	if (iNumberCount < 3) {
		vMarkIncomplete(oElement);
		vShowElement("M_sPhone");
		return false;
	} else {
		vMarkComplete(oElement);
		vHideElement("M_sPhone");
		return true;
	}
}

// Trim whitespace from a given string
function sTrimWhitespace(sText) {
	sText = sText.replace(/\s*\b/, ""); // From start
	sText = sText.replace(/\s*$/, ""); // From end
	return sText;
}

// Hide an element
function vHideElement(sElementID) {
	document.getElementById(sElementID).className = "Hide";
}

// Show an element
function vShowElement(sElementID) {
	document.getElementById(sElementID).className = "Show";
}

/*
// Set the "Day" input box to the numeric value of the current day of the month
// Set the "Month" select menu to the numeric value of the current month
// Set the "Year" input box to the current year in 4 digits
// Usage EG: <body onload="vSetDateDefaults()">
function vSetDateDefaults() {
	var oDate, iDay, iMonth, iYear;
	oDate = new Date();
	oDate.toUTCString();
	iDay = oDate.getDate();
	iMonth = oDate.getMonth() + 1;
	iYear = oDate.getYear() + 1900;
	//alert("Day: " + iDay + ", Month: " + iMonth + ", Year: " + iYear);
	try {
		document.getElementById("Day").value = iDay;
		document.getElementById("Month").value = iMonth;
		document.getElementById("Year").value = iYear;
	} catch(e) {
		// This happens when those elements don't exist
	}
}
*/

/*
// Add event listeners dynamically
// EG: vAddEvent(oElement, "click", vEventListener);
// Note: This would be handy to avoid having to add listeners to every element, but IE does not preserve the "this" keyword from the calling element and therefore it is generally useless - the only use I can think of therefore would be to add "onload" or "onsubmit" events to a page
function vAddEvent(oElement, sEvent, oHandler) {
	if (oElement.addEventListener) { // DOM Compliant browsers
		oElement.addEventListener(sEvent, oHandler, false);
	} else if (oElement.attachEvent) { // MSIE
		oElement.attachEvent("on" + sEvent, oHandler);
	} else { // Others
		oElement["on" + sEvent] = oHandler;
	}
}
*/
