/*!
 * Denbel.util.FormValidator
 * Package Util
 * Version 1.0
 * Author MFE
 * Copyright (c) 2008 Denbel
 */

// load namespaces
Denbel.load( 'util.FormValidatorManager' );
Denbel.load( 'util.FormValidator' );
Denbel.load( 'util.FieldValidator' );

Denbel.util.FormValidatorManager =
{
  /**
   * Collection
   * @var Denbel.lang.Object
   */
  items: new Denbel.lang.Object(),
  
  /**
   * Sets a FormValidator instance
   * @param String id
   * @param FormValidator fv
   * @return void
   */
  set: function( id, fv )
  {
    Denbel.util.FormValidatorManager.items.set( id, fv );
  },
  
  /**
   * Returns a FormValidator instance by ID
   * @param String id
   * @return Denbel.util.FormValidator
   */
  get: function( id )
  {
    return Denbel.util.FormValidatorManager.items.get( id );
  }
};

/**
 * constructor
 * @param HTMLFormElement form
 * @param object config
 * @return Denbel.util.FormValidator
 */
Denbel.util.FormValidator = function( form, config )
{
  if( YAHOO.lang.isString( form ) )
  {
    form = YAHOO.util.Dom.get( form );
  }
  
  if( !form )
  {
    YAHOO.log( 'FormValidator needs a form to work with', 'error', 'Denbel.util.FormValidator' );
    return;
  }
  
  this.form = form;
  this.config = new Denbel.lang.Object();
  
  if( config.blur )
  {
    this.config.set( 'blur', config.blur );
  }
  
  if( config.errorContainer )
  {
    this.config.set( 'errorContainer', config.errorContainer );
  }
  
  if( config.useEffect != undefined )
  {
    this.config.set( 'useEffect', config.useEffect );
  }
  else
  {
    this.config.set( 'useEffect', false );
  }
  
  if( config.invisibles != undefined )
  {
    this.config.set( 'invisibles', config.invisibles );
  }
  else
  {
    this.config.set( 'invisibles', true );
  }
  
  this.init();
};

/**
 * Validation types
 * @var Array
 */
Denbel.util.FormValidator.types =
[
  'required',
  'email',
  'alpha',
  'numeric',
  'maxlen',
  'minlen',
  'minnum',
  'maxnum',
  'phone',
  'ip',
  'url',
  'match',
  'uripart',
  'remote',
  'custom'
];

Denbel.util.FormValidator.TYPE_REQUIRED = 'required';
Denbel.util.FormValidator.TYPE_EMAIL = 'email';
Denbel.util.FormValidator.TYPE_ALPHA = 'alpha';
Denbel.util.FormValidator.TYPE_NUMERIC = 'numeric';
Denbel.util.FormValidator.TYPE_MAXLEN = 'maxlen';
Denbel.util.FormValidator.TYPE_MAXNUM = 'maxnum';
Denbel.util.FormValidator.TYPE_MINNUM = 'minnum';
Denbel.util.FormValidator.TYPE_MAXIMUM = 'maxlen';
Denbel.util.FormValidator.TYPE_MINLEN = 'minlen';
Denbel.util.FormValidator.TYPE_MINIMUM = 'minlen';
Denbel.util.FormValidator.TYPE_PHONE = 'phone';
Denbel.util.FormValidator.TYPE_IPADDRESS = 'ip';
Denbel.util.FormValidator.TYPE_IP = 'ip';
Denbel.util.FormValidator.TYPE_URL = 'url';
Denbel.util.FormValidator.TYPE_URIPART = 'uripart';
Denbel.util.FormValidator.TYPE_MATCH = 'match';
Denbel.util.FormValidator.TYPE_REMOTE = 'remote';
Denbel.util.FormValidator.TYPE_RPC = 'remote';
Denbel.util.FormValidator.TYPE_CUSTOM = 'custom';

Denbel.util.FormValidator.EXP_MAIL = new RegExp( /^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/i );
Denbel.util.FormValidator.EXP_ALPHA = new RegExp( /^[a-z]+$/i );
Denbel.util.FormValidator.EXP_NUMERIC = new RegExp( /^[0-9\.\-]+$/ );
Denbel.util.FormValidator.EXP_PHONE = new RegExp( /(\+\d)*\s*(\(\d{3}\)\s*)*\d{3}(-{0,1}|\s{0,1})\d{2}(-{0,1}|\s{0,1})\d{2}$/ );
Denbel.util.FormValidator.EXP_IP = new RegExp( /^\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$/ );
Denbel.util.FormValidator.EXP_URL = new RegExp( /^http(s)?:\/\/([a-z\.])+$/i );
Denbel.util.FormValidator.EXP_URIPART = new RegExp( /^([a-z0-9\-\+])+$/i );

/**
 * Returns the value of the label for the given field
 * @param variant field
 * @return string
 */
Denbel.util.FormValidator.getFieldLabel = function( field )
{
  if( YAHOO.lang.isString( field ) )
  {
    field = YAHOO.util.Dom.get( field );
  }
  
  if( !field )
  {
    return null;
  }
  
  var labels = document.getElementsByTagName( 'label' );
  var fieldId = field.getAttribute( 'id' );
  var node = null;
  
  for( var i = 0; i < labels.length; i++ )
  {
    if( labels[i].getAttribute( 'for' ) == fieldId || labels[i].getAttribute( 'id' ) == fieldId + '_label' )
    {
      if( labels[i].childNodes.length > 1 )
      {
        node = labels[i].childNodes[1];
      }
      else
      {
        node = labels[i];
      }
      
      return Denbel.util.XmlHelper.getElementValue( node );
    }
  }
  
  return null;
};

/**
 * Executes a validation
 * @param Denbel.util.FieldValidator val
 * @param Boolean silent
 * @param Variant obj
 * @return bool
 */
Denbel.util.FormValidator.exec = function( val, silent, obj )
{
  if( val.type != 'remote' )
  {
    return Denbel.util.FormValidator.check( val.getFieldValue(), val.type, val.extra, obj );
  }
  
  var rpc = new Denbel.rpc.XmlRpcClient();
  var msg = rpc.createMessage( val.extra.method );
  msg.createAndAddParameter( val.getFieldValue() );
  
  if( val.extra.params )
  {
    msg.createAndAddParameter( val.extra.params );
  }
  
  var fire = true;
  
  if( silent )
  {
    fire = false;
  }
  
  if( val.isInService )
  {
    return;
  }
  
  val.isInService = true;
  
  rpc.callService( msg,
  {
    success: function( res )
    {
      res.argument.validator.isInService = false;
      res.argument.validator.inProgress = false;
      res.argument.validator.isCompleted = true;
      res.argument.validator.error = false;
      
      if( res.data[0] == false )
      {
        res.argument.validator.setError();
      }
      else
      {
        res.argument.validator.setValid();
      }
      
      if( res.argument.fire )
      {
        res.argument.validator.completeEvent.fire(
        {
          obj: res.argument.validator,
          result: true,
          source: this
        } );
      }
    },
    failure: function( res )
    {
      res.argument.validator.isInService = false;
      res.argument.validator.inProgress = false;
      res.argument.validator.isCompleted = true;
      res.argument.validator.error = true;
      
      res.argument.validator.setError( res.fault.get( 'faultString' ) );
      
      if( res.argument.fire )
      {
        res.argument.validator.completeEvent.fire(
        {
          obj: res.argument.validator,
          result: false,
          source: this
        } );
      }
    },
    argument:
    {
      validator: val,
      fire: fire
    }
  } );
};

/**
 * Does a check against
 * @param String value
 * @param String type
 * @param Variant o
 * @param Variant obj
 * @return Boolean
 */
Denbel.util.FormValidator.check = function( value, type, o, obj )
{
  var c = false;
  
  switch( type )
  {
    case 'required':
      c = ( value != '' );
      break;
    case 'email':
      c = Denbel.util.FormValidator.EXP_MAIL.test( value );
      break;
    case 'alpha':
      c = Denbel.util.FormValidator.EXP_ALPHA.test( value );
      break;
    case 'numeric':
      c = Denbel.util.FormValidator.EXP_NUMERIC.test( value );
      break;
    case 'maxlen':
      c = ( value.length <= o );
      break;
    case 'minlen':
      c = ( value.length >= o );
      break;
    case 'maxnum':
      c = ( parseInt( value ) <= parseInt( o ) );
      break;
    case 'minnum':
      c = ( parseInt( value ) >= parseInt( o ) );
      break;
    case 'phone':
      c = Denbel.util.FormValidator.EXP_PHONE.test( value );
      break;
    case 'ip':
      c = Denbel.util.FormValidator.EXP_IP.test( value );
      break;
    case 'url':
      c = Denbel.util.FormValidator.EXP_URL.test( value );
      break;
    case 'uripart':
      c = Denbel.util.FormValidator.EXP_URIPART.test( value );
      break;
    case 'match':
      if( YAHOO.lang.isString( o ) )
      {
        c = ( value == o );
      }
      else if( YAHOO.lang.isObject( o ) )
      {
        c = ( value == o.value );
      }
      break;
    case 'custom':
      if( YAHOO.lang.isFunction( o ) )
      {
        c = o.call( this, value, obj );
      }
      break;
  }
  
  return c;
};

// prototype
Denbel.util.FormValidator.prototype =
{
  /**
   * Form
   * @var HTMLFormElement
   */
  form: null,
  
  /**
   * Validators
   * @var Array
   */
  validators: null,
  
  /**
   * Holds a value indicating all validators were completed valid
   * @var Boolean
   */
  allComplete: false,
  
  /**
   * config
   * @var Object
   */
  config: null,
  
  /**
   * initializer
   * @return bool
   */
  init: function()
  {
    this.validators = new Array();
    
    var elements = this.form.elements;
    var types = Denbel.util.FormValidator.types;
    var j = null;
    var ext = null;
    var obj = null;
    
    for( var i = 0; i < elements.length; i++ )
    {
      if( elements[i] == null )
      {
        continue;
      }
      
      for( j = 0; j < types.length; j++ )
      {
        if( YAHOO.util.Dom.hasClass( elements[i], types[j] ) )
        {
          ext = null;
          
          switch( types[j] )
          {
            case 'maxlen':
              ext = parseInt( elements[i].getAttribute( 'maxlength' ) );
              break;
            case 'minlen':
              ext = null;
              break;
            case 'match':
              ext = null;
              break;
          }
          
          obj = new Denbel.util.FieldValidator( elements[i], types[j], null, ext );
          obj.parent = this;
          
          if( !obj.apply() )
          {
            continue;
          }
          
          if( this.config.get( 'blur' ) )
          {
            //YAHOO.util.Event.removeListener( elements[i] );
            YAHOO.util.Event.addBlurListener( elements[i], function( e, o )
            {
              YAHOO.util.Event.stopEvent( e );
              
              var vals = o.getValidatorsByField( this );
              
              for( var i = 0; i < vals.length; i++ )
              {
                if( !vals[i].validate() )
                {
                  break;
                }
              }
            }, this );
          }
          
          this.validators.push( obj );
        }
      }
    }
    
    return true;
  },
  
  /**
   * Returns a value indicating the given type is a valid validation type
   * @param string type
   * @return bool
   */
  isValidType: function( type )
  {
    return Denbel.util.inArray( type, Denbel.util.FormValidator.types );
  },

  /**
   * Adds a validator (equivalent of addValidation)
   * @param Variant form field
   * @param String type
   * @param String message
   * @param Variant o set an extra value for type=maxlen: the length, for type=custom: a callback function
   * @param Variant arg an optional object to pass to a function
   * @return Boolean
   */
  addValidator: function( field, type, message, o, arg )
  {
    return this.addValidation( field, type, message, o, arg );
  },
  
  /**
   * Adds a validation
   * @param Variant form field
   * @param String type
   * @param String message
   * @param Variant o set an extra value for type=maxlen: the length, for type=custom: a callback function
   * @param Variant arg an optional object to pass to a function
   * @return Boolean
   */
  addValidation: function( field, type, message, o, arg )
  {
    if( !this.isValidType( type ) )
    {
      YAHOO.log( 'Incorrect validation type', 'error', 'Denbel.util.FormValidator' );
      return false;
    }
    
    if( this.hasValidator( field, type ) )
    {
      this.removeValidator( field, type );
    }
    
    var val = new Denbel.util.FieldValidator( field, type, message, o );
    val.parent = this;
    
    if( !val.apply() )
    {
      return false;
    }
    
    if( this.config.get( 'blur' ) )
    {
      YAHOO.util.Event.removeListener( field, 'blur' );
      YAHOO.util.Event.addBlurListener( field, function( e, o )
      {
        YAHOO.util.Event.stopEvent( e );
        var vals = o.fv.getValidatorsByField( this );
        
        for( var i = 0; i < vals.length; i++ )
        {
          if( !vals[i].validate( o.obj ) )
          {
            break;
          }
        }
      },
      {
        fv: this,
        obj: arg
      } );
    }
    
    this.validators.push( val );
    
    return true;
  },

  /**
   * Removes a validator by index
   * @param variant field
   * @param string type
   * @return void
   */
  removeValidator: function( field, type )
  {
    return this.removeValidation( field, type );
  },
  
  /**
   * Removes a validator by index
   * @param variant field
   * @param string type
   * @return void
   */
  removeValidation: function( field, type )
  {
    if( YAHOO.lang.isString( field ) )
    {
      field = YAHOO.util.Dom.get( field );
    }
    
    if( !field )
    {
      return;
    }
    
    var val = null;
    var i = 0;
    
    for( i = 0; i < this.validators.length; i++ )
    {
      val = this.validators[i];
      
      if( val.field == field && val.type == type )
      {
        this.validators[i] = null;
        break;
      }
    }
    
    var vals = this.validators;
    this.validators = new Array();
    
    for( i = 0; i < vals.length; i++ )
    {
      if( vals[i] != null )
      {
        this.validators.push( vals[i] );
      }
    }
  },
  
  /**
   * Returns a value indicating if the given field has a validator of given type
   * @param variant field
   * @param string type
   * @return variant returns false if not, returns the index if it exists
   */
  hasValidator: function( field, type )
  {
    if( YAHOO.lang.isString( field ) )
    {
      field = YAHOO.util.Dom.get( field );
    }
    
    if( !field )
    {
      return false;
    }
    
    var val = null;
    
    for( var i = 0; i < this.validators.length; i++ )
    {
      val = this.validators[i];
      
      if( val.field == field && val.type == type )
      {
        return true;
      }
    }
    
    return false;
  },
  
  /**
   * Returns the validators by field
   * @param variant field
   * @return Array of Denbel.util.FieldValidator objects
   */
  getValidatorsByField: function( field )
  {
    if( YAHOO.lang.isString( field ) )
    {
      field = YAHOO.util.Dom.get( field );
    }
    
    if( !field )
    {
      return null;
    }
    
    if( !this.validators )
    {
      this.validators = new Array();
      return null;
    }
    
    var vals = [];
    
    for( var i = 0; i < this.validators.length; i++ )
    {
      if( this.validators[i].field == field )
      {
        vals.push( this.validators[i] );
      }
    }
    
    return vals;
  },
  
  /**
   * Marks a field as invalid
   * @param variant field
   * @return bool
   */
  invalidate: function( field )
  {
    var fv = this.getValidatorsByField( field );
    
    if( !fv || fv.length == 0 )
    {
      return false;
    }
    
    fv[0].setError();
    
    return true;
  },
  
  /**
   * Invalidates the Form
   * @return Boolean
   */
  invalidateAll: function()
  {
    for( var i = 0; i < this.validators.length; i++ )
    {
      this.invalidate( this.validators[i].field );
    }
    
    return true;
  },
  
  /**
   * Resets as it was never validated
   * @param Boolean clear
   * @return Boolean
   */
  reset: function( clear )
  {
    for( var i = 0; i < this.validators.length; i++ )
    {
      this.validators[i].completeEvent.unsubscribeAll();
      
      if( clear === true )
      {
        this.validators[i].clear();
      }
    }
    
    return true;
  },
  
  /**
   * Event handler for the completeEvent of FieldValidator objects
   * @param Object event
   * @param Object object
   * @return void
   */
  onComplete: function( e, o )
  {
    if( o.waitForAll && e.obj == e.source )
    {
      return;
    }
    
    var self = o.parent;
    var callback = o.callback;
    
    var allComplete = true;
    var error = false;
    
    for( var i = 0; i < self.validators.length; i++ )
    {
      if( !self.validators[i].isCompleted )
      {
        allComplete = false;
        break;
      }
      
      if( self.validators[i].error )
      {
        error = true;
      }
    }
    
    if( allComplete && e.obj != e.source )
    {
      if( error )
      {
        callback.failure.call( self,
        {
          argument: callback.argument
        } );
      }
      else
      {
        callback.success.call( self,
        {
          argument: callback.argument
        } );
      }
    }
  },
  
  /**
   * Returns a value indicating the given field has an invalid value
   * @param HTMLElement field
   * @return bool
   */
  hasInvalidValue: function( field )
  {
    var vals = this.getValidatorsByField( field );
    
    for( var i = 0; i < vals.length; i++ )
    {
      if( vals[i].error )
      {
        return true;
      }
    }
    
    return false;
  },
  
  /**
   * Validates the form
   * @param Object callback object with functions for success and failure and an optional argument object
   * @param Boolean set to true to also validate invisible fields
   * @return void
   */
  validate: function( callback, invisibles )
  {
    var ok = true;
    var wait = false;
    
    if( !this.validators )
    {
      return;
    }
    
    if( invisibles !== true && invisibles !== false )
    {
      invisibles = this.config.get( 'invisibles' );
    }
    
    var invalids = [];
    
    for( var i = 0; i < this.validators.length; i++ )
    {
      if( !invisibles && !Denbel.util.isVisible( this.validators[i].field ) )
      {
        continue;
      }
      
      if( this.hasInvalidValue( this.validators[i].field ) )
      {
        this.validators[i].setError();
        ok = false;
        
        if( !this.config.get( 'errorContainer' ) || this.config.get( 'errorContainer' ) == undefined )
        {
          continue;
        }
      }
      
      if( !this.validators[i].validate() && this.validators[i].type != Denbel.util.FormValidator.TYPE_RPC )
      {
        ok = false;
        
        if( this.config.get( 'errorContainer' ) )
        {
          break;
        }
      }
      
      if( this.validators[i].type == Denbel.util.FormValidator.TYPE_RPC )
      {
        wait = true;
        
        this.validators[i].completeEvent.unsubscribeAll();
        this.validators[i].completeEvent.subscribe( this.onComplete,
        {
          parent: this,
          callback: callback,
          validator: this.validators[i],
          waitForAll: true
        } );
      }
    }
    
    if( !wait )
    {
      if( ok )
      {
        callback.success.call( this,
        {
          argument: callback.argument
        } );
      }
      else
      {
        callback.failure.call( this,
        {
          argument: callback.argument
        } );
      }
    }
  },
  
  /**
   * Converts this class to its string representation
   * @return String
   */
  toString: function()
  {
    return 'Denbel.util.FormValidator';
  }
};

/****************************************************************************************/

/**
 * constructor
 * @param variant field
 * @param string type
 * @param string message error message
 * @param variant o an extra value for special types like maxlen and custom
 * @return Denbel.util.FieldValidator
 */
Denbel.util.FieldValidator = function( field, type, message, o )
{
  if( YAHOO.lang.isString( field ) )
  {
    field = YAHOO.util.Dom.get( field );
  }
  
  if( !field )
  {
    YAHOO.log( 'FieldValidator needs a field to work with', 'error', 'Denbel.util.FieldValidator' );
    return;
  }
  
  if( !Denbel.util.inArray( type, Denbel.util.FormValidator.types ) )
  {
    YAHOO.log( 'Incorrect validation type', 'error', 'Denbel.util.FieldValidator' );
    return;
  }
  
  this.field = field;
  this.type = type;
  
  if( !message )
  {
    message = '';
  }
  
  this.message = message;
  
  if( o === null )
  {
    o = null;
  }
  
  this.extra = o;
};

// prototype
Denbel.util.FieldValidator.prototype =
{
  /**
   * Event that fires when validation is completed
   * @var YAHOO.util.CustomEvent
   */
  completeEvent: ( new YAHOO.util.CustomEvent( 'completed', this, true, YAHOO.util.CustomEvent.FLAT ) ),
  
  /**
   * Form field
   * @var HTMLElement
   */
  field: null,
  
  /**
   * Field's ID
   * @var string
   */
  fieldId: null,
  
  /**
   * parent FormValidator
   * @var Denbel.util.FormValidator
   */
  parent: null,
  
  /**
   * Container element
   * @var HTMLElement
   */
  container: null,
  
  /**
   * Validation type
   * @var string
   */
  type: null,
  
  /**
   * Error message
   * @var string
   */
  message: null,
  
  /**
   * Extra value
   * @var variant
   */
  extra: null,
  
  /**
   * Holds a value indicating the validation process is in progress
   * @var bool
   */
  inProgress: false,
  
  /**
   * Holds a value indicating the validation process is in service call
   * @var Boolean
   */
  isInService: false,
  
  /**
   * Holds a value indicating the validation process is completed
   * @var bool
   */
  isCompleted: false,
  
  /**
   * Holds a value indicating the validation has failed
   * @var bool
   */
  error: false,
  
  /**
   * The opaque value
   * @var Number
   */
  opaque: 0.9,
  
  /**
   * Resets the validator
   * @return Boolean
   */
  reset: function()
  {
    this.apply();
  },
  
  /**
   * applies the validator to the field
   * @return bool
   */
  apply: function()
  {
    if( this.type == 'maxlen' && this.extra != null )
    {
      this.field.setAttribute( 'maxlength', this.extra.toString() );
    }
    else
    {
      if( !YAHOO.util.Dom.hasClass( this.field, this.type ) )
      {
        YAHOO.util.Dom.addClass( this.field, this.type );
      }
    }

    if( this.field == null )
    {
      return false;
    }
    
    this.fieldId = this.field.getAttribute( 'id' );
    
    var mainEl = this.field;
    var parent = this.field.parentNode;
    
    if( parent.tagName.toLowerCase() != 'fieldset' )
    {
      this.container = parent;
      mainEl = this.container;
    }
    
    var span = null;
    
    if( this.parent.config.get( 'errorContainer' ) )
    {
      span = YAHOO.util.Dom.get( this.parent.config.get( 'errorContainer' ) );
      span.innerHTML = '';
    }
    else
    {
      if( !YAHOO.util.Dom.inDocument( this.field.getAttribute( 'id' ) + '-info' ) )
      {
        span = YAHOO.util.Dom.getNextSibling( mainEl );
      
        if( span && span.tagName.toLowerCase() == 'span' )
        {
          span.innerHTML = '';
          span.setAttribute( 'id', this.field.getAttribute( 'id' ) + '-info' );
          YAHOO.util.Dom.addClass( span, 'error-field' );
          YAHOO.util.Dom.setStyle( span, 'display', 'none' );
        }
        else
        {
          span = document.createElement( 'span' );
          span.setAttribute( 'id', this.field.getAttribute( 'id' ) + '-info' );
          span.setAttribute( 'class', 'error-field' );
          span.setAttribute( 'style', 'display:none;' );
          YAHOO.util.Dom.insertAfter( span, mainEl );
        }
      }
      else
      {
        span = YAHOO.util.Dom.get( this.field.getAttribute( 'id' ) + '-info' );
        
        if( span )
        {
          span.innerHTML = '';
        }
      }
    }
    
    return true;
  },
  
  /**
   * Clears the validation
   * @return void
   */
  clear: function()
  {
    var mainEl = this.field;
    
    if( this.container )
    {
      mainEl = this.container;
    }
    
    var parent = mainEl.parentNode;
    var span = null;
    
    if( this.parent.config.get( 'errorContainer' ) )
    {
      span = YAHOO.util.Dom.get( this.parent.config.get( 'errorContainer' ) );
    }
    else
    {
      span = YAHOO.util.Dom.get( this.field.getAttribute( 'id' ) + '-info' );
    }
    
    YAHOO.util.Dom.setStyle( span, 'display', 'none' );
    span.innerHTML = '';
    
    this.isCompleted = true;
    this.inProgress = false;
    this.error = false;
  },
  
  /**
   * Returns the value of the field
   * @return variant
   */
  getFieldValue: function()
  {
    return this.field.value;
  },
  
  /**
   * Sets the field on error
   * @param string msg optional message to override internal error message
   * @return void
   */
  setError: function( msg )
  {
    this.isCompleted = true;
    this.inProgress = false;
    this.error = true;
    
    var mainEl = this.field;
    
    if( this.container )
    {
      mainEl = this.container;
    }
    
    if( !msg )
    {
      msg = this.message;
    }
    
    var parent = mainEl.parentNode;
    var span = null;
    
    if( this.parent.config.get( 'errorContainer' ) )
    {
      span = YAHOO.util.Dom.get( this.parent.config.get( 'errorContainer' ) );
    }
    else
    {
      span = YAHOO.util.Dom.get( this.field.getAttribute( 'id' ) + '-info' );
      
      if( this.parent.config.get( 'useEffect' ) && YAHOO.util.Dom.getStyle( span, 'opacity' ) != this.opaque )
      {
        YAHOO.util.Dom.setStyle( span, 'opacity', '0' );
        YAHOO.util.Dom.setStyle( span, 'display', '' );
        
        var anim = new YAHOO.util.Anim( span,
        {
          opacity:
          {
            from: 0.0,
            to: this.opaque
          }
        }, 0.7 );
        
        anim.onStart.subscribe( function( e, o, p ) 
        {
          YAHOO.util.Dom.setX( p.element, YAHOO.util.Dom.getRegion( p.field ).right );
        },
        {
          element: span,
          field: this.field
        } );
        
        anim.animate();
      }
      else
      {
        YAHOO.util.Dom.setStyle( span, 'display', '' );
      }
    }
    
    if( !this.parent.config.get( 'errorContainer' ) )
    {
      span.innerHTML = '';
      
      var img = document.createElement( 'img' );
      img.setAttribute( 'alt', '' );
      img.setAttribute( 'src', '/media/img/error.gif' );
      img.setAttribute( 'style', 'margin-left:5px;' );
  
      span.appendChild( img );
      span.innerHTML += '&nbsp;&nbsp;' + msg;
      
      YAHOO.util.Dom.addClass( parent, 'error' );
    }
    else
    {
      span.innerHTML = msg;
    }
    
    this.completeEvent.fire(
    {
      obj: this,
      result: false,
      source: this
    } );
  },
  
  /**
   * Sets the field on valid
   * @return void
   */
  setValid: function()
  {
    this.inProgress = false;
    this.isCompleted = true;
    this.error = false;
    
    var mainEl = this.field;
    
    if( this.container )
    {
      mainEl = this.container;
    }
    
    var parent = mainEl.parentNode;
    var span = null;
    
    if( this.parent.config.get( 'errorContainer' ) )
    {
      span = YAHOO.util.Dom.get( this.parent.config.get( 'errorContainer' ) );
    }
    else
    {
      span = YAHOO.util.Dom.get( this.field.getAttribute( 'id' ) + '-info' );
    }
    
    if( !this.parent.config.get( 'errorContainer' ) )
    {
      span.innerHTML = '';
    
      var img = document.createElement( 'img' );
      img.setAttribute( 'alt', '' );
      img.setAttribute( 'src', '/media/img/checkbullet.gif' );
      img.setAttribute( 'style', 'margin-left:5px;' );

      span.appendChild( img );
      
      YAHOO.util.Dom.removeClass( parent, 'error' );
    }
    
    this.completeEvent.fire(
    {
      obj: this,
      result: true,
      source: this
    } );
  },
  
  /**
   * Validates this field
   * @param Variant obj
   * @return bool
   */
  validate: function( obj )
  {
    if( this.type == undefined )
    {
      return true;
    }
    
    if( ( this.field.value == '' ) && ( this.type != Denbel.util.FormValidator.TYPE_REQUIRED ) )
    {
      this.setValid();
      return true;
    }
    
    this.inProgress = true;
    this.isCompleted = false;
    this.error = false;
    var span = null;
    
    if( this.parent.config.get( 'errorContainer' ) )
    {
      span = YAHOO.util.Dom.get( this.parent.config.get( 'errorContainer' ) );
    }
    else
    {
      span = YAHOO.util.Dom.get( this.field.getAttribute( 'id' ) + '-info' );
    }

    span.innerHTML = '';
    
    if( !this.parent.config.get( 'errorContainer' ) )
    {
      YAHOO.util.Dom.setStyle( span, 'display', '' );
      YAHOO.util.Dom.setStyle( span, 'opacity', '0.9' );
      YAHOO.util.Dom.setX( span, YAHOO.util.Dom.getRegion( this.field ).right );
      
      var img = document.createElement( 'img' );
      img.setAttribute( 'alt', '' );
      img.setAttribute( 'src', '/media/img/spinner.gif' );
      img.setAttribute( 'style', 'margin-left:5px;' );
      span.appendChild( img );
    }
    
    var mainEl = this.field;
    
    if( this.container )
    {
      mainEl = this.container;
    }
    
    var parent = mainEl.parentNode;
    
    if( Denbel.util.FormValidator.exec( this, false, obj ) )
    {
      if( this.type != 'remote' )
      {
        this.setValid();
        return true;
      }
    }
    else
    {
      if( this.type != 'remote' )
      {
        this.setError();
        return false;
      }
    }
  },
  
  /**
  * Converts this class to its string representation
  * @return String
  */
 toString: function()
 {
   return 'Denbel.util.FieldValidator';
 }
};

