/*********************************************************************
JavaScript 1.2 Validation Script (IE and Netscape)
	version 1.00
	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:

		nn-validation.js  - this file
		nn-validation.htm - example form
		nn-readme.htm     - directions on using this script
		(nn-validation.zip contains all files)

If you use the script, please send me an email with the URL of the
site if it is public.  It will help me keep you updated on the latest
versions of script.  It will also let me keep up with necessary
changes.

		validation@mindspring.com

*********************************************************************/


/*====================================================================
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);

			// 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:   oElement - form element
	          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,3);
		var aFormatMDY=sFormat.split(sSepFormat,3);

		// Assign day, month, year based on format
		var iMonth,iDay,iYear;
		iMonth = aValueMDY[0];
		iDay   = aValueMDY[1];
		iYear  = aValueMDY[2];

		// Added by Ram Sangam, marchFIRST
		// Make sure the objects are valid
		//alert(iMonth) ;
		//alert(iDay) ;
		//alert(iYear) ;
		if (!(iMonth)
			||!(iDay)
			||!(iYear))
			return false ;
			
		// Added by Ram Sangam, marchFIRST
		// Make sure the length of all the fields
		if ((iMonth.length == 0)
			||(iDay.length == 0)
			||(iYear.length == 0))
			return false;

		// 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 (v.toString() && !/\D/.test(v));
	}
	/********************************************
	Function: GetvalueOf
	Purpose:  Return the value of a form field as seen at server
	Inputs:   oElement - form field
	Returns:  boolean
	********************************************/
	var GetvalueOf=function (oElement)
	{
		//alert("Inside GetvalueOf: ElementType = "+oElement.type) ;
		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;
					}
			case "radio" : case "checkbox" :
				if(oElement.checked)
					sReturnValue = oElement.value ? oElement.value : true;
				break;
		}
		//alert("Inside GetvalueOf: ReturnValue = "+sReturnValue) ;
		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;
			//alert("No. of Elements in Form = "+iElements) ;
			for(j=0; j<iElements; j++)
			{
				var oElement=oForm.elements[j];
				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 ()
					{
						//alert("I'm in Validation. Value is "+this.value) ;
						
						// Trim leading and trailing spaces
						if(this.value)
						{
							this.value = this.value.trim();
						}
						
						//alert("Going to Call GetvalueOf") ;
						var sValue = GetvalueOf(this);
						
						// Modified by Ram Sangam, marchFIRST to make it work for Netscape
						//sValue = this.value
						//var sValue = null;
						//alert("ElementType = "+this.type)
						//switch (this.type)
						//{
						//	case "text" : case "textarea" : case "file" : case "password" : case "hidden" :
						//		sValue = this.value;
						//		break;
						//	case "select-one" :
						//		sValue = this.options[this.selectedIndex].value;
						//		break;
						//	case "select-multiple" :
						//		var i, iOptions = this.options.length;
						//		for(i=0; i<iOptions; i++)
						//			if(this.options[i].selected && this.options[i].value.toString().trim())
						//			{
						//				sValue = true;
						//				break;
						//			}
						//	case "radio" : case "checkbox" :
						//		if(this.checked)
						//			sValue = this.value ? this.value : true;
						//		break;
						//}
						//alert("Value is "+sValue) ;
						
						// FLOAT
						var bSigned=this.SIGNED;
						if (this.FLOAT && sValue)
						{
							var re=new RegExp("^"+((bSigned)?"[\\-\\+]?":"")+"(\\d*\\.?\\d+|\\d+\\.?\\d*)$");
							if (!re.test(sValue))
							{
								Validation.Err.add(this, "Please enter a number");
								return false;
							}
						}
						// AMOUNT
						if (this.AMOUNT && sValue)
						{
							var re = new RegExp("^"+((bSigned)?"[\\-\\+]?":"")+"(\\d*\\.?\\d{1,2}|\\d+\\.?(\\d{1,2})?)$");
							if(!re.test(sValue))
							{
								Validation.Err.add(this, "Please enter a dollar amount");
								return false;
							}
						}
						// INTEGER
						if (this.INTEGER && sValue)
						{
							var re=new RegExp("^"+((bSigned)?"[\\-\\+]?":"")+"\\d+$");
							if (!re.test(sValue))
							{
								Validation.Err.add(this, "Please enter an integer");
								return false;
							}
							// MIN
							var iMin = this.MIN;
							if(iMin==parseInt(iMin) && sValue<iMin)
							{
								Validation.Err.add(this, "Please enter a value greater than or equal to "+iMin);
								return false;
							}
							// MAX
							var iMax = this.MAX;
							if(iMax==parseInt(iMax) && sValue>iMax)
							{
								Validation.Err.add(this, "Please enter a value less than or equal to "+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;
							}
						}
						// EMAIL
						if(this.EMAIL && sValue)
						{
							if(!/^[\w_-]+(\.[\w_-]+)*@[\w_-]+(\.[\w_-]+)*\.\w{2,4}$/.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;
							}
						}
						// REQUIRED
						if(this.REQUIRED && !sValue)
						{
							Validation.Err.add(this, "Please enter a value");
					 		return false;
						}
						// AND
						var vAnd=this.AND;
						//alert("vAnd = "+vAnd) ;
						if(vAnd!=null && sValue)
						{
							//alert("Inside AND routine") ;
							// 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;
							//alert("no. of fields = "+iFields) ;
							for(i=0; i<iFields; i++)
							{
								//alert("Getting Element") ;
								var oNewElement =
									(vAnd[i].form)
									? vAnd[i]
									: this.form.elements[vAnd[i].trim()];
								if(oNewElement)
								{
									//alert("Inside Element") ;
									//alert("New Element ="+oNewElement) ;
									//alert("New Element Type ="+oNewElement.type) ;
									var sNewValue = GetvalueOf(oNewElement) ;
									//Added by Ram Sangam, marchFIRST
									//since valueOf does not work in Netscape
									//Getting the Value
									//alert("Going to get New Value") ;
									//switch (oNewElement.type)
									//{
									//	case "text" : case "textarea" : case "file" : case "password" : case "hidden" :
									//		sNewValue = oNewElement.value;
									//		break;
									//	case "select-one" :
									//		sNewValue = oNewElement.options[oNewElement.selectedIndex].value;
									//		break;
									//	case "select-multiple" :
									//		var i, iOptions = oNewElement.options.length;
									//		for(i=0; i<iOptions; i++)
									//			if(oNewElement.options[i].selected && oNewElement.options[i].value.toString().trim())
									//			{
									//				sNewValue = true;
									//				break;
									//			}
									//	case "radio" : case "checkbox" :
									//		if(oNewElement.checked)
									//			sNewValue = this.value ? this.value : true;
									//		break;
									//}
									//if(oNewElement.type == "text" || oNewElement.type == "textarea" || oNewElement.type == "file" || oNewElement.type == "password" || oNewElement.type == "hidden")
									//{
									//		alert("Getting New Value") ;
									//		sNewValue = oNewElement.value;
									//}
									//alert("After New Value ="+sNewValue) ;
									if(!sNewValue)
									{
										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;
					}
					this.bProcessed=true;
					//alert("Processed = 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();
			// Expose error object to outside script
			this.Err=new Err;
		}
	}
}

Validation=new Validation;