/*
*   Form Validation Plugin
*
*   Requires:
*     - jquery >= 1.4
*/

(function($){

   /* Form Validate Plugin ---------------------------------------------
      $('form').validate();
   */
   $.extend($.fn, {
      validate : function (options) {
         var defaults = {
            validate: {
               submit : true,
               blur:    true
            },
            errors: {
               useTitles : true
            },
            classes: {
               success : 'validation-success',
               failed  : 'validation-failed'
            },
            events: {
               success : false,
               failed  : false
            }
         }, options  = $.extend(defaults, options || {});

         return this.each(function() {
            var obj       = this,
                validator = $.data(obj, 'validator');


            if (!validator) {
               validator = new $.validator(options, obj);
               $.data(obj, 'validator', validator);
            }

            validator.inputs().each(function() {
               var f = $(this);
               f.bind('validation-failed', function(event, msg) {
                  var field   = $(this),
                      success = options.classes.success,
                      failed  = options.classes.failed;

                  field.removeClass(success).addClass(failed);

                  switch ($.type(options.events.failed)) {
                     case 'function' : {
                        options.events.failed.call(this, msg);
                        break;
                     }
                     default :
                        var key     = failed + '-message',
                            error   = field.data(key);

                        if (!error) {
                           error = $('<div class="' + failed + '"/>').hide().insertAfter(field);
                           field.data(key, error);
                        }
                        $(error).html(msg).show();
                        break;
                  }
               });


               f.bind('validation-success',  function(event, msg) {
                  $(this).removeClass(options.classes.failed)
                         .addClass(options.classes.success);

                  switch ($.type(options.events.success)) {
                     case 'function' : {
                        options.events.success.call(this, msg);
                        break;
                     }
                     default:
                        var error = $.data(this, options.classes.failed + '-message');
                        if (error)
                           $(error).hide();
                  }
               });

               if (options.validate.blur) {
                  f.blur(function() {
                     validator.field(this);
                  });
               }

            });

            if (options.validate.submit) {
              return $(this).submit(function() {
                  return validator.form()
               });
            }
         });
      }
   });

   // Validator Instance -----------------------------------------------
   $.validator = function(options, form) {
      this.f = form;
      this.init(options);
   };

   $.extend($.validator, {
      defaults : {
         rules : {
            '.required,.mandatory'    : {
               match : function(){
                  var obj = $(this);
                  switch (obj.attr('type')) {
                     case 'radio':    return $('input[name="' + obj.attr('name') + '"]').filter(':checked').length > 0;
                     case 'checkbox': return this[0].checked;
                     default:         return /.+/.test(obj.val());
                  }
               }
            },
            '.validate-email,.valid-email'   : { match : /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i },
            '.validate-digit,.valid-digit'   : { match : /^[0-9]*$/ },
            '.validate-number,.valid-number' : { match : /^(-?[0-9]+((\.|,)[0-9]+)?)?$/ }
         }
      },
      prototype : {
         init: function(options) {
            this.opts = $.extend($.validator.defaults, options);
         },
         inputs : function() {
            return $('input, textarea, select', this.f)
               .filter(':not([type="hidden"])')
               .filter(':not([readonly])')
               .filter(':not([disabled])');
         },
         form: function() {
            var obj   = this,
                valid = true,
                f     = this.inputs();
            // Looking for matched rules
            for (var selector in obj.opts.rules) {
               var callback = obj.opts.rules[selector].match,
                   type     = $.type(callback);

               f.filter(selector).each(function() {
                  var result = false,
                      field  = $(this);
                  switch(type) {
                     case 'function' : result = callback.call(field); break;
                     case 'regexp'   : result = callback.test(field.val()); break;
                  }
                  if (obj.opts.errors.useTitles)
                     msg = field.attr('title') || obj.opts.rules[selector].error || '';
                  field.trigger('validation-' + (result ? 'success' : 'failed') , [msg, selector]);
                  valid = valid && result;
               });
            }
            return valid;
         },
         field: function(field) {
            var obj   = this,
                valid = true,
                field = $(field);

            for (var selector in obj.opts.rules) {
               var callback = obj.opts.rules[selector].match,
                   type     = $.type(callback);
               field.filter(selector).each(function() {
                  var result = false,
                      field  = $(this);
                  switch(type) {
                     case 'function' : result = callback.call(field); break;
                     case 'regexp'   : result = callback.test(field.val()); break;
                  }
                  valid = valid && result;
                  if (obj.opts.errors.useTitles)
                     msg = field.attr('title') || obj.opts.rules[selector].error || '';
                  field.trigger('validation-' + (result ? 'success' : 'failed') , [msg]);
               });
            }
            return valid;
         }
      }
   });

})(jQuery);
