(function($){

  $.fn.extend({
  
    validate:function(options){
      var options = $.extend({
        debug: false
      }, options); 
      
      var getmeta = function(el) {
        var data = '';
        var m = /({.*})/.exec(el.className);
        if (m) data = m[1];
        if (data.indexOf('{')<0) data='{'+data+'}';
        try {
          return eval('('+data+')')||{};
        } catch(e) {
          return {};
        }
      }
      
      var getelements = function(frm){
        var cache = {};
        return $([]).add(frm.elements).filter(":input").not(":submit, :reset, :image, [disabled]").filter(function() {
          if (this.name in cache) return false;
          cache[this.name] = true;
          return true;
        });
      }
      
      var methods = {
        
        req: function(el,value,meta,frm){
          switch( el.nodeName.toLowerCase() ) {
            case 'select':
              var options = $("option:selected", el);
              return options.length > 0 && ( el.type == "select-multiple" || ($.browser.msie && !(options[0].attributes['value'].specified) ? options[0].text : options[0].value).length > 0);
            case 'input':
              if ( /radio|checkbox/i.test(el.type) ) 
                return $(frm).find(':input[name='+el.name+']:checked').length > 0;
          }
          return $.trim(value).length > 0;
        },
        phone: function(el,value,meta){
          return /^(\(?\d\d\d\)?)?( |-|\.)?\d\d\d( |-|\.)?\d{4,4}(( |-|\.)?[ext\.]+ ?\d+)?$/i.test(value);
        },
        url: function(el,value){
          return /http:\/\/+(www\.)?[a-z0-9\-\.]{3,}\.[a-z]{2,4}$/i.test(value);
        },
        email: function(el,value){
          return /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}/i.test(value);
        },
        number: function(el,value){
          return /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/i.test(value);
        },
        zip: function(el,value){
          return /^((\d{5}-\d{4})|(\d{5}))?$/i.test(value);
        },
        postal: function(el,value){
          return /^((\d{5}-\d{4})|(\d{5})|([a-z]\d[a-z]\s?\d[a-z]\d))$/i.test(value);
        },
        date: function(el,value){
          return /^(((((0[13578])|([13578])|(1[02]))[\-\/\s]?((0[1-9])|([1-9])|([1-2][0-9])|(3[01])))|((([469])|(11))[\-\/\s]?((0[1-9])|([1-9])|([1-2][0-9])|(30)))|((02|2)[\-\/\s]?((0[1-9])|([1-9])|([1-2][0-9]))))[\-\/\s]?\d{4})(\s(((0[1-9])|([1-9])|(1[0-2]))\:([0-5][0-9])((\s)|(\:([0-5][0-9])\s))([am|pm]{2,2})))?$/i.test(value);
        },
        regexp: function(el,value,params){
          return new RegExp(params.regex).test(value);
        },
        numeric: function(el,value){
          return /[^0-9]/i.text(value);
        },
        alphanumeric: function(el,value){
          return /[^a-z0-9]/i.text(value);
        },
        alpha: function(el,value){
          return /[^a-z]/i.text(value);
        },
        alnumhyphen: function(el,value){
          return /[^a-z0-9\-_]/i.text(value);
        },
        nosel: function(el,value,params){
          return (el.selectedIndex!=params.index);
        }
      }
      
      var val = function(frm){
        var b_err = false;
        $.each(getelements(frm),function(){
          var el = this;
          var meta = getmeta(this);
          var b_ok = true;
          var str_msg = meta.message||meta.msg||'Unable to validate [{0}]';
          if(meta.val){
            if(typeof meta.val=='string') meta.val=[meta.val];
            $.each(meta.val,function(i,method){
              var fn = methods[method]||eval(method)||null;
              if(fn) b_ok = fn(el,el.value,meta.params||{},frm);
              else b_ok = false;
              if(!b_ok) return false;
            });
          }
          if(!b_ok) {
            b_err = true;
            alert(str_msg.replace('{0}',el.name));
            $(this).focus();
            return false;
          }
        });
        return !b_err; 
      }
     
      return this.each(function(){
        $(this).bind("submit",function(){
          var b_ok = val(this);
          /*
          if (formRules.length == 0 || performCheck(this.id, formRules,'jsVar')){
            return ((typeof config.onOk == "function")?config.onOk(this):true);
          }else{
            showErrors(this);
            return ((typeof config.onError == "function")?config.onError(this):false);
          }
          */
          return b_ok;
        });
      });
    }
  });
  $(document).ready(function(){ $('form.validate').validate(); });
})(jQuery);