/***************************************************************
* 作者：mousecat, mousecat4444@126.com
* 版本：v1.3
* 修改记录：
* （1）增加默认验证提示信息。
* （2）修改电话号码验证规则，使之符合086-010-87654321
* （3）简化类调用方式
* （4）简化描述符字符个数
* （5）增加相关验证符（小数验证，整数验证）
* （6）更新帮助说明
* （7）去掉收尾两边空格
* （8）没有rq标识符验证的控件，当为空时可以正确提交，当不为空时根据添加的验证符进行判断
* （9）没有自定义函数对象可以正常运行
* （10）修改程序容错信息
*
* 特点：
* （1）描述符（validation descriptor）实现验证
* （2）允许添加自定义函数
* （3）面向对象（the oriented_object）
* （4）验证方式包括：onSubmit，onBlur，AJAX。
* （5）应用div形式显示提示信息
* （6）对提示信息应用css样式
* （7）对于程序级错误给予提示
* （8）正确识别中文、英文字符长度
* （9）实现表单控件验证、表单集合验证
* （10）对于大型表单验证更加方便容易
* （11）支持 ie6、firefox2.0 浏览器
* （12）方便调用方式
* （13）设置缺省提示信息
*
* 使用说明：
* （1）包含js验证类
* <head>
*   <script language="Javascript" src="gen_validator.js" type="text/javascript"></script>
* </head>
* （2）定义验证对象集合
* var a_fields = {
* 'username':{'rq':'必填项', 'mx=5':'', 'aa':'字母'},
* 'Email':{'mx=20':'', 'rq':'', 'el':''},
* 'Phone':{'te':''},
* 'introduce':{'mx=20':''},
* 'address':{'ds=0':'必须选择地区!'}
* }
* 其中，
* 'username'：代表需要验证的表单控件
* 'rq'：验证标识符（参见标识符说明）
* '必填项'：验证提示信息
* （3）调用验证主函数
* html_form(a_fields, 'myform', 'testCustom');
* 参数1：验证对象集合
* 参数2：表单名称
* 参数3：自定义函数名称
* （4）备注：
* testCustom：自定义验证函数
*
* 注意事项：
* （1）验证提示div的id需要按照验证控件id值+"txt"形式表示
* 如：验证控件id为username，那么显示提示信息的位置id为txtusername。
* （2）当使用控件验证时，需要增加控件的自定义属性（详见使用范例）
* descriptor：验证描述符列表，中间用"|"分隔（可采用默认提示信息）
* errormsg：错误信息列表，中间用"|"分隔，对应顺序应该和descriptor顺序一致，最后一项应为正确时（默认）的提示信息
*     如果某一提示信息采用默认形式"|"不能省略
* （3）js验证类对象声明必须在html form之后
* （4）验证标识符必须保持一致
* （5）验证提示信息必须保持一致
* （6）控件验证标识符（descriptor）和定义的验证标识符对象必须相同。
*
* 标识符说明（括号内标识为略写模式）：
* （1）required（rq）：必填项
* （2）maxlength=???（mx=???）：最大长度，例：maxlen=25
* （3）minlength=???（mi=???）：最小长度，例：minlen=5
* （4）alphanumeric（an）：验证字符和数字
* （5）numeric（nc）：验证数字
* （6）alpha（aa）：验证字符
* （7）email（el）：验证邮箱
* （8）lessthan=???（lt）：验证对象要小于某值，例：lt=1000
* （9）greaterthan=???（gt）：验证对象要大于某值，例：gt=10
* （10）regexp=???（rg=???）：正则表达式验证，例：regexp=^[A-Za-z]{1,20}$
* （11）doselect=0（ds=0）：验证select控件是否被选择
* （12）alnumhyphen（ay）：验证字符、数字、-
* （13）telephone（te）：验证电话格式，例：086-010-58022633
* （14）real（rl）：验证实数格式，例：12.34
* （15）http（hp）：验证url地址格式，例：http://www.baidu.com/aa.index?flag=1&fadsf=2
*
****************************************************************/
/////////////////参数初始化////////////////////////////
//缺省样式规则
//格式：错误样式|正确样式
var defaultStyle = "style2|style1";
//缺省错误提示
var objDescTips = {
required : "required",
maxlength : "输入项最大长度不能超过",
minlength : "输入项最小长度不能小于",
alphanumeric : "输入项必须为字母和数字",
numeric : "输入项必须为数字",
alpha : "输入项必须为字母",
alnumhyphen : "输入项必须为字母、数字、横线、下划线",
email : "not an valid email address",
lessthan : "输入项必须为数字",
lessthan_two : "输入项必须小于", 
greaterthan : "输入项必须为数字",
greaterthan_two : "输入项必须大于",
regexp : "输入项必须符合自定义格式",
doselect : "请选择下拉选项",
telephone : "固定电话格式错误",
real : "实数格式错误",
http : "网址格式错误"
}
//缺省正确提示信息
var defaultTip = {

};
///////////////////////功能函数//////////////////////////////////
window.onerror = function() {return true};
String.prototype.trueLen = function() {return this.replace(/[^\x00-\xff]/g,"**").length;}
String.prototype.trim = function() {return this.replace(/(^\s*)|(\s*$)/g, "");};
//快捷方式
function $() {
   return document.getElementById(arguments[0]);
}
//信息提示
function dispInfo(msgObj, msgInfo, msgClass) {
var objName = msgObj.name
var msgItem = $("txt" + objName);

if (!msgItem) {
   alert("Bug: " + objName + "缺少信息显示区域!");
   return false;
}

msgItem.style.display = "block";
msgItem.className = msgClass;
msgItem.innerHTML = msgInfo;
}
//////////////////////////////核心类//////////////////////////////
//控件验证
function unitCheck(evt) {
var evt = evt ? evt : window.event;
var srcEle = (evt.target) ? evt.target : evt.srcElement;
var strDescriptor = srcEle.getAttribute("descriptor");
var arrDescriptor = strDescriptor.split('|');
var arrStyle = defaultStyle.split('|');

if (srcEle.getAttribute("errormsg") == null) {
   var arrErrorMsg = new Array();
} else {
   var strErrorMsg = srcEle.getAttribute("errormsg");
   var arrErrorMsg = strErrorMsg.split('|');
}

if (strDescriptor.search(/rq|required|ds/gi) == -1) {
   if (srcEle.value.length == 0) {
dispInfo(srcEle, defaultTip[srcEle.id], arrStyle[1]);
return true; 
   }
}

for (var iteDes = 0; iteDes < arrDescriptor.length; iteDes++) {
   if (!V2validateData(arrDescriptor[iteDes], srcEle, arrErrorMsg[iteDes])) return false;
}
disName = srcEle.name;
//dispInfo(srcEle, defaultTip[disName], arrStyle[1]);
return true;
}
function Validator(frmname) {
this.formobj = document.forms[frmname];
this.strForm = frmname;
if(!this.formobj) {
   alert("BUG: 不能得到表单对象" + frmname);
   return false;
}
if(this.formobj.onsubmit) {
   this.formobj.old_onsubmit = this.formobj.onsubmit;
   this.formobj.onsubmit = null;
} else {
   this.formobj.old_onsubmit = null;
}
this.formobj.onsubmit = form_submit_handler;
this.addValidation = add_validation;
this.setAddnlValidationFunction = set_addnl_vfunction;
this.clearAllValidations = clear_all_validations;
}
function set_addnl_vfunction(functionname) {
   this.formobj.addnlvalidation = functionname;
}
function clear_all_validations() {
for(var itr = 0;itr < this.formobj.elements.length;itr++) {
   this.formobj.elements[itr].validationset = null;
}
}
function form_submit_handler() {
for(var itr = 0;itr < this.elements.length;itr++) {
   if(this.elements[itr].validationset && !this.elements[itr].validationset.validate()) { 
return false;
   }
}
if(this.addnlvalidation) {
   str =" var ret = "+this.addnlvalidation+"()";
   eval(str);
   if(!ret) return ret;
}
return true;
}
function add_validation(itemname, descriptor, errstr) {
if(!this.formobj) {
   alert("BUG: 不能得到表单对象!");
   return;
}
var itemobj = this.formobj[itemname];
if(!itemobj) {
   alert("BUG: 不能得到被验证对象: "+itemname);
   return false;
}
if(!itemobj.validationset) {
   itemobj.validationset = new ValidationSet(itemobj);
}
itemobj.validationset.add(descriptor, errstr);
}
function ValidationSet(inputitem) {
this.vSet = new Array();
this.add = add_validationdesc;
this.validate = vset_validate;
this.itemobj = inputitem;
}
function add_validationdesc(desc, error) {
   this.vSet[this.vSet.length] = new ValidationDesc(this.itemobj, desc, error);
}
function vset_validate() {
for(var itr=0;itr<this.vSet.length;itr++) {
   if(!this.vSet[itr].validate()) {
return false;
   }
}
return true;
}
function ValidationDesc(inputitem, desc, error) {
this.desc=desc;
this.error=error;
this.itemobj = inputitem;
this.validate=vdesc_validate;
}
function vdesc_validate() {
var arrStyle = defaultStyle.split('|');

if (!this.itemobj.getAttribute("descriptor")) {
   alert('Bug：缺少' + this.itemobj.id + '验证标识符descriptor');
   return false;
} 
if (this.itemobj.getAttribute("descriptor").search(/rq|required|ds/gi) == -1) {
   if (this.itemobj.value.length == 0) {
dispInfo(this.itemobj, defaultTip[this.itemobj.id], arrStyle[1]);
return true; 
   }
}

if(!V2validateData(this.desc, this.itemobj, this.error)) {
   this.itemobj.focus();
   return false;
}
return true;
}
function V2validateData(strValidateStr, objValue, strError) { 
var epos = strValidateStr.search("="); 
var command   = ""; 
var cmdvalue = ""; 
var arrStyle = defaultStyle.split('|');

if(epos >= 0) { 
   command   = strValidateStr.substring(0,epos); 
   cmdvalue = strValidateStr.substr(epos+1); 
} else { 
   command = strValidateStr; 
} 
var _value = objValue.value.trim();
switch(command) {
   case "required": 
   case "rq":
   { 
if(eval(_value.trueLen()) == 0) { 
if (!strError || strError.length == 0) {
    strError = objDescTips.required;
}
dispInfo(objValue, strError, arrStyle[0]);
return false; 
}
break;          
   }
   case "maxlength":
   case "mx":
   {
if(eval(_value.trueLen()) >   eval(cmdvalue)) { 
if (!strError || strError.length == 0) {
    strError = objDescTips.maxlength + cmdvalue;
}
dispInfo(objValue, strError, arrStyle[0]);
return false; 
}
break; 
   }
   case "minlength": 
   case "mi":
   { 
if(eval(_value.trueLen()) <   eval(cmdvalue)) { 
if (!strError || strError.length == 0) {
    strError = objDesTip.minlength + cmdvalue;
    return false;
}
dispInfo(objValue, strError, arrStyle[0]);
return false;                
}
break; 
   }
   case "alphanumeric": 
   case "an":
   { 
var charpos = _value.search("[^A-Za-z0-9]"); 
if(_value.trueLen() > 0 &&   charpos >= 0) { 
if (!strError || strError.length == 0) {
    strError = objDescTips.alphanumeric;
}
dispInfo(objValue, strError, arrStyle[0]);
return false; 
}
break; 
   }
   case "numeric":
   case "nc":
   { 
var charpos = _value.search("[^0-9]"); 
if(_value.trueLen() > 0 &&   charpos >= 0) { 
if (!strError || strError.length == 0) {
    strError = objDescTips.numeric;
}
dispInfo(objValue, strError, arrStyle[0]);
return false; 
}
break;             
   }
   case "alpha":
   case "aa":
   { 
var charpos = _value.search("[^A-Za-z]"); 
if(_value.trueLen() > 0 &&   charpos >= 0) { 
if (!strError || strError.length == 0) {
    strError = objDescTips.alpha;
}
dispInfo(objValue, strError, arrStyle[0]);
return false; 
}
break; 
   }
   case "alnumhyphen":
   case "ay":
   {
var charpos = _value.search("[^A-Za-z0-9\-_]"); 
if(_value.trueLen() > 0 &&   charpos >= 0) {    
if (!strError || strError.length == 0) {
    strError = objDescTips.alnumhyphen;
}
dispInfo(objValue, strError, arrStyle[0]);
return false; 
} 
break;
   }
   case "email": 
   case "el":
   { 
var pattern = /^[\w-\.]+\@[\w\.-]+\.[a-z]{2,4}$/;
if (!pattern.test(_value)) {
if (!strError || strError.length == 0) {
    strError = objDescTips.email;
}
dispInfo(objValue, strError, arrStyle[0]);
return false; 
}
break; 
   }
   case "lessthan": 
   case "lt":
   { 
if(isNaN(_value)) { 
if (!strError || strError.length == 0) {
    strError = objDescTips.lessthan;
}
dispInfo(objValue, strError, arrStyle[0]);
return false; 
}
if(eval(_value) >=   eval(cmdvalue)) { 
if (!strError || strError.length == 0) {
    strError = objDescTips.lessthan_two + cmdvalue;
}
dispInfo(objValue, strError, arrStyle[0]);
return false;                
}       
break; 
   }
   case "greaterthan":
   case "gt":
   { 
if(isNaN(_value))   { 
if (!strError || strError.length == 0) {
    strError = objDescTips.greaterthan;
}
dispInfo(objValue, strError, arrStyle[0]); 
return false; 
}
if(eval(_value) <=   eval(cmdvalue)) {  
if (!strError || strError.length == 0) {
    strError = objDescTips.greaterthan_two + cmdvalue;
}
dispInfo(objValue, strError, arrStyle[0]); 
return false;                
}          
break; 
   }
   case "regexp": 
   case "rg":
   { 
if(_value.trueLen() > 0) {
if(!_value.match(cmdvalue)) {  
    if (!strError || strError.length == 0) {
   strError = objDescTips.regexp;
    }
    dispInfo(objValue, strError, arrStyle[0]); 
    return false;                
}
}
break; 
   }
   case "doselect": 
   case "ds":
   { 
if(objValue.selectedIndex == null) { 
alert("BUG: 不能找到select对象");
return false; 
} 
if(objValue.selectedIndex == eval(cmdvalue)) {  
if (!strError || strError.length == 0) {
    strError = objDescTips.doselect;
}
dispInfo(objValue, strError, arrStyle[0]); 
return false;                                  
} 
break; 
   }
   case "telephone":
   case "te":
   {
var pattern = /^0?\d{2}\-?\d{3}\-?\d{8}$/;
if (!pattern.test(_value)) {
if (!strError || strError.length == 0) {
    strError = objDescTips.telephone;
}
dispInfo(objValue, strError, arrStyle[0]); 
return false;
}
break;
   }
   case "real":
   case "rl":
   {
var pattern = /^[\+\-]?\d*\.?\d*$/;
if (!pattern.test(_value)) {
if (!strError || strError.length == 0) {
    strError = objDescTips.real;
}
dispInfo(objValue, strError, arrStyle[0]); 
return false;
}
break;
   }
   case "http":
   case "hp":
   {
var pattern = /^http:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?$/;
if (!pattern.test(_value)) {
if (!strError || strError.length == 0) {
    strError = objDescTips.http;
}
dispInfo(objValue, strError, arrStyle[0]); 
return false;
}
break;
   }
}
return true; 
}
/////////////////////////用户调用接口////////////////////////////
function html_form(objSet, frmName) {
var frmvalidator   = new Validator(frmName);
for (var objSingle in objSet) {
   for (var descSets in objSet[objSingle]) {
if (objSet[objSingle][descSets] == '') {
frmvalidator.addValidation(objSingle, descSets);
} else {
frmvalidator.addValidation(objSingle, descSets, objSet[objSingle][descSets]);
}
   }
}
//自定义函数
frmvalidator.setAddnlValidationFunction(arguments[2]);
}
