$=function(id) {
  return document.getElementById(id);
}

Array.prototype.indexOf = function(e) {
  var i = this.length;
  for (i; i > -1 && e != this[i]; i--);
  return i;
}

Object.prototype.attachEvent = function (event, handler, use_capture) {
  use_capture = use_capture || false;
  this.addEventListener(event.indexOf('on') == 0 ? event.replace('on', '') : event, handler, use_capture);
}

Validation = function(f){
  this.f = f;
  this.default_class = f.getAttribute('default_css');
  this.error_class = f.getAttribute('error_css');
  if (!this.default_class) this.default_class = 'test';
  if (!this.error_class) this.error_class = 'test';
  this.sbm = null;
  this.rules = ['reg_ex', 'numeric', 'confirm', 'counter'];
  this.initialize();
}

Validation.prototype.confirm = function(value, match, required) {
  val2 = $(match).value;
  if (!value && !val2 && !required) {
    return true;
  }

  return val2 == value;
}

Validation.prototype.reg_ex = function(value, reg_ex, required) {
  if (!value && !required) {
    return true;
  }

  return new RegExp(reg_ex).test(value);
}

Validation.prototype.numeric = function(value, cond, required) {
  if (!value && !required) {
    return true;
  }

  if (!/^[0-9]+$/.test(value)) {
    return false;
  }

  cond = cond.split(',');
  return value >= cond[0]*1 && value <= cond[1]*1;  
}

Validation.prototype.counter = function(value, counter_id, required, id) {
  var maxlength = $(id).getAttribute('maxlength') || 500;
  var curlength = $(id).value.length;
  $(counter_id).innerHTML = curlength+'/'+maxlength;
  return curlength <= maxlength;
}

Validation.prototype.validate = function(id) {
  var value = $(id).value != $(id).getAttribute('default') ? $(id).value : false;
  var required = $(id).getAttribute('required') !== null ? true : false;
  var ok = true;


  if ($(id).type == 'checkbox') {
    value = $(id).checked;
  }  

  if (required && (!value || value == 0)) {
    ok = false;
  }

  // функция проверки по имени
  var rules = $(id).attributes;
  for (var i = 0; i < rules.length; i++) {
    if (this.rules.indexOf(rules[i].name) != -1) {
      var n = rules[i].name;
      var v = rules[i].value;
      if (!eval('this.'+n+'(value, v, required, id)')) {
        ok = false;
      }
    }
  }


  // устанавливаем стиль и флаг ошибки в зависимости 
  // от результата проверки
  if (ok) {
    $(id).className = $(id).getAttribute('native_class');
    // Opera, FF не преобразуют 'false' в bool при последующем стравнении
    $(id).setAttribute('errors', '');
  }
  else {
    $(id).className = this.error_class;
    $(id).setAttribute('errors', true);
  }


  // активируем кнопку submit, если нет ошибок
  var e = this.f.elements;
  this.sbm.disabled = false;

  for (var i = 0; i< e.length; i++) {
    if (e[i].getAttribute('errors')) {
      this.sbm.disabled = true;
      break;
    }
  }
}


Validation.prototype.initialize = function() {
  var e = this.f.elements;
  var has_error = false;

  // при отправке необходимо удалить поля c default,
  // которые могут быть пустыми
  obj = this;
  this.f.attachEvent('onsubmit', new Function('', 'return obj.form_submited(obj)'));  
  
 
  for (var i = 0; i < e.length; i++){
    // вешаем обработчики на поля required & must_validate
    if (e[i].id) { 

      if (e[i].getAttribute('required') === null &&
          e[i].getAttribute('must_validate') === null) continue;
  
      e[i].setAttribute('native_class', e[i].className);

      // IE bug fix: утечка памяти
      var handle_gained = function(obj, id) {
        return function() {obj.focus_gained(obj, id)};
      };

      var handle_lost = function(obj, id) {
        return function() {obj.focus_lost(obj, id)};
      };

      switch(e[i].type) {
        case 'image':
          this.sbm = e[i];
          break;
        case 'textarea':
        case 'password':
        case 'text':      
          e[i].attachEvent('onfocus', handle_gained(obj, e[i].id));
          e[i].attachEvent('onblur', handle_lost(obj, e[i].id));
          e[i].attachEvent('onkeyup', handle_gained(obj, e[i].id));
          break;
        case 'checkbox':
          e[i].attachEvent('onclick', handle_gained(obj, e[i].id));
          break;
        case 'select-one': 
        case 'select':
        case 'file':
          e[i].attachEvent('onchange', handle_gained(obj, e[i].id));
          e[i].check = handle_gained(obj, e[i].id);
          break;
      }


      // условимся, require поля не будут заполнены
      if (e[i].getAttribute('required') !== null && (e[i].value == '' || e[i].value==0)) {
        e[i].setAttribute('errors', true);
        has_error = true;
      } 

      // устанавливаем описание (в поле)
      if (e[i].getAttribute('default') !== null && e[i].value == '') {
        e[i].value = e[i].getAttribute('default');
        e[i].className = this.default_class;
      } 

      // чтобы не выскакивало null в проле, где проверяется эта запись
      if (e[i].getAttribute('default') === null) {
        e[i].setAttribute('default', '');
      }  

    } 
    // понадобится для контроля доступности отправки
    if (e[i].type == 'submit') {
      this.sbm = e[i];
    } 
  } // for

  // если имеются require-поля
  if (has_error) {
    this.sbm.disabled = true;
  }
}

Validation.prototype.form_submited = function(obj) {
  var e = this.f.elements;
  for (var i = 0; i< e.length; i++) {
    if (e[i].getAttribute('default') == e[i].value) {
      e[i].value = '';
    }
    if (e[i].value == '' && e[i].getAttribute('required')) {
      return false;
    }
  }
  
  // если форма без кнопки, можно придумать проверку в этом блоке
  return true;
}

Validation.prototype.focus_gained = function(obj, id) {
  if ($(id).value == $(id).getAttribute('default')) {
    $(id).value = '';
    $(id).className = $(id).getAttribute('native_class');
  }

  obj.validate(id);
}

Validation.prototype.focus_lost = function(obj, id) {

  if ($(id).value == '') {
    $(id).className = obj.default_class;
    $(id).value = $(id).getAttribute('default');
  }

  obj.validate(id);
}