/*
Functions by Steven Moss (steve@mossianic.com)
the main purpose of these functions is to validate form input, there is more than one master function which can do this, each for slightly different situations (see function-specific comments) and per-element functions which can be called independently
 */

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

/*
Validation method 1: ALL form elements are required
NOTE: A parameter to switch between hard and soft validation is needed here! Currently soft by default
This function and related functions are called from html elements using the code below...
FORM
 onsubmit="return vValidateAllElements('ElementName1[,ElementName2(etc.)]', 'Please check your responses!');"
INPUT:CHECKBOX
 onblur="bValidateElement(this);" onclick="bValidateElement(this);"
INPUT:RADIO
 onblur="bValidateElement(this);" onclick="bValidateElement(this);"
INPUT:TEXT
 onblur="bValidateElement(this);" onkeyup="bValidateElement(this);"
SELECT
 onblur="bValidateElement(this);" onclick="bValidateElement(this);"
TEXTAREA
 onblur="bValidateElement(this);" onkeyup="bValidateElement(this);"
...here's a Reg-Ex. replacement that should come in handy
Search for <input ([^\>].*) />
Replace with <input $1 onblur="bValidateElement(this);" onkeyup="bValidateElement(this);" />
...to add events to all text boxes
*/
/*
function vValidateAllElements(sAlert) {
	try {
		var bFirstErrorFound = false, bElementIsValid = true, bFormIsValid = true, iJumpTo;
		bWillValidate = true;  // this is a global variable
		//Make those punters wait!
		document.getElementById("btnValidate").disabled = true;
		document.getElementById("btnValidate").style.cursor = "wait";
		//document.forms[0].disabled=true;
		for (i = 0; i < document.forms[0].length; i++ ) {
			//alert(document.forms[0].elements[i].name);
			bElementIsValid = bValidateElement(document.forms[0].elements[i]);
			if ( ! bFirstErrorFound) {
				if ( ! bElementIsValid) { 
					iJumpTo = i;
					bFormIsValid = false;
					bFirstErrorFound = true;
				}
			}
		}
		//if (bFormIsValid) { submitForm(); }
		//else {
			//document.getElementById("btnValidate").style.display = "none";
			//document.getElementById("btnSubmit").style.display = "";
			//document.getElementById("btnValidate").disabled = false;
			//document.getElementById("btnValidate").style.cursor = "pointer";
			//document.forms[0].disabled = false;
			document.getElementById("btnValidate").style.display = "none";
			// Needed for save and return feature of some PS surveys
			//vToggleExit();
			//eval("document.L_" + document.forms[0].elements[iJumpTo] + ".focus()");
		//}
		if ( ! bFormIsValid) alert(sAlert);
	}
	catch(e) {
		// Error occurs when all questions are answered first time, no problem
		//alert("Error occured : " + e.description);
		//submitForm ();
		//document.Survey.submit();
		//document.forms[0].submit();
	}
}
*/
/*
Validation method 2: A list of specified form elements required
NOTE: A parameter to switch between hard and soft validation is needed here!
This functions and related functions are called from html elements using the code below...
FORM
 onsubmit="return vValidateListedElements('ElementName1[,ElementName2(etc.)]', 'Please check your responses!');"
INPUT:CHECKBOX
 onblur="bValidateElement(this);" onclick="bValidateElement(this);"
INPUT:RADIO
 onblur="bValidateElement(this);" onclick="bValidateElement(this);"
INPUT:TEXT
 onblur="bValidateElement(this);" onkeyup="bValidateElement(this);"
SELECT
 onblur="bValidateElement(this);" onclick="bValidateElement(this);"
TEXTAREA
 onblur="bValidateElement(this);" onkeyup="bValidateElement(this);"
...here's a Reg-Ex. replacement that should come in handy
Search for <input class="Text" ([^\>].*) />
Replace with <input class="Text" $1 onblur="bValidateElement(this);" onkeyup="bValidateElement(this);" />
...to add events to all text boxes
*/
function vValidateListedElements(sRequired, sAlert) {
	var bError, bElementIsValid, bFormIsValid, i, sJumpTo, aElements;
	bError = false;
	bElementIsValid = true;
	bFormIsValid = true;
	i = 0;
	bWillValidate = true;  // this is a global variable
	if (sRequired != "") {
		sRequired = sRequired.replace(/\s/g, ""); // Get rid of any nasty spaces
		aElements = sRequired.split(","); // 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!)
		for (i = 0; i < aElements.length; i++ ) {
			//alert(aElements[i]);
			//prompt("i=", i);
			bElementIsValid = bValidateElement( document.getElementById(aElements[i]) );
			if ( ! bError) {
				if ( ! bElementIsValid) { 
					sJumpTo = "L_" + document.getElementById(aElements[i]).name;
					bFormIsValid = false;
					bError = true;
				}
			}
		}
	}
	if ( ! bFormIsValid) {
		//vShowElement("UserMessages"); // Custom bit here
		alert(sAlert);
	}
/*
	else {
		vSubmitForm("Submit"); // "Submit" is the name of the submit button in html, which for the purposes of this function for now I'm assuming is the only button.  The soft validation version usually has two buttons
	}
*/
	return bFormIsValid;
}

/*
// Specific function for PS Survey "Save & Return" feature
function vToggleExit() {
	try {
		if (document.getElementById("chkExit").checked && document.getElementById("btnSubmit").style.display == "") {
			document.getElementById("btnSubmit").style.display = "none";
			document.getElementById("btnExit").style.display = "";
		}
		else if ( ! document.getElementById("chkExit").checked && document.getElementById("btnExit").style.display == "") {
			document.getElementById("btnExit").style.display = "none";
			document.getElementById("btnSubmit").style.display = "";
		}
	}
	catch(e) { // This occurs when I crudely remove the Save & Return checkbox "chkExit" in a survey
		document.getElementById("btnSubmit").style.display = "";
	}
}
*/

// For soft validation this is called directly from the html form submit button
function vSubmitForm(oElement) {
	// Make the punters wait for submission!
	oElement.style.cursor = "wait";
	oElement.form.style.cursor = "wait";
	oElement.disabled = "true";
	oElement.form.submit();
}

// Delegate validation to the appropriate individual method given a specific element
function bValidateElement(oElement) {
	//alert('bValidateElement(' + oElement.name + ')');
	if ( ! bWillValidate) {
		return true;
	}
	else {
		// For customized behaviour questions get a 'case' of their own...
		switch (oElement.name) {
/*		case "chkExit": // Save & Return feature checkbox
			return true;
			break;*/
		case "EMail1":
			bValidateEMail2(oElement); // Have to run this in order to update the screen after EMail1 changes!
			return bValidateEMail1(oElement);
			break;
		case "EMail2":
			return bValidateEMail2(oElement);
			break;
		case "Phone":
			return bValidatePhone(oElement);
			break;
/*		case "addressL1":
			return bValidateStreetAddress();
			break;*/
		default:
			// ...everything else uses default behaviour defined below...
			switch (oElement.type) {
			case "checkbox":
				var iArrayLength;
				iArrayLength = document.getElementsByName(oElement.name).length; // the count of items in an element array (often null)
				if (iArrayLength > 0) {
					return bValidateCheckboxArray(oElement, 1, iArrayLength);
				}
				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;
	}
}

// checkbox check
function bValidateCheckbox(oElement) {
	var aElements;
	// Use the individual element object to get an array of all the elements of the same name
	aElements = document.getElementsByName(oElement.name);
	if (aElements.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.selectedIndex == 0) {
		vMarkIncomplete(oElement);
		return false;
	}
	else {
		vMarkComplete(oElement);
		return true;
	}
}

// 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 = iCount + 1;
			}
		}
		// 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;
	sLabelID = "L_" + oElement.name;
	try {
		oLabel = document.getElementById(sLabelID);
		oLabel.className = "ErrorLabel";
/*		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;
	sLabelID = "L_" + oElement.name;
	try {
		oLabel = document.getElementById(sLabelID);
		oLabel.className = "StandardLabel";
		/*
		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);
	}
}

/*
// Specific E-Mail Address field validation (they must enter e-mail address twice)
function bMatchEMail(oElement) {
	if (oElement.value == "") { // The E-Mail field is blank
		vMarkIncomplete(oElement);
		return false;
	}
	// Check first if the prompt has already been satisfied
	if (document.getElementById("txtEMailConfirmation").value != oElement.value) {
		sConfirmedEMail = prompt("Please repeat your e-mail address");
		document.getElementById("txtEMailConfirmation").value = sConfirmedEMail;
		if (sConfirmedEMail == null) { // No confirmation
			alert("You must confirm your e-mail address");
			return false;
		}
		if (sConfirmedEMail != oElement.value) { // Failed confirmation
			alert("Your confirmation did not match the e-mail address above");
			return false;
		}
	}
	// Both fields must now match
	vMarkComplete(oElement);
	return true;
}
*/

// Validate an EMail 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 bValidateEMail(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("EMail").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 that the last dot appears after the @
	if (iPositionOfLastDot < iPositionOfAtSymbol) { // There is no domain suffix
		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 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 bValidateEMail1(oElement) {
	var bValid;
	bValid = bValidateEMail(oElement);
	if (bValid) {
		vHideElement("M_EMail1");
	}
	else {
		vShowElement("M_EMail1");
	}
	return bValid;
}

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

/*
// Check if EITHER of the street addresses were filled out, if so it's valid
function bValidateStreetAddress() {
	var oElement1, oElement2, bFirstValid, bSecondValid;
	oElement1 = document.getElementById('AddressL1');
	oElement2 = document.getElementById('AddressL2');
	bFirstValid = bValidateText(oElement1);
	bSecondValid = bValidateText(oElement2);
	if (bFirstValid || bSecondValid) {
		vMarkComplete(oElement1); // There's only one label above the addresses, so only that must be marked complete
		vHideElement("M_AddressL1");
		return true;
	}
	else {
		vMarkIncomplete(oElement1); // There's only one label above the addresses, so only that must be marked incomplete
		vShowElement("M_AddressL1");
		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_Phone");
		return false;
	}
	iNumberCount = aNumbers.length;
	if (iNumberCount < 3) {
		vMarkIncomplete(oElement);
		vShowElement("M_Phone");
		return false;
	}
	else {
		vMarkComplete(oElement);
		vHideElement("M_Phone");
		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";
}

// Launch a pop-up window for a supplied URI
function vPopUp(sURI) {
	var iLeft, iTop, sArgs;
	if (window.screenLeft != undefined) {
		iLeft = window.screenLeft + 100;
		iTop = window.screenTop + 100;
	}
	else {
		iLeft = window.screenX + 100;
		iTop = window.screenY + 150;
	}
	sArgs = "width=550, height=350, left=" + iLeft + ", top=" + iTop + ", scrollbars=yes";
	//alert(sArgs);
	window.open (sURI, "_blank", sArgs);
}

// "addEventListener" does not work in IE, this proxy handles compatibility issues
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;
	}
}

// Handle events added via the "addEventListener" method
function vEventListener() {
	bValidateElement(this);
}

// 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
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 (most of the time), but I'd rather do it this way than use a separate header.jsp for each form or paste it into the main body
	}
}

// function to call other functions from <body onload="">
function vOnLoad() {
	vSetDateDefaults();
}
