/***************************************************************************************
JavaScript 1.2 Validation Script (IE and Netscape)
	version 1.00
	by matthew frank
	
	validation@mindspring.com

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:

		enformiaValidation.js	- this file
		mftestsetup.htm			- example using setup form(s)
		mftestform.htm			- example validating one form (no setup)
		mftestcontrol.htm		- example validating controls
		mfreadme.htm			- directions on using this script

adjusted by enformia
was tested on IE 5.0, NS 4.08, 4.7, 6.0

interface:
==========
enValidation.isDate(sDate,sFormat,bConvert)
	checks that sDate is valid according to sFormat (default to enValidation.DATEFORMAT)
	sFormat if a basic format such as "DD/MM/YYYY", "YY/MM/DD", ets.
	returnd true/false, or a date object if bConvert is true.
enValidation.hasDate(oElement,sFormat,bConvert) - same, gets a control

enValidation.isNum(v)
	validate v as a numeric value.
enValidation.hasNum(oElement) - same, gets a control

enValidation.isNotEmpty(v)
	checks for any value
enValidation.hasNotEmpty(oElement) - same, gets a control

enValidation.isFloat(sValue,bSigned)
	checks for numeric value
enValidation.hasFloat(oElement,bSigned) - same, gets a control

enValidation.isInteger(sValue,bSigned)
	checks for integer value
enValidation.hasInteger(oElement,bSigned) - same, gets a control

enValidation.isRegExp(sValue,oRegExp)
	Check that parameter has a pattern for a given regexp
enValidation.hasRegExp(oElement,oRegExp) - same, gets a control

enValidation.isGivenValues(sValue,sVals)
	Check that parameter has a given value
enValidation.hasGivenValue(oElement,sVals) - same, gets a control
	
enValidation.isAllowedFileType(sValue,sTypes)
	Check that parameter has file name from a list of valid file types
enValidation.hasAllowedFileType(oElement,sTypes) - same, gets a control

enValidation.isForbiddenFileType(sValue,sTypes)
	Check that parameter does not have forbidden file name from a list of forbidden file types
enValidation.hasForbiddenFileType(oElement,sTypes) - same, gets a control
	
enValidation.isEmail(sValue)
	checks for one email pattern
enValidation.hasEmail(oElement) - same, gets a control

enValidation.isEmails(sValue)
	checks for few emails pattern
enValidation.hasEmails(oElement) - same, gets a control

enValidation.isPhone(sValue)
	checks for phone pattern
enValidation.hasPhone(oElement) - same, gets a control

enValidation.isZip(sValue)
	checks for zip pattern
enValidation.hasZip(oElement) - same, gets a control

enValidation.isSysName(sValue)
	checks for valid system name pattern
enValidation.hasSysName(oElement) - same, gets a control

enValidation.getValueOf(oElement)
	gets oElement's value, whatever control type it is
	
enValidation.validateControl(oElement)
	validate a control according to it's validation attributes
	if not valid an alert shows. unless NOMSG is on for the control,
	the form or the validation object

enValidation.validateControlsArray(form,controlName,validate)
	validate an array of controls
	
enValidation.validateForm(oForm,bIgnore)
	validates all controls on a form.
	first runs onbeforevalidate function, if successfull,
	validates all control on the form using validation.validateControl.
	if all validation is ok runs onaftervalidate. if successfull,
	runs original onsubmit. if not successfull returns false.
	else ,returns true.
enValidation.setupForms(oForm)
	set up form(s) for validation:
	saves form's onsubmit event and set it temporarilly to validation script
enValidation.Err
	Err object add, raise, clear
enValidation.DATEFORMAT
	date format to be used if no format attribute is present on the form or control
	or passed to isDate(). default value="MM/DD/YYYY".
		
how to use:
===========
1. include this script in the page:
		<!-- validation script. instanciate a window level object enValidation-->
		&lt;script language="JavaScript1.2" type="text/javascript"
			src="enformiaValidation.js"&gt;&lt;/script&gt;
	the script will instanciate an object called enValidation fro u.
2. write your form(s)
3. after each form insert a script block and define validation properties for
	controls on the form
4. u may set a custom date dormat (the default id MM/DD/YYYY)
		enValidation.DATEFORMAT="DD/MM/YYYY";
	if u are using the setup uproach call setup method
		enValidation.setupForms();
		or
		enValidation.setupForms(document.forms["formName"]);

enValidation attributes:
======================
enValidation object:
------------------
- NOMSG - validation will not alert the user at all
- NOFOCUS - validation will not pocus on relevant elements
- DATEFORMAT - a default date format for date validation

form level:
-----------
- NOMSG - validation will not alert the user when validating that form
- NOFOCUS - validation will not pocus on relevant elements on the form
- IGNORE_INACCESSIBLE - ignore inacessible elements (readonly, disabled, hidden)
- IGNORE_READONLY - ignore readonly elements
- IGNORE_DISABLED - ignore disabled elements
- IGNORE_HIDDEN - ignore hidden elements
- DATEFORMAT - a default date format for date validation on that form
- bProcessed (internal usage) - a form level flag to hold the setup state 
	of the form (has it been setup ?)
- onbeforevalidate - runs before validation. if returns false, validation is canceled
	(oForm.onbeforevalidate = function () { ... })
- onaftervalidate - runs after validation is complete successfuly. if returns false,
	the original onsubmit is not run
	(oForm.onaftervalidate = function () { ... })
- fnSubmit (internal usage) - a form level attribute to hold the 
	original onsubmit function

control level:
--------------
behaviour:
- NOMSG - validation will not alert the user when validating that control
- NOFOCUS - validation will not pocus on the element
- IGNORE_INACCESSIBLE - ignore if inacessible (readonly, disabled, hidden)
- IGNORE_READONLY - ignore if readonly
- IGNORE_DISABLED - ignore if disabled
- IGNORE_HIDDEN - ignore if hidden
- DATEFORMAT - a default date format for date validation on that control
- MSG - a custom msg per control
- NAME - a custom name to show in the alert
- onvalidate - runs directly after all default validation for a given control.
	Use this event to trigger validation on SELECT elements.
	(ctrl.onvalidate = function () { ... })
	
validation:
- REQUIRED - marks control as required (ctrl.REQUIRED=true;)

- FLOAT - value has to be numeric (ctrl.FLOAT=true;)
- INTEGER - value has to be an integer (ctrl.INTEGER=true;)
- SIGNED - value may be signed. the default is false (ctrl.SIGNED=true;)
- MIN - min value permitted. allowed only with FLOAT/INTEGER (ctrl.MIN=12;)
- MAX - max value permitted. allowed only with FLOAT/INTEGER (ctrl.MAX=18;)
- MAX_LEN - max no of chars
- MIN_LEN - min no of chars

- DATE - has to be a valid date (according to DATEFORMAT) (ctrl.DATE=true;)
- ZIP - a zip code (US 99999 or 99999-9999) (ctrl.ZIP=true;)
- EMAIL - a valid email (ctrl.EMAIL=true;)
- EMAILS - a valid few emails (ctrl.EMAILS=true;)
- ALLOWEDVALUES - given values to check existance (comma separated list)
- FORBIDDENVALUES - given values to check non existance (comma separated list)
- ALLOWEDFILETYPES - valid file types, based on a given file types list
- FORBIDDENFILETYPES - forbidden file types, based on a given file types list
- REGEXP - a custom regular expression (ctrl.REGEXP=/^\d{7}(-?\d{2})?$/ ..... ;)
- PHONE - a valid phone (US) (ctrl.PHONE=true;)
- SYSNAME - a valid system name

- ISARRAY - validate control as part of array (options and checkboxes)
	the validation attributes should be asssigned to the first element in the array:
		if(elementName[0])
		{			
			elementName[0].NAME="test";
			elementName[0].REQUIRED=true;
			elementName[0].ISARRAY=true;
		}
		else if(elementName)
		{			
			elementName.NAME="test";
			elementName.REQUIRED=true;
		}

- AND - a control / control that are required if this control is fead
	(ctrl.AND=new Array(ctrl1, ctrl2, ctrl3);
	or ctrl.AND=ctrl1;)
	
- OR - a controls that may be fead instead of this control
	(ctrl.OR=
		{
			"fields" : new Array(ctrl1,ctrl2),
			"msg"    : "Please enter ctrl1, ctrl2 or ctrl3"
		}
	)

post validation formatting:
- UPPERCASE - turns value to upper case (ctrl.UPPERCASE=true;)
- LOWERCASE - turns value to lower case (ctrl.LOWERCASE=true;)
- NOSPACE - trim spaces (ctrl.NOSPACE=true;)
- FIXSYSNAME - fix system name

remarks:
========
1. when using the setup approach your onsubmit event is replaced with the validation script
	and run arter the validation is successfull.
2. if u need the validation to avoid prompting the user, set NOMSG attribute
	on the control, form or validation object (element.NOMSG=true;).
3. the script adds a trim() method to the string type.
4. when NOMSG is on, no focus is set in any field
		

***************************************************************************************/


/*====================================================================
Function: enValidationConstructor
Purpose:  Custom object constructor.
Inputs:   None
Returns:  undefined
====================================================================*/
function enValidationConstructor(){
	/*====================================================================
	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:   enValidation.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;
			//handle silant condition
			if(!(oSource.NOMSG || oSource.form && oSource.form.NOMSG || enValidation.NOMSG)){
				// Alert appropriate error message
				sMsg = sMsg ? sMsg : sMessage + (enMsg.get("enValidation.inTheField").replace("~1~",sName));
				alert(sMsg);
				
				
				if(!(oSource.NOFOCUS || oSource.form && oSource.form.NOFOCUS || enValidation.NOFOCUS)){
					// Give invalid field focus
					try
					{
						if(oSource.focus)oSource.focus();
						if(oSource.select)oSource.select();
					}
					catch(e){}
				}
			}
			
			// Clear the Err object
			this.clear();
		}
	}
	
	/********************************************
	Function: getValueOf
	Purpose:  Return the value of a form field as seen at server
	Inputs:   oElement - form field or array of
	Returns:  boolean
	********************************************/
	var getValueOf=function (oElement){
		//check if form element or array		
		var retVal=null;
		if(oElement.ISARRAY)
		{
			oElement=oElement.form.elements[oElement.name];
			var len=oElement.length;
			for(var i=0;i<len;i++)
			{
				//return the first value that is not null
				retVal= _getValueOf(oElement[i]);
				if(retVal)return retVal;
			}
			return retVal;
		}
		else 
		{
			return _getValueOf(oElement);	
		}
	}
	var _getValueOf=function (oElement){
		var sReturnValue = null;
		if(!oElement)return sReturnValue;
		switch (oElement.type){
			case "text" : case "textarea" : case "file" : case "password" : case "hidden" :
				sReturnValue = oElement.value;
				break;
			case "select-one" :
				if(!oElement.options.length || oElement.selectedIndex==-1)return;
				sReturnValue = oElement.options[oElement.selectedIndex].value;
				break;
			case "select-multiple" :
				var i, iOptions = oElement.options.length;
				if(!iOptions || oElement.selectedIndex==-1)return;
				for(i=0; i<iOptions; i++){
					if(oElement.options[i].selected && oElement.options[i].value.toString().trim()){
						sReturnValue = true;
						break;
					}
				}
			case "checkbox" :
				if(oElement.checked)
					sReturnValue = oElement.value ? oElement.value : true;
				break;
			case "radio" :
				if(oElement.value!=null && oElement.checked) sReturnValue= oElement.value;
				break;
		}
		return sReturnValue;
	}
	
	/********************************************
	Function: isSet
	Purpose:  checks if a var is set
	Inputs:   prm - var to check
	Returns:  boolean
	********************************************/
	var isSet=function(prm)
	{
		var und;
		return(prm!=und && prm!=null);
	}
	
	/*********************************************************************
	Function: isDate
	Purpose:  Check that value is a date of the correct format
	Inputs:   sDate		- string date
	          sFormat	- string format
	          bConvert	- whether to return true/false or a date object
	Returns:  boolean / date object(if valid)
	*********************************************************************/
	var isDate=function (sDate,sFormat,bConvert){
		if(!sDate)return false;
		//default format
		if(!sFormat)sFormat=enValidation.DATEFORMAT;
		sFormat=sFormat.toUpperCase();

		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);
		
		var dayPos,monthPos,yearPos,yearLen;
		//search format
		for(var i=0;i<3;i++){
			if(aFormatMDY[i].indexOf("D")!=-1)dayPos=i;
			if(aFormatMDY[i].indexOf("M")!=-1)monthPos=i;
			if(aFormatMDY[i].indexOf("Y")!=-1)yearPos=i;
		}
		yearLen=aFormatMDY[yearPos].length;
		
		// Assign day, month, year based on format
		var iMonth,iDay,iYear;
		iMonth = aValueMDY[monthPos];
		iDay   = aValueMDY[dayPos];
		iYear  = aValueMDY[yearPos];

		// Validate that all pieces of the date are numbers
		if (  !isNum( iMonth )
			||!isNum( iDay   )
			||!isNum( iYear  ) )return false;

		// Require year digits
		if(yearLen!=2 && yearLen!=4)yearLen=4;
		if(iYear.length!=yearLen && 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);
		if (iDay!=null && iMonth!=null && iYear!=null
				&& iMonth<13 && iMonth>0 && iDay>0 && iDay<=iDaysInMonth){
			if(bConvert){
				return new Date(iYear,iMonth-1,iDay);
			}else return true;
		}else return false;
	}
	/*********************************************************************
	Function: hasDate
	Purpose:  Check that control has a date value
	Inputs:   sDate		- string date
	          sFormat	- string format
	          bConvert	- whether to return true/false or a date object
	Returns:  boolean / date object(if valid)
	*********************************************************************/
	var hasDate=function (oElement,sFormat,bConvert){
		if(!oElement)return false;
		if(!sFormat){
			if(oElement.DATEFORMAT){
				sFormat=oElement.DATEFORMAT;
			}else if(oElement.form && oElement.form.DATEFORMAT){
				sFormat=oElement.form.DATEFORMAT;
			}else{
				sFormat=enValidation.DATEFORMAT;
			}
		}
		return isDate(getValueOf(oElement),sFormat,bConvert);
	}

	/********************************************
	Function: isNotEmpty
	Purpose:  Check that parameter is not empty
	Inputs:   v - string value
	Returns:  boolean
	********************************************/
	var isNotEmpty=function (v){
		return(v?true:false);
	}
	/********************************************
	Function: hasNotEmpty
	Purpose:  Check that control has a not empty value
	Inputs:   oElement	- control
	Returns:  boolean
	********************************************/
	var hasNotEmpty=function (oElement){
		if(!oElement)return false;
		return isNotEmpty(getValueOf(oElement));
	}
	
	/********************************************
	Function: isNum
	Purpose:  Check that parameter is a number
	Inputs:   v - string value
	Returns:  boolean
	********************************************/
	var isNum=function (v){
		if(v!=0 && !v)return false;
		return (v.toString() && !/\D/.test(v));
	}
	/********************************************
	Function: hasNum
	Purpose:  Check that control has a number value
	Inputs:   oElement	- control
	Returns:  boolean
	********************************************/
	var hasNum=function (oElement){
		if(!oElement)return false;
		return isNum(getValueOf(oElement));
	}

	/*********************************************************************
	Function: isFloat
	Purpose:  Check that parameter is float
	Inputs:		sValue		- string date
				bSigned		- may be signed ?
	Returns:  boolean
	*********************************************************************/
	var isFloat=function (sValue,bSigned){
		if(!isSet(sValue))return false;
		var re=new RegExp("^"+((bSigned)?"[\\-\\+]?":"")+"(\\d*\\.?\\d+|\\d+\\.?\\d*)$");
		return re.test(sValue);
	}
	/*********************************************************************
	Function: hasFloat
	Purpose:  Check that control has a float value
	Inputs:		oElement	- control
				bSigned		- may be signed ?
	Returns:  boolean
	*********************************************************************/
	var hasFloat=function (oElement,bSigned){
		if(!oElement)return false;
		if(bSigned==null)bSigned=oElement.SIGNED;
		return isFloat(getValueOf(oElement),bSigned);
	}

	/*********************************************************************
	Function: isInteger
	Purpose:  Check that parameter is integer
	Inputs:		sValue		- string date
				bSigned		- may be signed ?
	Returns:  boolean
	*********************************************************************/
	var isInteger=function (sValue,bSigned){
		if(!isSet(sValue))return false;
		var re=new RegExp("^"+((bSigned)?"[\\-\\+]?":"")+"\\d+$");
		return re.test(sValue);
	}
	/*********************************************************************
	Function: hasInteger
	Purpose:  Check that control has a integer value
	Inputs:		oElement - control
				bSigned		- may be signed ?
	Returns:  boolean
	*********************************************************************/
	var hasInteger=function (oElement,bSigned){
		if(!oElement)return false;
		if(bSigned==null)bSigned=oElement.SIGNED;
		return isInteger(getValueOf(oElement),bSigned);
	}
	
	/*********************************************************************
	Function: isRegExp
	Purpose:  Check that parameter has a pattern for a given regexp
	Inputs:		sValue		- string date
				oRegExp		- reg expression object
	Returns:  boolean
	*********************************************************************/
	var isRegExp=function (sValue,oRegExp){
		if(!(oRegExp && oRegExp.constructor==RegExp && sValue))return false;
		return oRegExp.test(sValue);
	}
	/*********************************************************************
	Function: hasRegExp
	Purpose:  Check that control has a pattern for a given regexp
	Inputs:		oElement	- control
				oRegExp		- reg expression object
	Returns:  boolean
	*********************************************************************/
	var hasRegExp=function (oElement,oRegExp){
		if(!oElement)return false;
		if(!(oRegExp && oRegExp.constructor==RegExp))oRegExp=oElement.REGEXP;
		return isRegExp(getValueOf(oElement),oRegExp);
	}
	
	/*********************************************************************
	Function: isGivenValues
	Purpose:  Check that parameter has a given value
	Inputs:		sValue		- string date
				sVals		- list of given values
	Returns:  boolean
	*********************************************************************/
	var isGivenValues=function (sValue,sVals){
		if(!(isSet(sVals) && isSet(sValue)))return false;
		sValue=sValue.toLowerCase();
		var values=(""+sVals).toLowerCase().split(",");
		if(!values || values.length==0)return false;
		for(var i=0;i<values.length;i++)
		{
			if(sValue==values[i])return true;
		}
		return false;
	}
	
	/*********************************************************************
	Function: hasGivenValue
	Purpose:  Check that parameter has given value
	Inputs:		oElement	- control
				sVals		- list of forbidden values
	Returns:  boolean
	*********************************************************************/
	var hasGivenValue=function (oElement,sVals){
		if(!oElement)return false;
		if(!(sVals))sVals=oElement.ALLOWEDVALUES;
		return isGivenValues(getValueOf(oElement),sVals);
	}
	
	/*********************************************************************
	Function: isAllowedFileType
	Purpose:  Check that parameter has file name from a list of valid file types
	Inputs:		sValue		- string date
				sTypes		- list of allowed extenssions
	Returns:  boolean
	*********************************************************************/
	var isAllowedFileType=function (sValue,sTypes){
		if(!(sTypes && sValue))return false;
		//build reg expr
		var fileTypes=sTypes.split(",");
		if(!fileTypes || fileTypes.length==0)return false;
		var reStr="";
		for(var i=0;i<fileTypes.length;i++)
		{
			if(i>0)reStr+="|";
			reStr+="^.+\\."+fileTypes[i].trim()+"$";
		}
		return isRegExp(sValue,new RegExp(reStr,"i"));
	}
	
	/*********************************************************************
	Function: hasAllowedFileType
	Purpose:  Check that parameter does not have forbidden file name from a list of forbidden file types
	Inputs:		oElement	- control
				sTypes		- list of allowed extenssions
	Returns:  boolean
	*********************************************************************/
	var hasAllowedFileType=function (oElement,sTypes){
		if(!oElement)return false;
		if(!(sTypes))sTypes=oElement.ALLOWEDFILETYPES;
		return isAllowedFileType(getValueOf(oElement),sTypes);
	}
	
	/*********************************************************************
	Function: isForbiddenFileType
	Purpose:  Check that element does not have forbidden file name from a list of forbidden file types
	Inputs:		sValue		- string date
				sTypes		- list of forbidden extenssions
	Returns:  boolean
	*********************************************************************/
	var isForbiddenFileType=function (sValue,sTypes){
		if(!(sTypes && sValue))return false;
		return isAllowedFileType(sValue,sTypes);
	}
	
	/*********************************************************************
	Function: hasForbiddenFileType
	Purpose:  Check that control has file name from a list of valid file types
	Inputs:		oElement	- control
				sTypes		- list of forbidden extenssions
	Returns:  boolean
	*********************************************************************/
	var hasForbiddenFileType=function (oElement,sTypes){
		if(!oElement)return false;
		if(!(sTypes))sTypes=oElement.FORBIDDENFILETYPES;
		return isForbiddenFileType(getValueOf(oElement),sTypes);
	}
	
	/*********************************************************************
	Function: isEmail
	Purpose:  Check that parameter is a valid email
	Inputs:		sValue		- string 
	Returns:  boolean
	*********************************************************************/
	var isEmail=function (sValue){
		if(!sValue)return false;
		return isRegExp(sValue,/^[\w_-]+(\.[\w_-]+)*@[\w_-]+(\.[\w_-]+)*\.\w{2,4}$/);
	}
	/*********************************************************************
	Function: hasEmail
	Purpose:  Check that control has a valid email value
	Inputs:		oElement	- control
	Returns:  boolean
	*********************************************************************/
	var hasEmail=function (oElement){
		if(!oElement)return false;
		return isEmail(getValueOf(oElement));
	}
	
	/*********************************************************************
	Function: isEmails
	Purpose:  Check that parameter is a valid email (Work When the target is many email addresses)
	Inputs:		sValue		- string 
	Returns:  boolean
	*********************************************************************/
	var isEmails=function (sValue){
		if(!sValue)return false;
		var tmpValue = sValue.split(",");
		for(var i=0;i<tmpValue.length;i++) 
		{
			if(!isEmail(tmpValue[i]))
			{
			return false;
			}
		}
		return true;
	}
	/*********************************************************************
	Function: hasEmails
	Purpose:  Check that control has a valid few emails value
	Inputs:		oElement	- control
	Returns:  boolean
	*********************************************************************/
	var hasEmails=function (oElement){
		if(!oElement)return false;
		return isEmails(getValueOf(oElement));
	}

	/*********************************************************************
	Function: isPhone
	Purpose:  Check that parameter is a valid phone
	Inputs:		sValue		- string date
	Returns:  boolean
	*********************************************************************/
	var isPhone=function (sValue){
		if(!sValue)return false;
		var sPhone  = sValue.replace(/\D/g,"");
		var iDigits = sPhone.length;
		return (iDigits==10 || iDigits==11 && /^1/.test(sPhone));
	}
	/*********************************************************************
	Function: hasPhone
	Purpose:  Check that control has a valid phone value
	Inputs:		oElement	- control
	Returns:  boolean
	*********************************************************************/
	var hasPhone=function (oElement){
		if(!oElement)return false;
		return isPhone(getValueOf(oElement));
	}

	/*********************************************************************
	Function: isZip
	Purpose:  Check that parameter is a valid zip
	Inputs:		sValue		- string date
	Returns:  boolean
	*********************************************************************/
	var isZip=function (sValue){
		if(!sValue)return false;
		return isRegExp(sValue,/^\d{5}(-?\d{4})?$/);
	}
	/*********************************************************************
	Function: hasZip
	Purpose:  Check that control has a valid zip value
	Inputs:		oElement	- control
	Returns:  boolean
	*********************************************************************/
	var hasZip=function (oElement){
		if(!oElement)return false;
		return isZip(getValueOf(oElement));
	}

	/*********************************************************************
	Function: 	isSysName
	Purpose:  	Check that parameter is a valid system name
	Inputs:		sValue		- string 
	Returns:  	boolean
	*********************************************************************/
	var isSysName=function (sValue){
		if(!sValue)return false;
		return isRegExp(sValue,/^[A-Za-z][A-Za-z0-9_]*$/);
	}
	/*********************************************************************
	Function: 	hasZip
	Purpose:  	Check that control has a valid system name value
	Inputs:		oElement	- control
	Returns:  	boolean
	*********************************************************************/
	var hasSysName=function (oElement){
		if(!oElement)return false;
		return isSysName(getValueOf(oElement));
	}

	/*********************************************************************
	Function: 	fixSysName
	Purpose:  	fix name of system object
	Inputs:		name	- name to fix
	Returns:  	string
	*********************************************************************/
	//
	var fixSysName=function(name)
	{
		var o="";
		if(name.toLowerCase().indexOf("wf_")==0)name=name.substring(3);
		var len = name.length;
		if(len==0)return "";
		var idx=0;
	    var curChar;
		var curCharCode;
		//check first char - only letters
		curChar = name.charAt(idx);
		curCharCode = name.charCodeAt(idx);
		while(!((curCharCode>=65&&curCharCode<=90)||(curCharCode>=97&&curCharCode<=122)))
		{
			idx++;
			if(idx==len)return "";
			curChar = name.charAt(idx);
			curCharCode = name.charCodeAt(idx);
		}		
	    if((curCharCode>=65&&curCharCode<=90)||(curCharCode>=97&&curCharCode<=122))o+=curChar;
	
		//check chars - letters, nos or underscore
	    for(var i = idx+1 ; i < len ; i++)
	    {
	        curChar = name.charAt(i);
			curCharCode = name.charCodeAt(i);
	        if((curCharCode>=65&&curCharCode<=90)||(curCharCode>=97&&curCharCode<=122)||
	          (curCharCode>=48&&curCharCode<=57)||(curCharCode==95))o+=curChar;
	    }
		return o;
	}

	/**********************************************************
	Function: enValidation.validateControlsArray
	Purpose:  validate an array of controls
	Inputs:   form - form or form name
				controlName - name (or an array of) of controls in array
				validate - validation attributes - 2 dim array of attributes and values
	Returns:  boolean
	**********************************************************/
	var validateControlsArray=function (form,controlName,validate)
	{
		//get form ref
		if(form.constructor != Object)form=document.forms[form];

		//if an array of arrys
		if(controlName.constructor==Array)
		{
			for(var i=0;i<controlName.length;i++)
			{
				if(!validateControlsArray(form.name,controlName[i],validate))return false;
			}
		}
				
		//check 2 dim array
		if(validate.constructor != Array)return false;
		if(validate.length>0 && validate[0].constructor != Array)return false;
		//loop on all controls
		var el;
		for(var i=0;i<form.elements.length;i++)
		{
			el=form.elements[i];
			if(el.name==controlName)
			{
				//assign all validate attributes
				for(var j=0;j<validate.length;j++)
				{
					el[validate[j][0]]=validate[j][1];
				}
				//validate control
				if(!validateControl(el))return false;
			}
		}
		return true;	
	}
	
	/**********************************************************
	Function: enValidation.validateControl
	Purpose:  validate a control by it's validation attributes
	Inputs:   oElement - form field
	Returns:  boolean
	**********************************************************/
	var validateControl=function (oElement){
		if(!_validateControl(oElement))
		{
			enValidation.Err.raise();
			return false;
		}else return true;
	}
	var _validateControl=function (oElement){
	
		var multiLangMsg=function(varA,varB)
		{
			var result="";
			result=enMsg.get("enValidation.enterValBetween").replace("~1~",varA);
			result=enMsg.get("enValidation.enterValBetween").replace("~2~",varB);
			return result;
		}
		if(!oElement)return false;
		/******************** actions ******************************/
		// NOSPACE
		if (oElement.NOSPACE && oElement.value)
			oElement.value=oElement.value.replace(/\s/g,"");
		// FIXSYSNAME
		if (oElement.FIXSYSNAME && oElement.value)
			oElement.value=fixSysName(oElement.value);				
		// UPPERCASE
		if (oElement.UPPERCASE && oElement.value)
			oElement.value=oElement.value.toUpperCase();
		// LOWERCASE
		if (oElement.LOWERCASE && oElement.value)
			oElement.value=oElement.value.toLowerCase();
		/******************* end actions ****************************/

		// Trim leading and trailing spaces
		if(oElement.value)oElement.value = oElement.value.trim();
		var sValue = getValueOf(oElement);
		var bRequired=(oElement.REQUIRED);
		var bElHidden=(oElement.type=="hidden"
			|| oElement.style.display=="none" || oElement.style.visibility=="hidden");
		var bElReadonly=oElement.readOnly;
		var bElDIsabled=oElement.disabled;
		var bInaccessible=bElHidden || bElReadonly || bElDIsabled || bElHidden;
			
		//ignore any elements?
		var bIgnore=
		(
			(oElement.form && oElement.form.IGNORE_INACCESSIBLE || oElement.IGNORE_INACCESSIBLE) && bInaccessible
			|| (oElement.form && oElement.form.IGNORE_READONLY || oElement.IGNORE_READONLY) &&  bElReadonly
			|| (oElement.form && oElement.form.IGNORE_DISABLED || oElement.IGNORE_DISABLED) && bElDIsabled
			|| (oElement.form && oElement.form.IGNORE_HIDDEN || oElement.IGNORE_HIDDEN) && bElHidden
		);
		
		// REQUIRED
		if(!bIgnore && bRequired && !sValue)
		{
			enValidation.Err.add(oElement,enMsg.get("enValidation.enterValue"));
			return false;
		}
		
		// FLOAT
		var bSigned=oElement.SIGNED;
		if (!bIgnore && oElement.FLOAT && sValue){
			if(!isFloat(sValue,bSigned)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.enterNum"));
				return false;
			}
			// MIN and MAX
			var iMin = oElement.MIN;
			var iMax = oElement.MAX;
			if(iMin && !iMax){
				if(sValue<parseFloat(iMin)){
					enValidation.Err.add(oElement, enMsg.get("enValidation.enterValueGreater")+iMin);
					return false;
				}
			}else if(!iMin && iMax){
				if(sValue>parseFloat(iMax)){
					enValidation.Err.add(oElement, enMsg.get("enValidation.enterValueLess")+iMax);
					return false;
				}
			}else if(iMin && iMax){
				if(sValue<parseFloat(iMin) || sValue>parseFloat(iMax)){
					enValidation.Err.add(oElement,multiLangMsg(iMin,iMax));
					return false;
				}
			}
		}
		// AMOUNT
		if (!bIgnore && oElement.AMOUNT && sValue){
			var re = new RegExp("^"+((bSigned)?"[\\-\\+]?":"")+"(\\d*\\.?\\d{2}|\\d+\\.?(\\d{2})?)$");
			if(!re.test(sValue))
			{
				enValidation.Err.add(oElement, enMsg.get("enValidation.enterDollar"));
				return false;
			}
		}
		// INTEGER
		if (!bIgnore && oElement.INTEGER && sValue){
			if(!isInteger(sValue,bSigned)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.enterInteger"));
				return false;
			}
			// MIN and MAX
			var iMin = oElement.MIN;
			var iMax = oElement.MAX;
			if(iMin && !iMax){
				if(sValue<parseInt(iMin)){
					enValidation.Err.add(oElement, enMsg.get("enValidation.enterValueGreater")+iMin);
					return false;
				}
			}else if(!iMin && iMax){
				if(sValue>parseInt(iMax)){
					enValidation.Err.add(oElement, enMsg.get("enValidation.enterValueLess")+iMax);
					return false;
				}
			}else if(iMin && iMax){
				if(sValue<parseInt(iMin) || sValue>parseInt(iMax)){
					enValidation.Err.add(oElement, multiLangMsg(iMin,iMax));
					return false;
				}
			}
		}
		// DATE
		if (!bIgnore && oElement.DATE && sValue)
		{
			// Set default date format (from control or form)
			var sFormat=null;
			if(oElement.DATEFORMAT){
				sFormat=oElement.DATEFORMAT;
			}else if(oElement.form && oElement.form.DATEFORMAT){
				sFormat=oElement.form.DATEFORMAT;
			}else{
				sFormat=enValidation.DATEFORMAT;
			}
			if (!isDate(sValue,sFormat)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.enterDate")
											+sFormat.toUpperCase()+"'");
				return false;
			}
		}

		// REGEXP
		var oRegexp=oElement.REGEXP;
		if(!bIgnore && oRegexp && oRegexp.constructor==RegExp && sValue){
			if(!isRegExp(sValue,oRegexp)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.enterPattern"));
				return false;
			}
		}
		// PHONE
		if(!bIgnore && oElement.PHONE && sValue){
			if(!isPhone(sValue)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.enterPhone"));
				return false;
			}
		}
		// EMAIL
		if(!bIgnore && oElement.EMAIL && sValue){
			if(!isEmail(sValue)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.enterEmail"));
				return false;
			}
		}
		// EMAILS
		if(!bIgnore && oElement.EMAILS && sValue){
			if(!isEmails(sValue)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.enterEmails"));
				return false;
			}
		}
		//ALLOWEDVALUES
		if(!bIgnore && isSet(oElement.ALLOWEDVALUES) && sValue){
		
			if(!isGivenValues(sValue,oElement.ALLOWEDVALUES)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.allowedValues").replace("~1~",oElement.ALLOWEDVALUES));				
				return false;
			}
		}
		//FORBIDDENVALUES
		if(!bIgnore && isSet(oElement.FORBIDDENVALUES) && sValue){
			if(isGivenValues(sValue,oElement.FORBIDDENVALUES)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.forbiddenValues").replace("~1~",oElement.FORBIDDENVALUES));
				return false;
			}
		}
		// ALLOWEDFILETYPES
		if(!bIgnore && oElement.ALLOWEDFILETYPES && sValue){
			if(!isAllowedFileType(sValue,oElement.ALLOWEDFILETYPES)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.allowedFileTypes"));
				return false;
			}
		}
		// FORBIDDENFILETYPES
		if(!bIgnore && oElement.FORBIDDENFILETYPES && sValue){
			if(isForbiddenFileType(sValue,oElement.FORBIDDENFILETYPES)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.forbiddenFileTypes"));
				return false;
			}
		}
		// ZIP Code
		if(!bIgnore && oElement.ZIP && sValue){
			if(!isZip(sValue)){
				enValidation.Err.add(oElement, enMsg.get("enValidation.enterZip"));
				return false;
			}
		}
		// SYSTEM NAME
		if(!bIgnore && oElement.SYSNAME && sValue){
			if(!isSysName(sValue)){
				enValidation.Err.add(oElement,enMsg.get("enValidation.enterSystemName") );
				return false;
			}
		}
		// MIN and MAX no of chars (if required and has value)
		if(!bIgnore && (bRequired || sValue))
		{
			var iMin = oElement.MIN_LEN;
			var iMax = oElement.MAX_LEN;
			if(iMin && !iMax){
				if(sValue.length<parseInt(iMin)){
					var msg=enMsg.get("enValidation.enterNoOfCharsGreater")+iMin+"\n"
						+enMsg.get("enValidation.noOfChars").replace("~1~",sValue.length);
					enValidation.Err.add(oElement, msg);
					return false;
				}
			}else if(!iMin && iMax){
				if(sValue.length>parseInt(iMax)){
					var msg=enMsg.get("enValidation.enterNoOfCharsLess")+iMax+"\n"
						+enMsg.get("enValidation.noOfChars").replace("~1~",sValue.length);
					enValidation.Err.add(oElement, msg);
					return false;
				}
			}else if(iMin && iMax){
				var msg=enMsg.get("enValidation.enterNoOfCharsBetween").replace("~1~",iMin).replace("~2~",iMax)+"\n"
						+enMsg.get("enValidation.noOfChars").replace("~1~",sValue.length);
				if(sValue.length<parseInt(iMin) || sValue.length>parseInt(iMax)){
					enValidation.Err.add(oElement, msg);
					return false;
				}
			}
		}
			
		// AND
		var vAnd=oElement.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]
					: oElement.form.elements[vAnd[i].trim()];
				if(oNewElement)
				{
					var bElNewHidden=(oNewElement.type=="hidden"
						|| oNewElement.style.display=="none" || oNewElement.style.visibility=="hidden");
					var bElNewReadonly=oNewElement.readOnly;
					var bElNewDIsabled=oNewElement.disabled;
					var bElNewInaccessible=bElNewHidden || bElNewReadonly || bElNewDIsabled ;
					//ignore any elements?
					var bIgnoreNewEl=
					(
						(oNewElement.form && oNewElement.form.IGNORE_INACCESSIBLE || oNewElement.IGNORE_INACCESSIBLE) && bElNewInaccessible
						|| (oNewElement.form && oNewElement.form.IGNORE_READONLY || oNewElement.IGNORE_READONLY) &&  bElNewReadonly
						|| (oNewElement.form && oNewElement.form.IGNORE_DISABLED || oNewElement.IGNORE_DISABLED) && bElNewDIsabled
						|| (oNewElement.form && oNewElement.form.IGNORE_HIDDEN || oNewElement.IGNORE_HIDDEN) && bElNewHidden
					);
					if(!getValueOf(oNewElement) && !bIgnoreNewEl){
						enValidation.Err.add(oNewElement, enMsg.get("enValidation.enterValue"));
						return false;
					}
				}
			}
		}
		// OR
		var vOr=typeof oElement.OR=="object";
		if(vOr && !sValue){
			vOr = oElement.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]
						: oElement.form.elements[vOr[i].trim()];
					if(oNewElement)
						bValue |= !!getValueOf(oNewElement);
				}
				if(!bValue){
					enValidation.Err.add(oElement, oElement.OR["msg"]?oElement.OR["msg"]:enMsg.get("enValidation.enterValue"));
					return false;
				}
			}
		}

		// Perform onvalidate event handler
		if(typeof oElement.onvalidate=="function" && oElement.onvalidate()==false)return false;
		return true;
	}
	
	/**********************************************************
	Function: enValidation.validateForm
	Purpose:  validate a form and all it's controls
	Inputs:   oForm - form
			  bIgnore - ignore validation. allows to bypass validation
	Returns:  boolean
	**********************************************************/
	var validateForm=function (oForm,bIgnore)
	{
		if(bIgnore)return true;
		// Execute onbeforevalidate processing
		if(typeof oForm.onbeforevalidate=="function" && oForm.onbeforevalidate()==false)
			return false;
		var i,oElement,iElements=oForm.elements.length;
		// Validate individual elements
		for(i=0;i<iElements;i++){
			oElement=oForm.elements[i];

			// Perform default validation for element
			if(!_validateControl(oElement)){
				enValidation.Err.raise();
				return false;
			}
		}
		// Execute onaftervalidate processing
		if(typeof oForm.onaftervalidate=="function" && oForm.onaftervalidate()==false)
			return false;

		// Perform original onsubmit event handler
		if (typeof oForm.fnSubmit=="function" && oForm.fnSubmit()==false)return false;
		return true;
	}
	
	/*********************************************************************
	Method:   setup
	Purpose:  Set up methods and event handlers for all forms
	Inputs:   oForm - optional form pointer
	Returns:  undefined
	*********************************************************************/
	var setup=function (oForm){
		if(!oForm){
			// Fan through forms on page to perform initializations
			var i,iForms=document.forms.length;
			for(i=0; i<iForms; i++){
				oForm=document.forms[i];
				_setupForm(oForm);
			}
		}else{
			_setupForm(oForm);
		}
	}
	var _setupForm=function (oForm){
		if(!oForm.bProcessed){
			// Capture and replace onsubmit event handler
			oForm.fnSubmit=oForm.onsubmit;

			// Create new event handlers
			oForm.onsubmit=function (){
				return validateForm(this);
			}
			oForm.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,"");
		}
		
		//more prototype extentions
		//=========================		
		this.isDate=isDate;
		this.hasDate=hasDate;
		this.isNotEmpty=isNotEmpty;
		this.hasNotEmpty=hasNotEmpty;
		this.isNum=isNum;
		this.hasNum=hasNum;
		this.isFloat=isFloat;
		this.hasFloat=hasFloat;
		this.isInteger=isInteger;
		this.hasInteger=hasInteger;
		this.isRegExp=isRegExp;
		this.hasRegExp=hasRegExp;
		this.isGivenValues=isGivenValues;
		this.hasGivenValue=hasGivenValue;
		this.isAllowedFileType=isAllowedFileType;
		this.hasAllowedFileType=hasAllowedFileType;
		this.isForbiddenFileType=isForbiddenFileType;
		this.hasForbiddenFileType=hasAllowedFileType;
				
		this.isEmail=isEmail;
		this.hasEmail=hasEmail;
		this.isEmails=isEmails;
		this.hasEmails=hasEmails;		
		this.isPhone=isPhone;
		this.hasPhone=hasPhone;
		this.isZip=isZip;
		this.hasZip=hasZip;
		this.isSysName=isSysName;
		this.hasSysName=hasSysName;
		this.fixSysName=fixSysName;
		
		this.getValueOf=getValueOf;
		this.validateControl=validateControl;
		this.validateControlsArray=validateControlsArray;
		this.validateForm=validateForm;
		this.setupForms=setup;
		
		// Expose error object to outside script
		this.Err=new Err;
		
		//object level date format
		this.DATEFORMAT="MM/DD/YYYY";
	}
}

//instanciate object
if(!window.enValidation)window.enValidation=new enValidationConstructor();

