/* 
 * Form Validation Functions
 * Ver. 1.6
 * December 8, 2004
 * Update notes: Added conditional check functionality within fElement constructor.
 */

// Global Variables
var gField;
var gAlert;
var gConditionalField;
var gConditionalValue;
var hConditionalField;
var hConditionalValue;
var conditionalFuncTag	= "function:";
var expandableContent	= false;
 
// Dispacher Lookup Constructor
function dispatcher(validationFunction) {
	this.doValidate = validationFunction;
}

// Form Element to check Constructor
function fElement(form, field, check, alertMsg) {

	var args		= fElement.arguments;
	
	this.form		= form;
	this.field		= field;
	this.check		= check;
	this.alertMsg	= alertMsg;
	
	// set up conditional array
	this.condChecks	= new Array();
	
	if (args.length > 4) {
		for (var j = 4; j < args.length; j = j + 2) {  // loop through each name/value pair
		
			var thisCondField	= args[j];
			var thisCondValue	= args[j+1];
			
			this.condChecks.push(thisCondField);
			this.condChecks.push(thisCondValue);
			
		}
	} 
}

var dispatchLookup = new Array ();

	dispatchLookup.isNotEmpty				= new dispatcher(isNotEmpty);
	dispatchLookup.doFieldsMatch			= new dispatcher(doFieldsMatch);
	dispatchLookup.madeSelection			= new dispatcher(madeSelection);
	dispatchLookup.madeGroupSelection		= new dispatcher(madeGroupSelection);
	dispatchLookup.madeRadioSelection		= new dispatcher(madeRadioSelection);
	dispatchLookup.isChecked				= new dispatcher(isChecked);
	dispatchLookup.isNotChecked				= new dispatcher(isNotChecked);
	dispatchLookup.isDate					= new dispatcher(isDate);
	dispatchLookup.isEmail					= new dispatcher(isEmail);
	dispatchLookup.conditionalDate			= new dispatcher(conditionalDate);
	dispatchLookup.conditionalTextField		= new dispatcher(conditionalTextField);
	dispatchLookup.condRadioFieldMultReq	= new dispatcher(condRadioFieldMultReq);
	dispatchLookup.condRadioFieldMultNotReq	= new dispatcher(condRadioFieldMultNotReq);
	dispatchLookup.isPhoneNum				= new dispatcher(isPhoneNum);
	dispatchLookup.isPosInteger				= new dispatcher(isPosInteger);
	dispatchLookup.isZip					= new dispatcher(isZip);
	dispatchLookup.isPrice					= new dispatcher(isPrice);


function validate(form, field, alertMsg, method, conditionals) {

	gField = (field.indexOf(" ") == -1) ?
		document.forms[form].elements[field] :
		document.getElementById(field);

	gAlert = alertMsg;
	
	if (conditionals.length == 0) {		// Standard validation routine
	
		if (!dispatchLookup[method].doValidate()) {
			alert(gAlert);
			
			if (expandableContent)
				expandHiddenContainer(gField);
			
			setFocus();		
			
			return false;
			
		}
				
	} else if (conditionals.length > 0) {		//  handle conditional name/value pairs
	
		for (var j = 0; j < conditionals.length; j = j + 2) {  // loop through each name/value pair
		
			var thisCondField	= conditionals[j];
			var thisCondValue	= conditionals[j+1];
			var cField;
			var cValue;
			
			// get a reference to the conditional field
			cField = (thisCondField.indexOf(" ") == -1) ?
				document.forms[form].elements[thisCondField] :
				document.getElementById(thisCondField);
				
			// do we want to compare the field's value or run it through an existing check?
			// 	if we do, the passed value should begin with "function:"	
			if (thisCondValue.indexOf(conditionalFuncTag) == 0) {
			
				// Run an existing Check
				
				// First, extract the method to run...
				var cMethod = thisCondValue.substring(conditionalFuncTag.length, thisCondValue.length);
				
				// temporarily set the gFeild (global) reference to cField (conditional)
				//	Be sure to save the gField to switch back later.
				var tempField	= gField;
				gField			= cField;
				
				// run the conditional check
				if (dispatchLookup[cMethod].doValidate()) {
					// if it returns true, then reset the gField
					gField	= tempField;
					
					// and run the check on the current field
					if (!dispatchLookup[method].doValidate()) {
						alert(gAlert);
					
						if (expandableContent)
							expandHiddenContainer(gField);
							
						setFocus();
						
						return false;
				
					}
				}
			
			} else {
				// Just check the value of the conditional field.
			
				// Check if we are dealing with a radio group
				var isRadio = (cField.length && cField.length > 0) ? true : false; 
							
				if (isRadio) {
					
					for (var k = 0; k < cField.length; k++) {
						if (cField[k].checked) {
							cValue = cField[k].value;
							break;
						}
					}			
				
				// Deal with other form elements
				} else {
				
					cValue = (cField.type.indexOf("select") > -1) ? 
						cField.options[cField.selectedIndex].value :
						cField.value;
					
				}
				
				if (cValue == thisCondValue) {
				
					if (!dispatchLookup[method].doValidate()) {
						alert(gAlert);
					
						if (expandableContent)
							expandHiddenContainer(gField);
									
						setFocus();
						
						return false;
				
					}
				}
			}
		}	
	}
	return true;
}

function setFocus() {
	if (gField.length && gField.length > 0) {	// Narrow us down to selects and radio objects
		if (gField.type == null) {				// Parse out radio buttons
			gField[0].focus();					// and set focus on the first one in a group
			return;								// exit the function
		}
	}
	gField.focus();								// anything other than a radio button is set here.
}


// Expand any collapsed section that a form field may be in.
function expandHiddenContainer(obj) {

	// catch radio objects
	if (obj.length && obj.length > 0) {
		if (obj.type == null) {
			obj = obj[0];
		}
	}
	
	if (obj.parentNode) {
		var p = obj.parentNode;
		if (p.tagName == "FORM")
			return;
			
		if (p.className == "hidden") {
			toggleContent(p.id);
		} else {
			expandHiddenContainer(p);
		}
	}
}
	
	
// Generic function to check for empty fields.
function isNotEmpty() {
	var inputStr = gField.value;
	if (inputStr == null || inputStr == "") {
		return false;
	}
	return true;
}


// Generic function to check for matching fields (used for PW confirmation).
function doFieldsMatch() {
	var field1 = gField.value;
	var field2 = document.getElementById(gConditionalField).value;
	if (field1 == field2) {
		return true;
	}
	return false;
}



// Generic function to ensure a selection has been made in a drop down list.
function madeSelection() {
	var listVal = gField.value;
	if (listVal == "-1" || listVal == "#" || listVal == "") {
		return false;
	}
	return true;
}

function madeGroupSelection() {
	var f = document.forms[form1];
	for (var x = 0; x < f.elements.length; x++) {
		if (f.elements[x].type == "radio" || f.elements[x].type == "checkbox") {
			if (f.elements[x].checked) {
				return true;
			}
		}
	}
	return false;
}

// Generic function to ensure a radio button in a group has been selected.
function madeRadioSelection() {
	var radioGroup = gField;
	for (i = 0; i < radioGroup.length; i++) {
		if (radioGroup[i].checked) {
			return true;
		}
	}
	return false;
}


// generic function to see if a checkbox or single radio button is checked
function isChecked() {
	if (gField.type == "checkbox" || gField.type == "radio") {
		if (gField.checked) {
			return true;
		}
	}
	return false;
}

// generic function to see if a checkbox or single radio button is NOT checked
function isNotChecked() {
	if (gField.type == "checkbox" || gField.type == "radio") {
		if (!gField.checked) {
			return true;
		}
	}
	return false;
}


// Generic function to check if a string is an integer
function isPosInteger() {
	var inputVal;
	if (arguments.length == 0) {
		inputVal = gField.value;
	} else {
		inputVal = arguments[0];
	}
	var inputStr = inputVal.toString();
	
	for (z=0; z < inputStr.length; z++) {
		if (inputStr.charAt(z) < "0" || inputStr.charAt(z) > "9") {
			return false;
		}
	}
	return true;
}


// Generic function to check if a string is an integer with acceptance of "," and "." properly formatted.
function isPrice() {

	var inputStr = gField.value;
	var dollars = inputStr;
	var cents;
	
	if (inputStr.indexOf(".") != -1) {  // Checks for a decimal
		if (inputStr.indexOf(".") == inputStr.lastIndexOf(".")) { // checks for only one decimal
			dollars = inputStr.substring(0, inputStr.indexOf(".")); // Save Dollars to varable
			cents = inputStr.substring(inputStr.indexOf(".") + 1, inputStr.length); // save the cents to a variable
			if (cents.length != 2 || !isPosInteger(cents)) { // makes sure that the cents is only 2 numbers.
				return false;
			}	
		} else {
			return false;
		}
	} else {
		cents = "00";
	}
	
	if (dollars.indexOf(",") != -1) { // checks for a comma
		var triplets = dollars.split(","); // split at the commas
		for (var t = 0; t < triplets.length; t++) {
			if (t == 0) {
				if (triplets[t].length > 3 || !isPosInteger(triplets[t])) {
					return false;
				}
			} else {
				if (triplets[t].length != 3 || !isPosInteger(triplets[t])) {
					return false;
				}
			}
		}
		
		var justNumbers = stripNonDigits(dollars);
		dollars = justNumbers;
		
	} else if (!isPosInteger(dollars)) {
		return false;
	}

	var formattedPrice = dollars + "." + cents;
	
	gField.value = formattedPrice;
	
	return true;
}



// Generic function to strip non-digits out of a string
function stripNonDigits() {
	var inputVal;
	if (arguments.length == 0) {
		inputVal = gField.value;
	} else {
		inputVal = arguments[0];
	}
	var inputStr = inputVal.toString();
	var digits = "0123456789";
	var returnStr = "";

	for (i=0; i < inputStr.length; i++) {
		var c = inputStr.charAt(i);
		if (digits.indexOf(c) != -1) {
			returnStr += c;
		}
	}
	return returnStr;
}


// Generic Function to validate a phone number
function isPhoneNum() {
	var minDigInPhone = 10;

	var optimizedNum = stripNonDigits();

	if (optimizedNum.length < minDigInPhone) {
		return false;
	}
	
	gField.value = optimizedNum;
	return true;
}


// Generic function to validate US Zip Codes
function isZip() {
	var inputStr = gField.value;
	var l = inputStr.length;
	
	// if the zip entered is not 5 or 10 characters, it can't be right
	if ((l != 5) && (l != 10)) {
		return false;
	}
	
	// Checks for a 5 digit zip code
	if (l == 5 && !isPosInteger()) {	
		return false;
	}

	// Checks for a 5 + 4 zip code with hyphen (12345-6789)
	if (l == 10) {
		var compLength = 5;
		var components = inputStr.split("-"); // Checks for a "-" and splits the zip into the two seperate components
		if (components.length == 2) {
			for (y = 0; y < components.length; y++) {
				if (components[y].length != compLength) {
					return false;
				}
				if (!isPosInteger(components[y])) {
					return false;
				}
				compLength--;
			}
		}  
	}
	return true;
}

// Check for MM/DD/YY format if status = complete
function conditionalDate() {
	var inputStr = gField.value;
	var cf = eval(gConditionalField).value;
	
	if (cf == gConditionalValue) {
		if(!isNotEmpty()) {
			return false;
		}
		if(!isDate()) {
			return false;
		}
	}
	return true;
}

// requires that a conditional text field is filled in when another text field has been filled in
function conditionalTextField() {
	var inputStr = eval(hConditionalField);
	if (inputStr.value != "") {
		return false;
	}
	return true;
}

// requires (or not) that a conditional text field is filled in depending on a radio box selection
// when ONLY ONE selection DOES NOT require an input to a text field
function condRadioFieldMultReq() {
	var inputStr = gField.value;
	var cf = eval(gConditionalField);
	
	for (i = 0; i < cf.length; i++) {
		if (cf[i].checked) {
			if (cf[i].value != gConditionalValue) {
				if (!isNotEmpty()) {
					return false;
				}
			}
		}
	}
	return true;
}

// requires (or not) that a conditional text field is filled in depending on a radio box selection
// when ONLY ONE selection DOES require an input to a text field
function condRadioFieldMultNotReq() {
	var inputStr = gField.value;
	var cf = eval(gConditionalField);
	
	for (i = 0; i < cf.length; i++) {
		if (cf[i].checked) {
			if (cf[i].value == gConditionalValue) {
				if (!isNotEmpty()) {
					return false;
				}
			}
		}
	}
	return true;
}

// Check for MM/DD/YY date format
function isDate() {
	var inputStr = gField.value;
	var delim1 = inputStr.indexOf("/");
	var delim2 = inputStr.lastIndexOf("/");
	
	// If there are no delimiters or there is only one '/'
	if ((delim1 == -1) || (delim1 != -1 && delim1 == delim2)) {	
		gAlert = "Please enter the date in MM/DD/YY format.";
		return false;
	}
	
	var mm = parseInt(inputStr.substring(0, delim1), 10);
	var dd = parseInt(inputStr.substring(delim1 + 1, delim2), 10);
	var yy = parseInt(inputStr.substring(delim2 + 1, inputStr.length), 10);
	
	// Make sure the extracted elements are numeric
	
	if (isNaN(mm) || isNaN(dd) || isNaN(yy)) {		
		gAlert = "Please enter the date in MM/DD/YY format and that you are using digits.";
		return false;
	}
	
	if (mm < 1 || mm > 12) {
		gAlert = "Months must be entered in the range of 01 (January) to 12 (December)\n\nPlease correct the date you entered.";
		return false;
	}
	
	if (yy > 100) {
		gAlert = "Please enter the year in 2 digit format.";
		return false;
	}
	
	if(mm == 2) {
		if (!checkLeapMonth(mm,dd,yy)) {
			return false;
		}
	}
	
	if(!checkMonthLength(mm,dd)) {
		return false;
	}
	
	return true;

}
	  

// Check Month Lengths
function checkMonthLength(mm,dd) {
	
	var months = new Array ("", "January", "February", "March",	"April", "May", "June", "July", "August", "September", "October", "November", "December");
	
	if ((mm == 4 || mm == 6 || mm == 9 || mm == 11) && dd > 30) {
		gAlert = months[mm] + " has only 30 days.\n\nPlease correct the date you entered.";
		return false;
	} else if (dd > 31) {
		gAlert = months[mm] + " has only 31 days.\n\nPlease correct the date you entered.";
		return false;
	}
	return true;
}

// And Check February
function checkLeapMonth(mm, dd, yy) {
	
	// convert to 4 digit date (between 1930 and 2029)
	
	var yyyy = (yy >= 30) ? yy + 1900 : yy + 2000;
	
	if (yyyy % 4 > 0 && dd > 28) {
		gAlert = "February of " + yyyy + " has only 28 days.\n\nPlease correct the date you entered.";
		return false;
	} else if (dd > 29) {
		gAlert = "February of " + yyyy + " has only 29 days.\n\nPlease correct the date you entered.";
		return false;
	}
	return true;
}


// Checks for a valid e-mail address 
function isEmail() {
	var inputStr = gField.value;
	var as = inputStr.indexOf("@");
	var se = inputStr.indexOf(" ");
	var ns = inputStr.substring(as + 1, inputStr.length); // domain (after '@')
	var nd = ns.indexOf(".");
	
	if ((as != -1) && (as != 0)) { 	// If there is an '@' and it is not the first character,
		if (se == -1) {		// and if no spaces exist,
			if ((nd != -1) && (nd != 0)) {	// and if there is a '.' after the '@', and there is at least one character between them,
				if (nd < (ns.length - 1)) {	// and the '.' is not the last character
					return true;
				}
			}
		}
	}
	return false;
}

	


/********************************************************************************/

// Check Form Function
function checkForm() {

	// Are there Multiple Forms?
	var args 		= checkForm.arguments;
	var formCount	= (args.length > 1) ? args.length : 1;
	
	// Loop through the Element Lookup array 
	for (x = 0; x < elementLookup.length; x++) {
		// loop through the names listed in the function call
		for (q = 0; q < formCount; q++) {
			// if the form name matches
			if (args.length == 0 || elementLookup[x].form == args[q]) {
				// run the check
				var fm = elementLookup[x].form;
				var fd = elementLookup[x].field;
				var al = elementLookup[x].alertMsg;
				var ck = elementLookup[x].check;
				var cc = elementLookup[x].condChecks;
				if (!validate(fm, fd, al, ck, cc)) {
					return false;
				}
			}
		}
	}
	return true;
}
