/**
 * @projectDescription 表单验证插件
 * @author Poly 89490364a@gmail.com
 * @version 0.2
 * 现在可以同时处理多个表单了。
 * 补充了关联性验证的提示。用relMsg保存关联性验证提示信息。
 * @version 0.1
 * 在包含表单的jQuery对象上调用checkFormAdvance方法，传入一个对象作为参数
 * 
 * 参数选项：
 * group:"jQuery selector"			含有表单元素和提示窗口的元素选择器；若没有提示窗口，则采用弹出窗口提示[可选]；
 * msgBox:"jQuery selector"			提示窗口的元素选择器，用于控制提示窗口样式[当设置group后可选]；
 * msgTxtBox:"jQuery selector"		存放提示文本的元素的选择器，当提示内容不直接放在提示窗口内时使用[当设置msgBox后可选]；
 * btnSubmit:"jQuery selector"		指定提交按钮[可选]；
 * callBack:"function name"			指定在所有验证通过后执行的回调函数，如果该函数返回值为false，则表单不提交[可选];
 * 
 * 需要验证的表单元素属性设置：
 * reg:"String"		用作验证的正则表达式字符串，[必须]，有以下默认值：
 * 		"ID"	---身份证号码应为15～18位数字；
 * 		"email"	---邮箱地址应如：xxx@yyy.zzz格式；
 * 		"msn"	---msn号码应是一个邮箱；
 * 		"date"	---日期应为yyyy-mm-dd格式，分割符可以为：-_/.；
 * 		"qq"	---qq号应为5～10个数字；
 * 		"tel"	---电话号码应为8～12位数字；
 * msg:"String"		验证失败时显示的字符串，[可选]；
 * suc:"String"		验证成功时显示的字符串，[可选]；
 * rel:"jQuery selector"	关联性验证元素选择器，[可选]；
 * relMsg:"String"			关联性验证提示信息，[可选]；
 * 
 * 验证结果提示框元素样式设置：
 * .err:"style"		验证失败时的样式[必须]；
 * .suc:"style"		验证成功时的样式[可选]；
 * 
 */

(function($){
	$.fn.formCheck=function(o)
	{
		o=o?o:{};
		var root=this;
		if(o.group&&o.msgBox)
		{
			$(o.group).each(bindGroup);
		}
        this.each(function(){
            iniFormCheck.call($(this));
        });
		return this;
		/**
		 * 初始化表单检查；
		 * @method
		 */
		function iniFormCheck()
		{
			var that=this;
			this.formItems=$(":input[reg]",this);
			this.formItems.each(iniFormItem);
			this.checkForm=checkForm;
			this.btn=o.btnSubmit?$(o.btnSubmit,this):null;
            if (this.btn) {
                this.btn.click(function(){
                    return that.checkForm()
                })
            }
            else {
            
                this.form = this.is("form") ? this : $("form", this);
                if (this.form.length) {
                    this.form.submit(function(){
                        return that.checkForm()
                    })
                }
            }

		}
		/**
		 * 检查整个表单并调用回调函数
		 * @method
		 */
		function checkForm()
		{
			for(var i=0;i<this.formItems.length;i++)
			{
				if(!this.formItems[i].doTest())
				{return false}
			}
			var formComplete=true;
			var callBack=o.callBack?eval(o.callBack):null;
			if($.isFunction(callBack))
			{
				formComplete=callBack.apply(this);
			}
			return formComplete;
		}
		/**
		 * 初始化每个需要检查的表单；
		 * @method
		 */
		function iniFormItem()
		{
			var that=$(this);
			this.msg=that.attr("msg")||decodeURI("%E4%B8%8D%E8%83%BD%E8%BE%93%E5%85%A5%E7%A9%BA%E5%AD%97%E7%AC%A6%E4%B8%B2");
			this.rel=getRelationEle(that.attr("rel"));
			this.relMsg=that.attr("relMsg")||this.msg;
			this.relPass=true;
			this.reg=that.attr("reg")?getRegExp(that.attr("reg")):null;
			this.func=$.isFunction(eval(that.attr("func")))?eval(that.attr("func")):null;
			this.doTest=doTest;
			this.showTest=showTest;
			that.change(doTest);
		}
		/**
		 * 根据传入的字符串生成正则表达式，内置了部分简单常用表达式。
		 * @param {String} regStr 用于生成正则的字符串;
		 * @return {RegExp} 返回正则表达式
		 */ 
		function getRegExp(regStr)
		{
			var regExp;
			switch (regStr)
			{
				case "ID":
					regExp=/(?:^\d{15}$)|(?:^\d{17}[\dxX]$)/;
					break;//身份证号码应为15～18位数字
				case "email":
					regExp=/^\w+@\w+\.\w{2,4}$/;
					break;//邮箱地址应如：xxx@yyy.zzz格式
				case "msn":
					regExp=/^\w+@\w+\.\w{2,4}$/;
					break;//msn号码应是一个邮箱
				case "date":
					regExp=/^\d{4}([-_\/\.]\d{2}){2}$/;
					break;//日期应为yyyy-mm-dd格式，分割符可以为：-_/.
				case "qq":
					regExp=/^\d{5,10}$/;
					break;//qq号应为5～10个数字
				case "tel":
					regExp=/^\d{8,12}$/;
					break;//电话号码应为8～12位数字
				default:
					regExp=new RegExp(regStr)
					break;
			}
			return regExp;
		}
		/**
		 * 获取相关联的元素,若传入的是字符串，则生成jQuery对象
		 * @param {String,jQuery} selector 
		 */
		function getRelationEle(selector)
		{
			var ele=typeof(selector)=="string"?$(selector,root):selector;
			return selector&&ele.length?ele:null;
		}
		/**
		 * 执行测试，先以正则测试，通过后若有额外的方法，则执行它，并根据返回值显示信息。
		 * @method
		 */
		function doTest()
		{
			if(this.reg.test(this.value))
			{
				this.relPass=this.pass=this.rel&&this.value!=this.rel.val()?false:true;
				this.pass=this.pass&&(!this.func||this.func())?true:false;
			}
			else
			{
				this.pass=false;
			}
			this.showTest();
			return this.pass;
		}
		/**
		 * 显示验证结果
		 * @method
		 */
		function showTest()
		{
			if(!this.pass)
			{
				if(this.msgBox)
				{this.msgBox.showMsg(this.relPass?"err":"relMsg");}
				else
				{alert(this.relPass?this.msg:this.relMsg)}
				this.focus();
				this.select();
			}
			else
			{
				if(this.msgBox)
				{this.msgBox.showMsg("suc");}
			}
		}
		/**
		 * 显示提示信息
		 * @param {String} msgType 提示信息的类型
		 * @method
		 */
		function showMsg(msgType)
		{
			switch (msgType)
			{
				case "suc":
					this.removeClass("err").addClass("suc");
					break;
				case "err":
					this.addClass("err").removeClass("suc");
					break;
				case "relMsg":
					this.addClass("err").removeClass("suc");
					break;
			}
			var msgTxtBox=this.msgTxtBox.length?this.msgTxtBox:this;
			msgTxtBox.html(this[msgType]);
		}
		/**
		 * 按组绑定msgBox
		 * @method
		 */
		function bindGroup()
		{
			var itemForCheck=$(":input[reg]",this)
			var msgBox=$(o.msgBox, this);
			if (itemForCheck[0]&&msgBox[0])
			{
				itemForCheck[0].msgBox = msgBox;
				if(o.msgTxtBox)
				{
					msgBox.msgTxtBox=$(o.msgTxtBox,msgBox);
				}
				msgBox.def=msgBox.msgTxtBox&&msgBox.msgTxtBox.length?msgBox.msgTxtBox.html():msgBox.html();
				msgBox.suc=itemForCheck.attr("suc")||msgBox.def;
				msgBox.err=itemForCheck.attr("msg");
				msgBox.showMsg=showMsg;
			}
		}
	}
})(jQuery);