/**================================================================
* version:  1.1.0
* date:     2006-12-12
* summary:
*    这几天拜读了我佛山人的大作我觉得思路很好，根据
*    实际的验证需求，我做了一些调整 ：
*    
*    	如果是必录项，验证分两步：
*		1、查空；
*		2、合法性验证；
*		
*		如果非必录项，有两种可能：
*		1、没有输入或选择内容，无需验证；
*		2、输入或选择内容，需要验证；
*	
*	 对原有代码进行了修改：
*		1、扩展了页面录入元素属性require和标签label，
*		   将必录项和非必录项区分开；
*		   
*		2、去除掉一些dataType，例如：Require,QQ等；
*
* 修改历史：
* 2007-01-25 抽象出ValidateEngine，使之适应Form和单个元素的验证
* 2007-10-12 增加DecimalRange方法来检查数字是否在某一区间内（开闭区间均可，解决Range的不足之处）;
*            自定义的检验通过扩展属性validScript实现，指定调用函数即可，多个函数用";"分隔;
*            将出错类型分为error和warning两种，error型出现提交失败，warning则只提示由用户决定是否提交;
*            通过在require、dataType、validScript属性中添加分隔符":"和0(error)或者1(warning)指定出错类型;
*            dataType可以制定多个，每个在msg有对应的不同错误提示，都用";"隔开
*                  
================================================================**/
 Validator = {	
	Email : /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,
	Phone : /^((\(\d{2,3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}(\-\d{1,4})?$/,
	Mobile : /^((\(\d{2,3}\))|(\d{3}\-))?(13|15)\d{9}$/,
	Zip : /^[1-9]\d{5}$/,
	Url : /^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$/,	
	Currency : /^\d+(\.\d+)?$/,
	Number : /^\d+$/,	
	Integer : /^[-\+]?\d+$/,
	Double : /^[-\+]?\d+(\.\d+)?$/,
	English : /^[A-Za-z]+$/,
	Chinese :  /^[\u0391-\uFFE5]+$/,	
	IdCard : "this.IsIdCard(value)",
	Filter : "this.DoFilter(value, getAttribute('accept'))",
	Limit : "this.limit(value.length,getAttribute('min'),  getAttribute('max'))",
	LimitB : "this.limit(this.LenB(value), getAttribute('min'), getAttribute('max'))",
	Date : "this.IsDate(value, getAttribute('min'), getAttribute('format'))",
	Repeat : "value == document.getElementsByName(getAttribute('to'))[0].value",
	Range : "getAttribute('min') < (value|0) && (value|0) < getAttribute('max')",
	Compare : "this.compare(value,getAttribute('operator'),getAttribute('to'))",	
	CompareObject : "this.compare(value,getAttribute('operator'),document.getElementsByName(getAttribute('to'))[0].value)",
	DecimalRange : "this.CheckDecimalRange(value, getAttribute('min'), getAttribute('max'))",
	Group : "this.MustChecked(getAttribute('name'), getAttribute('min'), getAttribute('max'))",
	Custom : "this.Exec(value, getAttribute('regexp'))",
	ErrorLevel: [],	
	ErrorItem : [],
	ErrorMessage : ["\t\t"],
	Validate : function(objOrForm, mode){//error --0 warning --1
		this.ErrorMessage.length = 1;
		this.ErrorItem.length = 0;
		this.ErrorLevel.length = 0;
		
		var count = 0;
		
		if(objOrForm.elements){
			//form
			count = objOrForm.elements.length;			
			for(var i=0;i<count;i++){			
				//验证				
				var obj = objOrForm.elements[i];
				//var attrRequire = obj.getAttribute("require");
				//if(attrRequire != null  && attrRequire=="true"){//为避免非必输项无法效验，此处注释掉
				//alert(obj.name);
				//alert(attrRequire);
					this.ValidateEngine(obj);
				//}
			}
		}else{
			//element
			count = 1;
			this.ValidateEngine(objOrForm);
		}
		
		//提示
		if(this.ErrorMessage.length > 1){
			var warningCount = 0;
			mode = mode || 1;
			var errCount = this.ErrorItem.length;
			switch(mode){
			case 2 ://控件变成红色
				for(var i=0;i<errCount;i++){
					this.ErrorItem[i].style.color = "red";
					if(this.ErrorLevel[i] !=0) warningCount++;		
				}	
			case 1 ://弹出窗口显示
				alert(this.ErrorMessage.join("\n"));
				for(var i=0;i<errCount;i++){
					if(this.ErrorLevel[i] !=0) warningCount++;		
				}
				try{
				    this.ErrorItem[0].focus(); // 这里有没有更好的解决方法？
				}catch(e){}
				break;
			case 3 ://错误提示图片
				for(var i=0;i<errCount;i++){
				try{
					var srcstr = "images/error.gif";
					if(this.ErrorLevel[i] !=0) {
						warningCount++;
						srcstr = "images/help.gif";
					}			
					var img = document.createElement("img");
					img.id = "__ErrorMessagePanel";
					img.src= srcstr;
					img.style.cursor="pointer";
					img.align="middle";
					img.alt=this.ErrorMessage[i+1].replace(/\d+:/,"*");
					this.ErrorItem[i].parentNode.appendChild(img);
					}
					catch(e){alert(e.description);}
				}
                try{//控件被隐藏时，focus方法会抛出异常
				    this.ErrorItem[0].focus();
                }
                catch(e){}
                if(errCount>0 && errCount>warningCount){//有错误信息
                    alert("数据校验错误，您无法继续操作，请返回页面查看详细错误内容，根据错误提示进行修改。\n"+this.ErrorMessage.join("\n\n"));//使用tab方式，需提示有错误信息
                }
                break;
			case 4 ://控件后跟错误信息
				for(var i=0;i<errCount;i++){
				try{
					var color = "red";
					if(this.ErrorLevel[i] !=0) {
						warningCount++;
						color = "blue";
					}			
					var span = document.createElement("SPAN");
					span.id = "__ErrorMessagePanel";
					span.style.color = color;
					this.ErrorItem[i].parentNode.appendChild(span);
					span.innerHTML = this.ErrorMessage[i+1].replace(/\d+:/,"*");
					}
					catch(e){alert(e.description);}
				}
                try{
				    this.ErrorItem[0].focus();
                }
                catch(e){}
                if(errCount>0 && errCount>warningCount){//有错误信息
                    alert("数据校验错误，您无法继续操作，请返回页面查看详细错误内容");//使用tab方式，需提示有错误信息
                }
                break;
            case 5 ://什么都不提示，用于监控
                //不记录警告信息：如果监控中选择了取消提交，不管监控是控制还是提示，都不会往下走
                break;    
			default :
				alert(this.ErrorMessage.join("\n"));
				break;
			}
			var fatalCount = errCount - warningCount;
			if(fatalCount!=0){//有控制信息
				return false;
			}
			else {//只有警告信息
                if(mode!=5){//非监控，需提示有警告信息
    				if(confirm("数据校验有警告内容，您可以选择忽略警告，或查看详细警告内容，是否忽略警告？")){
    					return true;
    				}
    				else{ 
    					return false;
    				}
                }else{//监控已弹出详细窗口，不需提示警告信息
                    return true;
                }
			}		
		}
		return true;
	},
	//验证引擎
	ValidateEngine : function(elem){		    
		with(elem){	
		   			
			this.ClearState(elem);
			var ifRequire = getAttribute("require");
			
			//非必录项且没有输入或选择内容
			if( ifRequire==null || (ifRequire.indexOf("true")!=0 && this.IsNotValued(elem)) ) 
				;//continue;
			else{
				//是必录项且没有输入或选择内容(查空)
				if(ifRequire.indexOf("true")==0 && this.IsNotValued(elem)){
					var reqAndLevel = ifRequire.split(":");
					var alertLevel = 0;
					if(reqAndLevel.length == 2){
						alertLevel = parseInt(reqAndLevel[1],10);
					}
					switch(type){
						case "text":
						case "textarea":
						case "password":
							this.AddError(elem, "请输入"+(getAttribute("label")||getAttribute("name"))+"!",alertLevel);
							break;
						case "select-one":
						case "select-mutiple":
						case "radio":
						case "checkbox":
						case "file":
							this.AddError(elem, "请选择"+(getAttribute("label")||getAttribute("name"))+"!",alertLevel);
							break;
						default:
							this.AddError(elem, "请输入"+(getAttribute("label")||getAttribute("name"))+"!",alertLevel);
							break;
					}
				}else{
					var dataTypes = getAttribute("dataType");
					var msgs = getAttribute("msg");
					dataLabel:
					if(dataTypes!=null && dataTypes!="" && msgs!=null && msgs!=""){
						var typeAndLevelArray =  dataTypes.split(";");
						var msgArray = msgs.split(";");
						if(msgArray.length != typeAndLevelArray.length) {
							alert(getAttribute("name")+getAttribute("label")+"dataType和msg数目不匹配");
							break dataLabel;
						}
 					   					
    					for(var i=0; i<typeAndLevelArray.length;i++){
    						var _typeAndLevel = typeAndLevelArray[i].split(":");
    						var alertLevel = 0;
    						if(_typeAndLevel.length ==2){
    							alertLevel = parseInt(_typeAndLevel[1],10);
    						}
    						var _dataType = _typeAndLevel[0];
    						if(typeof(_dataType) == "object" || typeof(this[_dataType]) == "undefined"){
    					    }//continue;
    						else{			
    							switch(_dataType){
    								case "IdCard" :
    								case "Date" :
    								case "Repeat" :
    								case "Range" :
    								case "Compare" :
    								case "CompareObject" :
    								case "Custom" :
    								case "Group" :
    								case "Limit" :
    								case "LimitB" :					
    								case "Filter" :
    								case "DecimalRange" :
    									if(!eval(this[_dataType])){
    										this.AddError(elem, msgArray[i],alertLevel);
    									} 
    									break;
    								default :						
    									if(!this[_dataType].test(value)){ 
    										this.AddError(elem, msgArray[i],alertLevel);
    									}	
    									break;
    							}
    						}
						}
					}
				}
			}
            //根据 validScript 进行其它自定义校验
            //validScript=script1;script2;script3... script定义规范：如果有错return errorMsg，如果没有错，return null;
            var validScript = getAttribute("validScript");
            if(validScript && validScript!=""){
                var scriptArray = validScript.split(";");
                for(var i=0; i<scriptArray.length;i++){
                    var scriptAndLevel = scriptArray[i].split(":");
                    var alertLevel = 0;
                    if(scriptAndLevel.length ==2){
                        alertLevel = parseInt(scriptAndLevel[1],10);
                    }
                    var res = eval(scriptAndLevel[0]);
                    if(res!=null && res!=""){
                        this.AddError(elem, res,alertLevel);
                    }
                }   
            }
		}
		
	}, 
	//清除元素的红色高亮显示（1、原有录入内容的红色；2、跟随的红色字符提示；）
	ClearState : function(elem){
		with(elem){
			if(style.color == "red")
				style.color = "";
			//var lastNode = parentNode.childNodes[parentNode.childNodes.length-1];
			//if(lastNode.id == "__ErrorMessagePanel")
				//parentNode.removeChild(lastNode);
			for(var lastNode=parentNode.childNodes[parentNode.childNodes.length-1];
			     lastNode!=elem && lastNode.id == "__ErrorMessagePanel";){
                parentNode.removeChild(lastNode);
                lastNode = parentNode.childNodes[parentNode.childNodes.length-1];
			}				
		}
	},
	//增加错误
	AddError : function(elem, str,errLevel){
		this.ErrorLevel[this.ErrorLevel.length] = errLevel;
		this.ErrorItem[this.ErrorItem.length] = elem;
		this.ErrorMessage[this.ErrorMessage.length] = this.ErrorMessage.length + ":" + str;
	},
	//执行正则表达式
	Exec : function(op, reg){
		return new RegExp(reg,"g").test(op);
	},
	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;
	},	
	compare : function(op1,operator,op2){
		//alert(op1);
		//alert(operator);
		//alert(op2);
		switch (operator) {
			case "NotEqual":
				return (op1 != op2);
			case "GreaterThan":
				return (op1 > op2);
			case "GreaterThanEqual":
				return (op1 >= op2);
			case "LessThan":
				return (op1 < op2);
			case "LessThanEqual":
				return (op1 <= op2);
			default:
				return (op1 == op2);            
		}
	},	
	MustChecked : function(name, min, 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;
	},
	IsNotChecked : function(name){
		var groups = document.getElementsByName(name);
		var hasChecked = 0;		
		for(var i=groups.length-1;i>=0;i--)
			if(groups[i].checked) hasChecked++;
		return 0 == hasChecked;
	},
	IsNotValued : function(obj){
		switch(obj.type){
			case "radio":
			case "checkbox":				
				return this.IsNotChecked(obj.getAttribute('name'));				
			default:
			    // 判空之前先将所有空格去掉 				
				return obj.value.replace(/\s/g, '') == "";	//仅适合于ie
		}	
	},
	DoFilter : function(input, filter){
		return new RegExp("^.+\.(?=EXT)(EXT)$".replace(/EXT/g, filter.split(/\s*,\s*/).join("|")), "gi").test(input);
	},
	IsIdCard : function(number){
		var date, Ai;
		// 对于输入大写X不通过的处理,全部转为小写进行校验
		number = number.toLowerCase();
		var verify = "10x98765432";
		var Wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
		var area = ['','','','','','','','','','','','北京','天津','河北','山西','内蒙古','','','','','','辽宁','吉林','黑龙江','','','','','','','','上海','江苏','浙江','安微','福建','江西','山东','','','','河南','湖北','湖南','广东','广西','海南','','','','重庆','四川','贵州','云南','西藏','','','','','','','陕西','甘肃','青海','宁夏','新疆','','','','','','台湾','','','','','','','','','','香港','澳门','','','','','','','','','国外'];
		var re = number.match(/^(\d{2})\d{4}(((\d{2})(\d{2})(\d{2})(\d{3}))|((\d{4})(\d{2})(\d{2})(\d{3}[x\d])))$/i);
		if(re == null) return false;
		if(re[1] >= area.length || area[re[1]] == "") return false;
		if(re[2].length == 12){
			Ai = number.substr(0, 17);
			date = [re[9], re[10], re[11]].join("-");
		}
		else{
			Ai = number.substr(0, 6) + "19" + number.substr(6);
			date = ["19" + re[4], re[5], re[6]].join("-");
		}
		if(!this.IsDate(date, "ymd")) return false;
		var sum = 0;
		for(var i = 0;i<=16;i++){
			sum += Ai.charAt(i) * Wi[i];
		}
		Ai +=  verify.charAt(sum%11);
		return (number.length ==15 || number.length == 18 && number == Ai);
	},
	IsDate : function(op, formatString){
		formatString = formatString || "ymd";
		var m, year, month, day;
		switch(formatString){
			case "ymd" :
				m = op.match(new RegExp("^((\\d{4})|(\\d{2}))([-./])(\\d{1,2})\\4(\\d{1,2})$"));
				if(m == null ) return false;
				day = m[6];
				month = m[5]*1;
				year =  (m[2].length == 4) ? m[2] : GetFullYear(parseInt(m[3], 10));
				break;
			case "dmy" :
				m = op.match(new RegExp("^(\\d{1,2})([-./])(\\d{1,2})\\2((\\d{4})|(\\d{2}))$"));
				if(m == null ) return false;
				day = m[1];
				month = m[3]*1;
				year = (m[5].length == 4) ? m[5] : getFullYear(parseInt(m[6], 10));
				break;
			default :
				break;
		}
		if(!parseInt(month,10)) return false;
		month = month==0 ?12:month;
		var date = new Date(year, month-1, day);
        return (typeof(date) == "object" && year == date.getFullYear() && month == (date.getMonth()+1) && day == date.getDate());
		function GetFullYear(y){return ((y<30 ? "20" : "19") + y)|0;}
	},
	CheckDecimalRange :function(value, min, max){
	    var res = true;
	    if(min!=null) res = res && (value*1 >= min);
	    if(max!=null) res = res && (value*1 <= max);
	    return res;
	}

}