/*!
 * FormValidator class
 * @version: 1.0
 * @author: M.F.Endenburg
 * @copyright (c) Denbel Systems, 2007
 */

// load namespaces
Denbel.load( 'util.FormValidator' );
Denbel.load( 'util.FormValidationResult' );
Denbel.load( 'util.CustomValidation' );

var _ValidationResults = null;
var _CustomValidationsLeft = 0;

( function(){

	/**
	 * constructor
	 * @param mixed HTMLFormElement or the string ID of the HTML form element
	 * @return void
	 */
	Denbel.util.FormValidator = function( form )
	{
	    this._form = form;
	    this._changeColorOnError = true;
	    this._errorColor = 'ffdddd';
	    
	    this._errorFields = new Array();
	    
	    this.validateComplete = new YAHOO.util.CustomEvent( 'validateComplete', this, true, YAHOO.util.CustomEvent.FLAT );
	    
	    // E-mail
	    // 1. A series of characters A-Z a-z 0-9 . (dot) % (percent) + (plus) - (dash) of length 1 or more to specify user name
	    // 2. Followed by one @ (at)
	    // 3. Then another series of characters A-Z a-z 0-9 . (dot) - (dash) of length 1 or more to specify domain name
	    // 4. Followed by one dot
	    // 5. Closed by a series of characters A-Z and/or a-z of length 2 or 3 or 4 to specify TLD
	    this._regEmail = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
	    
	    // Phone
	    // In general, spaces, parentheses, dashes and plus signs are allowed.
	    // The rest of it must all be digits.
	    this._regPhone = /(\+\d)*\s*(\(\d{3}\)\s*)*\d{3}(-{0,1}|\s{0,1})\d{2}(-{0,1}|\s{0,1})\d{2}$/;
	    
	    // Domain
	    // Testing against a domain name with TLD
	    this._regDomain = /^[A-Za-z0-9-]{2,}\.[A-Za-z]{2,4}$/;
	    
	    // Name
	    // Testing a name. No digits, no special chars except spaces and dashes.
	    this._regName = /^[A-Za-z\s-]{3,}$/;
	    
	    // Username
	    // Modify according to system needs (default: allow alpha-numeric chars, digits, dashes, underscores minimum length of 3)
	    this._regUsername = /^[A-Za-z0-9-_]{3,}$/;
	    
	    // Password
	    // Modify according to system needs (default: allow alpha-numeric chars, digits, dashes, underscores minimum length of 6)
	    this._regPassword = /^[A-Za-z0-9-_]{6,}$/;
	    
	    // IP
	    // Testing IP address. 4 blocks of 3 digits between 0 and 255 which are separated by dots.
	    this._regIP = /^\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b$/;
	    
	    // URL
	    this._regURL = /^.$/;
	    
	    // Date
	    // Testing a date value in the format mm-dd-yyyy or m-d-yyyy
	    this._regDate = /^(0?[1-9]|[12][0-9]|3[01])[- /.](0?[1-9]|1[012])[- /.](19|20)\d\d$/;
	};
	
	// prototype
	Denbel.util.FormValidator.prototype =
	{
		// fields
		_form: null,
		_regEmail: null,
		_regPhone: null,
		_regDomain: null,
		_regName: null,
		_regUsername: null,
		_regPassword: null,
		_regIP: null,
		_regURL: null,
		_regDate: null,
		_results: null,
		_changeColorOnError: null,
		_errorColor: null,
		_customValidations: null,
		_errorFields: null,
		
		// event
		validateComplete: null,
		
		/**
		 * adds a custom validation object
		 * @param CustomValidation
		 * @return void
		 */
		addCustomValidation: function( c )
		{
		    if( !this._customValidations )
		    {
		        this._customValidations = new Array();
		    }
		
		    this._customValidations.push( c );
		},
		
		/**
		 * When value is true, the form will be set up for validation on submit. When value is false, the subscription will be removed.
		 * @param bool
		 * @return void
		 */
		validateOnSubmit: function( value )
		{
		    if( !this._form )
		    {
		        throw Error( 'Form is not set' );
		        return;
		    }
		
		    if( value )
		    {
		        YAHOO.util.Event.addListener( this._form, 'submit', this.validate, this );
		    }
		    else
		    {
		        YAHOO.util.Event.removeListener( this._form, 'submit', this.validate );
		    }
		},
		
		/**
		 * Sets the ChangeColorOnError configuration option
		 * @param bool
		 * @return void
		 */
		changeColorOnError: function( value )
		{
		    this._changeColorOnError = value;
		},
		
		/**
		 * Returns the HEX value of the color that is used on fields that are in error
		 * @return string
		 */
		getErrorColor: function()
		{
		    return this._errorColor;
		},
		
		/**
		 * Sets the HEX color value that is used on fields that are in error
		 * @param string HEX color
		 * @return void
		 */
		setErrorColor: function( value )
		{
		    if( value.substring( 0, 1 ) == '#' )
		    {
		        value = value.substring( 1 );
		    }
		    
		    this._errorColor = value;
		},
		
		/*
		 * When value is true, the field that losses focus will immediately be validated.
		 * @param bool
		 * @return void
		 *//*
		Denbel.util.FormValidator.prototype.validateOnBlur = function( value )
		{
		    if( !this._form )
		    {
		        YAHOO.log( 'Form is not set', 'error' );
		        return;
		    }
		    
		    if( value )
		    {
		        for( var i = 0; i < this._form.elements.length; i++ )
		        {
		            YAHOO.util.Event.addListener( this._form.elements[i], 'blur', this.validateField, this );
		        }
		    }
		    else
		    {
		        for( var i = 0; i < this._form.elements.length; i++ )
		        {
		            YAHOO.util.Event.removeListener( this._form.elements[i], 'blur', this.validateField );
		        }
		    }
		}
		*/
		
		/**
		 * Returns the inner HTMLFormObject
		 * @return HTMLFormElement
		 */
		getForm: function()
		{
		    return this._form;
		},
		
		/**
		 * Sets the inner HTMLFormObject
		 * @param HTMLFormElement
		 * @return void
		 */
		setForm: function( value )
		{
		    this._form = YAHOO.util.Dom.get( value );
		},
		
		/**
		 * Submits the inner Form object
		 * @return void
		 */
		submit: function()
		{
		    this._form.submit();
		},
		
		/**
		 * Validates the inner Form object
		 * @param Event
		 * @param object
		 * @return bool
		 */
		validate: function( e, o )
		{
		    YAHOO.log( 'validation started' );
		    
		    if( e )
		    {
		        YAHOO.util.Event.stopEvent( e );
		    }
		
		    var forms = null;
		    
		    if( !this._form )
		    {
		        forms = o;
		    }
		    else
		    {
		        forms = this;
		    }
		    
		    if( !forms._form )
		    {
		        YAHOO.log( 'a Form is required', 'error', 'formvalidator' );
		        throw Error( 'a Form is required!' );
		        return false;
		    }
		
		    this._errorFields = new Array();
		    
		    forms.clearErrors();
		    
		    var i = 0;
		    var field = null;
		    var r = null;
		    
		    YAHOO.log( 'validating', 'info', 'formvalidator' );
		    
		    _ValidationResults = new Array();
		    
		    for( i = 0; i < forms._form.elements.length; i++ )
		    {
		        field = forms._form.elements[i];
		        r = forms.validateField( field );
		        
		        if( r != null )
		        {
		            _ValidationResults.push( r );
		        }
		        
		        r = null;
		    }
		    
		    forms.validateCustom();
		    
		    YAHOO.lang.later( ( 1000 * forms._customValidations.length ), forms, 'fireComplete' );
		    
		    if( _ValidationResults.length == 0 )
		    {
                YAHOO.log( 'validation OK', 'info', 'formvalidator' );
		        return true;
		    }
		    else
		    {
                YAHOO.log( 'validation NOT OK', 'info', 'formvalidator' );
		        return false;
		    }
		},
		
		/**
		 * Fire the 'complete' event
		 * @return void
		 */
		fireComplete: function()
		{
		    this.validateComplete.fire( _ValidationResults );
		},
		
		/**
		 * Returns the field which value should be equal to the given field its value
		 * @param HTMLEelement
		 * @return HTMLElement
		 */
		getEqualField: function( field )
		{
		    if( !field )
		    {
		        return null;
		    }
		    
		    var len = 7; // size of the text '_equals'
		    
		    var pos = field.className.indexOf( '_equals' );
		    pos = parseInt( pos + len );
		    
		    var rest = field.className.substring( pos );
		    var fieldId = rest.split( ' ' )[0].substring( 1 );
		    
		    return YAHOO.util.Dom.get( fieldId );
		},
		
		/**
		 * Returns the fieldset container of the given field if any
		 * @param HTMLElement
		 * @return HTMLFieldSetElement
		 */
		getFieldset: function( field )
		{
		    if( !field )
		    {
		        return null;
		    }
		    
		    var fieldset = null;
		    
		    if( field.parentNode && field.parentNode.tagName == 'FIELDSET' )
		    {
		        fieldset = field.parentNode;
		    }
		    
		    return fieldset;
		},
		
		/**
		 * Returns the error field of the given field
		 * @param HTMLElement
		 * @return HTMLElement
		 */
		getErrorField: function( field )
		{
		    if( !field )
		    {
		        return null;
		    }
		    
		    var errorLabel = null;
		    
		    var labels = document.getElementsByTagName( 'label' );
		    var fieldId = field.getAttribute( 'id' );
		    
		    if( !fieldId )
		    {
		        return null;
		    }
		    
		    for( var i = 0; i < labels.length; i++ )
		    {
		        if( labels[i].id == fieldId + '_error' )
		        {
		            errorLabel = labels[i];
		            break;
		        }
		    }
		    
		    return errorLabel;
		},
		
		/**
		 * Clears all errors from the form
		 * @return void
		 */
		clearErrors: function()
		{
		    if( !this._form )
		    {
		        return;
		    }
		    
		    this._results = new Array();
		    
		    var element = null;
		    var node = null;
		    
		    for( var i = 0; i < this._form.elements.length; i++ )
		    {
		        element = this._form.elements[i];
		        this.setErrorField( element, null );
		        
		        if( this._changeColorOnError )
		        {
		            node = this.getFieldset( element );
		        
		            if( node )
		            {
		                YAHOO.util.Dom.setStyle( node, 'background-color', '' );
		            }
		        }
		    }
		    
		    return;
		},
		
		/**
		 * Sets the error field's value
		 * @param HTMLElement the field for the error field that should be set
		 * @param string the error message to set
		 * @return bool
		 */
		setErrorField: function( field, error )
		{
		    if( !field )
		    {
		        return false;
		    }
		    
		    if( error == null )
		    {
		        error = '';
		    }
		    
		    if( this._changeColorOnError && this._errorColor )
		    {
		        var fieldset = this.getFieldset( field );
		    
		        if( fieldset )
		        {
		            YAHOO.util.Dom.setStyle( fieldset, 'background-color', '#' + this._errorColor );
		        }
		    }
		    
		    var errorLabel = this.getErrorField( field );
		    
		    if( !errorLabel || errorLabel == null )
		    {
		        return false;
		    }
		    
		    errorLabel.innerHTML = error;
		    return true;
		},
		
		/**
		 * Looks up the display name of the given field by finding the corresponding LABEL element
		 * @param HTMLElement
		 * @return string
		 */
		getDisplayName: function( field )
		{
		    if( !field )
		    {
		        return null;
		    }
		    
		    var id = field.getAttribute( 'id' );
		    var labels = document.getElementsByTagName( 'label' );
		    var name = '';
		    
		    for( var i = 0; i < labels.length; i++ )
		    {
		        if( labels[i].getAttribute( 'for' ) == id )
		        {
		            name = labels[i].firstChild.nodeValue;
		            break;
		        }
		    }
		    
		    if( name.substring( name.length - 1 ) == ':' )
		    {
		        name = name.substring( 0, name.length - 1 );
		    }
		    
		    return name;
		},
		
		/**
		 * Gets the validation results
		 * @return Array
		 */
		getResults: function()
		{
		    return this._results;
		},
		
		/**
		 * Validates a single form field
		 * @param HTMLElement
		 * @return FormValidationResult
		 */
		validateField: function( field, o )
		{
		    if( YAHOO.util.Event.getTarget( field ) )
		    {
		        field = YAHOO.util.Event.getTarget( field );
		    }
		    
		    if( !field )
		    {
		        return null;
		    }
		    
		    if( !o )
		    {
		        o = this;
		    }
		    
		    if( !this._errorFields )
		    {
		        this._errorFields = new Array();
		    }
		    
		    var out = null;
		    
		    switch( field.tagName )
		    {
		        case 'INPUT':
		            out = o._validateInput( field );
		            break;
		        
		        case 'SELECT':
		            out = o._validateSelect( field );
		            break;
		        
		        case 'TEXTAREA':
		            out = o._validateTextArea( field );
		            break;
		        
		        case 'BUTTON':
		            out = o._validateButton( field );
		            break;
		    }
		    
		    if( out )
		    {
		        this._errorFields.push( field );
		    }
		    
		    return out;
		},
		
		/**
		 * Checks if the given field is a required field
		 * @param HTMLElement
		 * @return bool
		 */
		isRequired: function( field )
		{
		    return YAHOO.util.Dom.hasClass( field, '_required' );
		},
		
		/**
		 * Checks if the given field is an e-mail field
		 * @param HTMLElement
		 * @return bool
		 */
		isEmail: function( field )
		{
		    return YAHOO.util.Dom.hasClass( field, '_email' );
		},
		
		/**
		 * Checks if the given field is a domain field
		 * @param HTMLElement
		 * @return bool
		 */
		isDomain: function( field )
		{
		    return YAHOO.util.Dom.hasClass( field, '_domain' );
		},
		
		/**
		 * Checks if the given field is a phone field
		 * @param HTMLElement
		 * @return bool
		 */
		isPhone: function( field )
		{
		    return YAHOO.util.Dom.hasClass( field, '_phone' );
		},
		
		/**
		 * Checks if the given field is a 'equals' field
		 * @param HTMLElement
		 * @return bool
		 */
		isEquals: function( field )
		{
		    return ( field.className.match( '_equals' ) != null );
		},
		
		/**
		 * Checks if the given field is a name field
		 * @param HTMLElement
		 * @return bool
		 */
		isName: function( field )
		{
		    return YAHOO.util.Dom.hasClass( field, '_name' );
		},
		
		/**
		 * Checks if the given field is a username field
		 * @param HTMLElement
		 * @return bool
		 */
		isUsername: function( field )
		{
		    return YAHOO.util.Dom.hasClass( field, '_username' );
		},
		
		/**
		 * Checks if the given field is a date field
		 * @param HTMLElement
		 * @return bool
		 */
		isDate: function( field )
		{
		    return YAHOO.util.Dom.hasClass( field, '_date' );
		},
		
		/**
		 * Validates a date field
		 * @param HTMLElement
		 * @return bool
		 */
		validateDate: function( field )
		{
		    if( !field )
		    {
		        return false;
		    }
		    
		    if( !field.value )
		    {
		        return true;
		    }
		    
		    return this._regDate.test( field.value );
		},
		
		/**
		 * Validates a username field
		 * @param HTMLElement
		 * @return bool
		 */
		validateUsername: function( field )
		{
		    if( !field )
		    {
		        return false;
		    }
		    
		    if( !field.value )
		    {
		        return true;
		    }
		    
		    return this._regUsername.test( field.value );
		},
		
		/**
		 * Checks if the given field is a password field
		 * @param HTMLElement
		 * @return bool
		 */
		isPassword: function( field )
		{
		    return YAHOO.util.Dom.hasClass( field, '_password' );
		},
		
		/**
		 * Validates a password field
		 * @param HTMLElement
		 * @return bool
		 */
		validatePassword: function( field )
		{
		    if( !field )
		    {
		        return false;
		    }
		    
		    if( !field.value )
		    {
		        return true;
		    }
		    
		    return this._regPassword.test( field.value );
		},
		
		/**
		 * Checks if the given field is an IP field
		 * @param HTMLElement
		 * @return bool
		 */
		isIP: function( field )
		{
		    return YAHOO.util.Dom.hasClass( field, '_ip' );
		},
		
		/**
		 * Validates an IP field
		 * @param HTMLElement
		 * @return bool
		 */
		validateIP: function( field )
		{
		    if( !field )
		    {
		        return false;
		    }
		    
		    if( !field.value )
		    {
		        return true;
		    }
		    
		    return this._regIP.test( field.value );
		},
		
		/**
		 * Validates a name field
		 * @param HTMLElement
		 * @return bool
		 */
		validateName: function( field )
		{
		    if( !field )
		    {
		        return false;
		    }
		    
		    if( !field.value )
		    {
		        return true;
		    }
		    
		    return this._regName.test( field.value );
		},
		
		/**
		 * Checks if the given field has custom validation
		 * @return bool
		 */
		isCustom: function( field )
		{
		    return YAHOO.util.Dom.hasClass( '_custom' );
		},
		
		/**
		 * Validates a CustomValidation field
		 * @return bool
		 */
		validateCustom: function()
		{
		    if( !this._customValidations || this._customValidations.length == 0 )
		    {
		        _CustomValidationsLeft = 0;
		        return;
		    }
		    
		    var c = null;
		    var forms = this;
		    
		    for( var i = 0; i < this._customValidations.length; i++ )
		    {
		        c = this._customValidations[i];
		        
		        if( !c )
		        {
                    YAHOO.log( 'customvalidation corrupted', 'error' );
		            continue;
		        }
		        
		        if( Denbel.util.inArray( c.getField(), forms._errorFields ) )
		        {
                    YAHOO.log( 'field ' + c.getField().name + ' already on error', 'warn' );
                    continue;
		        }
		        
		        _CustomValidationsLeft++;
		        c.complete.unsubscribeAll();
		        
			    c.complete.subscribe( function( e, o )
			    {
		            var r = true;
		            
			        if( o[0].success )
			        {
			            if( !o[0].valid )
			            {
		                    var field = o[0].field;
			                _ValidationResults.push( new Denbel.util.FormValidationResult( field, forms.getDisplayName( field ), o[0].message ) );
			                r = false;
			            }
			        }
		            
		            _CustomValidationsLeft--;
			    } );
			    
			    YAHOO.log( 'validating customvalidation', 'info', 'formvalidator' );
		        c.validate( true );
		    }
		
		    return;
		},
		
		/**
		 * Validates a field value against another's
		 * @param HTMLElement
		 * @return bool
		 */
		validateEquals: function( field )
		{
		    if( !field )
		    {
		        return false;
		    }
		    
		    var against = this.getEqualField( field );
		    
		    if( !against )
		    {
		        return false;
		    }
		    
		    return ( against.value == field.value );
		},
		
		/**
		 * Validates a field value against e-mail
		 * @param HTMLElement
		 * @return bool
		 */
		validateEmail: function( field )
		{
		    if( !field.value )
		    {
		        return true;
		    }
		    
		    if( !field.value )
		    {
		        return true;
		    }
		    
		    return this._regEmail.test( field.value );
		},
		
		/**
		 * Validates a field value against phone
		 * @param HTMLElement
		 * @return bool
		 */
		validatePhone: function( field )
		{
		    if( !field.value )
		    {
		        return true;
		    }
		    
		    return this._regPhone.test( field.value );
		},
		
		/**
		 * Validates a field value against domain
		 * @param HTMLElement
		 * @return bool
		 */
		validateDomain: function( field )
		{
		    if( !field.value )
		    {
		        return true;
		    }
		    
		    return this._regDomain.test( field.value );
		},
		
		/**
		 * Validates a HTMLInputElement
		 * @param HTMLIntputElement
		 * @return FormValidationResult
		 */
		_validateInput: function( field )
		{
		    if( field.tagName != 'INPUT' )
		    {
		        return null;
		    }
		    
		    switch( field.type )
		    {
		        case 'text':
		        case 'hidden':
		        case 'password':
		            if( this.isRequired( field ) && ( field.value == '' || field.value == null ) )
		            {
		                return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_REQUIRED' ) );
		            }
		            
		            if( this.isEmail( field ) )
		            {
		                if( !this.validateEmail( field ) )
		                {
		                    return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_INVALID_EMAIL' ) );
		                }
		            }
		            
		            if( this.isPhone( field ) )
		            {
		                if( !this.validatePhone( field ) )
		                {
		                    return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_INVALID_PHONE' ) );
		                }
		            }
		            
		            if( this.isDomain( field ) )
		            {
		                if( !this.validateDomain( field ) )
		                {
		                    return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_INVALID_DOMAIN' ) );
		                }
		            }
		            
		            if( this.isEquals( field ) )
		            {
		                if( !this.validateEquals( field ) )
		                {
		                    return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_NOT_SAME' ).replace( '%1', '<strong>' + this.getDisplayName( this.getEqualField( field ) ) + '</strong>' ) );
		                }
		            }
		            
		            if( this.isName( field ) )
		            {
		                if( !this.validateName( field ) )
		                {
		                    return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_INVALID_NAME' ) );
		                }
		            }
		            
		            if( this.isUsername( field ) )
		            {
		                if( !this.validateUsername( field ) )
		                {
		                    return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_INVALID_USERNAME' ) );
		                }
		            }
		            
		            if( this.isPassword( field ) )
		            {
		                if( !this.validatePassword( field ) )
		                {
		                    return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_INVALID_PASSWORD' ) );
		                }
		            }
		            
		            if( this.isIP( field ) )
		            {
		                if( !this.validateIP( field ) )
		                {
		                    return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_INVALID_IP' ) );
		                }
		            }
		            
		            if( this.isDate( field ) )
		            {
		                if( !this.validateDate( field ) )
		                {
		                    return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_INVALID_DATE' ) );
		                }
		            }
		            break;
		        
		        case 'radio':
		            break;
		        
		        case 'checkbox':
		            break;
		        
		        case 'image':
		            break;
		        
		        case 'button':
		            break;
		        
		        case 'submit':
		            break;
		    }
		    
		    return null;
		},
		
		/**
		 * Validates a HTMLSelectElement
		 * @param HTMLSelectElement
		 * @return bool
		 */
		_validateSelect: function( field )
		{
		    if( field.tagName != 'SELECT' )
		    {
		        return false;
		    }
		    
		    if( this.isRequired( field ) )
		    {
			    if( field.getAttribute( 'multiple' ) == 'multiple' )
			    {
			        // this SELECT box is multiselect and required, so we need to check if at least one selection is made
			        var oneSelected = false;
			        
			        for( var i = 0; i < field.options.length; i++ )
			        {
			            if( field.options[i].selected )
			            {
			                oneSelected = true;
			                break;
			            }
			        }
			        
			        if( !oneSelected )
			        {
			            return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), 'Field is required' );
			        }
			    }
		    }
		    
		    return null;
		},
		
		/**
		 * Validates a HTMLTextareaElement
		 * @param HTMLTextAreaElement
		 * @return bool
		 */
		_validateTextArea: function( field )
		{
		    if( field.tagName != 'TEXTAREA' )
		    {
		        return false;
		    }
		    
		   if( this.isRequired( field ) )
		    {
		        if( field.value == '' || field.value == null )
		        {
		            return new Denbel.util.FormValidationResult( field, this.getDisplayName( field ), Denbel.Website.getResource( 'C_FV_REQUIRED' ) );
		        }
		    }
		},
		
		/**
		 * Validates a HTMLButtonElement
		 * @param HTMLButtonElement
		 * @return bool
		 */
		_validateButton: function( field )
		{
		    if( field.tagName != 'BUTTON' )
		    {
		        return false;
		    }
		},
		
		/**
		 * Converts this object to its string representation
		 * @return string
		 */
		toString: function()
		{
            return 'Denbel.util.FormValidator';
		}
	};
} )();
	
	// *** ----------------------------------------------------------------------- *** //
	
( function()
{
	/**
	 * constructor
	 * @param HTMLElement
	 * @param string module to call
	 * @param string method on the module to invoke
	 * @param string the ignore value
	 * @return void
	 */
	Denbel.util.CustomValidation = function( field, module, method, ignore )
	{
	    this._module = module;
	    this._method = method;
	    this._ignore = ignore;
	    this._field = YAHOO.util.Dom.get( field );
	    
	    this._asyncCallback =
	    {
	        success:this.onAsyncComplete,
	        failure:this.onAsyncFailure,
	        argument:null
	    };
	    
	    this._asyncCallback.argument = new Array();
	    this._asyncCallback.argument['this'] = this;
	    
	    this.complete = new YAHOO.util.CustomEvent( 'complete', this, true, YAHOO.util.CustomEvent.LIST );
	};
	
	Denbel.util.CustomValidation.prototype =
	{
		// fields
		_module: null,
		_method: null,
		_ignore: null,
		_initialRequest: null,
		_field: null,
		_asyncCallback: null,
		_message: null,
		_waitCaption: null,
		
		// events
		complete: null,
		
		/**
		 * Returns the HTMLElement associated with this CustomValidation object
		 * @return HTMLElement
		 */
		getField: function()
		{
		    return this._field;
		},
		
		/**
		 * Gets the message
		 * @return string
		 */
		getMessage: function()
		{
		    return this._message;
		},
		
		/**
		 * Set the text message
		 * @param string
		 * @return void
		 */
		setMessage: function( value )
		{
		    this._message = value;
		},
		
		/**
		 * Validates
		 * @param bool true to show a status box
		 * @return void
		 */
		validate: function( showWait )
		{
		    if( showWait )
		    {
		        Denbel.Website.showWaitDialog( this._waitCaption );
		    }
		    
		    var rpc = new Denbel.rpc.XmlRpcClient();
		    var msg = new Denbel.rpc.XmlRpcMessage( 'invokeModule' );
		    msg.addParameter( new Denbel.rpc.XmlRpcParameter( this._module ) );
		    msg.addParameter( new Denbel.rpc.XmlRpcParameter( this._method ) );
		    msg.addParameter( new Denbel.rpc.XmlRpcParameter( this._field.value ) );
		    
		    if( this._ignore )
		    {
                msg.addParameter( new Denbel.rpc.XmlRpcParameter( this._ignore ) );
            }
		    
		    rpc.callService( msg,
		    {
                argument:
                {
                    'obj': this,
                    'message': null
                },
                success: function( o )
                {
                    o.argument.obj.complete.fire(
		            {
		                success: true,
		                valid: o.data,
		                field: o.argument.obj._field,
		                message: o.argument.obj._message
		            } );
                },
                failure: function( o )
                {
                    o.argument.obj.complete.fire(
                    {
                        success: false,
                        field: o.argument.obj._field,
                        message: o.argument.obj._message
                    } );
                }
		    } );
		},
		
        /**
         * Converts this object to its string representation
         * @return string
         */
		toString: function()
		{
            return 'Denbel.util.CustomValidation';
		}
	};
} )();
	
	// *** ----------------------------------------------------------------------- *** //
	
( function()
{
	/**
	 * constructor
	 * @param HTMLElement the error field
	 * @param string the display name of the field
	 * @param string the message
	 * @return void
	 */
	Denbel.util.FormValidationResult = function( field, name, message )
	{
	    this._field = field;
	    this._name = name;
	    this._message = message;
	};
	
	Denbel.util.FormValidationResult.prototype =
	{
		// fields
		_field: null,
		_name: null,
		_message: null,
		
		/**
		 * Returns the HTMLElement
		 * @return HTMLElement
		 */
		getField: function()
		{
		    return this._field;
		},
		
		/**
		 * Returns the display name of the field
		 * @return string
		 */
		getName: function()
		{
		    return this._name;
		},
		
		/**
		 * Returns the error message
		 * @return string
		 */
		getMessage: function()
		{
		    return this._message;
		},
		
        /**
         * Converts this object to its string representation
         * @return string
         */
		toString: function()
		{
            return 'Denbel.util.FormValidationResult';
		}
    };
} )();

