/**
* Library to provide standard validation methods.
* History
* Version	Date		By		Description
*----------------------------------------------------------------
* 1.10		03/24/2005	ml		Rewrite validateAsString to correctly generate length != (minLength == maxLength) error
*								wording
* 1.9		03/09/2005	as		Got rid of Array.push(), Array.pop(), and Array.shift() methods
								because they are not supported in browsers earlier than IE5.5 and NN4.
								Rewrote affected functions (errorAlert.prototype.addMessage, 
								errorAlert.prototype.removeMessage, and errorAlert.prototype.showAlert)
								using code compatible with early browser versions.
* 1.8		01/10/2005	ml		add/remove/show error methods converted object instance methods,
*								Addition of validateAsTime
* 1.7		11/16/2004	as		Addition of errorAlert, addMessage, removeMessage, showAlert,
*								and myErrorAlert.  Addition of addToQueue to most methods for 
*								option to show all errors in one alert.  Rewrite of most alert
*								text to instruct users on how to fix errors.  Fix to
*								validateAsDate to correctly handle dates before minDate.
* 1.6		10/05/2004	ml		Addition of validateAsCheckBoxGroup, validateAsDate
* 1.5		09/08/2004	ml		Minor fix to validateAsUSPhoneNumber to correctly handle area codes
* 1.4		08/25/2004	ml		Fix to validateAsString to not handle error if showError == false
* 1.3		08/11/2004	ml		Addition of validateAsFloat
* 1.2		06/22/2004	ml		Fix of isBlank to use regexp object
* 1.1		04/23/2004	ml		Addition of validateAsInteger, validateAsUSPhoneNumber,
*								validateAsDropDown, validateAsZIP, validateAsUSPhone methods,
*								Addition of extNameStr to most methods to allow more meaningful
*								error handling, addition of handleValidationError
* 1.0		03/30/2004	ml		New
*/

/**
* Convenience error handler for this library
* @param fieldName -HTML element (name of which is used when extNameStr is null)
* @param errStr - message body to display
* @param extNameStr - external name of field e.g. "Reservation type" instead of "res_type"
* @return void
*/
function handleValidationError(fieldName, errStr, extNameStr){
	var titleStr = "Validation error\nField: '";
	
	if(extNameStr != null)
		titleStr += extNameStr;
	else
		titleStr += fieldName.name;
	
	titleStr += "'\n" + errStr;
	alert(titleStr);
}

/**
* Error alert object holds error messages and can be used to display all errors in one alert
* @return void
*/
function errorAlert() {
	// an array to hold multiple errror messages
	this.errorArray = new Array();
}

/**
* Add an error message to the error alert
* @param errorMessage - string containing the error message
* @return number of messages in the error alert to ensure compatiability with Array.push()
*/
errorAlert.prototype.addMessage = function ( errorMessage ) {
	// Array.push() function not supported in browsers earlier than IE5.5 and NN4
	var length = this.errorArray.length;
	this.errorArray[length] = errorMessage;
	length = this.errorArray.length;
	return(length);
}

/**
* Remove an error message from the bottom of the error array
* @return error message that was removed to ensure compatiability with Array.pop()
*/
errorAlert.prototype.removeMessage = function() {
	// Array.pop() function not supported in browsers earlier than IE5.5 and NN4
	var length = this.errorArray.length;
	var last = this.errorArray[length-1];
	this.errorArray.length = length-1;
	return(last);
}

/**
* Check for absence of error massages
* @return true if no errors present, else false
*/
errorAlert.prototype.isValid = function() {
	var retValue = true;

	if ( this.errorArray.length > 0 ) {
		retValue = false;
	}

	return retValue;
}

/**
* Show the error alert if there are error messages
* @return true if no errors present, else false
*/
errorAlert.prototype.showAlert = function() {
	// Array.shift() function not supported in browsers earlier than IE5.5 and NN4
	// if there are errors, shows an alert and returns false
	var length = this.errorArray.length;
	var errorText = "Please correct the following:\n\n";	
	var i = 0;
	var retValue = false;
	
	if ( length > 0 ) {
		// set retValue to false because there are errors		
		retValue = false;
		
		for(i; i < length; i++) {
			errorText += "- " + this.errorArray[i] + "\n";
		}
		this.errorArray.length = 0;
		
		// show the error alert
		alert( errorText );
	}
	
	// set retValue to true because there are no erros
	else 
		retValue = true;
	
	return(retValue);
}

/**
* Create the myErrorAlert object.  This is a global object available to all
* pages and will contain any error messages.
*/
var myErrorAlert = new errorAlert();

/**
* Convenience method to see if we have a blank string
* @param targetStr - the string to use
* @return true if blank, else false
*/
function isBlank(targetStr){
	return(targetStr.search(/^[ \n\t]+$/) != -1);
}

/**
* Validate the contents of the field pointed to by fieldName as a string.
* @param fieldName - HTML field to validate
* @param minLength - minimum length of string (if present).  -1 == don't care
* @param maxLength - maximum length of string (if present).  -1 == don't care
* @param showError - should the method show display an error
* @param extNameStr - external representation of this field for error handling
* @param addToQueue - should we add the error to the myErrorAlert queue
* @return boolean true if OK, else false
*/
function validateAsString(fieldName, theMinLength, theMaxLength, showError, extNameStr, addToQueue){
	var valueStr = fieldName.value;
	var errStr = null;
	var retValue = false;
	var strLength = valueStr.length;
	var minLength = -1;
	var maxLength = -1;

//Do we have a minimum length?
	if(arguments.length >= 2)
		minLength = theMinLength;
//Do we have a maximum length?	
	if(arguments.length >= 3)
		maxLength = theMaxLength;


//Must the string exactly equal some length?
	if(minLength > -1 && minLength == maxLength){
		
		if((retValue = isBlank(valueStr) == false && valueStr.length == minLength) == false)
			errStr = "exactly " + minLength + " characters";
	}
	else{
		//Must the string be at least some length?
		if(minLength > -1 == true){
			
			if((retValue = isBlank(valueStr) == false && valueStr.length >= minLength) == false)
				errStr = "at least " + minLength + " characters";
		}
		else
			retValue = true;
		//Must the string be no greater than some length?
		if(maxLength > -1 == true){
			/*
			If we have an invalid upper bound, we have an error, regardless of the state of retValue
			at this point.  On the other hand, if the upper bound is valid, we need to base the state
			on the previous state of retValue as this might already be invalid.
			*/
			if((isBlank(valueStr) == false && valueStr.length <= maxLength) == false){
				retValue = false;
				errStr = "no more than " + maxLength + " characters";
			}
		}
	}

	if(retValue == false ){
		
		if(errStr != null)
			errStr = "Please enter " + errStr;
			
		if(arguments.length >= 4 && showError == true)
			handleValidationError(fieldName, errStr, extNameStr);

		if(addToQueue == true)
			myErrorAlert.addMessage(extNameStr + ": " + errStr);
	
	}
	return(retValue);
}

/**
* Validate the field supplied as an email address.  In order to return a true the following must
* be true:
* <ol>
*	<li>That fieldName.value is a non-empty string</li>
*	<li>That fieldName.value contains the pattern .+@.+ to determine an email address</li>
* </ol>
* @param fieldName - HTML field to show
* @param showError - should we display the error
* @param extNameStr - field name for error handling
* @param addToQueue - should we add the error to the myErrorAlert queue
* @return true if valid, else false
*/
function validateAsEmail(fieldName, showError, extNameStr, addToQueue){
	var valueStr = null;
	var errStr = "Enter a valid email address";
	var retValue = validateAsString(fieldName, 2, -1, showError, extNameStr);
	
	if(retValue == true){
		valueStr = fieldName.value;
		retValue = valueStr.search(/.+@.+/) != -1;
		
		if(retValue == false && arguments.length >= 2 && showError == true)
			handleValidationError(fieldName, errStr, extNameStr);
	}
	
	if(addToQueue && !retValue) {
		myErrorAlert.addMessage(extNameStr + ": " + errStr);
	}
	
	return(retValue);
}

/**
* Validate the HTML field as a drop down list and make sure that at least 1 item is selected.
* The hintIndex argument allows authors to insert text such as "select an item" into the list
* and ensure that this element does not get passed to the server when the form is submitted.
* @param fieldName - HTML field to validate
* @param hintIndex - index of "hint item" (see above)
* @param showError - if true, this method will display an alert box with the error string
* @param extNameStr - external representation of this string
* @param addToQueue - should we add the error to the myErrorAlert queue
* @return true if valid, else false.
*/
function validateAsDropDown(fieldName, hintIndex, showError, extNameStr, addToQueue){
	var excludeIdx = 0;
	var retValue = false;
	var errStr = null;

	if(arguments.length >= 3)
		excludeIdx = hintIndex;
	
	retValue = fieldName.selectedIndex > -1 && fieldName.selectedIndex != excludeIdx;
	
	if(retValue == false){
		errStr = "Select an item from the list";
	
		if(arguments.length > 2 && showError == true)
			handleValidationError(fieldName, errStr, extNameStr);
	}
	
	if(addToQueue && !retValue) {
		myErrorAlert.addMessage(extNameStr + ": " + errStr);
	}
	
	return(retValue);
}

/**
* Validate the field as a an integer.  Return true if it is a number and between the (optional) bounds
* @param fieldName - name of HTML field to process
* @param showError - true if method should display errors
* @param minVal - the value should be greater than or equal to this value
* @param maxVal - the value should be less than or equal to this value
* @param extNameStr - external field name for error handling
* @param addToQueue - should we add the error to the myErrorAlert queue
* @return - true if it is a number and minVal >= value =< maxVal
*/
function validateAsInteger(fieldName, showError, minVal, maxVal, extNameStr, addToQueue){
	
	var errStr = null;
	var intVal = null;
	var tempStr = fieldName.value;
	var retValue = tempStr.search("^[-]?[0-9]+$") != -1;
	
	if(retValue == true) {
	
		intVal = parseInt(fieldName.value, 10);
		retValue = !isNaN(intVal);
		
		
		if(retValue == true){
			
			if(arguments.length >=3){
	
				if((retValue = intVal >= minVal) == true){
					
					if(arguments.length >= 4){
						
						if((retValue = intVal <= maxVal) == false)
							errStr = "Enter a whole number less than " + (maxVal + 1);
					}
				}
				else
					errStr = "Enter a whole number greater than " + (minVal - 1);
			}
		}
		else
			errStr = "Enter a whole number";
	}
	else
		errStr = "Enter a whole number";
	
	if(retValue == false && arguments.length >= 2 && showError == true)
		handleValidationError(fieldName, errStr, extNameStr);
	
	if(addToQueue && !retValue) {
		myErrorAlert.addMessage(extNameStr + ": " + errStr);
	}
	
	return(retValue);
}

/**
* Validate the field as a float.  Return true if it is a number and between the (optional) bounds
* @param fieldName - name of HTML field to process
* @param showError - true if method should display errors
* @param minVal - the value should be greater than or equal to this value
* @param maxVal - the value should be less than or equal to this value
* @param extNameStr - external field name for error handling
* @param addToQueue - should we add the error to the myErrorAlert queue
* @return - true if it is a number and minVal >= value =< maxVal
*/
function validateAsFloat(fieldName, showError, minVal, maxVal, extNameStr, addToQueue){
	
	var errStr = null;
	var floatVal = null;
	var tempStr = fieldName.value;
	var retValue = tempStr.search("^[-]?[0-9]+([.][0-9]+)?$") != -1;
	
	if(retValue == true) {
	
		floatVal = parseFloat(tempStr, 10);
		retValue = !isNaN(floatVal);
		
		if(retValue == true){
			
			if(arguments.length >=3){
	
				if((retValue = floatVal >= minVal) == true){
					
					if(arguments.length >= 4){
						
						if((retValue = floatVal <= maxVal) == false)
							errStr = "Enter a number less than or equal to " + maxVal;
					}
				}
				else
					errStr = "Enter a number greater than or equal to " + minVal;
			}
		}
		else
			errStr = "Enter a number";
	} // close if(retValue > -1)
	else
		errStr = "Enter a number";
	
	if(retValue == false && arguments.length >= 2 && showError == true)
		handleValidationError(fieldName, errStr, extNameStr);
	
	if(addToQueue && !retValue) {
		myErrorAlert.addMessage(extNameStr + ": " + errStr);
	}
	
	return(retValue);
}

/**
*Validate field as a date.  the granularity at which the date should be compared is crude in that it depends
* on the granularity of the string in fieldName.value which represents the date to compare
* @param fieldName - field name to parse
* @param showError - should this method handle errors
* @param minDate - minimum date to compare against - null if no minimum is required.  Note this should be a Date object
* @param maxDate - maximum date to compare against - null if no maximum is required.  Note this should be a Date object
* @param addToQueue - should we add the error to the myErrorAlert queue
* @return true if validate and between minDate and maxDate if supplied, else false.
*/
function validateAsDate(fieldName, showError, minDate, maxDate, extNameStr, addToQueue){
	var dateVal = new Date(fieldName.value);
	var retValue = validateAsDateObject(dateVal, minDate, maxDate, extNameStr, addToQueue);
	
	if((retValue == false && arguments.length >= 2) && showError == true)
		handleValidationError(fieldName, "Invalid Date / Time", extNameStr);
	
	return(retValue);
}

function validateAsDateObject(dateObject, minDate, maxDate, extNameStr, addToQueue){
	var retValue = false;
	
	/*
	if((retValue = fieldName.value != null && fieldName.value.search("^[ \t\n]*$") == -1) == true){
	*/	
		if((retValue = dateObject != null && isNaN(dateObject) == false) == true){
			
			if(arguments.length >= 3 && minDate != null){
				if((retValue = dateObject.getTime() >= minDate.getTime()) == true) {
					if(arguments.length >= 4 && maxDate != null){
						if((retValue = dateObject.getTime() <= maxDate.getTime()) == false)
							errStr = "Enter a date on or before " + maxDate;
					}
				}
				else
					errStr = "Enter a date on or after " + minDate;
			}
			
		}
		else
			errStr = "Enter a correctly formatted date.  Ex: January 15 2004";
	/*
	}
	else
		errStr = "Enter a date.";
	*/
	
	if(addToQueue && !retValue) {
		myErrorAlert.addMessage(extNameStr + ": " + errStr);
	}
	
	return(retValue);
}


/**
* Validate the field as a String HTML corresponding to a phone number.  In order to do this,
* a phone number is considered as having met the pattern (0)[0-9][0-9][0-9][-. ][0-9][0-9][0-9][-. ][0-9][0-9][0-9][0-9]
* @param fieldName - name of HTML field to validate
* @param showErrors - true if method should display errors
* @param extNameStr - name of field used for error handling
* @param addToQueue - should we add the error to the myErrorAlert queue
* @return true if field validates as a US phone number, else false.
*/
function validateAsUSPhoneNumber(fieldName, showErrors, extNameStr, addToQueue){
	var phoneStr = fieldName.value;
	var errStr = null;
	var retValue = phoneStr.search(/^(\(*0?[0-9][0-9][0-9]\)*[-\. ])?[0-9][0-9][0-9][-\. ][0-9][0-9][0-9][0-9]$/) != -1;

	if(retValue == false){
		errStr = "Enter a valid phone number (area code is optional). Ex: 111-222-3333";

		if(arguments.length >= 2 && showErrors == true)
			handleValidationError(fieldName, errStr, extNameStr);
	}
	
	if(addToQueue && !retValue) {
		myErrorAlert.addMessage(extNameStr + ": " + errStr);
	}
	
	return(retValue);
}

/**
* Validate the field as an HTML text field corresponding to a ZIP code.  A ZIP code
* must contain 5 digits only.  It may then contain an optional delimiter (e.g. space or
* -), and then another 4 digits only.
* @param fieldName - name of HTML field to validate
* @param fullZIP - true to parse a full 5-4 ZIP code, else only a 5 digit code
* @param showErrors - true if method should display errors
* @param extNameStr - external representation of string for error handling
* @param addToQueue - should we add the error to the myErrorAlert queue
* @return true if valid and meets pattern, else false.
*/
function validateAsZIP(fieldName, fullZIP, showErrors, extNameStr, addToQueue){
	var regExpStr = "^[0-9][0-9][0-9][0-9][0-9]";
	var errStr = null;
	var retValue = false;
	var zipStr = fieldName.value;
	
	if(fullZIP == true)
		regExpStr += "[ -\.]?\s*[0-9][0-9][0-9][0-9]";
	
	regExpStr += "$";
	retValue = zipStr.search(regExpStr) != -1;
	
	if(retValue == false){
		if(fullZIP == true)
			errStr = "Enter a 5+4 digit zip code.  Ex: 12345-1234"
		else
			errStr = "Enter a 5 digit zip code";
		
		if(arguments.length >= 3 && showErrors == true)
			handleValidationError(fieldName, errStr, extNameStr);
	}
	
	if(addToQueue && !retValue) {
		myErrorAlert.addMessage(extNameStr + ": " + errStr);
	}

	return(retValue);
}
/**
* Validate the array of checkboxes by making sure that at least one of them is checked.
* The group is an arbitrary array of checkboxes and is in no way related to radio boxes (these
* assume that the choices are mutually exclusive).
* @param checkBoxArray - array to check
* @param showErrors - true if method should handle errors
* @param extNameStr - external name for this group
* @param addToQueue - should we add the error to the myErrorAlert queue
* @return true if OK, else false
*/
function validateAsCheckBoxGroup(checkBoxArray, showErrors, extNameStr, addToQueue){
	var retValue = false;
	var errStr = null;
	var counter = 0;
	
	while(counter < checkBoxArray.length && retValue == false){
		
		if((retValue = checkBoxArray[counter].checked == true) == false)
			counter++;
	}
	
	if(retValue == false){
		errStr = "Select at least one item from the group";
		
		if(arguments.length >= 2 && showErrors == true)
			handleValidationError("Checkbox group", errStr, extNameStr);
	}
	
	if(addToQueue && !retValue) {
		myErrorAlert.addMessage(extNameStr + ": " + errStr);
	}
	
	return(retValue);
}

/**
* Function to validate as time in the format hh:mm where hh between 0 and 23 or 0 and 12
* depending on the value of validate24.  Note that this is only valid when the UI widget is a text field
* and mm in the range 0-59
* @param field - field to validate
* @param validate24 - should the validation be a 24hr validation or a 12hr
* @param showErrors - if true errors handled by this method.  False indicates that the calling method
* will be responsible for 
* @param extNameStr - External name of string to be used for error handling
* @param addToQueue - if true then pass on to external messaging
*/
function validateAsTime(fieldName, validate24, showErrors, extNameStr, addToQueue){
	var regExpStr = null;
	var errStr = null;
	var retValue = false;
	var timeStr = fieldName.value;
	var time = 0;
	var startHrs = 0;
	var endHrs = 23;
	
	if(arguments.length >= 2){
		//Switch to 12 hour validation
		if(validate24 == false){
			startHrs = 1;
			endHrs = 12;
			regExpStr = "^[0-9]*[0-9]:[0-9][0-9]$";
		}
		else
			regExpStr = "^[0-9][0-9]:[0-9][0-9]$";
	}
	retValue = timeStr.search(regExpStr) != -1;
	
	if(retValue == true){
		time = parseInt(timeStr.substring(0, 2));
		
		if((retValue = isNaN(time) == false && time >= startHrs && time <= endHrs) == true){
			time = parseInt(timeStr.substring(3,5));
			
			if((retValue = isNaN(time) == false && time >= 0 && time <= 59) == false)
				errStr = "Minutes must be between 0 and 59";
		}
		else
			errStr = "Hours must be be between " + startHrs + " and " + endHrs;
	}
	else{
		errStr = "Enter a time in the format hh:mm";
		
		if(arguments.length >= 3 && showErrors == true)
			handleValidationError(fieldName, errStr, extNameStr);
	}
	
	if(addToQueue && !retValue)
		myErrorAlert.addMessage(extNameStr + ": " + errStr);

	return(retValue);
}
