// General form validation routine

// Return the error message associated with the element e.
function getMsg(e) {
	
	var msg;

	// Idiosyncratic error messages can be assigned in the form:
	if (e.msg) {
		msg = e.msg;
	}
	else {
		// These steps imposes a naming convention on form elements that don't have a special error message applied.
		// E.g., first_name => First name
		msg = e.name.replace(/_/g, " ");
		// In Perl this is just \u
		// msg = msg.replace(/^(.)/, $1.toUpperCase()) doesn't work
		msg = msg.substring(0,1).toUpperCase() + msg.substring(1,msg.length);

	}
	return msg + "\n\n";	
}


// Validate form submission data.  "this" is the form object.
// If any form data is invalid, generate an alert and return false; else return true.
function verifySubmit(event) {

	if (typeof event == "undefined") {
		event = window.event;
	}
	
	var msg = "",
		emptyFields = "",
		formatErrors = "",
		missingFiles = "",
		fileTypeErrors = "",
		otherErrors = "";
	
	for (var i = 0; i < this.length; i++) {

		var e = this.elements[i]; 

		// Text input elements, textareas, and select elements:
		// Single-select element = type "select-one", multiple-select element = type "select-multiple".
		// Text input = type "text", textarea = type "textarea".
		if ( /^(text|select)/.test(e.type) ) {

			// 1. Check for blank required elements
			if (isblank(getTextOrSelectValue(e))) {
				if (!e.optional) {
					emptyFields += getMsg(e);
				}
			}
			// 2. Non-blank form fields: validate as specified in the form validation rules
			else if (e.type == "text") { 
			
				if (e.validate != null) {
			
					switch (e.validate) {
				
						case "email": 
							if (isValidEmail(e.value)) { continue; }
							break;
						
						// This should be "us_zipcode", and below just "zipcode", but too late to change now
						case "zipcode":
							if (isValidUSZipcode(e.value)) { continue; }
							break;
							
						// Could match against state vs province selection (i.e., only allow Canadian postal
						// code format with Canadian province selection.						
						case "us_or_can_zipcode":
							if (isValidUSOrCanZipcode(e.value)) { continue; }
							break;
						
						// This should be "full_phone_number", but too late to change now
						case "phone_number":
							if (isValidFullPhoneNumber(e.value)) { continue; }
							break;
							
						case "local_phone_number":
							if (isValidLocalPhoneNumber(e.value)) { continue; }
							break;
							
						case "local_or_full_phone_number":
							if (isValidPhoneNumber(e.value)) { continue; }
							break;
							
						case "year":
						case "year4":
							if (isYear(e.value)) { continue; }
							break;
						
						case "year2or4":
							if (isTwoOrFourDigitYear(e.value)) { continue; }
							break;
						
						case "date_mmyyyy":
							if (isDate_mmyyyy(e.value)) { continue; }
							break;		
						
						case "date_mdy":
							if (isDate_mdy(e.value)) { continue; }
							break;		
						
						case "date_mmddyyyy":
							if (isDate_mmddyyyy(e.value)) { continue; }
							break;
												
						case "int":
							if (isInt(e.value)) { continue; }
							break;
						
						case "float":
							if (isFloat(e.value)) { continue; }
							break;
						
						default: 
							continue;											
					}
				
					formatErrors += getMsg(e);
				}
			
			}
		}
		
		// Radio buttons and checkboxes
	    else if ( e.type == "radio" || e.type == "checkbox" ) {

			var elementName = e.name;
			
			// Check only once per group of radio buttons/checkboxes.
			// Optionality and special error messages are defined as attributes of the first element
			// of the array.
			if (e != this[elementName][0]) { continue; }
			
			if (!e.optional) {
				if (!optionSelected(this, elementName)) {
					emptyFields += getMsg(e);
				}
			}
		}
		
		// File input elements
		else if (e.type == "file") {
		
			// Missing required files
			if (isblank(e.value)) {
				if (!e.optional) {
					missingFiles += getMsg(e);
				}
			}
			// Invalid filetype
			else if (e.filetypes != null) {
				if (!hasValidFiletype(e.value, e.filetypes)) {
					fileTypeErrors += getMsg(e);
				}
			}
		
		}
			
	}
	
	// Execute form-specific validation code
	if (this.validationFunction) {
		// Another strategy: always name the function validateForm, and put each one in
		// a different file. The right file will be determined by the <script> tag.	
	
		// Can't figure out syntax for calling the function without passing "this" as argument
		// Not: otherErrors = eval(this.validationFunction)();
		otherErrors = eval(this.validationFunction)(this); 
		
	}
	
	if (!emptyFields && !formatErrors && !missingFiles && !fileTypeErrors && !otherErrors) { 
		return true; 
	} 
	
	// Report the errors
	var divider = "-----------------------------------------------------------------------\n\n";
	
	msg = "The form was not submitted due to errors in the input.\n" +
		  "Please correct the following error(s) and resubmit the form.\n\n" +
		  divider;
	
	if (emptyFields) {
		msg += "The following required information has not been specified:\n\n" + emptyFields + divider;
	}
	if (formatErrors) {
		msg += "The following input has an invalid format:\n\n" + formatErrors + divider;
	}
	if (missingFiles) {
		msg += "The following required files have not been attached:\n\n" + missingFiles + divider;
	}
	if (fileTypeErrors) {
		msg += "The following attached files have invalid file formats:\n\n" + fileTypeErrors + divider;
	}
	if (otherErrors) {
		msg += "Other errors:\n\n" + otherErrors + divider;
	}
	alert(msg);
	
	// Stop default action on failure
	// W3C DOM Level 2 event listener approach
	// Test for existence of method first, else in older forms that don't include this function
	// an error is thrown, and we don't reach the "return false" statement, so the default 
	// action doesn't get cancelled.
	if (typeof stopDefaultAction != "undefined") {
		stopDefaultAction(event);
	}
	// DOM Level 0 approach
	return false;
}



/*******************************************************************************
Another way of handling radio buttons/checkboxes - handle each element as it
passes through the main loop.  checkedOptions is declared at top of function as
checkedOptions = new Array();

		else if ( e.type == "radio" || e.type == "checkbox" ) {
		
			var elementName = e.name;
			var firstElement = formObj[elementName][0];
			var lastElement = formObj[elementName][formObj[elementName].length - 1];
			
			// Optionality for the group is defined as an attribute of the first element, for convenience.
			if (!firstElement.optional) {	
				// We've already seen a checked option for this group
				if (checkedOptions[elementName] == true) {
					continue;
				}
				// If this element is checked, add its name to the array of checked options
				else if (e.checked == true) {
					checkedOptions[elementName] = true;
				}
				// If we've reached the last element and no options have been checked, issue an error message
				else if (e == lastElement) {
					// An error message for the group is defined as an attribute of the first 
					// element, for convenience.
					emptyFields += getMsg(firstElement);
					
				}
			}
		}
********************************************************************************/