	/************************************************************************\
	|* System.Xml.Forms.Extended.js											*|
	|* By Cory Dambach 														*|
	|* Classes Built upon System.Xml.Forms.js								*|
	|* Reference Material: HTML 4.01 W3C Recommendation 24-December-1999	*|
	\************************************************************************/
	
	function FormWatcher( formObj, OnChangeHandler ) //catches all clicks, keyups, changes, for every type of form element
	{
		this.FormObj	= formObj;		//HTMLFormElement object
		var frmInputs	= this.FormObj.elements;	//such a long reference
		
		this.SetOnChange = function( OnChangeHandler )
		{
			for( var i = 0; i < this.FormObj.elements.length; i++ )
			{
				switch( frmInputs[i].nodeName )
				{
					case Form.ControlType.Input:			
						switch( frmInputs[i].type )
						{
							case Form.InputType.Text:	
							case Form.InputType.Password:
								frmInputs[i].onkeyup	= OnChangeHandler;
								frmInputs[i].onchange	= OnChangeHandler;
								break;
							case Form.InputType.Submit:
							case Form.InputType.Image:
							case Form.InputType.Reset:
							case Form.InputType.Button:
								frmInputs[i].onclick = OnChangeHandler;
								break
							case Form.InputType.Radio:
							case Form.InputType.CheckBox:
								frmInputs[i].onclick = OnChangeHandler;
								break;
						}
						break
					case Form.ControlType.Select:
						frmInputs[i].onkeyup	= OnChangeHandler;
						frmInputs[i].onchange	= OnChangeHandler;
						break;
						
					case Form.ControlType.TextArea:
						frmInputs[i].onkeyup	= OnChangeHandler;
						frmInputs[i].onchange	= OnChangeHandler;
						break;
				}
			}
		}
		if( OnChangeHandler != null )
		{
			this.SetOnChange( OnChangeHandler );
		}
	}
	
	//case "int": case "double": case "single": case "float": case "long": case "decimal":
	//case "date": case "datetime": case "smalldatetime":
	//Constraints shall henceforth be defined to check one thing about one piece of data, therefore if you wish to check type and length, well, your boned for now!
	function Constraint() //abstract object constructor, includes some fallbacks
	{
		this.Evaluate = function( Value ) { return true; }
		this.ErrorMsg = "does not meet the contraints applied to it."; //user should never see this error message
	}
	
	ConstraintType = {
		DataType	: 0,
		Function	: 1,
		Regex		: 2
	}
	
	function RegexConstraint( RegexObj )
	{
		Constraint.call( this ); //inherits from Constraint
		this.__RegexObj	= RegexObj;
		this.ErrorMsg = "does not match the Regex filter of " + this.__RegexObj.toString();
		this.Evaluate = function( Value )
		{
			return ( Value.match( this.__RegexObj ) != null );
		}
	}
	
	function FunctionConstraint( BooleanFunction )
	{
		Constraint.call( this );
		this.__FunctionObj = BooleanFunction;
		this.Evaluate = function( Value )
		{
			return BooleanFunction( Value );
		}
	}

	function FormValidator( FormObj ) //constraint reader takes a formObj and produces a ConstraintCollection(an array of constraints)
	{
		Form.call( this, FormObj ); //this constructor extends the form class
		//initializes this.FormObj, among many other things
		
		//Specialized Member Initialization */
		this.__BaseValidate = this.Validate; //preserve it for the override, this way GC won't collect our little monster
		this.__Constraints	= new NameValueCollection(); //this array holds custom constraints not defined with attributes like dlength, dtype etc...
		this.Validate = function( OnInputFail, OnInputPass )
		{
			this.__BaseValidate( OnInputFail, OnInputPass ); //call the original Validate		
			if( OnInputFail == null )
				OnInputFail = Form.ValidateExceptionHandler;							  
			if( OnInputPass == null )
				OnInputPass = Form.ValidateSuccessHandler;
			
			var FormNVC		= this.ToNVC();
			var CheckCol	= this.__Constraints;
			var Errors		= 0; //number of invalid inputs
			for( var i = 0; i < CheckCol.Keys.length; i++ ) //this block checks a single constraint on a single input
			{
				var InputObj		= CheckCol.Get( CheckCol.Keys[i] );
				var ConstraintObj	= CheckCol.Keys[i];
				if( InputObj.Value != "" && !ConstraintObj.Evaluate( InputObj.value ) ) //The moment of utmost importance!				
				{
					Errors++;
					OnInputFail( InputObj, null, ConstraintObj.ErrorMsg );
				}
			}
			if( Errors > 0 )
				return false;
			return true;
		}

		//input to be constrained, constraint
		this.AddConstraint = function( InputObj, ConstraintObj )
		{
			this.__Constraints.Add( ConstraintObj, InputObj );	
		}		
	}
	
	//Unless otherwise noted, all regular expressions were created, tested and debugged by Cory Dambach
	var Constraints = new Object();
	//Constraints.Email			= new RegexConstraint( /^[\w]+@[\w]+\.[\w]+$/ );
	
	//added by JNickell 4/16/08
	Constraints.Name            = new RegexConstraint( /^[A-Za-z]{2,}/);
	Constraints.Name.ErrorMsg   = "is not a valid name.";
	//end addition
	
	Constraints.Email			= new RegexConstraint( /\b[A-Z0-9._%-]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b/i );
	Constraints.Email.ErrorMsg	= "is not a valid email address.";
		
	Constraints.Money			= new RegexConstraint( /^\$\d+\.\d{0,2}$|^\$\d+$|^\d+\.\d{0,2}$|^\d+$/ );
	Constraints.Money.ErrorMsg	= "is not a valid value for physical money, please, use no more than two decimal places.";	

	Constraints.Url				= new RegexConstraint( /^http:\/\/(\w+\.)+\w+$/ );
	Constraints.Url.ErrorMsg	= "is not a valid Http(RFC 2616) Web Address. e.g., http://example.com"; 
	
	//RegexBuddy's Library - Modified by Cory
	Constraints.IpAddress			= new RegexConstraint( /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ );
	Constraints.IpAddress.ErrorMsg	= "is not a valid IP Address. e.g., 64.233.187.99";
	
	//RegexBuddy's Library
	Constraints.PhoneNumber				= new RegexConstraint( /^\(?[0-9]{3}\)?[-. ]?[0-9]{3}[-. ]?[0-9]{4}$/ );
	Constraints.PhoneNumber.ErrorMsg	= "is not a valid North American phone number. e.g., 555-555-5555";
	
	//Added by JNickell 04/17/2008 since we are using individual boxes to for the different parts of the phone number
	Constraints.AreaCode           = new RegexConstraint(/^[0-9]{3}/);
	Constraints.AreaCode.ErrorMsg  = "is not a valid Area Code.(Phone Box 1)";
	
	Constraints.Prefix             = new RegexConstraint(/^[0-9]{3}/);
	Constraints.Prefix.ErrorMsg    = "is not a valid Phone Prefix.(Phone Box 2)";
	
	Constraints.LineNumber           = new RegexConstraint(/^[0-9]{4}/);
	Constraints.LineNumber.ErrorMsg  = "is not a valid Line Number.(Phone Box 3)";
	//end addition
	
	Constraints.Zip				= new RegexConstraint( /^[0-9]{5}(?:-[0-9]{4})?$/ );
	Constraints.Zip.ErrorMsg	= "is not a valid Zip Code. e.g., 1234-121345 or 12345";