/***********************************************************************
**         Validate form input before submission to server           ***
**                                                                   ***
**         Validation is handled based on attributes assigned        ***
**         to form fields in the HTML.  The following types of       ***
**         validation are possible:                                  ***
**                                                                   ***
**         Required Field Validation:                                ***
**                Unless marked optional in the HTML by an           ***
**                assignment of fieldname.optional = true            ***
**                all TEXT, TEXTAREA, PASSWORD, SELECT, and HIDDEN   ***
**                fields will be checked for non-white-space         ***
**                entries.                                           ***
**                Can also be used on checkbox groups to enforce     ***
**                selection of at least one option.                  ***
**                                                                   ***
**         Data Type Validation:                                     ***
**                Data types can be validated by assigning           ***
**                the mask attribute to a field in the HTML.         ***
**                The following mask settings can be validated:      ***
**                     numeric: positive integers, no symbols,       ***
**                              punctuation, or alpha chars          ***
**                     float:   numeric with or without decimals     ***
**                     email:   weak validation that the string      ***
**                              contains '@' anywhere other than     ***
**                              the first or last char               ***
**                     time:    entry is either numeric or of the    ***
**                              format nn:nn or n:nn.  AM and PM     ***
**                              should be handled as CHECKBOX or     ***
**                              SELECT elements in the HTML form     ***
**                     date:    entry is a valid date, sent in       ***
**                              mm-dd-yyyy format and is not         ***
**                              earlier than the current sys date    ***
**                     tight:   entry can contain any characters     ***
**                              except white space                   ***
**                                                                   ***
**                                                                   ***
**        Minimum Length Validation:                                 ***
**                Minimum field length can be validated by           ***
**                assigning an integer value to  fieldname.minlength ***
**                in the HTML.                                       ***
**                                                                   ***
**        Maximum Length Validation:                                 ***
**                Maximum lengths should be validated using          ***
**                the MAXLENGTH attribute of INPUT elements in       ***
**                the HTML.  There is no maxlength validation here   ***
**                                                                   ***
**        Password Matching:                                         ***
**                If the form contains multiple PASSWORD inputs,     ***
**                validate() assumes the second field is a           ***
**                confirmation of the first and compares the two.    ***
**                If the form contains only one PASSWORD input, it   ***
**                is checked for the presence of a value (unless     ***
**                it was marked fieldname.optional = true in the     ***
**                HTML                                               ***
**                                                                   ***
**        Range Validation:                                          ***
**                Numeric/Float range validation can be peformed by  ***
**                assigning integers to fieldname.minrange and       ***
**                fieldname.maxrange.  Validation occurs             ***
**                independently for each, so a field can have        ***
**                a min value, max value, or both.  Range validation ***
**                will occur ONLY for fields set with                ***
**                fieldname.mask = 'numeric' or 'float'              ***
**                                                                   ***
**        Field Labels:                                              ***
**                To allow for readable field labels to display in   ***
**                error messages rather than internal field names,   ***
**                set the field's fieldname.label attribute          ***
**                                                                   ***
************************************************************************/

// UTILITY FUNCTIONS

// Return true if a string is blank or contains only white space
function isBlank(s) {

	for (var i=0; i < s.length; i++) {
		var c = s.charAt(i);
		if ((c != ' ') && (c != '\n') && (c != '\t')) return false;
	}
		return true;
}

// Return true if a string contains NO white space
function noSpace(s) {

	for (var i=0; i < s.length; i++) {
		var c = s.charAt(i);
		if ((c == ' ') || (c == '\n') || (c == '\t')) return false;
	}
		return true;
}

// Return true if compared password entries match
function passwordsMatch (pwd1, pwd2) {
	if (pwd1 == pwd2) {
		return true;
	} else {
		return false;
	}
}

// Return true if entry is a valid number (int)
function isNumeric (num) {

	// if it's blank, return true. Req'd fields will be checked for a value before 
	// coming to the mask validation, so if we're here and it's blank, 
	// it must be optional
	
	if (isBlank(num)) return true;

    for(var i = 0; i < num.length; i++) {
		var c = num.charAt(i);
		switch(c) {
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
		    break;
			default:
	    	return false;
		}
    }
    return true;

}

// Return true if entry is a valid float (int or int.int or .int)
function isFloat (num) {

	// if it's blank, return true. Req'd fields will be checked for a value before 
	// coming to the mask validation, so if we're here and it's blank, 
	// it must be optional
	
	if (isBlank(num)) return true;

    for(var i = 0; i < num.length; i++) {
		var c = num.charAt(i);
		switch(c) {
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case '.':
		    break;
			default:
	    	return false;
		}
    }
    return true;

}

// return true if a number falls within a certain range (inclusive)
function isInRange(low, high, numToCheck) {

	if ((low <= numToCheck) && (numToCheck <= high)) return true;
	
	return false;

}

// return true if entry is a valid date
function isValidDate (date) {
	// dates are assembled from multiple fields and
	// stored as mm-dd-yyyy
	
	var dMonth = "";
	var dDay = "";
	var dYear = "";
	var dash1 = date.indexOf("-");
	var dash2 = date.lastIndexOf("-");
	
	if (dash1 == -1 && dash2 == -1) {
		dash1 = date.indexOf("/");
		dash2 = date.lastIndexOf("/");
	}

	dMonth = date.substring(0, dash1);
	dDay = date.substring (dash1 + 1, dash2);
	dYear = date.substring (dash2 + 1, date.length);
	
	// remove any leading zeros
	if (dMonth.charAt(0) == "0") dMonth = dMonth.substring(1,dMonth.length);
	if (dDay.charAt(0) == "0") dDay = dDay.substring(1, dDay.length);
	
	// make sure the date does not preceed today
	// actually past dates are ok..disable this call
	//if (datePassed(dDay, dMonth, dYear))
	//	return false;

	// make sure the date is valid
	if (!validDayMonth(dDay, dMonth, dYear))
		return false;
		
	return true;
	
}

// BEGIN DATE UTILITIES

function datePassed(inputDay, inputMonth, inputYear) {

	var todayDate = new Date();
	sysYear = todayDate.getYear() < 2000 ? todayDate.getYear + 1900 : todayDate.getYear();
	
	// alert("Testing: " + inputMonth + " " + inputDay + ", " + inputYear + " --- Against: " + sysMonth + " " + sysDay + ", " + sysYear);

	if(inputYear > sysYear) {
	//	alert (inputYear + " is later than " + sysYear);
		return false;
	} else if (inputYear == sysYear && inputMonth > sysMonth) {
	//	alert (inputMonth + ", " + inputYear + " is later than " + sysMonth + ", " + sysYear);
		return false;
	} else if (inputMonth == sysMonth && inputDay >= sysDay) {
	//	alert (inputMonth + " " + inputDay + ", " + inputYear + " is later than " + sysMonth + " " + sysDay + ", " + sysYear);
		return false;
	} else {
	//	alert (inputMonth + " " + inputDay + ", " + inputYear + " is earlier than today's date");
		return true;
	}
	return true;
}

//
// return true if first date preceeds second
//
function isValidDatePair(date1, date2, userMsg) {

	// dates are assembled from multiple fields and
	// stored as mm-dd-yyyy
	var dMonth1 = "";
	var dDay1 = "";
	var dYear1 = "";
	
	var dMonth2 = "";
	var dDay2 = "";
	var dYear2 = "";
	
	var dash1a = date1.indexOf("-");
	var dash1b = date1.lastIndexOf("-");
	var dash2a = date2.indexOf("-");
	var dash2b = date2.lastIndexOf("-");

	dMonth1 = date1.substring(0, dash1a);
	dDay1 = date1.substring (dash1a + 1, dash1b);
	dYear1 = date1.substring (dash1b + 1, date1.length);
	
	dMonth2 = date2.substring(0, dash2a);
	dDay2 = date2.substring (dash2a + 1, dash2b);
	dYear2 = date2.substring (dash2b + 1, date2.length);

	//alert("Testing: " + dMonth1 + " " + dDay1 + ", " + dYear1 + " --- Against: " + dMonth2 + " " + dDay2 + ", " + dYear2);

	var datePairMsg = "";
	if (userMsg)
		datePairMsg = userMsg;
	else
		datePairMsg = "Pickup Date cannot be later than Delivery Date";
	
	if(dYear1 > dYear2) {
		alert (datePairMsg);
		return false;
	} else if ((dYear1 == dYear2) && (dMonth1 > dMonth2)) {
		alert (datePairMsg);
		return false;
	} else if ((dYear1 == dYear2) && (dMonth1 == dMonth2) && (dDay1 > dDay2)) {
		alert (datePairMsg);
		return false;
	} else {
		// alert ("Date pair is okay");
		return true;
	}
	return true;
}

function validDayMonth(dayNum,monthNum,yearNum) {

	var daysInMonth = new Array(12);
	daysInMonth[1] = 31;	
	daysInMonth[2] = 29;
	daysInMonth[3] = 31;
	daysInMonth[4] = 30;
	daysInMonth[5] = 31;
	daysInMonth[6] = 30;
	daysInMonth[7] = 31;
	daysInMonth[8] = 31;
	daysInMonth[9] = 30;
	daysInMonth[10] = 31;
	daysInMonth[11] = 30;
	daysInMonth[12] = 31;
	
	//alert ("checking " + dayNum + " against num days in month " + monthNum + " against " + daysInMonth[monthNum]);

	if((dayNum > daysInMonth[monthNum]) || (dayNum == 0 || monthNum == 0) ||
	(monthNum == 2) && (dayNum > daysInFeb(yearNum))) {
		alert (monthNum + " " + dayNum + ", " + yearNum + " is not a valid date\n");
		return false;
	}
	else return true;

}

function daysInFeb(year) {
	return (((year % 4 == 0) && ((!(year % 100 == 0)) || (year % 400 == 0))) ? 29:28);
}

// END DATE UTILITIES

// return true if entry is a valid time
function isValidTime(time) {

	// first if the field is blank, return true...required time fields will be validated
	// as any other text field before they are validated as 'time' fields
	if (isBlank(time)) return true;
	
	var separator = time.indexOf(':');
	
		if (separator == -1) {
		
			// there's no colon...make sure they entered a reasonable number
			
			if (isInRange(1, 12, time)) {
				return true;
			} else {
				return false;
			}
			
		} else {
		
			// okay, there's a colon, make sure there are the right number of
			// numeric chars on either side.
			
			var hour = time.substring(0, separator);
			var minute = time.substring(separator+1, time.length);
			
			if (isInRange(1, 12, hour) && isInRange(0, 59, minute)) {
				return true;
			} else {
				return false;
			}
		
		}
}


// return true if entry is a properly formatted email address
function isValidEmail (addr) {

	// if it's blank, return true. Req'd fields will be checked for a value before 
	// coming to the mask validation, so if we're here and it's blank, 
	// it must be optional
	if (isBlank(addr)) return true;

	if ((0 < addr.indexOf('@')) && (addr.indexOf('@') < (addr.length - 1))) {
		//alert ("@ found at pos: " + addr.indexOf('@') + ". Returning true.");
		return true;
	} else {
		//alert ("@ found at pos: " + addr.indexOf('@') + ". Returning false.");
		return false;
	}

}

// GLOBALS

	// variable to hold the name of the first field with an error so we can send 
	// focus to it later
	var fieldToCorrect = "";
// VALIDATION ROUTINE

function validate (f) {

	var msg;
	var emptyFields = "";
	var errors = "";
	var pwdFields = 0;
	var pwdMismatch = false;
	var basePwd = "";
	var radioFlag = false;


	// Loop through the elements to find all those who do not have an optional property
	// then make a list of the req'd fields that are blank
	// and assemble error msgs for incorrect entries.

	for (var i = 0; i < f.length; i++) {

		var e = f.elements[i];

		// First check for text fields left blank
		if (((e.type == "text") ||
              (e.type == "textarea") ||
              (e.type == "password") ||
              (e.type == "hidden"))
              && (!e.optional)) {
			// is it empty?
			if ((e.value == null) || (e.value == "") || isBlank(e.value)) {
				if (e.label)
					emptyFields += "\n       " + e.label;
				else
					emptyFields += "\n       " + e.name;
				if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
				continue;
			}
		}

		// don't forget list boxes
		if (((e.type == "select-one") || (e.type == "select-multiple")) && (!e.optional)) {

			if (e.options[e.selectedIndex].value == "") {
				if (e.label)
					emptyFields += "\n       " + e.label;
				else
					emptyFields += "\n       " + e.name;
				if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
			}
		}

		// Now check for passwords that need confirming

		if (e.type == "password") {

			// if this is not the first pwd field, compare it to the prev field
			if (pwdFields > 0) {
				if (!passwordsMatch(e.value, basePwd)){
					errors += "Passwords do not match.\n";
					if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
				}
			// otherwise use this field as the reference for the next
			} else {
				basePwd = e.value;
				pwdFields++;
			}

			continue;
		}

        // check numeric fields

        if (e.mask == "numeric") {

            if (!isNumeric(e.value)) {
							if (e.label)
                errors += e.label + " must be a positive number: " + e.value + ".\n";
							else
                errors += e.name + " must be a positive number: " + e.value + ".\n";
				if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
            }
        }

        // check float fields

        if (e.mask == "float") {

            if (!isFloat(e.value)) {
							if (e.label)
                errors += e.label + " must be a positive number: " + e.value + ".\n";
							else
                errors += e.name + " must be a positive number: " + e.value + ".\n";
				if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
            }
        }
		
		// check email fields
		
		if (e.mask == "email") {
			//alert ('found an email: ' + e.name + ': ' + e.value);
            if (!isValidEmail(e.value)) {
                errors += e.value + " is not a valid email address.\n";
				if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
            }
		
		}
		
		// check time fields
		
		if (e.mask == "time") {
		
			if (!isValidTime(e.value)) {
			
				errors += e.value + " is not a valid time \(HH:MM, H:MM, HH, H\)\n";
				if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
				
			}
		
		}

		// check date fields
		
		if (e.mask == "date") {
		
			if (!isValidDate(e.value)) {
			
				if (e.label)
					errors += e.label + " is not a valid date\n"; // or is prior to today's date\n";
				else
					errors += e.name + " is not a valid daten"; // or is prior to today's date\n";
				//if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
				
			}
		
		}

		// check tight fields (not allowed to contain spaces)
		
		if (e.mask == "tight") {
		
			if (!noSpace(e.value)) {
			
				if (e.label)
					errors += e.label + " cannot contain any white space\n";
				else
					errors += e.name + " cannot contain any white space\n";
				if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
				
			}
		
		}
		
		// make sure it meets any defined minimum length
		// but if it's blank AND optional, ignore it.
		if (e.minlength != null) {
		
			if (e.minlength > e.value.length && !((e.value.length == 0) && (e.optional == true))) {
				if (e.label)
					errors += e.label + " must be at least " + e.minlength + " characters.\n";
				else
					errors += e.name + " must be at least " + e.minlength + " characters.\n";
				if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
			}
		}
		
		// check numerics for being in any ranges set
		
		if (e.minrange != null && (e.mask == "numeric" || e.mask == 'float')) {

		// first pull off any leading zeros...f-ing javascript
			
			var checkString = e.value;
			
			while (true) {
			    if (checkString.charAt(0) == "0")
					checkString = checkString.substring(1, checkString.length);
				else
					break;
   			}

			if (checkString == "")	checkString = "0"; // they entered only zeros
			var passed = parseFloat(checkString);
			var theMin = parseFloat(e.minrange);
			//alert ("validating: " + passed + " against " + min);
			if ((theMin > passed) && !((e.value.length == 0) && (e.optional == true))) {
					if (e.label)
						errors += e.label + " must be " + e.minrange + " or higher\n";
					else
						errors += e.name + " must be " + e.minrange + " or higher\n";
					if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
			}
		}
		if (e.maxrange != null && (e.mask == "numeric" || e.mask == "float")) {
			var passed = parseFloat(e.value);
			var theMax = parseFloat(e.maxrange);
			//alert ("comparing " + passed + "to " + theMax);
			if ((theMax < passed) && !((e.value.length == 0) && (e.optional == true))) {
				if (e.label)
					errors += e.label + " must be " + e.maxrange + " or lower\n";
				else
					errors += e.name + " must be " + e.maxrange + " or lower\n";
					if ((!fieldToCorrect) && (e.type.indexOf("select") == -1) && (e.type.indexOf("hidden") == -1)) fieldToCorrect = e.name;
			}
		}
	}

	// return error messages appropriately and return false
	// or return true if all is well.

	if (!emptyFields && !errors) return true;

	msg  = "-------------------------------------\n\n";
	msg += "Data could not be submitted for the following reasons.\n";
	msg += "Please correct any error(s) and resubmit.\n";
	msg += "-------------------------------------\n\n";

	if (emptyFields) {
		msg += "- The following field(s) are empty:" + emptyFields + "\n";
	}

	if (errors) {
		msg += "\n" + errors;
	}

	alert(msg);								//tell them what's wrong
	if (fieldToCorrect != "")
		f.elements[fieldToCorrect].focus();		//send them to first wrong entry
	
	fieldToCorrect = "";					//reset for next submit
	
	return false;							//prevent form submission
}

function cleanChars (fld) {

	var tmp = ""
	for (i=0; i<fld.value.length; i++) {
		if (fld.value.charAt(i) == "'") {
			tmp += "&rsquo;"
		} else if (fld.value.charAt(i) == "\n") {
			tmp += "<br/>"
		} else {
			tmp += fld.value.charAt(i)
		}
	}
	fld.form.elements[fld.name].value = tmp;
	return true;

}
