// Date Version: 2009-06-02

// define the namespace if necessary
if ( Lutz == undefined )
	var Lutz = { };

Lutz.Validation = { 
	// classes in this namespace
	FormValidator:     function(params) { this.initialize(params); },
	BaseValidator:     function(params) { this.initialize(params); },
	ValidatorFactory:  function()       { this.initialize(); },
	BaseVisualizer:    function(params) { this.initialize(params); },
	VisualizerFactory: function()       { this.initialize(); },
 	
	// enumerations in this namespace
	ValidatorType: {
		Required : 1,
		Length   : 2,
		Number   : 3,
		Date     : 4,
		Compare  : 5,
		Format   : 4,
		RegEx    : 6,
		Radio    : 7,
		Custom   : 8
	},
	
	Format: {
		Email : 1,
		Time  : 2
	},
	
	VisualizerType: {
		ErrorMarker : 1,
		Label       : 2,
		Summary     : 3
	}
};

Lutz.Validation.BaseValidator.prototype = {
	// public properties
	IsValid           : true,
	Key               : null,
	ErrorMessage      : "",
	ControlToValidate : null,
	ValidatorType     : null,
	Enabled           : true,
	
	// private properties
	_parameters       : null,
	
	// constructor
	initialize: function(params) {
		this.IsValid = true;
		this.ErrorMessage = "";
		this.ControlToValidate = null;
		
		// register custom events
		this.AfterFieldValidate = new YAHOO.util.CustomEvent("AfterFieldValidate", this);
		
		this._parameters = params;
		
		if ( this._parameters.Key != undefined )
			this.Key = this._parameters.Key;
		
		if ( this._parameters.ErrorMessage != undefined )
			this.ErrorMessage = this._parameters.ErrorMessage;
			
		if ( this._parameters.ControlToValidate != undefined )
			this.ControlToValidate = this._parameters.ControlToValidate;
	},
	
	// methods shared by all validators
	validate: function() {
		this.IsValid = true;
		this.AfterFieldValidate.fire();
	},
	
	hookupControlEvents: function() {
		var field = Lutz.DomUtil.getControl(this.ControlToValidate);
		if ( field == null ) 
			return;

		if ( (field.tagName == 'INPUT' && (field.type == 'text' || field.type == 'password' )) 
		   || field.tagName == 'TEXTAREA' )
		{
			YAHOO.util.Event.addListener(field, "change", this.onControlValueChange, this, true);
			YAHOO.util.Event.addListener(field, "keyup", this.onControlValueChange, this, true);
		}
		else if ( field.tagName == 'SELECT' 
		   || (field.tagName == 'INPUT' && field.type == 'checkbox') 
		   || (field.tagName == 'INPUT' && field.type == 'file') )
		{
			YAHOO.util.Event.addListener(field, "change", this.onControlValueChange, this, true);
		}
		else if ( field.tagName == 'INPUT' && field.type == 'radio' && field.name != null )
		{
			var controls = Lutz.DomUtil.findRadioButtonsByName(field.form, field.name);
			for ( var  i = 0; i < controls.length; i++ )
			{
				YAHOO.util.Event.addListener(controls[i], "change", this.onControlValueChange, this, true);
			}
		}
	},
	
	unhookControlEvents: function() {
		var field = Lutz.DomUtil.getControl(this.ControlToValidate);
		if ( field == null ) 
			return;

		if ( (field.tagName == 'INPUT' && (field.type == 'text' || field.type == 'password' )) 
		   || field.tagName == 'TEXTAREA' )
		{
			YAHOO.util.Event.removeListener(field, "change", this.onControlValueChange);
			YAHOO.util.Event.removeListener(field, "keyup", this.onControlValueChange);
		}
		else if ( field.tagName == 'SELECT' 
		   || (field.tagName == 'INPUT' && field.type == 'checkbox') 
		   || (field.tagName == 'INPUT' && field.type == 'file') )
		{
			YAHOO.util.Event.removeListener(field, "change", this.onControlValueChange);
		}
		else if ( field.tagName == 'INPUT' && field.type == 'radio' && field.name != null )
		{
			var controls = Lutz.DomUtil.findRadioButtonsByName(field.form, field.name);
			for ( var  i = 0; i < controls.length; i++ )
			{
				YAHOO.util.Event.removeListener(controls[i], "change", this.onControlValueChange);
			}
		}
	},
	
	setValid: function(isValid, reason) {
		this.IsValid = isValid;
		this.AfterFieldValidate.fire();
		return isValid;
	},
	
	onControlValueChange: function(e) {
		if ( this.Enabled )
			this.validate();
	}

};


Lutz.Validation.BaseVisualizer.prototype = {
	// public properties
	Key            : null,
	VisualizerType : null,
	
	// private properties
	_parameters    : null,
	
	// constructor
	initialize: function(params) {
		this._parameters = params;
		this._validatorKeys = [];
		this._subscribedValidators = [];
		
		if ( this._parameters.Key != null )
			this.Key = this._parameters.Key;
	},
	
	registerFormValidator: function(formValidator) {
	},
	
	unregisterFormValidator: function(formValidator) {
	},
	
	registerFieldValidator: function(fieldValidator) {
		if ( fieldValidator == null || fieldValidator.Key == null )
			return;
			
		if ( this._parameters == null || this._parameters.ValidatorKeys == null )
			return;
		
		// if there are any validators that are waiting to be subscribed, do it now.
		for ( var i = 0; i < this._parameters.ValidatorKeys.length; i++ )
		{
			if ( fieldValidator.Key == this._parameters.ValidatorKeys[i] )
			{
				fieldValidator.AfterFieldValidate.subscribe( this.onAfterFieldValidate, this );
				this._subscribedValidators.push( fieldValidator );
			}
		}
	},
	
	unregisterFieldValidator: function(fieldValidator) {
		if ( fieldValidator == null || fieldValidator.Key == null )
			return;
			
		if ( this._parameters == null || this._parameters.ValidatorKeys == null )
			return;

		// if the validator that is being unregisterd has been subscribed to this visualizer, 
		// unsubscribe it now.
		for ( var i = 0; i < this._subscribedValidators.length; i++ )
		{
			if ( fieldValidator.Key == this._subscribedValidators[i].Key )
			{
				this._subscribedValidators[i].unsubscribe( this.onAfterFieldValidate, this );
				this._subscribedValidators.slice(i, 1);
			}
		}
	},

	areRegisteredValidatorsValid: function() {
		for ( var i = 0; i < this._subscribedValidators.length; i++ )
		{
			if ( !this._subscribedValidators[i].IsValid )
				return false;
		}
		return true;
	},	
	
	onAfterFieldValidate: function(type, args, me) {
	}
	
}

Lutz.Validation.ValidatorFactory.prototype = {

	// constructor
	initialize: function() { 
	},
	
	CreateValidator: function(params) {
		switch ( params.Type ) 
		{
			case Lutz.Validation.ValidatorType.Required:
				return this.CreateRequiredValidator(params);
				
			case Lutz.Validation.ValidatorType.Length:
				return this.CreateLengthValidator(params);
			
			case Lutz.Validation.ValidatorType.Number:
				return this.CreateNumberValidator(params);

			case Lutz.Validation.ValidatorType.Date:
				return this.CreateDateValidator(params);

			case Lutz.Validation.ValidatorType.Compare:
				return this.CreateCompareValidator(params);

			case Lutz.Validation.ValidatorType.Format:
				return this.CreateFormatValidator(params);

			case Lutz.Validation.ValidatorType.RegEx:
				return this.CreateRegexValidator(params);

			case Lutz.Validation.ValidatorType.Radio:
				return this.CreateRadioValidator(params);

			case Lutz.Validation.ValidatorType.Custom:
				return this.CreateCustomValidator(params);
		}
		YAHOO.log("FormValidator.addValidator(): Request to add unknown validator type");
		return null;
	},
	
	// methods
	CreateRequiredValidator: function(params) {
		var obj = new Lutz.Validation.BaseValidator(params);
		obj.ValidatorType = Lutz.Validation.ValidatorType.Required;
		if ( params.EmptyState == null )
		{
			obj.EmptyState = new Array();
			obj.EmptyState.push('');
			obj.EmptyState.push(null);
		}
		else
			obj.EmptyState = params.EmptyState;
		
		obj.validate = function() {
			this.IsValid = true;
		
			if ( this.ControlToValidate == null )
				return;

			var field = document.getElementById(this.ControlToValidate);
			if ( field == null )
				return;
			
			var value = Lutz.DomUtil.getFormFieldValue(field);
			
			if ( !Lutz.DomUtil.isArray(this.EmptyState) )
			{
				if ( value == this.EmptyState )
					this.IsValid = false;
			}
			else
			{
				for ( var i = 0; i < this.EmptyState.length; i++ )
				{
					if ( value == this.EmptyState[i] )
					{
						this.IsValid = false;
						break;
					}
				}
			}
			
			//if ( value == '' ||  value == null )
			//	this.IsValid = false;
			
			this.AfterFieldValidate.fire();
		}
		
		return obj;
	},
	
	CreateLengthValidator: function(params) {
		var obj = new Lutz.Validation.BaseValidator(params);
		obj.ValidatorType = Lutz.Validation.ValidatorType.Length;
		obj.Maximum = this.getValueOrDefault( params.Maximum, null );
		obj.Minimum = this.getValueOrDefault( params.Minimum, 0 );
		
		obj.validate = function() {
			this.IsValid = true;
			
			if ( this.ControlToValidate == null )
				return;
			
			var field = Lutz.DomUtil.getControl(this.ControlToValidate);
			if ( field == null )
				return;
				
			var value = Lutz.DomUtil.getFormFieldValue(field);
			if ( value == '' || value == null )
				return;
				
			if ( this.Maximum != null && value.length > this.Maximum )
				this.IsValid = false;
				
			if ( this.Minimum != null && value.length < this.Minimum )
				this.IsValid = false;
			
			this.AfterFieldValidate.fire();
		}
	
		return obj;
	},

	CreateNumberValidator: function(params) {
		var obj = new Lutz.Validation.BaseValidator(params);
		obj.ValidatorType = Lutz.Validation.ValidatorType.Number;
		obj.ValidPrefixChars = this.getValueOrDefault( params.ValidPrefixChars, null );
		obj.ValidSuffixChars = this.getValueOrDefault( params.ValidSuffixChar, null );
		obj.DecimalPlaces = this.getValueOrDefault( params.DecimalPlaces, null );
		obj.AllowComma = this.getValueOrDefault( params.AllowComma, true );
		obj.AllowNegative = this.getValueOrDefault( params.AllowNegative, true );
		obj.Minimum = this.getValueOrDefault( params.Minimum, null );
		obj.Maximum = this.getValueOrDefault( params.Maximum, null );
		
		obj.checkValidChars = function(value, validChars) {
			for ( var i = 0; i < value.length; i++ )
			{
				var foundValid = false;
				for ( var j = 0; j < validChars.length; j++ )
				{
					if ( value.charAt(i) == validChars.charAt(j) )
					{
						foundValid = true;
						break; // exit the inner for loop
					}
				}
				
				if ( !foundValid )
					return false;
			}
			return true;
		}
		
		obj.isNumeric = function(value, validChars) {
			if ( typeof validChars == undefined )
				validChars = "1234567890";
			return this.checkValidChars(value, validChars);
		}

		obj.validate = function() {
			this.IsValid = true;
			
			if ( this.ControlToValidate == null )
				return this.setValid(true);
			
			var field = Lutz.DomUtil.getControl(this.ControlToValidate);
			if ( field == null )
				return this.setValid(true);
				
			var value = Lutz.DomUtil.getFormFieldValue(field);
			if ( value == '' || value == null )
				return this.setValid(true);
				
			var allowDecimals = true;
			if ( this.DecimalPlaces == 0 )
				allowDecimals = false;
				
			var validChars = "1234567890";
			if ( this.AllowComma )
				validChars += ",";
			if ( this.AllowNegative )
				validChars += "-";
			if ( allowDecimals )
				validChars += ".";
			
			// find the first & last numeric characters, and count the decimal places
			var firstNumber = value.length+1;
			var lastNumber = -1;
			var decimalCount = 0;
			for (var i = 0; i < value.length; i++ )
			{
				if ( this.isNumeric( value.charAt(i), validChars ) )
				{
					if ( i < firstNumber ) 
						firstNumber = i;
					if ( i > lastNumber )
						lastNumber = i;
				}
				
				if ( value.charAt(i) == '.' )
					decimalCount++;
			}
			
			// if we didn't find a number, we already know the value is invalid
			if ( firstNumber > value.length )
				return this.setValid(false, "No number found in the string.");
			
			// if the there is more than one decimal point, or if there is one but decimals aren't allowed, 
			// we already know the value is invalid
			if ( decimalCount > 1 || ( !allowDecimals && decimalCount == 1) )
				return this.setValid(false, "Too many decimal points found.");
			
			// get the prefix, suffix, and numeric portion of the string
			var prefix = value.substring(0, firstNumber);
			var number = value.substring(firstNumber, lastNumber+1);
			var suffix = value.substring(lastNumber+1);
			
			if ( this.ValidPrefixChars == null && prefix.length > 0 )
				return this.setValid(false, "Found a prefix, but there are no valid characters for a prefix.");

			if ( this.ValidSuffixChars == null && suffix.length > 0 )
				return this.setValid(false, "Found a suffix, but there are no valid characters for a suffix.");
				
			// make sure the prefix and suffix don't contain characters that are invalid
			if ( !this.checkValidChars(prefix, this.ValidPrefixChars) )
				return this.setValid(false, "Invalid characters in the prefix.");
			
			if ( !this.checkValidChars(suffix, this.ValidSuffixChars) )
				return this.setValid(false, "Invalid characters in the suffix.");
				
			// if negative numbers are allowed, check to see if the first character is a minus sign.
			// if it is, remember that for later and strip it off.
			var isNegative = false;
			if ( this.AllowNegative && number.charAt(0) == '-' )
			{
				number = number.substring(1);
				isNegative = true;
			}
			
			// make sure all characters in the number portion are numeric
			number = number.replace(",", "");
			validChars = "1234567890";
			if ( allowDecimals )
				validChars += ".";
			if ( !this.checkValidChars(number, validChars) )
				return this.setValid(false, "Invalid characters in number portion of string.");
			
			if ( isNegative )
				number = "-" + number.toString();
			
			// make sure the decimal places don't exceed the number of decimal places allowed by the configuration
			if ( allowDecimals )
			{
				var decimalLocation = number.indexOf('.');
				if ( decimalLocation != -1 )
				{
					var decimals = number.substring(decimalLocation+1);
					if ( decimals.length == 0 )
						number = number.substring(numbers, numbers.length-2);
					else
					{
						if ( this.DecimalPlaces != null && decimals.length > this.DecimalPlaces )
							return this.setValid(false, "Too many decimals found.");
					}
				}
			}
			
			var decNumber = parseFloat(number);
			
			if ( this.Maximum != null && decNumber > this.Maximum )
				return this.setValid(false, "Number is greater than the maximum allowable value.");
				
			if ( this.Minimum != null && decNumber < this.Minimum )
				return this.setValid(false, "Number is less than the minimum allowable value.");
			
			return this.setValid(true, "The number is valid.");
			
		}

		return obj;
	},

	CreateDateValidator: function(params) {
		var obj = new Lutz.Validation.BaseValidator(params);
		obj.ValidatorType = Lutz.Validation.ValidatorType.Date;
		obj.Format = this.getValueOrDefault( params.Fomrat, "mm/dd/yyyy" );
		obj.Separator = this.getValueOrDefault( params.Separator, "/" );
		obj.Minimum = this.getValueOrDefault( params.Minimum, null );
		obj.Maximum = this.getValueOrDefault( params.Maximum, null );
		
		return null;
	},

	CreateCompareValidator: function(params) {
		YAHOO.log("FormValidator.addValidator(): Request to add Compare validator");
		return null;
	},

	CreateFormatValidator: function(params) {
		YAHOO.log("FormValidator.addValidator(): Request to add Format validator");
		return null;
	},

	CreateRegexValidator: function(params) {
		YAHOO.log("FormValidator.addValidator(): Request to add RegEx validator");
		return null;
	},
	
	CreateRadioValidator: function(params) {
		var obj = new Lutz.Validation.BaseValidator(params);
		obj.ValidatorType = Lutz.Validation.ValidatorType.Radio;
		obj.ControlsToValidate = params.ControlsToValidate;
		
		obj.validate = function() {
			//alert("CreateRadioValidator.validate()");
			if ( !Lutz.DomUtil.isArray(this.ControlsToValidate) || this.ControlsToValidate.length == 0 )
				return;
			
			for (var i = 0; i < this.ControlsToValidate.length; i++ )
			{
				var chk = Lutz.DomUtil.getControl( this.ControlsToValidate[i] );
				if ( chk == null || chk.tagName != 'INPUT' || chk.type != 'radio' )
					continue;
					
				if ( chk.checked )
				{
					this.setValid(true, "A radiobutton was checked.");
					return;
				}
			}
			
			this.setValid(false, "No radiobutton was checked.");
		}
		
		return obj;
	},

	CreateCustomValidator: function(params) {
		var obj = new Lutz.Validation.BaseValidator(params);
		obj.validate = this.getValueOrDefault( params.ValidationFunction, null );
		return obj;
	},
	
	getValueOrDefault: function(value, defaultvalue) {
		if ( value != null )
			return value;
		return defaultvalue;
	}

}

Lutz.Validation.VisualizerFactory.prototype = {
	
	// constructor
	initialize: function() {
	},
	
	// methods
	CreateVisualizer: function(params) {
		if ( params == null || params.Type == null )
			return null;
			
		switch ( params.Type )
		{
			case Lutz.Validation.VisualizerType.ErrorMarker:
				var test = this.CreateErrorMarkerVisualizer( params );
				return test;
			case Lutz.Validation.VisualizerType.Label:
				return this.CreateLabelVisualizer( params );
			case Lutz.Validation.VisualizerType.Summary:
				return this.CreateSummaryVisualizer( params );
		}
		
		YAHOO.log("VisualizerFactory.CreateVisualizer(): Unknown visualizer type");
		return null;
	},
	
	CreateCssSwapVisualizer: function(params) {
		if ( params.Control == null )
			return null;
		
		var control = Lutz.DomUtil.getControl(params.Control);
		if ( control == null )
			return null;
			
		// Inherit from BaseVisualizer. 
		// Then we'll override the properties specific to the Summary control.
		var obj = new Lutz.Validation.BaseVisualizer( params );
		
		// construct the object's public properties
		obj.CssClassValid   = null;
		if ( params.CssClassValid != undefined )
			obj.CssClassValid = params.CssClassValid;
		
		obj.CssClassInvalid = null;
		if ( params.CssClassInvalid != undefined )
			obj.CssClassInvalid = params.CssClassInvalid;
		
		// construct the object's private properties
		obj._control = control;
		
		// methods
		obj.refreshDisplay = function() {
			var isValid = true;
			for ( var i = 0; i < this._validators.length; i++ )
			{
				if ( !this._validators[i].IsValid )
					isValid = false;
			}
			if ( isValid )
				this.setValid();
			else
				this.setInvalid();
		}
		
		obj.setValid = function() {
			if ( this.CssClassInvalid != null )
				YAHOO.util.Dom.removeClass(this._control, this.CssClassInvalid);	
			if ( this.CssClassValid != null )
				YAHOO.util.Dom.addClass(this._control, this.CssClassValid);	
		}
		
		obj.setInvalid = function() {
			if ( this.CssClassValid != null )
				YAHOO.util.Dom.removeClass(this._control, this.CssClassValid);	
			if ( this.CssClassInvalid != null )
				YAHOO.util.Dom.addClass(this._control, this.CssClassInvalid);	
		}
		
		obj.onAfterFieldValidate = function(type, args, me) {
			if ( me.areRegisteredValidatorsValid() )
				me.setValid();
			else
				me.setInvalid();
		}
		
		return obj;	
	},
	
	CreateErrorMarkerVisualizer: function(params) {
		var obj = this.CreateCssSwapVisualizer( params );
		if ( obj == null )
			return null;
			
		obj.VisualizerType = Lutz.Validation.VisualizerType.ErrorMarker;
		
		if ( params.CssClassValid == undefined )
			obj.CssClassValid = "hidden";
		if ( params.CssClassInvalid == undefined )
			obj.CssClassInvalid = null;
		
		return obj;
	},
	
	CreateLabelVisualizer: function(params) {
		var obj = this.CreateCssSwapVisualizer( params );
		if ( obj == null )
			return null;
			
		obj.VisualizerType = Lutz.Validation.VisualizerType.Label;
		
		if ( params.CssClassValid == undefined )
			obj.CssClassValid = null;
		if ( params.CssClassInvalid == undefined )
			obj.CssClassInvalid = "labelError";
		
		return obj;
	},
	
	CreateSummaryVisualizer: function(params) {
		if ( params.Control == null )
			return null;
		
		var control = Lutz.DomUtil.getControl(params.Control);
		if ( control == null )
			return null;

		// Inherit from BaseVisualizer. 
		// Then we'll override the properties specific to the Summary control.
		var obj = new Lutz.Validation.BaseVisualizer( params );
		
		// construct the object's public properties
		obj.VisualizerType = Lutz.Validation.VisualizerType.Summary;
		obj.HeaderText = "Please correct the following errors:";
	
		// construct the object's private properties
		obj._control = control;
		obj._errorMessages = [];
		
		obj.registerFormValidator = function(formValidator) {
			if ( formValidator == null || formValidator.AfterFormValidate == null )
				return;
			formValidator.AfterFormValidate.subscribe(this.onAfterFormValidate, this);
		}
	
		obj.unregisterFormValidator = function(formValidator) {
			if ( formValidator == null || formValidator.AfterFormValidate == null )
				return;
			formValidator.AfterFormValidate.unsubscribe(this.onAfterFormValidate, this);
		}
	
		obj.onAfterFormValidate = function(type, args, me) {
			me.setErrorMessages( args[0] );
			me.render();
		}
	
		obj.setErrorMessages = function(errorMessages) {
			if ( !Lutz.DomUtil.isArray(errorMessages) )
			{
				// log a warning
				return;
			}
			this._errorMessages = errorMessages;
		}
	
		obj.render = function() {
			if ( this._control == null )
				return;
			
			while ( this._control.firstChild ) 
				this._control.removeChild( this._control.firstChild );
				
			if ( this._errorMessages.length == 0 )
				return;
				
			if ( this.HeaderText != null )
			{
				var para = document.createElement("p");
				para.appendChild( document.createTextNode(this.HeaderText) );
				this._control.appendChild(para);
			}

			var ul = document.createElement("ul");
			for ( var i = 0; i < this._errorMessages.length; i++ )
			{
				var li = document.createElement("li");
				li.appendChild( document.createTextNode( this._errorMessages[i] ) );
				ul.appendChild(li);
			}
			
			this._control.appendChild( ul );
		}
		
		return obj;
	}
	
}

Lutz.Validation.FormValidator.prototype = {
	// public properties
	IsValid       : true,
	HasSubmitted  : false,
	
	// private properties
	_form         : null,
	_enabled      : false,
	
	// constructor
	initialize: function(params) {
		this._validators = [];
		this._errorMessages = [];
		this._visualizers = [];
		this._parameters = params;
		this._form = Lutz.DomUtil.getControl(params.FormId);
		this.AfterFormValidate = new YAHOO.util.CustomEvent("AfterFormValidate", this);
		this.IsValid = true;
		
		// hook up the form's submit buttons
		if ( this._form != null )
		{
			var buttons = Lutz.DomUtil.findSubmitButtons(this._form);
			for ( var i = 0; i < buttons.length; i++ )
				YAHOO.util.Event.addListener(buttons[i], "click", this.validateOnSubmit, this, true);
		}
		
		// register the validators
		if ( params.Validators != null )
		{
			for ( var i = 0; i < params.Validators.length; i++ )
				this.addValidator( params.Validators[i] );
		}
		
		// register the visualizers
		if ( params.Visualizers != null )
		{
			var visualizerFactory = new Lutz.Validation.VisualizerFactory();

			for ( var i = 0; i < params.Visualizers.length; i++ )
			{
				var visualizer = visualizerFactory.CreateVisualizer( params.Visualizers[i] );
				if ( visualizer != null )
					this.addVisualizer( visualizer );
			}
		}
	},
	
	addValidator: function( parameters ) {
		if ( parameters == null || parameters.Type == undefined )
			return;
	
		var factory = new Lutz.Validation.ValidatorFactory();
		var validator = factory.CreateValidator( parameters );

		// if the parameters weren't valid for some reason, take an early exit
		if ( validator == null )
			return;
		
		validator.Enabled = this._enabled;
		
		// notify all visualizers attached this this FormValidator that there a new validator has been attached
		for ( var i = 0; i < this._visualizers.length; i++ )
			this._visualizers[i].registerFieldValidator( validator );
			
		// hook up the events that cause the validation to happen dynamically
		validator.hookupControlEvents();
		
		this._validators.push(validator);
	},
	
	addVisualizer: function( control ) {
		if ( control == null ) 
			return;

		// notify the visualizer that it is being attached to this FormValidator
		control.registerFormValidator( this );
		
		// notify the visualizer of all the validators in this FormValidator
		for ( var i = 0; i < this._validators.length; i++ )
			control.registerFieldValidator( this._validators[i] );

		this._visualizers.push(control);
	},

	validate: function() {
		this.HasSubmitted = true;
		this.IsValid = true;
		this._enabled = true;
		this._errorMessages = [];
		for ( var i = 0; i < this._validators.length; i++ )
		{
			this._validators[i].Enabled = true;			
			this._validators[i].validate();
			if ( !this._validators[i].IsValid )
			{
				this.IsValid = false;
				if ( this._validators[i].ErrorMessage != null )
					this._errorMessages.push( this._validators[i].ErrorMessage );
			}
		}

		this.AfterFormValidate.fire( this._errorMessages );
		return this.IsValid;
	},
	
	validateOnSubmit: function(e) {
		if ( !this.validate() )
		{
			YAHOO.util.Event.preventDefault(e);
		}
	},
	
	getErrorMessages: function() {
		this.validate();
		return this._errorMessages;
	},
	
	focusFirstInvalidControl: function() {
		for ( var i = 0; i < this._validators.length; i++ )
		{
			var validator = this._validators[i];
			if ( !validator.IsValid )
			{
				if ( validator.ControlToValidate != null )
				{
					var ctl = Lutz.DomUtil.getControl( validator.ControlToValidate );
					ctl.focus()
					return;
				}
			}
		}
	} 
	
}
