/*********************************************************************
JavaScript 1.2 Validation Script (IE and Netscape)
	version 1.02
	by matthew frank

There are no warranties expressed or implied.  This script may be
re-used and distrubted freely provided this header remains intact
and all supporting files are included (unaltered) in the distribution:

10/01/01 tda - changes to taste.

*********************************************************************/


/*====================================================================
Function: Validation
Purpose:  Custom object constructor.
Inputs:   None
Returns:  undefined
====================================================================*/
function Validation()
{
	/*====================================================================
	Function: Err
	Purpose:  Custom object constructor
	Inputs:   None
	Returns:  undefined
	====================================================================*/
	var Err=function()
	{
		var oSource  = new Object;
		var sMessage = new String;
		/*********************************************************************
		Method:   Err.clear
		Purpose:  Clear values from Error object
		Inputs:   none
		Returns:  undefined
		*********************************************************************/
		this.clear=function ()
		{
			oSource  = new Object;
			sMessage = new String;
		}
		/*********************************************************************
		Method:   Validation.Err.add
		Purpose:  Adds error to Error object
		Inputs:   oSource - source element object
				  vType   - integer value of error type (or custom string)
				  sFormat - optional date format
		Returns:  undefined
		*********************************************************************/
		this.add=function (oSrc,sMsg)
		{
			oSource  = oSrc;
			sMessage = sMsg;
		}
		/*********************************************************************
		Method:   Err.raise
		Purpose:  Gives visual warning to user about all errors contained in
				  the Error object
		Inputs:   none
		Returns:  undefined
		*********************************************************************/
		this.raise=function ()
		{
			var sName = oSource.NAME;
			var sMsg  = oSource.MSG;

			// Alert appropriate error message
			sMsg = sMsg ? sMsg : sMessage + (sName ? " in the "+sName+" field" : "");
			alert(sMsg);

			// 09/07/01 TA
			// Set the className. The HTML should have required=true...
			oSource.className = ((oSource.required) || (oSource.REQUIRED)) ? "requiredHighlight" : "Highlight";

			// Give invalid field focus
			if(oSource.focus)
				oSource.focus();
			if(oSource.select)
				oSource.select();
			// Clear the Err object
			this.clear();
		}
	}
	/*********************************************************************
	Function: isDate
	Purpose:  Check that value is a date of the correct format
	Inputs:   sDate   - date as string
	          sFormat - string format
	Returns:  boolean
	*********************************************************************/
	var isDate=function (sDate,sFormat)
	{
		var aDaysInMonth=new Array(31,28,31,30,31,30,31,31,30,31,30,31);

		// Fetch the date separator from the user's input
		var sSepDate=sDate.charAt(sDate.search(/\D/));
		// Fetch the date separator from the format
		var sSepFormat=sFormat.charAt(sFormat.search(/[^MDY]/i));
		// Compare separators
		if (sSepDate!=sSepFormat)
			return false;

		// Fetch the three pieces of the date from the user's input and the format
		var aValueMDY=sDate.split(sSepDate);
		var aFormatMDY=sFormat.split(sSepFormat);

		// Assign day, month, year based on format
		var iMonth,iDay,iYear;
		iMonth = aValueMDY[0];
		iDay   = aValueMDY[1];
		iYear  = aValueMDY[2];

		// Validate that all pieces of the date are numbers
		if (  !isNum( iMonth )
			||!isNum( iDay   )
			||!isNum( iYear  ) )
			return false;

		// Require 4 digit year
		if(iYear.length!=4)
			return false;

		// Check for leap year
		var iDaysInMonth=(iMonth!=2)?aDaysInMonth[iMonth-1]:
			((iYear%4==0 && iYear%100!=0 || iYear % 400==0)?29:28);

		return (iDay!=null && iMonth!=null && iYear!=null
				&& iMonth<13 && iMonth>0 && iDay>0 && iDay<=iDaysInMonth);
	}
	/********************************************
	Function: isNum
	Purpose:  Check that parameter is a number
	Inputs:   v - string value
	Returns:  boolean
	********************************************/
	var isNum=function (v)
	{
		return (typeof v!="undefined" && v.toString() && !/\D/.test(v));
	}
	/********************************************
	Function: formatNumber
	Purpose:  Format an integer for display
	Inputs:   i - integer value (as string)
	Returns:  string
	********************************************/
	var formatNumber=function (i)
	{
		var sEnd="$";
		i = i.toString();
		if (/\./.test(i))
			sEnd="\\.";
		var re = new RegExp("(\\d)(\\d{3})(,|"+sEnd+")");
		if(re.test(i))
			i = formatNumber(i.replace(re, "$1,$2$3"));
		return i;
	}
	/********************************************
	Function: getValueOf
	Purpose:  Return the value of a form field as seen at server
	Inputs:   oElement - form field
	Returns:  boolean
	********************************************/
	var getValueOf=function (oElement)
	{
		var sReturnValue = null;
		switch (oElement.type)
		{
			case "text" : case "textarea" : case "file" : case "password" : case "hidden" :
				sReturnValue = oElement.value;
				break;
			case "select-one" :
				sReturnValue = oElement.options[oElement.selectedIndex].value;
				break;
			case "select-multiple" :
				var i, iOptions = oElement.options.length;
				for(i=0; i<iOptions; i++)
					if(oElement.options[i].selected && oElement.options[i].value.toString().trim())
					{
						sReturnValue = true;
						break;
					}
				break;
			case "radio" : case "checkbox" :
				if(oElement.checked)
					sReturnValue = oElement.value ? oElement.value : true;
				break;
		}
		return sReturnValue;
	}
	/*********************************************************************
	Method:   Validation.setup
	Purpose:  Set up methods and event handlers for all forms and elements
	Inputs:   none
	Returns:  undefined
	*********************************************************************/
	this.setup=function ()
	{
		// Fan through forms on page to perform initializations
		var i,oForm,iForms=document.forms.length;
		for(i=0; i<iForms; i++){
			oForm=document.forms[i];
			if(!oForm.bProcessed)
			{
				// Capture and replace onsubmit event handler
				var fnSubmit=oForm.onsubmit;

				// Create new event handlers
				oForm.onsubmit=function ()
				{
					// Execute onbeforevalidate processing
					if(typeof this.onbeforevalidate=="function" && this.onbeforevalidate()==false)
						return false;
					var i, oElement, iElements=this.elements.length;
					// Validate individual elements
					for(i=0;i<iElements;i++)
					{
						oElement=this.elements[i];
						// Perform default validation for element
						if (!oElement.valid())
						{
							Validation.Err.raise();
							return false;
						}
					}
					// Execute onaftervalidate processing
					if(typeof this.onaftervalidate=="function" && this.onaftervalidate()==false)
						return false;

					// Perform original onsubmit event handler
					if (fnSubmit && fnSubmit()==false)
						return false;
				}
				oForm.bProcessed=true;
			}
			// Create Input methods
			var j, iElements=oForm.elements.length;
			for(j=0; j<iElements; j++)
			{
				var oElement=oForm.elements[j];
				
				// 09/07/01 TA
				if (oElement.required) oElement.className = "required";
				
				if(!oElement.bProcessed)
				{
					/**********************************************
					Method:   Input.valid
					Purpose:  Validate an element based on the
					          attributes provided in the HTML text
					Inputs:   none
					Returns:  boolean
					**********************************************/
					oElement.valid=function ()
					{
					
						// 09/07/01 TA
						this.className = ((this.required) || (this.REQUIRED)) ? "required" : "";
						
						// Trim leading and trailing spaces
						if(this.value)
							this.value = this.value.trim();
						var sValue = getValueOf(this);
						// REQUIRED
						if(((this.REQUIRED) || (this.required)) && !sValue)
						{
							Validation.Err.add(this, "Please enter a value for " + this.name);
					 		return false;
						}
						// FLOAT
						var bSigned=this.SIGNED;
						if (this.FLOAT && sValue)
						{
							var re=new RegExp("^"+((bSigned)?"-?":"")+"(\\d*(,?\\d{3})*\\.?\\d+|\\d+(,?\\d{3})*\\.?\\d*)$");
							if (!re.test(sValue))
							{
								Validation.Err.add(this, "Please enter a number with decimals");
								return false;
							}
							// MIN
							var iMin = this.MIN;
							if(iMin==parseFloat(iMin) && sValue.replace(/,/,"")<iMin)
							{
								Validation.Err.add(this, "Please enter a value greater than or equal to "+formatNumber(iMin));
								return false;
							}
							// MAX
							var iMax = this.MAX;
							if(iMax==parseFloat(iMax) && sValue.replace(/,/,"")>iMax)
							{
								Validation.Err.add(this, "Please enter a value less than or equal to "+formatNumber(iMax));
								return false;
							}
						}
						// AMOUNT
						if (this.AMOUNT && sValue)
						{
							var sSigned = "(\\$?-?|-?\\$?)";
							var re = new RegExp("^"+((bSigned)?sSigned:"\\$?")+"((\\d{1,3})*(,?\\d{3})*\\.?\\d{2}|\\d{1,3}(,?\\d{3})*\\.?(\\d{2})?)$");
							if(!re.test(sValue))
							{
								Validation.Err.add(this, "Please enter a dollar amount");
								return false;
							}
							// MIN
							var iMin = this.MIN;
							if(iMin==parseFloat(iMin) && sValue.replace(/[\$,]/,"")<iMin)
							{
								Validation.Err.add(this, "Please enter a value greater than or equal to $"+formatNumber(iMin));
								return false;
							}
							// MAX
							var iMax = this.MAX;
							if(iMax==parseFloat(iMax) && sValue.replace(/[\$,]/,"")>iMax)
							{
								Validation.Err.add(this, "Please enter a value less than or equal to $"+formatNumber(iMax));
								return false;
							}
						}
						// INTEGER
						if (this.INTEGER && sValue)
						{
							var re=new RegExp("^"+((bSigned)?"-?":"")+"\\d{1,3}(,?\\d{3})*$");
							if (!re.test(sValue))
							{
								Validation.Err.add(this, "Please enter an integer");
								return false;
							}
							// MIN
							var iMin = this.MIN;
							if(iMin==parseInt(iMin) && sValue.replace(/,/,"")<iMin)
							{
								Validation.Err.add(this, "Please enter a value greater than or equal to "+formatNumber(iMin));
								return false;
							}
							// MAX
							var iMax = this.MAX;
							if(iMax==parseInt(iMax) && sValue.replace(/,/,"")>iMax)
							{
								Validation.Err.add(this, "Please enter a value less than or equal to "+formatNumber(iMax));
								return false;
							}
						}
						// DATE
						if (this.DATE && sValue)
						{
							// Set default date format
							
							var sFormat = "MM/DD/YYYY";
							if (!isDate(sValue,sFormat)) 
							{
								Validation.Err.add(this, "Please enter a date as MM/DD/YYYY");
								return false;
							}
						}
						// REGEXP
						var oRegexp=this.REGEXP;
						if(oRegexp && oRegexp.constructor==RegExp && sValue)
						{
							if(!oRegexp.test(sValue))
							{
								Validation.Err.add(this, "Please enter a valid value");
								return false;
							}
						}
						// PHONE
						if(this.PHONE && sValue)
						{
							var sPhone  = sValue.replace(/\D/g,"");
							var iDigits = sPhone.length;
							if( !(iDigits==10 || iDigits==11 && /^1/.test(sPhone)) )
							{
								Validation.Err.add(this, "Please enter a valid phone number");
								return false;
							}
						}
						// LENGTH
						if(sValue && this.LENGTH && isNum(this.LENGTH) && sValue.length>this.LENGTH)
						{
							Validation.Err.add(this, "Please enter a value with fewer than " + formatNumber(this.LENGTH) + " characters");
							return false;
						}
						// EMAIL
						if(this.EMAIL && sValue)
						{
							if(!/^[\w_-]+(\.[\w_-]+)*@[\w_-]+(\.[\w_-]+)*\.\w{2,3}$/i.test(sValue))
							{
								Validation.Err.add(this, "Please enter a valid email address");
								return false;
							}
						}
						// ZIP Code
						if(this.ZIP && sValue)
						{
							if(!/^\d{5}(-?\d{4})?$/.test(sValue))
							{
								Validation.Err.add(this, "Please enter a valid ZIP code");
								return false;
							}
						}
						// AND
						var vAnd=this.AND;
						if(vAnd && sValue)
						{
							// If not an array, create one
							if(vAnd.constructor!=Array)
								vAnd = vAnd.toString().split(/,/);
							// Require each element in the list
							var i, iFields=vAnd.length;
							for(i=0; i<iFields; i++)
							{
								var oNewElement =
									(vAnd[i].form)
									? vAnd[i]
									: this.form.elements[vAnd[i].trim()];
								if(oNewElement)
								{
									if(!getValueOf(oNewElement))
									{
										Validation.Err.add(oNewElement, "Please enter a value");
										return false;
									}
								}
							}
						}
						// OR
						var vOr=typeof this.OR=="object";
						if(vOr && !sValue)
						{
							vOr = this.OR["fields"];
							if(vOr)
							{
								// If not an array, create one
								if(vOr.constructor!=Array)
									vOr = vOr.toString().split(/,/);
								// Require each element in the list
								var i, iFields=vOr.length;
								var bValue=false;
								for(i=0; i<iFields; i++)
								{
									var oNewElement =
										(vOr[i].form)
										? vOr[i]
										: this.form.elements[vOr[i].trim()];
									if(oNewElement)
										bValue |= !!getValueOf(oNewElement);
								}
								if(!bValue)
								{
									Validation.Err.add(this, this.OR["msg"]?this.OR["msg"]:"Please enter a value");
									return false;
								}
							}
						}
						// NOSPACE
						if (this.NOSPACE && this.value)
							this.value=this.value.replace(/\s/g,"");
						// UPPERCASE
						if (this.UPPERCASE && this.value)
							this.value=this.value.toUpperCase();
						// LOWERCASE
						if (this.LOWERCASE && this.value)
							this.value=this.value.toLowerCase();

						// Perform onvalidate event handler
						if(typeof this.onvalidate=="function" && this.onvalidate()==false)
							return false;

						return true;
					}
					oElement.bProcessed=true;
				}
			}
		}
	}
	// Limit use of script to valid environments
	if("".replace && window.RegExp)
	{
		/*********************************************************************
		Method:   String.trim
		Purpose:  Removing leading and trailing spaces
		Inputs:   none
		Returns:  string
		*********************************************************************/
		String.prototype.trim=function ()
		{
			return this.replace(/^\s+|\s+$/g,"");
		}

		// Form setup
		if(document.forms)
		{
			this.setup();
			this.Err=new Err;
		}
	}
}

Validation=new Validation;