/*
 * CakePHP jQuery Validation Plugin
 * Copyright (c) 2009 Matt Curry
 * www.PseudoCoder.com
 * http://github.com/mcurry/cakephp_plugin_validation
 * http://sandbox2.pseudocoder.com/demo/validation
 *
 * @author      mattc <matt@pseudocoder.com>
 * @license     MIT
 *
 */

(function($) {
  var options = null;
  var errors = [];
  
  $.fn.validate = function(rules, opts) {
    options = $.extend({}, $.fn.validate.defaults, opts);
		
    $.each(opts.watch, function(fieldId) {
      $("#" + opts.watch[fieldId]).change(function() {
        $.fn.validate.ajaxField($(this));
      });
    });
		
    $.each(rules, function(field) {
      var input = $("#" + field);
      var validationRules = this;
      input.blur(function(){
       $.fn.validate.clearError($(this));
	   $.fn.validate.clearValid($(this));
       $.fn.validate.validateField(field,validationRules);
      });
    });

    return this.each(function() {
	  var messageId = options.messageId;
      $(this).submit(function() {
        $.fn.validate.beforeFilter(messageId);
        errors = [];
        $.each(rules, function(field) {
          $.fn.validate.validateField(field,this);
        });       
            
        if(errors.length > 0) {
          $.fn.validate.afterFilter(errors, messageId, options.message, options.click_message);
          return false;
        }
        return true;
      });
    });    
  };
  
  $.fn.validate.validateField = function (field,validationRules) {
    var val = $("#" + field).val();
    var fieldName = $('#' + field).attr('name');
    if(typeof val == "string") {
          val = $.trim(val);
    }

	var valid = true;

	$.each(validationRules, function() {
      //field doesn't exist...skip
      if ($("#" + field).attr("id") == undefined) {
        return true;
      }

      if (this['allowEmpty'] && typeof val == "string" && val == '') {
        return true;
      }

      if (this['allowEmpty'] && typeof val == "object" && val == null) {
        return true;
      }

      if (!$.fn.validate.validateRule(val, this['rule'], this['negate'], fieldName)) {
        errors.push({
          message: this['message'],
          id: $("#" + field).attr("id")
        });
		
        valid = false;
        $.fn.validate.setError(field, this['message']);       

        if (this['last'] === true) {
          return false;
        }
      }      
    });

    if( valid )
    {
      $.fn.validate.setValid(field);
    }
  }

  $.fn.validate.validateRule = function(val, rule, negate, fieldName) {
    if(negate == undefined) {
      negate = false;
    }
        
    //handle custom functions
    if(typeof rule == 'object') {
      if($.fn.validate[rule.rule] != undefined) {
        return $.fn.validate[rule.rule](val, rule.params, fieldName);
      } else {
        return true;
      }
    }

    //handle regex rules
	if(val == null)
	{
		return false;
	}
    val = val.replace(/\n/g, ' ');
    if (negate && val.match(eval(rule))) {
      return false;
    } else if (!negate && !val.match(eval(rule))) {
      return false;
    }
    
    return true;
  };
  
  $.fn.validate.boolean = function(val) {
    return $.fn.validate.inList(val, [0, 1, '0', '1', true, false]);
  };
    
  $.fn.validate.comparison = function(val, params) {
    if(val == "") {
      return false;
    }
    
    val = Number(val);
    if(val == "NaN") {
      return false;
    }
    
    if(eval(val + params[0] + params[1])) {
      return true;
    }
    
    return false;
  };
  
  $.fn.validate.inList = function(val, params) {
    if(params != null) {
      if($.inArray(val, params) == -1) {
        return false;
      }
    }
    
    return true;
  };
  
  $.fn.validate.range = function(val, params) {
    if (val < parseInt(params[0])) {
      return false;
    }
    if (val > parseInt(params[1])) {
      return false;
    }
    
    return true;
  };
  
  $.fn.validate.multiple = function(val, params) {
    if(typeof val != "object" || val == null) {
      return false;
    }
    
    if(params.min != null && val.length < params.min) {
      return false;
    }
    if(params.max != null && val.length > params.max) {
      return false;
    }
    
    if(params["in"] != null) {
      for(i = 0; i < params["in"].length; i ++) {
        if($.inArray(params["in"][i], val) == -1) {
          return false;
        }
      }
    }
    
    return true;
  };
	
	$.fn.validate.ajaxField = function($field) {
		$.fn.validate.clearError($field);
		$.fn.validate.ajaxBeforeFilter($field);

		var data = new Object;
		data[$field.attr("name")] = $field.val();
		$.post(options.root + "js_validate/field/" + $field.attr("id"), data,
			function(validates) {
				$.fn.validate.ajaxAfterFilter($("#" + validates.field));
				if(!validates.result) {
					$.fn.validate.setError(validates.field, validates.message);
				}
			},
			"json");
	}
	
	$.fn.validate.ajaxBeforeFilter = function($field) {
		$field.after("<img class=\"ajax-loader\" src=\"" + options.root + "js_validate/img/ajax-loader.gif\">");
	}

	$.fn.validate.ajaxAfterFilter = function($field) {
		$field.siblings(".ajax-loader").remove();
	}
	
	$.fn.validate.clearError = function($field) {
		if(typeof $field == "string") {
			$field = $("#" + field);
		}
		
		$field.removeClass("form-error")
					.parents("div:first").removeClass("error")
					.children(".error-message").remove();		
	}

  $.fn.validate.setError = function(field, message) {
    $("#" + field).addClass("form-error")
                  .removeClass("form-valid")
                  .parents("div:first").addClass("error").removeClass("valid")
                  .append('<div class="error-message">'  + message +  '</div>')
				  .children(".valid-message").remove();
  };

  $.fn.validate.setValid = function(field) {
    $("#" + field).addClass("form-valid")
                  .parents("div:first").addClass("valid")
				  .append('<div class="valid-message">OK</div>');
  };

  $.fn.validate.clearValid = function(field) {
	field.removeClass("form-valid")
                  .parents("div:first").removeClass("valid")
				  .children(".valid-message").remove();
  };
	
  $.fn.validate.beforeFilter = function(messageId) {
    if(messageId != null) {
      $("#" + messageId).html("")
                                .slideDown();
    }
    
    $(".error-message").remove();
	$(".valid-message").remove();
    $("input").removeClass("form-error").removeClass("form-valid");
    $("div").removeClass("error").removeClass("valid");
  };  

   $.fn.validate.afterFilter = function(errors, messageId, message, click_message) {
    if(messageId != null) {
      var $list = $('<ol></ol>');
      $.each(errors, function( i, error) {
         var $element= $('<li></li>');
         var $link = $('<a href="#' + error.id + '">' + error.message + '</a>')
            .click( function() {
                $('#' + error.id).focus();
                return false;
            })
            .appendTo($element);

         $list.append($element);
      });

      var $content = $('<div class="errors"></div>').html($list);
      if(message != null) {
        $content.prepend('<p><a href="#">' + message.replace( '%s', errors.length ) +
                         '</a></p>');
      }

      $("#" + messageId).html($content).slideDown();
      $("#" + messageId + ' a:first').focus();
    }
  };
})(jQuery);

