/**
 * Name: JavaScript Validate Framework
 * Author: Henry Huang, QQ: 192532, Email: hsl1230@yeah.net.
 * Function: JavaScript表单自动验证框架
 * Description: 可以自动验证表单，提供多种提示风格，具有很强的扩展功能，大大减小页面编程的工作量，使得网页更加容易维护。
 *              与JavaScript UI Component, JavaScript Template Engine配合使用可大大提高开发效率和系统性能。
 * License: 本脚本可以自由使用，也可自行更改，但是必须标明更改缘由、位置和以下信息"原作者：Henry Huang, QQ: 192532, Email: hsl1230@yeah.net"。
 * Date: 2006-07-08
 *
 * TODO: 本框架的正在进一步完善中
 *
 * JavaScript Template Engine:    template.js
 * JavaScript Validate Framework: validator.js
 * Javascript UI Component:       supertable.js
 */

// 增加一个名为 trim 的函数作为
// String 构造函数的原型对象的一个方法。
String.prototype.trim = function()
{
    // 用正则表达式将前后空格
    // 用空字符串替代。
    return this.replace(/(^\s*)|(\s*$)/g, "");
}

//规则定义库
PatternDict = new Object();

//pattern的构造函数定义了两个参数
//	rule				-- 可以是正则表达式，也可以是自定义函数，参数是表单项。
//	和
//	msg					-- 可以是String(静态信息)，也可以是自定义函数（动态信息），参数是表单项。
//Pattern中定义了两个方法：
//	validate(el)		-- el为表单项，验证通过返回true, 否则返回false。
//	和
//	getMessage(el)		-- el为表单项，返回验证失败信息。
function Pattern(rule, msg) {
	if (typeof(rule) == "function") {
		this.definedValidate = rule;
	}
	else {
		this.pattern = rule;
		this.definedValidate = ValidatorUtil.definedValidate;
	}
	
	this.validate = ValidatorUtil.validate;
	
	if (typeof(msg) == "function") {
		this.getMessage = function(el) {
			if ("msg" in el) {
				return el.getAttribute("msg");
			}
			else {
				return msg(el);
			}
		};
	}
	else {
		this.msg = msg;
		this.getMessage = ValidatorUtil.getMessage;		
	}	
}

//校验工具类
ValidatorUtil = {
	validate: function(el) {
		if (this.definedValidate(el)) {
			return true;
		}
		else {
			el.showmsg = this.getMessage(el);
			return false;
		}
	},
	
	definedValidate: function(el) {
		return this.pattern.test(el.value);
	},

	getMessage: function(el) {
		if ("msg" in el) {
			return el.getAttribute("msg");
		}
		else {
			return this.msg;
		}
	},
	
	isSafe : function(el) {
		var unSafePattern = new Pattern(/^(([A-Z]*|[a-z]*|\d*|[-_\~!@#\$%\^&\*\.\(\)\[\]\{\}<>\?\\\/\'\"]*)|.{0,5})$|\s/, "");
		return !unSafePattern.validate(el);
	},
	
	checkLimit: function(el) {
		return ValidatorUtil.limit(el.value.length, el.getAttribute('min'),  el.getAttribute('max'));
	},
	msgLimit: function(el) {
		var min = el.getAttribute("min") || 0;
		var max = el.getAttribute("max") || Number.MAX_VALUE;
		return "必须为" + min + "到" + max + "个字符";
	},
	
	checkLimitB: function(el) {
		return ValidatorUtil.limit(ValidatorUtil.LenB(el.value), el.getAttribute('min'),  el.getAttribute('max'));
	},
	msgLimitB: function(el) {
		var min = el.getAttribute("min") || 0;
		var max = el.getAttribute("max") || Number.MAX_VALUE;
		return "必须为" + min + "到" + max + "个字节";
	},

	checkDate: function(el) {
		return ValidatorUtil.IsDate(el.value, el.getAttribute('format'));
	},
	
	checkRepeat: function(el) {
		var target = document.getElementsByName(el.getAttribute('to'));
		if (length in target) {
			return el.value == target[0].value;
		}
		else {
			alert("[" + ValidatorUtil.getLabel(el) + "]指定的to表单项[" + el.getAttribute('to') + "]不存在");
			return false;
		}
	},
	msgRepeat: function(el) {
		var target = document.getElementsByName(el.getAttribute('to'));
		return "与[" + ValidatorUtil.getLabel(target[0]) + "]必须相同";
	},
		
	checkRange: function(el) {
		return parseFloat(el.min) <= parseFloat(el.value) && parseFloat(el.value) <= parseFloat(el.max);
	},
	msgRange: function(el) {
		return "必须在" + el.getAttribute("min") + "到" + el.getAttribute("max") + "之间";
	},
	
	checkCompare: function(el) {
		return ValidatorUtil.compare(el.value, el.operator, el.to);
	},
	msgCompare: function(el) {
		var operatorName = "等于";
		switch (el.getAttribute("operator")) {
			case "NotEqual":
			case "NE":
				operatorName = "不等于";
				break;
			case "GreaterThan":
			case "GT":
				operatorName = "大于";
				break;
			case "GreaterThanEqual":
			case "GE":
				operatorName = "大于或等于";
				break;
			case "LessThan":
			case "LT":
				operatorName = "小于";
				break;
			case "LessThanEqual":
			case "LE":
				operatorName = "小于或等于";
				break;
			default:
				operatorName = "等于";
				break;
		}
		var toObj = document.getElementsByName(el.getAttribute('to'));
		if (length in toObj) {
			return "必须" + operatorName + ValidatorUtil.getLabel(toObj[0]);
		}
		else {
			return "必须" + operatorName + el.getAttribute('to');
		}
	},
			
	checkCustom : function(el) {
		try {
			var strRegexp = el.getAttribute("regexp");
			regexp = new RegExp(strRegexp);
			return regexp.test(el.value);
		}
		catch (e) {
			alert(ValidatorUtil.getLabel(el) + ":" + e.message);
			return false;
		}
	},
	
	checkGroup : function(el) {
		var name = el.getAttribute('name');
		var min = el.getAttribute('min');
		var max = el.getAttribute('max');
		var groups = document.getElementsByName(name);
		var hasChecked = 0;
		min = min || 1;
		max = max || groups.length;
		for(var i=groups.length-1;i>=0;i--)
			if(groups[i].checked) hasChecked++;
		return min <= hasChecked && hasChecked <= max;
	},
	msgGroup: function(el) {
		with (el) {
			var min = getAttribute("min") || 0;
			var max = getAttribute("max") || Number.MAX_VALUE;
			if (min == 0 || min == 1){
				if (max == Number.MAX_VALUE) {
					return "您必须选择一项";
				}
				else {
					return "您最多可选择" + max + "项";									
				}
			}
			else {
				if (max == Number.MAX_VALUE) {
					return "您至少必须选择" + min + "项";
				}
				else {
					return "您必须选择" + min + "到" + max + "项";							
				}
			}
		}
	},
	
	limit : function(len, min, max){
		min = min || 0;
		max = max || Number.MAX_VALUE;
		return min <= len && len <= max;
	},
	
	LenB : function(str){
		return str.replace(/[^\x00-\xff]/g,"**").length;
	},
	Exec : function(op, reg){
		return new RegExp(reg,"g").test(op);
	},
	compare : function(op1,operator,op2){
		var value = document.getElementsByName(op2);
		if (length in value) {
			op2 = value[0].value;
		}
		
		var value = document.getElementsByName(op2);
		try {
			var v1 = parseFloat(op1);
			if (typeof v1 == "number") {
				op1 = v1;
				op2 = parseFloat(op2);
			}
		}
		catch (ex) {
		}

		switch (operator) {
			case "NotEqual":
			case "NE":
				return (op1 != op2);
			case "GreaterThan":
			case "GT":
				return (op1 > op2);
			case "GreaterThanEqual":
			case "GE":
				return (op1 >= op2);
			case "LessThan":
			case "LT":
				return (op1 < op2);
			case "LessThanEqual":
			case "LE":
				return (op1 <= op2);
			default:
				return (op1 == op2);            
		}
	},
	IsDate : function(op, formatString) {
		formatString = formatString;
		var m, year, month, day;
		switch(formatString){
			case "ymd" :
				m = op.match(new RegExp("^((\\d{4})|(\\d{2}))([-./]?)(\\d{2})\\4(\\d{2})$"));
				if(m == null ) return false;
				day = m[6];
				month = parseInt(m[5], 10)-1;
				year =  (m[2].length == 4) ? m[2] : GetFullYear(parseInt(m[3], 10));
				break;
			case "dmy" :
				m = op.match(new RegExp("^(\\d{2})([-./]?)(\\d{2})\\2((\\d{4})|(\\d{2}))$"));
				if(m == null ) return false;
				day = m[1];
				month = parseInt(m[3], 10)-1;
				year = (m[5].length == 4) ? m[5] : GetFullYear(parseInt(m[6], 10));
				break;
			default :
				m = op.match(new RegExp("^((\\d{4})(\\d{2})(\\d{2})$)"));
				if(m == null ) return false;
				day = m[4];
				month = parseInt(m[3], 10)-1;
				year = m[2];
				break;
		}
		var date = new Date(year, month, day);
		return (typeof(date) == "object" && year == date.getFullYear() && month == date.getMonth() && day == date.getDate());
		function GetFullYear(y){return ((y<30 ? "20" : "19") + y)|0;}
	},
	getLabel : function(el) {
		with (el) {
			var label = getAttribute("label");
			if (label == null) {
				label = getAttribute("name");
				if (label == null) {
					label = getAttribute("id");
				}
			}
			return label;
		}
	}		
};	

//表单验证主程序
Validator = {
	ErrorItem : [document.forms[0]],
	ErrorMessage : ["以下原因导致提交失败：\t\t\t\t"],

	Validate : function(theForm, mode){
		mode = parseInt(mode);
		var obj = theForm || event.srcElement;
		var count = obj.elements.length;
		this.ErrorMessage.length = 1;
		this.ErrorItem.length = 1;
		this.ErrorItem[0] = obj;
		out:
		for(var i=0;i<count;i++){
			obj.elements[i].value = obj.elements[i].value.trim();
			with(obj.elements[i]){
				if (disabled) {
					continue;
				}
				
				var isvalidate = true;
				this.ClearState(obj.elements[i]);
				if(getAttribute("require") != "false" || (value != null && value.trim() != "")) {
					var dataType = getAttribute("dataType");
					
					if(typeof(dataType) != "object")  {
						dataTypes = dataType.split("&");
										
						for (var j=0; j<dataTypes.length; j++) {
							var _dataType = dataTypes[j].trim();
							if (typeof(PatternDict[_dataType]) == "undefined") {
								continue;
							}
							if (!PatternDict[_dataType].validate(obj.elements[i])) {
								this.AddError(i);
								isvalidate = false;
								break;
							}
						}
					}
				}

				if (isvalidate && !IsValidate(obj.elements[i])) {
					this.AddError(i);
					obj.elements[i].showmsg = null;
				}					
			}
		}
		if(this.ErrorMessage.length > 1){
			mode = mode || 1;
			var errCount = this.ErrorItem.length;
			switch(mode){
			case 2 :
				for(var i=1;i<errCount;i++)
					this.ErrorItem[i].style.color = "red";
			case 1 :
				alert(this.ErrorMessage.join("\n"));
				for(var i=1;i<errCount;i++){
					if(this.ErrorItem[i].type!='hidden') {
						this.ErrorItem[i].focus();
						break;
					}
				}
				break;
			case 3 :
				for(var i=1;i<errCount;i++){
					try{
						var span = document.createElement("SPAN");
						span.id = "__ErrorMessagePanel";
						span.style.color = "red";
						this.ErrorItem[i].parentNode.appendChild(span);
						span.innerHTML = this.ErrorMessage[i].replace(/\d+:/,"*");
					}
					catch(e){alert(e.description);}
				}
				
				for(var i=1;i<errCount;i++){
					if(this.ErrorItem[i].type!='hidden') {
						this.ErrorItem[i].focus();
						break;
					}
				}
				break;
			default :
				alert(this.ErrorMessage.join("\n"));
				break;
			}
			return false;
		}
		return true;
		
		function IsValidate(obj) {
			try {
				if (obj.onValidate != null) {
					var str_f = "function f_handleOnValidate() {";
					str_f += obj.onValidate;
					str_f += "}";
					
					eval(str_f);
										
					try {
						return f_handleOnValidate.call(obj);
					}
					catch(exp1) {
						return false;
					}					
				}
			}
			catch (ex) {
				return false;
			}
			return true;
		}
	},
	ClearState : function(elem){
		with(elem){
			showmsg = null;
			if(style.color == "red")
				style.color = "";
			var lastNode = parentNode.childNodes[parentNode.childNodes.length-1];
			if(lastNode.id == "__ErrorMessagePanel")
				parentNode.removeChild(lastNode);
		}
	},
	AddError : function(index){
		var el = this.ErrorItem[0].elements[index];
		this.ErrorItem[this.ErrorItem.length] = el;
		//this.ErrorMessage[this.ErrorMessage.length] = this.ErrorMessage.length + ":[" + ValidatorUtil.getLabel(this.ErrorItem[0].elements[index]) + "]" + str;
		var str = el.showmsg;
		if (str==null || str.trim()=="") {
			str = el.msg;
		}
		
		if (str==null || str.trim()=="") {
			str = "数据无效";
		}
		
		this.ErrorMessage[this.ErrorMessage.length] = this.ErrorMessage.length + ":[" + ValidatorUtil.getLabel(this.ErrorItem[0].elements[index]) + "]" + str;
	}
}



function bindSubmit(theForm) {
	var userFormSubmit = theForm.submit;
	theForm.submit = function() {
		if (theForm.onsubmit()) {
			userFormSubmit();
		}
	}
}

function bindOnSubmit(theForm) {
	var userFormOnsubmit = theForm.onsubmit;
	theForm.onsubmit = function() {
		var theMode = theForm.mode;
		var ret = Validator.Validate(theForm, theMode);
		if (!ret) {
			return false;
		}
		else {
			if (typeof userFormOnsubmit == "function") {
				return userFormOnsubmit();
			}
			return true;
		} 
	};
}

//扩展Form对象,使其具备自动验证功能
var userWindowOnload = window.onload;
window.onload = function() {
	for (var i=0; i<document.forms.length; i++) {
		//var userFormOnsubmit = document.forms[i].onsubmit;
		var theForm = document.forms[i];
		//在onsubmit事件处理中加入表单验证功能
		bindOnSubmit(theForm);

/*		
		theForm.onsubmit = function() {
			var theMode = theForm.mode;
			if (!Validator.Validate(theForm, theMode)) {
				return false;
			}
			else {
				if (userFormOnsubmit != null) {
					return userFormOnsubmit();
				}
				return true;
			} 
		};
*/			
		//缺省情况下,调用form.submit()时不会激发onsubmit事件
		//这里对他进行了改造, 使得调用form.submit()时自动激发onsubmit事件
		bindSubmit(theForm);
/*
		var userFormSubmit = theForm.submit;		
		theForm.submit = function() {
			if (theForm.onsubmit()) {
				userFormSubmit();
			}
		}
 */
	}
	
	if (userWindowOnload != null) {
		userWindowOnload();
	}
}



/**
 *	用正则表达式定义校验规则
 *	校验失败时的信息提示策略：如果在表单项上定义了msg属性，则显示此信息；
 *	否则显示pattern中定义的信息。pattern中的信息也可以是动态的，
 *	可以定义一个方法来生成信息。此方法必须传入相应表单项作为参数。
 */
//非空检查
PatternDict.Require 	= new Pattern(/^\S+$/, "不允许为空");
//邮件地址有效性检查
PatternDict.Email 		= new Pattern(/^\w+([-+.]\w+)*@\w+([-.]\\w+)*\.\w+([-.]\w+)*$/, "不符合邮箱地址规则");
//电话号码有效性检查
PatternDict.Phone 		= new Pattern(/^((\(\d{3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{3,10}$/, "不符合电话号码规则");
//手机号码有效性检查
PatternDict.Mobile 		= new Pattern(/^\d{11}$/, "必须为11位数字");
//PatternDict.Mobile 		= new Pattern(/^((\(\d{3}\))|(\d{3}\-))?13\d{9}$/, "不符合手机号码规则");
//超级链接有效性检查
PatternDict.Url 		= new Pattern(/^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$/, "不符合URL规则");
//身份证号码规则检查
PatternDict.IdCard 		= new Pattern(/^\d{15}(\d{2}[A-Za-z0-9])?$/, "不符合身份证号码规则");
//金额规则检查
PatternDict.Currency 	= new Pattern(/^\d+(\.\d{1,2})?$/, "必须为数字（并且小数点后不得超过两位）");
//数字规则检查
PatternDict.Number 		= new Pattern(/^\d+$/, "必须为数字");
//邮政编码检查
PatternDict.Zip 		= new Pattern(/^[1-9]\d{5}$/, "必须为6位数字");
PatternDict.QQ 			= new Pattern(/^[1-9]\d{4,8}$/, "必须为5-9为数字");
PatternDict.Integer 	= new Pattern(/^[-\+]?\d+$/, "必须为整数");
PatternDict.Double 		= new Pattern(/^[-\+]?\d+(\.\d+)?$/, "必须为Double");
PatternDict.English 	= new Pattern(/^(([A-Za-z][\s\.A-Za-z]*[A-Za-z])|([A-Za-z]+))$/, "必须为字母");
PatternDict.Chinese 	= new Pattern(/^[\u0391-\uFFE5]+$/, "必须为中文");

PatternDict.ABCPassword 	= new Pattern(/^\d{4,6}$/, "必须为4~6位数字");
PatternDict.RemitPassword 	= new Pattern(/^\d{6}$/, "必须为6位数字");
PatternDict.cDddPrefix 	= new Pattern(/^[1-9]\d{0,3}$/, "必须为1-4为数字");
//区号检查
//用自定义方法定义校验规则，此方法必须传入相应表单项作为参数。
//	校验失败时的信息提示策略：如果在表单项上定义了msg属性，则显示此信息；
//	否则显示pattern中定义的信息。pattern中的信息也可以是动态的，
//	可以定义一个方法来生成信息。此方法必须传入相应表单项作为参数。
PatternDict.SafeString	= new Pattern(ValidatorUtil.isSafe, "不符合安全规则");
PatternDict.Limit 		= new Pattern(ValidatorUtil.checkLimit, ValidatorUtil.msgLimit);
PatternDict.LimitB 		= new Pattern(ValidatorUtil.checkLimitB, ValidatorUtil.msgLimitB);
PatternDict.Date 		= new Pattern(ValidatorUtil.checkDate, "日期格式错");
PatternDict.Repeat 		= new Pattern(ValidatorUtil.checkRepeat, ValidatorUtil.msgRepeat);
PatternDict.Range 		= new Pattern(ValidatorUtil.checkRange, ValidatorUtil.msgRange);
PatternDict.Compare 	= new Pattern(ValidatorUtil.checkCompare, ValidatorUtil.msgCompare);
PatternDict.Custom 		= new Pattern(ValidatorUtil.checkCustom, "不符合自定义规则");
PatternDict.Group 		= new Pattern(ValidatorUtil.checkGroup, ValidatorUtil.msgGroup);
