
// assoc array of validators
var validators = {};

// used to split the name into object and field
var nameRE = /(.*)\[(.*)\]/;

jQuery.fn.validate_after = function(watching, timeout) {
  // have to save all watched values
  var val = '';
  $(watching).each(function() {
    val += $(this).val();
  });
  var input_being_validated = this;

  // clear all validation
  $('.validation_error, .validation_ok, .validation_spinner', $(this.parents('.validated_input')[0]) ).hide();

  setTimeout(function() {
    // if none of the watched values have changed, do validation
    var new_val = '';
    $(watching).each(function() {
      new_val += $(this).val();
    });
    if (new_val == val) {
      input_being_validated.validate();
    }
  }, timeout)
}

// do validation
jQuery.fn.validate = function() {

  // split name into field and object
  var name = this.attr('name');
  var object;
  var field;
  var name_arr = nameRE.exec(name);
  name = name_arr[0];
  object = name_arr[1];
  field = name_arr[2];

  hide_all(object, field);  
  // abort if no validators
  if (! validators[object]) return '';

  var value = this.val();
  
 

  // run client side validation
  var client_err = validate_client.apply(this, [object, field, value]);
  if (client_err != '') {
    show_validation_error(object, field, client_err);
    return false;
  }
  
  
  if (validators[object][field].validate_with_server) {
    validate_server.apply(this, [object, field, value].concat(validators[object][field].validate_with_server));
    return false;
  }
  else {
    show_validation_ok.apply(this);
    return true;
  }


  // run all validators associated with object + field
  function validate_client(object, field, value) {    
    var input = this;
    for (var i = 0; i < validators[object][field].length; i++) {
      var validator_info = validators[object][field][i];
      var v = validator_info[0];
      var msg = v.apply(input, validator_info.slice(1));
      if (msg != '') {
        return msg;
      }
    };
    
    // all validators successful
    return '';  
  }

  // run server validation
  function validate_server(object, field, value, id, additional_params) {
    var input_being_validated = this;
    
    $('.validation_spinner', $('#' + object + '_' + field + '_validated_input')).show();
    
    var params = input_being_validated.serialize();
    $.each(additional_params, function(i, param) {
      params += '&' + $('#' + object + '_' + param).serialize();
    });
    
    var ajax = $.ajax({
      type: 'GET',
      dataType: 'html',
      url: '/validation/' + object + '/' + field + id + '?' + params,
      success: function(data, textStatus) {
        // delete reference to this ajax call
        input_being_validated.removeData('validation_running');
        
        $('##{ spinner_id }').hide();
        if (data != '') {
          show_validation_error(object, field, data);
        } else {
          show_validation_ok(object, field)
        }
      }
    });
    input_being_validated.data('validation_running', ajax);
  }

  function show_validation_error(object, field, msg) {
    var wrapper = $('#' + object + '_' + field + '_validated_input');
    $('.validation_error_msg', wrapper).text(msg)
    $('.validation_error', wrapper).show();
    $('.validation_ok, .validation_spinner', wrapper).hide();
  }
  
  function show_validation_ok(object, field) {
    var wrapper = $('#' + object + '_' + field + '_validated_input');
    $('.validation_error, .validation_spinner', wrapper).hide();

    // only show OK checkmark when value != ''
    if ($('#' + object + '_' + field).val() != '') $('.validation_ok', wrapper).show();
  }
  
  function hide_all(object, field) {
    var wrapper = $('#' + object + '_' + field + '_validated_input');
    $('.validation_error, .validation_ok, .validation_spinner', wrapper).hide();
  }
}

// makes sure a checkbox is checked
function validator_acceptance(error_msg) {
  return this.attr('checked') ? '' : error_msg;
}

// makes sure a value exists
function validator_presence(error_msg){
  return (this.val().trim() != '') ? '' : error_msg;
}

function validator_length(min, max, too_short_msg, too_long_msg) {
  var value = this.val().trim();
  if (value.length > 0) {
    if (value.length > max) {
      return too_long_msg;
    }
    if (value.length < min) {
      return too_short_msg;
    }
  }
  return '';
}

function validator_format(error_msg, regexp, allow_blank) {
  if (allow_blank && this.val().trim() == '')
    return '';
  
  if (! regexp.test( this.val().trim() ) ) {
    return error_msg;
  }
  return '';
}
