├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── build.js ├── demo ├── bootstrap │ ├── css │ │ └── bootstrap.min.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── index.html ├── complex.html ├── semantic │ ├── index.html │ ├── semantic.min.css │ └── themes │ │ └── default │ │ └── assets │ │ └── fonts │ │ ├── icons.eot │ │ ├── icons.otf │ │ ├── icons.ttf │ │ ├── icons.woff │ │ └── icons.woff2 ├── simplest.html └── style.css ├── dist ├── SMValidator.js ├── SMValidator.min.js └── SMValidator.pure.min.js ├── package.json └── src ├── SMValidator.js └── config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 WLDragon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SMValidator(strong muscle validator) 2 | 3 | ## Features 4 | - Light weight 5 | 6 | - No dependencies 7 | 8 | - Customizable rules messages and styles 9 | 10 | - Support bootstrap and semantic 11 | 12 | - Useable RegExp on html 13 | 14 | - Support IE8+ 15 | 16 | ## Tutor And Documents 17 | - [中文版教程](https://wldragon.github.io/SMValidator/tutor/tutor1.html) 18 | 19 | - [English Tutor](https://wldragon.github.io/SMValidator/tutor/tutor-en1.html) 20 | 21 | - [快速开始](https://github.com/WLDragon/SMValidator/wiki/%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B) 22 | 23 | ## Preview 24 | - **Required** (required rule in html) 25 | 26 | ``` html 27 | 28 | ``` 29 | 30 | - **RegExp** (using RegExp in html) 31 | 32 | ``` html 33 | 34 | ``` 35 | 36 | - **Your Style** (add .fail-error on input when validate failed) 37 | 38 | ``` html 39 | 40 | ``` 41 | 42 | - **Javascript** (use only js without data-rule) 43 | 44 | ``` javascript 45 | new SMValidator('form', { 46 | rules: { 47 | //define a rule by RegExp 48 | onlyNumber: [/^-?\d+(\.{1}\d+)?$/, 'Please input letters'], 49 | //define a rule by Function 50 | greater: function(val, num) { 51 | return (val*1 > num*1) || 'Please input a number that greater than ' + num; 52 | } 53 | }, 54 | fields: { 55 | //fieldName1 match input's name 56 | fieldName1: { 57 | required: true, 58 | rule: 'onlyNumber|greater(10)', 59 | failCss: 'fail-error' 60 | } 61 | } 62 | }); 63 | ``` 64 | 65 | ## Demos 66 | - [A complex demo](https://wldragon.github.io/SMValidator/) 67 | 68 | - Support third-party UI framework 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
bootstrap demosemantic demo
79 | 80 | ## Install 81 | - ```npm install --save SMValidator``` 82 | 83 | - ```bower install SMValidator``` 84 | 85 | ## Build 86 | - ```npm run build``` 87 | 88 | ## Notice 89 | - checkbox invalid in IE8 90 | 91 | - ` 20 | 21 | 22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 | 30 |
31 | 32 |
33 |
34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 |
42 | 43 |
44 |
45 | 46 | 47 | 48 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /demo/complex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SMValidator复杂案例 7 | 8 | 9 | 10 | 11 |

使用HTML

12 |
13 |
14 |

获取焦点后清除样式,失去焦点时不验证,要求长度大于5,验证成功后边框绿色

15 | 16 |
17 |
18 |

oninput不验证,必填且要求长度小于5,失去焦点时验证

19 |

失败时不使用默认failStyle,只使用默认配置的failHtml

20 | 21 |
22 |
23 |

手动验证,要求内容为-100~100的数字,使用自定义消息容器

24 | 25 | 点击验证 26 |

27 |

验证通过

28 |
29 |
30 |

要求内容为-100~100的数字,使用自定义消息容器,且容器不会消失

31 | 32 |

33 |
34 |
35 |

要求长度大于5,使用自定义类名,不显示规则消息,父容器添加样式

36 | 37 | 长度大于5,这里使用的是自定义文本,不使用规则消息 38 |
39 |
40 |

要求必填,正则匹配只能数字,自定义必填提示语

41 | 42 |
43 |
44 |

长度大于7小于17的密码,必填,获得焦点时不清理样式

45 | 46 |

重复密码

47 | 48 |
49 |
50 |

测试checkbox

51 | 52 | 53 | 54 |
55 |
56 |
57 |

测试one select

58 | 63 |
64 |
65 |

测试multiple select

66 | 73 |
74 |
75 |

只能输入英文数字的textarea

76 | 77 |
78 |
79 |

测试&分割符,必填,长度大于3的数字,自定义容器

80 | 81 |
82 |
83 |
84 |

使用JavaScript,功能更全面

85 |
86 |
87 |

onblur不验证,必填,只能填数字,使用自定义样式

88 |

同一个name绑定多个input

89 |
90 |
91 |
92 |
93 |

oninput不验证,必填,且要求长度为5,失去焦点时验证

94 |

局部禁用focus,获得焦点时不清除样式

95 | 96 |
97 |
98 |

正则匹配只能数字

99 | 100 |
101 |
102 |

正则匹配只能英文字母

103 | 104 |
105 |
106 |

手动验证,长度为4,必须数字,自定义类名,自定义样式,自定义消息容器

107 | 108 | 点击验证 109 | 110 |

要求长度为4

111 |
112 |
113 |

测试radio,因为只有required规则,所以请滚到页面底部点击提交按钮测试

114 | 115 | 116 | 117 |
118 |
119 |

测试async功能,先点击页面底部提交按钮查看效果

120 |

点击true或false按钮模拟收到服务器结果进行设值

121 |
122 | 123 | 124 | 125 |
126 |
127 | 128 | 129 | 130 |
131 |
132 |
133 |

测试&分割符,必填,长度大于3,只能数字

134 | 135 |
136 |
137 |

测试fail/pass,必填,只能数字,长度大于5,通过时文字绿色,失败时自定义错误文本格式

138 | 139 |
140 |
141 | 142 | 143 |
提交按钮在网页底部,测试submit和locate功能
144 | 145 |
146 | 252 | 253 | -------------------------------------------------------------------------------- /demo/semantic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SMValidator semantic skin demo 8 | 9 | 10 | 20 | 21 | 22 | 23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 |
31 | 32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 |
41 | 42 |
43 |
44 | 45 |
46 |
47 |
48 |
49 | 50 |
51 |
52 | 53 |
54 |
55 |
56 |
57 | 58 |
59 |
60 | 61 |
62 |
63 |
64 |
65 |
66 | 67 |
68 |
69 |
70 |
71 |
72 |
73 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /demo/semantic/themes/default/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WLDragon/SMValidator/7434b253cda0e673570bcbe27d06f8bd7a24405b/demo/semantic/themes/default/assets/fonts/icons.eot -------------------------------------------------------------------------------- /demo/semantic/themes/default/assets/fonts/icons.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WLDragon/SMValidator/7434b253cda0e673570bcbe27d06f8bd7a24405b/demo/semantic/themes/default/assets/fonts/icons.otf -------------------------------------------------------------------------------- /demo/semantic/themes/default/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WLDragon/SMValidator/7434b253cda0e673570bcbe27d06f8bd7a24405b/demo/semantic/themes/default/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /demo/semantic/themes/default/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WLDragon/SMValidator/7434b253cda0e673570bcbe27d06f8bd7a24405b/demo/semantic/themes/default/assets/fonts/icons.woff -------------------------------------------------------------------------------- /demo/semantic/themes/default/assets/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WLDragon/SMValidator/7434b253cda0e673570bcbe27d06f8bd7a24405b/demo/semantic/themes/default/assets/fonts/icons.woff2 -------------------------------------------------------------------------------- /demo/simplest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SMValidator最简单的案例 6 | 7 | 8 | 9 |
10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 | 24 | 25 | -------------------------------------------------------------------------------- /demo/style.css: -------------------------------------------------------------------------------- 1 | h2 { 2 | background-color: #090; 3 | color: #fff; 4 | padding: 8px; 5 | margin: 5px; 6 | } 7 | p{ 8 | margin: 0; 9 | color: #ccc; 10 | } 11 | div { 12 | margin: 0 5px 10px; 13 | } 14 | select,input[type="text"],input[type="password"] { 15 | width: 250px; 16 | height: 24px; 17 | padding: 1px; 18 | margin: 0; 19 | font-size: 14px; 20 | border: 1px solid #9a9a9a; 21 | } 22 | input:focus { 23 | outline: #000 auto 0px; 24 | } 25 | .error { 26 | background-color: #ffc; 27 | } 28 | button { 29 | margin-left: 10px; 30 | } 31 | select[multiple] { 32 | height: 100px; 33 | } -------------------------------------------------------------------------------- /dist/SMValidator.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * SMValidator 1.2.7 3 | * Copyright (c) 2016 WLDragon(cwloog@qq.com) 4 | * Released under the MIT License. 5 | */(function (global, factory) { 6 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 7 | typeof define === 'function' && define.amd ? define(factory) : 8 | (global.SMValidator = factory()); 9 | } (this, function () { 10 | 'use strict'; 11 | 12 | var document = window.document; 13 | 14 | var isIE8 = !document.addEventListener; 15 | 16 | function on(context, eventType, callback) { 17 | if(isIE8) { 18 | context.attachEvent('on' + eventType, callback); 19 | }else { 20 | context.addEventListener(eventType, callback); 21 | } 22 | } 23 | 24 | if(isIE8) { 25 | Array.prototype.indexOf = function(searchElement) { 26 | for(var i = 0, len = this.length; i < len; i++) { 27 | if(this[i] === searchElement) return i; 28 | } 29 | return -1; 30 | } 31 | // Source: https://github.com/Alhadis/Snippets/blob/master/js/polyfills/IE8-child-elements.js 32 | Object.defineProperty(Element.prototype, "nextElementSibling", { 33 | get: function(){ 34 | var e = this.nextSibling; 35 | while(e && 1 !== e.nodeType) 36 | e = e.nextSibling; 37 | return e; 38 | } 39 | }); 40 | }else { 41 | //标记输入法是否完成,完成之前不验证表单 42 | var isCompositionend = true; 43 | document.addEventListener('compositionstart', function(e){ 44 | isCompositionend = false; 45 | }); 46 | document.addEventListener('compositionend', function(e){ 47 | isCompositionend = true; 48 | var inputEvent = document.createEvent('HTMLEvents'); 49 | inputEvent.initEvent('input', true, true); 50 | e.target.dispatchEvent(inputEvent); 51 | }); 52 | //值改变时验证表单 53 | document.addEventListener('input', function(e) { 54 | if(isCompositionend) { 55 | var input = e.target; 56 | if(input._sm && !input._sm.rule.disinput) validate(input); 57 | } 58 | }); 59 | } 60 | 61 | //checkbox和radio校验 62 | on(document, 'change', function(e){ 63 | var input = e.target; 64 | if(isCheckBoxOrRadio(input.type)) { 65 | if(input._sm && !input._sm.rule.manul) { 66 | validate(input); 67 | } 68 | } 69 | }); 70 | 71 | function isCheckBoxOrRadio(type) { 72 | return type === 'checkbox' || type === 'radio'; 73 | } 74 | 75 | function isString(obj) { 76 | return typeof obj === 'string'; 77 | } 78 | 79 | var hasOwnProperty = Object.prototype.hasOwnProperty; 80 | function hasOwn(obj, key) { 81 | return hasOwnProperty.call(obj, key); 82 | } 83 | 84 | /**全局属性 */ 85 | var GLOBAL_ATTRIBUTES = [ 86 | 'required', 87 | 'async', 88 | 'short', 89 | 'disinput', 90 | 'disblur', 91 | 'focus', 92 | 'manul', 93 | 'failHtml', 94 | 'passHtml', 95 | 'failStyle', 96 | 'failCss', 97 | 'passStyle', 98 | 'passCss' 99 | ]; 100 | /**input规则赋值时忽略required、async和short属性 */ 101 | var INPUT_ATTRIBUTES = GLOBAL_ATTRIBUTES.slice(3); 102 | 103 | function SMValidator(selectors, options) { 104 | var self = this; 105 | if(!options) options = {}; 106 | //初始化局部属性,如果没填,则使用全局属性 107 | for(var i = GLOBAL_ATTRIBUTES.length - 1; i >= 0; i--) { 108 | var attr = GLOBAL_ATTRIBUTES[i]; 109 | self[attr] = hasOwn(options, attr) ? options[attr] : config[attr]; 110 | } 111 | 112 | self.fields = {}; 113 | self.rules = options.rules || {}; 114 | if(selectors) { 115 | self.submit = options.submit; 116 | 117 | //解析fields字段的规则 118 | for(var k in options.fields) { 119 | self.fields[k] = self.parseField(options.fields[k]); 120 | } 121 | //查询并把规则绑定到input上 122 | self.queryInput(selectors); 123 | } 124 | } 125 | 126 | var _proto = SMValidator.prototype; 127 | 128 | /** 129 | * 提取选择器选择的input 130 | * @selectors 选择描述符 131 | */ 132 | _proto.queryInput = function(selectors) { 133 | var self = this; 134 | var inputs = []; 135 | var els = document.querySelectorAll(selectors); 136 | for (var i = els.length - 1; i >= 0; i--) { 137 | var el = els[i]; 138 | var tagName = el.tagName; 139 | if(tagName === 'FORM') { 140 | el.novalidate = 'novalidate'; 141 | var ins = []; 142 | for (var j = el.length - 1; j >= 0; j--) { 143 | if(self.bindInput(el[j])){ 144 | ins.push(el[j]); 145 | } 146 | } 147 | inputs = inputs.concat(ins); 148 | //如果设置了submit属性,则阻止Form默认的提交行为,回调submit方法 149 | if(self.submit && ins.length) { 150 | el._smInputs = ins; 151 | on(el, 'submit', function(e) { 152 | e.preventDefault(); 153 | var target = e.target || e.srcElement; 154 | var result = SMValidator.validate(target._smInputs, { 155 | locate: true, 156 | short: self.short 157 | }); 158 | self.submit(result, target); 159 | }); 160 | } 161 | }else if(tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') { 162 | if(self.bindInput(el)){ 163 | inputs.push(el); 164 | } 165 | } 166 | } 167 | return inputs; 168 | } 169 | 170 | /**把规则绑定到input上 */ 171 | _proto.bindInput = function(input) { 172 | //如果已经绑定过规则,则不重复处理 173 | if(input._sm) return true; 174 | 175 | var self = this; 176 | var name = input.getAttribute('name'); 177 | var dataRule = input.getAttribute('data-rule'); 178 | var item = dataRule ? this.parseString(dataRule) : this.fields[name]; 179 | if(item) { 180 | input._sm = {rule: item, flag: 0}; 181 | 182 | //对于name相同的input,共用同一个规则,只初始化一次 183 | if(!item._isInit) { 184 | item._isInit = true; 185 | 186 | self.handleProperty(item, 'required'); 187 | self.handleProperty(item, 'async'); 188 | //服务器验证必须是手动验证 189 | if(item.async) item.manul = true; 190 | 191 | //初始化field属性,如果没填,则使用局部或全局属性 192 | for(var i = INPUT_ATTRIBUTES.length - 1; i >= 0; i--) { 193 | var attr = INPUT_ATTRIBUTES[i]; 194 | if(!hasOwn(item, attr)) item[attr] = this[attr]; 195 | } 196 | 197 | self.handleStyle(item, 'failStyle'); 198 | self.handleStyle(item, 'passStyle'); 199 | } 200 | 201 | //当有failStyle或passStyle时记录原始样式 202 | self.recordStyle(input, item.failStyle); 203 | self.recordStyle(input, item.passStyle); 204 | 205 | var _sm = input._sm; 206 | if(isCheckBoxOrRadio(input.type)) { 207 | //对于checkbox和radio只解析一个,其他input都引用这个规则 208 | var inputs = document.querySelectorAll('input[name="' + name + '"]'); 209 | for(var i = inputs.length - 1; i >= 0; i--) { 210 | if(inputs[i] !== input) inputs[i]._sm = _sm; 211 | } 212 | //checkbox和radio只能使用onchange触发校验 213 | item.disblur = item.disinput = true; 214 | item.focus = false; 215 | }else if(!input.style.borderImage){ 216 | //防止IE自动添加borderImage 217 | if(!_sm.style) { 218 | _sm.style = {}; 219 | } 220 | _sm.style.borderImage = ''; 221 | } 222 | 223 | //用于提示消息的html,如果是html文本则新建一个Dom,如果是选择器则使用这个选择器的Dom 224 | self.handleHtml(input, 'failHtml', item); 225 | self.handleHtml(input, 'passHtml', item); 226 | 227 | //记录使用样式的对象,如果className中有+号表示应用样式的是input的父节点,一个+号往上一层 228 | self.handleCss(input, 'failCss', item.failCss); 229 | self.handleCss(input, 'passCss', item.passCss); 230 | 231 | //设置手动后blur和input均失效 232 | if(item.manul) item.disblur = item.disinput = true; 233 | 234 | if(item.focus) { 235 | on(input, 'focus', function(e){ 236 | clear(e.target || e.srcElement); 237 | }); 238 | } 239 | if(!item.disblur) { 240 | on(input, 'blur', function(e){ 241 | validate(e.target || e.srcElement); 242 | }); 243 | } 244 | //注:非IE8浏览器使用document代理input事件 245 | if(isIE8 && !item.disinput) { 246 | input.attachEvent('onpropertychange', function(e){ 247 | if(e.propertyName === 'value') validate(e.srcElement); 248 | }); 249 | } 250 | 251 | return true; 252 | } 253 | 254 | return false; 255 | } 256 | 257 | _proto.handleProperty = function (item, prop) { 258 | //required和async没指定则默认为false 259 | //如果为true则赋值为显示的消息文本 260 | if(!hasOwn(item, prop)) { 261 | item[prop] = false; 262 | }else if(item[prop] === true) { 263 | item[prop] = this[prop]; 264 | } 265 | } 266 | 267 | _proto.handleStyle = function (item, prop) { 268 | if(isString(item[prop])) { 269 | try{ 270 | item[prop] = JSON.parse(item[prop].replace(/'/g,'\"')); 271 | }catch(e) { 272 | console.error('error json format: ' + item[prop]); 273 | } 274 | } 275 | } 276 | 277 | _proto.recordStyle = function (input, style) { 278 | if(!style) return; 279 | if(!input._sm.style) input._sm.style = {}; 280 | var s = input._sm.style; 281 | for(var k in style) { 282 | if(!s[k]) s[k] = input.style[k]; 283 | } 284 | } 285 | 286 | _proto.handleHtml = function (input, prop, item) { 287 | var htmls = item[prop]; 288 | if(!htmls) return; 289 | 290 | input._sm[prop] = []; 291 | if(isString(htmls)) htmls = [htmls]; 292 | for(var i = 0; i < htmls.length; i++) { 293 | var htmlDom; 294 | var html = htmls[i]; 295 | var htmlItem = {}; //object format:{dom:null, quiet:false, keep:false} 296 | if(html.indexOf('*') === 0) { 297 | html = html.substring(1); 298 | //failHtml不使用display:'none'来隐藏,而是设置innerHTML='' 299 | htmlItem.keep = true; 300 | } 301 | if(html.indexOf('!') === 0) { 302 | html = html.substring(1); 303 | //failHtml不使用规则的消息,只显示html 304 | htmlItem.quiet = true; 305 | } 306 | if(html.indexOf('<') > -1) { 307 | //Dom 308 | //去掉“+”号 309 | var tar = input; 310 | while(html.indexOf('+') === 0) { 311 | html = html.substring(1); 312 | tar = tar.parentNode; 313 | } 314 | //使用html字符串生成Dom 315 | var div = document.createElement('div'); 316 | div.innerHTML = html; 317 | htmlDom = div.children[0]; 318 | //把Dom插到相应位置 319 | tar.parentNode.insertBefore(htmlDom, tar.nextElementSibling); 320 | }else { 321 | //选择器 322 | htmlDom = document.querySelector(html); 323 | } 324 | if(htmlDom) { 325 | htmlItem.dom = htmlDom; 326 | input._sm[prop].push(htmlItem); 327 | if(!htmlItem.keep) htmlDom.style.display = 'none'; 328 | } 329 | } 330 | } 331 | 332 | _proto.handleCss = function (input, prop, classNames) { 333 | if(!classNames) return; 334 | if(isString(classNames)) classNames = [classNames]; 335 | input._sm[prop] = []; 336 | for(var i = classNames.length - 1; i >= 0; i--) { 337 | var cn = classNames[i]; 338 | //object format:[{target: element, className: ''},...] 339 | var tar = input; 340 | while(cn.indexOf('+') === 0) { 341 | cn = cn.substring(1); 342 | tar = tar.parentNode; 343 | } 344 | input._sm[prop].push({target: tar, className: cn}); 345 | } 346 | } 347 | 348 | /** 349 | * 解析表示函数或数组规则 350 | */ 351 | _proto.parseRule = function(result, item) { 352 | var n = result.name; 353 | var definition = this.rules[n] || config.rules[n]; 354 | if(definition) { 355 | if(definition instanceof Array) { 356 | //数组正则规则 357 | item.rules.push({rule: definition[0], message: definition[1]}); 358 | }else { 359 | //函数规则 360 | item.rules.push({rule: definition, params: result.params}); 361 | } 362 | } 363 | } 364 | 365 | _proto.parseStringFunction = function(str) { 366 | var result = {name: str}; 367 | var begin = str.indexOf('('); 368 | if(begin > 0) { 369 | //带有参数 370 | result.name = str.substring(0, begin); 371 | if(str.indexOf('{') > -1) { 372 | //failStyle和passStyle只能有一个json格式的参数 373 | result.params = str.substring(begin + 1, str.length - 1); 374 | }else { 375 | result.params = str.substring(begin + 1, str.length - 1).split(','); 376 | } 377 | } 378 | return result; 379 | } 380 | 381 | /** 382 | * 解析规则字符串,使用'|'或'&'分割 383 | */ 384 | _proto.parseString = function(str) { 385 | var item = {rules: []}; 386 | var token = str.indexOf('&') > -1 ? '&' : '|'; 387 | var statements = str.split(token); 388 | item.token = token; 389 | for(var i = statements.length - 1; i >= 0; i--) { 390 | var statement = statements[i]; 391 | if(!statement) continue; //防止规则中出现||的情况 392 | if(statement.indexOf('/') === 0) { 393 | //正则 394 | var a = statement.substring(1).split('/'); 395 | if(a.length === 2) { 396 | //没有修饰符i 397 | item.rules.push({rule: new RegExp(a[0]), message: a[1]}); 398 | }else if(a.length === 3) { 399 | //带有修饰符i 400 | item.rules.push({rule: new RegExp(a[0], 'i'), message: a[2]}); 401 | } 402 | }else { 403 | var result = this.parseStringFunction(statement); 404 | var n = result.name; 405 | var v = true; 406 | if(n.charAt(0) === '!') { 407 | n = n.substring(1); 408 | v = false; 409 | } 410 | if(GLOBAL_ATTRIBUTES.indexOf(n) > -1) { 411 | //关键属性 412 | var params = result.params; 413 | if(params) { 414 | item[n] = params; 415 | }else { 416 | item[n] = v; 417 | } 418 | }else { 419 | //函数或数组规则 420 | this.parseRule(result, item); 421 | } 422 | } 423 | } 424 | return item; 425 | } 426 | 427 | /** 428 | * 解析验证规则,有Array|Function|String|Object四种类型 429 | */ 430 | _proto.parseField = function(item) { 431 | if(item instanceof Array) { 432 | return {rules: [{rule: item[0], message: item[1]}]}; 433 | }else if(item instanceof Function) { 434 | return {rules: [{rule: item}]}; 435 | }else if(isString(item)) { 436 | return this.parseString(item); 437 | }else if(typeof item === 'object') { 438 | item.token = '|'; //初始化item.token,防止没有rule为string时没有token的情况 439 | if(item.rule instanceof Array) { 440 | item.rules = [{rule: item.rule[0], message: item.rule[1]}]; 441 | }else if(item.rule instanceof Function) { 442 | item.rules = [{rule: item.rule}]; 443 | }else if(isString(item.rule)) { 444 | var r = item.rule; 445 | delete item.rule; 446 | if(r.indexOf('&') > -1) { 447 | item.token = '&'; 448 | } 449 | var a = r.split(item.token); 450 | item.rules = []; 451 | for(var i = a.length - 1; i >= 0; i--) { 452 | if(a[i]) this.parseRule(this.parseStringFunction(a[i]), item); 453 | } 454 | } 455 | return item; 456 | } 457 | } 458 | 459 | /** 460 | * 验证通过时去掉样式,验证失败时添加样式 461 | */ 462 | function toggleClass(items, isAdd) { 463 | if(!items) return; 464 | for(var i = items.length - 1; i >= 0; i--) { 465 | var m = items[i]; 466 | var tar = m.target; 467 | //使用数组而不是字符串的方式来处理可以避免字符串查询不完整的情况 468 | var newCss = m.className.split(' '); 469 | var oldCss = tar.className ? tar.className.split(' ') : []; 470 | for(var k = newCss.length - 1; k >=0; k--) { 471 | var c = newCss[k]; 472 | var j = oldCss.indexOf(c); 473 | if(!isAdd && j > -1) { 474 | oldCss.splice(j, 1); 475 | }else if(isAdd && j === -1){ 476 | oldCss.push(c); 477 | } 478 | } 479 | tar.className = oldCss.join(' '); 480 | } 481 | } 482 | 483 | /**显示或隐藏指定的消息标签 */ 484 | function toggleElement(items, isShow, result) { 485 | if(!items) return; 486 | for(var i = items.length - 1; i >= 0; i--) { 487 | var m = items[i]; 488 | if(m.keep) { 489 | if(!isShow && !m.quiet) m.dom.innerHTML = ''; 490 | }else { 491 | m.dom.style.display = isShow ? '' : 'none'; 492 | } 493 | 494 | if(result && !m.quiet) m.dom.innerHTML = result; 495 | } 496 | } 497 | 498 | /**应用样式到input上 */ 499 | function applyStyle(input, style) { 500 | if(!style) return; 501 | for(var k in style) { 502 | input.style[k] = style[k]; 503 | } 504 | } 505 | 506 | function clear(input) { 507 | var sm = input._sm; 508 | applyStyle(input, sm.style); 509 | toggleElement(sm.failHtml, false); 510 | toggleElement(sm.passHtml, false); 511 | toggleClass(sm.failCss, false); 512 | toggleClass(sm.passCss, false); 513 | } 514 | 515 | /**验证input的值 */ 516 | function validate(input, options) { 517 | var type = input.type; 518 | var name = input.name; 519 | var sm = input._sm; 520 | var item = sm.rule; 521 | var result = true; 522 | var results = []; 523 | /**0初始状态 1通过 2失败 */ 524 | var flag = 1; 525 | var value = ''; 526 | var isBreak = item.token === '|'; 527 | if(isCheckBoxOrRadio(type) && input.form) { 528 | //对于checkbox|radio|select-multiple,value为其选择项的数量 529 | var els = input.form.elements[name]; 530 | for(var i = els.length - 1; i >= 0; i--) { 531 | if(els[i].checked) value += ' '; 532 | } 533 | }else if(type === 'select-multiple'){ 534 | var els = input.children; 535 | for(var i = els.length - 1; i >= 0; i--) { 536 | if(els[i].selected && els[i].value) value += ' '; 537 | } 538 | }else { 539 | value = input.value; 540 | } 541 | 542 | if(options && (typeof options.forceFlag === 'number')) { 543 | //强制设置验证结果 544 | flag = options.forceFlag; 545 | //服务端验证,通过forceFlag设置的结果 546 | if(item.async) { 547 | sm.asyncFlag = flag; 548 | if(options.asyncMessage) sm.asyncMessage = options.asyncMessage; 549 | if(flag === 2) { 550 | result = sm.asyncMessage || 'no asyncMessage!'; 551 | } 552 | } 553 | }else { 554 | if(item.async) { 555 | if(item.required && !value) { 556 | flag = 2; 557 | result = item.required; 558 | }else { 559 | if(typeof sm.asyncFlag === 'number') { 560 | flag = sm.asyncFlag; 561 | if(flag === 2) { 562 | result = sm.asyncMessage || 'no asyncMessage!'; 563 | } 564 | }else if(value){ 565 | //如果有值且没有设置过asyncFlag则不通过 566 | flag = 2; 567 | result = item.async; 568 | }else { 569 | flag = 0; 570 | } 571 | } 572 | }else if(value) { 573 | //验证各项规则 574 | for(var i = item.rules.length - 1; i >= 0; i--) { 575 | var ruleItem = item.rules[i]; 576 | var rule = ruleItem.rule; 577 | if(rule instanceof RegExp) { 578 | //正则规则 579 | if(!rule.test(value)) { 580 | result = ruleItem.message; 581 | flag = 2; 582 | if(isBreak) { 583 | break; 584 | }else { 585 | results.push(result); 586 | } 587 | } 588 | }else { 589 | //函数规则 590 | if(ruleItem.params) { 591 | result = rule.apply(null, [value].concat(ruleItem.params)); 592 | }else { 593 | result = rule.call(null, value); 594 | } 595 | if(result !== true) { 596 | flag = 2; 597 | if(isBreak) { 598 | break; 599 | }else { 600 | results.push(result); 601 | } 602 | } 603 | } 604 | } 605 | }else if(item.required) { 606 | flag = 2; 607 | result = item.required; 608 | if(!isBreak) results.push(result); 609 | }else{ 610 | flag = 0; 611 | } 612 | } 613 | 614 | if(!isBreak) { 615 | //2 分割符为&的时候,至少一个规则验证失败 616 | //1 全部规则验证通过 617 | flag = results.length ? 2 : 1; 618 | } 619 | 620 | clear(input); 621 | if(flag === 1) { 622 | toggleClass(sm.failCss, false); 623 | toggleClass(sm.passCss, true); 624 | applyStyle(input, item.passStyle); 625 | toggleElement(sm.passHtml, true); 626 | 627 | if(item.pass) item.pass.call(input); 628 | }else if(flag === 2){ 629 | toggleClass(sm.passCss, false); 630 | toggleClass(sm.failCss, true); 631 | applyStyle(input, item.failStyle); 632 | toggleElement(sm.failHtml, true, isBreak ? result : results.join('
')); 633 | 634 | if(item.fail) item.fail.call(input, isBreak ? result : results); 635 | } 636 | //定位验证失败的字段 637 | if(flag === 2 && isLocate) { 638 | input.scrollIntoView(); 639 | isLocate = false; 640 | } 641 | 642 | return flag !== 2; 643 | } 644 | 645 | /**全局配置 */ 646 | var config = { 647 | async: 'not been validated by async', 648 | required: 'this is required', 649 | rules: {} 650 | }; 651 | 652 | /**公共validate使用的SMValidator实例 */ 653 | var globalInstance; 654 | /** 是否使用了定位错误表单位置 */ 655 | var isLocate; 656 | /**设置全局配置 */ 657 | SMValidator.config = function (options) { 658 | for(var i = GLOBAL_ATTRIBUTES.length - 1; i >= 0; i--) { 659 | var attr = GLOBAL_ATTRIBUTES[i]; 660 | if(hasOwn(options, attr)) config[attr] = options[attr]; 661 | } 662 | if(options.rules) { 663 | for(var k in options.rules) { 664 | config.rules[k] = options.rules[k]; 665 | } 666 | } 667 | 668 | globalInstance = new SMValidator(); 669 | } 670 | /** 671 | * 手动验证表单 672 | * @param inputs{Array|String} 表单数组或表单选择器描述符 673 | * @param options {Object} 674 | * @param options.forceFlag //强行设置验证结果,0没验证 1通过 2失败 675 | * @param options.locate //是否定位到第一个验证失败的表单 676 | * @param options.short //是否遇到验证失败的表单后就跳出 677 | * @param options.asyncMessage //服务器返回来的消息,用于设置带有async属性的验证 678 | * @return 如果全部通过则返回true,否则返回false 679 | */ 680 | SMValidator.validate = function (inputs, options) { 681 | var ins = isString(inputs) ? globalInstance.queryInput(inputs) : inputs; 682 | var passCount = 0; 683 | var count = 0; 684 | var short = false; 685 | if(options) { 686 | isLocate = options.locate; 687 | short = options.short; 688 | } 689 | for(var i = ins.length - 1; i >= 0; i--) { 690 | var input = ins[i]; 691 | if(input._sm) { 692 | count++; 693 | if(validate(input, options)) { 694 | passCount++; 695 | }else if(short) { 696 | return false; 697 | } 698 | } 699 | } 700 | return count === passCount; 701 | } 702 | 703 | SMValidator.reset = function (inputs) { 704 | var ins = isString(inputs) ? globalInstance.queryInput(inputs) : inputs; 705 | for(var i = ins.length - 1; i >= 0; i--) { 706 | clear(ins[i]); 707 | } 708 | } 709 | 710 | function replace(str, loaners) { 711 | for(var i = 0; i < loaners.length; i++) { 712 | str = str.replace('{'+i+'}', loaners[i]); 713 | } 714 | return str; 715 | } 716 | 717 | var lang = { 718 | number: 'only number', 719 | names: 'wrong name format', 720 | email: 'wrong email format', 721 | range_equal: 'value must be equal to {0}', 722 | range_scope: 'value must be greater than {0} and less than {1}', 723 | range_greater: 'value must be greater than {0}', 724 | range_less: 'value must be less than {0}', 725 | range_no_number: 'value must be number', 726 | error_param: 'error param', 727 | length_equal: 'length must be equal to {0}', 728 | length_scope: 'length must be greater than {0} and less than {1}', 729 | length_greater: 'length must be greater than {0}', 730 | length_less: 'length must be less than {0}', 731 | password: 'passwords don\'t match' 732 | } 733 | 734 | SMValidator.config({ 735 | failHtml: '', 736 | failStyle: { 737 | color: '#c00', 738 | border: '1px solid #c00' 739 | }, 740 | rules: { 741 | number: function(val) { 742 | //正负数整数或小数 743 | return /^-?\d+(\.{1}\d+)?$/.test(val) || lang.number; 744 | }, 745 | names: function(val){ 746 | // validate names 747 | return /^(\w+[\-']?\w+\s?)+$/i.test(val) || lang.names; 748 | }, 749 | email: function(val) { 750 | //邮箱格式 751 | return /^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/i.test(val) || lang.email; 752 | }, 753 | range: function(val, a, b) { 754 | val = val * 1; 755 | //数值范围 756 | //range(a,b) 大于a小于b 757 | //range(a,) 大于a 758 | //range(,b) 小于b 759 | //range(a) 等于a 760 | var numberRegExp = /^-?\d+(\.{1}\d+)?$/; 761 | if(numberRegExp.test(val)) { 762 | if((!a || numberRegExp.test(a)) || (!b || numberRegExp.test(b))) { 763 | if(arguments.length === 2) { 764 | return val == a || replace(lang.range_equal, [a]); 765 | }else if(arguments.length === 3) { 766 | if(a && b) { 767 | return (val > a && val < b) || replace(lang.range_scope, [a,b]); 768 | }else if(a) { 769 | return (val > a) || replace(lang.range_greater, [a]); 770 | }else if(b) { 771 | return (val < b) || replace(lang.range_less, [b]); 772 | }else { 773 | return lang.error_param; 774 | } 775 | } 776 | }else { 777 | return lang.error_param; 778 | } 779 | }else { 780 | return lang.range_no_number; 781 | } 782 | }, 783 | length: function(val, a, b) { 784 | //判断字符串长度范围,格式与range一致 785 | var n = val.length; 786 | if(arguments.length === 2) { 787 | return n == a || replace(lang.length_equal, [a]); 788 | }else if(arguments.length === 3){ 789 | if(a && b) { 790 | return (n > a && n < b) || replace(lang.length_scope, [a,b]); 791 | }else if(a) { 792 | return (n > a) || replace(lang.length_greater, [a]); 793 | }else if(b) { 794 | return (n < b) || replace(lang.length_less, [b]); 795 | }else { 796 | return lang.error_param; 797 | } 798 | } 799 | }, 800 | equal: function(val, targetSelector) { 801 | var target = document.querySelector(targetSelector); 802 | return val === target.value || lang.password; 803 | } 804 | } 805 | }); 806 | 807 | /** 808 | * 设置语言包 809 | */ 810 | SMValidator.setLang = function(options) { 811 | for(var k in options) { 812 | lang[k] = options[k]; 813 | } 814 | } 815 | 816 | var skins = { 817 | bootstrap: { 818 | failStyle: false, //覆盖默认样式的值 819 | failHtml: ['!', ''], 820 | failCss: '++has-error has-feedback', 821 | passHtml: '', 822 | passCss: '++has-success has-feedback' 823 | }, 824 | semantic: { 825 | failStyle: false, 826 | failHtml: ['!', '+'], 827 | failCss: '++error', 828 | passHtml: '' 829 | } 830 | } 831 | 832 | /** 833 | * 设置表单外观 834 | * @param {String} skin 以下值可填"bootstrap" 835 | */ 836 | SMValidator.setSkin = function(skin) { 837 | SMValidator.config(skins[skin]); 838 | } 839 | 840 | 841 | return SMValidator; 842 | })); -------------------------------------------------------------------------------- /dist/SMValidator.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * SMValidator 1.2.7 3 | * Copyright (c) 2016 WLDragon(cwloog@qq.com) 4 | * Released under the MIT License. 5 | */!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):e.SMValidator=r()}(this,function(){"use strict";function e(e,r,t){p?e.attachEvent("on"+r,t):e.addEventListener(r,t)}function r(e){return"checkbox"===e||"radio"===e}function t(e){return"string"==typeof e}function n(e,r){return g.call(e,r)}function s(e,r){var t=this;r||(r={});for(var s=d.length-1;s>=0;s--){var a=d[s];t[a]=n(r,a)?r[a]:_[a]}if(t.fields={},t.rules=r.rules||{},e){t.submit=r.submit;for(var l in r.fields)t.fields[l]=t.parseField(r.fields[l]);t.queryInput(e)}}function a(e,r){if(e)for(var t=e.length-1;t>=0;t--){for(var n=e[t],s=n.target,a=n.className.split(" "),l=s.className?s.className.split(" "):[],i=a.length-1;i>=0;i--){var u=a[i],o=l.indexOf(u);!r&&o>-1?l.splice(o,1):r&&o===-1&&l.push(u)}s.className=l.join(" ")}}function l(e,r,t){if(e)for(var n=e.length-1;n>=0;n--){var s=e[n];s.keep?r||s.quiet||(s.dom.innerHTML=""):s.dom.style.display=r?"":"none",t&&!s.quiet&&(s.dom.innerHTML=t)}}function i(e,r){if(r)for(var t in r)e.style[t]=r[t]}function u(e){var r=e._sm;i(e,r.style),l(r.failHtml,!1),l(r.passHtml,!1),a(r.failCss,!1),a(r.passCss,!1)}function o(e,t){var n=e.type,s=e.name,o=e._sm,f=o.rule,c=!0,p=[],m=1,g="",d="|"===f.token;if(r(n)&&e.form)for(var h=e.form.elements[s],v=h.length-1;v>=0;v--)h[v].checked&&(g+=" ");else if("select-multiple"===n)for(var h=e.children,v=h.length-1;v>=0;v--)h[v].selected&&h[v].value&&(g+=" ");else g=e.value;if(t&&"number"==typeof t.forceFlag)m=t.forceFlag,f.async&&(o.asyncFlag=m,t.asyncMessage&&(o.asyncMessage=t.asyncMessage),2===m&&(c=o.asyncMessage||"no asyncMessage!"));else if(f.async)f.required&&!g?(m=2,c=f.required):"number"==typeof o.asyncFlag?(m=o.asyncFlag,2===m&&(c=o.asyncMessage||"no asyncMessage!")):g?(m=2,c=f.async):m=0;else if(g)for(var v=f.rules.length-1;v>=0;v--){var y=f.rules[v],_=y.rule;if(_ instanceof RegExp){if(!_.test(g)){if(c=y.message,m=2,d)break;p.push(c)}}else if(c=y.params?_.apply(null,[g].concat(y.params)):_.call(null,g),c!==!0){if(m=2,d)break;p.push(c)}}else f.required?(m=2,c=f.required,d||p.push(c)):m=0;return d||(m=p.length?2:1),u(e),1===m?(a(o.failCss,!1),a(o.passCss,!0),i(e,f.passStyle),l(o.passHtml,!0),f.pass&&f.pass.call(e)):2===m&&(a(o.passCss,!1),a(o.failCss,!0),i(e,f.failStyle),l(o.failHtml,!0,d?c:p.join("
")),f.fail&&f.fail.call(e,d?c:p)),2===m&&b&&(e.scrollIntoView(),b=!1),2!==m}function f(e,r){for(var t=0;t=0;l--){var i=a[l],u=i.tagName;if("FORM"===u){i.novalidate="novalidate";for(var o=[],f=i.length-1;f>=0;f--)t.bindInput(i[f])&&o.push(i[f]);n=n.concat(o),t.submit&&o.length&&(i._smInputs=o,e(i,"submit",function(e){e.preventDefault();var r=e.target||e.srcElement,n=s.validate(r._smInputs,{locate:!0,"short":t["short"]});t.submit(n,r)}))}else"INPUT"!==u&&"SELECT"!==u&&"TEXTAREA"!==u||t.bindInput(i)&&n.push(i)}return n},v.bindInput=function(t){if(t._sm)return!0;var s=this,a=t.getAttribute("name"),l=t.getAttribute("data-rule"),i=l?this.parseString(l):this.fields[a];if(i){if(t._sm={rule:i,flag:0},!i._isInit){i._isInit=!0,s.handleProperty(i,"required"),s.handleProperty(i,"async"),i.async&&(i.manul=!0);for(var f=h.length-1;f>=0;f--){var m=h[f];n(i,m)||(i[m]=this[m])}s.handleStyle(i,"failStyle"),s.handleStyle(i,"passStyle")}s.recordStyle(t,i.failStyle),s.recordStyle(t,i.passStyle);var g=t._sm;if(r(t.type)){for(var d=c.querySelectorAll('input[name="'+a+'"]'),f=d.length-1;f>=0;f--)d[f]!==t&&(d[f]._sm=g);i.disblur=i.disinput=!0,i.focus=!1}else t.style.borderImage||(g.style||(g.style={}),g.style.borderImage="");return s.handleHtml(t,"failHtml",i),s.handleHtml(t,"passHtml",i),s.handleCss(t,"failCss",i.failCss),s.handleCss(t,"passCss",i.passCss),i.manul&&(i.disblur=i.disinput=!0),i.focus&&e(t,"focus",function(e){u(e.target||e.srcElement)}),i.disblur||e(t,"blur",function(e){o(e.target||e.srcElement)}),p&&!i.disinput&&t.attachEvent("onpropertychange",function(e){"value"===e.propertyName&&o(e.srcElement)}),!0}return!1},v.handleProperty=function(e,r){n(e,r)?e[r]===!0&&(e[r]=this[r]):e[r]=!1},v.handleStyle=function(e,r){if(t(e[r]))try{e[r]=JSON.parse(e[r].replace(/'/g,'"'))}catch(n){console.error("error json format: "+e[r])}},v.recordStyle=function(e,r){if(r){e._sm.style||(e._sm.style={});var t=e._sm.style;for(var n in r)t[n]||(t[n]=e.style[n])}},v.handleHtml=function(e,r,n){var s=n[r];if(s){e._sm[r]=[],t(s)&&(s=[s]);for(var a=0;a-1){for(var o=e;0===i.indexOf("+");)i=i.substring(1),o=o.parentNode;var f=c.createElement("div");f.innerHTML=i,l=f.children[0],o.parentNode.insertBefore(l,o.nextElementSibling)}else l=c.querySelector(i);l&&(u.dom=l,e._sm[r].push(u),u.keep||(l.style.display="none"))}}},v.handleCss=function(e,r,n){if(n){t(n)&&(n=[n]),e._sm[r]=[];for(var s=n.length-1;s>=0;s--){for(var a=n[s],l=e;0===a.indexOf("+");)a=a.substring(1),l=l.parentNode;e._sm[r].push({target:l,className:a})}}},v.parseRule=function(e,r){var t=e.name,n=this.rules[t]||_.rules[t];n&&(n instanceof Array?r.rules.push({rule:n[0],message:n[1]}):r.rules.push({rule:n,params:e.params}))},v.parseStringFunction=function(e){var r={name:e},t=e.indexOf("(");return t>0&&(r.name=e.substring(0,t),e.indexOf("{")>-1?r.params=e.substring(t+1,e.length-1):r.params=e.substring(t+1,e.length-1).split(",")),r},v.parseString=function(e){var r={rules:[]},t=e.indexOf("&")>-1?"&":"|",n=e.split(t);r.token=t;for(var s=n.length-1;s>=0;s--){var a=n[s];if(a)if(0===a.indexOf("/")){var l=a.substring(1).split("/");2===l.length?r.rules.push({rule:new RegExp(l[0]),message:l[1]}):3===l.length&&r.rules.push({rule:new RegExp(l[0],"i"),message:l[2]})}else{var i=this.parseStringFunction(a),u=i.name,o=!0;if("!"===u.charAt(0)&&(u=u.substring(1),o=!1),d.indexOf(u)>-1){var f=i.params;f?r[u]=f:r[u]=o}else this.parseRule(i,r)}}return r},v.parseField=function(e){if(e instanceof Array)return{rules:[{rule:e[0],message:e[1]}]};if(e instanceof Function)return{rules:[{rule:e}]};if(t(e))return this.parseString(e);if("object"==typeof e){if(e.token="|",e.rule instanceof Array)e.rules=[{rule:e.rule[0],message:e.rule[1]}];else if(e.rule instanceof Function)e.rules=[{rule:e.rule}];else if(t(e.rule)){var r=e.rule;delete e.rule,r.indexOf("&")>-1&&(e.token="&");var n=r.split(e.token);e.rules=[];for(var s=n.length-1;s>=0;s--)n[s]&&this.parseRule(this.parseStringFunction(n[s]),e)}return e}};var y,b,_={async:"not been validated by async",required:"this is required",rules:{}};s.config=function(e){for(var r=d.length-1;r>=0;r--){var t=d[r];n(e,t)&&(_[t]=e[t])}if(e.rules)for(var a in e.rules)_.rules[a]=e.rules[a];y=new s},s.validate=function(e,r){var n=t(e)?y.queryInput(e):e,s=0,a=0,l=!1;r&&(b=r.locate,l=r["short"]);for(var i=n.length-1;i>=0;i--){var u=n[i];if(u._sm)if(a++,o(u,r))s++;else if(l)return!1}return a===s},s.reset=function(e){for(var r=t(e)?y.queryInput(e):e,n=r.length-1;n>=0;n--)u(r[n])};var S={number:"only number",names:"wrong name format",email:"wrong email format",range_equal:"value must be equal to {0}",range_scope:"value must be greater than {0} and less than {1}",range_greater:"value must be greater than {0}",range_less:"value must be less than {0}",range_no_number:"value must be number",error_param:"error param",length_equal:"length must be equal to {0}",length_scope:"length must be greater than {0} and less than {1}",length_greater:"length must be greater than {0}",length_less:"length must be less than {0}",password:"passwords don't match"};s.config({failHtml:'',failStyle:{color:"#c00",border:"1px solid #c00"},rules:{number:function(e){return/^-?\d+(\.{1}\d+)?$/.test(e)||S.number},names:function(e){return/^(\w+[\-']?\w+\s?)+$/i.test(e)||S.names},email:function(e){return/^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/i.test(e)||S.email},range:function(e,r,t){e=1*e;var n=/^-?\d+(\.{1}\d+)?$/;return n.test(e)?r&&!n.test(r)&&t&&!n.test(t)?S.error_param:2===arguments.length?e==r||f(S.range_equal,[r]):3===arguments.length?r&&t?e>r&&er||f(S.range_greater,[r]):t?er&&nr||f(S.length_greater,[r]):t?n