var FormValidate =
{
  title: 'Some fields are invalid:',
  containers: $H(),
  badValidators: $A(),
  singleValidatorHandler: null,

  clearError: function()
  {
    for(var i = 0; i < this.badValidators.length; i++)
    {
      this.badValidators[i].clearError();
    }
    var gw = $('globuleWarning');
    if(gw)
        gw.hide();
  },

  validate: function(containerForm, showError, errorHandler)
  {  	
    showError = (showError == null || typeof(showError) == 'undefined')?true:showError;
    try
    {
      this.clearError();
      try
      {       		   
	    this.badValidators = FormValidate.Validators.reject(function(validator)
        {			
                	        
		  if((validator.options.containerForm != null && validator.options.containerForm != containerForm) || validator.isValid())
          {
            return true;
          }
          else
          {
            if(showError)
              validator.setError();
            return false;
          }
        });
                
      }
      catch(e)
      {
        console.debug(e);
        return false;
      }
      if(this.badValidators.length > 0)
      {
        if(errorHandler && typeof(errorHandler) != 'undefined')
        {
          errorHandler(containerForm, showError, this.badValidators);
        }
        else
          this.displayError(showError, this.badValidators);
        return false;
      }
      else
        return true;
    }
    catch(e)
    {
      console.error("Can't validate form!", e);
      return false;
    }
  },

  displayError: function(showError, validators)
  {
    if(!showError)
      return;
    var gw = $('globuleWarning');
    var p = (gw)?'<li>':'* ';
    var s = (gw)?'</li>':'\n';
    var str = this.title+((gw)?'<br/><ul>':'\n');
    validators.each(function(validator)
    {
      str += p + validator.options.message + s;
    });
    if(gw)
    {
        gw.update(str+'</ul>');
        gw.show();
        new Effect.ScrollTo(gw);
    }
    else
        alert(str);

  },

  buildValidators: function(containerForm)
  {
    if(this.containers.keys().indexOf(containerForm) >= 0)
    {
      var validators = this.containers.get(containerForm);
      validators.each(function(validator)
      {
        FormValidate.Validators.unregister(validator);
      });
    }
    this.containers.set(containerForm, $A())

    var elements = $A($(containerForm).descendants());

    elements.each(function(element)
    {
      var vs = FormValidate.Validators.createValidators(containerForm, element);
      if(vs != null)
      {
        vs.each(function(v){this.containers.get(containerForm).push(v);}.bind(this));
      }
    }.bind(this));
  },

  validateOnChange: function(containerForm, handler)
  {
    this.singleValidatorHandler = handler;
    FormValidate.Validators.each(function(v){
      if(containerForm == null || v.options.containerForm == containerForm)
        v.bindEvents();
    });
  },

  stopValidateOnChange: function(containerForm)
  {
    this.singleValidatorHandler = null;
    FormValidate.Validators.each(function(v){
      if(containerForm == null || v.options.containerForm == containerForm)
        v.clearEvents();
    });
  }
};

FormValidate.Validators =
{
  validators: [],

  _each: function(iterator)
  {
    this.validators._each(iterator);
  },

  register: function(validator)
  {
    if(!this.include(validator))
      this.validators.push(validator);
  },

  unregister: function(validator)
  {
    this.validators = this.validators.without(validator);
  },

  createValidators: function(containerForm, element)
  {
    var a = Element.readAttribute(element, 'fv:validators');
    if(a != null)
    {
      var v = $A(a.split(','));
      var vs = $A();
      v.each(function(validator){
        vs.push(eval(validator + '.parseElement(containerForm, element)'));
      });
      return vs;
    }
    return null;
  },

  clear: function()
  {
    this.each(function(validator){ validator.unregister(); });
  }
};

Object.extend(FormValidate.Validators, Enumerable);

Validator = function() {};
Validator.parseDefaultOptions = function(containerForm, element)
{
  var opts =
  {
    containerForm: containerForm
  };
  var a = Element.readAttribute(element, 'fv:message');
  if(a != null)
    opts.message = a;
  a = Element.readAttribute(element, 'fv:errorClass');
  if(a != null)
    opts.errorClass = a;

  a = Element.readAttribute(element, 'fv:moreOptions');
  if(a != null)
  {
    a = eval('(' + a + ')');
    Object.extend(opts, a);
  }
  return opts;
}

Validator.prototype =
{
  _events: null,
  _values: null,
  options: null,
  element: null,

  register: function()
  {
    FormValidate.Validators.register(this);
  },

  unregister: function()
  {
    FormValidate.Validators.unregister(this);
  },

  bindEvents: function()
  {
    if(this._events)
      return;

    this._events = $A();
    var o = Element.readAttribute(this.element, 'fv:container');
    var elements = $A();
    if(o)
    {
      var inputs = $A(this.element.getElementsByTagName('INPUT'));
      var selects = $A(this.element.getElementsByTagName('SELECT'));
      var textAreas = $A(this.element.getElementsByTagName('TEXTAREA'));
      elements = $A(inputs.concat(selects.concat(textAreas)));
    }
    else
      elements.push(this.element);

    elements.each(function(el)
    {
      var evt = {target: el};
      switch(el.tagName)
      {
        case 'INPUT':
        switch($(el).readAttribute('type').toLowerCase())
        {
          case 'radio': case 'checkbox':
          evt.type = 'change';
          break;
          case 'text':
          evt.type = 'blur';
          break;
        }
          break;
        case 'SELECT':
          evt.type = 'change';
          break;
        case 'TEXTAREA':
          evt.type = 'blur';
          break;
      }
      if(evt.type)
      {
        evt.handler = this._validate.bind(this);
        Event.observe(evt.target, evt.type, evt.handler);
        this._events.push(evt);
      }
    }.bind(this));
  },

  clearEvents: function()
  {
    if(this._events)
    {
      this._events.each(function(evt){
        Event.stopObserving(evt.target, evt.type, evt.handler);
      });
      this._events = null;
    }
  },

  _validate: function()
  {	
	var status = this.isValid();
    if(FormValidate.singleValidatorHandler)
      FormValidate.singleValidatorHandler(this, status);
    else
      this[(status)?'clearError':'setError']();
  },

  _processNode: function(node)
  {
    if(node.tagName == 'INPUT' && (node.type.toLowerCase() == 'image' || node.type.toLowerCase() == 'button'))
      return;
    var o = Element.readAttribute(node, 'fv:exclude');
    if(typeof(o) != 'undefined' && o == 'true')
      return;
    this._values.push(this.getValue(node));
  },

  setOptions: function(validatorOptions, options)
  {
    this.options =
    {
      message: 'Field is invalid',
      containerForm: null,
      errorClass: null
    };
    Object.extend(this.options, validatorOptions || {});
    Object.extend(this.options, options || {});
  },
  getValue: function(node)
  {    	
	if(node.tagName == 'INPUT' && (node.type.toLowerCase() == 'radio' || node.type.toLowerCase() == 'checkbox'))
    {
      if(node.checked)
        return (typeof(node.value) != 'undefined')?node.value.strip():true;
      else
        return null;
    } 
	else if(node.type == 'select-multiple')
	{
		var selectedValues = new Array();
    	$A(node.options).each(function(e) {
			if(e.selected)
				selectedValues.push(e.value);
		});
    	return selectedValues;
    }
    else
      return $F(node).strip();
  },
  getValues: function(container)
  {
    var e = ($(container) || this.element);
    var o = Element.readAttribute(e, 'fv:container');
    this._values = [];
    if(typeof(o) != 'undefined' && o == 'true')
    {
      var inputs = $A(e.getElementsByTagName('INPUT'));
      var selects = $A(e.getElementsByTagName('SELECT'));
      var textAreas = $A(e.getElementsByTagName('TEXTAREA'));

      inputs.each(this._processNode.bind(this));

      selects.each(this._processNode.bind(this));

      textAreas.each(this._processNode.bind(this));

      return this._values;
    }
    else
    {
      this._values.push(this.getValue(e));      
      return this._values;
    }
  },
  destroy: function()
  {
    this.unregister();
  },
  setError: function()
  {
    if(this.options.errorClass != null)
      Element.addClassName(this.element, this.options.errorClass);
  },
  clearError: function()
  {
    if(this.options.errorClass != null)
      Element.removeClassName(this.element, this.options.errorClass);
  }
};

RequiredValidator = Class.create();
RequiredValidator.parseElement = function(containerForm, element)
{
  var opts = Validator.parseDefaultOptions(containerForm, element);
  var a = Element.readAttribute(element, 'fv:required_message');
  if(a != null)
    opts.message = a;
  a = Element.readAttribute(element, 'fv:minCount');
  if(a != null)
    opts.minCount = a;
  a = Element.readAttribute(element, 'fv:maxCount');
  if(a != null)
    opts.maxCount = a;
  a = Element.readAttribute(element, 'fv:minLength');
  if(a != null)
    opts.minLength = a;
  a = Element.readAttribute(element, 'fv:maxLength');
  if(a != null)
    opts.maxLength = a;
  return new RequiredValidator(element, opts);
}

RequiredValidator.prototype = Object.extend(new Validator(),
{
  initialize: function(element, options)
  {
    this.element = $(element);

    var opts =
    {
      message: 'Field ' + this.element.id + ' is required',
      minCount: 1,
      maxCount: -1,
      minLength: -1,
      maxLength: -1
    };
    this.setOptions(opts, options);
    this.register();
  },
  isValid: function()
  {
    var v = this.getValues();
    /*if(v.length <= 0)
      return true;*/
            
    var count = 0;
    for(var i = 0; i < v.length; i++)
    {
      if(v[i] != null && v[i].length > 0)
      {
        if((this.options.minLength >= 0 && v[i].length < this.options.minLength) || (this.options.maxLength >= 0 && v[i].length > this.options.maxLength))
          return false;
        count++;
      }
    }
    if(count >= this.options.minCount && (this.options.maxCount < 0 || count <= this.options.maxCount))
      return true;
    else
      return false;
  }
});

PatternValidator = Class.create();
PatternValidator.parseElement = function(containerForm, element)
{
  var opts = Validator.parseDefaultOptions(containerForm, element);
  var a = Element.readAttribute(element, 'fv:pattern_message');
  if(a != null)
    opts.message = a;
  a = Element.readAttribute(element, 'fv:pattern');
  if(a != null)
  {
    if(PatternValidator.Patterns[a])
      opts.pattern = PatternValidator.Patterns[a];
    else
      opts.pattern = a;
  }
  a = Element.readAttribute(element, 'fv:reOptions');
  if(a != null)
    opts.reOptions = a;

  return new PatternValidator(element, opts);
}

PatternValidator.Patterns =
{
  http: "http:\\/\\/([_a-z\\d\\-]+(\\.[_a-z\\d\\-]+)+)(([_a-z\\d\\-\\\\\\.\\/]+[_a-z\\d\\-\\\\\\/])+)*",
  email: "^[\\w\\.=\\+-]+@[\\w\\.-]+\\.[\\w\\.-]{2,4}$",
  lettersonly: "^[a-zA-Z]*$",
  alphanumeric: "^\\w*$",
  integer: "^-?\\d\\d*$",
  positiveinteger: "^\\d\\d*$",
  number: "^-?(\\d\\d*\\.\\d*$)|(^-?\\d\\d*$)|(^-?\\.\\d\\d*$)",
  decimal: "^-?(\\d\\d*(\\,|\\.)\\d*$)|(^-?\\d\\d*$)|(^-?\\.\\d\\d*$)",
  filepath_pdf: "\\.([pP][dD][fF])$",
  filepath_jpg_gif_png: "\\.([pP][nN][gG])|([gG][iI][fF])|([jJ][pP][eE]?[gG])$",
  filepath_jpg: "\\.([jJ][pP][eE]?[gG])$",
  filepath_png: "\\.([pP][nN][gG])$",
  filepath_zip: "\\.([zZ][iI][pP])$"
};

PatternValidator.prototype = Object.extend(new Validator(),
{
  initialize: function(element, options)
  {
    this.element = $(element);

    var opts =
    {
      message: 'Field ' + this.element.id + ' is not properly formated',
      pattern: '',
      reOptions: ''
    };
    this.setOptions(opts, options);
    this.register();
    this.re = new RegExp(this.options.pattern, this.options.reOptions);
  },
  isValid: function()
  {
      var v = this.getValue(this.element);
      if(v == null || v.length <= 0)
        return true;
      return this.re.test(v);
  }
});

DateValidator = Class.create();
DateValidator.parseElement = function(containerForm, element)
{
  var opts = Validator.parseDefaultOptions(containerForm, element);
  var a = Element.readAttribute(element, 'fv:date_message');
  if(a != null)
    opts.message = a;
  a = Element.readAttribute(element, 'fv:dateFormat');
  if(a != null)
    opts.dateFormat = a;
  a = Element.readAttribute(element, 'fv:minDate');
  if(a != null)
    opts.minDate = a;
  a = Element.readAttribute(element, 'fv:maxDate');
  if(a != null)
    opts.maxDate = a;

  return new DateValidator(element, opts);
}

DateValidator.prototype = Object.extend(new Validator(),
{
  _monthDays: [31,29,31,30,31,30,31,31,30,31,30,31],

  _checkDate: function(year, month, day)
  {
    if(isNaN(year) || isNaN(month) || isNaN(day))
      return false;
      
    var y = parseInt(year, 10);
    if(y < 100)
      y += 1900;
    var m = parseInt(month, 10);
    var d = parseInt(day, 10);

    if(m < 1 || m > 12 || y < 0 || d < 1 || d > 31 || d > this._monthDays[m-1])
      return false;
    if(m == 2 && !((y%4 != 0 || (y%100 == 0 && y%400 != 0))?(d <= 28):(d <= 29)))
      return false;
    
    if(this.options.maxDate)
    {
        if(this.options.maxDate.getDate)
        {
        }
    	else if(this.options.maxDate == 'now')
    	{
    		this.options.maxDate = new Date();
    		this.options.maxDate.setHours(0);
    		this.options.maxDate.setMinutes(0);
    		this.options.maxDate.setSeconds(0);
    	}
    	else
    	{
    		var match = this.options.maxDate.match(/(\d{4,4})-(\d{2,2})-(\d{2,2})/);
    		this.options.maxDate = new Date(match[1], match[2]-1, match[3]);
    	}
    	
    	var date = new Date(year, month, day);
    	
    	if(date > this.options.maxDate)
    		return false;
    }
    
    if(this.options.minDate)
    {
        if(this.options.minDate.getDate)
        {
        }
    	else if(this.options.minDate == 'now')
    	{
    		this.options.minDate = new Date();
    		this.options.minDate.setHours(0);
    		this.options.minDate.setMinutes(0);
    		this.options.minDate.setSeconds(0);
    	}
    	else
    	{
    		var match = this.options.minDate.match(/(\d{4,4})-(\d{2,2})-(\d{2,2})/);
    		this.options.minDate = new Date(match[1], match[2]-1, match[3]);
    	}
    	
    	var date = new Date(year, month-1, day);
    	
    	if(date < this.options.minDate)
    		return false;
    }
    
    return true;
  },

  initialize: function(element, options)
  {
    this.element = $(element);
    var opts =
    {
      message: 'Date is invalid',
      dateFormat: 'D/M/Y',
      minDate: null,
      maxDate: null
    };
    this.setOptions(opts, options);
    this.register();
    this.fieldOrder = [0,0,0];
    var o = 1;
    for(var i = 0; i < this.options.dateFormat.length; i++)
    {
      var c = this.options.dateFormat.charAt(i);
      if(c == 'D')
        this.fieldOrder[2] = o++;
      else if(c == 'M')
        this.fieldOrder[1] = o++;
      else if(c == 'Y')
        this.fieldOrder[0] = o++;
    }
    this.fieldCount = o-1;
    this.re = new RegExp('^'+this.options.dateFormat.replace('D', '([0-9]{1,2})').replace('M', '([0-9]{1,2})').replace('Y', '([0-9]{2,4})')+'$');
  },
  
  isValid: function()
  {
    var v = this.getValues(this.element);
    if(v == null || v.length <= 0)
      return true;
    if(v.length == 1)
    {
      v = v[0];
      if(v.length <= 0)
        return true;
      var match = this.re.exec(v);
      if(match != null)
        return this._checkDate(
          match[this.fieldOrder[0]],
          match[this.fieldOrder[1]],
          match[this.fieldOrder[2]]);
      else
        return false;
    }
    else
    {
      if(v[0].length <= 0 && v[1].length <= 0 && v[2].length <= 0)
        return true;
      return this._checkDate(v[this.fieldOrder[0]-1],v[this.fieldOrder[1]-1],v[this.fieldOrder[2]-1]);
    }
	
  }
});

DateComparator = Class.create();
DateComparator.parseElement = function(containerForm, element)
{
  var opts = Validator.parseDefaultOptions(containerForm, element);
  var a = Element.readAttribute(element, 'fv:date_comparator_message');
  if(a != null)
    opts.message = a;
  a = Element.readAttribute(element, 'fv:dateFormat');
  if(a != null)
    opts.dateFormat = a;
  a = Element.readAttribute(element, 'fv:compareTo');
  if(a != null)
    opts.compareTo = a;

  return new DateComparator(element, opts);
}

DateComparator.prototype = Object.extend({}, DateValidator.prototype);
DateComparator.prototype = Object.extend(DateComparator.prototype,
{
  isValid: function()
  {
    var vs1 = this.getValues(this.element);
    var vs2 = this.getValues(this.options.compareTo);

    if(vs1.length <= 0 || vs2.length <= 0)
      return true;

    if(vs1.length == 1)
    {
      var match = this.re.exec(vs1[0]);
      if(match == null)
        return true;
      vs1 = [match[1],match[2],match[3]];
    }
    if(vs2.length == 1)
    {
      var match = this.re.exec(vs2[0]);
      if(match == null)
        return true;
      vs2 = [match[1],match[2],match[3]];
    }
    if(! this._checkDate(
      vs1[this.fieldOrder[0]-1],
      vs1[this.fieldOrder[1]-1],
      vs1[this.fieldOrder[2]-1]))
      return true;

    if(! this._checkDate(
      vs2[this.fieldOrder[0]-1],
      vs2[this.fieldOrder[1]-1],
      vs2[this.fieldOrder[2]-1]))
      return true;

    var date1 = Date.parse(vs1[this.fieldOrder[0]-1] + '/' + vs1[this.fieldOrder[1]-1] + '/' + vs1[this.fieldOrder[2]-1]);
    var date2 = Date.parse(vs2[this.fieldOrder[0]-1] + '/' + vs2[this.fieldOrder[1]-1] + '/' + vs2[this.fieldOrder[2]-1]);

    if(date1 > date2) return false;
    else return true;
  },
  setError: function()
  {
    if(this.options.errorClass != null)
    {
      Element.addClassName(this.element, this.options.errorClass);
      Element.addClassName(this.options.compareTo, this.options.errorClass);
    }
  },
  clearError: function()
  {
    if(this.options.errorClass != null)
    {
      Element.removeClassName(this.element, this.options.errorClass);
      Element.removeClassName(this.options.compareTo, this.options.errorClass);
    }
  }
});

SmartyDateValidator = Class.create();
SmartyDateValidator.parseElement = function(containerForm, element)
{
  var opts = Validator.parseDefaultOptions(containerForm, element);
  var a = Element.readAttribute(element, 'fv:date_message');
  if(a != null)
    opts.message = a;
  a = Element.readAttribute(element, 'fv:dateFormat');
  if(a != null)
    opts.dateFormat = a;

  return new SmartyDateValidator(element, opts);
}

SmartyDateValidator.prototype = Object.extend({}, DateValidator.prototype);
SmartyDateValidator.prototype = Object.extend(SmartyDateValidator.prototype,
{
  isValid: function()
  {
    var vs = this.getValues();
    if(vs.length < this.fieldCount)
      return false;
    var empty = true;
    if(this.fieldCount > 0 && vs[0] != null && vs[0].length > 0)
      empty = false;
    if(this.fieldCount > 1 && vs[1] != null && vs[1].length > 0)
      empty = false;
    if(this.fieldCount > 2 && vs[2] != null && vs[2].length > 0)
      empty = false;
    if(empty)
      return true;

    return this._checkDate(
      (this.fieldCount > 0)?vs[this.fieldOrder[0]-1]:1,
      (this.fieldCount > 1)?vs[this.fieldOrder[1]-1]:1,
      (this.fieldCount > 2)?vs[this.fieldOrder[2]-1]:1);
  }
});

SmartyDateComparator = Class.create();
SmartyDateComparator.parseElement = function(containerForm, element)
{
  var opts = Validator.parseDefaultOptions(containerForm, element);
  var a = Element.readAttribute(element, 'fv:date_comparator_message');
  if(a != null)
    opts.message = a;
  a = Element.readAttribute(element, 'fv:dateFormat');
  if(a != null)
    opts.dateFormat = a;
  a = Element.readAttribute(element, 'fv:compareTo');
  if(a != null)
    opts.compareTo = a;

  return new SmartyDateComparator(element, opts);
}

SmartyDateComparator.prototype = Object.extend({}, DateValidator.prototype);
SmartyDateComparator.prototype = Object.extend(SmartyDateComparator.prototype,
{
  isValid: function()
  {
    var vs1 = this.getValues(this.element);
    var vs2 = this.getValues(this.options.compareTo);

    if(vs1.length < 3 || vs2.length < 3)
      return false;
    if((vs1[0] == null || vs1[0].length < 1) || (vs1[0] == null || vs1[2].length < 1) || (vs1[2] == null || vs1[2].length < 1))
      return true;
    if((vs2[0] == null || vs2[0].length < 1) || (vs2[0] == null || vs2[2].length < 1) || (vs2[2] == null || vs2[2].length < 1))
      return true;
    if(! this._checkDate(
      vs1[this.fieldOrder[0]-1],
      vs1[this.fieldOrder[1]-1],
      vs1[this.fieldOrder[2]-1]))
      return true;

    if(! this._checkDate(
      vs2[this.fieldOrder[0]-1],
      vs2[this.fieldOrder[1]-1],
      vs2[this.fieldOrder[2]-1]))
      return true;

    var date1 = Date.parse(vs1[this.fieldOrder[0]-1] + '/' + vs1[this.fieldOrder[1]-1] + '/' + vs1[this.fieldOrder[2]-1]);
    var date2 = Date.parse(vs2[this.fieldOrder[0]-1] + '/' + vs2[this.fieldOrder[1]-1] + '/' + vs2[this.fieldOrder[2]-1]);

    if(date1 > date2) return false;
    else return true;
  },
  setError: function()
  {
    if(this.options.errorClass != null)
    {
      Element.addClassName(this.element, this.options.errorClass);
      Element.addClassName(this.options.compareTo, this.options.errorClass);
    }
  },
  clearError: function()
  {
    if(this.options.errorClass != null)
    {
      Element.removeClassName(this.element, this.options.errorClass);
      Element.removeClassName(this.options.compareTo, this.options.errorClass);
    }
  }
});

DistinctValidator = Class.create();
DistinctValidator.parseElement = function(containerForm, element)
{
  var opts = Validator.parseDefaultOptions(containerForm, element);
  var a = Element.readAttribute(element, 'fv:distinct_message');
  if(a != null)
    opts.message = a;

  return new DistinctValidator(element, opts);
}

DistinctValidator.prototype = Object.extend(new Validator(),
{
  elements: null,
  values: null,

  initialize: function(element, options)
  {
    this.element = $(element);
    this.setOptions({}, options);
    this.elements = $A();
    this.values = $H();
    this.register();
  },

  isValid: function()
  {
    this.values = $H();
    var ret = true;
    for(var i = 0; i < this.elements.length; i++)
    {
      var v = this.getValue(this.elements[i]);
      if(v != null && v.length > 0)
      {
        if(typeof(this.values.get(v)) != 'undefined')
        {
          this.values.get(v).push(this.elements[i]);
          ret = false;
        }
        else
          this.values.set(v, [this.elements[i]]);
      }
    }
    return ret;
  },

  setError: function()
  {
    if(this.options.errorClass != null)
    {
      var ec = this.options.errorClass;
      this.values.each(function(v)
      {
        if(v[1].length > 1)
        {
          v[1].each(function(vv)
          {
            vv.addClassName(ec);
          });
        }
      });
    }
  },

  clearError: function()
  {
    if(this.options.errorClass != null)
    {
      var ec = this.options.errorClass;
      this.values.each(function(v)
      {
        if(v[1].length > 1)
        {
          v[1].each(function(vv)
          {
            vv.removeClassName(ec);
          });
        }
      });
    }
  },

  addElement: function(element)
  {
    this.elements.push($(element));
  },

  addElementsByName: function(container, name)
  {
    var c = $(container);
    var e = c.getElementsByTagName('INPUT');
    for(var i = 0; i < e.length; i++)
    {
      if(e[i].name == name)
        this.addElement(e[i]);
    }
    e = c.getElementsByTagName('SELECT');
    for(var i = 0; i < e.length; i++)
    {
      if(e[i].name == name)
        this.addElement(e[i]);
    }
    e = c.getElementsByTagName('TEXTAREA');
    for(var i = 0; i < e.length; i++)
    {
      if(e[i].name == name)
        this.addElement(e[i]);
    }
  },

  removeElement: function(element)
  {
    this.elements = this.elements.reject(function(e)
    {
      if(e == element)
        return true;
      return false;
    });
  },

  removeElementsFromContainer: function(container)
  {
    var c = $(container);
    var e = c.getElementsByTagName('INPUT');
    for(var i = 0; i < e.length; i++)
    {
        this.removeElement(e[i]);
    }
    e = c.getElementsByTagName('SELECT');
    for(var i = 0; i < e.length; i++)
    {
        this.removeElement(e[i]);
    }
    e = c.getElementsByTagName('TEXTAREA');
    for(var i = 0; i < e.length; i++)
    {
        this.removeElement(e[i]);
    }
  }
});


CompareValidator = Class.create();
CompareValidator.parseElement = function(containerForm, element)
{
  var opts = Validator.parseDefaultOptions(containerForm, element);
  var a = Element.readAttribute(element, 'fv:compare_message');
  if(a != null)
    opts.message = a;
  a = Element.readAttribute(element, 'fv:compareTo');

  return new CompareValidator(element, a, opts);
}

CompareValidator.prototype = Object.extend(new Validator(),
{
  initialize: function(source, compareTo, options)
  {
    this.element = $(source);
    this.compareTo = $(compareTo);
    this.setOptions({}, options);
    this.register();
  },

  isValid: function()
  {
    var v1 = this.getValue(this.element);
    var v2 = this.getValue(this.compareTo);
    if((v1 == null || v1.length <= 0) && (v2 == null || v2.length <= 0))
      return true;
    return (v1 == v2);
  },

  setError: function()
  {
    if(this.options.errorClass != null)
    {
      Element.addClassName(this.element, this.options.errorClass);
      Element.addClassName(this.compareTo, this.options.errorClass);
    }
  },
  clearError: function()
  {
    if(this.options.errorClass != null)
    {
      Element.removeClassName(this.element, this.options.errorClass);
      Element.removeClassName(this.compareTo, this.options.errorClass);
    }
  }
});

RequiredIfValidator = Class.create();
RequiredIfValidator.parseElement = function(containerForm, element)
{
  var opts = Validator.parseDefaultOptions(containerForm, element);
  var a = Element.readAttribute(element, 'fv:compare_message');
  if(a != null)
    opts.message = a;
  a = Element.readAttribute(element, 'fv:against');
  if(a != null)
    opts.against = a;

  a = Element.readAttribute(element, 'fv:values');
  if(a != null)
    opts.values = a.split(',');

  return new RequiredIfValidator(element, opts);
}
RequiredIfValidator.prototype = Object.extend({}, RequiredValidator.prototype);
RequiredIfValidator.prototype._isValid = RequiredIfValidator.prototype.isValid;
RequiredIfValidator.prototype = Object.extend(RequiredIfValidator.prototype,
{
  isValid: function()
  {
    var v = this.getValues($(this.options.against)).compact().reduce();
    console.debug(v);
    if(v == null || v.length == 0)
      return true;
    for(var i = 0; i < this.options.values.length; i++)
    {

      if(v == this.options.values[i])
        return this._isValid();
    }
    return true;
  }
});
