收藏本站腾讯微博新浪微博

经典论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

蓝色理想 最新研发动态 网站开通淘帖功能 - 蓝色理想插件 论坛内容导读一页看论坛 - 给官方提建议

论坛活动及任务 地图和邮件任务 请多用悬赏提问 热夏来袭,选一款蓝色理想的个性T恤吧!

手机上论坛,使用APP获得更好体验 急需前端攻城狮,获得内部推荐机会 论坛开通淘帖功能,收藏终于可以分类了!

搜索
查看: 2078|回复: 0

[讨论] 对Validator v1.05 表单验证类的代码分析

[复制链接]
发表于 2013-7-12 15:17:32 | 显示全部楼层 |阅读模式
本帖最后由 GaFish 于 2013-7-12 15:29 编辑

最近在做表单验证相关的功能,基本功能完成后我就想是不是可以考虑把功能扩展成一个通用组件,所以想看看其它人是怎么个处理思路,于是在蓝色的老贴里找到了@我佛山人在2004-11-24写的一个《表单验证类 Validator v1.05》,但是他的代码一丝注释都没有,真是让人桑心,为了理解他的思路,我对代码进行了一个详细的分析,在分析过程也发现了蛮多有用的细节,现将分析结果与大家分享下。

关于身份证验证部分的参考资料:
> http://zhidao.baidu.com/question/351008503.html
> http://zhidao.baidu.com/question/1937947.html

  1.         /*************************************************
  2.         Validator v1.05
  3.         code by 我佛山人
  4.         wfsr@msn.com
  5.         *************************************************/
  6.         Validator = {
  7.             // 必填项
  8.             Require: /.+/,
  9.             // 验证EMAIL
  10.             Email: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,
  11.             // 验证电话
  12.             Phone: /^((\(\d{2,3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}(\-\d{1,4})?$/,
  13.             // 验证手机
  14.             Mobile: /^((\(\d{2,3}\))|(\d{3}\-))?13\d{9}$/,
  15.             // 验证网址
  16.             Url: /^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$/,
  17.             // 验证货币
  18.             Currency: /^\d+(\.\d+)?$/,
  19.             // 验证数字
  20.             Number: /^\d+$/,
  21.             // 验证邮编
  22.             Zip: /^[1-9]\d{5}$/,
  23.             // 验证qq
  24.             QQ: /^[1-9]\d{4,8}$/,
  25.             // 验证整数
  26.             Integer: /^[-\+]?\d+$/,
  27.             Double: /^[-\+]?\d+(\.\d+)?$/,
  28.             // 验证英文
  29.             English: /^[A-Za-z]+$/,
  30.             // 验证中文
  31.             Chinese: /^[\u0391-\uFFE5]+$/,
  32.             // 验证用户名
  33.             Username: /^[a-z]\w{3,}$/i,
  34.             // 密码安全规则
  35.             UnSafe: /^(([A-Z]*|[a-z]*|\d*|[-_\~!@#\$%\^&\*\.\(\)\[\]\{\}<>\?\\\/\'\"]*)|.{0,5})$|\s/,
  36.             // 检测密码安全
  37.             IsSafe: function(str) {
  38.                 return !this.UnSafe.test(str);
  39.             },
  40.             // 验证身份证
  41.             IdCard: "this.IsIdCard(value)",
  42.             SafeString: "this.IsSafe(value)",
  43.             // 过滤文件格式
  44.             // 自定义属性 accept ,用途:可接受的文件格式,参考值:".jpg,.gif,png"
  45.             Filter: "this.DoFilter(value, getAttribute('accept'))",
  46.             // 长度限制,中文算一个字符
  47.             // 自定义属性 min ,用途:最小值,默认值:0
  48.             // 自定义属性 max ,用途:最大值,默认值:Number.MAX_VALUE
  49.             Limit: "this.limit(value.length,getAttribute('min'),  getAttribute('max'))",
  50.             // 长度限制,中文算两个字符
  51.             // 自定义属性 min ,用途:最小值,默认值:0
  52.             // 自定义属性 max ,用途:最大值,默认值:Number.MAX_VALUE
  53.             LimitB: "this.limit(this.LenB(value), getAttribute('min'), getAttribute('max'))",
  54.             // 验证日期
  55.             // 自定义属性 format ,用途:日期显示格式,默认值:"ymd",可选值:"dmy"
  56.             Date: "this.IsDate(value, getAttribute('format'))",
  57.             // 检测值是否重复
  58.             // 自定义属性 to ,用途:指定需要检测值是否重复的表单对象name值
  59.             Repeat: "value == document.getElementsByName(getAttribute('to'))[0].value",
  60.             // 检测取值范围,value|0 的作用在于强制转换value为Number类型,如果value不为Number类型强制转换成0
  61.             // 自定义属性 min ,用途:指定最小值,默认值:0
  62.             // 自定义属性 max ,用途:指定最大值
  63.             Range: "getAttribute('min') < (value|0) && (value|0) < getAttribute('max')",
  64.             // 比较值大小
  65.             // 自定义属性 operator ,用途:设置比较方式
  66.             // 自定义属性 to ,用途:设置比较值
  67.             Compare: "this.compare(value,getAttribute('operator'),getAttribute('to'))",
  68.             // 自定义检测
  69.             // 自定义属性 regexp ,用途:设置自定义比较的正则表达式
  70.             Custom: "this.Exec(value, getAttribute('regexp'))",
  71.             // 检测一组数据
  72.             // 自定义属性 name ,用途:单选或多选按钮的name值
  73.             // 自定义属性 min ,用途:多选按钮的最小选择数量
  74.             // 自定义属性 max ,用途:多选按钮的最大选择数量
  75.             Group: "this.MustChecked(getAttribute('name'), getAttribute('min'), getAttribute('max'))",
  76.             // 错误表单列表数组
  77.             ErrorItem: [document.forms[0]],
  78.             // 对应上面列表的错误提示
  79.             ErrorMessage: ["以下原因导致提交失败:\t\t\t\t"],
  80.             // 开始验证
  81.             Validate: function(theForm, mode) {
  82.                 // 得到所有表单元素的数量
  83.                 var count = theForm.elements.length;
  84.                 // 重置错误提示信息
  85.                 this.ErrorMessage.length = 1;
  86.                 // 重置错误表单
  87.                 this.ErrorItem.length = 1;
  88.                 this.ErrorItem[0] = theForm;
  89.                 // 对表单元素进行循环遍历验证
  90.                 for (var i = 0; i < count; i++) {
  91.                     with(theForm.elements[i]) {
  92.                         // 获取自定义属性dataType
  93.                         var _dataType = getAttribute("dataType");
  94.                         // 如果属性不存在就跳过
  95.                         if (typeof(_dataType) == "object" || typeof(this[_dataType]) == "undefined") continue;
  96.                         this.ClearState(theForm.elements[i]);
  97.                         // 如果是非必填元素且值为空就跳过
  98.                         if (getAttribute("require") == "false" && value == "") continue;
  99.                         // 判断验证类型
  100.                         switch (_dataType) {
  101.                             case "IdCard":
  102.                             case "Date":
  103.                             case "Repeat":
  104.                             case "Range":
  105.                             case "Compare":
  106.                             case "Custom":
  107.                             case "Group":
  108.                             case "Limit":
  109.                             case "LimitB":
  110.                             case "SafeString":
  111.                             case "Filter":
  112.                                 // 以上类型直接执行验证方法
  113.                                 if (!eval(this[_dataType])) {
  114.                                     this.AddError(i, getAttribute("msg"));
  115.                                 }
  116.                                 break;
  117.                             default:
  118.                                 // 其它的执行正则验证
  119.                                 if (!this[_dataType].test(value)) {
  120.                                     this.AddError(i, getAttribute("msg"));
  121.                                 }
  122.                                 break;
  123.                         }
  124.                     }
  125.                 }
  126.                 if (this.ErrorMessage.length > 1) {
  127.                     // 错误提示模式默认为1
  128.                     mode = mode || 1;
  129.                     // 统计错误表单队列
  130.                     var errCount = this.ErrorItem.length;
  131.                     // 判断所选用的错误提示模式
  132.                     switch (mode) {
  133.                         case 2:
  134.                             // 错误提示模式为2,给所有提示加红色
  135.                             for (var i = 1; i < errCount; i++)
  136.                                 this.ErrorItem[i].style.color = "red";
  137.                         case 1:
  138.                             // 错误提示模式为1,弹出所有错误
  139.                             alert(this.ErrorMessage.join("\n"));
  140.                             // 聚焦到第一个错误的元素
  141.                             this.ErrorItem[1].focus();
  142.                             break;
  143.                         case 3:
  144.                             // 错误提示模式为3,遍历所有错误节点
  145.                             for (var i = 1; i < errCount; i++) {
  146.                                 try {
  147.                                     // 创建一个span标签并设置ID和加红色
  148.                                     var span = document.createElement("SPAN");
  149.                                     span.id = "__ErrorMessagePanel";
  150.                                     span.style.color = "red";
  151.                                     // 把它添加到错误元素父节点的最后
  152.                                     this.ErrorItem[i].parentNode.appendChild(span);
  153.                                     // 写入错误提示
  154.                                     span.innerHTML = this.ErrorMessage[i].replace(/\d+:/, "*");
  155.                                 } catch (e) {
  156.                                     alert(e.description);
  157.                                 }
  158.                             }
  159.                             // 聚焦到第一个错误的元素
  160.                             this.ErrorItem[1].focus();
  161.                             break;
  162.                         default:
  163.                             // 给出的错误模式不在预设范围则弹出错误提示
  164.                             alert(this.ErrorMessage.join("\n"));
  165.                             break;
  166.                     }
  167.                     return false;
  168.                 }
  169.                 return true;
  170.             },
  171.             limit: function(len, min, max) {
  172.                 // 长度限制,默认最小0最大Number.MAX_VALUE
  173.                 min = min || 0;
  174.                 max = max || Number.MAX_VALUE;
  175.                 return min <= len && len <= max;
  176.             },
  177.             LenB: function(str) {
  178.                 // 双字节字符转换成单字节计算长度
  179.                 return str.replace(/[^\x00-\xff]/g, "**").length;
  180.             },
  181.             ClearState: function(elem) {
  182.                 // 初始化节点状态
  183.                 with(elem) {
  184.                     // 清除红色显示
  185.                     if (style.color == "red")
  186.                         style.color = "";
  187.                     // 寻找当前父节点最后一个子节点
  188.                     var lastNode = parentNode.childNodes[parentNode.childNodes.length - 1];
  189.                     // 当前父节点最后一个子节点如果是错误提示就删除节点
  190.                     if (lastNode.id == "__ErrorMessagePanel")
  191.                         parentNode.removeChild(lastNode);
  192.                 }
  193.             },
  194.             AddError: function(index, str) {
  195.                 // 添加错误提示
  196.                 this.ErrorItem[this.ErrorItem.length] = this.ErrorItem[0].elements[index];
  197.                 this.ErrorMessage[this.ErrorMessage.length] = this.ErrorMessage.length + ":" + str;
  198.             },
  199.             Exec: function(op, reg) {
  200.                 // 执行正则匹配
  201.                 return new RegExp(reg, "g").test(op);
  202.             },
  203.             compare: function(op1, operator, op2) {
  204.                 // 比较op1、op2的大小,operator为比较方式
  205.                 switch (operator) {
  206.                     // 不等于
  207.                     case "NotEqual":
  208.                         return (op1 != op2);
  209.                     // 大于
  210.                     case "GreaterThan":
  211.                         return (op1 > op2);
  212.                     // 大于等于
  213.                     case "GreaterThanEqual":
  214.                         return (op1 >= op2);
  215.                     // 小于
  216.                     case "LessThan":
  217.                         return (op1 < op2);
  218.                     // 小于等于
  219.                     case "LessThanEqual":
  220.                         return (op1 <= op2);
  221.                     // 默认为等于
  222.                     default:
  223.                         return (op1 == op2);
  224.                 }
  225.             },
  226.             MustChecked: function(name, min, max) {
  227.                 // 检测必选项
  228.                 var groups = document.getElementsByName(name);
  229.                 // 初始化已选中数量
  230.                 var hasChecked = 0;
  231.                 // 设置默认最小值是1
  232.                 min = min || 1;
  233.                 // 设置默认最大值是选项总数
  234.                 max = max || groups.length;
  235.                 // 循环遍历所有选项,检查已选中的数量
  236.                 for (var i = groups.length - 1; i >= 0; i--)
  237.                     if (groups[i].checked) hasChecked++;
  238.                 // 返回已选数量是否在指定区间
  239.                 return min <= hasChecked && hasChecked <= max;
  240.             },
  241.             DoFilter: function(input, filter) {
  242.                 // 过滤文件格式
  243.                 return new RegExp("^.+\.(?=EXT)(EXT)$".replace(/EXT/g, filter.split(/\s*,\s*/).join("|")), "gi").test(input);
  244.             },
  245.             IsIdCard: function(number) {
  246.                 // 验证身份证规则,data是年月日,Ai为计算出来的合法的身份证号码
  247.                 var date, Ai;
  248.                 // 地区编码在数组中对应的位置
  249.                 var area = ['', '', '', '', '', '', '', '', '', '', '', '北京', '天津', '河北', '山西', '内蒙古', '', '', '', '', '', '辽宁', '吉林', '黑龙江', '', '', '', '', '', '', '', '上海', '江苏', '浙江', '安微', '福建', '江西', '山东', '', '', '', '河南', '湖北', '湖南', '广东', '广西', '海南', '', '', '', '重庆', '四川', '贵州', '云南', '西藏', '', '', '', '', '', '', '陕西', '甘肃', '青海', '宁夏', '新疆', '', '', '', '', '', '台湾', '', '', '', '', '', '', '', '', '', '香港', '澳门', '', '', '', '', '', '', '', '', '国外'];
  250.                 // 对号码进行正则匹配,中国的身份证号码有三种形式,如
  251.                 // 第一种:362424700808161
  252.                 // 第二种:36242419880808161x
  253.                 // 第三种:362424198808081616
  254.                 // 以第三种为例,匹配出来的结果应该是
  255.                 // re[0]=362424198408281612
  256.                 // re[1]=36
  257.                 // re[2]=198408281612
  258.                 // re[3]=
  259.                 // re[4]=
  260.                 // re[5]=
  261.                 // re[6]=
  262.                 // re[7]=
  263.                 // re[8]=198408281612
  264.                 // re[9]=1984
  265.                 // re[10]=08
  266.                 // re[11]=28
  267.                 // re[12]=1612
  268.                 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);
  269.                 // 匹配出错返回false
  270.                 if (re == null) return false;
  271.                 // 如果头两个数字大小超过area的长度或在area里的值为空则返回false
  272.                 if (re[1] >= area.length || area[re[1]] == "") return false;
  273.                 // 判断不同形式的身份证
  274.                 if (re[2].length == 12) {
  275.                     // 第二、三种的情况,截取前17个数字
  276.                     Ai = number.substr(0, 17);
  277.                     // 从号码中提取出年月日
  278.                     date = [re[9], re[10], re[11]].join("-");
  279.                 } else {
  280.                     // 第一种情况,通过补上19,截取出前17个数字
  281.                     Ai = number.substr(0, 6) + "19" + number.substr(6);
  282.                     // 给年份补全19,从号码中提取出年月日
  283.                     date = ["19" + re[4], re[5], re[6]].join("-");
  284.                 }
  285.                 // 判断提取出的日期是否符合日期规则
  286.                 if (!this.IsDate(date, "ymd")) return false;
  287.                 // 身份证最后一位的计算公示
  288.                 var verify = "10x98765432"; // 身份证未位数算法中0-10所对应的实际显示值
  289.                 var Wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; // 身份证前17位相对应的系数
  290.                 var sum = 0; // 将这17位数字和系数相乘的结果相加
  291.                 for (var i = 0; i <= 16; i++) {
  292.                     sum += Ai.charAt(i) * Wi[i];
  293.                 }
  294.                 Ai += verify.charAt(sum % 11); // 用加出来和除以11,看余数是多少,在verify中匹配对应的值
  295.                 // 号码长度等于15或18且号码合法返回true否则返回false
  296.                 return (number.length == 15 || number.length == 18 && number == Ai);
  297.             },
  298.             IsDate: function(op, formatString) {
  299.                 // 验证日期
  300.                 // 默认格式设置为ymd
  301.                 formatString = formatString || "ymd";
  302.                 // 定义变量
  303.                 // m 通过正则匹配出来的日期数组对象
  304.                 // year 年份
  305.                 // month 月份
  306.                 // day 天
  307.                 var m, year, month, day;
  308.                 // 判断日期格式
  309.                 switch (formatString) {
  310.                     // 显示格式 ymd
  311.                     case "ymd":
  312.                         // 匹配正则获取日期数据,合法日期值参考:"2013-7-9" "2013.7.9" "2013/7/9" "13-7-9" "13.7.9" "13/7/9"
  313.                         m = op.match(new RegExp("^((\\d{4})|(\\d{2}))([-./])(\\d{1,2})\\4(\\d{1,2})$"));
  314.                         // 如果匹配不到日期返回false
  315.                         if (m == null) return false;
  316.                         // 从匹配到的数据中分析出 年份 月份 天
  317.                         // 月份 * 1 是为了将字符串类型转换为数字类型
  318.                         // 年月如果不是4位数,则通过 GetFullYear 方法转换成4位
  319.                         day = m[6];
  320.                         month = m[5] * 1;
  321.                         year = (m[2].length == 4) ? m[2] : GetFullYear(parseInt(m[3], 10));
  322.                         break;
  323.                     // 显示格式 dmy
  324.                     case "dmy":
  325.                         // 匹配正则获取日期数据,合法日期值参考:"9-7-2013" "9.7.2013" "9/7/2013" "9-7-13" "9.7.13" "9/7/13"
  326.                         m = op.match(new RegExp("^(\\d{1,2})([-./])(\\d{1,2})\\2((\\d{4})|(\\d{2}))$"));
  327.                         // 如果匹配不到日期返回false
  328.                         if (m == null) return false;
  329.                         // 从匹配到的数据中分析出 年份 月份 天
  330.                         // 月份 * 1 是为了将字符串类型转换为数字类型
  331.                         // 年月如果不是4位数,则通过 GetFullYear 方法转换成4位
  332.                         day = m[1];
  333.                         month = m[3] * 1;
  334.                         year = (m[5].length == 4) ? m[5] : GetFullYear(parseInt(m[6], 10));
  335.                         break;
  336.                     default:
  337.                         break;
  338.                 }
  339.                 // 如果匹配不到数字类型的 month 返回false
  340.                 if (!parseInt(month)) return false;
  341.                 // 如果 month == 0 将其默认为12月
  342.                 month = month == 0 ? 12 : month;
  343.                 // 将匹配到的日期转换为标准日期,月份要 -1 处理
  344.                 var date = new Date(year, month - 1, day);
  345.                 // 如果 date 的类型是 object,且年份、月份、天都符合日期规则值则返回true,否则返回false
  346.                 return (typeof(date) == "object" && year == date.getFullYear() && month == (date.getMonth() + 1) && day == date.getDate());

  347.                 function GetFullYear(y) {
  348.                     // 将2位数的年份转换为4数,年份值范围:1930 - 2029
  349.                     return ((y < 30 ? "20" : "19") + y) | 0;
  350.                 }
  351.             }
  352.         }
复制代码
分析的过程可能有些片面,如果有人能花时间看完我的分析结果并指出错误的地方,我将非常感谢!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|小黑屋|Archiver|手机版|blueidea.com ( 湘ICP备12001430号 )  

GMT+8, 2020-11-29 04:06 , Processed in 0.077928 second(s), 7 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表