├── .gitignore
├── example
├── loader-medium.gif
├── demo.html
└── jquery.validation.css
├── bower.json
├── README.md
├── package.json
├── html5-form-validation.jquery.json
├── LICENSE
├── Gruntfile.js
├── dist
├── jquery.validation.min.js
└── jquery.validation.js
└── src
└── jquery.validation.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | node_modules
3 | npm-debug.log
4 |
--------------------------------------------------------------------------------
/example/loader-medium.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/running-coder/jquery-form-validation/HEAD/example/loader-medium.gif
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "html5-form-validation",
3 | "version": "1.5.3",
4 | "authors": [
5 | "Tom Bertrand"
6 | ],
7 | "description": "jQuery plugin that provides a client site form validation with builtin options and deep customization.",
8 | "main": "jquery.validation.min.js",
9 | "keywords": [
10 | "form",
11 | "html5",
12 | "validate",
13 | "validation",
14 | "input"
15 | ],
16 | "ignore": [],
17 | "licenses": "MIT",
18 | "homepage": "http://www.runningcoder.org/jqueryvalidation/",
19 | "repository": {
20 | "type": "git",
21 | "url": "git://github.com/running-coder/jquery-form-validation.git"
22 | },
23 | "dependencies": {
24 | "jquery": ">=1.7.2"
25 | }
26 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | jQuery Form Validation
2 | ======================
3 |
4 | The jQuery form validation plugin unifies the way to validate HTML forms using JavaScript.
5 | It is a simple clientside library that will save you a lot of time when it comes to adding validation on your HTML form inputs or selects!
6 |
7 | The jQuery form Validation plugin is released under the MIT License.
8 |
9 | The complete documentation, demo and further instructions can be found at www.runningcoder.org
10 |
11 | Documentation
12 | ======================
13 |
14 | www.runningcoder.org/jqueryvalidation/documentation/
15 |
16 | Demos
17 | ======================
18 |
19 | www.runningcoder.org/jqueryvalidation/demo/
20 |
21 | Patch Notes
22 | ======================
23 |
24 | www.runningcoder.org/jqueryvalidation/version/
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "html5-form-validation",
3 | "description": "jQuery plugin that provides a client site form validation with builtin options and deep customization.",
4 | "keywords": [
5 | "form",
6 | "html5",
7 | "validate",
8 | "validation",
9 | "input"
10 | ],
11 | "homepage": "http://www.runningcoder.org/jqueryvalidation/",
12 | "bugs": "https://github.com/running-coder/jquery-form-validation/issues",
13 | "repository": {
14 | "type": "git",
15 | "url": "git://github.com/running-coder/jquery-form-validation.git"
16 | },
17 | "author": {
18 | "name": "Tom Bertrand"
19 | },
20 | "devDependencies": {
21 | "grunt": "~0.4",
22 | "grunt-contrib-clean": "~0.6.0",
23 | "grunt-contrib-uglify": "~0.9.1",
24 | "grunt-contrib-copy": "~0.8.0",
25 | "grunt-replace": "~0.8.0",
26 | "grunt-jsbeautifier": "~0.2.10",
27 | "grunt-stripcomments": "~0.3.1"
28 | },
29 | "version": "1.5.3",
30 | "main": "dist/jquery.validation.min.js"
31 | }
--------------------------------------------------------------------------------
/html5-form-validation.jquery.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "html5-form-validation",
3 | "title": "jQuery Form Validation",
4 | "description": "jQuery plugin that provides a client site form validation with builtin options and deep customization.",
5 | "keywords": [
6 | "form",
7 | "html5",
8 | "validate",
9 | "validation",
10 | "input"
11 | ],
12 | "version": "1.5.3",
13 | "author": {
14 | "name": "Tom Bertrand",
15 | "url": "http://www.runningcoder.org/jqueryvalidation/"
16 | },
17 | "licenses": [
18 | {
19 | "type": "MIT",
20 | "url": "https://github.com/running-coder/jquery-form-validation/blob/master/LICENSE"
21 | }
22 | ],
23 | "bugs": "https://github.com/running-coder/jquery-form-validation/issues",
24 | "homepage": "http://www.runningcoder.org/jqueryvalidation/",
25 | "docs": "http://www.runningcoder.org/jqueryvalidation/documentation/",
26 | "demo": "http://www.runningcoder.org/jqueryvalidation/demo/",
27 | "dependencies": {
28 | "jquery": ">=1.7.2"
29 | }
30 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 tombertrand
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.
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 |
3 | grunt.initConfig({
4 |
5 | pkg: grunt.file.readJSON('package.json'),
6 |
7 | banner: '/*!\n' +
8 | ' * jQuery Form Validation\n' +
9 | ' * Copyright (C) 2015 RunningCoder.org\n' +
10 | ' * Licensed under the MIT license\n' +
11 | ' *\n' +
12 | ' * @author <%= pkg.author.name %>\n' +
13 | ' * @version <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd") %>)\n' +
14 | ' * @link http://www.runningcoder.org/jqueryvalidation/\n' +
15 | '*/\n',
16 |
17 | clean: {
18 | dist: ["dist"]
19 | },
20 |
21 | copy: {
22 | dist: {
23 | files: [
24 | {
25 | src: ['src/jquery.validation.js'],
26 | dest: 'dist/jquery.validation.js'
27 | },
28 | {
29 | src: ['src/jquery.validation.js'],
30 | dest: 'dist/jquery.validation.min.js'
31 | }
32 | ]
33 | }
34 | },
35 |
36 | comments: {
37 | dist: {
38 | options: {
39 | singleline: true,
40 | multiline: true
41 | },
42 | src: [ 'dist/jquery.validation.js']
43 | }
44 | },
45 |
46 | replace: {
47 | banner: {
48 | options: {
49 | patterns: [
50 | {
51 | match: /\/\*![\S\s]+?\*\/[\r\n]*/,
52 | replacement: '<%= banner %>'
53 | }
54 | ]
55 | },
56 | files: [
57 | {
58 | src: ['src/jquery.validation.js'],
59 | dest: 'src/jquery.validation.js'
60 | }
61 | ]
62 | },
63 | removeDebug: {
64 | options: {
65 | patterns: [
66 | {
67 | match: /\/\/\s?\{debug}[\s\S]*?\{\/debug}/g,
68 | replacement: ''
69 | }
70 | ]
71 | },
72 | files: [
73 | {
74 | src: ['dist/jquery.validation.min.js'],
75 | dest: 'dist/jquery.validation.min.js'
76 | }
77 | ]
78 | },
79 | removeComments: {
80 | options: {
81 | patterns: [
82 | {
83 | match: /\/\*[^!][\S\s]+?\*\//gm,
84 | replacement: ''
85 | }
86 | ]
87 | },
88 | files: [
89 | {
90 | src: ['dist/jquery.validation.js'],
91 | dest: 'dist/jquery.validation.js'
92 | }
93 | ]
94 | }
95 | },
96 |
97 | jsbeautifier : {
98 | files : ['dist/jquery.validation.js'],
99 | options : {
100 | }
101 | },
102 |
103 | uglify: {
104 | dist: {
105 | options: {
106 | mangle: true,
107 | compress: true,
108 | banner: '<%= banner %>'
109 | },
110 | files: {
111 | 'dist/jquery.validation.min.js': ['dist/jquery.validation.min.js']
112 | }
113 | }
114 |
115 | }
116 |
117 | });
118 |
119 | grunt.loadNpmTasks('grunt-contrib-clean');
120 | grunt.loadNpmTasks('grunt-contrib-copy');
121 | grunt.loadNpmTasks('grunt-stripcomments');
122 | grunt.loadNpmTasks('grunt-replace');
123 | grunt.loadNpmTasks("grunt-jsbeautifier");
124 | grunt.loadNpmTasks('grunt-contrib-uglify');
125 |
126 | grunt.registerTask('default', [
127 | 'clean:dist',
128 | 'replace:banner',
129 | 'copy:dist',
130 | 'comments',
131 | 'replace:removeComments',
132 | 'jsbeautifier',
133 | 'replace:removeDebug',
134 | 'uglify'
135 | ]);
136 |
137 | };
138 |
--------------------------------------------------------------------------------
/example/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Signup_v1 Demo
20 |
21 |
29 |
30 |
118 |
119 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/example/jquery.validation.css:
--------------------------------------------------------------------------------
1 | /*------------------------------------*\
2 | CONTENTS
3 | \*------------------------------------*/
4 | /*
5 | NORMALIZE BUTTON & INPUT - https://github.com/necolas/normalize.css
6 | LAYOUT
7 | INPUT, BUTTON & LABEL
8 | ERROR
9 | */
10 |
11 | /*------------------------------------*\
12 | NORMALIZE BUTTON & INPUT
13 | \*------------------------------------*/
14 |
15 | /**
16 | * Known limitation: by default, Chrome and Safari on OS X allow very limited
17 | * styling of `select`, unless a `border` property is set.
18 | */
19 |
20 | /**
21 | * 1. Correct color not being inherited.
22 | * Known issue: affects color of disabled elements.
23 | * 2. Correct font properties not being inherited.
24 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
25 | */
26 |
27 | button,
28 | input,
29 | optgroup,
30 | select,
31 | textarea {
32 | color: inherit; /* 1 */
33 | font: inherit; /* 2 */
34 | margin: 0; /* 3 */
35 | }
36 |
37 | /**
38 | * Address `overflow` set to `hidden` in IE 8/9/10/11.
39 | */
40 |
41 | button {
42 | overflow: visible;
43 | }
44 |
45 | /**
46 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
47 | * All other form control elements do not inherit `text-transform` values.
48 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
49 | * Correct `select` style inheritance in Firefox.
50 | */
51 |
52 | button,
53 | select {
54 | text-transform: none;
55 | }
56 |
57 | /**
58 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
59 | * and `video` controls.
60 | * 2. Correct inability to style clickable `input` types in iOS.
61 | * 3. Improve usability and consistency of cursor style between image-type
62 | * `input` and others.
63 | */
64 |
65 | button,
66 | html input[type="button"], /* 1 */
67 | input[type="reset"],
68 | input[type="submit"] {
69 | -webkit-appearance: button; /* 2 */
70 | cursor: pointer; /* 3 */
71 | }
72 |
73 | /**
74 | * Re-set default cursor for disabled elements.
75 | */
76 |
77 | button[disabled],
78 | html input[disabled] {
79 | cursor: default;
80 | }
81 |
82 | /**
83 | * Remove inner padding and border in Firefox 4+.
84 | */
85 |
86 | button::-moz-focus-inner,
87 | input::-moz-focus-inner {
88 | border: 0;
89 | padding: 0;
90 | }
91 |
92 | /**
93 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in
94 | * the UA stylesheet.
95 | */
96 |
97 | input {
98 | line-height: normal;
99 | }
100 |
101 |
102 | /*------------------------------------*\
103 | LAYOUT
104 | \*------------------------------------*/
105 |
106 | .validation-form-container {
107 | position: relative;
108 | background-color: #fff;
109 | font-family: "Helvetica Neue", "Helvetica", Arial;
110 | color: rgba(0, 0, 0, 0.7);
111 | font-size: 16px;
112 | padding: 16px;
113 | width: 100%;
114 | max-width: 500px;
115 | border-radius: 4px;
116 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1) inset;
117 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1) inset;
118 | line-height: 16px;
119 | }
120 |
121 | .validation-form-container * {
122 | -webkit-box-sizing: border-box;
123 | -moz-box-sizing: border-box;
124 | box-sizing: border-box;
125 | }
126 |
127 | .validation-form-container :last-child {
128 | margin-bottom: 0em;
129 | }
130 |
131 | .validation-form-container .field {
132 | clear: both;
133 | margin: 0em 0em 1em;
134 | }
135 |
136 | .validation-form-container ul {
137 | list-style: none;
138 | margin: 0.2em 0;
139 | padding: 0;
140 | }
141 |
142 | .validation-form-container .ui.loader.active {
143 | display: block;
144 | }
145 | .validation-form-container .ui.loader {
146 | width: 32px;
147 | height: 32px;
148 | background: url(loader-medium.gif) no-repeat;
149 | background-position: 48% 0px;
150 | display: none;
151 | position: absolute;
152 | top: 50%;
153 | left: 50%;
154 | margin: 0px;
155 | z-index: 1000;
156 | -webkit-transform: translateX(-50%) translateY(-50%);
157 | -ms-transform: translateX(-50%) translateY(-50%);
158 | transform: translateX(-50%) translateY(-50%);
159 | }
160 |
161 |
162 | /*------------------------------------*\
163 | INPUT, BUTTON & LABEL
164 | \*------------------------------------*/
165 |
166 | .validation-form-container .ui.button {
167 | cursor: pointer;
168 | display: inline-block;
169 | vertical-align: middle;
170 | min-height: 1em;
171 | outline: none;
172 | border: none;
173 | background-color: #FAFAFA;
174 | color: #808080;
175 | margin: 0em;
176 | padding: 0.8em 1.5em;
177 | font-size: 1rem;
178 | text-transform: uppercase;
179 | line-height: 1;
180 | font-weight: bold;
181 | font-style: normal;
182 | text-align: center;
183 | text-decoration: none;
184 | background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), to(rgba(0, 0, 0, 0.05)));
185 | background-image: -webkit-linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.05));
186 | background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.05));
187 | border-radius: 0.25em;
188 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.08) inset;
189 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.08) inset;
190 | -webkit-user-select: none;
191 | -moz-user-select: none;
192 | -ms-user-select: none;
193 | user-select: none;
194 | -webkit-box-sizing: border-box;
195 | -moz-box-sizing: border-box;
196 | -ms-box-sizing: border-box;
197 | box-sizing: border-box;
198 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
199 | -webkit-transition: opacity 0.25s ease, background-color 0.25s ease, color 0.25s ease, background 0.25s ease, -webkit-box-shadow 0.25s ease;
200 | transition: opacity 0.25s ease, background-color 0.25s ease, color 0.25s ease, background 0.25s ease, box-shadow 0.25s ease;
201 | }
202 |
203 | .validation-form-container .ui.blue.button {
204 | background-color: #6ECFF5;
205 | color: #FFFFFF;
206 | }
207 |
208 | .validation-form-container .ui.blue.button:hover,
209 | .validation-form-container .ui.blue.button.active {
210 | background-color: #1AB8F3;
211 | color: #FFFFFF;
212 | }
213 |
214 | .validation-form-container .ui.blue.button:active {
215 | background-color: #0AA5DF;
216 | color: #FFFFFF;
217 | }
218 |
219 | .validation-form-container .ui.mini.button {
220 | font-size: 0.8rem;
221 | padding: 0.6em 0.8em;
222 | }
223 |
224 | .validation-form-container .ui.basic.button {
225 | background-color: transparent !important;
226 | background-image: none;
227 | color: #808080 !important;
228 | font-weight: normal;
229 | text-transform: none;
230 | -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1) inset;
231 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1) inset;
232 | }
233 |
234 | .validation-form-container .ui.input {
235 | width: 100%;
236 | font-size: 1em;
237 | display: inline-block;
238 | position: relative;
239 | color: rgba(0, 0, 0, 0.7);
240 | }
241 |
242 | .validation-form-container .ui.labeled.input input {
243 | padding-right: 2.5em !important;
244 | }
245 |
246 | .validation-form-container textarea,
247 | .validation-form-container input[type="text"],
248 | .validation-form-container input[type="password"] {
249 | width: 100%;
250 | margin: 0em;
251 | padding: 0.65em 1em;
252 | font-size: 1em;
253 | background-color: #FFFFFF;
254 | border: 1px solid rgba(0, 0, 0, 0.15);
255 | outline: none;
256 | color: rgba(0, 0, 0, 0.7);
257 | border-radius: 0.3125em;
258 | -webkit-transition: background-color 0.3s ease-out, -webkit-box-shadow 0.2s ease, border-color 0.2s ease;
259 | transition: background-color 0.3s ease-out, box-shadow 0.2s ease, border-color 0.2s ease;
260 | -webkit-box-shadow: 0em 0em 0em 0em rgba(0, 0, 0, 0.3) inset;
261 | box-shadow: 0em 0em 0em 0em rgba(0, 0, 0, 0.3) inset;
262 | -webkit-appearance: none;
263 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
264 | }
265 |
266 | .validation-form-container textarea:focus,
267 | .validation-form-container input[type="text"]:focus,
268 | .validation-form-container input[type="password"]:focus {
269 | color: rgba(0, 0, 0, 0.85);
270 | border-color: rgba(0, 0, 0, 0.2);
271 | border-bottom-left-radius: 0;
272 | border-top-left-radius: 0;
273 | -webkit-appearance: none;
274 | -webkit-box-shadow: 0.3em 0em 0em 0em rgba(0, 0, 0, 0.2) inset;
275 | box-shadow: 0.3em 0em 0em 0em rgba(0, 0, 0, 0.2) inset;
276 | }
277 |
278 | .validation-form-container textarea[readonly],
279 | .validation-form-container textarea[disabled],
280 | .validation-form-container input[readonly],
281 | .validation-form-container input[disabled] {
282 | cursor: not-allowed;
283 | background-color: #f7f7f7;
284 | color: #999;
285 | }
286 |
287 | .validation-form-container .field > label {
288 | margin: 0em 0em 0.3em;
289 | display: block;
290 | color: #555555;
291 | font-size: 0.875em;
292 | position: relative;
293 | }
294 |
295 | .validation-form-container .ui.label {
296 | display: inline-block;
297 | vertical-align: middle;
298 | margin: -0.25em 0.25em 0em;
299 | background-color: #E8E8E8;
300 | border-color: #E8E8E8;
301 | padding: 0.5em 0.8em;
302 | color: rgba(0, 0, 0, 0.65);
303 | text-transform: uppercase;
304 | font-weight: normal;
305 | border-radius: 0.325em;
306 | -webkit-box-sizing: border-box;
307 | -moz-box-sizing: border-box;
308 | -ms-box-sizing: border-box;
309 | box-sizing: border-box;
310 | -webkit-transition: background 0.1s linear;
311 | transition: background 0.1s linear;
312 | }
313 |
314 | .validation-form-container .ui.corner.label {
315 | top: 1px;
316 | right: 1px;
317 | overflow: hidden;
318 | font-size: 0.7em;
319 | border-radius: 0 0.3125em;
320 | background-color: transparent;
321 | position: absolute;
322 | z-index: 10;
323 | margin: 0em;
324 | width: 3em;
325 | height: 3em;
326 | padding: 0em;
327 | text-align: center;
328 | -webkit-transition: color 0.2s ease;
329 | transition: color 0.2s ease;
330 | }
331 |
332 | .validation-form-container .ui.corner.label:after {
333 | position: absolute;
334 | content: "";
335 | right: 0em;
336 | top: 0em;
337 | z-index: -1;
338 | width: 0em;
339 | height: 0em;
340 | border-top: 0em solid transparent;
341 | border-right: 3em solid transparent;
342 | border-bottom: 3em solid transparent;
343 | border-left: 0em solid transparent;
344 | border-right-color: inherit;
345 | -webkit-transition: border-color 0.2s ease;
346 | transition: border-color 0.2s ease;
347 | }
348 |
349 | .validation-form-container .ui.corner.label .icon {
350 | font-size: 2em;
351 | margin: 0.25em 0 0 0.5em;
352 | width: auto;
353 | display: inline-block;
354 | height: 1em;
355 | font-style: normal;
356 | line-height: 1;
357 | font-weight: normal;
358 | text-decoration: inherit;
359 | text-align: center;
360 | speak: none;
361 | -webkit-font-smoothing: antialiased;
362 | -moz-font-smoothing: antialiased;
363 | font-smoothing: antialiased;
364 | }
365 |
366 |
367 | /*------------------------------------*\
368 | ERROR
369 | \*------------------------------------*/
370 |
371 | div.error,
372 | div.error-list,
373 | label.error,
374 | input.error,
375 | select.error,
376 | textarea.error {
377 | color: #D95C5C !important;
378 | border-color: #D95C5C !important;
379 | }
380 |
381 |
382 | .validation-form-container .error .corner.label {
383 | border-color: #D95C5C;
384 | color: #FFFFFF;
385 | }
--------------------------------------------------------------------------------
/dist/jquery.validation.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery Form Validation
3 | * Copyright (C) 2015 RunningCoder.org
4 | * Licensed under the MIT license
5 | *
6 | * @author Tom Bertrand
7 | * @version 1.5.3 (2015-12-02)
8 | * @link http://www.runningcoder.org/jqueryvalidation/
9 | */
10 | !function(window,document,$,undefined){function _buildRegexFromString(a){function b(){}if(!a||"string"!=typeof a&&!(a instanceof RegExp))return b(),!1;"string"!=typeof a&&(a=a.toString());for(var c,d,e,f=a.charAt(0),g=a.length-1;g>0&&/[gimsxeU]/.test(a.charAt(g));)g--;a.charAt(g)!==f&&(f=null),f&&g!==a.length-1&&(d=a.substr(g+1,a.length-1)),c=f?a.substr(1,g-1):a;try{e=new RegExp(c,d)}catch(h){return b(),!1}return e}function isEmpty(a){for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}window.Validation={form:[],labels:{},hasScrolled:!1},"function"!=typeof Object.preventExtensions&&(Object.preventExtensions=function(a){return a});var _rules={NOTEMPTY:/\S/,INTEGER:/^\d+$/,NUMERIC:/^\d+(?:[,\s]\d{3})*(?:\.\d+)?$/,MIXED:/^[\w\s-]+$/,NAME:/^['a-zãàáäâẽèéëêìíïîõòóöôùúüûñç\s-]+$/i,NOSPACE:/^(?!\s)\S*$/,TRIM:/^[^\s].*[^\s]$/,DATE:/^\d{4}-\d{2}-\d{2}(\s\d{2}:\d{2}(:\d{2})?)?$/,EMAIL:/^([^@]+?)@(([a-z0-9]-*)*[a-z0-9]+\.)+([a-z0-9]+)$/i,URL:/^(https?:\/\/)?((([a-z0-9]-*)*[a-z0-9]+\.?)*([a-z0-9]+))(\/[\w?=\.-]*)*$/,PHONE:/^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/,OPTIONAL:/\S/,COMPARISON:/^\s*([LV])\s*([<>]=?|==|!=)\s*([^<>=!]+?)\s*$/},_messages={"default":"$ contain error(s).",NOTEMPTY:"$ must not be empty.",INTEGER:"$ must be an integer.",NUMERIC:"$ must be numeric.",MIXED:"$ must be letters or numbers (no special characters).",NAME:"$ must not contain special characters.",NOSPACE:"$ must not contain spaces.",TRIM:"$ must not start or end with space character.",DATE:"$ is not a valid with format YYYY-MM-DD.",EMAIL:"$ is not valid.",URL:"$ is not valid.",PHONE:"$ is not a valid phone number.","<":"$ must be less than % characters.","<=":"$ must be less or equal to % characters.",">":"$ must be greater than % characters.",">=":"$ must be greater or equal to % characters.","==":"$ must be equal to %","!=":"$ must be different than %"},_data={validation:"data-validation",validationMessage:"data-validation-message",regex:"data-validation-regex",regexReverse:"data-validation-regex-reverse",regexMessage:"data-validation-regex-message",group:"data-validation-group",label:"data-validation-label",errorList:"data-error-list"},_options={submit:{settings:{form:null,display:"inline",insertion:"append",allErrors:!1,trigger:"click",button:"[type='submit']",errorClass:"error",errorListClass:"error-list",errorListContainer:null,errorTemplate:null,inputContainer:null,clear:"focusin",scrollToError:!1},callback:{onInit:null,onValidate:null,onError:null,onBeforeSubmit:null,onSubmit:null,onAfterSubmit:null}},dynamic:{settings:{trigger:null,delay:300},callback:{onSuccess:null,onError:null,onComplete:null}},rules:{},messages:{},labels:{},debug:!1},_supported={submit:{settings:{display:["inline","block"],insertion:["append","prepend"],allErrors:[!0,!1],clear:["focusin","keypress",!1],trigger:["click","dblclick","focusout","hover","mousedown","mouseenter","mouseleave","mousemove","mouseout","mouseover","mouseup","toggle"]}},dynamic:{settings:{trigger:["focusout","keydown","keypress","keyup"]}},debug:[!0,!1]},Validation=function(node,options){function extendRules(){options.rules=$.extend(!0,{},_rules,options.rules)}function extendMessages(){options.messages=$.extend(!0,{},_messages,options.messages)}function extendOptions(){options instanceof Object||(options={});var a=Object.preventExtensions($.extend(!0,{},_options));for(var b in options)if(options.hasOwnProperty(b)&&"debug"!==b)if(~["labels","messages","rules"].indexOf(b)&&options[b]instanceof Object)a[b]=options[b];else if(_options[b]&&options[b]instanceof Object)for(var c in options[b])if(options[b].hasOwnProperty(c)&&_options[b][c]&&options[b][c]instanceof Object){for(var d in options[b][c])options[b][c].hasOwnProperty(d)&&_supported[b]&&_supported[b][c]&&_supported[b][c][d]&&-1===$.inArray(options[b][c][d],_supported[b][c][d])&&delete options[b][c][d];a[b]&&a[b][c]&&(a[b][c]=$.extend(Object.preventExtensions(a[b][c]),options[b][c]))}a.dynamic.settings.trigger&&"keypress"===a.dynamic.settings.trigger&&"keypress"===a.submit.settings.clear&&(a.dynamic.settings.trigger="keydown"),options=a}function delegateDynamicValidation(){if(!options.dynamic.settings.trigger)return!1;if(!node.find("["+_data.validation+"],["+_data.regex+"]")[0])return!1;var a=options.dynamic.settings.trigger+delegateSuffix;"focusout"!==options.dynamic.settings.trigger&&(a+=" change"+delegateSuffix+" paste"+delegateSuffix),$.each(node.find("["+_data.validation+"],["+_data.regex+"]"),function(b,c){$(c).unbind(a).on(a,function(a){if($(this).is(":disabled"))return!1;var b=this,c=a.keyCode||null;_typeWatch(function(){validateInput(b)?_executeCallback(options.dynamic.callback.onSuccess,[node,b,c]):(displayOneError(b.name),_executeCallback(options.dynamic.callback.onError,[node,b,c,errors[b.name]])),_executeCallback(options.dynamic.callback.onComplete,[node,b,c])},options.dynamic.settings.delay)})})}function delegateValidation(){_executeCallback(options.submit.callback.onInit,[node]);var a=options.submit.settings.trigger+".vd";return node.find(options.submit.settings.button)[0]?(node.on("submit",!1),void node.find(options.submit.settings.button).off(".vd").on(a,function(a){return a.preventDefault(),resetErrors(),_executeCallback(options.submit.callback.onValidate,[node]),validateForm()?(_executeCallback(options.submit.callback.onBeforeSubmit,[node]),"function"==typeof options.submit.callback.onSubmit?_executeCallback(options.submit.callback.onSubmit,[node,formData])===!0&&submitForm():submitForm(),_executeCallback(options.submit.callback.onAfterSubmit,[node])):(displayErrors(),_executeCallback(options.submit.callback.onError,[node,errors,formData])),!1})):!1}function validateForm(){var a=isEmpty(errors);return formData={},$.each(node.find('input:not([type="submit"]), select, textarea').not(":disabled"),function(b,c){c=$(c);var d=_getInputValue(c[0]),e=c.attr("name");e&&(/\[]$/.test(e)?(e=e.replace(/\[]$/,""),formData[e]instanceof Array||(formData[e]=[]),formData[e].push(d)):formData[e]=d),(c.attr(_data.validation)||c.attr(_data.regex))&&(validateInput(c[0],d)||(a=!1))}),prepareFormData(),a}function prepareFormData(){var a,b,c={};for(var d in formData)if(formData.hasOwnProperty(d)){b=0,a=d.split(/\[(.+?)]/g);for(var e={},f=[],g=a.length-1;g>=0;g--)""!==a[g]?(f.length<1?e[a[g]]=Number(formData[d])||formData[d]:(e={},e[a[g]]=f[f.length-1]),f.push(e)):a.splice(g,1);c=$.extend(!0,c,e)}formData=c}function validateInput(a,b){var c=$(a).attr("name"),b=b||_getInputValue(a);if(!c)return!1;var d=c.replace(/]$/,"").split(/]\[|[[\]]/g),e=window.Validation.labels[c]||options.labels[c]||$(a).attr(_data.label)||d[d.length-1],f=$(a).attr(_data.validation),g=$(a).attr(_data.validationMessage),h=$(a).attr(_data.regex),i=!($(a).attr(_data.regexReverse)===undefined),j=$(a).attr(_data.regexMessage),k=!1;if(f&&(f=_api._splitValidation(f)),f instanceof Array&&f.length>0){if(""===$.trim(b)&&~f.indexOf("OPTIONAL"))return!0;$.each(f,function(a,d){if(k===!0)return!0;try{validateRule(b,d)}catch(f){(g||!options.submit.settings.allErrors)&&(k=!0),f[0]=g||f[0],registerError(c,f[0].replace("$",e).replace("%",f[1]))}})}if(h){var l=_buildRegexFromString(h);if(!(l instanceof RegExp))return!0;try{validateRule(b,l,i)}catch(m){m[0]=j||m[0],registerError(c,m[0].replace("$",e))}}return!errors[c]||errors[c]instanceof Array&&0===errors[c].length}function validateRule(value,rule,reversed){if(rule instanceof RegExp){var isValid=rule.test(value);if(reversed&&(isValid=!isValid),!isValid)throw[options.messages["default"],""]}else if(options.rules[rule]){if(!options.rules[rule].test(value))throw[options.messages[rule],""]}else{var comparison=rule.match(options.rules.COMPARISON);if(comparison&&4===comparison.length){var type=comparison[1],operator=comparison[2],compared=comparison[3],comparedValue;switch(type){case"L":if(isNaN(compared))return!1;if(!value||eval(value.length+operator+parseFloat(compared))===!1)throw[options.messages[operator],compared];break;case"V":default:if(isNaN(compared)){if(comparedValue=node.find('[name="'+compared+'"]').val(),!comparedValue)return!1;if(!value||!eval('"'+encodeURIComponent(value)+'"'+operator+'"'+encodeURIComponent(comparedValue)+'"'))throw[options.messages[operator].replace(" characters",""),compared]}else if(!value||isNaN(value)||!eval(value+operator+parseFloat(compared)))throw[options.messages[operator].replace(" characters",""),compared]}}}}function registerError(a,b){errors[a]||(errors[a]=[]),b=b.capitalize();for(var c=!1,d=0;d";if(!errors.hasOwnProperty(a))return!1;if(b=node.find('[name="'+a+'"]'),e=null,!b[0])return!1;if(f=b.attr(_data.group),f?(g=node.find('[name="'+a+'"]'),e=node.find('[id="'+f+'"]'),e[0]&&(e.addClass(options.submit.settings.errorClass),d=e)):(b.addClass(options.submit.settings.errorClass),options.submit.settings.inputContainer&&b.parentsUntil(node,options.submit.settings.inputContainer).addClass(options.submit.settings.errorClass),c=b.attr("id"),c&&(e=node.find('label[for="'+c+'"]')[0]),e||(e=b.parentsUntil(node,"label")[0]),e&&(e=$(e),e.addClass(options.submit.settings.errorClass))),"inline"===options.submit.settings.display?d=options.submit.settings.errorListContainer?b.parentsUntil(node,options.submit.settings.errorListContainer):d||b.parent():"block"===options.submit.settings.display&&(d=node),"inline"===options.submit.settings.display&&d.find("["+_data.errorList+"]")[0])return!1;("inline"===options.submit.settings.display||"block"===options.submit.settings.display&&!d.find("["+_data.errorList+"]")[0])&&("append"===options.submit.settings.insertion?d.append(h):"prepend"===options.submit.settings.insertion&&d.prepend(h));for(var i=0;i"+options.submit.settings.errorTemplate.replace("{{validation-message}}",errors[a][i])+"":""+errors[a][i]+"");if(options.submit.settings.clear||options.dynamic.settings.trigger){f&&g&&(b=g);var j="coucou"+resetSuffix;options.submit.settings.clear&&(j+=" "+options.submit.settings.clear+resetSuffix,~["radio","checkbox"].indexOf(b[0].type)&&(j+=" change"+resetSuffix)),options.dynamic.settings.trigger&&(j+=" "+options.dynamic.settings.trigger+resetSuffix,"focusout"===options.dynamic.settings.trigger||~["radio","checkbox"].indexOf(b[0].type)||(j+=" change"+resetSuffix+" paste"+resetSuffix)),b.unbind(j).on(j,function(a,b,c,d,e){return function(){e?$(c).hasClass(options.submit.settings.errorClass)&&resetOneError(a,b,c,d,e):$(b).hasClass(options.submit.settings.errorClass)&&resetOneError(a,b,c,d)}}(a,b,e,d,f))}if(options.submit.settings.scrollToError&&!window.Validation.hasScrolled){window.Validation.hasScrolled=!0;var k=parseFloat(options.submit.settings.scrollToError.offset)||0,l=parseFloat(options.submit.settings.scrollToError.duration)||500,m="block"===options.submit.settings.display?d:b;$("html, body").animate({scrollTop:m.offset().top+k},l)}}function displayErrors(){for(var a in errors)errors.hasOwnProperty(a)&&displayOneError(a)}function resetOneError(a,b,c,d,e){if(delete errors[a],d)options.submit.settings.inputContainer&&(e?c:b).parentsUntil(node,options.submit.settings.inputContainer).removeClass(options.submit.settings.errorClass),c&&c.removeClass(options.submit.settings.errorClass),b.removeClass(options.submit.settings.errorClass),"inline"===options.submit.settings.display&&d.find("["+_data.errorList+"]").remove();else{if(!b&&(b=node.find('[name="'+a+'"]'),!b[0]))return!1;b.trigger("coucou"+resetSuffix)}}function resetErrors(){errors=[],window.Validation.hasScrolled=!1,node.find("["+_data.errorList+"]").remove(),node.find("."+options.submit.settings.errorClass).removeClass(options.submit.settings.errorClass)}function submitForm(){node[0].submit()}function destroy(){return resetErrors(),node.find("["+_data.validation+"],["+_data.regex+"]").off(delegateSuffix+" "+resetSuffix),node.find(options.submit.settings.button).off(delegateSuffix).on("click"+delegateSuffix,function(){$(this).closest("form")[0].submit()}),!0}var errors=[],messages={},formData={},delegateSuffix=".vd",resetSuffix=".vr";window.Validation.hasScrolled=!1;var _getInputValue=function(a){var b;switch($(a).attr("type")){case"checkbox":b=$(a).is(":checked")?1:"";break;case"radio":b=node.find('input[name="'+$(a).attr("name")+'"]:checked').val()||"";break;default:b=$(a).val()}return b},_typeWatch=function(){var a=0;return function(b,c){clearTimeout(a),a=setTimeout(b,c)}}(),_executeCallback=function(a,b){if(!a)return!1;var c;if("function"==typeof a)c=a;else if("string"==typeof a||a instanceof Array){c=window,"string"==typeof a&&(a=[a,[]]);for(var d=a[0].split("."),e=a[1],f=!0,g=0;g>>0,c=Number(arguments[1])||0;for(c=0>c?Math.ceil(c):Math.floor(c),0>c&&(c+=b);b>c;c++)if(c in this&&this[c]===a)return c;return-1})}(window,document,window.jQuery);
--------------------------------------------------------------------------------
/dist/jquery.validation.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery Form Validation
3 | * Copyright (C) 2015 RunningCoder.org
4 | * Licensed under the MIT license
5 | *
6 | * @author Tom Bertrand
7 | * @version 1.5.3 (2015-12-02)
8 | * @link http://www.runningcoder.org/jqueryvalidation/
9 | */
10 | ;
11 | (function(window, document, $, undefined) {
12 |
13 | window.Validation = {
14 | form: [],
15 | labels: {},
16 | hasScrolled: false
17 | };
18 |
19 | if (typeof Object.preventExtensions !== "function") {
20 | Object.preventExtensions = function(obj) {
21 | return obj;
22 | };
23 | }
24 | var _rules = {
25 | NOTEMPTY: /\S/,
26 | INTEGER: /^\d+$/,
27 | NUMERIC: /^\d+(?:[,\s]\d{3})*(?:\.\d+)?$/,
28 | MIXED: /^[\w\s-]+$/,
29 | NAME: /^['a-zãàáäâẽèéëêìíïîõòóöôùúüûñç\s-]+$/i,
30 | NOSPACE: /^(?!\s)\S*$/,
31 | TRIM: /^[^\s].*[^\s]$/,
32 | DATE: /^\d{4}-\d{2}-\d{2}(\s\d{2}:\d{2}(:\d{2})?)?$/,
33 | EMAIL: /^([^@]+?)@(([a-z0-9]-*)*[a-z0-9]+\.)+([a-z0-9]+)$/i,
34 | URL: /^(https?:\/\/)?((([a-z0-9]-*)*[a-z0-9]+\.?)*([a-z0-9]+))(\/[\w?=\.-]*)*$/,
35 | PHONE: /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/,
36 | OPTIONAL: /\S/,
37 | COMPARISON: /^\s*([LV])\s*([<>]=?|==|!=)\s*([^<>=!]+?)\s*$/
38 | };
39 |
40 | var _messages = {
41 | 'default': '$ contain error(s).',
42 | 'NOTEMPTY': '$ must not be empty.',
43 | 'INTEGER': '$ must be an integer.',
44 | 'NUMERIC': '$ must be numeric.',
45 | 'MIXED': '$ must be letters or numbers (no special characters).',
46 | 'NAME': '$ must not contain special characters.',
47 | 'NOSPACE': '$ must not contain spaces.',
48 | 'TRIM': '$ must not start or end with space character.',
49 | 'DATE': '$ is not a valid with format YYYY-MM-DD.',
50 | 'EMAIL': '$ is not valid.',
51 | 'URL': '$ is not valid.',
52 | 'PHONE': '$ is not a valid phone number.',
53 | '<': '$ must be less than % characters.',
54 | '<=': '$ must be less or equal to % characters.',
55 | '>': '$ must be greater than % characters.',
56 | '>=': '$ must be greater or equal to % characters.',
57 | '==': '$ must be equal to %',
58 | '!=': '$ must be different than %'
59 | };
60 |
61 | var _data = {
62 | validation: 'data-validation',
63 | validationMessage: 'data-validation-message',
64 | regex: 'data-validation-regex',
65 | regexReverse: 'data-validation-regex-reverse',
66 | regexMessage: 'data-validation-regex-message',
67 | group: 'data-validation-group',
68 | label: 'data-validation-label',
69 | errorList: 'data-error-list'
70 | }
71 |
72 | var _options = {
73 | submit: {
74 | settings: {
75 | form: null,
76 | display: "inline",
77 | insertion: "append",
78 | allErrors: false,
79 | trigger: "click",
80 | button: "[type='submit']",
81 | errorClass: "error",
82 | errorListClass: "error-list",
83 | errorListContainer: null,
84 | errorTemplate: null,
85 | inputContainer: null,
86 | clear: "focusin",
87 | scrollToError: false
88 | },
89 | callback: {
90 | onInit: null,
91 | onValidate: null,
92 | onError: null,
93 | onBeforeSubmit: null,
94 | onSubmit: null,
95 | onAfterSubmit: null
96 | }
97 | },
98 | dynamic: {
99 | settings: {
100 | trigger: null,
101 | delay: 300
102 | },
103 | callback: {
104 | onSuccess: null,
105 | onError: null,
106 | onComplete: null
107 | }
108 | },
109 | rules: {},
110 | messages: {},
111 | labels: {},
112 | debug: false
113 | };
114 |
115 | var _supported = {
116 | submit: {
117 | settings: {
118 | display: ["inline", "block"],
119 | insertion: ["append", "prepend"], //"before", "insertBefore", "after", "insertAfter"
120 | allErrors: [true, false],
121 | clear: ["focusin", "keypress", false],
122 | trigger: [
123 | "click", "dblclick", "focusout",
124 | "hover", "mousedown", "mouseenter",
125 | "mouseleave", "mousemove", "mouseout",
126 | "mouseover", "mouseup", "toggle"
127 | ]
128 | }
129 | },
130 | dynamic: {
131 | settings: {
132 | trigger: ["focusout", "keydown", "keypress", "keyup"]
133 | }
134 | },
135 | debug: [true, false]
136 | };
137 |
138 | var Validation = function(node, options) {
139 |
140 | var errors = [],
141 | messages = {},
142 | formData = {},
143 | delegateSuffix = ".vd", // validation.delegate
144 | resetSuffix = ".vr"; // validation.resetError
145 |
146 | window.Validation.hasScrolled = false;
147 |
148 | function extendRules() {
149 | options.rules = $.extend(
150 | true, {},
151 | _rules,
152 | options.rules
153 | );
154 | }
155 |
156 | function extendMessages() {
157 | options.messages = $.extend(
158 | true, {},
159 | _messages,
160 | options.messages
161 | );
162 | }
163 |
164 | function extendOptions() {
165 |
166 | if (!(options instanceof Object)) {
167 | options = {};
168 | }
169 |
170 | var tpmOptions = Object.preventExtensions($.extend(true, {}, _options));
171 |
172 | for (var method in options) {
173 |
174 | if (!options.hasOwnProperty(method) || method === "debug") {
175 | continue;
176 | }
177 |
178 | if (~["labels", "messages", "rules"].indexOf(method) && options[method] instanceof Object) {
179 | tpmOptions[method] = options[method];
180 | continue;
181 | }
182 |
183 | if (!_options[method] || !(options[method] instanceof Object)) {
184 | options.debug && window.Debug.log({
185 | 'node': node,
186 | 'function': 'extendOptions()',
187 | 'arguments': '{' + method + ': ' + JSON.stringify(options[method]) + '}',
188 | 'message': 'WARNING - ' + method + ' - invalid option'
189 | });
190 |
191 | continue;
192 | }
193 |
194 | for (var type in options[method]) {
195 | if (!options[method].hasOwnProperty(type)) {
196 | continue;
197 | }
198 |
199 | if (!_options[method][type] || !(options[method][type] instanceof Object)) {
200 | options.debug && window.Debug.log({
201 | 'node': node,
202 | 'function': 'extendOptions()',
203 | 'arguments': '{' + type + ': ' + JSON.stringify(options[method][type]) + '}',
204 | 'message': 'WARNING - ' + type + ' - invalid option'
205 | });
206 |
207 | continue;
208 | }
209 |
210 | for (var option in options[method][type]) {
211 | if (!options[method][type].hasOwnProperty(option)) {
212 | continue;
213 | }
214 |
215 | if (_supported[method] &&
216 | _supported[method][type] &&
217 | _supported[method][type][option] &&
218 | $.inArray(options[method][type][option], _supported[method][type][option]) === -1) {
219 | options.debug && window.Debug.log({
220 | 'node': node,
221 | 'function': 'extendOptions()',
222 | 'arguments': '{' + option + ': ' + JSON.stringify(options[method][type][option]) + '}',
223 | 'message': 'WARNING - ' + option.toString() + ': ' + JSON.stringify(options[method][type][option]) + ' - unsupported option'
224 | });
225 |
226 | delete options[method][type][option];
227 | }
228 |
229 | }
230 | if (tpmOptions[method] && tpmOptions[method][type]) {
231 | tpmOptions[method][type] = $.extend(Object.preventExtensions(tpmOptions[method][type]), options[method][type]);
232 | }
233 | }
234 | }
235 | if (options.debug && $.inArray(options.debug, _supported.debug !== -1)) {
236 | tpmOptions.debug = options.debug;
237 | }
238 | if (tpmOptions.dynamic.settings.trigger) {
239 | if (tpmOptions.dynamic.settings.trigger === "keypress" && tpmOptions.submit.settings.clear === "keypress") {
240 | tpmOptions.dynamic.settings.trigger = "keydown";
241 | }
242 | }
243 |
244 | options = tpmOptions;
245 |
246 | }
247 |
248 | function delegateDynamicValidation() {
249 |
250 | if (!options.dynamic.settings.trigger) {
251 | return false;
252 | }
253 | options.debug && window.Debug.log({
254 | 'node': node,
255 | 'function': 'delegateDynamicValidation()',
256 | 'message': 'OK - Dynamic Validation activated on ' + node.length + ' form(s)'
257 | });
258 |
259 | if (!node.find('[' + _data.validation + '],[' + _data.regex + ']')[0]) {
260 | options.debug && window.Debug.log({
261 | 'node': node,
262 | 'function': 'delegateDynamicValidation()',
263 | 'arguments': 'node.find([' + _data.validation + '],[' + _data.regex + '])',
264 | 'message': 'ERROR - [' + _data.validation + '] not found'
265 | });
266 |
267 | return false;
268 | }
269 |
270 | var event = options.dynamic.settings.trigger + delegateSuffix;
271 | if (options.dynamic.settings.trigger !== "focusout") {
272 | event += " change" + delegateSuffix + " paste" + delegateSuffix;
273 | }
274 |
275 | $.each(
276 | node.find('[' + _data.validation + '],[' + _data.regex + ']'),
277 | function(index, input) {
278 |
279 | $(input).unbind(event).on(event, function(e) {
280 |
281 | if ($(this).is(':disabled')) {
282 | return false;
283 | }
284 |
285 | var input = this,
286 | keyCode = e.keyCode || null;
287 |
288 | _typeWatch(function() {
289 |
290 | if (!validateInput(input)) {
291 |
292 | displayOneError(input.name);
293 | _executeCallback(options.dynamic.callback.onError, [node, input, keyCode, errors[input.name]]);
294 |
295 | } else {
296 |
297 | _executeCallback(options.dynamic.callback.onSuccess, [node, input, keyCode]);
298 |
299 | }
300 |
301 | _executeCallback(options.dynamic.callback.onComplete, [node, input, keyCode]);
302 |
303 | }, options.dynamic.settings.delay);
304 |
305 | });
306 | }
307 | );
308 | }
309 |
310 | function delegateValidation() {
311 |
312 | _executeCallback(options.submit.callback.onInit, [node]);
313 |
314 | var event = options.submit.settings.trigger + '.vd';
315 | options.debug && window.Debug.log({
316 | 'node': node,
317 | 'function': 'delegateValidation()',
318 | 'message': 'OK - Validation activated on ' + node.length + ' form(s)'
319 | });
320 |
321 | if (!node.find(options.submit.settings.button)[0]) {
322 | options.debug && window.Debug.log({
323 | 'node': node,
324 | 'function': 'delegateValidation()',
325 | 'arguments': '{button: ' + options.submit.settings.button + '}',
326 | 'message': 'ERROR - node.find("' + options.submit.settings.button + '") not found'
327 | });
328 |
329 | return false;
330 |
331 | }
332 |
333 | node.on("submit", false);
334 | node.find(options.submit.settings.button).off('.vd').on(event, function(e) {
335 |
336 | e.preventDefault();
337 |
338 | resetErrors();
339 |
340 | _executeCallback(options.submit.callback.onValidate, [node]);
341 |
342 | if (!validateForm()) {
343 |
344 | displayErrors();
345 | _executeCallback(options.submit.callback.onError, [node, errors, formData]);
346 |
347 | } else {
348 |
349 | _executeCallback(options.submit.callback.onBeforeSubmit, [node]);
350 |
351 | if (typeof options.submit.callback.onSubmit === "function") {
352 | if (_executeCallback(options.submit.callback.onSubmit, [node, formData]) === true) {
353 | submitForm();
354 | }
355 | } else {
356 | submitForm();
357 | }
358 |
359 | _executeCallback(options.submit.callback.onAfterSubmit, [node]);
360 |
361 | }
362 | options.debug && window.Debug.print();
363 |
364 | return false;
365 |
366 | });
367 |
368 | }
369 |
370 | function validateForm() {
371 |
372 | var isValid = isEmpty(errors);
373 |
374 | formData = {};
375 |
376 | $.each(
377 | node.find('input:not([type="submit"]), select, textarea').not(':disabled'),
378 | function(index, input) {
379 |
380 | input = $(input);
381 |
382 | var value = _getInputValue(input[0]),
383 | inputName = input.attr('name');
384 |
385 | if (inputName) {
386 | if (/\[]$/.test(inputName)) {
387 | inputName = inputName.replace(/\[]$/, '');
388 | if (!(formData[inputName] instanceof Array)) {
389 | formData[inputName] = [];
390 | }
391 | formData[inputName].push(value)
392 | } else {
393 | formData[inputName] = value;
394 | }
395 | }
396 |
397 | if (!!input.attr(_data.validation) || !!input.attr(_data.regex)) {
398 | if (!validateInput(input[0], value)) {
399 | isValid = false;
400 | }
401 | }
402 | }
403 | );
404 |
405 | prepareFormData();
406 |
407 | return isValid;
408 |
409 | }
410 |
411 | function prepareFormData() {
412 |
413 | var data = {},
414 | matches,
415 | index;
416 |
417 | for (var i in formData) {
418 | if (!formData.hasOwnProperty(i)) continue;
419 |
420 | index = 0;
421 | matches = i.split(/\[(.+?)]/g);
422 |
423 | var tmpObject = {},
424 | tmpArray = [];
425 |
426 | for (var k = matches.length - 1; k >= 0; k--) {
427 | if (matches[k] === "") {
428 | matches.splice(k, 1);
429 | continue;
430 | }
431 |
432 | if (tmpArray.length < 1) {
433 | tmpObject[matches[k]] = Number(formData[i]) || formData[i];
434 | } else {
435 | tmpObject = {};
436 | tmpObject[matches[k]] = tmpArray[tmpArray.length - 1];
437 | }
438 | tmpArray.push(tmpObject)
439 |
440 | }
441 |
442 | data = $.extend(true, data, tmpObject);
443 |
444 | }
445 |
446 | formData = data;
447 |
448 | }
449 |
450 | function validateInput(input, value) {
451 |
452 | var inputName = $(input).attr('name'),
453 | value = value || _getInputValue(input);
454 |
455 | if (!inputName) {
456 | options.debug && window.Debug.log({
457 | 'node': node,
458 | 'function': 'validateInput()',
459 | 'arguments': '$(input).attr("name")',
460 | 'message': 'ERROR - Missing input [name]'
461 | });
462 |
463 | return false;
464 | }
465 |
466 | var matches = inputName.replace(/]$/, '').split(/]\[|[[\]]/g),
467 | inputShortName = window.Validation.labels[inputName] ||
468 | options.labels[inputName] ||
469 | $(input).attr(_data.label) ||
470 | matches[matches.length - 1],
471 |
472 | validationArray = $(input).attr(_data.validation),
473 | validationMessage = $(input).attr(_data.validationMessage),
474 | validationRegex = $(input).attr(_data.regex),
475 | validationRegexReverse = !($(input).attr(_data.regexReverse) === undefined),
476 | validationRegexMessage = $(input).attr(_data.regexMessage),
477 |
478 | validateOnce = false;
479 |
480 | if (validationArray) {
481 | validationArray = _api._splitValidation(validationArray);
482 | }
483 | if (validationArray instanceof Array && validationArray.length > 0) {
484 | if ($.trim(value) === '' && ~validationArray.indexOf('OPTIONAL')) {
485 | return true;
486 | }
487 |
488 | $.each(validationArray, function(i, rule) {
489 |
490 | if (validateOnce === true) {
491 | return true;
492 | }
493 |
494 | try {
495 |
496 | validateRule(value, rule);
497 |
498 | } catch (error) {
499 |
500 | if (validationMessage || !options.submit.settings.allErrors) {
501 | validateOnce = true;
502 | }
503 |
504 | error[0] = validationMessage || error[0];
505 |
506 | registerError(inputName, error[0].replace('$', inputShortName).replace('%', error[1]));
507 |
508 | }
509 |
510 | });
511 |
512 | }
513 | if (validationRegex) {
514 |
515 | var rule = _buildRegexFromString(validationRegex);
516 | if (!(rule instanceof RegExp)) {
517 | return true;
518 | }
519 |
520 | try {
521 |
522 | validateRule(value, rule, validationRegexReverse);
523 |
524 | } catch (error) {
525 |
526 | error[0] = validationRegexMessage || error[0];
527 |
528 | registerError(inputName, error[0].replace('$', inputShortName));
529 |
530 | }
531 |
532 | }
533 |
534 | return !errors[inputName] || errors[inputName] instanceof Array && errors[inputName].length === 0;
535 |
536 | }
537 |
538 | function validateRule(value, rule, reversed) {
539 | if (rule instanceof RegExp) {
540 | var isValid = rule.test(value);
541 |
542 | if (reversed) {
543 | isValid = !isValid;
544 | }
545 |
546 | if (!isValid) {
547 | throw [options.messages['default'], ''];
548 | }
549 | return;
550 | }
551 |
552 | if (options.rules[rule]) {
553 | if (!options.rules[rule].test(value)) {
554 | throw [options.messages[rule], ''];
555 | }
556 | return;
557 | }
558 | var comparison = rule.match(options.rules.COMPARISON);
559 |
560 | if (!comparison || comparison.length !== 4) {
561 | options.debug && window.Debug.log({
562 | 'node': node,
563 | 'function': 'validateRule()',
564 | 'arguments': 'value: ' + value + ' rule: ' + rule,
565 | 'message': 'WARNING - Invalid comparison'
566 | });
567 |
568 | return;
569 | }
570 |
571 | var type = comparison[1],
572 | operator = comparison[2],
573 | compared = comparison[3],
574 | comparedValue;
575 |
576 | switch (type) {
577 | case "L":
578 | if (isNaN(compared)) {
579 | options.debug && window.Debug.log({
580 | 'node': node,
581 | 'function': 'validateRule()',
582 | 'arguments': 'compare: ' + compared + ' rule: ' + rule,
583 | 'message': 'WARNING - Invalid rule, "L" compare must be numeric'
584 | });
585 |
586 | return false;
587 |
588 | } else {
589 |
590 | if (!value || eval(value.length + operator + parseFloat(compared)) === false) {
591 | throw [options.messages[operator], compared];
592 | }
593 |
594 | }
595 |
596 | break;
597 | case "V":
598 | default:
599 | if (isNaN(compared)) {
600 |
601 | comparedValue = node.find('[name="' + compared + '"]').val();
602 | if (!comparedValue) {
603 | options.debug && window.Debug.log({
604 | 'node': node,
605 | 'function': 'validateRule()',
606 | 'arguments': 'compare: ' + compared + ' rule: ' + rule,
607 | 'message': 'WARNING - Unable to find compared field [name="' + compared + '"]'
608 | });
609 |
610 | return false;
611 | }
612 |
613 | if (!value || !eval('"' + encodeURIComponent(value) + '"' + operator + '"' + encodeURIComponent(comparedValue) + '"')) {
614 | throw [options.messages[operator].replace(' characters', ''), compared];
615 | }
616 |
617 | } else {
618 | if (!value || isNaN(value) || !eval(value + operator + parseFloat(compared))) {
619 | throw [options.messages[operator].replace(' characters', ''), compared];
620 | }
621 |
622 | }
623 | break;
624 |
625 | }
626 |
627 | }
628 |
629 | function registerError(inputName, error) {
630 |
631 | if (!errors[inputName]) {
632 | errors[inputName] = [];
633 | }
634 |
635 | error = error.capitalize();
636 |
637 | var hasError = false;
638 | for (var i = 0; i < errors[inputName].length; i++) {
639 | if (errors[inputName][i] === error) {
640 | hasError = true;
641 | break;
642 | }
643 | }
644 |
645 | if (!hasError) {
646 | errors[inputName].push(error);
647 | }
648 |
649 | }
650 |
651 | function displayOneError(inputName) {
652 |
653 | var input,
654 | inputId,
655 | errorContainer,
656 | label,
657 | html = '',
658 | group,
659 | groupInput;
660 |
661 | if (!errors.hasOwnProperty(inputName)) {
662 | return false;
663 | }
664 |
665 | input = node.find('[name="' + inputName + '"]');
666 |
667 | label = null;
668 |
669 | if (!input[0]) {
670 | options.debug && window.Debug.log({
671 | 'node': node,
672 | 'function': 'displayOneError()',
673 | 'arguments': '[name="' + inputName + '"]',
674 | 'message': 'ERROR - Unable to find input by name "' + inputName + '"'
675 | });
676 |
677 | return false;
678 | }
679 |
680 | group = input.attr(_data.group);
681 |
682 | if (group) {
683 |
684 | groupInput = node.find('[name="' + inputName + '"]');
685 | label = node.find('[id="' + group + '"]');
686 |
687 | if (label[0]) {
688 | label.addClass(options.submit.settings.errorClass);
689 | errorContainer = label;
690 | }
691 |
692 | } else {
693 |
694 | input.addClass(options.submit.settings.errorClass);
695 |
696 | if (options.submit.settings.inputContainer) {
697 | input.parentsUntil(node, options.submit.settings.inputContainer).addClass(options.submit.settings.errorClass);
698 | }
699 |
700 | inputId = input.attr('id');
701 |
702 | if (inputId) {
703 | label = node.find('label[for="' + inputId + '"]')[0];
704 | }
705 |
706 | if (!label) {
707 | label = input.parentsUntil(node, 'label')[0];
708 | }
709 |
710 | if (label) {
711 | label = $(label);
712 | label.addClass(options.submit.settings.errorClass);
713 | }
714 | }
715 |
716 | if (options.submit.settings.display === 'inline') {
717 | if (options.submit.settings.errorListContainer) {
718 | errorContainer = input.parentsUntil(node, options.submit.settings.errorListContainer);
719 | } else {
720 | errorContainer = errorContainer || input.parent();
721 | }
722 | } else if (options.submit.settings.display === 'block') {
723 | errorContainer = node;
724 | }
725 | if (options.submit.settings.display === 'inline' && errorContainer.find('[' + _data.errorList + ']')[0]) {
726 | return false;
727 | }
728 |
729 | if (options.submit.settings.display === "inline" ||
730 | (options.submit.settings.display === "block" && !errorContainer.find('[' + _data.errorList + ']')[0])
731 | ) {
732 | if (options.submit.settings.insertion === 'append') {
733 | errorContainer.append(html);
734 | } else if (options.submit.settings.insertion === 'prepend') {
735 | errorContainer.prepend(html);
736 | }
737 | }
738 |
739 | for (var i = 0; i < errors[inputName].length; i++) {
740 | if (options.submit.settings.errorTemplate) {
741 | errorContainer.find('[' + _data.errorList + '] ul')
742 | .append('' + options.submit.settings.errorTemplate.replace('{{validation-message}}', errors[inputName][i]) + '');
743 | } else {
744 | errorContainer.find('[' + _data.errorList + '] ul').append('' + errors[inputName][i] + '');
745 | }
746 | }
747 |
748 | if (options.submit.settings.clear || options.dynamic.settings.trigger) {
749 |
750 | if (group && groupInput) {
751 | input = groupInput;
752 | }
753 |
754 | var event = "coucou" + resetSuffix;
755 | if (options.submit.settings.clear) {
756 | event += " " + options.submit.settings.clear + resetSuffix;
757 | if (~['radio', 'checkbox'].indexOf(input[0].type)) {
758 | event += " change" + resetSuffix;
759 | }
760 | }
761 | if (options.dynamic.settings.trigger) {
762 | event += " " + options.dynamic.settings.trigger + resetSuffix;
763 | if (options.dynamic.settings.trigger !== "focusout" && !~['radio', 'checkbox'].indexOf(input[0].type)) {
764 | event += " change" + resetSuffix + " paste" + resetSuffix;
765 | }
766 | }
767 |
768 | input.unbind(event).on(event, function(a, b, c, d, e) {
769 |
770 | return function() {
771 | if (e) {
772 | if ($(c).hasClass(options.submit.settings.errorClass)) {
773 | resetOneError(a, b, c, d, e);
774 | }
775 | } else if ($(b).hasClass(options.submit.settings.errorClass)) {
776 | resetOneError(a, b, c, d);
777 | }
778 | };
779 |
780 | }(inputName, input, label, errorContainer, group));
781 | }
782 |
783 | if (options.submit.settings.scrollToError && !window.Validation.hasScrolled) {
784 |
785 | window.Validation.hasScrolled = true;
786 |
787 | var offset = parseFloat(options.submit.settings.scrollToError.offset) || 0,
788 | duration = parseFloat(options.submit.settings.scrollToError.duration) || 500,
789 | handle = (options.submit.settings.display === 'block') ? errorContainer : input;
790 |
791 | $('html, body').animate({
792 | scrollTop: handle.offset().top + offset
793 | }, duration);
794 |
795 | }
796 |
797 | }
798 |
799 | function displayErrors() {
800 |
801 | for (var inputName in errors) {
802 | if (!errors.hasOwnProperty(inputName)) continue;
803 | displayOneError(inputName);
804 | }
805 |
806 | }
807 |
808 | function resetOneError(inputName, input, label, container, group) {
809 |
810 | delete errors[inputName];
811 |
812 | if (container) {
813 |
814 | if (options.submit.settings.inputContainer) {
815 | (group ? label : input).parentsUntil(node, options.submit.settings.inputContainer).removeClass(options.submit.settings.errorClass);
816 | }
817 |
818 | label && label.removeClass(options.submit.settings.errorClass);
819 |
820 | input.removeClass(options.submit.settings.errorClass);
821 |
822 | if (options.submit.settings.display === 'inline') {
823 | container.find('[' + _data.errorList + ']').remove();
824 | }
825 |
826 | } else {
827 |
828 | if (!input) {
829 | input = node.find('[name="' + inputName + '"]');
830 |
831 | if (!input[0]) {
832 | options.debug && window.Debug.log({
833 | 'node': node,
834 | 'function': 'resetOneError()',
835 | 'arguments': '[name="' + inputName + '"]',
836 | 'message': 'ERROR - Unable to find input by name "' + inputName + '"'
837 | });
838 |
839 | return false;
840 | }
841 | }
842 |
843 | input.trigger('coucou' + resetSuffix);
844 | }
845 |
846 | }
847 |
848 | function resetErrors() {
849 |
850 | errors = [];
851 | window.Validation.hasScrolled = false;
852 |
853 | node.find('[' + _data.errorList + ']').remove();
854 | node.find('.' + options.submit.settings.errorClass).removeClass(options.submit.settings.errorClass);
855 |
856 | }
857 |
858 | function submitForm() {
859 |
860 | node[0].submit()
861 |
862 | }
863 |
864 | function destroy() {
865 |
866 | resetErrors();
867 | node.find('[' + _data.validation + '],[' + _data.regex + ']').off(delegateSuffix + ' ' + resetSuffix);
868 |
869 | node.find(options.submit.settings.button).off(delegateSuffix).on('click' + delegateSuffix, function() {
870 | $(this).closest('form')[0].submit();
871 | });
872 |
873 | return true;
874 |
875 | }
876 |
877 | var _getInputValue = function(input) {
878 |
879 | var value;
880 | switch ($(input).attr('type')) {
881 | case 'checkbox':
882 | value = ($(input).is(':checked')) ? 1 : '';
883 | break;
884 | case 'radio':
885 | value = node.find('input[name="' + $(input).attr('name') + '"]:checked').val() || '';
886 | break;
887 | default:
888 | value = $(input).val();
889 | break;
890 | }
891 |
892 | return value;
893 |
894 | };
895 |
896 | var _typeWatch = (function() {
897 | var timer = 0;
898 | return function(callback, ms) {
899 | clearTimeout(timer);
900 | timer = setTimeout(callback, ms);
901 | };
902 | })();
903 |
904 | var _executeCallback = function(callback, extraParams) {
905 |
906 | if (!callback) {
907 | return false;
908 | }
909 |
910 | var _callback;
911 |
912 | if (typeof callback === "function") {
913 |
914 | _callback = callback;
915 |
916 | } else if (typeof callback === "string" || callback instanceof Array) {
917 |
918 | _callback = window;
919 |
920 | if (typeof callback === "string") {
921 | callback = [callback, []];
922 | }
923 |
924 | var _exploded = callback[0].split('.'),
925 | _params = callback[1],
926 | _isValid = true,
927 | _splitIndex = 0;
928 |
929 | while (_splitIndex < _exploded.length) {
930 |
931 | if (typeof _callback !== 'undefined') {
932 | _callback = _callback[_exploded[_splitIndex++]];
933 | } else {
934 | _isValid = false;
935 | break;
936 | }
937 | }
938 |
939 | if (!_isValid || typeof _callback !== "function") {
940 | options.debug && window.Debug.log({
941 | 'node': node,
942 | 'function': '_executeCallback()',
943 | 'arguments': JSON.stringify(callback),
944 | 'message': 'WARNING - Invalid callback function"'
945 | });
946 |
947 | return false;
948 | }
949 |
950 | }
951 |
952 | return _callback.apply(this, $.merge(_params || [], (extraParams) ? extraParams : []));
953 |
954 | };
955 |
956 | this.__construct = function() {
957 |
958 | extendOptions();
959 | extendRules();
960 | extendMessages();
961 |
962 | delegateDynamicValidation();
963 | delegateValidation();
964 | options.debug && window.Debug.print();
965 |
966 | }();
967 |
968 | return {
969 |
970 | registerError: registerError,
971 |
972 | displayOneError: displayOneError,
973 |
974 | displayErrors: displayErrors,
975 |
976 | resetOneError: resetOneError,
977 |
978 | resetErrors: resetErrors,
979 |
980 | destroy: destroy
981 |
982 | };
983 |
984 | };
985 |
986 | $.fn.validate = $.validate = function(options) {
987 |
988 | return _api.validate(this, options);
989 |
990 | };
991 |
992 | $.fn.addValidation = function(validation) {
993 |
994 | return _api.addValidation(this, validation);
995 |
996 | };
997 |
998 | $.fn.removeValidation = function(validation) {
999 |
1000 | return _api.removeValidation(this, validation);
1001 |
1002 | };
1003 |
1004 | $.fn.addError = function(error) {
1005 |
1006 | return _api.addError(this, error);
1007 |
1008 | };
1009 |
1010 | $.fn.removeError = function(error) {
1011 |
1012 | return _api.removeError(this, error);
1013 |
1014 | };
1015 |
1016 | $.fn.alterValidationRules = $.alterValidationRules = function(rules) {
1017 |
1018 | if (!(rules instanceof Array)) {
1019 | rules = [rules];
1020 | }
1021 |
1022 | for (var i = 0; i < rules.length; i++) {
1023 | _api.alterValidationRules(rules[i]);
1024 | }
1025 |
1026 | };
1027 |
1028 | var _api = {
1029 |
1030 | _formatValidation: function(validation) {
1031 |
1032 | validation = validation.toString().replace(/\s/g, '');
1033 |
1034 | if (validation.charAt(0) === "[" && validation.charAt(validation.length - 1) === "]") {
1035 | validation = validation.replace(/^\[|]$/g, '');
1036 | }
1037 |
1038 | return validation;
1039 |
1040 | },
1041 |
1042 | _splitValidation: function(validation) {
1043 |
1044 | var validationArray = this._formatValidation(validation).split(','),
1045 | oneValidation;
1046 |
1047 | for (var i = 0; i < validationArray.length; i++) {
1048 | oneValidation = validationArray[i];
1049 | if (/^[a-z]+$/i.test(oneValidation)) {
1050 | validationArray[i] = oneValidation.toUpperCase();
1051 | }
1052 | }
1053 |
1054 | return validationArray;
1055 | },
1056 |
1057 | _joinValidation: function(validation) {
1058 |
1059 | return '[' + validation.join(', ') + ']';
1060 |
1061 | },
1062 |
1063 | validate: function(node, options) {
1064 |
1065 | if (typeof node === "function") {
1066 |
1067 | if (!options.submit.settings.form) {
1068 | window.Debug.log({
1069 | 'node': node,
1070 | 'function': '$.validate()',
1071 | 'arguments': '',
1072 | 'message': 'Undefined property "options.submit.settings.form - Validation dropped'
1073 | });
1074 |
1075 | window.Debug.print();
1076 |
1077 | return;
1078 | }
1079 |
1080 | node = $(options.submit.settings.form);
1081 |
1082 | if (!node[0] || node[0].nodeName.toLowerCase() !== "form") {
1083 | window.Debug.log({
1084 | 'function': '$.validate()',
1085 | 'arguments': options.submit.settings.form,
1086 | 'message': 'Unable to find jQuery form element - Validation dropped'
1087 | });
1088 |
1089 | window.Debug.print();
1090 |
1091 | return;
1092 | }
1093 |
1094 | } else if (typeof node[0] === 'undefined') {
1095 | window.Debug.log({
1096 | 'node': node,
1097 | 'function': '$.validate()',
1098 | 'arguments': '$("' + node.selector + '").validate()',
1099 | 'message': 'Unable to find jQuery form element - Validation dropped'
1100 | });
1101 |
1102 | window.Debug.print();
1103 |
1104 | return;
1105 | }
1106 |
1107 | if (options === "destroy") {
1108 |
1109 | if (!window.Validation.form[node.selector]) {
1110 | window.Debug.log({
1111 | 'node': node,
1112 | 'function': '$.validate("destroy")',
1113 | 'arguments': '',
1114 | 'message': 'Unable to destroy "' + node.selector + '", perhaps it\'s already destroyed?'
1115 | });
1116 |
1117 | window.Debug.print();
1118 |
1119 | return;
1120 | }
1121 |
1122 | window.Validation.form[node.selector].destroy();
1123 |
1124 | return;
1125 |
1126 | }
1127 |
1128 | return node.each(function() {
1129 | window.Validation.form[node.selector] = new Validation($(this), options);
1130 | });
1131 |
1132 | },
1133 |
1134 | addValidation: function(node, validation) {
1135 |
1136 | var self = this;
1137 |
1138 | validation = self._splitValidation(validation);
1139 |
1140 | if (!validation) {
1141 | return false;
1142 | }
1143 |
1144 | return node.each(function() {
1145 |
1146 | var $this = $(this),
1147 | validationData = $this.attr(_data.validation),
1148 | validationArray = (validationData && validationData.length) ? self._splitValidation(validationData) : [],
1149 | oneValidation;
1150 |
1151 | for (var i = 0; i < validation.length; i++) {
1152 |
1153 | oneValidation = self._formatValidation(validation[i]);
1154 |
1155 | if ($.inArray(oneValidation, validationArray) === -1) {
1156 | validationArray.push(oneValidation);
1157 | }
1158 | }
1159 |
1160 | if (validationArray.length) {
1161 | $this.attr(_data.validation, self._joinValidation(validationArray));
1162 | }
1163 |
1164 | });
1165 |
1166 | },
1167 |
1168 | removeValidation: function(node, validation) {
1169 |
1170 | var self = this;
1171 |
1172 | validation = self._splitValidation(validation);
1173 | if (!validation) {
1174 | return false;
1175 | }
1176 |
1177 | return node.each(function() {
1178 |
1179 | var $this = $(this),
1180 | validationData = $this.attr(_data.validation),
1181 | validationArray = (validationData && validationData.length) ? self._splitValidation(validationData) : [],
1182 | oneValidation,
1183 | validationIndex;
1184 |
1185 | if (!validationArray.length) {
1186 | $this.removeAttr(_data.validation);
1187 | return true;
1188 | }
1189 |
1190 | for (var i = 0; i < validation.length; i++) {
1191 | oneValidation = self._formatValidation(validation[i]);
1192 | validationIndex = $.inArray(oneValidation, validationArray);
1193 | if (validationIndex !== -1) {
1194 | validationArray.splice(validationIndex, 1);
1195 | }
1196 |
1197 | }
1198 |
1199 | if (!validationArray.length) {
1200 | $this.removeAttr(_data.validation);
1201 | return true;
1202 | }
1203 |
1204 | $this.attr(_data.validation, self._joinValidation(validationArray));
1205 |
1206 | });
1207 |
1208 | },
1209 |
1210 | addError: function(node, error) {
1211 |
1212 | if (!window.Validation.form[node.selector]) {
1213 | window.Debug.log({
1214 | 'node': node,
1215 | 'function': '$.addError()',
1216 | 'arguments': 'window.Validation.form[' + node.selector + ']',
1217 | 'message': 'ERROR - Invalid node selector'
1218 | });
1219 |
1220 | window.Debug.print();
1221 |
1222 | return false;
1223 | }
1224 |
1225 | if (typeof error !== "object" || Object.prototype.toString.call(error) !== "[object Object]") {
1226 | window.Debug.log({
1227 | 'node': node,
1228 | 'function': '$.addError()',
1229 | 'arguments': 'window.Validation.form[' + node.selector + ']',
1230 | 'message': 'ERROR - Invalid argument, must be type object'
1231 | });
1232 |
1233 | window.Debug.print();
1234 |
1235 | return false;
1236 | }
1237 |
1238 | var input,
1239 | onlyOnce = true;
1240 | for (var inputName in error) {
1241 |
1242 | if (!error.hasOwnProperty(inputName)) {
1243 | continue;
1244 | }
1245 |
1246 | if (!(error[inputName] instanceof Array)) {
1247 | error[inputName] = [error[inputName]];
1248 | }
1249 |
1250 | input = $(node.selector).find('[name="' + inputName + '"]');
1251 | if (!input[0]) {
1252 | window.Debug.log({
1253 | 'node': node,
1254 | 'function': '$.addError()',
1255 | 'arguments': inputName,
1256 | 'message': 'ERROR - Unable to find ' + '$(' + node.selector + ').find("[name="' + inputName + '"]")'
1257 | });
1258 |
1259 | window.Debug.print();
1260 |
1261 | continue;
1262 | }
1263 |
1264 | if (onlyOnce) {
1265 | window.Validation.hasScrolled = false;
1266 | onlyOnce = false;
1267 | }
1268 |
1269 | window.Validation.form[node.selector].resetOneError(inputName, input);
1270 |
1271 | for (var i = 0; i < error[inputName].length; i++) {
1272 |
1273 | if (typeof error[inputName][i] !== "string") {
1274 | window.Debug.log({
1275 | 'node': node,
1276 | 'function': '$.addError()',
1277 | 'arguments': error[inputName][i],
1278 | 'message': 'ERROR - Invalid error object property - Accepted format: {"inputName": "errorString"} or {"inputName": ["errorString", "errorString"]}'
1279 | });
1280 |
1281 | window.Debug.print();
1282 |
1283 | continue;
1284 | }
1285 |
1286 | window.Validation.form[node.selector].registerError(inputName, error[inputName][i]);
1287 |
1288 | }
1289 |
1290 | window.Validation.form[node.selector].displayOneError(inputName);
1291 |
1292 | }
1293 |
1294 | },
1295 |
1296 | removeError: function(node, inputName) {
1297 |
1298 | if (!window.Validation.form[node.selector]) {
1299 | window.Debug.log({
1300 | 'node': node,
1301 | 'function': '$.removeError()',
1302 | 'arguments': 'window.Validation.form[' + node.selector + ']',
1303 | 'message': 'ERROR - Invalid node selector'
1304 | });
1305 |
1306 | window.Debug.print();
1307 |
1308 | return false;
1309 | }
1310 |
1311 | if (!inputName) {
1312 | window.Validation.form[node.selector].resetErrors();
1313 | return false;
1314 | }
1315 |
1316 | if (typeof inputName === "object" && Object.prototype.toString.call(inputName) !== "[object Array]") {
1317 | window.Debug.log({
1318 | 'node': node,
1319 | 'function': '$.removeError()',
1320 | 'arguments': inputName,
1321 | 'message': 'ERROR - Invalid inputName, must be type String or Array'
1322 | });
1323 |
1324 | window.Debug.print();
1325 |
1326 | return false;
1327 | }
1328 |
1329 | if (!(inputName instanceof Array)) {
1330 | inputName = [inputName];
1331 | }
1332 |
1333 | var input;
1334 | for (var i = 0; i < inputName.length; i++) {
1335 |
1336 | input = $(node.selector).find('[name="' + inputName[i] + '"]');
1337 | if (!input[0]) {
1338 | window.Debug.log({
1339 | 'node': node,
1340 | 'function': '$.removeError()',
1341 | 'arguments': inputName[i],
1342 | 'message': 'ERROR - Unable to find ' + '$(' + node.selector + ').find("[name="' + inputName[i] + '"]")'
1343 | });
1344 |
1345 | window.Debug.print();
1346 |
1347 | continue;
1348 | }
1349 |
1350 | window.Validation.form[node.selector].resetOneError(inputName[i], input);
1351 |
1352 | }
1353 |
1354 | },
1355 |
1356 | alterValidationRules: function(ruleObj) {
1357 |
1358 | if (!ruleObj.rule || (!ruleObj.regex && !ruleObj.message)) {
1359 | window.Debug.log({
1360 | 'function': '$.alterValidationRules()',
1361 | 'message': 'ERROR - Missing one or multiple parameter(s) {rule, regex, message}'
1362 | });
1363 | window.Debug.print();
1364 | return false;
1365 | }
1366 |
1367 | ruleObj.rule = ruleObj.rule.toUpperCase();
1368 |
1369 | if (ruleObj.regex) {
1370 |
1371 | var regex = _buildRegexFromString(ruleObj.regex);
1372 |
1373 | if (!(regex instanceof RegExp)) {
1374 | window.Debug.log({
1375 | 'function': '$.alterValidationRules(rule)',
1376 | 'arguments': regex.toString(),
1377 | 'message': 'ERROR - Invalid rule'
1378 | });
1379 | window.Debug.print();
1380 | return false;
1381 | }
1382 |
1383 | _rules[ruleObj.rule] = regex;
1384 | }
1385 |
1386 | if (ruleObj.message) {
1387 | _messages[ruleObj.rule] = ruleObj.message;
1388 | }
1389 |
1390 | return true;
1391 | }
1392 |
1393 | };
1394 |
1395 | function _buildRegexFromString(regex) {
1396 |
1397 | if (!regex || (typeof regex !== "string" && !(regex instanceof RegExp))) {
1398 | _regexDebug();
1399 | return false;
1400 | }
1401 |
1402 | if (typeof regex !== 'string') {
1403 | regex = regex.toString();
1404 | }
1405 |
1406 | var separator = regex.charAt(0),
1407 | index = regex.length - 1,
1408 | pattern,
1409 | modifier,
1410 | rule;
1411 |
1412 | while (index > 0) {
1413 | if (/[gimsxeU]/.test(regex.charAt(index))) {
1414 | index--;
1415 | } else {
1416 | break;
1417 | }
1418 | }
1419 |
1420 | if (regex.charAt(index) !== separator) {
1421 | separator = null;
1422 | }
1423 |
1424 | if (separator && index !== regex.length - 1) {
1425 | modifier = regex.substr(index + 1, regex.length - 1);
1426 | }
1427 |
1428 | if (separator) {
1429 | pattern = regex.substr(1, index - 1);
1430 | } else {
1431 | pattern = regex;
1432 | }
1433 |
1434 | try {
1435 | rule = new RegExp(pattern, modifier);
1436 | } catch (error) {
1437 | _regexDebug();
1438 | return false;
1439 | }
1440 |
1441 | return rule;
1442 |
1443 | function _regexDebug() {
1444 | window.Debug.log({
1445 | 'function': '_buildRegexFromString()',
1446 | 'arguments': '{pattern: {' + (pattern || '') + '}, modifier: {' + (modifier || '') + '}',
1447 | 'message': 'WARNING - Invalid regex given: ' + regex
1448 | });
1449 | window.Debug.print();
1450 | }
1451 |
1452 | }
1453 | window.Debug = {
1454 |
1455 | table: {},
1456 | log: function(debugObject) {
1457 |
1458 | if (!debugObject.message || typeof debugObject.message !== "string") {
1459 | return false;
1460 | }
1461 |
1462 | this.table[debugObject.message] = $.extend(
1463 | Object.preventExtensions({
1464 | 'node': '',
1465 | 'function': '',
1466 | 'arguments': ''
1467 | }), debugObject
1468 | );
1469 |
1470 | },
1471 | print: function() {
1472 |
1473 | if (isEmpty(this.table)) {
1474 | return false;
1475 | }
1476 |
1477 | if (console.group !== undefined || console.table !== undefined) {
1478 |
1479 | console.groupCollapsed('--- jQuery Form Validation Debug ---');
1480 |
1481 | if (console.table) {
1482 | console.table(this.table);
1483 | } else {
1484 | $.each(this.table, function(index, data) {
1485 | console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms');
1486 | });
1487 | }
1488 |
1489 | console.groupEnd();
1490 |
1491 | } else {
1492 | console.log('Debug is not available on your current browser, try the most recent version of Chrome or Firefox.');
1493 | }
1494 |
1495 | this.table = {};
1496 |
1497 | }
1498 |
1499 | };
1500 |
1501 | String.prototype.capitalize = function() {
1502 | return this.charAt(0).toUpperCase() + this.slice(1);
1503 | };
1504 |
1505 | function isEmpty(obj) {
1506 | for (var prop in obj) {
1507 | if (obj.hasOwnProperty(prop))
1508 | return false;
1509 | }
1510 |
1511 | return true;
1512 | }
1513 |
1514 | if (!Array.prototype.indexOf) {
1515 | Array.prototype.indexOf = function(elt) {
1516 | var len = this.length >>> 0;
1517 |
1518 | var from = Number(arguments[1]) || 0;
1519 | from = (from < 0) ? Math.ceil(from) : Math.floor(from);
1520 | if (from < 0)
1521 | from += len;
1522 |
1523 | for (; from < len; from++) {
1524 | if (from in this &&
1525 | this[from] === elt)
1526 | return from;
1527 | }
1528 | return -1;
1529 | };
1530 | }
1531 | if (!JSON && !JSON.stringify) {
1532 | JSON.stringify = function(obj) {
1533 | var t = typeof(obj);
1534 | if (t !== "object" || obj === null) {
1535 | if (t === "string") {
1536 | obj = '"' + obj + '"';
1537 | }
1538 | return String(obj);
1539 | } else {
1540 | var n, v, json = [],
1541 | arr = (obj && obj.constructor === Array);
1542 | for (n in obj) {
1543 | if (true) {
1544 | v = obj[n];
1545 | t = typeof(v);
1546 | if (t === "string") {
1547 | v = '"' + v + '"';
1548 | } else if (t === "object" && v !== null) {
1549 | v = JSON.stringify(v);
1550 | }
1551 | json.push((arr ? "" : '"' + n + '": ') + String(v));
1552 | }
1553 | }
1554 | return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
1555 | }
1556 | };
1557 | }
1558 |
1559 | }(window, document, window.jQuery));
1560 |
--------------------------------------------------------------------------------
/src/jquery.validation.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery Form Validation
3 | * Copyright (C) 2015 RunningCoder.org
4 | * Licensed under the MIT license
5 | *
6 | * @author Tom Bertrand
7 | * @version 1.5.3 (2015-12-02)
8 | * @link http://www.runningcoder.org/jqueryvalidation/
9 | */
10 | ;
11 | (function (window, document, $, undefined) {
12 |
13 | window.Validation = {
14 | form: [],
15 | labels: {},
16 | hasScrolled: false
17 | };
18 |
19 | /**
20 | * Fail-safe preventExtensions function for older browsers
21 | */
22 | if (typeof Object.preventExtensions !== "function") {
23 | Object.preventExtensions = function (obj) {
24 | return obj;
25 | };
26 | }
27 |
28 | // Not using strict to avoid throwing a window error on bad config extend.
29 | // console.debug is used instead to debug Validation
30 | //"use strict";
31 |
32 | // =================================================================================================================
33 | /**
34 | * @private
35 | * RegExp rules
36 | */
37 | var _rules = {
38 | NOTEMPTY: /\S/,
39 | INTEGER: /^\d+$/,
40 | NUMERIC: /^\d+(?:[,\s]\d{3})*(?:\.\d+)?$/,
41 | MIXED: /^[\w\s-]+$/,
42 | NAME: /^['a-zãàáäâẽèéëêìíïîõòóöôùúüûñç\s-]+$/i,
43 | NOSPACE: /^(?!\s)\S*$/,
44 | TRIM: /^[^\s].*[^\s]$/,
45 | DATE: /^\d{4}-\d{2}-\d{2}(\s\d{2}:\d{2}(:\d{2})?)?$/,
46 | EMAIL: /^([^@]+?)@(([a-z0-9]-*)*[a-z0-9]+\.)+([a-z0-9]+)$/i,
47 | URL: /^(https?:\/\/)?((([a-z0-9]-*)*[a-z0-9]+\.?)*([a-z0-9]+))(\/[\w?=\.-]*)*$/,
48 | PHONE: /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/,
49 | OPTIONAL: /\S/,
50 | COMPARISON: /^\s*([LV])\s*([<>]=?|==|!=)\s*([^<>=!]+?)\s*$/
51 | };
52 |
53 | /**
54 | * @private
55 | * Error messages
56 | */
57 | var _messages = {
58 | 'default': '$ contain error(s).',
59 | 'NOTEMPTY': '$ must not be empty.',
60 | 'INTEGER': '$ must be an integer.',
61 | 'NUMERIC': '$ must be numeric.',
62 | 'MIXED': '$ must be letters or numbers (no special characters).',
63 | 'NAME': '$ must not contain special characters.',
64 | 'NOSPACE': '$ must not contain spaces.',
65 | 'TRIM': '$ must not start or end with space character.',
66 | 'DATE': '$ is not a valid with format YYYY-MM-DD.',
67 | 'EMAIL': '$ is not valid.',
68 | 'URL': '$ is not valid.',
69 | 'PHONE': '$ is not a valid phone number.',
70 | '<': '$ must be less than % characters.',
71 | '<=': '$ must be less or equal to % characters.',
72 | '>': '$ must be greater than % characters.',
73 | '>=': '$ must be greater or equal to % characters.',
74 | '==': '$ must be equal to %',
75 | '!=': '$ must be different than %'
76 | };
77 |
78 | /**
79 | * @private
80 | * HTML5 data attributes
81 | */
82 | var _data = {
83 | validation: 'data-validation',
84 | validationMessage: 'data-validation-message',
85 | regex: 'data-validation-regex',
86 | regexReverse: 'data-validation-regex-reverse',
87 | regexMessage: 'data-validation-regex-message',
88 | group: 'data-validation-group',
89 | label: 'data-validation-label',
90 | errorList: 'data-error-list'
91 | }
92 |
93 | /**
94 | * @private
95 | * Default options
96 | *
97 | * @link http://www.runningcoder.org/jqueryvalidation/documentation/
98 | */
99 | var _options = {
100 | submit: {
101 | settings: {
102 | form: null,
103 | display: "inline",
104 | insertion: "append",
105 | allErrors: false,
106 | trigger: "click",
107 | button: "[type='submit']",
108 | errorClass: "error",
109 | errorListClass: "error-list",
110 | errorListContainer: null,
111 | errorTemplate: null,
112 | inputContainer: null,
113 | clear: "focusin",
114 | scrollToError: false
115 | },
116 | callback: {
117 | onInit: null,
118 | onValidate: null,
119 | onError: null,
120 | onBeforeSubmit: null,
121 | onSubmit: null,
122 | onAfterSubmit: null
123 | }
124 | },
125 | dynamic: {
126 | settings: {
127 | trigger: null,
128 | delay: 300
129 | },
130 | callback: {
131 | onSuccess: null,
132 | onError: null,
133 | onComplete: null
134 | }
135 | },
136 | rules: {},
137 | messages: {},
138 | labels: {},
139 | debug: false
140 | };
141 |
142 | /**
143 | * @private
144 | * Limit the supported options on matching keys
145 | */
146 | var _supported = {
147 | submit: {
148 | settings: {
149 | display: ["inline", "block"],
150 | insertion: ["append", "prepend"], //"before", "insertBefore", "after", "insertAfter"
151 | allErrors: [true, false],
152 | clear: ["focusin", "keypress", false],
153 | trigger: [
154 | "click", "dblclick", "focusout",
155 | "hover", "mousedown", "mouseenter",
156 | "mouseleave", "mousemove", "mouseout",
157 | "mouseover", "mouseup", "toggle"
158 | ]
159 | }
160 | },
161 | dynamic: {
162 | settings: {
163 | trigger: ["focusout", "keydown", "keypress", "keyup"]
164 | }
165 | },
166 | debug: [true, false]
167 | };
168 |
169 | // =================================================================================================================
170 |
171 | /**
172 | * @constructor
173 | * Validation Class
174 | *
175 | * @param {Object} node jQuery form object
176 | * @param {Object} options User defined options
177 | */
178 | var Validation = function (node, options) {
179 |
180 | var errors = [],
181 | messages = {},
182 | formData = {},
183 | delegateSuffix = ".vd", // validation.delegate
184 | resetSuffix = ".vr"; // validation.resetError
185 |
186 | window.Validation.hasScrolled = false;
187 |
188 | /**
189 | * Extends user-defined "options.message" into the default Validation "_message".
190 | */
191 | function extendRules() {
192 | options.rules = $.extend(
193 | true,
194 | {},
195 | _rules,
196 | options.rules
197 | );
198 | }
199 |
200 | /**
201 | * Extends user-defined "options.message" into the default Validation "_message".
202 | */
203 | function extendMessages() {
204 | options.messages = $.extend(
205 | true,
206 | {},
207 | _messages,
208 | options.messages
209 | );
210 | }
211 |
212 | /**
213 | * Extends user-defined "options" into the default Validation "_options".
214 | * Notes:
215 | * - preventExtensions prevents from modifying the Validation "_options" object structure
216 | * - filter through the "_supported" to delete unsupported "options"
217 | */
218 | function extendOptions() {
219 |
220 | if (!(options instanceof Object)) {
221 | options = {};
222 | }
223 |
224 | var tpmOptions = Object.preventExtensions($.extend(true, {}, _options));
225 |
226 | for (var method in options) {
227 |
228 | if (!options.hasOwnProperty(method) || method === "debug") {
229 | continue;
230 | }
231 |
232 | if (~["labels", "messages", "rules"].indexOf(method) && options[method] instanceof Object) {
233 | tpmOptions[method] = options[method];
234 | continue;
235 | }
236 |
237 | if (!_options[method] || !(options[method] instanceof Object)) {
238 |
239 | // {debug}
240 | options.debug && window.Debug.log({
241 | 'node': node,
242 | 'function': 'extendOptions()',
243 | 'arguments': '{' + method + ': ' + JSON.stringify(options[method]) + '}',
244 | 'message': 'WARNING - ' + method + ' - invalid option'
245 | });
246 | // {/debug}
247 |
248 | continue;
249 | }
250 |
251 | for (var type in options[method]) {
252 | if (!options[method].hasOwnProperty(type)) {
253 | continue;
254 | }
255 |
256 | if (!_options[method][type] || !(options[method][type] instanceof Object)) {
257 |
258 | // {debug}
259 | options.debug && window.Debug.log({
260 | 'node': node,
261 | 'function': 'extendOptions()',
262 | 'arguments': '{' + type + ': ' + JSON.stringify(options[method][type]) + '}',
263 | 'message': 'WARNING - ' + type + ' - invalid option'
264 | });
265 | // {/debug}
266 |
267 | continue;
268 | }
269 |
270 | for (var option in options[method][type]) {
271 | if (!options[method][type].hasOwnProperty(option)) {
272 | continue;
273 | }
274 |
275 | if (_supported[method] &&
276 | _supported[method][type] &&
277 | _supported[method][type][option] &&
278 | $.inArray(options[method][type][option], _supported[method][type][option]) === -1) {
279 |
280 | // {debug}
281 | options.debug && window.Debug.log({
282 | 'node': node,
283 | 'function': 'extendOptions()',
284 | 'arguments': '{' + option + ': ' + JSON.stringify(options[method][type][option]) + '}',
285 | 'message': 'WARNING - ' + option.toString() + ': ' + JSON.stringify(options[method][type][option]) + ' - unsupported option'
286 | });
287 | // {/debug}
288 |
289 | delete options[method][type][option];
290 | }
291 |
292 | }
293 | if (tpmOptions[method] && tpmOptions[method][type]) {
294 | tpmOptions[method][type] = $.extend(Object.preventExtensions(tpmOptions[method][type]), options[method][type]);
295 | }
296 | }
297 | }
298 |
299 | // {debug}
300 | if (options.debug && $.inArray(options.debug, _supported.debug !== -1)) {
301 | tpmOptions.debug = options.debug;
302 | }
303 | // {/debug}
304 |
305 | // @TODO Would there be a better fix to solve event conflict?
306 | if (tpmOptions.dynamic.settings.trigger) {
307 | if (tpmOptions.dynamic.settings.trigger === "keypress" && tpmOptions.submit.settings.clear === "keypress") {
308 | tpmOptions.dynamic.settings.trigger = "keydown";
309 | }
310 | }
311 |
312 | options = tpmOptions;
313 |
314 | }
315 |
316 | /**
317 | * Delegates the dynamic validation on data-validation and data-validation-regex attributes based on trigger.
318 | *
319 | * @returns {Boolean} false if the option is not set
320 | */
321 | function delegateDynamicValidation() {
322 |
323 | if (!options.dynamic.settings.trigger) {
324 | return false;
325 | }
326 |
327 | // {debug}
328 | options.debug && window.Debug.log({
329 | 'node': node,
330 | 'function': 'delegateDynamicValidation()',
331 | 'message': 'OK - Dynamic Validation activated on ' + node.length + ' form(s)'
332 | });
333 | // {/debug}
334 |
335 | if (!node.find('[' + _data.validation + '],[' + _data.regex + ']')[0]) {
336 |
337 | // {debug}
338 | options.debug && window.Debug.log({
339 | 'node': node,
340 | 'function': 'delegateDynamicValidation()',
341 | 'arguments': 'node.find([' + _data.validation + '],[' + _data.regex + '])',
342 | 'message': 'ERROR - [' + _data.validation + '] not found'
343 | });
344 | // {/debug}
345 |
346 | return false;
347 | }
348 |
349 | var event = options.dynamic.settings.trigger + delegateSuffix;
350 | if (options.dynamic.settings.trigger !== "focusout") {
351 | event += " change" + delegateSuffix + " paste" + delegateSuffix;
352 | }
353 |
354 | $.each(
355 | node.find('[' + _data.validation + '],[' + _data.regex + ']'),
356 | function (index, input) {
357 |
358 | $(input).unbind(event).on(event, function (e) {
359 |
360 | if ($(this).is(':disabled')) {
361 | return false;
362 | }
363 |
364 | //e.preventDefault();
365 |
366 | var input = this,
367 | keyCode = e.keyCode || null;
368 |
369 | _typeWatch(function () {
370 |
371 | if (!validateInput(input)) {
372 |
373 | displayOneError(input.name);
374 | _executeCallback(options.dynamic.callback.onError, [node, input, keyCode, errors[input.name]]);
375 |
376 | } else {
377 |
378 | _executeCallback(options.dynamic.callback.onSuccess, [node, input, keyCode]);
379 |
380 | }
381 |
382 | _executeCallback(options.dynamic.callback.onComplete, [node, input, keyCode]);
383 |
384 | }, options.dynamic.settings.delay);
385 |
386 | });
387 | }
388 | );
389 | }
390 |
391 | /**
392 | * Delegates the submit validation on data-validation and data-validation-regex attributes based on trigger.
393 | * Note: Disable the form submit function so the callbacks are not by-passed
394 | */
395 | function delegateValidation() {
396 |
397 | _executeCallback(options.submit.callback.onInit, [node]);
398 |
399 | var event = options.submit.settings.trigger + '.vd';
400 |
401 | // {debug}
402 | options.debug && window.Debug.log({
403 | 'node': node,
404 | 'function': 'delegateValidation()',
405 | 'message': 'OK - Validation activated on ' + node.length + ' form(s)'
406 | });
407 | // {/debug}
408 |
409 | if (!node.find(options.submit.settings.button)[0]) {
410 |
411 | // {debug}
412 | options.debug && window.Debug.log({
413 | 'node': node,
414 | 'function': 'delegateValidation()',
415 | 'arguments': '{button: ' + options.submit.settings.button + '}',
416 | 'message': 'ERROR - node.find("' + options.submit.settings.button + '") not found'
417 | });
418 | // {/debug}
419 |
420 | return false;
421 |
422 | }
423 |
424 | node.on("submit", false);
425 | node.find(options.submit.settings.button).off('.vd').on(event, function (e) {
426 |
427 | e.preventDefault();
428 |
429 | resetErrors();
430 |
431 | _executeCallback(options.submit.callback.onValidate, [node]);
432 |
433 | if (!validateForm()) {
434 |
435 | displayErrors();
436 | _executeCallback(options.submit.callback.onError, [node, errors, formData]);
437 |
438 | } else {
439 |
440 | _executeCallback(options.submit.callback.onBeforeSubmit, [node]);
441 |
442 | if (typeof options.submit.callback.onSubmit === "function") {
443 | if (_executeCallback(options.submit.callback.onSubmit, [node, formData]) === true) {
444 | submitForm();
445 | }
446 | } else {
447 | submitForm();
448 | }
449 |
450 | _executeCallback(options.submit.callback.onAfterSubmit, [node]);
451 |
452 | }
453 |
454 | // {debug}
455 | options.debug && window.Debug.print();
456 | // {/debug}
457 |
458 | return false;
459 |
460 | });
461 |
462 | }
463 |
464 | /**
465 | * For every "data-validation" & "data-pattern" attributes that are not disabled inside the jQuery "node" object
466 | * the "validateInput" function will be called.
467 | *
468 | * @returns {Boolean} true if no error(s) were found (valid form)
469 | */
470 | function validateForm() {
471 |
472 | var isValid = isEmpty(errors);
473 |
474 | formData = {};
475 |
476 | $.each(
477 | node.find('input:not([type="submit"]), select, textarea').not(':disabled'),
478 | function(index, input) {
479 |
480 | input = $(input);
481 |
482 | var value = _getInputValue(input[0]),
483 | inputName = input.attr('name');
484 |
485 | if (inputName) {
486 | if (/\[]$/.test(inputName)) {
487 | inputName = inputName.replace(/\[]$/, '');
488 | if (!(formData[inputName] instanceof Array)) {
489 | formData[inputName] = [];
490 | }
491 | formData[inputName].push(value)
492 | } else {
493 | formData[inputName] = value;
494 | }
495 | }
496 |
497 | if (!!input.attr(_data.validation) || !!input.attr(_data.regex)) {
498 | if (!validateInput(input[0], value)) {
499 | isValid = false;
500 | }
501 | }
502 | }
503 | );
504 |
505 | prepareFormData();
506 |
507 | return isValid;
508 |
509 | }
510 |
511 | /**
512 | * Loop through formData and build an object
513 | *
514 | * @returns {Object} data
515 | */
516 | function prepareFormData() {
517 |
518 | var data = {},
519 | matches,
520 | index;
521 |
522 | for (var i in formData) {
523 | if (!formData.hasOwnProperty(i)) continue;
524 |
525 | index = 0;
526 | matches = i.split(/\[(.+?)]/g);
527 |
528 | var tmpObject = {},
529 | tmpArray = [];
530 |
531 | for (var k = matches.length - 1; k >= 0; k--) {
532 | if (matches[k] === "") {
533 | matches.splice(k, 1);
534 | continue;
535 | }
536 |
537 | if (tmpArray.length < 1) {
538 | tmpObject[matches[k]] = Number(formData[i]) || formData[i];
539 | } else {
540 | tmpObject = {};
541 | tmpObject[matches[k]] = tmpArray[tmpArray.length - 1];
542 | }
543 | tmpArray.push(tmpObject)
544 |
545 | }
546 |
547 | data = $.extend(true, data, tmpObject);
548 |
549 | }
550 |
551 | formData = data;
552 |
553 | }
554 |
555 | /**
556 | * Prepare the information from the data attributes
557 | * and call the "validateRule" function.
558 | *
559 | * @param {Object} input Reference of the input element
560 | *
561 | * @returns {Boolean} true if no error(s) were found (valid input)
562 | */
563 | function validateInput(input, value) {
564 |
565 | var inputName = $(input).attr('name'),
566 | value = value || _getInputValue(input);
567 |
568 | if (!inputName) {
569 |
570 | // {debug}
571 | options.debug && window.Debug.log({
572 | 'node': node,
573 | 'function': 'validateInput()',
574 | 'arguments': '$(input).attr("name")',
575 | 'message': 'ERROR - Missing input [name]'
576 | });
577 | // {/debug}
578 |
579 | return false;
580 | }
581 |
582 | var matches = inputName.replace(/]$/, '').split(/]\[|[[\]]/g),
583 | inputShortName = window.Validation.labels[inputName] ||
584 | options.labels[inputName] ||
585 | $(input).attr(_data.label) ||
586 | matches[matches.length - 1],
587 |
588 | validationArray = $(input).attr(_data.validation),
589 | validationMessage = $(input).attr(_data.validationMessage),
590 | validationRegex = $(input).attr(_data.regex),
591 | validationRegexReverse = !($(input).attr(_data.regexReverse) === undefined),
592 | validationRegexMessage = $(input).attr(_data.regexMessage),
593 |
594 | validateOnce = false;
595 |
596 | if (validationArray) {
597 | validationArray = _api._splitValidation(validationArray);
598 | }
599 |
600 | // Validates the "data-validation"
601 | if (validationArray instanceof Array && validationArray.length > 0) {
602 |
603 | // "OPTIONAL" input will not be validated if it's empty
604 | if ($.trim(value) === '' && ~validationArray.indexOf('OPTIONAL')) {
605 | return true;
606 | }
607 |
608 | $.each(validationArray, function (i, rule) {
609 |
610 | if (validateOnce === true) {
611 | return true;
612 | }
613 |
614 | try {
615 |
616 | validateRule(value, rule);
617 |
618 | } catch (error) {
619 |
620 | if (validationMessage || !options.submit.settings.allErrors) {
621 | validateOnce = true;
622 | }
623 |
624 | error[0] = validationMessage || error[0];
625 |
626 | registerError(inputName, error[0].replace('$', inputShortName).replace('%', error[1]));
627 |
628 | }
629 |
630 | });
631 |
632 | }
633 |
634 | // Validates the "data-validation-regex"
635 | if (validationRegex) {
636 |
637 | var rule = _buildRegexFromString(validationRegex);
638 |
639 | // Do not block validation if a regexp is bad, only skip it
640 | if (!(rule instanceof RegExp)) {
641 | return true;
642 | }
643 |
644 | try {
645 |
646 | validateRule(value, rule, validationRegexReverse);
647 |
648 | } catch (error) {
649 |
650 | error[0] = validationRegexMessage || error[0];
651 |
652 | registerError(inputName, error[0].replace('$', inputShortName));
653 |
654 | }
655 |
656 | }
657 |
658 | return !errors[inputName] || errors[inputName] instanceof Array && errors[inputName].length === 0;
659 |
660 | }
661 |
662 | /**
663 | * Validate an input value against one rule.
664 | * If a "value-rule" mismatch occurs, an error is thrown to the caller function.
665 | *
666 | * @param {String} value
667 | * @param {*} rule
668 | * @param {Boolean} [reversed]
669 | *
670 | * @returns {*} Error if a mismatch occurred.
671 | */
672 | function validateRule(value, rule, reversed) {
673 |
674 | // Validate for "data-validation-regex" and "data-validation-regex-reverse"
675 | if (rule instanceof RegExp) {
676 | var isValid = rule.test(value);
677 |
678 | if (reversed) {
679 | isValid = !isValid;
680 | }
681 |
682 | if (!isValid) {
683 | throw [options.messages['default'], ''];
684 | }
685 | return;
686 | }
687 |
688 | if (options.rules[rule]) {
689 | if (!options.rules[rule].test(value)) {
690 | throw [options.messages[rule], ''];
691 | }
692 | return;
693 | }
694 |
695 | // Validate for comparison "data-validation"
696 | var comparison = rule.match(options.rules.COMPARISON);
697 |
698 | if (!comparison || comparison.length !== 4) {
699 |
700 | // {debug}
701 | options.debug && window.Debug.log({
702 | 'node': node,
703 | 'function': 'validateRule()',
704 | 'arguments': 'value: ' + value + ' rule: ' + rule,
705 | 'message': 'WARNING - Invalid comparison'
706 | });
707 | // {/debug}
708 |
709 | return;
710 | }
711 |
712 | var type = comparison[1],
713 | operator = comparison[2],
714 | compared = comparison[3],
715 | comparedValue;
716 |
717 | switch (type) {
718 |
719 | // Compare input "Length"
720 | case "L":
721 |
722 | // Only numeric value for "L" are allowed
723 | if (isNaN(compared)) {
724 |
725 | // {debug}
726 | options.debug && window.Debug.log({
727 | 'node': node,
728 | 'function': 'validateRule()',
729 | 'arguments': 'compare: ' + compared + ' rule: ' + rule,
730 | 'message': 'WARNING - Invalid rule, "L" compare must be numeric'
731 | });
732 | // {/debug}
733 |
734 | return false;
735 |
736 | } else {
737 |
738 | if (!value || eval(value.length + operator + parseFloat(compared)) === false) {
739 | throw [options.messages[operator], compared];
740 | }
741 |
742 | }
743 |
744 | break;
745 |
746 | // Compare input "Value"
747 | case "V":
748 | default:
749 |
750 | // Compare Field values
751 | if (isNaN(compared)) {
752 |
753 | comparedValue = node.find('[name="' + compared + '"]').val();
754 | if (!comparedValue) {
755 |
756 | // {debug}
757 | options.debug && window.Debug.log({
758 | 'node': node,
759 | 'function': 'validateRule()',
760 | 'arguments': 'compare: ' + compared + ' rule: ' + rule,
761 | 'message': 'WARNING - Unable to find compared field [name="' + compared + '"]'
762 | });
763 | // {/debug}
764 |
765 | return false;
766 | }
767 |
768 | if (!value || !eval('"' + encodeURIComponent(value) + '"' + operator + '"' + encodeURIComponent(comparedValue) + '"')) {
769 | throw [options.messages[operator].replace(' characters', ''), compared];
770 | }
771 |
772 | } else {
773 | // Compare numeric value
774 | if (!value || isNaN(value) || !eval(value + operator + parseFloat(compared))) {
775 | throw [options.messages[operator].replace(' characters', ''), compared];
776 | }
777 |
778 | }
779 | break;
780 |
781 | }
782 |
783 | }
784 |
785 | /**
786 | * Register an error into the global "error" variable.
787 | *
788 | * @param {String} inputName Input where the error occurred
789 | * @param {String} error Description of the error to be displayed
790 | */
791 | function registerError(inputName, error) {
792 |
793 | if (!errors[inputName]) {
794 | errors[inputName] = [];
795 | }
796 |
797 | error = error.capitalize();
798 |
799 | var hasError = false;
800 | for (var i = 0; i < errors[inputName].length; i++) {
801 | if (errors[inputName][i] === error) {
802 | hasError = true;
803 | break;
804 | }
805 | }
806 |
807 | if (!hasError) {
808 | errors[inputName].push(error);
809 | }
810 |
811 | }
812 |
813 | /**
814 | * Display a single error based on "inputName" key inside the "errors" global array.
815 | * The input, the label and the "inputContainer" will be given the "errorClass" and other
816 | * settings will be considered.
817 | *
818 | * @param {String} inputName Key used for search into "errors"
819 | *
820 | * @returns {Boolean} false if an unwanted behavior occurs
821 | */
822 | function displayOneError(inputName) {
823 |
824 | var input,
825 | inputId,
826 | errorContainer,
827 | label,
828 | html = '',
829 | group,
830 | groupInput;
831 |
832 | if (!errors.hasOwnProperty(inputName)) {
833 | return false;
834 | }
835 |
836 | input = node.find('[name="' + inputName + '"]');
837 |
838 | label = null;
839 |
840 | if (!input[0]) {
841 |
842 | // {debug}
843 | options.debug && window.Debug.log({
844 | 'node': node,
845 | 'function': 'displayOneError()',
846 | 'arguments': '[name="' + inputName + '"]',
847 | 'message': 'ERROR - Unable to find input by name "' + inputName + '"'
848 | });
849 | // {/debug}
850 |
851 | return false;
852 | }
853 |
854 | group = input.attr(_data.group);
855 |
856 | if (group) {
857 |
858 | groupInput = node.find('[name="' + inputName + '"]');
859 | label = node.find('[id="' + group + '"]');
860 |
861 | if (label[0]) {
862 | label.addClass(options.submit.settings.errorClass);
863 | errorContainer = label;
864 | }
865 |
866 | //node.find('[' + _data.group + '="' + group + '"]').addClass(options.submit.settings.errorClass)
867 |
868 | } else {
869 |
870 | input.addClass(options.submit.settings.errorClass);
871 |
872 | if (options.submit.settings.inputContainer) {
873 | input.parentsUntil(node, options.submit.settings.inputContainer).addClass(options.submit.settings.errorClass);
874 | }
875 |
876 | inputId = input.attr('id');
877 |
878 | if (inputId) {
879 | label = node.find('label[for="' + inputId + '"]')[0];
880 | }
881 |
882 | if (!label) {
883 | label = input.parentsUntil(node, 'label')[0];
884 | }
885 |
886 | if (label) {
887 | label = $(label);
888 | label.addClass(options.submit.settings.errorClass);
889 | }
890 | }
891 |
892 | if (options.submit.settings.display === 'inline') {
893 | if (options.submit.settings.errorListContainer) {
894 | errorContainer = input.parentsUntil(node, options.submit.settings.errorListContainer);
895 | } else {
896 | errorContainer = errorContainer || input.parent();
897 | }
898 | } else if (options.submit.settings.display === 'block') {
899 | errorContainer = node;
900 | }
901 |
902 | // Prevent double error list if the previous one has not been cleared.
903 | if (options.submit.settings.display === 'inline' && errorContainer.find('[' + _data.errorList + ']')[0]) {
904 | return false;
905 | }
906 |
907 | if (options.submit.settings.display === "inline" ||
908 | (options.submit.settings.display === "block" && !errorContainer.find('[' + _data.errorList + ']')[0])
909 | ) {
910 | if (options.submit.settings.insertion === 'append') {
911 | errorContainer.append(html);
912 | } else if (options.submit.settings.insertion === 'prepend') {
913 | errorContainer.prepend(html);
914 | }
915 | }
916 |
917 | for (var i = 0; i < errors[inputName].length; i++) {
918 | if (options.submit.settings.errorTemplate) {
919 | errorContainer.find('[' + _data.errorList + '] ul')
920 | .append('' + options.submit.settings.errorTemplate.replace('{{validation-message}}', errors[inputName][i]) + '');
921 | } else {
922 | errorContainer.find('[' + _data.errorList + '] ul').append('' + errors[inputName][i] + '');
923 | }
924 | }
925 |
926 | if (options.submit.settings.clear || options.dynamic.settings.trigger) {
927 |
928 | if (group && groupInput) {
929 | input = groupInput;
930 | }
931 |
932 | var event = "coucou" + resetSuffix;
933 | if (options.submit.settings.clear) {
934 | event += " " + options.submit.settings.clear + resetSuffix;
935 | if (~['radio', 'checkbox'].indexOf(input[0].type)) {
936 | event += " change" + resetSuffix;
937 | }
938 | }
939 | if (options.dynamic.settings.trigger) {
940 | event += " " + options.dynamic.settings.trigger + resetSuffix;
941 | if (options.dynamic.settings.trigger !== "focusout" && !~['radio', 'checkbox'].indexOf(input[0].type)) {
942 | event += " change" + resetSuffix + " paste" + resetSuffix;
943 | }
944 | }
945 |
946 | input.unbind(event).on(event, function (a, b, c, d, e) {
947 |
948 | return function () {
949 | if (e) {
950 | if ($(c).hasClass(options.submit.settings.errorClass)) {
951 | resetOneError(a, b, c, d, e);
952 | }
953 | } else if ($(b).hasClass(options.submit.settings.errorClass)) {
954 | resetOneError(a, b, c, d);
955 | }
956 | };
957 |
958 | }(inputName, input, label, errorContainer, group));
959 | }
960 |
961 | if (options.submit.settings.scrollToError && !window.Validation.hasScrolled) {
962 |
963 | window.Validation.hasScrolled = true;
964 |
965 | var offset = parseFloat(options.submit.settings.scrollToError.offset) || 0,
966 | duration = parseFloat(options.submit.settings.scrollToError.duration) || 500,
967 | handle = (options.submit.settings.display === 'block') ? errorContainer : input;
968 |
969 | $('html, body').animate({
970 | scrollTop: handle.offset().top + offset
971 | }, duration);
972 |
973 | }
974 |
975 | }
976 |
977 | /**
978 | * Display all of the errors
979 | */
980 | function displayErrors() {
981 |
982 | for (var inputName in errors) {
983 | if (!errors.hasOwnProperty(inputName)) continue;
984 | displayOneError(inputName);
985 | }
986 |
987 | }
988 |
989 | /**
990 | * Remove an input error.
991 | *
992 | * @param {String} inputName Key reference to delete the error from "errors" global variable
993 | * @param {Object} input jQuery object of the input
994 | * @param {Object} label jQuery object of the input's label
995 | * @param {Object} container jQuery object of the "errorList"
996 | * @param {String} [group] Name of the group if any (ex: used on input radio)
997 | */
998 | function resetOneError(inputName, input, label, container, group) {
999 |
1000 | delete errors[inputName];
1001 |
1002 | if (container) {
1003 |
1004 | //window.Validation.hasScrolled = false;
1005 |
1006 | if (options.submit.settings.inputContainer) {
1007 | (group ? label : input).parentsUntil(node, options.submit.settings.inputContainer).removeClass(options.submit.settings.errorClass);
1008 | }
1009 |
1010 | label && label.removeClass(options.submit.settings.errorClass);
1011 |
1012 | input.removeClass(options.submit.settings.errorClass);
1013 |
1014 | if (options.submit.settings.display === 'inline') {
1015 | container.find('[' + _data.errorList + ']').remove();
1016 | }
1017 |
1018 | } else {
1019 |
1020 | if (!input) {
1021 | input = node.find('[name="' + inputName + '"]');
1022 |
1023 | if (!input[0]) {
1024 |
1025 | // {debug}
1026 | options.debug && window.Debug.log({
1027 | 'node': node,
1028 | 'function': 'resetOneError()',
1029 | 'arguments': '[name="' + inputName + '"]',
1030 | 'message': 'ERROR - Unable to find input by name "' + inputName + '"'
1031 | });
1032 | // {/debug}
1033 |
1034 | return false;
1035 | }
1036 | }
1037 |
1038 | input.trigger('coucou' + resetSuffix);
1039 | }
1040 |
1041 | }
1042 |
1043 | /**
1044 | * Remove all of the input error(s) display.
1045 | */
1046 | function resetErrors() {
1047 |
1048 | errors = [];
1049 | window.Validation.hasScrolled = false;
1050 |
1051 | node.find('[' + _data.errorList + ']').remove();
1052 | node.find('.' + options.submit.settings.errorClass).removeClass(options.submit.settings.errorClass);
1053 |
1054 | }
1055 |
1056 | /**
1057 | * Submits the form once it succeeded the validation process.
1058 | * Note:
1059 | * - This function will be overridden if "options.submit.settings.onSubmit" is defined
1060 | * - The node can't be submitted by jQuery since it has been disabled, use the form native submit function instead
1061 | */
1062 | function submitForm() {
1063 |
1064 | node[0].submit()
1065 |
1066 | }
1067 |
1068 | /**
1069 | * Destroy the Validation instance
1070 | *
1071 | * @returns {Boolean}
1072 | */
1073 | function destroy() {
1074 |
1075 | resetErrors();
1076 | node.find('[' + _data.validation + '],[' + _data.regex + ']').off(delegateSuffix + ' ' + resetSuffix);
1077 |
1078 | node.find(options.submit.settings.button).off(delegateSuffix).on('click' + delegateSuffix, function () {
1079 | $(this).closest('form')[0].submit();
1080 | });
1081 |
1082 | //delete window.Validation.form[node.selector];
1083 |
1084 | return true;
1085 |
1086 | }
1087 |
1088 | /**
1089 | * @private
1090 | * Helper to get the value of an regular, radio or chackbox input
1091 | *
1092 | * @param input
1093 | *
1094 | * @returns {String} value
1095 | */
1096 | var _getInputValue = function (input) {
1097 |
1098 | var value;
1099 |
1100 | // Get the value or state of the input based on its type
1101 | switch ($(input).attr('type')) {
1102 | case 'checkbox':
1103 | value = ($(input).is(':checked')) ? 1 : '';
1104 | break;
1105 | case 'radio':
1106 | value = node.find('input[name="' + $(input).attr('name') + '"]:checked').val() || '';
1107 | break;
1108 | default:
1109 | value = $(input).val();
1110 | break;
1111 | }
1112 |
1113 | return value;
1114 |
1115 | };
1116 |
1117 | /**
1118 | * @private
1119 | * Execute function once the timer is reached.
1120 | * If the function is recalled before the timer ends, the first call will be canceled.
1121 | */
1122 | var _typeWatch = (function () {
1123 | var timer = 0;
1124 | return function (callback, ms) {
1125 | clearTimeout(timer);
1126 | timer = setTimeout(callback, ms);
1127 | };
1128 | })();
1129 |
1130 | /**
1131 | * @private
1132 | * Executes an anonymous function or a string reached from the window scope.
1133 | *
1134 | * @example
1135 | * Note: These examples works with every callbacks (onInit, onError, onSubmit, onBeforeSubmit & onAfterSubmit)
1136 | *
1137 | * // An anonymous function inside the "onInit" option
1138 | * onInit: function() { console.log(':D'); };
1139 | *
1140 | * * // myFunction() located on window.coucou scope
1141 | * onInit: 'window.coucou.myFunction'
1142 | *
1143 | * // myFunction(a,b) located on window.coucou scope passing 2 parameters
1144 | * onInit: ['window.coucou.myFunction', [':D', ':)']];
1145 | *
1146 | * // Anonymous function to execute a local function
1147 | * onInit: function () { myFunction(':D'); }
1148 | *
1149 | * @param {String|Array} callback The function to be called
1150 | * @param {Array} [extraParams] In some cases the function can be called with Extra parameters (onError)
1151 | *
1152 | * @returns {Boolean}
1153 | */
1154 | var _executeCallback = function (callback, extraParams) {
1155 |
1156 | if (!callback) {
1157 | return false;
1158 | }
1159 |
1160 | var _callback;
1161 |
1162 | if (typeof callback === "function") {
1163 |
1164 | _callback = callback;
1165 |
1166 | } else if (typeof callback === "string" || callback instanceof Array) {
1167 |
1168 | _callback = window;
1169 |
1170 | if (typeof callback === "string") {
1171 | callback = [callback, []];
1172 | }
1173 |
1174 | var _exploded = callback[0].split('.'),
1175 | _params = callback[1],
1176 | _isValid = true,
1177 | _splitIndex = 0;
1178 |
1179 | while (_splitIndex < _exploded.length) {
1180 |
1181 | if (typeof _callback !== 'undefined') {
1182 | _callback = _callback[_exploded[_splitIndex++]];
1183 | } else {
1184 | _isValid = false;
1185 | break;
1186 | }
1187 | }
1188 |
1189 | if (!_isValid || typeof _callback !== "function") {
1190 |
1191 | // {debug}
1192 | options.debug && window.Debug.log({
1193 | 'node': node,
1194 | 'function': '_executeCallback()',
1195 | 'arguments': JSON.stringify(callback),
1196 | 'message': 'WARNING - Invalid callback function"'
1197 | });
1198 | // {/debug}
1199 |
1200 | return false;
1201 | }
1202 |
1203 | }
1204 |
1205 | return _callback.apply(this, $.merge(_params || [], (extraParams) ? extraParams : []));
1206 |
1207 | };
1208 |
1209 | /**
1210 | * @private
1211 | * Constructs Validation
1212 | */
1213 | this.__construct = function () {
1214 |
1215 | extendOptions();
1216 | extendRules();
1217 | extendMessages();
1218 |
1219 | delegateDynamicValidation();
1220 | delegateValidation();
1221 |
1222 | // {debug}
1223 | options.debug && window.Debug.print();
1224 | // {/debug}
1225 |
1226 | }();
1227 |
1228 | return {
1229 |
1230 | /**
1231 | * @public
1232 | * Register error
1233 | *
1234 | * @param inputName
1235 | * @param error
1236 | */
1237 | registerError: registerError,
1238 |
1239 | /**
1240 | * @public
1241 | * Display one error
1242 | *
1243 | * @param inputName
1244 | */
1245 | displayOneError: displayOneError,
1246 |
1247 | /**
1248 | * @public
1249 | * Display all errors
1250 | */
1251 | displayErrors: displayErrors,
1252 |
1253 | /**
1254 | * @public
1255 | * Remove one error
1256 | */
1257 | resetOneError: resetOneError,
1258 |
1259 | /**
1260 | * @public
1261 | * Remove all errors
1262 | */
1263 | resetErrors: resetErrors,
1264 |
1265 | /**
1266 | * @public
1267 | * Destroy the Validation instance
1268 | */
1269 | destroy: destroy
1270 |
1271 | };
1272 |
1273 | };
1274 |
1275 | // =================================================================================================================
1276 |
1277 | /**
1278 | * @public
1279 | * jQuery public function to implement the Validation on the selected node(s).
1280 | *
1281 | * @param {object} options To configure the Validation class.
1282 | *
1283 | * @return {object} Modified DOM element
1284 | */
1285 | $.fn.validate = $.validate = function (options) {
1286 |
1287 | return _api.validate(this, options);
1288 |
1289 | };
1290 |
1291 | /**
1292 | * @public
1293 | * jQuery public function to add one or multiple "data-validation" argument.
1294 | *
1295 | * @param {string|array} validation Arguments to add in the node's data-validation
1296 | *
1297 | * @return {object} Modified DOM element
1298 | */
1299 | $.fn.addValidation = function (validation) {
1300 |
1301 | return _api.addValidation(this, validation);
1302 |
1303 | };
1304 |
1305 | /**
1306 | * @public
1307 | * jQuery public function to remove one or multiple "data-validation" argument.
1308 | *
1309 | * @param {string|array} validation Arguments to remove in the node's data-validation
1310 | *
1311 | * @return {object} Modified DOM element
1312 | */
1313 | $.fn.removeValidation = function (validation) {
1314 |
1315 | return _api.removeValidation(this, validation);
1316 |
1317 | };
1318 |
1319 | /**
1320 | * @public
1321 | * jQuery public function to add one or multiple errors.
1322 | *
1323 | * @param {object} error Object of errors where the keys are the input names
1324 | * @example
1325 | * $('form#myForm').addError({
1326 | * 'username': 'Invalid username, please choose another one.'
1327 | * });
1328 | *
1329 | * @return {object} Modified DOM element
1330 | */
1331 | $.fn.addError = function (error) {
1332 |
1333 | return _api.addError(this, error);
1334 |
1335 | };
1336 |
1337 | /**
1338 | * @public
1339 | * jQuery public function to remove one or multiple errors.
1340 | *
1341 | * @param {array} error Array of errors where the keys are the input names
1342 | * @example
1343 | * $('form#myForm').removeError([
1344 | * 'username'
1345 | * ]);
1346 | *
1347 | * @return {object} Modified DOM element
1348 | */
1349 | $.fn.removeError = function (error) {
1350 |
1351 | return _api.removeError(this, error);
1352 |
1353 | };
1354 |
1355 | /**
1356 | * @public
1357 | * jQuery public function to add a validation rule.
1358 | *
1359 | * @example
1360 | * $.alterValidationRules({
1361 | * rule: 'FILENAME',
1362 | * regex: /^[^\\/:\*\?<>\|\"\']*$/,
1363 | * message: '$ has an invalid filename.'
1364 | * })
1365 | *
1366 | * @param {Object|Array} name
1367 | */
1368 | $.fn.alterValidationRules = $.alterValidationRules = function (rules) {
1369 |
1370 | if (!(rules instanceof Array)) {
1371 | rules = [rules];
1372 | }
1373 |
1374 | for (var i = 0; i < rules.length; i++) {
1375 | _api.alterValidationRules(rules[i]);
1376 | }
1377 |
1378 | };
1379 |
1380 | // =================================================================================================================
1381 |
1382 | /**
1383 | * @private
1384 | * API to handles "addValidation" and "removeValidation" on attribute "data-validation".
1385 | * Note: Contains fail-safe operations to unify the validation parameter.
1386 | *
1387 | * @example
1388 | * $.addValidation('NOTEMPTY, L>=6')
1389 | * $.addValidation('[notempty, v>=6]')
1390 | * $.removeValidation(['OPTIONAL', 'V>=6'])
1391 | *
1392 | * @returns {object} Updated DOM object
1393 | */
1394 | var _api = {
1395 |
1396 | /**
1397 | * @private
1398 | * This function unifies the data through the validation process.
1399 | * String, Uppercase and spaceless.
1400 | *
1401 | * @param {string|array} validation
1402 | *
1403 | * @returns {string}
1404 | */
1405 | _formatValidation: function (validation) {
1406 |
1407 | validation = validation.toString().replace(/\s/g, '');
1408 |
1409 | if (validation.charAt(0) === "[" && validation.charAt(validation.length - 1) === "]") {
1410 | validation = validation.replace(/^\[|]$/g, '');
1411 | }
1412 |
1413 | return validation;
1414 |
1415 | },
1416 |
1417 | /**
1418 | * @private
1419 | * Splits the validation into an array, Uppercase the rules if they are not comparisons
1420 | *
1421 | * @param {String|Array} validation
1422 | *
1423 | * @returns {Array} Formatted validation keys
1424 | */
1425 | _splitValidation: function (validation) {
1426 |
1427 | var validationArray = this._formatValidation(validation).split(','),
1428 | oneValidation;
1429 |
1430 | for (var i = 0; i < validationArray.length; i++) {
1431 | oneValidation = validationArray[i];
1432 | if (/^[a-z]+$/i.test(oneValidation)) {
1433 | validationArray[i] = oneValidation.toUpperCase();
1434 | }
1435 | }
1436 |
1437 | return validationArray;
1438 | },
1439 |
1440 | /**
1441 | * @private
1442 | * Joins the validation array to create the "data-validation" value
1443 | *
1444 | * @param {Array} validation
1445 | *
1446 | * @returns {String}
1447 | */
1448 | _joinValidation: function (validation) {
1449 |
1450 | return '[' + validation.join(', ') + ']';
1451 |
1452 | },
1453 |
1454 | /**
1455 | * API method to attach the submit event type on the specified node.
1456 | * Note: Clears the previous event regardless to avoid double submits or unwanted behaviors.
1457 | *
1458 | * @param {Object} node jQuery object(s)
1459 | * @param {Object} options To configure the Validation class.
1460 | *
1461 | * @returns {*}
1462 | */
1463 | validate: function (node, options) {
1464 |
1465 | if (typeof node === "function") {
1466 |
1467 | if (!options.submit.settings.form) {
1468 |
1469 | // {debug}
1470 | window.Debug.log({
1471 | 'node': node,
1472 | 'function': '$.validate()',
1473 | 'arguments': '',
1474 | 'message': 'Undefined property "options.submit.settings.form - Validation dropped'
1475 | });
1476 |
1477 | window.Debug.print();
1478 | // {/debug}
1479 |
1480 | return;
1481 | }
1482 |
1483 | node = $(options.submit.settings.form);
1484 |
1485 | if (!node[0] || node[0].nodeName.toLowerCase() !== "form") {
1486 |
1487 | // {debug}
1488 | window.Debug.log({
1489 | 'function': '$.validate()',
1490 | 'arguments': options.submit.settings.form,
1491 | 'message': 'Unable to find jQuery form element - Validation dropped'
1492 | });
1493 |
1494 | window.Debug.print();
1495 | // {/debug}
1496 |
1497 | return;
1498 | }
1499 |
1500 | } else if (typeof node[0] === 'undefined') {
1501 |
1502 | // {debug}
1503 | window.Debug.log({
1504 | 'node': node,
1505 | 'function': '$.validate()',
1506 | 'arguments': '$("' + node.selector + '").validate()',
1507 | 'message': 'Unable to find jQuery form element - Validation dropped'
1508 | });
1509 |
1510 | window.Debug.print();
1511 | // {/debug}
1512 |
1513 | return;
1514 | }
1515 |
1516 | if (options === "destroy") {
1517 |
1518 | if (!window.Validation.form[node.selector]) {
1519 |
1520 | // {debug}
1521 | window.Debug.log({
1522 | 'node': node,
1523 | 'function': '$.validate("destroy")',
1524 | 'arguments': '',
1525 | 'message': 'Unable to destroy "' + node.selector + '", perhaps it\'s already destroyed?'
1526 | });
1527 |
1528 | window.Debug.print();
1529 | // {/debug}
1530 |
1531 | return;
1532 | }
1533 |
1534 | window.Validation.form[node.selector].destroy();
1535 |
1536 | return;
1537 |
1538 | }
1539 |
1540 | return node.each(function () {
1541 | window.Validation.form[node.selector] = new Validation($(this), options);
1542 | });
1543 |
1544 | },
1545 |
1546 | /**
1547 | * API method to handle the addition of "data-validation" arguments.
1548 | * Note: ONLY the predefined validation arguments are allowed to be added
1549 | * inside the "data-validation" attribute (see configuration).
1550 | *
1551 | * @param {Object} node jQuery objects
1552 | * @param {String|Array} validation arguments to add in the node(s) "data-validation"
1553 | *
1554 | * @returns {*}
1555 | */
1556 | addValidation: function (node, validation) {
1557 |
1558 | var self = this;
1559 |
1560 | validation = self._splitValidation(validation);
1561 |
1562 | if (!validation) {
1563 | return false;
1564 | }
1565 |
1566 | return node.each(function () {
1567 |
1568 | var $this = $(this),
1569 | validationData = $this.attr(_data.validation),
1570 | validationArray = (validationData && validationData.length) ? self._splitValidation(validationData) : [],
1571 | oneValidation;
1572 |
1573 | for (var i = 0; i < validation.length; i++) {
1574 |
1575 | oneValidation = self._formatValidation(validation[i]);
1576 |
1577 | if ($.inArray(oneValidation, validationArray) === -1) {
1578 | validationArray.push(oneValidation);
1579 | }
1580 | }
1581 |
1582 | if (validationArray.length) {
1583 | $this.attr(_data.validation, self._joinValidation(validationArray));
1584 | }
1585 |
1586 | });
1587 |
1588 | },
1589 |
1590 | /**
1591 | * API method to handle the removal of "data-validation" arguments.
1592 | *
1593 | * @param {Object} node jQuery objects
1594 | * @param {String|Array} validation arguments to remove in the node(s) "data-validation"
1595 | *
1596 | * @returns {*}
1597 | */
1598 | removeValidation: function (node, validation) {
1599 |
1600 | var self = this;
1601 |
1602 | validation = self._splitValidation(validation);
1603 | if (!validation) {
1604 | return false;
1605 | }
1606 |
1607 | return node.each(function () {
1608 |
1609 | var $this = $(this),
1610 | validationData = $this.attr(_data.validation),
1611 | validationArray = (validationData && validationData.length) ? self._splitValidation(validationData) : [],
1612 | oneValidation,
1613 | validationIndex;
1614 |
1615 | if (!validationArray.length) {
1616 | $this.removeAttr(_data.validation);
1617 | return true;
1618 | }
1619 |
1620 | for (var i = 0; i < validation.length; i++) {
1621 | oneValidation = self._formatValidation(validation[i]);
1622 | validationIndex = $.inArray(oneValidation, validationArray);
1623 | if (validationIndex !== -1) {
1624 | validationArray.splice(validationIndex, 1);
1625 | }
1626 |
1627 | }
1628 |
1629 | if (!validationArray.length) {
1630 | $this.removeAttr(_data.validation);
1631 | return true;
1632 | }
1633 |
1634 | $this.attr(_data.validation, self._joinValidation(validationArray));
1635 |
1636 | });
1637 |
1638 | },
1639 |
1640 | /**
1641 | * API method to manually trigger a form error.
1642 | * Note: The same form jQuery selector MUST be used to recuperate the Validation configuration.
1643 | *
1644 | * @example
1645 | * $('#form-signup_v3').addError({
1646 | * 'inputName': 'my error message',
1647 | * 'inputName2': [
1648 | * 'first error message',
1649 | * 'second error message'
1650 | * ]
1651 | * })
1652 | *
1653 | * @param {Object} node jQuery object
1654 | * @param {Object} error Object of errors to add on the node
1655 | *
1656 | * @returns {*}
1657 | */
1658 | addError: function (node, error) {
1659 |
1660 | if (!window.Validation.form[node.selector]) {
1661 |
1662 | // {debug}
1663 | window.Debug.log({
1664 | 'node': node,
1665 | 'function': '$.addError()',
1666 | 'arguments': 'window.Validation.form[' + node.selector + ']',
1667 | 'message': 'ERROR - Invalid node selector'
1668 | });
1669 |
1670 | window.Debug.print();
1671 | // {/debug}
1672 |
1673 | return false;
1674 | }
1675 |
1676 | if (typeof error !== "object" || Object.prototype.toString.call(error) !== "[object Object]") {
1677 |
1678 | // {debug}
1679 | window.Debug.log({
1680 | 'node': node,
1681 | 'function': '$.addError()',
1682 | 'arguments': 'window.Validation.form[' + node.selector + ']',
1683 | 'message': 'ERROR - Invalid argument, must be type object'
1684 | });
1685 |
1686 | window.Debug.print();
1687 | // {/debug}
1688 |
1689 | return false;
1690 | }
1691 |
1692 | var input,
1693 | onlyOnce = true;
1694 | for (var inputName in error) {
1695 |
1696 | if (!error.hasOwnProperty(inputName)) {
1697 | continue;
1698 | }
1699 |
1700 | if (!(error[inputName] instanceof Array)) {
1701 | error[inputName] = [error[inputName]];
1702 | }
1703 |
1704 | input = $(node.selector).find('[name="' + inputName + '"]');
1705 | if (!input[0]) {
1706 |
1707 | // {debug}
1708 | window.Debug.log({
1709 | 'node': node,
1710 | 'function': '$.addError()',
1711 | 'arguments': inputName,
1712 | 'message': 'ERROR - Unable to find ' + '$(' + node.selector + ').find("[name="' + inputName + '"]")'
1713 | });
1714 |
1715 | window.Debug.print();
1716 | // {/debug}
1717 |
1718 | continue;
1719 | }
1720 |
1721 | if (onlyOnce) {
1722 | window.Validation.hasScrolled = false;
1723 | onlyOnce = false;
1724 | }
1725 |
1726 | window.Validation.form[node.selector].resetOneError(inputName, input);
1727 |
1728 | for (var i = 0; i < error[inputName].length; i++) {
1729 |
1730 | if (typeof error[inputName][i] !== "string") {
1731 |
1732 | // {debug}
1733 | window.Debug.log({
1734 | 'node': node,
1735 | 'function': '$.addError()',
1736 | 'arguments': error[inputName][i],
1737 | 'message': 'ERROR - Invalid error object property - Accepted format: {"inputName": "errorString"} or {"inputName": ["errorString", "errorString"]}'
1738 | });
1739 |
1740 | window.Debug.print();
1741 | // {/debug}
1742 |
1743 | continue;
1744 | }
1745 |
1746 | window.Validation.form[node.selector].registerError(inputName, error[inputName][i]);
1747 |
1748 | }
1749 |
1750 | window.Validation.form[node.selector].displayOneError(inputName);
1751 |
1752 | }
1753 |
1754 | },
1755 |
1756 | /**
1757 | * API method to manually remove a form error.
1758 | * Note: The same form jQuery selector MUST be used to recuperate the Validation configuration.
1759 | *
1760 | * @example
1761 | * $('#form-signin_v2').removeError([
1762 | * 'signin_v2[username]',
1763 | * 'signin_v2[password]'
1764 | * ])
1765 | *
1766 | * @param {Object} node jQuery object
1767 | * @param {Object} inputName Object of errors to remove on the node
1768 | *
1769 | * @returns {*}
1770 | */
1771 | removeError: function (node, inputName) {
1772 |
1773 | if (!window.Validation.form[node.selector]) {
1774 |
1775 | // {debug}
1776 | window.Debug.log({
1777 | 'node': node,
1778 | 'function': '$.removeError()',
1779 | 'arguments': 'window.Validation.form[' + node.selector + ']',
1780 | 'message': 'ERROR - Invalid node selector'
1781 | });
1782 |
1783 | window.Debug.print();
1784 | // {/debug}
1785 |
1786 | return false;
1787 | }
1788 |
1789 | if (!inputName) {
1790 | window.Validation.form[node.selector].resetErrors();
1791 | return false;
1792 | }
1793 |
1794 | if (typeof inputName === "object" && Object.prototype.toString.call(inputName) !== "[object Array]") {
1795 |
1796 | // {debug}
1797 | window.Debug.log({
1798 | 'node': node,
1799 | 'function': '$.removeError()',
1800 | 'arguments': inputName,
1801 | 'message': 'ERROR - Invalid inputName, must be type String or Array'
1802 | });
1803 |
1804 | window.Debug.print();
1805 | // {/debug}
1806 |
1807 | return false;
1808 | }
1809 |
1810 | if (!(inputName instanceof Array)) {
1811 | inputName = [inputName];
1812 | }
1813 |
1814 | var input;
1815 | for (var i = 0; i < inputName.length; i++) {
1816 |
1817 | input = $(node.selector).find('[name="' + inputName[i] + '"]');
1818 | if (!input[0]) {
1819 |
1820 | // {debug}
1821 | window.Debug.log({
1822 | 'node': node,
1823 | 'function': '$.removeError()',
1824 | 'arguments': inputName[i],
1825 | 'message': 'ERROR - Unable to find ' + '$(' + node.selector + ').find("[name="' + inputName[i] + '"]")'
1826 | });
1827 |
1828 | window.Debug.print();
1829 | // {/debug}
1830 |
1831 | continue;
1832 | }
1833 |
1834 | window.Validation.form[node.selector].resetOneError(inputName[i], input);
1835 |
1836 | }
1837 |
1838 | },
1839 |
1840 | /**
1841 | * API method to add a validation rule.
1842 | *
1843 | * @example
1844 | * $.alterValidationRules({
1845 | * rule: 'FILENAME',
1846 | * regex: /^[^\\/:\*\?<>\|\"\']*$/,
1847 | * message: '$ has an invalid filename.'
1848 | * })
1849 | *
1850 | * @param {Object} ruleObj
1851 | */
1852 | alterValidationRules: function (ruleObj) {
1853 |
1854 | if (!ruleObj.rule || (!ruleObj.regex && !ruleObj.message)) {
1855 | // {debug}
1856 | window.Debug.log({
1857 | 'function': '$.alterValidationRules()',
1858 | 'message': 'ERROR - Missing one or multiple parameter(s) {rule, regex, message}'
1859 | });
1860 | window.Debug.print();
1861 | // {/debug}
1862 | return false;
1863 | }
1864 |
1865 | ruleObj.rule = ruleObj.rule.toUpperCase();
1866 |
1867 | if (ruleObj.regex) {
1868 |
1869 | var regex = _buildRegexFromString(ruleObj.regex);
1870 |
1871 | if (!(regex instanceof RegExp)) {
1872 | // {debug}
1873 | window.Debug.log({
1874 | 'function': '$.alterValidationRules(rule)',
1875 | 'arguments': regex.toString(),
1876 | 'message': 'ERROR - Invalid rule'
1877 | });
1878 | window.Debug.print();
1879 | // {/debug}
1880 | return false;
1881 | }
1882 |
1883 | _rules[ruleObj.rule] = regex;
1884 | }
1885 |
1886 | if (ruleObj.message) {
1887 | _messages[ruleObj.rule] = ruleObj.message;
1888 | }
1889 |
1890 | return true;
1891 | }
1892 |
1893 | };
1894 |
1895 | /**
1896 | * @private
1897 | * Converts string into a regex
1898 | *
1899 | * @param {String|Object} regex
1900 | * @returns {Object|Boolean} rule
1901 | */
1902 | function _buildRegexFromString(regex) {
1903 |
1904 | if (!regex || (typeof regex !== "string" && !(regex instanceof RegExp))) {
1905 | _regexDebug();
1906 | return false;
1907 | }
1908 |
1909 | if (typeof regex !== 'string') {
1910 | regex = regex.toString();
1911 | }
1912 |
1913 | var separator = regex.charAt(0),
1914 | index = regex.length - 1,
1915 | pattern,
1916 | modifier,
1917 | rule;
1918 |
1919 | while (index > 0) {
1920 | if (/[gimsxeU]/.test(regex.charAt(index))) {
1921 | index--;
1922 | } else {
1923 | break;
1924 | }
1925 | }
1926 |
1927 | if (regex.charAt(index) !== separator) {
1928 | separator = null;
1929 | }
1930 |
1931 | if (separator && index !== regex.length - 1) {
1932 | modifier = regex.substr(index + 1, regex.length - 1);
1933 | }
1934 |
1935 | if (separator) {
1936 | pattern = regex.substr(1, index - 1);
1937 | } else {
1938 | pattern = regex;
1939 | }
1940 |
1941 | try {
1942 | rule = new RegExp(pattern, modifier);
1943 | } catch (error) {
1944 | _regexDebug();
1945 | return false;
1946 | }
1947 |
1948 | return rule;
1949 |
1950 | function _regexDebug() {
1951 | // {debug}
1952 | window.Debug.log({
1953 | 'function': '_buildRegexFromString()',
1954 | 'arguments': '{pattern: {' + (pattern || '') + '}, modifier: {' + (modifier || '') + '}',
1955 | 'message': 'WARNING - Invalid regex given: ' + regex
1956 | });
1957 | window.Debug.print();
1958 | // {/debug}
1959 | }
1960 |
1961 | }
1962 |
1963 | // {debug}
1964 | window.Debug = {
1965 |
1966 | table: {},
1967 | log: function (debugObject) {
1968 |
1969 | if (!debugObject.message || typeof debugObject.message !== "string") {
1970 | return false;
1971 | }
1972 |
1973 | this.table[debugObject.message] = $.extend(
1974 | Object.preventExtensions(
1975 | {
1976 | 'node': '',
1977 | 'function': '',
1978 | 'arguments': ''
1979 | }
1980 | ), debugObject
1981 | );
1982 |
1983 | },
1984 | print: function () {
1985 |
1986 | if (isEmpty(this.table)) {
1987 | return false;
1988 | }
1989 |
1990 | if (console.group !== undefined || console.table !== undefined) {
1991 |
1992 | console.groupCollapsed('--- jQuery Form Validation Debug ---');
1993 |
1994 | if (console.table) {
1995 | console.table(this.table);
1996 | } else {
1997 | $.each(this.table, function (index, data) {
1998 | console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms');
1999 | });
2000 | }
2001 |
2002 | console.groupEnd();
2003 |
2004 | } else {
2005 | console.log('Debug is not available on your current browser, try the most recent version of Chrome or Firefox.');
2006 | }
2007 |
2008 | this.table = {};
2009 |
2010 | }
2011 |
2012 | };
2013 | // {/debug}
2014 |
2015 | String.prototype.capitalize = function () {
2016 | return this.charAt(0).toUpperCase() + this.slice(1);
2017 | };
2018 |
2019 | function isEmpty (obj) {
2020 | for (var prop in obj) {
2021 | if (obj.hasOwnProperty(prop))
2022 | return false;
2023 | }
2024 |
2025 | return true;
2026 | }
2027 |
2028 | if (!Array.prototype.indexOf) {
2029 | Array.prototype.indexOf = function (elt /*, from*/) {
2030 | var len = this.length >>> 0;
2031 |
2032 | var from = Number(arguments[1]) || 0;
2033 | from = (from < 0)
2034 | ? Math.ceil(from)
2035 | : Math.floor(from);
2036 | if (from < 0)
2037 | from += len;
2038 |
2039 | for (; from < len; from++) {
2040 | if (from in this &&
2041 | this[from] === elt)
2042 | return from;
2043 | }
2044 | return -1;
2045 | };
2046 | }
2047 |
2048 | // {debug}
2049 | if (!JSON && !JSON.stringify) {
2050 | JSON.stringify = function (obj) {
2051 | var t = typeof (obj);
2052 | if (t !== "object" || obj === null) {
2053 | // simple data type
2054 | if (t === "string") {
2055 | obj = '"' + obj + '"';
2056 | }
2057 | return String(obj);
2058 | }
2059 | else {
2060 | var n, v, json = [], arr = (obj && obj.constructor === Array);
2061 | for (n in obj) {
2062 | if (true) {
2063 | v = obj[n];
2064 | t = typeof(v);
2065 | if (t === "string") {
2066 | v = '"' + v + '"';
2067 | }
2068 | else if (t === "object" && v !== null) {
2069 | v = JSON.stringify(v);
2070 | }
2071 | json.push((arr ? "" : '"' + n + '": ') + String(v));
2072 | }
2073 | }
2074 | return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
2075 | }
2076 | };
2077 | }
2078 | // {/debug}
2079 |
2080 | }(window, document, window.jQuery));
--------------------------------------------------------------------------------