$.fn.highlight = function () {
  for (var i = 0; i < 3; i++) {
    this.fadeOut(80);
    this.fadeIn(80)
  }
};

$.validation = {
  validate_luhn: function(value, element) {
    value = value.replace(/\D/g, '');
    var length = value.length;
    var parity = length % 2;
    var sum = 0;
    for (var i = 0; i < length; i++) {
      var chk = value.charAt(i) * ((i % 2 == parity) ? 2 : 1);
      sum += (chk < 10) ? chk : (chk - 9);
    }
    return (sum % 10) == 0;
  },
  
  cc_regexp:
    /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/,
  
  validate_cc_num: function(value, element) {
    return this.cc_regexp.test(value.replace(/\D/g, '')) && this.validate_luhn(value);
  },

  validate_cc_exp: function(value, element) {
    var match = value.match(/^(\d{1,2})[\/\.:]?(\d{1,2})$/);
    if (!match)
      return false;
    var m = Number(match[1]);
    var y = Number(match[2]);
    if ((m < 1) || (m > 12) || (y < 9) || (y > 20))
      return false;

    return true;
  },
  
  validate_email: function(value, element) {
    return /^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(value);
  },
  
  validate_phone: function(value, element) {
    return /^(0(?:[23489]|(?:5[02479])|(?:7[23467])))\D*(\d{7})$/.test(value);    
  },

  validate_id: function(value, element) {
    return /^\d{5,9}$/.test(value) && this.validate_luhn(value);
  },
  
  validate_name: function(value, element) {
    return /^((([a-zא-ת\.\-\']{2,})\s)+)([a-zא-ת\.\-\']{2,})$/.test(value);
  },
  
  validate_terms: function(value, element) {
    return element.checked;
  },
  
  validate: function(value, kind, element) {
    var fn = this['validate_' + kind];
    return fn ? fn.apply(this, [value, element]) : false;
  },
  
  update_error_message: function(valid, kind, element, highlight) {
    var error_msg = $(element).parent().find('.error');
    if (valid) {
      error_msg.hide();
    }
    else {
      error_msg.show();
      if (highlight) {
        error_msg.highlight();
        element.focus();
      }
    }
  },

  update: function() {}
}

$.fn.value = function(v) {
  var ret = null;
  this.each(function () {
    if (v)
      this.value = String(v);
    else
      ret = this.value;
  });
  return ret;
};

$.fn.validate = function(checkOnly) {
  var size = $(this).length;
  var all_valid = true;
  for (var i = 0; i < size; i++) {
    var e = $(this).get(i);
    var kind = $(e).attr('validate');
    if (kind) {
      var value = $(e).value();
      var valid = $.validation.validate(value, kind, e);
      
      // highlight only first invalid element
      if (!checkOnly || valid) {
        $.validation.update_error_message(valid, kind, e, all_valid);
      }

      if (!valid) all_valid = false;
    }
  }
  if (!checkOnly)
    $.validation.update();
  return all_valid;
};

$(document).ready(function() {
  $('form.validate').submit(function() {
    return $(this).find('input').validate();
  });
  
  $('form.validate').find('input').change(function () {
    $(this).validate();
  });

  $('form.validate input.checkbox').click(function () {
    $(this).validate();
  });

  $('form.validate input.checkbox').blur(function () {
    $(this).validate();
  });
});