UNPKG

14.3 kBJavaScriptView Raw
1function leapYear(year) {
2 return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0);
3}
4
5function isValidDate(inDate) {
6 var valid = true;
7
8 // reformat if supplied as mm.dd.yyyy (period delimiter)
9 if (typeof inDate === 'string') {
10 var pos = inDate.indexOf('.');
11 if ((pos > 0 && pos <= 6)) {
12 inDate = inDate.replace(/\./g, '-');
13 }
14 }
15
16 var testDate = new Date(inDate);
17 var yr = testDate.getFullYear();
18 var mo = testDate.getMonth();
19 var day = testDate.getDate();
20
21 var daysInMonth = [31, (leapYear(yr) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
22
23 if (yr < 1000) { return false; }
24 if (isNaN(mo)) { return false; }
25 if (mo + 1 > 12) { return false; }
26 if (isNaN(day)) { return false; }
27 if (day > daysInMonth[mo]) { return false; }
28
29 return valid;
30}
31
32var rules = {
33
34 required: function(val) {
35 var str;
36
37 if (val === undefined || val === null) {
38 return false;
39 }
40
41 str = String(val).replace(/\s/g, "");
42 return str.length > 0 ? true : false;
43 },
44
45 required_if: function(val, req, attribute) {
46 req = this.getParameters();
47 if (this.validator._objectPath(this.validator.input, req[0]) === req[1]) {
48 return this.validator.getRule('required').validate(val);
49 }
50
51 return true;
52 },
53
54 required_unless: function(val, req, attribute) {
55 req = this.getParameters();
56 if (this.validator._objectPath(this.validator.input, req[0]) !== req[1]) {
57 return this.validator.getRule('required').validate(val);
58 }
59
60 return true;
61 },
62
63 required_with: function(val, req, attribute) {
64 if (this.validator._objectPath(this.validator.input, req)) {
65 return this.validator.getRule('required').validate(val);
66 }
67
68 return true;
69 },
70
71 required_with_all: function(val, req, attribute) {
72
73 req = this.getParameters();
74
75 for(var i = 0; i < req.length; i++) {
76 if (!this.validator._objectPath(this.validator.input, req[i])) {
77 return true;
78 }
79 }
80
81 return this.validator.getRule('required').validate(val);
82 },
83
84 required_without: function(val, req, attribute) {
85
86 if (this.validator._objectPath(this.validator.input, req)) {
87 return true;
88 }
89
90 return this.validator.getRule('required').validate(val);
91 },
92
93 required_without_all: function(val, req, attribute) {
94
95 req = this.getParameters();
96
97 for(var i = 0; i < req.length; i++) {
98 if (this.validator._objectPath(this.validator.input, req[i])) {
99 return true;
100 }
101 }
102
103 return this.validator.getRule('required').validate(val);
104 },
105
106 'boolean': function (val) {
107 return (
108 val === true ||
109 val === false ||
110 val === 0 ||
111 val === 1 ||
112 val === '0' ||
113 val === '1' ||
114 val === 'true' ||
115 val === 'false'
116 );
117 },
118
119 // compares the size of strings
120 // with numbers, compares the value
121 size: function(val, req, attribute) {
122 if (val) {
123 req = parseFloat(req);
124
125 var size = this.getSize();
126
127 return size === req;
128 }
129
130 return true;
131 },
132
133 string: function(val, req, attribute) {
134 return typeof val === 'string';
135 },
136
137 sometimes: function(val) {
138 return true;
139 },
140
141 /**
142 * Compares the size of strings or the value of numbers if there is a truthy value
143 */
144 min: function(val, req, attribute) {
145 var size = this.getSize();
146 return size >= req;
147 },
148
149 /**
150 * Compares the size of strings or the value of numbers if there is a truthy value
151 */
152 max: function(val, req, attribute) {
153 var size = this.getSize();
154 return size <= req;
155 },
156
157 between: function(val, req, attribute) {
158 req = this.getParameters();
159 var size = this.getSize();
160 var min = parseFloat(req[0], 10);
161 var max = parseFloat(req[1], 10);
162 return size >= min && size <= max;
163 },
164
165 email: function(val) {
166 var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
167 return re.test(val);
168 },
169
170 numeric: function(val) {
171 var num;
172
173 num = Number(val); // tries to convert value to a number. useful if value is coming from form element
174
175 if (typeof num === 'number' && !isNaN(num) && typeof val !== 'boolean') {
176 return true;
177 } else {
178 return false;
179 }
180 },
181
182 array: function(val) {
183 return val instanceof Array;
184 },
185
186 url: function(url) {
187 return (/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/i).test(url);
188 },
189
190 alpha: function(val) {
191 return (/^[a-zA-Z]+$/).test(val);
192 },
193
194 alpha_dash: function(val) {
195 return (/^[a-zA-Z0-9_\-]+$/).test(val);
196 },
197
198 alpha_num: function(val) {
199 return (/^[a-zA-Z0-9]+$/).test(val);
200 },
201
202 same: function(val, req) {
203 var val1 = this.validator._flattenObject(this.validator.input)[req];
204 var val2 = val;
205
206 if (val1 === val2) {
207 return true;
208 }
209
210 return false;
211 },
212
213 different: function(val, req) {
214 var val1 = this.validator._flattenObject(this.validator.input)[req];
215 var val2 = val;
216
217 if (val1 !== val2) {
218 return true;
219 }
220
221 return false;
222 },
223
224 "in": function(val, req) {
225 var list, i;
226
227 if (val) {
228 list = this.getParameters();
229 }
230
231 if (val && !(val instanceof Array)) {
232 var localValue = val;
233
234 for (i = 0; i < list.length; i++) {
235 if (typeof list[i] === 'string') {
236 localValue = String(val);
237 }
238
239 if (localValue === list[i]) {
240 return true;
241 }
242 }
243
244 return false;
245 }
246
247 if (val && val instanceof Array) {
248 for (i = 0; i < val.length; i++) {
249 if (list.indexOf(val[i]) < 0) {
250 return false;
251 }
252 }
253 }
254
255 return true;
256 },
257
258 not_in: function(val, req) {
259 var list = this.getParameters();
260 var len = list.length;
261 var returnVal = true;
262
263 for (var i = 0; i < len; i++) {
264 var localValue = val;
265
266 if (typeof list[i] === 'string') {
267 localValue = String(val);
268 }
269
270 if (localValue === list[i]) {
271 returnVal = false;
272 break;
273 }
274 }
275
276 return returnVal;
277 },
278
279 accepted: function(val) {
280 if (val === 'on' || val === 'yes' || val === 1 || val === '1' || val === true) {
281 return true;
282 }
283
284 return false;
285 },
286
287 confirmed: function(val, req, key) {
288 var confirmedKey = key + '_confirmation';
289
290 if (this.validator.input[confirmedKey] === val) {
291 return true;
292 }
293
294 return false;
295 },
296
297 integer: function(val) {
298 return String(parseInt(val, 10)) === String(val);
299 },
300
301 digits: function(val, req) {
302 var numericRule = this.validator.getRule('numeric');
303 if (numericRule.validate(val) && String(val).length === parseInt(req)) {
304 return true;
305 }
306
307 return false;
308 },
309
310 regex: function(val, req) {
311 var mod = /[g|i|m]{1,3}$/;
312 var flag = req.match(mod);
313 flag = flag ? flag[0] : "";
314 req = req.replace(mod, "").slice(1, -1);
315 req = new RegExp(req, flag);
316 return !!val.match(req);
317 },
318
319 date: function(val, format) {
320 return isValidDate(val);
321 },
322
323 present: function(val) {
324 return typeof val !== 'undefined';
325 },
326
327 after: function(val, req){
328 var val1 = this.validator.input[req];
329 var val2 = val;
330
331 if(!isValidDate(val1)){ return false;}
332 if(!isValidDate(val2)){ return false;}
333
334 if (new Date(val1).getTime() < new Date(val2).getTime()) {
335 return true;
336 }
337
338 return false;
339 },
340
341 after_or_equal: function(val, req){
342 var val1 = this.validator.input[req];
343 var val2 = val;
344
345 if(!isValidDate(val1)){ return false;}
346 if(!isValidDate(val2)){ return false;}
347
348 if (new Date(val1).getTime() <= new Date(val2).getTime()) {
349 return true;
350 }
351
352 return false;
353 },
354
355 before: function(val, req){
356 var val1 = this.validator.input[req];
357 var val2 = val;
358
359 if(!isValidDate(val1)){ return false;}
360 if(!isValidDate(val2)){ return false;}
361
362 if (new Date(val1).getTime() > new Date(val2).getTime()) {
363 return true;
364 }
365
366 return false;
367 },
368
369 before_or_equal: function(val, req){
370 var val1 = this.validator.input[req];
371 var val2 = val;
372
373 if(!isValidDate(val1)){ return false;}
374 if(!isValidDate(val2)){ return false;}
375
376 if (new Date(val1).getTime() >= new Date(val2).getTime()) {
377 return true;
378 }
379
380 return false;
381 }
382
383
384};
385
386var missedRuleValidator = function() {
387 throw new Error('Validator `' + this.name + '` is not defined!');
388};
389var missedRuleMessage;
390
391function Rule(name, fn, async) {
392 this.name = name;
393 this.fn = fn;
394 this.passes = null;
395 this._customMessage = undefined;
396 this.async = async;
397}
398
399Rule.prototype = {
400
401 /**
402 * Validate rule
403 *
404 * @param {mixed} inputValue
405 * @param {mixed} ruleValue
406 * @param {string} attribute
407 * @param {function} callback
408 * @return {boolean|undefined}
409 */
410 validate: function(inputValue, ruleValue, attribute, callback) {
411 var _this = this;
412 this._setValidatingData(attribute, inputValue, ruleValue);
413 if (typeof callback === 'function') {
414 this.callback = callback;
415 var handleResponse = function(passes, message) {
416 _this.response(passes, message);
417 };
418
419 if (this.async) {
420 return this._apply(inputValue, ruleValue, attribute, handleResponse);
421 } else {
422 return handleResponse(this._apply(inputValue, ruleValue, attribute));
423 }
424 }
425 return this._apply(inputValue, ruleValue, attribute);
426 },
427
428 /**
429 * Apply validation function
430 *
431 * @param {mixed} inputValue
432 * @param {mixed} ruleValue
433 * @param {string} attribute
434 * @param {function} callback
435 * @return {boolean|undefined}
436 */
437 _apply: function(inputValue, ruleValue, attribute, callback) {
438 var fn = this.isMissed() ? missedRuleValidator : this.fn;
439
440 return fn.apply(this, [inputValue, ruleValue, attribute, callback]);
441 },
442
443 /**
444 * Set validating data
445 *
446 * @param {string} attribute
447 * @param {mixed} inputValue
448 * @param {mixed} ruleValue
449 * @return {void}
450 */
451 _setValidatingData: function(attribute, inputValue, ruleValue) {
452 this.attribute = attribute;
453 this.inputValue = inputValue;
454 this.ruleValue = ruleValue;
455 },
456
457 /**
458 * Get parameters
459 *
460 * @return {array}
461 */
462 getParameters: function() {
463 var value = [];
464
465 if (typeof this.ruleValue === 'string') {
466 value = this.ruleValue.split(',');
467 }
468
469 if (typeof this.ruleValue === 'number') {
470 value.push(this.ruleValue);
471 }
472
473 if (this.ruleValue instanceof Array) {
474 value = this.ruleValue;
475 }
476
477 return value;
478 },
479
480 /**
481 * Get true size of value
482 *
483 * @return {integer|float}
484 */
485 getSize: function() {
486 var value = this.inputValue;
487
488 if (value instanceof Array) {
489 return value.length;
490 }
491
492 if (typeof value === 'number') {
493 return value;
494 }
495
496 if (this.validator._hasNumericRule(this.attribute)) {
497 return parseFloat(value, 10);
498 }
499
500 return value.length;
501 },
502
503 /**
504 * Get the type of value being checked; numeric or string.
505 *
506 * @return {string}
507 */
508 _getValueType: function() {
509
510 if (typeof this.inputValue === 'number' || this.validator._hasNumericRule(this.attribute)) {
511 return 'numeric';
512 }
513
514 return 'string';
515 },
516
517 /**
518 * Set the async callback response
519 *
520 * @param {boolean|undefined} passes Whether validation passed
521 * @param {string|undefined} message Custom error message
522 * @return {void}
523 */
524 response: function(passes, message) {
525 this.passes = (passes === undefined || passes === true);
526 this._customMessage = message;
527 this.callback(this.passes, message);
528 },
529
530 /**
531 * Set validator instance
532 *
533 * @param {Validator} validator
534 * @return {void}
535 */
536 setValidator: function(validator) {
537 this.validator = validator;
538 },
539
540 /**
541 * Check if rule is missed
542 *
543 * @return {boolean}
544 */
545 isMissed: function() {
546 return typeof this.fn !== 'function';
547 },
548
549 get customMessage() {
550 return this.isMissed() ? missedRuleMessage : this._customMessage;
551 }
552};
553
554var manager = {
555
556 /**
557 * List of async rule names
558 *
559 * @type {Array}
560 */
561 asyncRules: [],
562
563 /**
564 * Implicit rules (rules to always validate)
565 *
566 * @type {Array}
567 */
568 implicitRules: ['required', 'required_if', 'required_unless', 'required_with', 'required_with_all', 'required_without', 'required_without_all', 'accepted', 'present'],
569
570 /**
571 * Get rule by name
572 *
573 * @param {string} name
574 * @param {Validator}
575 * @return {Rule}
576 */
577 make: function(name, validator) {
578 var async = this.isAsync(name);
579 var rule = new Rule(name, rules[name], async);
580 rule.setValidator(validator);
581 return rule;
582 },
583
584 /**
585 * Determine if given rule is async
586 *
587 * @param {string} name
588 * @return {boolean}
589 */
590 isAsync: function(name) {
591 for (var i = 0, len = this.asyncRules.length; i < len; i++) {
592 if (this.asyncRules[i] === name) {
593 return true;
594 }
595 }
596 return false;
597 },
598
599 /**
600 * Determine if rule is implicit (should always validate)
601 *
602 * @param {string} name
603 * @return {boolean}
604 */
605 isImplicit: function(name) {
606 return this.implicitRules.indexOf(name) > -1;
607 },
608
609 /**
610 * Register new rule
611 *
612 * @param {string} name
613 * @param {function} fn
614 * @return {void}
615 */
616 register: function(name, fn) {
617 rules[name] = fn;
618 },
619
620 /**
621 * Register new implicit rule
622 *
623 * @param {string} name
624 * @param {function} fn
625 * @return {void}
626 */
627 registerImplicit: function(name, fn) {
628 this.register(name, fn);
629 this.implicitRules.push(name);
630 },
631
632 /**
633 * Register async rule
634 *
635 * @param {string} name
636 * @param {function} fn
637 * @return {void}
638 */
639 registerAsync: function(name, fn) {
640 this.register(name, fn);
641 this.asyncRules.push(name);
642 },
643
644 /**
645 * Register implicit async rule
646 *
647 * @param {string} name
648 * @param {function} fn
649 * @return {void}
650 */
651 registerAsyncImplicit: function(name, fn) {
652 this.registerImplicit(name, fn);
653 this.asyncRules.push(name);
654 },
655
656 registerMissedRuleValidator: function(fn, message) {
657 missedRuleValidator = fn;
658 missedRuleMessage = message;
659 }
660};
661
662
663
664module.exports = manager;