├── .gitignore
├── .jshintrc
├── .travis.yml
├── LICENSE
├── README.md
├── bower.json
├── dist
├── approve.js
├── approve.min.gzip.js
├── approve.min.gzip.js.map
├── approve.min.js
└── approve.min.js.map
├── gulpfile.js
├── package.json
├── rollup.config.js
├── src
├── approve.js
└── tests
│ ├── approve.creditcard.js
│ ├── approve.strength.js
│ └── approve.tests.js
└── test
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /docs
3 | *.log
4 | coppersmith.*
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "console": false,
4 | "jQuery": false,
5 | "_": false
6 | },
7 | "maxparams": 5,
8 | "maxdepth": 5,
9 | "maxstatements": 25,
10 | "maxcomplexity": 10,
11 | "es5": true,
12 | "browser": true,
13 | "boss": false,
14 | "curly": false,
15 | "debug": false,
16 | "devel": false,
17 | "eqeqeq": true,
18 | "evil": true,
19 | "forin": false,
20 | "immed": true,
21 | "laxbreak": false,
22 | "newcap": true,
23 | "noarg": true,
24 | "noempty": false,
25 | "nonew": false,
26 | "nomen": false,
27 | "onevar": true,
28 | "plusplus": false,
29 | "regexp": false,
30 | "undef": true,
31 | "sub": true,
32 | "strict": false,
33 | "white": true,
34 | "unused": true
35 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - stable
5 |
6 | install:
7 | - npm install
8 |
9 | script:
10 | - npm test
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Charl Gottschalk
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ApproveJs
2 | #### A simple validation library that doesn't interfere
3 |   [](https://cdnjs.com/libraries/approvejs) [](https://travis-ci.org/CharlGottschalk/approvejs) 
4 |
5 | ---
6 |
7 | ApproveJs doesn't automatically attach itself to input change events or form submit events. It also doesn't manipulate the DOM for you by automatically displaying errors. This allows you to automate validation how you want.
8 |
9 | With a single method (`approve.value()`), you can decide how to handle validation.
10 |
11 | If you like to be in control or have a little OCD like me, ApproveJs is for you.
12 |
13 | ApproveJs is also easily extended with [custom tests](https://charlgottschalk.github.io/approvejs/docs/custom-tests).
14 |
15 | ---
16 |
17 | ### Installation
18 |
19 | ##### Standalone
20 |
21 | [Download Latest Release](https://github.com/CharlGottschalk/approvejs/archive/master.zip)
22 |
23 | Unzip `master.zip` into your desired folder and add a `script` tag to the library before the end of your closing `
` tag
24 |
25 | ```html
26 |
27 | ```
28 |
29 | ##### Bower
30 |
31 | In your terminal run:
32 |
33 | ```
34 | $ bower install approvejs
35 | ```
36 |
37 | Add a `script` tag to the library before the end of your closing `` tag
38 |
39 | ```html
40 |
41 | ```
42 |
43 | ##### cdnjs
44 |
45 | Add a `script` tag to the library CDN url before the end of your closing `` tag
46 |
47 | ```html
48 |
49 | ```
50 |
51 | Get the cdn urls from [here](https://cdnjs.com/libraries/approvejs)
52 |
53 | *Many thanks to [cdnjs](https://cdnjs.com/) who kindly hosts ApproveJS through a reliable CDN*
54 |
55 | ##### Node
56 |
57 | In your terminal run:
58 |
59 | ```
60 | $ npm install approvejs
61 | ```
62 |
63 | or if you're using [Yarn](https://yarnpkg.com/)
64 |
65 | ```
66 | $ yarn add approvejs
67 | ```
68 |
69 | Require `approvejs`.
70 |
71 | ```javascript
72 | var approve = require('approvejs');
73 | ```
74 |
75 | ---
76 |
77 | ### Usage
78 |
79 | ApproveJS exposes a single method `value` that takes two parameters.
80 |
81 | The first parameter is the value to validate and the second is the set of rules to test against.
82 |
83 | ```javascript
84 | var rules = {
85 | required: true,
86 | email: true
87 | };
88 |
89 | var result = approve.value('user@domain.com', rules);
90 | ```
91 |
92 | The returned `result` contains two properties:
93 |
94 | ```javascript
95 | {
96 | approved: boolean,
97 | errors: []
98 | }
99 | ```
100 |
101 | #### Accessing Errors
102 |
103 | You can access errors returned by the result in one of two ways:
104 |
105 | ##### Errors Property
106 |
107 | ```javascript
108 | var i = result.errors.length;
109 | while(i--) {
110 | console.log(result.errors[i]);
111 | }
112 | ```
113 |
114 | ##### `.each` Method
115 |
116 | The result object exposes an `each()` method for easily getting to errors.
117 |
118 | ```javascript
119 | result.each(function(error) {
120 | console.log(error);
121 | });
122 | ```
123 |
124 | ---
125 |
126 | Read [documentation here](https://charlgottschalk.github.io/approvejs/docs/).
127 |
128 | If you would like to contribute to the project, please read [contributing](https://charlgottschalk.github.io/approvejs/docs/contributing/).
129 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "approvejs",
3 | "description": "A simple JavaScript validation library that doesn't interfere.",
4 | "main": "dist/approve.js",
5 | "authors": [
6 | "Charl Gottschalk (http://charlg.co.za)"
7 | ],
8 | "license": "MIT",
9 | "keywords": [
10 | "validation",
11 | "validate"
12 | ],
13 | "homepage": "https://github.com/CharlGottschalk/approvejs",
14 | "moduleType": [
15 | "globals",
16 | "amd",
17 | "node"
18 | ],
19 | "ignore": [
20 | "**/.*",
21 | "node_modules",
22 | "bower_components",
23 | "test",
24 | "tests",
25 | "docs",
26 | "src"
27 | ],
28 | "repository": {
29 | "type": "git",
30 | "url": "git://github.com/CharlGottschalk/approvejs.git"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/dist/approve.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 | typeof define === 'function' && define.amd ? define(factory) :
4 | (global.approve = factory());
5 | }(this, (function () { 'use strict';
6 |
7 | /**
8 | * The result object containing the outcome of the credit card test.
9 | */
10 | function Card() {
11 | this.scheme = '';
12 | this.valid = false;
13 | }
14 |
15 | /**
16 | * Checks if a value is a valid credit card.
17 | */
18 | var cc = {
19 | /**
20 | * The default error message.
21 | */
22 | message: '{title} is not a valid credit card number',
23 | schemes: [
24 | {
25 | regex: /^(5610|560221|560222|560223|560224|560225)/,
26 | scheme: 'Australian Bank Card'
27 | },
28 | {
29 | regex: /^(2014|2149)/,
30 | scheme: 'Diner\'s Club'
31 | },
32 | {
33 | regex: /^36/,
34 | scheme: 'Diner\'s Club International'
35 | },
36 | {
37 | regex: /^(30[0-5]|36|38|54|55|2014|2149)/,
38 | scheme: 'Diner\'s Club / Carte Blanche'
39 | },
40 | {
41 | regex: /^35(2[89]|[3-8][0-9])/,
42 | scheme: 'Japanese Credit Bureau'
43 | },
44 | {
45 | regex: /^(5018|5020|5038|6304|6759|676[1-3])/,
46 | scheme: 'Maestro'
47 | },
48 | {
49 | regex: /^5[1-5]/,
50 | scheme: 'Mastercard'
51 | },
52 | {
53 | regex: /^(6304|670[69]|6771)/,
54 | scheme: 'Laser'
55 | },
56 | {
57 | regex: /^(6334|6767)/,
58 | scheme: 'Solo (Paymentech)'
59 | },
60 | {
61 | regex: /^(6011|622|64|65)/,
62 | scheme: 'Discover'
63 | },
64 | {
65 | regex: /^3[47]/,
66 | scheme: 'American Express'
67 | },
68 | {
69 | regex: /^(4026|417500|4508|4844|491(3|7))/,
70 | scheme: 'Visa Electron'
71 | },
72 | {
73 | regex: /^(4)/,
74 | scheme: 'Visa'
75 | }
76 | ],
77 | /**
78 | * Returns the name of the credit card scheme.
79 | * @param {Object} value - The credit card number to test.
80 | * @return {Object} The result of the test.
81 | */
82 | _getScheme: function(value) {
83 | value = (''+ value).replace(/\D/g, '');
84 |
85 | var i = this.schemes.length;
86 | while (i--) {
87 | if (this.schemes[i].regex.test(value)) {
88 | return this.schemes[i].scheme;
89 | }
90 | }
91 |
92 | return undefined;
93 | },
94 | /**
95 | * The method that is called by ApproveJs to perform the test.
96 | * @param {Object} value - The value to test.
97 | * @return {Object} The result object of the test.
98 | */
99 | validate: function(value) {
100 | value = (''+ value).replace(/\D/g, '');
101 |
102 | var card = new Card(),
103 | i = value.length,
104 | sum = 0,
105 | mul = 1,
106 | ca;
107 |
108 | // Not enough numbers. Shortest currently is 12.
109 | if (i < 12) {
110 | return false;
111 | }
112 |
113 | while (i--) {
114 | ca = value.charAt(i) * mul;
115 | sum += ca - (ca > 9) * 9;
116 | mul ^= 3;
117 | }
118 |
119 | card.valid = (sum % 10 === 0) && (sum > 0);
120 | card.scheme = this._getScheme(value);
121 |
122 | return card;
123 | }
124 | };
125 |
126 | /**
127 | * The result object containing the outcome of the strength test.
128 | */
129 | function Score(strength) {
130 | this.strength = strength;
131 | this.points = 0;
132 | this.isMinimum = false;
133 | this.hasLower = false;
134 | this.hasUpper = false;
135 | this.hasNumber = false;
136 | this.hasSpecial = false;
137 | this.isBonus = false;
138 | this.percent = 0;
139 | this.valid = false;
140 | this.errors = [];
141 | }
142 | /**
143 | * Checks if a value is a strong password string.
144 | */
145 | var strength = {
146 | /**
147 | * The minimum length a password must be.
148 | */
149 | minimum: 8,
150 | /**
151 | * The minimum length a password must be for a bonus point.
152 | */
153 | minimumBonus: 10,
154 | /**
155 | * The text representing the strength of a password.
156 | */
157 | strengths: {
158 | 0: 'Very Weak',
159 | 1: 'Weak',
160 | 2: 'Better',
161 | 3: 'Almost',
162 | 4: 'Acceptable',
163 | 5: 'Strong',
164 | 6: 'Very Strong'
165 | },
166 | /**
167 | * The default error message.
168 | */
169 | message: '{title} did not pass the strength test.',
170 | /**
171 | * Expects the 'min' and 'bonus' parameters.
172 | */
173 | expects: ['min', 'bonus'],
174 | /**
175 | * Default error messages
176 | */
177 | errors: {
178 | isMinimum: '{title} must be at least {min} characters',
179 | hasLower: '{title} must have at least 1 lower case character',
180 | hasUpper: '{title} must have at least 1 upper case character',
181 | hasNumber: '{title} must have at least 1 number',
182 | hasSpecial: '{title} must have at least 1 special character'
183 | },
184 | /**
185 | * Returns an object containing the score of a value.
186 | */
187 | _getScore: function(text) {
188 | // Create the object that represents the score of the text
189 | var result = new Score(this.strengths[0]);
190 | // If text is longer than minimum give 1 point.
191 | // If text is longer than minimumBonus give another 1 point.
192 | if (text.length > this.minimumBonus) {
193 | result.points += 2;
194 | result.isBonus = true;
195 | result.isMinimum = true;
196 | } else if (text.length > this.minimum){
197 | result.points++;
198 | result.isMinimum = true;
199 | } else {
200 | result.points = 1;
201 | result.isMinimum = false;
202 | }
203 | // If text has lowercase characters give 1 point.
204 | result.hasLower = text.match(/[a-z]/) === null ? false : true;
205 | if(result.isMinimum && result.hasLower) {
206 | result.points++;
207 | }
208 | // If text has uppercase characters give 1 point.
209 | result.hasUpper = text.match(/[A-Z]/) === null ? false : true;
210 | if(result.isMinimum && result.hasUpper) {
211 | result.points++;
212 | }
213 | // If text has at least one number give 1 point.
214 | result.hasNumber = text.match(/\d+/) === null ? false : true;
215 | if(result.isMinimum && result.hasNumber) {
216 | result.points++;
217 | }
218 | // If text has at least one special caracther give 1 point.
219 | result.hasSpecial = text.match(/.[!,@,#,$,%,^,&,*,?,_,~,-,(,)]/) === null ? false : true;
220 | if(result.isMinimum && result.hasSpecial) {
221 | result.points++;
222 | }
223 | // Set the percentage value.
224 | result.percent = Math.ceil((result.points / 6) * 100);
225 | // Return the score object.
226 | return result;
227 | },
228 | /**
229 | * Returns an object containing the score and validation of a value.
230 | */
231 | _getStrength: function (text) {
232 | var result = this._getScore(text);
233 | result.strength = this.strengths[result.points];
234 | if (!result.isMinimum) {
235 | result.errors.push(this.errors.isMinimum);
236 | }
237 | if (!result.hasLower) {
238 | result.errors.push(this.errors.hasLower);
239 | }
240 | if (!result.hasUpper) {
241 | result.errors.push(this.errors.hasUpper);
242 | }
243 | if (!result.hasSpecial) {
244 | result.errors.push(this.errors.hasSpecial);
245 | }
246 | if (!result.hasNumber) {
247 | result.errors.push(this.errors.hasNumber);
248 | }
249 | if (result.points > 4) {
250 | result.valid = true;
251 | }
252 | return result;
253 | },
254 | /**
255 | * The method that is called by ApproveJs to perform the test.
256 | */
257 | validate: function(value, pars) {
258 | this.minimum = pars.min || this.minimum;
259 | this.minimumBonus = pars.bonus || this.minimumBonus;
260 | if (pars.hasOwnProperty('config') && pars.config.hasOwnProperty('messages')) {
261 | for (var message in pars.config.messages) {
262 | if (pars.config.messages.hasOwnProperty(message)) {
263 | this.errors[message] = pars.config.messages[message];
264 | }
265 | }
266 | }
267 | return this._getStrength(value);
268 | }
269 | };
270 |
271 | var tests = {
272 | /**
273 | * Checks if a value is present.
274 | */
275 | required: {
276 | validate: function(value) {
277 | return !!value;
278 | },
279 | message: '{title} is required',
280 | expects: false
281 | },
282 | /**
283 | * Checks if a value is a valid email address.
284 | */
285 | email: {
286 | regex: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i, // eslint-disable-line no-control-regex
287 | validate: function(value) {
288 | return this.regex.test(value);
289 | },
290 | message: '{title} must be a valid email address',
291 | expects: false
292 | },
293 | /**
294 | * Checks if a value is a valid web address.
295 | */
296 | url: {
297 | regex: /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/i,
298 | validate: function(value) {
299 | return this.regex.test(value);
300 | },
301 | message: '{title} must be a valid web address',
302 | expects: false
303 | },
304 | /**
305 | * Checks if a value contains both letters and numbers.
306 | */
307 | alphaNumeric: {
308 | regex: /^[A-Za-z0-9]+$/i,
309 | validate: function(value) {
310 | return this.regex.test(value);
311 | },
312 | message: '{title} may only contain [A-Za-z] and [0-9]',
313 | expects: false
314 | },
315 | /**
316 | * Checks if a value contains only numbers.
317 | */
318 | numeric: {
319 | regex: /^-?[0-9]+$/,
320 | validate: function(value) {
321 | return this.regex.test(value);
322 | },
323 | message: '{title} may only contain [0-9]',
324 | expects: false
325 | },
326 | /**
327 | * Checks if a value contains only letters.
328 | */
329 | alpha: {
330 | regex: /^[A-Za-z]+$/,
331 | validate: function(value) {
332 | return this.regex.test(value);
333 | },
334 | message: '{title} may only contain [A-Za-z]',
335 | expects: false
336 | },
337 | /**
338 | * Checks if a value is a valid decimal.
339 | */
340 | decimal: {
341 | regex: /^\s*(\+|-)?((\d+(\.\d+)?)|(\.\d+))\s*$/,
342 | validate: function(value) {
343 | return this.regex.test(value);
344 | },
345 | message: '{title} must be a valid decimal',
346 | expects: false
347 | },
348 | /**
349 | * Similar to 'decimal', but for currency values.
350 | */
351 | currency: {
352 | regex: /^\s*(\+|-)?((\d+(\.\d\d)?)|(\.\d\d))\s*$/,
353 | validate: function(value) {
354 | return this.regex.test(value);
355 | },
356 | message: '{title} must be a valid currency value',
357 | expects: false
358 | },
359 | /**
360 | * Checks if a value is a valid ipv4 or ipv6 address.
361 | */
362 | ip: {
363 | regex: {
364 | ipv4: /^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/,
365 | ipv4Cidr: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/,
366 | ipv6: /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i,
367 | ipv6Cidr: /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/
368 | },
369 | validate: function(value) {
370 | return this.regex.ipv4.test(value) || this.regex.ipv6.test(value) || this.regex.ipv4Cidr.test(value) || this.regex.ipv6Cidr.test(value);
371 | },
372 | message: '{title} must be a valid IP address',
373 | expects: false
374 | },
375 | /**
376 | * Checks if a value is a minimum of n characters.
377 | */
378 | min: {
379 | validate: function(value, pars) {
380 | return typeof value === 'string' && value.length >= pars.min;
381 | },
382 | message: '{title} must be a minimum of {min} characters',
383 | expects: ['min']
384 | },
385 | /**
386 | * Checks if a value is a maximum of n characters.
387 | */
388 | max: {
389 | validate: function(value, pars) {
390 | return typeof value === 'string' && value.length <= pars.max;
391 | },
392 | message: '{title} must be a maximum of {max} characters',
393 | expects: ['max']
394 | },
395 | /**
396 | * Checks if a string's length or number is between a minimum and maximum.
397 | */
398 | range: {
399 | validate: function(value, pars) {
400 | if (typeof value === 'string')
401 | {
402 | return value.length >= pars.min && value.length <= pars.max;
403 | } else if (typeof value === 'number') {
404 | return value >= pars.min && value <= pars.max;
405 | }
406 | return false;
407 | },
408 | message: '{title} must be a minimum of {min} and a maximum of {max} characters',
409 | expects: ['min', 'max']
410 | },
411 | /**
412 | * Checks if a value is the same as the value of another.
413 | */
414 | equal: {
415 | validate: function(value, pars) {
416 | return '' + value === '' + pars.value;
417 | },
418 | message: '{title} must be equal to {field}',
419 | expects: ['value', 'field']
420 | },
421 | /**
422 | * Checks if a value passes a given regular expression.
423 | */
424 | format: {
425 | validate: function(value, pars) {
426 | if (Object.prototype.toString.call(pars.regex) === '[object RegExp]') {
427 | return pars.regex.test(value);
428 | }
429 | throw 'approve.value(): [format] - regex is not a valid regular expression.';
430 | },
431 | message: '{title} did not pass the [{regex}] test',
432 | expects: ['regex']
433 | },
434 | /**
435 | * Checks if a value is a valid time string (hh:mm:ss).
436 | */
437 | time: {
438 | regex: /^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/,
439 | validate: function(value) {
440 | return this.regex.test(value);
441 | },
442 | message: '{title} is not a valid time',
443 | expects: false
444 | },
445 | /**
446 | * Checks if a value is a valid date.
447 | */
448 | date: {
449 | formats: {
450 | ymd: /^(?:\2)(?:[0-9]{2})?[0-9]{2}([\/-])(1[0-2]|0?[1-9])([\/-])(3[01]|[12][0-9]|0?[1-9])$/,
451 | dmy: /^(3[01]|[12][0-9]|0?[1-9])([\/-])(1[0-2]|0?[1-9])([\/-])(?:[0-9]{2})?[0-9]{2}$/
452 | },
453 | validate: function(value, pars) {
454 | return this.formats[pars.format].test(value);
455 | },
456 | message: '{title} is not a valid date',
457 | expects: ['format']
458 | },
459 | /**
460 | * Checks if a value is truthy ('yes', 'true', 'ok[ay]', '1').
461 | */
462 | truthy: {
463 | regex: /^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/i,
464 | validate: function(value) {
465 | return this.regex.test(value);
466 | },
467 | message: '{title} is not valid',
468 | expects: false
469 | },
470 | /**
471 | * Checks if a value is falsy ('No', 'false', '0').
472 | */
473 | falsy: {
474 | regex: /^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/i,
475 | validate: function(value) {
476 | return !this.regex.test(value);
477 | },
478 | message: '{title} is not valid',
479 | expects: false
480 | },
481 | cc: cc,
482 | strength: strength
483 | };
484 |
485 | var Result = function() {
486 | this.approved = true;
487 | this.errors = [];
488 | this.failed = [];
489 | // Provides easy access to the loop for the errors.
490 | this.each = function(callback) {
491 | var isFunc = callback && callback.constructor && callback.call && callback.apply,
492 | i = this.errors.length;
493 | while (i--) {
494 | if (isFunc) {
495 | callback(this.errors[i]);
496 | }
497 | }
498 | };
499 | // Provides easy access to the loop for a test's errors.
500 | this.filter = function(test, callback) {
501 | var isFunc = callback && callback.constructor && callback.call && callback.apply,
502 | i = 0;
503 | if (this.hasOwnProperty(test)) {
504 | i = this[test].errors.length;
505 | while (i--) {
506 | if (isFunc) {
507 | callback(this[test].errors[i]);
508 | }
509 | }
510 | }
511 | };
512 | };
513 |
514 | /** @constructor */
515 | var approve = {
516 | /**
517 | * Default tests.
518 | */
519 | tests: tests,
520 | /**
521 | * A helper function for formatting strings.
522 | */
523 | _format: function(text, col) {
524 | col = typeof col === 'object' ? col : Array.prototype.slice.call(arguments, 1);
525 | return text.replace(/\{\{|\}\}|\{(\w+)\}/g, function (m, n) {
526 | if (m === "{{") { return "{"; }
527 | if (m === "}}") { return "}"; }
528 | return col[n];
529 | }).trim();
530 | },
531 | /**
532 | * Checks whether the given rule is not a config property.
533 | */
534 | _isRule: function(rule) {
535 | var props = [
536 | 'title',
537 | 'stop',
538 | 'ignoreNull'
539 | ];
540 | return props.indexOf(rule) < 0;
541 | },
542 | /**
543 | * The start of the validation process.
544 | */
545 | _start: function(value, rules) {
546 | // Instantiate a result object.
547 | var result = new Result(),
548 | // This is used to format the message with the value title.
549 | title = '',
550 | // When true, tests will not continue after first failed test.
551 | stop = false,
552 | // When true, tests will not be executed if the value is null.
553 | ignoreNull = false;
554 | // Check if the rule has a title property?
555 | if (rules.hasOwnProperty('title')) {
556 | title = rules.title;
557 | }
558 | // Check if the rule has a stop property?
559 | if (rules.hasOwnProperty('stop')) {
560 | stop = rules.stop;
561 | }
562 | // Check if the rule has an ignoreNull property?
563 | if (rules.hasOwnProperty('ignoreNull')) {
564 | ignoreNull = rules.ignoreNull;
565 | }
566 | // Loop through given rules.
567 | for (var rule in rules) {
568 | // Stop validating after each failed test
569 | if (stop && !result.approved) {
570 | break;
571 | }
572 | if (rules.hasOwnProperty(rule) && this._isRule(rule)) {
573 | var constraint = rules[rule];
574 | // Check if rule exists in tests.
575 | if (this.tests.hasOwnProperty(rule)) {
576 | // Set a pointer to the current test.
577 | var params = {
578 | constraint: constraint,
579 | rule: rule,
580 | title: title,
581 | test: this.tests[rule],
582 | value: value,
583 | ignoreNull: ignoreNull
584 | };
585 | this._test(params, result);
586 | } else {
587 | throw 'approve.value(): ' + rule + ' test not defined.';
588 | }
589 | }
590 | }
591 |
592 | return result;
593 | },
594 | /**
595 | * Performs the actual testing of the value and returns the result including any errors.
596 | */
597 | _test: function(params, result) {
598 | // Check if nulls should be ignored
599 | if (params.hasOwnProperty('ignoreNull')) {
600 | if (!params.value && params.ignoreNull) {
601 | return;
602 | }
603 | }
604 | // Create an args object for required parameters.
605 | var args = this._getArgs(params),
606 | // Test the value.
607 | ret = params.test.validate(params.value, args);
608 |
609 | result[params.rule] = {
610 | approved: true,
611 | errors: []
612 | };
613 | // Check if the returned value is an object.
614 | if(typeof ret === 'object')
615 | {
616 | // An object was returned.
617 | // Check if the test was successful.
618 | result.approved = !ret.valid ? false : result.approved;
619 | result[params.rule].approved = ret.valid;
620 | // Add the error messages returned by the resluting object.
621 | if (ret.hasOwnProperty('errors')) {
622 | var messages = this._formatMessages(ret.errors, params);
623 | result.errors = result.errors.concat(messages);
624 | result[params.rule].errors = messages;
625 | }
626 | // Merge any properties from the resulting object with the main result to be returned.
627 | for (var prop in ret) {
628 | if (ret.hasOwnProperty(prop) && !result.hasOwnProperty(prop)) {
629 | result[params.rule][prop] = ret[prop];
630 | }
631 | }
632 | } else if (typeof ret !== 'boolean') {
633 | // We don't process if it's not a boolean or object.
634 | throw 'approve.value(): ' + params.rule + ' returned an invalid value';
635 | } else {
636 | result.approved = !ret ? false : result.approved;
637 | result[params.rule].approved = ret;
638 | }
639 | if (!result.approved) {
640 | var message = this._formatMessage(params);
641 | result.errors.push(message);
642 | result[params.rule].errors.push(message);
643 | }
644 | if (!ret.valid) {
645 | result.failed.push(params.rule);
646 | }
647 | },
648 | /**
649 | * Helper method to loop over expected test parameters.
650 | */
651 | _eachExpected: function(params, fn) {
652 | if (Array.isArray(params.test.expects)) {
653 | var expectsLength = params.test.expects.length,
654 | i = expectsLength;
655 | // This test expects paramaters.
656 | // Loop through the test's expected parameters and call the given function.
657 | while (i--) {
658 | fn(params.test.expects[i], expectsLength);
659 | }
660 | }
661 | },
662 | /**
663 | * Returns an object containing the arguments for a test's expected parameters.
664 | */
665 | _getArgs: function(params) {
666 | var pars = {};
667 | // Does the test for this rule expect any paramaters?
668 | this._eachExpected(params, function(expects, expectsLength) {
669 | // Check if the rule object has the required parameter.
670 | if (params.constraint.hasOwnProperty(expects)) {
671 | // Add the expected parameter value to the pars object.
672 | pars[expects] = params.constraint[expects];
673 | } else if (expectsLength <= 1 && (/^[A-Za-z0-9]+$/i.test(params.constraint) || toString.call(params.constraint) === '[object RegExp]')) {
674 | // Set the parameter to the rule's value.
675 | pars[expects] = params.constraint;
676 | } else {
677 | throw 'approve.value(): ' + params.rule + ' expects the ' + expects + ' parameter.';
678 | }
679 | });
680 |
681 | // Does the rule have config?
682 | if (params.constraint.hasOwnProperty('config')) {
683 | // Add the config to the pars object.
684 | pars.config = params.constraint.config;
685 | }
686 | // Return the parameters object
687 | return pars;
688 | },
689 | /**
690 | * Returns an object containing placholder values to correctly format an error message.
691 | */
692 | _getFormat: function(params) {
693 | var format = {};
694 | // Does the test for the rule expect parameters?
695 | this._eachExpected(params, function(expects) {
696 | // Check if the rule object has the required parameter.
697 | if (params.constraint.hasOwnProperty(expects)) {
698 | // Add the expected parameter's format to the parameter value.
699 | format[expects] = params.constraint[expects];
700 | }
701 | // Expected parameter not present, is the constraint formattable?
702 | if (/^[A-Za-z0-9]+$/i.test(params.constraint)) {
703 | format[expects] = params.constraint;
704 | }
705 | });
706 | format.title = params.title;
707 | // Return the formatted message.
708 | return format;
709 | },
710 | /**
711 | * Returns an array of formatted error messages returned by tests that return objects instead of booleans.
712 | */
713 | _formatMessages: function(errors, params) {
714 | var format = this._getFormat(params),
715 | i = errors.length;
716 | while (i--) {
717 | errors[i] = this._format(errors[i], format);
718 | }
719 | return errors;
720 | },
721 | /**
722 | * Returns the correctly formatted message representing the current test's failure.
723 | */
724 | _formatMessage: function(params) {
725 | var format = this._getFormat(params);
726 | var message;
727 |
728 | // Does the provided rule have a custom message?
729 | if (params.constraint.hasOwnProperty('message')) {
730 | // The rule has a custom message, return it.
731 | message = params.constraint.message;
732 | return this._format(message, format);
733 | }
734 | else {
735 | // The rule does not have a custom message.
736 | // Get the default message from the tests.
737 | message = params.test.message;
738 | return this._format(message, format);
739 | }
740 | },
741 | /**
742 | * Executes the tests based on given rules to validate a given value.
743 | */
744 | value: function(value, rules) {
745 |
746 | // If rules is not an object, we cannot continue.
747 | if (typeof rules !== 'object') {
748 | throw 'approve.value(value, rules): rules is not a valid object.';
749 | }
750 | // Return the result object.
751 | return this._start(value, rules);
752 | },
753 | /**
754 | * Used to add custom tests.
755 | */
756 | addTest: function(obj, name) {
757 | // If obj is not a valid object, we cannot continue.
758 | if (typeof obj !== 'object') {
759 | throw 'approve.addTest(obj, name): obj is not a valid object.';
760 | }
761 | try {
762 | // Check if the test name already exists.
763 | if (!this.tests.hasOwnProperty(name)) {
764 | // The name does not exist, add it to the tests.
765 | this.tests[name] = obj;
766 | }
767 | } catch (e) {
768 | throw 'approve.addTest(): ' + e.message;
769 | }
770 | }
771 | };
772 |
773 | return approve;
774 |
775 | })));
776 |
--------------------------------------------------------------------------------
/dist/approve.min.gzip.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CharlGottschalk/approvejs/7eae1506de900f56660e35c878502c0b7b0c91ae/dist/approve.min.gzip.js
--------------------------------------------------------------------------------
/dist/approve.min.gzip.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"names":[],"mappings":"","sources":["approve.min.js"],"sourcesContent":["!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define(t):e.approve=t()}(this,function(){\"use strict\";function e(){this.scheme=\"\",this.valid=!1}function t(e){this.strength=e,this.points=0,this.isMinimum=!1,this.hasLower=!1,this.hasUpper=!1,this.hasNumber=!1,this.hasSpecial=!1,this.isBonus=!1,this.percent=0,this.valid=!1,this.errors=[]}var r={message:\"{title} is not a valid credit card number\",schemes:[{regex:/^(5610|560221|560222|560223|560224|560225)/,scheme:\"Australian Bank Card\"},{regex:/^(2014|2149)/,scheme:\"Diner's Club\"},{regex:/^36/,scheme:\"Diner's Club International\"},{regex:/^(30[0-5]|36|38|54|55|2014|2149)/,scheme:\"Diner's Club / Carte Blanche\"},{regex:/^35(2[89]|[3-8][0-9])/,scheme:\"Japanese Credit Bureau\"},{regex:/^(5018|5020|5038|6304|6759|676[1-3])/,scheme:\"Maestro\"},{regex:/^5[1-5]/,scheme:\"Mastercard\"},{regex:/^(6304|670[69]|6771)/,scheme:\"Laser\"},{regex:/^(6334|6767)/,scheme:\"Solo (Paymentech)\"},{regex:/^(6011|622|64|65)/,scheme:\"Discover\"},{regex:/^3[47]/,scheme:\"American Express\"},{regex:/^(4026|417500|4508|4844|491(3|7))/,scheme:\"Visa Electron\"},{regex:/^(4)/,scheme:\"Visa\"}],_getScheme:function(e){e=(\"\"+e).replace(/\\D/g,\"\");for(var t=this.schemes.length;t--;)if(this.schemes[t].regex.test(e))return this.schemes[t].scheme},validate:function(t){t=(\"\"+t).replace(/\\D/g,\"\");var r,s=new e,a=t.length,i=0,n=1;if(a<12)return!1;for(;a--;)r=t.charAt(a)*n,i+=r-9*(r>9),n^=3;return s.valid=i%10===0&&i>0,s.scheme=this._getScheme(t),s}},s={minimum:8,minimumBonus:10,strengths:{0:\"Very Weak\",1:\"Weak\",2:\"Better\",3:\"Almost\",4:\"Acceptable\",5:\"Strong\",6:\"Very Strong\"},message:\"{title} did not pass the strength test.\",expects:[\"min\",\"bonus\"],errors:{isMinimum:\"{title} must be at least {min} characters\",hasLower:\"{title} must have at least 1 lower case character\",hasUpper:\"{title} must have at least 1 upper case character\",hasNumber:\"{title} must have at least 1 number\",hasSpecial:\"{title} must have at least 1 special character\"},_getScore:function(e){var r=new t(this.strengths[0]);return e.length>this.minimumBonus?(r.points+=2,r.isBonus=!0,r.isMinimum=!0):e.length>this.minimum?(r.points++,r.isMinimum=!0):(r.points=1,r.isMinimum=!1),r.hasLower=null!==e.match(/[a-z]/),r.isMinimum&&r.hasLower&&r.points++,r.hasUpper=null!==e.match(/[A-Z]/),r.isMinimum&&r.hasUpper&&r.points++,r.hasNumber=null!==e.match(/\\d+/),r.isMinimum&&r.hasNumber&&r.points++,r.hasSpecial=null!==e.match(/.[!,@,#,$,%,^,&,*,?,_,~,-,(,)]/),r.isMinimum&&r.hasSpecial&&r.points++,r.percent=Math.ceil(r.points/6*100),r},_getStrength:function(e){var t=this._getScore(e);return t.strength=this.strengths[t.points],t.isMinimum||t.errors.push(this.errors.isMinimum),t.hasLower||t.errors.push(this.errors.hasLower),t.hasUpper||t.errors.push(this.errors.hasUpper),t.hasSpecial||t.errors.push(this.errors.hasSpecial),t.hasNumber||t.errors.push(this.errors.hasNumber),t.points>4&&(t.valid=!0),t},validate:function(e,t){if(this.minimum=t.min||this.minimum,this.minimumBonus=t.bonus||this.minimumBonus,t.hasOwnProperty(\"config\")&&t.config.hasOwnProperty(\"messages\"))for(var r in t.config.messages)t.config.messages.hasOwnProperty(r)&&(this.errors[r]=t.config.messages[r]);return this._getStrength(e)}},a={required:{validate:function(e){return!!e},message:\"{title} is required\",expects:!1},email:{regex:/^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$/i,validate:function(e){return this.regex.test(e)},message:\"{title} must be a valid email address\",expects:!1},url:{regex:/^(?:(?:https?|ftp):\\/\\/)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:\\/\\S*)?$/i,validate:function(e){return this.regex.test(e)},message:\"{title} must be a valid web address\",expects:!1},alphaNumeric:{regex:/^[A-Za-z0-9]+$/i,validate:function(e){return this.regex.test(e)},message:\"{title} may only contain [A-Za-z] and [0-9]\",expects:!1},numeric:{regex:/^-?[0-9]+$/,validate:function(e){return this.regex.test(e)},message:\"{title} may only contain [0-9]\",expects:!1},alpha:{regex:/^[A-Za-z]+$/,validate:function(e){return this.regex.test(e)},message:\"{title} may only contain [A-Za-z]\",expects:!1},decimal:{regex:/^\\s*(\\+|-)?((\\d+(\\.\\d+)?)|(\\.\\d+))\\s*$/,validate:function(e){return this.regex.test(e)},message:\"{title} must be a valid decimal\",expects:!1},currency:{regex:/^\\s*(\\+|-)?((\\d+(\\.\\d\\d)?)|(\\.\\d\\d))\\s*$/,validate:function(e){return this.regex.test(e)},message:\"{title} must be a valid currency value\",expects:!1},ip:{regex:{ipv4:/^(?:(?:\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])$/,ipv4Cidr:/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/([0-9]|[1-2][0-9]|3[0-2]))$/,ipv6:/^((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})((([\\dA-F]{1,4}((?!\\3)::|:\\b|$))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})$/i,ipv6Cidr:/^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/},validate:function(e){return this.regex.ipv4.test(e)||this.regex.ipv6.test(e)||this.regex.ipv4Cidr.test(e)||this.regex.ipv6Cidr.test(e)},message:\"{title} must be a valid IP address\",expects:!1},min:{validate:function(e,t){return\"string\"==typeof e&&e.length>=t.min},message:\"{title} must be a minimum of {min} characters\",expects:[\"min\"]},max:{validate:function(e,t){return\"string\"==typeof e&&e.length<=t.max},message:\"{title} must be a maximum of {max} characters\",expects:[\"max\"]},range:{validate:function(e,t){return\"string\"==typeof e?e.length>=t.min&&e.length<=t.max:\"number\"==typeof e&&(e>=t.min&&e<=t.max)},message:\"{title} must be a minimum of {min} and a maximum of {max} characters\",expects:[\"min\",\"max\"]},equal:{validate:function(e,t){return\"\"+e==\"\"+t.value},message:\"{title} must be equal to {field}\",expects:[\"value\",\"field\"]},format:{validate:function(e,t){if(\"[object RegExp]\"===Object.prototype.toString.call(t.regex))return t.regex.test(e);throw\"approve.value(): [format] - regex is not a valid regular expression.\"},message:\"{title} did not pass the [{regex}] test\",expects:[\"regex\"]},time:{regex:/^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/,validate:function(e){return this.regex.test(e)},message:\"{title} is not a valid time\",expects:!1},date:{formats:{ymd:/^(?:\\2)(?:[0-9]{2})?[0-9]{2}([\\/-])(1[0-2]|0?[1-9])([\\/-])(3[01]|[12][0-9]|0?[1-9])$/,dmy:/^(3[01]|[12][0-9]|0?[1-9])([\\/-])(1[0-2]|0?[1-9])([\\/-])(?:[0-9]{2})?[0-9]{2}$/},validate:function(e,t){return this.formats[t.format].test(e)},message:\"{title} is not a valid date\",expects:[\"format\"]},truthy:{regex:/^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/i,validate:function(e){return this.regex.test(e)},message:\"{title} is not valid\",expects:!1},falsy:{regex:/^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/i,validate:function(e){return!this.regex.test(e)},message:\"{title} is not valid\",expects:!1},cc:r,strength:s},i=function(){this.approved=!0,this.errors=[],this.failed=[],this.each=function(e){for(var t=e&&e.constructor&&e.call&&e.apply,r=this.errors.length;r--;)t&&e(this.errors[r])},this.filter=function(e,t){var r=t&&t.constructor&&t.call&&t.apply,s=0;if(this.hasOwnProperty(e))for(s=this[e].errors.length;s--;)r&&t(this[e].errors[s])}},n={tests:a,_format:function(e,t){return t=\"object\"==typeof t?t:Array.prototype.slice.call(arguments,1),e.replace(/\\{\\{|\\}\\}|\\{(\\w+)\\}/g,function(e,r){return\"{{\"===e?\"{\":\"}}\"===e?\"}\":t[r]}).trim()},_isRule:function(e){var t=[\"title\",\"stop\",\"ignoreNull\"];return t.indexOf(e)<0},_start:function(e,t){var r=new i,s=\"\",a=!1,n=!1;t.hasOwnProperty(\"title\")&&(s=t.title),t.hasOwnProperty(\"stop\")&&(a=t.stop),t.hasOwnProperty(\"ignoreNull\")&&(n=t.ignoreNull);for(var u in t){if(a&&!r.approved)break;if(t.hasOwnProperty(u)&&this._isRule(u)){var o=t[u];if(!this.tests.hasOwnProperty(u))throw\"approve.value(): \"+u+\" test not defined.\";var d={constraint:o,rule:u,title:s,test:this.tests[u],value:e,ignoreNull:n};this._test(d,r)}}return r},_test:function(e,t){if(!e.hasOwnProperty(\"ignoreNull\")||e.value||!e.ignoreNull){var r=this._getArgs(e),s=e.test.validate(e.value,r);if(t[e.rule]={approved:!0,errors:[]},\"object\"==typeof s){if(t.approved=!!s.valid&&t.approved,t[e.rule].approved=s.valid,s.hasOwnProperty(\"errors\")){var a=this._formatMessages(s.errors,e);t.errors=t.errors.concat(a),t[e.rule].errors=a}for(var i in s)s.hasOwnProperty(i)&&!t.hasOwnProperty(i)&&(t[e.rule][i]=s[i])}else{if(\"boolean\"!=typeof s)throw\"approve.value(): \"+e.rule+\" returned an invalid value\";t.approved=!!s&&t.approved,t[e.rule].approved=s}if(!t.approved){var n=this._formatMessage(e);t.errors.push(n),t[e.rule].errors.push(n)}s.valid||t.failed.push(e.rule)}},_eachExpected:function(e,t){if(Array.isArray(e.test.expects))for(var r=e.test.expects.length,s=r;s--;)t(e.test.expects[s],r)},_getArgs:function(e){var t={};return this._eachExpected(e,function(r,s){if(e.constraint.hasOwnProperty(r))t[r]=e.constraint[r];else{if(!(s<=1)||!/^[A-Za-z0-9]+$/i.test(e.constraint)&&\"[object RegExp]\"!==toString.call(e.constraint))throw\"approve.value(): \"+e.rule+\" expects the \"+r+\" parameter.\";t[r]=e.constraint}}),e.constraint.hasOwnProperty(\"config\")&&(t.config=e.constraint.config),t},_getFormat:function(e){var t={};return this._eachExpected(e,function(r){e.constraint.hasOwnProperty(r)&&(t[r]=e.constraint[r]),/^[A-Za-z0-9]+$/i.test(e.constraint)&&(t[r]=e.constraint)}),t.title=e.title,t},_formatMessages:function(e,t){for(var r=this._getFormat(t),s=e.length;s--;)e[s]=this._format(e[s],r);return e},_formatMessage:function(e){var t,r=this._getFormat(e);return e.constraint.hasOwnProperty(\"message\")?(t=e.constraint.message,this._format(t,r)):(t=e.test.message,this._format(t,r))},value:function(e,t){if(\"object\"!=typeof t)throw\"approve.value(value, rules): rules is not a valid object.\";return this._start(e,t)},addTest:function(e,t){if(\"object\"!=typeof e)throw\"approve.addTest(obj, name): obj is not a valid object.\";try{this.tests.hasOwnProperty(t)||(this.tests[t]=e)}catch(r){throw\"approve.addTest(): \"+r.message}}};return n});\n//# sourceMappingURL=approve.min.js.map\n"],"file":"approve.min.gzip.js"}
--------------------------------------------------------------------------------
/dist/approve.min.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.approve=t()}(this,function(){"use strict";function e(){this.scheme="",this.valid=!1}function t(e){this.strength=e,this.points=0,this.isMinimum=!1,this.hasLower=!1,this.hasUpper=!1,this.hasNumber=!1,this.hasSpecial=!1,this.isBonus=!1,this.percent=0,this.valid=!1,this.errors=[]}var r={message:"{title} is not a valid credit card number",schemes:[{regex:/^(5610|560221|560222|560223|560224|560225)/,scheme:"Australian Bank Card"},{regex:/^(2014|2149)/,scheme:"Diner's Club"},{regex:/^36/,scheme:"Diner's Club International"},{regex:/^(30[0-5]|36|38|54|55|2014|2149)/,scheme:"Diner's Club / Carte Blanche"},{regex:/^35(2[89]|[3-8][0-9])/,scheme:"Japanese Credit Bureau"},{regex:/^(5018|5020|5038|6304|6759|676[1-3])/,scheme:"Maestro"},{regex:/^5[1-5]/,scheme:"Mastercard"},{regex:/^(6304|670[69]|6771)/,scheme:"Laser"},{regex:/^(6334|6767)/,scheme:"Solo (Paymentech)"},{regex:/^(6011|622|64|65)/,scheme:"Discover"},{regex:/^3[47]/,scheme:"American Express"},{regex:/^(4026|417500|4508|4844|491(3|7))/,scheme:"Visa Electron"},{regex:/^(4)/,scheme:"Visa"}],_getScheme:function(e){e=(""+e).replace(/\D/g,"");for(var t=this.schemes.length;t--;)if(this.schemes[t].regex.test(e))return this.schemes[t].scheme},validate:function(t){t=(""+t).replace(/\D/g,"");var r,s=new e,a=t.length,i=0,n=1;if(a<12)return!1;for(;a--;)r=t.charAt(a)*n,i+=r-9*(r>9),n^=3;return s.valid=i%10===0&&i>0,s.scheme=this._getScheme(t),s}},s={minimum:8,minimumBonus:10,strengths:{0:"Very Weak",1:"Weak",2:"Better",3:"Almost",4:"Acceptable",5:"Strong",6:"Very Strong"},message:"{title} did not pass the strength test.",expects:["min","bonus"],errors:{isMinimum:"{title} must be at least {min} characters",hasLower:"{title} must have at least 1 lower case character",hasUpper:"{title} must have at least 1 upper case character",hasNumber:"{title} must have at least 1 number",hasSpecial:"{title} must have at least 1 special character"},_getScore:function(e){var r=new t(this.strengths[0]);return e.length>this.minimumBonus?(r.points+=2,r.isBonus=!0,r.isMinimum=!0):e.length>this.minimum?(r.points++,r.isMinimum=!0):(r.points=1,r.isMinimum=!1),r.hasLower=null!==e.match(/[a-z]/),r.isMinimum&&r.hasLower&&r.points++,r.hasUpper=null!==e.match(/[A-Z]/),r.isMinimum&&r.hasUpper&&r.points++,r.hasNumber=null!==e.match(/\d+/),r.isMinimum&&r.hasNumber&&r.points++,r.hasSpecial=null!==e.match(/.[!,@,#,$,%,^,&,*,?,_,~,-,(,)]/),r.isMinimum&&r.hasSpecial&&r.points++,r.percent=Math.ceil(r.points/6*100),r},_getStrength:function(e){var t=this._getScore(e);return t.strength=this.strengths[t.points],t.isMinimum||t.errors.push(this.errors.isMinimum),t.hasLower||t.errors.push(this.errors.hasLower),t.hasUpper||t.errors.push(this.errors.hasUpper),t.hasSpecial||t.errors.push(this.errors.hasSpecial),t.hasNumber||t.errors.push(this.errors.hasNumber),t.points>4&&(t.valid=!0),t},validate:function(e,t){if(this.minimum=t.min||this.minimum,this.minimumBonus=t.bonus||this.minimumBonus,t.hasOwnProperty("config")&&t.config.hasOwnProperty("messages"))for(var r in t.config.messages)t.config.messages.hasOwnProperty(r)&&(this.errors[r]=t.config.messages[r]);return this._getStrength(e)}},a={required:{validate:function(e){return!!e},message:"{title} is required",expects:!1},email:{regex:/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,validate:function(e){return this.regex.test(e)},message:"{title} must be a valid email address",expects:!1},url:{regex:/^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/i,validate:function(e){return this.regex.test(e)},message:"{title} must be a valid web address",expects:!1},alphaNumeric:{regex:/^[A-Za-z0-9]+$/i,validate:function(e){return this.regex.test(e)},message:"{title} may only contain [A-Za-z] and [0-9]",expects:!1},numeric:{regex:/^-?[0-9]+$/,validate:function(e){return this.regex.test(e)},message:"{title} may only contain [0-9]",expects:!1},alpha:{regex:/^[A-Za-z]+$/,validate:function(e){return this.regex.test(e)},message:"{title} may only contain [A-Za-z]",expects:!1},decimal:{regex:/^\s*(\+|-)?((\d+(\.\d+)?)|(\.\d+))\s*$/,validate:function(e){return this.regex.test(e)},message:"{title} must be a valid decimal",expects:!1},currency:{regex:/^\s*(\+|-)?((\d+(\.\d\d)?)|(\.\d\d))\s*$/,validate:function(e){return this.regex.test(e)},message:"{title} must be a valid currency value",expects:!1},ip:{regex:{ipv4:/^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/,ipv4Cidr:/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/,ipv6:/^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i,ipv6Cidr:/^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/},validate:function(e){return this.regex.ipv4.test(e)||this.regex.ipv6.test(e)||this.regex.ipv4Cidr.test(e)||this.regex.ipv6Cidr.test(e)},message:"{title} must be a valid IP address",expects:!1},min:{validate:function(e,t){return"string"==typeof e&&e.length>=t.min},message:"{title} must be a minimum of {min} characters",expects:["min"]},max:{validate:function(e,t){return"string"==typeof e&&e.length<=t.max},message:"{title} must be a maximum of {max} characters",expects:["max"]},range:{validate:function(e,t){return"string"==typeof e?e.length>=t.min&&e.length<=t.max:"number"==typeof e&&(e>=t.min&&e<=t.max)},message:"{title} must be a minimum of {min} and a maximum of {max} characters",expects:["min","max"]},equal:{validate:function(e,t){return""+e==""+t.value},message:"{title} must be equal to {field}",expects:["value","field"]},format:{validate:function(e,t){if("[object RegExp]"===Object.prototype.toString.call(t.regex))return t.regex.test(e);throw"approve.value(): [format] - regex is not a valid regular expression."},message:"{title} did not pass the [{regex}] test",expects:["regex"]},time:{regex:/^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/,validate:function(e){return this.regex.test(e)},message:"{title} is not a valid time",expects:!1},date:{formats:{ymd:/^(?:\2)(?:[0-9]{2})?[0-9]{2}([\/-])(1[0-2]|0?[1-9])([\/-])(3[01]|[12][0-9]|0?[1-9])$/,dmy:/^(3[01]|[12][0-9]|0?[1-9])([\/-])(1[0-2]|0?[1-9])([\/-])(?:[0-9]{2})?[0-9]{2}$/},validate:function(e,t){return this.formats[t.format].test(e)},message:"{title} is not a valid date",expects:["format"]},truthy:{regex:/^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/i,validate:function(e){return this.regex.test(e)},message:"{title} is not valid",expects:!1},falsy:{regex:/^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/i,validate:function(e){return!this.regex.test(e)},message:"{title} is not valid",expects:!1},cc:r,strength:s},i=function(){this.approved=!0,this.errors=[],this.failed=[],this.each=function(e){for(var t=e&&e.constructor&&e.call&&e.apply,r=this.errors.length;r--;)t&&e(this.errors[r])},this.filter=function(e,t){var r=t&&t.constructor&&t.call&&t.apply,s=0;if(this.hasOwnProperty(e))for(s=this[e].errors.length;s--;)r&&t(this[e].errors[s])}},n={tests:a,_format:function(e,t){return t="object"==typeof t?t:Array.prototype.slice.call(arguments,1),e.replace(/\{\{|\}\}|\{(\w+)\}/g,function(e,r){return"{{"===e?"{":"}}"===e?"}":t[r]}).trim()},_isRule:function(e){var t=["title","stop","ignoreNull"];return t.indexOf(e)<0},_start:function(e,t){var r=new i,s="",a=!1,n=!1;t.hasOwnProperty("title")&&(s=t.title),t.hasOwnProperty("stop")&&(a=t.stop),t.hasOwnProperty("ignoreNull")&&(n=t.ignoreNull);for(var u in t){if(a&&!r.approved)break;if(t.hasOwnProperty(u)&&this._isRule(u)){var o=t[u];if(!this.tests.hasOwnProperty(u))throw"approve.value(): "+u+" test not defined.";var d={constraint:o,rule:u,title:s,test:this.tests[u],value:e,ignoreNull:n};this._test(d,r)}}return r},_test:function(e,t){if(!e.hasOwnProperty("ignoreNull")||e.value||!e.ignoreNull){var r=this._getArgs(e),s=e.test.validate(e.value,r);if(t[e.rule]={approved:!0,errors:[]},"object"==typeof s){if(t.approved=!!s.valid&&t.approved,t[e.rule].approved=s.valid,s.hasOwnProperty("errors")){var a=this._formatMessages(s.errors,e);t.errors=t.errors.concat(a),t[e.rule].errors=a}for(var i in s)s.hasOwnProperty(i)&&!t.hasOwnProperty(i)&&(t[e.rule][i]=s[i])}else{if("boolean"!=typeof s)throw"approve.value(): "+e.rule+" returned an invalid value";t.approved=!!s&&t.approved,t[e.rule].approved=s}if(!t.approved){var n=this._formatMessage(e);t.errors.push(n),t[e.rule].errors.push(n)}s.valid||t.failed.push(e.rule)}},_eachExpected:function(e,t){if(Array.isArray(e.test.expects))for(var r=e.test.expects.length,s=r;s--;)t(e.test.expects[s],r)},_getArgs:function(e){var t={};return this._eachExpected(e,function(r,s){if(e.constraint.hasOwnProperty(r))t[r]=e.constraint[r];else{if(!(s<=1)||!/^[A-Za-z0-9]+$/i.test(e.constraint)&&"[object RegExp]"!==toString.call(e.constraint))throw"approve.value(): "+e.rule+" expects the "+r+" parameter.";t[r]=e.constraint}}),e.constraint.hasOwnProperty("config")&&(t.config=e.constraint.config),t},_getFormat:function(e){var t={};return this._eachExpected(e,function(r){e.constraint.hasOwnProperty(r)&&(t[r]=e.constraint[r]),/^[A-Za-z0-9]+$/i.test(e.constraint)&&(t[r]=e.constraint)}),t.title=e.title,t},_formatMessages:function(e,t){for(var r=this._getFormat(t),s=e.length;s--;)e[s]=this._format(e[s],r);return e},_formatMessage:function(e){var t,r=this._getFormat(e);return e.constraint.hasOwnProperty("message")?(t=e.constraint.message,this._format(t,r)):(t=e.test.message,this._format(t,r))},value:function(e,t){if("object"!=typeof t)throw"approve.value(value, rules): rules is not a valid object.";return this._start(e,t)},addTest:function(e,t){if("object"!=typeof e)throw"approve.addTest(obj, name): obj is not a valid object.";try{this.tests.hasOwnProperty(t)||(this.tests[t]=e)}catch(r){throw"approve.addTest(): "+r.message}}};return n});
2 | //# sourceMappingURL=approve.min.js.map
3 |
--------------------------------------------------------------------------------
/dist/approve.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["approve.js"],"names":["global","factory","exports","module","define","amd","approve","this","Card","scheme","valid","Score","strength","points","isMinimum","hasLower","hasUpper","hasNumber","hasSpecial","isBonus","percent","errors","cc","message","schemes","regex","_getScheme","value","replace","i","length","test","validate","ca","card","sum","mul","charAt","minimum","minimumBonus","strengths","0","1","2","3","4","5","6","expects","_getScore","text","result","match","Math","ceil","_getStrength","push","pars","min","bonus","hasOwnProperty","config","messages","tests","required","email","url","alphaNumeric","numeric","alpha","decimal","currency","ip","ipv4","ipv4Cidr","ipv6","ipv6Cidr","max","range","equal","format","Object","prototype","toString","call","time","date","formats","ymd","dmy","truthy","falsy","Result","approved","failed","each","callback","isFunc","constructor","apply","filter","_format","col","Array","slice","arguments","m","n","trim","_isRule","rule","props","indexOf","_start","rules","title","stop","ignoreNull","constraint","params","_test","args","_getArgs","ret","_formatMessages","concat","prop","_formatMessage","_eachExpected","fn","isArray","expectsLength","_getFormat","addTest","obj","name","e"],"mappings":"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","file":"approve.min.js","sourcesContent":["(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n\ttypeof define === 'function' && define.amd ? define(factory) :\n\t(global.approve = factory());\n}(this, (function () { 'use strict';\n\n/**\r\n * The result object containing the outcome of the credit card test.\r\n */\r\nfunction Card() {\r\n this.scheme = '';\r\n this.valid = false;\r\n}\r\n\r\n/** \r\n * Checks if a value is a valid credit card.\r\n */\r\nvar cc = {\r\n /**\r\n * The default error message.\r\n */\r\n message: '{title} is not a valid credit card number',\r\n schemes: [\r\n {\r\n regex: /^(5610|560221|560222|560223|560224|560225)/,\r\n scheme: 'Australian Bank Card'\r\n },\r\n {\r\n regex: /^(2014|2149)/,\r\n scheme: 'Diner\\'s Club'\r\n },\r\n {\r\n regex: /^36/,\r\n scheme: 'Diner\\'s Club International'\r\n },\r\n {\r\n regex: /^(30[0-5]|36|38|54|55|2014|2149)/,\r\n scheme: 'Diner\\'s Club / Carte Blanche'\r\n },\r\n {\r\n regex: /^35(2[89]|[3-8][0-9])/,\r\n scheme: 'Japanese Credit Bureau'\r\n },\r\n {\r\n regex: /^(5018|5020|5038|6304|6759|676[1-3])/,\r\n scheme: 'Maestro'\r\n },\r\n {\r\n regex: /^5[1-5]/,\r\n scheme: 'Mastercard'\r\n },\r\n {\r\n regex: /^(6304|670[69]|6771)/,\r\n scheme: 'Laser'\r\n },\r\n {\r\n regex: /^(6334|6767)/,\r\n scheme: 'Solo (Paymentech)'\r\n },\r\n {\r\n regex: /^(6011|622|64|65)/,\r\n scheme: 'Discover'\r\n },\r\n {\r\n regex: /^3[47]/,\r\n scheme: 'American Express'\r\n },\r\n {\r\n regex: /^(4026|417500|4508|4844|491(3|7))/,\r\n scheme: 'Visa Electron'\r\n },\r\n {\r\n regex: /^(4)/,\r\n scheme: 'Visa'\r\n }\r\n ],\r\n /**\r\n * Returns the name of the credit card scheme.\r\n * @param {Object} value - The credit card number to test.\r\n * @return {Object} The result of the test.\r\n */\r\n _getScheme: function(value) {\r\n value = (''+ value).replace(/\\D/g, '');\r\n\r\n var i = this.schemes.length;\r\n while (i--) {\r\n if (this.schemes[i].regex.test(value)) {\r\n return this.schemes[i].scheme;\r\n }\r\n }\r\n\r\n return undefined;\r\n },\r\n /**\r\n * The method that is called by ApproveJs to perform the test.\r\n * @param {Object} value - The value to test.\r\n * @return {Object} The result object of the test.\r\n */\r\n validate: function(value) {\r\n value = (''+ value).replace(/\\D/g, '');\r\n\r\n var card = new Card(), \r\n i = value.length,\r\n sum = 0,\r\n mul = 1,\r\n ca;\r\n\r\n // Not enough numbers. Shortest currently is 12. \r\n if (i < 12) {\r\n return false;\r\n }\r\n\r\n while (i--) {\r\n ca = value.charAt(i) * mul;\r\n sum += ca - (ca > 9) * 9;\r\n mul ^= 3;\r\n }\r\n\r\n card.valid = (sum % 10 === 0) && (sum > 0);\r\n card.scheme = this._getScheme(value);\r\n\r\n return card;\r\n }\r\n};\n\n/**\r\n * The result object containing the outcome of the strength test.\r\n */\r\nfunction Score(strength) {\r\n this.strength = strength;\r\n this.points = 0;\r\n this.isMinimum = false;\r\n this.hasLower = false;\r\n this.hasUpper = false;\r\n this.hasNumber = false;\r\n this.hasSpecial = false;\r\n this.isBonus = false;\r\n this.percent = 0;\r\n this.valid = false;\r\n this.errors = [];\r\n}\r\n/** \r\n * Checks if a value is a strong password string.\r\n */\r\nvar strength = {\r\n /**\r\n * The minimum length a password must be.\r\n */\r\n minimum: 8,\r\n /**\r\n * The minimum length a password must be for a bonus point.\r\n */\r\n minimumBonus: 10,\r\n /**\r\n * The text representing the strength of a password.\r\n */\r\n strengths: {\r\n 0: 'Very Weak',\r\n 1: 'Weak',\r\n 2: 'Better',\r\n 3: 'Almost',\r\n 4: 'Acceptable',\r\n 5: 'Strong',\r\n 6: 'Very Strong'\r\n },\r\n /**\r\n * The default error message.\r\n */\r\n message: '{title} did not pass the strength test.',\r\n /**\r\n * Expects the 'min' and 'bonus' parameters.\r\n */\r\n expects: ['min', 'bonus'],\r\n /**\r\n * Default error messages\r\n */\r\n errors: {\r\n isMinimum: '{title} must be at least {min} characters',\r\n hasLower: '{title} must have at least 1 lower case character',\r\n hasUpper: '{title} must have at least 1 upper case character',\r\n hasNumber: '{title} must have at least 1 number',\r\n hasSpecial: '{title} must have at least 1 special character'\r\n },\r\n /**\r\n * Returns an object containing the score of a value.\r\n */\r\n _getScore: function(text) {\r\n // Create the object that represents the score of the text\r\n var result = new Score(this.strengths[0]);\r\n // If text is longer than minimum give 1 point.\r\n // If text is longer than minimumBonus give another 1 point.\r\n if (text.length > this.minimumBonus) {\r\n result.points += 2;\r\n result.isBonus = true;\r\n result.isMinimum = true;\r\n } else if (text.length > this.minimum){\r\n result.points++;\r\n result.isMinimum = true;\r\n } else {\r\n result.points = 1;\r\n result.isMinimum = false;\r\n }\r\n // If text has lowercase characters give 1 point.\r\n result.hasLower = text.match(/[a-z]/) === null ? false : true;\r\n if(result.isMinimum && result.hasLower) {\r\n result.points++;\r\n }\r\n // If text has uppercase characters give 1 point.\r\n result.hasUpper = text.match(/[A-Z]/) === null ? false : true;\r\n if(result.isMinimum && result.hasUpper) {\r\n result.points++;\r\n }\r\n // If text has at least one number give 1 point.\r\n result.hasNumber = text.match(/\\d+/) === null ? false : true;\r\n if(result.isMinimum && result.hasNumber) {\r\n result.points++;\r\n }\r\n // If text has at least one special caracther give 1 point.\r\n result.hasSpecial = text.match(/.[!,@,#,$,%,^,&,*,?,_,~,-,(,)]/) === null ? false : true;\r\n if(result.isMinimum && result.hasSpecial) {\r\n result.points++;\r\n }\r\n // Set the percentage value.\r\n result.percent = Math.ceil((result.points / 6) * 100);\r\n // Return the score object.\r\n return result;\r\n },\r\n /**\r\n * Returns an object containing the score and validation of a value.\r\n */\r\n _getStrength: function (text) {\r\n var result = this._getScore(text);\r\n result.strength = this.strengths[result.points];\r\n if (!result.isMinimum) {\r\n result.errors.push(this.errors.isMinimum);\r\n }\r\n if (!result.hasLower) {\r\n result.errors.push(this.errors.hasLower);\r\n }\r\n if (!result.hasUpper) {\r\n result.errors.push(this.errors.hasUpper);\r\n }\r\n if (!result.hasSpecial) {\r\n result.errors.push(this.errors.hasSpecial);\r\n }\r\n if (!result.hasNumber) {\r\n result.errors.push(this.errors.hasNumber);\r\n }\r\n if (result.points > 4) {\r\n result.valid = true;\r\n } \r\n return result;\r\n },\r\n /**\r\n * The method that is called by ApproveJs to perform the test.\r\n */\r\n validate: function(value, pars) {\r\n this.minimum = pars.min || this.minimum;\r\n this.minimumBonus = pars.bonus || this.minimumBonus;\r\n if (pars.hasOwnProperty('config') && pars.config.hasOwnProperty('messages')) {\r\n for (var message in pars.config.messages) {\r\n if (pars.config.messages.hasOwnProperty(message)) {\r\n this.errors[message] = pars.config.messages[message];\r\n }\r\n }\r\n }\r\n return this._getStrength(value);\r\n }\r\n};\n\nvar tests = {\r\n /**\r\n * Checks if a value is present.\r\n */\r\n required: {\r\n validate: function(value) {\r\n return !!value;\r\n },\r\n message: '{title} is required',\r\n expects: false\r\n },\r\n /**\r\n * Checks if a value is a valid email address.\r\n */\r\n email: {\r\n regex: /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$/i, // eslint-disable-line no-control-regex\r\n validate: function(value) {\r\n return this.regex.test(value);\r\n },\r\n message: '{title} must be a valid email address',\r\n expects: false\r\n },\r\n /**\r\n * Checks if a value is a valid web address.\r\n */\r\n url: {\r\n regex: /^(?:(?:https?|ftp):\\/\\/)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:\\/\\S*)?$/i,\r\n validate: function(value) {\r\n return this.regex.test(value);\r\n },\r\n message: '{title} must be a valid web address',\r\n expects: false\r\n },\r\n /**\r\n * Checks if a value contains both letters and numbers.\r\n */\r\n alphaNumeric: {\r\n regex: /^[A-Za-z0-9]+$/i,\r\n validate: function(value) {\r\n return this.regex.test(value);\r\n },\r\n message: '{title} may only contain [A-Za-z] and [0-9]',\r\n expects: false\r\n },\r\n /**\r\n * Checks if a value contains only numbers.\r\n */\r\n numeric: {\r\n regex: /^-?[0-9]+$/,\r\n validate: function(value) {\r\n return this.regex.test(value);\r\n },\r\n message: '{title} may only contain [0-9]',\r\n expects: false\r\n },\r\n /**\r\n * Checks if a value contains only letters.\r\n */\r\n alpha: {\r\n regex: /^[A-Za-z]+$/,\r\n validate: function(value) {\r\n return this.regex.test(value);\r\n },\r\n message: '{title} may only contain [A-Za-z]',\r\n expects: false\r\n },\r\n /**\r\n * Checks if a value is a valid decimal.\r\n */\r\n decimal: {\r\n regex: /^\\s*(\\+|-)?((\\d+(\\.\\d+)?)|(\\.\\d+))\\s*$/,\r\n validate: function(value) {\r\n return this.regex.test(value);\r\n },\r\n message: '{title} must be a valid decimal',\r\n expects: false\r\n },\r\n /**\r\n * Similar to 'decimal', but for currency values.\r\n */\r\n currency: {\r\n regex: /^\\s*(\\+|-)?((\\d+(\\.\\d\\d)?)|(\\.\\d\\d))\\s*$/,\r\n validate: function(value) {\r\n return this.regex.test(value);\r\n },\r\n message: '{title} must be a valid currency value',\r\n expects: false\r\n },\r\n /**\r\n * Checks if a value is a valid ipv4 or ipv6 address.\r\n */\r\n ip: {\r\n regex: {\r\n ipv4: /^(?:(?:\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])$/,\r\n ipv4Cidr: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/([0-9]|[1-2][0-9]|3[0-2]))$/,\r\n ipv6: /^((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})((([\\dA-F]{1,4}((?!\\3)::|:\\b|$))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})$/i,\r\n ipv6Cidr: /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/\r\n },\r\n validate: function(value) {\r\n return this.regex.ipv4.test(value) || this.regex.ipv6.test(value) || this.regex.ipv4Cidr.test(value) || this.regex.ipv6Cidr.test(value);\r\n },\r\n message: '{title} must be a valid IP address',\r\n expects: false\r\n },\r\n /**\r\n * Checks if a value is a minimum of n characters.\r\n */\r\n min: {\r\n validate: function(value, pars) {\r\n return typeof value === 'string' && value.length >= pars.min;\r\n },\r\n message: '{title} must be a minimum of {min} characters',\r\n expects: ['min']\r\n },\r\n /**\r\n * Checks if a value is a maximum of n characters.\r\n */\r\n max: {\r\n validate: function(value, pars) {\r\n return typeof value === 'string' && value.length <= pars.max;\r\n },\r\n message: '{title} must be a maximum of {max} characters',\r\n expects: ['max']\r\n },\r\n /**\r\n * Checks if a string's length or number is between a minimum and maximum.\r\n */\r\n range: {\r\n validate: function(value, pars) {\r\n if (typeof value === 'string')\r\n {\r\n return value.length >= pars.min && value.length <= pars.max;\r\n } else if (typeof value === 'number') {\r\n return value >= pars.min && value <= pars.max;\r\n }\r\n return false;\r\n },\r\n message: '{title} must be a minimum of {min} and a maximum of {max} characters',\r\n expects: ['min', 'max']\r\n },\r\n /**\r\n * Checks if a value is the same as the value of another.\r\n */\r\n equal: {\r\n validate: function(value, pars) {\r\n return '' + value === '' + pars.value;\r\n },\r\n message: '{title} must be equal to {field}',\r\n expects: ['value', 'field']\r\n },\r\n /**\r\n * Checks if a value passes a given regular expression.\r\n */\r\n format: {\r\n validate: function(value, pars) {\r\n if (Object.prototype.toString.call(pars.regex) === '[object RegExp]') {\r\n return pars.regex.test(value);\r\n }\r\n throw 'approve.value(): [format] - regex is not a valid regular expression.';\r\n },\r\n message: '{title} did not pass the [{regex}] test',\r\n expects: ['regex']\r\n },\r\n /**\r\n * Checks if a value is a valid time string (hh:mm:ss).\r\n */\r\n time: {\r\n regex: /^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/,\r\n validate: function(value) {\r\n return this.regex.test(value);\r\n },\r\n message: '{title} is not a valid time',\r\n expects: false\r\n },\r\n /**\r\n * Checks if a value is a valid date.\r\n */\r\n date: {\r\n formats: {\r\n ymd: /^(?:\\2)(?:[0-9]{2})?[0-9]{2}([\\/-])(1[0-2]|0?[1-9])([\\/-])(3[01]|[12][0-9]|0?[1-9])$/,\r\n dmy: /^(3[01]|[12][0-9]|0?[1-9])([\\/-])(1[0-2]|0?[1-9])([\\/-])(?:[0-9]{2})?[0-9]{2}$/\r\n },\r\n validate: function(value, pars) {\r\n return this.formats[pars.format].test(value);\r\n },\r\n message: '{title} is not a valid date',\r\n expects: ['format']\r\n },\r\n /**\r\n * Checks if a value is truthy ('yes', 'true', 'ok[ay]', '1').\r\n */\r\n truthy: {\r\n regex: /^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/i,\r\n validate: function(value) {\r\n return this.regex.test(value);\r\n },\r\n message: '{title} is not valid',\r\n expects: false\r\n },\r\n /**\r\n * Checks if a value is falsy ('No', 'false', '0').\r\n */\r\n falsy: {\r\n regex: /^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/i,\r\n validate: function(value) {\r\n return !this.regex.test(value);\r\n },\r\n message: '{title} is not valid',\r\n expects: false\r\n },\r\n cc: cc,\r\n strength: strength\r\n};\n\nvar Result = function() {\n this.approved = true;\n this.errors = [];\n this.failed = [];\n // Provides easy access to the loop for the errors.\n this.each = function(callback) {\n var isFunc = callback && callback.constructor && callback.call && callback.apply,\n i = this.errors.length;\n while (i--) {\n if (isFunc) {\n callback(this.errors[i]);\n }\n }\n };\n // Provides easy access to the loop for a test's errors.\n this.filter = function(test, callback) {\n \tvar isFunc = callback && callback.constructor && callback.call && callback.apply,\n \t\ti = 0;\n \tif (this.hasOwnProperty(test)) {\n \t\ti = this[test].errors.length;\n \t\twhile (i--) {\n\t if (isFunc) {\n\t callback(this[test].errors[i]);\n\t }\n\t }\n \t}\n };\n};\n\n/** @constructor */\nvar approve = {\n\t/**\n\t * Default tests.
\n\t */\n\ttests: tests,\n\t/**\n\t * A helper function for formatting strings.\n\t */\n\t_format: function(text, col) {\n\t col = typeof col === 'object' ? col : Array.prototype.slice.call(arguments, 1);\n\t return text.replace(/\\{\\{|\\}\\}|\\{(\\w+)\\}/g, function (m, n) {\n\t if (m === \"{{\") { return \"{\"; }\n\t if (m === \"}}\") { return \"}\"; }\n\t return col[n];\n\t }).trim();\n\t},\n\t/**\n\t * Checks whether the given rule is not a config property.\n\t */\n\t_isRule: function(rule) {\n\t\tvar props = [\n\t\t\t'title',\n\t\t\t'stop',\n\t\t\t'ignoreNull'\n\t\t];\n\t\treturn props.indexOf(rule) < 0;\n\t},\n\t/**\n\t * The start of the validation process.\n\t */\n\t_start: function(value, rules) {\n\t // Instantiate a result object.\n\t var result = new Result(),\n\t // This is used to format the message with the value title.\n\t title = '',\n\t // When true, tests will not continue after first failed test.\n\t stop = false,\n\t // When true, tests will not be executed if the value is null.\n\t ignoreNull = false;\n\t // Check if the rule has a title property?\n\t if (rules.hasOwnProperty('title')) {\n\t title = rules.title;\n\t }\n\t // Check if the rule has a stop property?\n\t if (rules.hasOwnProperty('stop')) {\n\t stop = rules.stop;\n\t }\n\t // Check if the rule has an ignoreNull property?\n\t if (rules.hasOwnProperty('ignoreNull')) {\n\t ignoreNull = rules.ignoreNull;\n\t }\n\t // Loop through given rules.\n\t for (var rule in rules) {\n\t \t// Stop validating after each failed test\n\t \tif (stop && !result.approved) {\n\t \t\tbreak;\n\t \t}\n\t if (rules.hasOwnProperty(rule) && this._isRule(rule)) {\n\t var constraint = rules[rule];\n\t // Check if rule exists in tests.\n\t if (this.tests.hasOwnProperty(rule)) {\n\t // Set a pointer to the current test.\n\t var params = {\n\t constraint: constraint,\n\t rule: rule,\n\t title: title,\n\t test: this.tests[rule],\n\t value: value,\n\t ignoreNull: ignoreNull\n\t };\n\t this._test(params, result);\n\t } else {\n\t throw 'approve.value(): ' + rule + ' test not defined.';\n\t }\n\t }\n\t }\n\n\t return result;\n\t},\n\t/**\n\t * Performs the actual testing of the value and returns the result including any errors.\n\t */\n\t_test: function(params, result) {\n\t\t// Check if nulls should be ignored\n\t\tif (params.hasOwnProperty('ignoreNull')) {\n\t\t\tif (!params.value && params.ignoreNull) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t // Create an args object for required parameters.\n\t var args = this._getArgs(params),\n\t // Test the value.\n\t ret = params.test.validate(params.value, args);\n\n\t result[params.rule] = {\n\t \tapproved: true,\n\t \terrors: []\n\t };\n\t // Check if the returned value is an object.\n\t if(typeof ret === 'object')\n\t {\n\t // An object was returned.\n\t // Check if the test was successful.\n\t result.approved = !ret.valid ? false : result.approved;\n\t result[params.rule].approved = ret.valid;\n\t // Add the error messages returned by the resluting object.\n\t if (ret.hasOwnProperty('errors')) {\n\t \tvar messages = this._formatMessages(ret.errors, params);\n\t result.errors = result.errors.concat(messages);\n\t result[params.rule].errors = messages;\n\t }\n\t // Merge any properties from the resulting object with the main result to be returned.\n\t for (var prop in ret) {\n\t if (ret.hasOwnProperty(prop) && !result.hasOwnProperty(prop)) {\n\t result[params.rule][prop] = ret[prop];\n\t }\n\t }\n\t } else if (typeof ret !== 'boolean') {\n\t // We don't process if it's not a boolean or object.\n\t throw 'approve.value(): ' + params.rule + ' returned an invalid value';\n\t } else {\n\t result.approved = !ret ? false : result.approved;\n\t result[params.rule].approved = ret;\n\t }\n\t if (!result.approved) {\n\t \tvar message = this._formatMessage(params);\n\t result.errors.push(message);\n\t result[params.rule].errors.push(message);\n\t }\n\t if (!ret.valid) {\n \tresult.failed.push(params.rule);\n }\n\t},\n\t/**\n\t * Helper method to loop over expected test parameters.\n\t */\n\t_eachExpected: function(params, fn) {\n\t if (Array.isArray(params.test.expects)) {\n\t var expectsLength = params.test.expects.length,\n\t i = expectsLength;\n\t // This test expects paramaters.\n\t // Loop through the test's expected parameters and call the given function.\n\t while (i--) {\n\t fn(params.test.expects[i], expectsLength);\n\t }\n\t }\n\t},\n\t/**\n\t * Returns an object containing the arguments for a test's expected parameters.\n\t */\n\t_getArgs: function(params) {\n\t var pars = {};\n\t // Does the test for this rule expect any paramaters?\n\t this._eachExpected(params, function(expects, expectsLength) {\n\t // Check if the rule object has the required parameter.\n\t if (params.constraint.hasOwnProperty(expects)) {\n\t // Add the expected parameter value to the pars object.\n\t pars[expects] = params.constraint[expects];\n\t } else if (expectsLength <= 1 && (/^[A-Za-z0-9]+$/i.test(params.constraint) || toString.call(params.constraint) === '[object RegExp]')) {\n\t // Set the parameter to the rule's value.\n\t pars[expects] = params.constraint;\n\t } else {\n\t throw 'approve.value(): ' + params.rule + ' expects the ' + expects + ' parameter.';\n\t }\n\t });\n\n\t // Does the rule have config?\n\t if (params.constraint.hasOwnProperty('config')) {\n\t // Add the config to the pars object.\n\t pars.config = params.constraint.config;\n\t }\n\t // Return the parameters object\n\t return pars;\n\t},\n\t/**\n\t * Returns an object containing placholder values to correctly format an error message.\n\t */\n\t_getFormat: function(params) {\n\t var format = {};\n\t // Does the test for the rule expect parameters?\n\t this._eachExpected(params, function(expects) {\n\t // Check if the rule object has the required parameter.\n\t if (params.constraint.hasOwnProperty(expects)) {\n\t // Add the expected parameter's format to the parameter value.\n\t format[expects] = params.constraint[expects];\n\t }\n\t // Expected parameter not present, is the constraint formattable?\n\t if (/^[A-Za-z0-9]+$/i.test(params.constraint)) {\n\t format[expects] = params.constraint;\n\t }\n\t });\n\t format.title = params.title;\n\t // Return the formatted message.\n\t return format;\n\t},\n\t/**\n\t * Returns an array of formatted error messages returned by tests that return objects instead of booleans.\n\t */\n\t_formatMessages: function(errors, params) {\n\t var format = this._getFormat(params),\n\t i = errors.length;\n\t while (i--) {\n\t errors[i] = this._format(errors[i], format);\n\t }\n\t return errors;\n\t},\n\t/**\n\t * Returns the correctly formatted message representing the current test's failure.\n\t */\n\t_formatMessage: function(params) {\n\t var format = this._getFormat(params);\n\t var message;\n\n\t // Does the provided rule have a custom message?\n\t if (params.constraint.hasOwnProperty('message')) {\n\t // The rule has a custom message, return it.\n\t message = params.constraint.message;\n\t return this._format(message, format);\n\t }\n\t else {\n\t // The rule does not have a custom message.\n\t // Get the default message from the tests.\n\t message = params.test.message;\n\t return this._format(message, format);\n\t }\n\t},\n\t/**\n\t * Executes the tests based on given rules to validate a given value.\n\t */\n\tvalue: function(value, rules) {\n\n\t // If rules is not an object, we cannot continue.\n\t if (typeof rules !== 'object') {\n\t throw 'approve.value(value, rules): rules is not a valid object.';\n\t }\n\t // Return the result object.\n\t return this._start(value, rules);\n\t},\n\t/**\n\t * Used to add custom tests.\n\t */\n\taddTest: function(obj, name) {\n\t // If obj is not a valid object, we cannot continue.\n\t if (typeof obj !== 'object') {\n\t throw 'approve.addTest(obj, name): obj is not a valid object.';\n\t }\n\t try {\n\t // Check if the test name already exists.\n\t if (!this.tests.hasOwnProperty(name)) {\n\t // The name does not exist, add it to the tests.\n\t this.tests[name] = obj;\n\t }\n\t } catch (e) {\n\t throw 'approve.addTest(): ' + e.message;\n\t }\n\t}\n};\n\nreturn approve;\n\n})));\n"]}
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp'),
2 | sourcemaps = require('gulp-sourcemaps'),
3 | uglify = require('gulp-uglify'),
4 | rename = require('gulp-rename'),
5 | concat = require('gulp-concat'),
6 | gzip = require('gulp-gzip'),
7 | util = require('gulp-util'),
8 | lec = require('gulp-line-ending-corrector'),
9 | jshint = require('gulp-jshint'),
10 | stylish = require('jshint-stylish'),
11 | exec = require('child_process').exec,
12 | packageJSON = require('./package.json'),
13 | jshintConf = packageJSON.jshintConf;
14 |
15 | var lintFiles = [
16 | //'./src/lib/_02.approve.start.js',
17 | './src/tests/*.js',
18 | //'./src/lib/_04.approve.end.js',
19 | './src/*.js'
20 | ];
21 |
22 | /*
23 | |--------------------------------------------------------------------------
24 | | Gulp Tasks
25 | |--------------------------------------------------------------------------
26 | */
27 | gulp.task('min', function(cb) {
28 | return gulp.src('./dist/approve.js')
29 | .pipe(sourcemaps.init())
30 | .pipe(uglify({preserveComments: 'license'}).on('error', util.log))
31 | .pipe(rename('approve.min.js'))
32 | .pipe(sourcemaps.write('/'))
33 | .pipe(gulp.dest('dist'));
34 | });
35 |
36 | gulp.task('gzip', ['min'], function(cb) {
37 | return gulp.src('./dist/approve.min.js')
38 | .pipe(sourcemaps.init())
39 | .pipe(gzip())
40 | .pipe(rename('approve.min.gzip.js'))
41 | .pipe(sourcemaps.write('/'))
42 | .pipe(gulp.dest('dist'));
43 | });
44 |
45 | gulp.task('lint', function() {
46 | jshintConf.lookup = false;
47 | return gulp.src(lintFiles)
48 | .pipe(jshint(jshintConf))
49 | .pipe(jshint.reporter(stylish));
50 | });
51 |
52 | gulp.task('default', ['gzip']);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "approvejs",
3 | "version": "3.1.2",
4 | "description": "A simple JavaScript validation library that doesn't interfere.",
5 | "main": "dist/approve.min.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git://github.com/CharlGottschalk/approvejs.git"
9 | },
10 | "keywords": [
11 | "validation",
12 | "validate",
13 | "approve"
14 | ],
15 | "author": "Charl Gottschalk (http://charlgottschalk.co.za)",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "chai": "^3.5.0",
19 | "gulp": "^3.9.1",
20 | "gulp-concat": "^2.6.0",
21 | "gulp-gzip": "^1.4.0",
22 | "gulp-jshint": "^2.0.1",
23 | "gulp-line-ending-corrector": "^1.0.1",
24 | "gulp-rename": "^1.2.2",
25 | "gulp-sourcemaps": "^1.6.0",
26 | "gulp-uglify": "^1.5.4",
27 | "gulp-util": "^3.0.7",
28 | "jshint": "^2.9.2",
29 | "jshint-stylish": "^2.2.0",
30 | "mocha": "^2.5.3",
31 | "rollup": "^0.36.3"
32 | },
33 | "scripts": {
34 | "test": "mocha --reporter spec",
35 | "build": "rollup -c && gulp"
36 | },
37 | "bugs": {
38 | "url": "https://github.com/CharlGottschalk/approvejs/issues"
39 | },
40 | "jshintConf": {
41 | "globals": {
42 | "approve": true
43 | },
44 | "maxparams": 5,
45 | "maxdepth": 5,
46 | "maxstatements": 25,
47 | "maxcomplexity": 10,
48 | "esversion": 6,
49 | "browser": true,
50 | "boss": false,
51 | "curly": false,
52 | "debug": false,
53 | "devel": false,
54 | "eqeqeq": true,
55 | "evil": true,
56 | "forin": false,
57 | "immed": true,
58 | "laxbreak": false,
59 | "newcap": true,
60 | "noarg": true,
61 | "noempty": false,
62 | "nonew": false,
63 | "nomen": false,
64 | "onevar": true,
65 | "plusplus": false,
66 | "regexp": false,
67 | "undef": true,
68 | "sub": true,
69 | "strict": false,
70 | "white": true,
71 | "unused": true
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | entry: 'src/approve.js',
3 | format: 'umd',
4 | moduleName: 'approve',
5 | dest: 'dist/approve.js'
6 | };
--------------------------------------------------------------------------------
/src/approve.js:
--------------------------------------------------------------------------------
1 | import tests from './tests/approve.tests';
2 |
3 | var Result = function() {
4 | this.approved = true;
5 | this.errors = [];
6 | this.failed = [];
7 | // Provides easy access to the loop for the errors.
8 | this.each = function(callback) {
9 | var isFunc = callback && callback.constructor && callback.call && callback.apply,
10 | i = this.errors.length;
11 | while (i--) {
12 | if (isFunc) {
13 | callback(this.errors[i]);
14 | }
15 | }
16 | };
17 | // Provides easy access to the loop for a test's errors.
18 | this.filter = function(test, callback) {
19 | var isFunc = callback && callback.constructor && callback.call && callback.apply,
20 | i = 0;
21 | if (this.hasOwnProperty(test)) {
22 | i = this[test].errors.length;
23 | while (i--) {
24 | if (isFunc) {
25 | callback(this[test].errors[i]);
26 | }
27 | }
28 | }
29 | };
30 | };
31 |
32 | /** @constructor */
33 | export default {
34 | /**
35 | * Default tests.
36 | */
37 | tests: tests,
38 | /**
39 | * A helper function for formatting strings.
40 | */
41 | _format: function(text, col) {
42 | col = typeof col === 'object' ? col : Array.prototype.slice.call(arguments, 1);
43 | return text.replace(/\{\{|\}\}|\{(\w+)\}/g, function (m, n) {
44 | if (m === "{{") { return "{"; }
45 | if (m === "}}") { return "}"; }
46 | return col[n];
47 | }).trim();
48 | },
49 | /**
50 | * Checks whether the given rule is not a config property.
51 | */
52 | _isRule: function(rule) {
53 | var props = [
54 | 'title',
55 | 'stop',
56 | 'ignoreNull'
57 | ];
58 | return props.indexOf(rule) < 0;
59 | },
60 | /**
61 | * The start of the validation process.
62 | */
63 | _start: function(value, rules) {
64 | // Instantiate a result object.
65 | var result = new Result(),
66 | // This is used to format the message with the value title.
67 | title = '',
68 | // When true, tests will not continue after first failed test.
69 | stop = false,
70 | // When true, tests will not be executed if the value is null.
71 | ignoreNull = false;
72 | // Check if the rule has a title property?
73 | if (rules.hasOwnProperty('title')) {
74 | title = rules.title;
75 | }
76 | // Check if the rule has a stop property?
77 | if (rules.hasOwnProperty('stop')) {
78 | stop = rules.stop;
79 | }
80 | // Check if the rule has an ignoreNull property?
81 | if (rules.hasOwnProperty('ignoreNull')) {
82 | ignoreNull = rules.ignoreNull;
83 | }
84 | // Loop through given rules.
85 | for (var rule in rules) {
86 | // Stop validating after each failed test
87 | if (stop && !result.approved) {
88 | break;
89 | }
90 | if (rules.hasOwnProperty(rule) && this._isRule(rule)) {
91 | var constraint = rules[rule];
92 | // Check if rule exists in tests.
93 | if (this.tests.hasOwnProperty(rule)) {
94 | // Set a pointer to the current test.
95 | var params = {
96 | constraint: constraint,
97 | rule: rule,
98 | title: title,
99 | test: this.tests[rule],
100 | value: value,
101 | ignoreNull: ignoreNull
102 | };
103 | this._test(params, result);
104 | } else {
105 | throw 'approve.value(): ' + rule + ' test not defined.';
106 | }
107 | }
108 | }
109 |
110 | return result;
111 | },
112 | /**
113 | * Performs the actual testing of the value and returns the result including any errors.
114 | */
115 | _test: function(params, result) {
116 | // Check if nulls should be ignored
117 | if (params.hasOwnProperty('ignoreNull')) {
118 | if (!params.value && params.ignoreNull) {
119 | return;
120 | }
121 | }
122 | // Create an args object for required parameters.
123 | var args = this._getArgs(params),
124 | // Test the value.
125 | ret = params.test.validate(params.value, args);
126 |
127 | result[params.rule] = {
128 | approved: true,
129 | errors: []
130 | };
131 | // Check if the returned value is an object.
132 | if(typeof ret === 'object')
133 | {
134 | // An object was returned.
135 | // Check if the test was successful.
136 | result.approved = !ret.valid ? false : result.approved;
137 | result[params.rule].approved = ret.valid;
138 | // Add the error messages returned by the resluting object.
139 | if (ret.hasOwnProperty('errors')) {
140 | var messages = this._formatMessages(ret.errors, params);
141 | result.errors = result.errors.concat(messages);
142 | result[params.rule].errors = messages;
143 | }
144 | // Merge any properties from the resulting object with the main result to be returned.
145 | for (var prop in ret) {
146 | if (ret.hasOwnProperty(prop) && !result.hasOwnProperty(prop)) {
147 | result[params.rule][prop] = ret[prop];
148 | }
149 | }
150 | } else if (typeof ret !== 'boolean') {
151 | // We don't process if it's not a boolean or object.
152 | throw 'approve.value(): ' + params.rule + ' returned an invalid value';
153 | } else {
154 | result.approved = !ret ? false : result.approved;
155 | result[params.rule].approved = ret;
156 | }
157 | if (!result.approved) {
158 | var message = this._formatMessage(params);
159 | result.errors.push(message);
160 | result[params.rule].errors.push(message);
161 | }
162 | if (!ret.valid) {
163 | result.failed.push(params.rule);
164 | }
165 | },
166 | /**
167 | * Helper method to loop over expected test parameters.
168 | */
169 | _eachExpected: function(params, fn) {
170 | if (Array.isArray(params.test.expects)) {
171 | var expectsLength = params.test.expects.length,
172 | i = expectsLength;
173 | // This test expects paramaters.
174 | // Loop through the test's expected parameters and call the given function.
175 | while (i--) {
176 | fn(params.test.expects[i], expectsLength);
177 | }
178 | }
179 | },
180 | /**
181 | * Returns an object containing the arguments for a test's expected parameters.
182 | */
183 | _getArgs: function(params) {
184 | var pars = {};
185 | // Does the test for this rule expect any paramaters?
186 | this._eachExpected(params, function(expects, expectsLength) {
187 | // Check if the rule object has the required parameter.
188 | if (params.constraint.hasOwnProperty(expects)) {
189 | // Add the expected parameter value to the pars object.
190 | pars[expects] = params.constraint[expects];
191 | } else if (expectsLength <= 1 && (/^[A-Za-z0-9]+$/i.test(params.constraint) || toString.call(params.constraint) === '[object RegExp]')) {
192 | // Set the parameter to the rule's value.
193 | pars[expects] = params.constraint;
194 | } else {
195 | throw 'approve.value(): ' + params.rule + ' expects the ' + expects + ' parameter.';
196 | }
197 | });
198 |
199 | // Does the rule have config?
200 | if (params.constraint.hasOwnProperty('config')) {
201 | // Add the config to the pars object.
202 | pars.config = params.constraint.config;
203 | }
204 | // Return the parameters object
205 | return pars;
206 | },
207 | /**
208 | * Returns an object containing placholder values to correctly format an error message.
209 | */
210 | _getFormat: function(params) {
211 | var format = {};
212 | // Does the test for the rule expect parameters?
213 | this._eachExpected(params, function(expects) {
214 | // Check if the rule object has the required parameter.
215 | if (params.constraint.hasOwnProperty(expects)) {
216 | // Add the expected parameter's format to the parameter value.
217 | format[expects] = params.constraint[expects];
218 | }
219 | // Expected parameter not present, is the constraint formattable?
220 | if (/^[A-Za-z0-9]+$/i.test(params.constraint)) {
221 | format[expects] = params.constraint;
222 | }
223 | });
224 | format.title = params.title;
225 | // Return the formatted message.
226 | return format;
227 | },
228 | /**
229 | * Returns an array of formatted error messages returned by tests that return objects instead of booleans.
230 | */
231 | _formatMessages: function(errors, params) {
232 | var format = this._getFormat(params),
233 | i = errors.length;
234 | while (i--) {
235 | errors[i] = this._format(errors[i], format);
236 | }
237 | return errors;
238 | },
239 | /**
240 | * Returns the correctly formatted message representing the current test's failure.
241 | */
242 | _formatMessage: function(params) {
243 | var format = this._getFormat(params);
244 | var message;
245 |
246 | // Does the provided rule have a custom message?
247 | if (params.constraint.hasOwnProperty('message')) {
248 | // The rule has a custom message, return it.
249 | message = params.constraint.message;
250 | return this._format(message, format);
251 | }
252 | else {
253 | // The rule does not have a custom message.
254 | // Get the default message from the tests.
255 | message = params.test.message;
256 | return this._format(message, format);
257 | }
258 | },
259 | /**
260 | * Executes the tests based on given rules to validate a given value.
261 | */
262 | value: function(value, rules) {
263 |
264 | // If rules is not an object, we cannot continue.
265 | if (typeof rules !== 'object') {
266 | throw 'approve.value(value, rules): rules is not a valid object.';
267 | }
268 | // Return the result object.
269 | return this._start(value, rules);
270 | },
271 | /**
272 | * Used to add custom tests.
273 | */
274 | addTest: function(obj, name) {
275 | // If obj is not a valid object, we cannot continue.
276 | if (typeof obj !== 'object') {
277 | throw 'approve.addTest(obj, name): obj is not a valid object.';
278 | }
279 | try {
280 | // Check if the test name already exists.
281 | if (!this.tests.hasOwnProperty(name)) {
282 | // The name does not exist, add it to the tests.
283 | this.tests[name] = obj;
284 | }
285 | } catch (e) {
286 | throw 'approve.addTest(): ' + e.message;
287 | }
288 | }
289 | };
290 |
--------------------------------------------------------------------------------
/src/tests/approve.creditcard.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The result object containing the outcome of the credit card test.
3 | */
4 | function Card() {
5 | this.scheme = '';
6 | this.valid = false;
7 | }
8 |
9 | /**
10 | * Checks if a value is a valid credit card.
11 | */
12 | export default {
13 | /**
14 | * The default error message.
15 | */
16 | message: '{title} is not a valid credit card number',
17 | schemes: [
18 | {
19 | regex: /^(5610|560221|560222|560223|560224|560225)/,
20 | scheme: 'Australian Bank Card'
21 | },
22 | {
23 | regex: /^(2014|2149)/,
24 | scheme: 'Diner\'s Club'
25 | },
26 | {
27 | regex: /^36/,
28 | scheme: 'Diner\'s Club International'
29 | },
30 | {
31 | regex: /^(30[0-5]|36|38|54|55|2014|2149)/,
32 | scheme: 'Diner\'s Club / Carte Blanche'
33 | },
34 | {
35 | regex: /^35(2[89]|[3-8][0-9])/,
36 | scheme: 'Japanese Credit Bureau'
37 | },
38 | {
39 | regex: /^(5018|5020|5038|6304|6759|676[1-3])/,
40 | scheme: 'Maestro'
41 | },
42 | {
43 | regex: /^5[1-5]/,
44 | scheme: 'Mastercard'
45 | },
46 | {
47 | regex: /^(6304|670[69]|6771)/,
48 | scheme: 'Laser'
49 | },
50 | {
51 | regex: /^(6334|6767)/,
52 | scheme: 'Solo (Paymentech)'
53 | },
54 | {
55 | regex: /^(6011|622|64|65)/,
56 | scheme: 'Discover'
57 | },
58 | {
59 | regex: /^3[47]/,
60 | scheme: 'American Express'
61 | },
62 | {
63 | regex: /^(4026|417500|4508|4844|491(3|7))/,
64 | scheme: 'Visa Electron'
65 | },
66 | {
67 | regex: /^(4)/,
68 | scheme: 'Visa'
69 | }
70 | ],
71 | /**
72 | * Returns the name of the credit card scheme.
73 | * @param {Object} value - The credit card number to test.
74 | * @return {Object} The result of the test.
75 | */
76 | _getScheme: function(value) {
77 | value = (''+ value).replace(/\D/g, '');
78 |
79 | var i = this.schemes.length;
80 | while (i--) {
81 | if (this.schemes[i].regex.test(value)) {
82 | return this.schemes[i].scheme;
83 | }
84 | }
85 |
86 | return undefined;
87 | },
88 | /**
89 | * The method that is called by ApproveJs to perform the test.
90 | * @param {Object} value - The value to test.
91 | * @return {Object} The result object of the test.
92 | */
93 | validate: function(value) {
94 | value = (''+ value).replace(/\D/g, '');
95 |
96 | var card = new Card(),
97 | i = value.length,
98 | sum = 0,
99 | mul = 1,
100 | ca;
101 |
102 | // Not enough numbers. Shortest currently is 12.
103 | if (i < 12) {
104 | return false;
105 | }
106 |
107 | while (i--) {
108 | ca = value.charAt(i) * mul;
109 | sum += ca - (ca > 9) * 9;
110 | mul ^= 3;
111 | }
112 |
113 | card.valid = (sum % 10 === 0) && (sum > 0);
114 | card.scheme = this._getScheme(value);
115 |
116 | return card;
117 | }
118 | };
119 |
--------------------------------------------------------------------------------
/src/tests/approve.strength.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The result object containing the outcome of the strength test.
3 | */
4 | function Score(strength) {
5 | this.strength = strength;
6 | this.points = 0;
7 | this.isMinimum = false;
8 | this.hasLower = false;
9 | this.hasUpper = false;
10 | this.hasNumber = false;
11 | this.hasSpecial = false;
12 | this.isBonus = false;
13 | this.percent = 0;
14 | this.valid = false;
15 | this.errors = [];
16 | }
17 | /**
18 | * Checks if a value is a strong password string.
19 | */
20 | export default {
21 | /**
22 | * The minimum length a password must be.
23 | */
24 | minimum: 8,
25 | /**
26 | * The minimum length a password must be for a bonus point.
27 | */
28 | minimumBonus: 10,
29 | /**
30 | * The text representing the strength of a password.
31 | */
32 | strengths: {
33 | 0: 'Very Weak',
34 | 1: 'Weak',
35 | 2: 'Better',
36 | 3: 'Almost',
37 | 4: 'Acceptable',
38 | 5: 'Strong',
39 | 6: 'Very Strong'
40 | },
41 | /**
42 | * The default error message.
43 | */
44 | message: '{title} did not pass the strength test.',
45 | /**
46 | * Expects the 'min' and 'bonus' parameters.
47 | */
48 | expects: ['min', 'bonus'],
49 | /**
50 | * Default error messages
51 | */
52 | errors: {
53 | isMinimum: '{title} must be at least {min} characters',
54 | hasLower: '{title} must have at least 1 lower case character',
55 | hasUpper: '{title} must have at least 1 upper case character',
56 | hasNumber: '{title} must have at least 1 number',
57 | hasSpecial: '{title} must have at least 1 special character'
58 | },
59 | /**
60 | * Returns an object containing the score of a value.
61 | */
62 | _getScore: function(text) {
63 | // Create the object that represents the score of the text
64 | var result = new Score(this.strengths[0]);
65 | // If text is longer than minimum give 1 point.
66 | // If text is longer than minimumBonus give another 1 point.
67 | if (text.length > this.minimumBonus) {
68 | result.points += 2;
69 | result.isBonus = true;
70 | result.isMinimum = true;
71 | } else if (text.length > this.minimum){
72 | result.points++;
73 | result.isMinimum = true;
74 | } else {
75 | result.points = 1;
76 | result.isMinimum = false;
77 | }
78 | // If text has lowercase characters give 1 point.
79 | result.hasLower = text.match(/[a-z]/) === null ? false : true;
80 | if(result.isMinimum && result.hasLower) {
81 | result.points++;
82 | }
83 | // If text has uppercase characters give 1 point.
84 | result.hasUpper = text.match(/[A-Z]/) === null ? false : true;
85 | if(result.isMinimum && result.hasUpper) {
86 | result.points++;
87 | }
88 | // If text has at least one number give 1 point.
89 | result.hasNumber = text.match(/\d+/) === null ? false : true;
90 | if(result.isMinimum && result.hasNumber) {
91 | result.points++;
92 | }
93 | // If text has at least one special caracther give 1 point.
94 | result.hasSpecial = text.match(/.[!,@,#,$,%,^,&,*,?,_,~,-,(,)]/) === null ? false : true;
95 | if(result.isMinimum && result.hasSpecial) {
96 | result.points++;
97 | }
98 | // Set the percentage value.
99 | result.percent = Math.ceil((result.points / 6) * 100);
100 | // Return the score object.
101 | return result;
102 | },
103 | /**
104 | * Returns an object containing the score and validation of a value.
105 | */
106 | _getStrength: function (text) {
107 | var result = this._getScore(text);
108 | result.strength = this.strengths[result.points];
109 | if (!result.isMinimum) {
110 | result.errors.push(this.errors.isMinimum);
111 | }
112 | if (!result.hasLower) {
113 | result.errors.push(this.errors.hasLower);
114 | }
115 | if (!result.hasUpper) {
116 | result.errors.push(this.errors.hasUpper);
117 | }
118 | if (!result.hasSpecial) {
119 | result.errors.push(this.errors.hasSpecial);
120 | }
121 | if (!result.hasNumber) {
122 | result.errors.push(this.errors.hasNumber);
123 | }
124 | if (result.points > 4) {
125 | result.valid = true;
126 | }
127 | return result;
128 | },
129 | /**
130 | * The method that is called by ApproveJs to perform the test.
131 | */
132 | validate: function(value, pars) {
133 | this.minimum = pars.min || this.minimum;
134 | this.minimumBonus = pars.bonus || this.minimumBonus;
135 | if (pars.hasOwnProperty('config') && pars.config.hasOwnProperty('messages')) {
136 | for (var message in pars.config.messages) {
137 | if (pars.config.messages.hasOwnProperty(message)) {
138 | this.errors[message] = pars.config.messages[message];
139 | }
140 | }
141 | }
142 | return this._getStrength(value);
143 | }
144 | };
145 |
--------------------------------------------------------------------------------
/src/tests/approve.tests.js:
--------------------------------------------------------------------------------
1 | import cc from './approve.creditcard';
2 | import strength from './approve.strength';
3 |
4 | export default {
5 | /**
6 | * Checks if a value is present.
7 | */
8 | required: {
9 | validate: function(value) {
10 | return !!value;
11 | },
12 | message: '{title} is required',
13 | expects: false
14 | },
15 | /**
16 | * Checks if a value is a valid email address.
17 | */
18 | email: {
19 | regex: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i, // eslint-disable-line no-control-regex
20 | validate: function(value) {
21 | return this.regex.test(value);
22 | },
23 | message: '{title} must be a valid email address',
24 | expects: false
25 | },
26 | /**
27 | * Checks if a value is a valid web address.
28 | */
29 | url: {
30 | regex: /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/i,
31 | validate: function(value) {
32 | return this.regex.test(value);
33 | },
34 | message: '{title} must be a valid web address',
35 | expects: false
36 | },
37 | /**
38 | * Checks if a value contains both letters and numbers.
39 | */
40 | alphaNumeric: {
41 | regex: /^[A-Za-z0-9]+$/i,
42 | validate: function(value) {
43 | return this.regex.test(value);
44 | },
45 | message: '{title} may only contain [A-Za-z] and [0-9]',
46 | expects: false
47 | },
48 | /**
49 | * Checks if a value contains only numbers.
50 | */
51 | numeric: {
52 | regex: /^-?[0-9]+$/,
53 | validate: function(value) {
54 | return this.regex.test(value);
55 | },
56 | message: '{title} may only contain [0-9]',
57 | expects: false
58 | },
59 | /**
60 | * Checks if a value contains only letters.
61 | */
62 | alpha: {
63 | regex: /^[A-Za-z]+$/,
64 | validate: function(value) {
65 | return this.regex.test(value);
66 | },
67 | message: '{title} may only contain [A-Za-z]',
68 | expects: false
69 | },
70 | /**
71 | * Checks if a value is a valid decimal.
72 | */
73 | decimal: {
74 | regex: /^\s*(\+|-)?((\d+(\.\d+)?)|(\.\d+))\s*$/,
75 | validate: function(value) {
76 | return this.regex.test(value);
77 | },
78 | message: '{title} must be a valid decimal',
79 | expects: false
80 | },
81 | /**
82 | * Similar to 'decimal', but for currency values.
83 | */
84 | currency: {
85 | regex: /^\s*(\+|-)?((\d+(\.\d\d)?)|(\.\d\d))\s*$/,
86 | validate: function(value) {
87 | return this.regex.test(value);
88 | },
89 | message: '{title} must be a valid currency value',
90 | expects: false
91 | },
92 | /**
93 | * Checks if a value is a valid ipv4 or ipv6 address.
94 | */
95 | ip: {
96 | regex: {
97 | ipv4: /^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/,
98 | ipv4Cidr: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/,
99 | ipv6: /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i,
100 | ipv6Cidr: /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/
101 | },
102 | validate: function(value) {
103 | return this.regex.ipv4.test(value) || this.regex.ipv6.test(value) || this.regex.ipv4Cidr.test(value) || this.regex.ipv6Cidr.test(value);
104 | },
105 | message: '{title} must be a valid IP address',
106 | expects: false
107 | },
108 | /**
109 | * Checks if a value is a minimum of n characters.
110 | */
111 | min: {
112 | validate: function(value, pars) {
113 | return typeof value === 'string' && value.length >= pars.min;
114 | },
115 | message: '{title} must be a minimum of {min} characters',
116 | expects: ['min']
117 | },
118 | /**
119 | * Checks if a value is a maximum of n characters.
120 | */
121 | max: {
122 | validate: function(value, pars) {
123 | return typeof value === 'string' && value.length <= pars.max;
124 | },
125 | message: '{title} must be a maximum of {max} characters',
126 | expects: ['max']
127 | },
128 | /**
129 | * Checks if a string's length or number is between a minimum and maximum.
130 | */
131 | range: {
132 | validate: function(value, pars) {
133 | if (typeof value === 'string')
134 | {
135 | return value.length >= pars.min && value.length <= pars.max;
136 | } else if (typeof value === 'number') {
137 | return value >= pars.min && value <= pars.max;
138 | }
139 | return false;
140 | },
141 | message: '{title} must be a minimum of {min} and a maximum of {max} characters',
142 | expects: ['min', 'max']
143 | },
144 | /**
145 | * Checks if a value is the same as the value of another.
146 | */
147 | equal: {
148 | validate: function(value, pars) {
149 | return '' + value === '' + pars.value;
150 | },
151 | message: '{title} must be equal to {field}',
152 | expects: ['value', 'field']
153 | },
154 | /**
155 | * Checks if a value passes a given regular expression.
156 | */
157 | format: {
158 | validate: function(value, pars) {
159 | if (Object.prototype.toString.call(pars.regex) === '[object RegExp]') {
160 | return pars.regex.test(value);
161 | }
162 | throw 'approve.value(): [format] - regex is not a valid regular expression.';
163 | },
164 | message: '{title} did not pass the [{regex}] test',
165 | expects: ['regex']
166 | },
167 | /**
168 | * Checks if a value is a valid time string (hh:mm:ss).
169 | */
170 | time: {
171 | regex: /^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/,
172 | validate: function(value) {
173 | return this.regex.test(value);
174 | },
175 | message: '{title} is not a valid time',
176 | expects: false
177 | },
178 | /**
179 | * Checks if a value is a valid date.
180 | */
181 | date: {
182 | formats: {
183 | ymd: /^(?:\2)(?:[0-9]{2})?[0-9]{2}([\/-])(1[0-2]|0?[1-9])([\/-])(3[01]|[12][0-9]|0?[1-9])$/,
184 | dmy: /^(3[01]|[12][0-9]|0?[1-9])([\/-])(1[0-2]|0?[1-9])([\/-])(?:[0-9]{2})?[0-9]{2}$/
185 | },
186 | validate: function(value, pars) {
187 | return this.formats[pars.format].test(value);
188 | },
189 | message: '{title} is not a valid date',
190 | expects: ['format']
191 | },
192 | /**
193 | * Checks if a value is truthy ('yes', 'true', 'ok[ay]', '1').
194 | */
195 | truthy: {
196 | regex: /^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/i,
197 | validate: function(value) {
198 | return this.regex.test(value);
199 | },
200 | message: '{title} is not valid',
201 | expects: false
202 | },
203 | /**
204 | * Checks if a value is falsy ('No', 'false', '0').
205 | */
206 | falsy: {
207 | regex: /^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/i,
208 | validate: function(value) {
209 | return !this.regex.test(value);
210 | },
211 | message: '{title} is not valid',
212 | expects: false
213 | },
214 | cc: cc,
215 | strength: strength
216 | };
217 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var expect = require('chai').expect;
4 | var approve = require('../dist/approve.js');
5 |
6 | // The main library
7 | describe('ApproveJs', function() {
8 | it('should be accessible from \'approve\' variable', function() {
9 | var is = approve ? true : false;
10 | expect(is).to.equal(true);
11 | });
12 | // The tests
13 | describe('Tests', function() {
14 | // required
15 | it('should be able to approve a required value', function() {
16 | var is = approve.value('Hello', {required: true}).approved,
17 | not = approve.value('', {required: true}).approved;
18 | expect(is).to.equal(true);
19 | expect(not).to.equal(false);
20 | });
21 | // email
22 | it('should be able to approve an email address', function() {
23 | var is = approve.value('user@domain.com', {email: true}).approved,
24 | not = approve.value('fake.email', {email: true}).approved;
25 | expect(is).to.equal(true);
26 | expect(not).to.equal(false);
27 | });
28 | // url
29 | it('should be able to approve a web address', function() {
30 | var dom1 = approve.value('http://domain.com', {url: true}).approved,
31 | dom2 = approve.value('http://www.domain.com', {url: true}).approved,
32 | dom3 = approve.value('www.domain.com', {url: true}).approved,
33 | dom4 = approve.value('www.domain.com/page', {url: true}).approved,
34 | dom5 = approve.value('www.domain.com/?hello=world', {url: true}).approved,
35 | dom6 = approve.value('www.domain.com/#fragment', {url: true}).approved,
36 | is = dom1 && dom2 && dom3 && dom4 && dom5 && dom6,
37 | not = approve.value('hello world', {url: true}).approved;
38 | expect(is).to.equal(true);
39 | expect(not).to.equal(false);
40 | });
41 | // cc
42 | it('should be able to approve a credit card number', function() {
43 | var testnumbers = [
44 | 4222222222222,
45 | 4012888888881881,
46 | 4111111111111111,
47 | 5105105105105100,
48 | 5555555555554444,
49 | 3566002020360505,
50 | 3530111333300000,
51 | 6011000990139424,
52 | 6011111111111117,
53 | 6011601160116611,
54 | 38520000023237,
55 | 30569309025904,
56 | 378734493671000,
57 | 371449635398431,
58 | 378282246310005,
59 | 341111111111111,
60 | 5431111111111111,
61 | 5610591081018250,
62 | 5019717010103742,
63 | 6331101999990016
64 | ],
65 | is = true,
66 | not = approve.value('6356565786896346', {cc: true}).approved;
67 | var rule = {
68 | cc: true
69 | };
70 | for (var i = testnumbers.length - 1; i >= 0; i--) {
71 | if (!approve.value(testnumbers[i], rule).approved) {
72 | is = false;
73 | }
74 | };
75 | expect(is).to.equal(true);
76 | expect(not).to.equal(false);
77 | });
78 | // alphaNumeric
79 | it('should be able to approve alpha numeric characters', function() {
80 | var is = approve.value('000', {alphaNumeric: true}).approved,
81 | not = approve.value('Hello World', {alphaNumeric: true}).approved;
82 | expect(is).to.equal(true);
83 | expect(not).to.equal(false);
84 | });
85 | // numeric
86 | it('should be able to approve numeric characters', function() {
87 | var is = approve.value('000', {numeric: true}).approved,
88 | not = approve.value('aaa', {numeric: true}).approved;
89 | expect(is).to.equal(true);
90 | expect(not).to.equal(false);
91 | });
92 | // alpha
93 | it('should be able to approve alpha characters', function() {
94 | var is = approve.value('aaa', {alpha: true}).approved,
95 | not = approve.value('000', {alpha: true}).approved;
96 | expect(is).to.equal(true);
97 | expect(not).to.equal(false);
98 | });
99 | // decimal
100 | it('should be able to approve decimal values', function() {
101 | var is = approve.value('10.000', {decimal: true}).approved,
102 | not = approve.value('1,0', {decimal: true}).approved;
103 | expect(is).to.equal(true);
104 | expect(not).to.equal(false);
105 | });
106 | // currency
107 | it('should be able to approve currency values', function() {
108 | var cur1 = approve.value('10.00', {currency: true}).approved,
109 | cur2 = approve.value('10', {currency: true}).approved,
110 | is = cur1 && cur2,
111 | not = approve.value('10.000', {currency: true}).approved;
112 | expect(is).to.equal(true);
113 | expect(not).to.equal(false);
114 | });
115 | // ip
116 | it('should be able to approve ip addresses', function() {
117 | var ipv4 = approve.value('172.16.112.50', {ip: true}).approved,
118 | ipv4Cidr = approve.value('172.16.112.50/11', {ip: true}).approved,
119 | ipv6 = approve.value('3ffe:1900:4545:3:200:f8ff:fe21:67cf', {ip: true}).approved,
120 | ipv6Cidr = approve.value('0000:0000:0000:0000:0000:0000:0000:0000/19', {ip: true}).approved,
121 | is = ipv4 && ipv6 && ipv4Cidr && ipv6Cidr,
122 | not = approve.value('172.16.5.2.40', {ip: true}).approved;
123 | expect(is).to.equal(true);
124 | expect(not).to.equal(false);
125 | });
126 | // min
127 | it('should be able to approve minimum length', function() {
128 | var is = approve.value('123456', {min: 6}).approved,
129 | not = approve.value('12345', {min: 6}).approved;
130 | expect(is).to.equal(true);
131 | expect(not).to.equal(false);
132 | });
133 | // max
134 | it('should be able to approve maximum length', function() {
135 | var is = approve.value('123456', {max: 6}).approved,
136 | not = approve.value('1234567', {max: 6}).approved;
137 | expect(is).to.equal(true);
138 | expect(not).to.equal(false);
139 | });
140 | // range
141 | it('should be able to approve a string\'s length or number between minimum and maximum', function() {
142 | var is = approve.value('1234567', {range: {min: 6, max: 8}}).approved,
143 | tooShort = approve.value('12345', {range: {min: 6, max: 8}}).approved,
144 | tooLong = approve.value('123456789', {range: {min: 6, max: 8}}).approved,
145 | numFail = approve.value(10, {range: {min: 6, max: 8}}).approved,
146 | numPass = approve.value(7, {range: {min: 6, max: 8}}).approved,
147 | not = !!tooShort && !!tooLong;
148 | expect(is).to.equal(true);
149 | expect(not).to.equal(false);
150 | expect(numFail).to.equal(false);
151 | expect(numPass).to.equal(true);
152 | });
153 | // equal
154 | it('should be able to approve that values are equal', function() {
155 | var is = approve.value('123456', {equal: {value: '123456', field: 'is equal'}}).approved,
156 | not = approve.value('1234567', {equal: {value: '123456', field: 'is not equal'}}).approved;
157 | expect(is).to.equal(true);
158 | expect(not).to.equal(false);
159 | });
160 | // format
161 | it('should be able to approve with a custom format', function() {
162 | var is = approve.value('AbCd', {format: /^[A-Za-z]+$/}).approved,
163 | not = approve.value('12345', {format: /^[A-Za-z]+$/}).approved;
164 | expect(is).to.equal(true);
165 | expect(not).to.equal(false);
166 | });
167 | // time
168 | it('should be able to approve time strings', function() {
169 | var is = approve.value('12:25:30', {time: true}).approved,
170 | not = approve.value('000', {time: true}).approved;
171 | expect(is).to.equal(true);
172 | expect(not).to.equal(false);
173 | });
174 | // date
175 | it('should be able to approve date strings', function() {
176 | var isymd = approve.value('2016-10-25', {date: 'ymd'}).approved,
177 | isdmy = approve.value('31/10/16', {date: {format: 'dmy'}}).approved,
178 | notymd = approve.value('2016-25-23', {date: 'ymd'}).approved,
179 | notdmy = approve.value('25-23-16', {date: {format: 'dmy'}}).approved;
180 | expect(isymd).to.equal(true);
181 | expect(isdmy).to.equal(true);
182 | expect(notymd).to.equal(false);
183 | expect(notdmy).to.equal(false);
184 | });
185 | // truthy
186 | it('should be able to approve truthy values', function() {
187 | var yes = approve.value('yes', {truthy: true}).approved,
188 | truth = approve.value('true', {truthy: true}).approved,
189 | one = approve.value('1', {truthy: true}).approved,
190 | ok = approve.value('ok', {truthy: true}).approved,
191 | is = yes && truth && one && ok,
192 | no = approve.value('no', {truthy: true}).approved,
193 | falsy = approve.value('false', {truthy: true}).approved,
194 | zero = approve.value('0', {truthy: true}).approved,
195 | notOk = approve.value('nope', {truthy: true}).approved,
196 | not = !!no && !!falsy && !!zero && !!notOk;
197 | expect(is).to.equal(true);
198 | expect(not).to.equal(false);
199 | });
200 | // falsy
201 | it('should be able to approve falsy values', function() {
202 | var yes = approve.value('yes', {falsy: true}).approved,
203 | truth = approve.value('true', {falsy: true}).approved,
204 | one = approve.value('1', {falsy: true}).approved,
205 | ok = approve.value('ok', {falsy: true}).approved,
206 | not = !!yes && !!truth && !!one && !!ok,
207 | no = approve.value('no', {falsy: true}).approved,
208 | falsy = approve.value('false', {falsy: true}).approved,
209 | zero = approve.value('0', {falsy: true}).approved,
210 | notOk = approve.value('nope', {falsy: true}).approved,
211 | is = no && falsy && zero && notOk;
212 |
213 | expect(is).to.equal(true);
214 | expect(not).to.equal(false);
215 | });
216 | // strength
217 | it('should be able to approve the strength of a password', function() {
218 | var rule = {
219 | strength: {
220 | min: 6,
221 | bonus: 7
222 | }
223 | };
224 | var result = approve.value('th!sIsaStr0ngPas$w0rd', rule),
225 | is = result.approved,
226 | has = result.strength.hasOwnProperty('points'),
227 | not = approve.value('Pfft!', rule).approved;
228 | expect(is).to.equal(true);
229 | expect(has).to.equal(true);
230 | expect(not).to.equal(false);
231 | });
232 | // multiple
233 | it('should be able to approve multiple tests', function() {
234 | var rule = {
235 | required: true,
236 | min: 6,
237 | max: 12
238 | };
239 | var is = approve.value('lorem ipsum', rule).approved,
240 | invalidMin = approve.value('lorem', rule).approved,
241 | invalidMax = approve.value('lorem ipsum dolar', rule).approved,
242 | not = approve.value('', rule).approved;
243 | expect(is).to.equal(true);
244 | expect(invalidMin).to.equal(false);
245 | expect(invalidMax).to.equal(false);
246 | expect(not).to.equal(false);
247 | });
248 | // stop
249 | it('should be able to stop after first failed test if stop = true', function() {
250 | var rule = {
251 | stop: true,
252 | email: true,
253 | required: true
254 | };
255 | var result = approve.value('', rule),
256 | stopped = result.errors.length === 1;
257 |
258 | expect(stopped).to.equal(true);
259 | });
260 | // continue
261 | it('should be able to continue all tests if stop = false', function() {
262 | var ruleFalse = {
263 | stop: false,
264 | required: true,
265 | email: true
266 | };
267 | var ruleAbsent = {
268 | required: true,
269 | email: true
270 | };
271 | var resultFalse = approve.value('', ruleFalse),
272 | resultAbsent = approve.value('', ruleAbsent),
273 | continuedFalse = resultFalse.errors.length > 1,
274 | continuedAbsent = resultAbsent.errors.length > 1;
275 |
276 | expect(continuedFalse).to.equal(true);
277 | expect(continuedAbsent).to.equal(true);
278 | });
279 | // ignore null value
280 | it('should be able to ignore null values', function() {
281 | var rule = {
282 | ignoreNull: true,
283 | email: true,
284 | required: true
285 | };
286 | var result = approve.value(null, rule),
287 | ignored = result.errors.length === 0;
288 |
289 | expect(ignored).to.equal(true);
290 | });
291 | // do not ignore null
292 | it('should be able to test null values', function() {
293 | var rule = {
294 | email: true,
295 | required: true
296 | };
297 | var result = approve.value(null, rule),
298 | ignored = result.errors.length > 0;
299 |
300 | expect(ignored).to.equal(true);
301 | });
302 | // errors
303 | it('should be able to correctly format an error message', function() {
304 | var fromProp = approve.value('not an email', {email: true, title: 'Email'}),
305 | noTitle = approve.value('not an email', {email: true}),
306 | isFromProp = fromProp.errors[0] === 'Email must be a valid email address',
307 | isNoTitle = noTitle.errors[0] === 'must be a valid email address';
308 | expect(isFromProp).to.equal(true);
309 | expect(isNoTitle).to.equal(true);
310 | });
311 | // config
312 | it('should be configurable', function() {
313 | var rule = {
314 | title: 'password',
315 | strength: {
316 | min: 6,
317 | bonus: 7,
318 | config: {
319 | messages: {
320 | hasLower: 'At least one lower case letter expected from {title}'
321 | }
322 | }
323 | }
324 | };
325 | var result = approve.value('@THISI$ASTR0NGPASSW0RD', rule),
326 | is = result.errors[0] === 'At least one lower case letter expected from password';
327 | expect(is).to.equal(true);
328 | });
329 | // =========================================
330 | // Add additional / new test testing here
331 | // =========================================
332 |
333 | // it('should ...', function() {
334 | // // Get test results
335 | // expect(is).to.equal(true);
336 | // expect(not).to.equal(false);
337 | // });
338 | });
339 | describe('Results', function() {
340 | // result
341 | it('should contain test result properties', function() {
342 | var rule = {
343 | required: true,
344 | email: true
345 | };
346 | var is = approve.value('example@domain.com', rule).required.approved,
347 | not = approve.value('lorem ipsum', rule).email.approved;
348 | expect(is).to.equal(true);
349 | expect(not).to.equal(false);
350 | });
351 | // failed
352 | it('should contain failed tests array', function() {
353 | var rule = {
354 | required: true,
355 | email: true
356 | };
357 | var result = approve.value('', rule),
358 | is = result.failed.length > 0;
359 | expect(is).to.equal(true);
360 | });
361 | });
362 | // =========================================
363 | // Add additional / new feature testing here
364 | // =========================================
365 |
366 | // describe('[new_feature]', function() {
367 | // it('should ...', function() {
368 | // // Test feature
369 | // expect(is).to.equal(true);
370 | // expect(not).to.equal(false);
371 | // });
372 | // });
373 | });
374 |
--------------------------------------------------------------------------------