UNPKG

@yolain/validation

Version:

Validation library inspired by Laravel's Validator

736 lines (606 loc) 15.5 kB
var isDateValid = require('date-fns/isValid'); var parseISO = require('date-fns/parseISO'); function leapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; } function isValidDate(inDate) { if (inDate instanceof Date) { return !isNaN(inDate); } // reformat if supplied as mm.dd.yyyy (period delimiter) if (typeof inDate === 'string') { var pos = inDate.indexOf('.'); if (pos > 0 && pos <= 6) { inDate = inDate.replace(/\./g, '-'); } if (inDate.length === 10) { return isDateValid(parseISO(inDate)); } } var testDate = new Date(inDate); var yr = testDate.getFullYear(); var mo = testDate.getMonth(); var day = testDate.getDate(); var daysInMonth = [31, leapYear(yr) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; if (yr < 1000) { return false; } if (isNaN(mo)) { return false; } if (mo + 1 > 12) { return false; } if (isNaN(day)) { return false; } if (day > daysInMonth[mo]) { return false; } return true; } function isJson(str){ if (typeof str == 'string') { try { let obj=JSON.parse(str); if(typeof obj == 'object' && obj ){ return true; }else{ return false; } } catch(e) { //console.log('json:'+str+'!!!'+e); return false; } } } var rules = { required: function(val) { var str; if (val === undefined || val === null) { return false; } str = String(val).replace(/\s/g, ''); return str.length > 0 ? true : false; }, required_if: function(val, req, attribute) { req = this.getParameters(); if (this.validator._objectPath(this.validator.input, req[0]) === req[1]) { return this.validator.getRule('required').validate(val); } return true; }, required_unless: function(val, req, attribute) { req = this.getParameters(); if (this.validator._objectPath(this.validator.input, req[0]) !== req[1]) { return this.validator.getRule('required').validate(val); } return true; }, required_with: function(val, req, attribute) { if (this.validator._objectPath(this.validator.input, req)) { return this.validator.getRule('required').validate(val); } return true; }, required_with_all: function(val, req, attribute) { req = this.getParameters(); for (var i = 0; i < req.length; i++) { if (!this.validator._objectPath(this.validator.input, req[i])) { return true; } } return this.validator.getRule('required').validate(val); }, required_without: function(val, req, attribute) { if (this.validator._objectPath(this.validator.input, req)) { return true; } return this.validator.getRule('required').validate(val); }, required_without_all: function(val, req, attribute) { req = this.getParameters(); for (var i = 0; i < req.length; i++) { if (this.validator._objectPath(this.validator.input, req[i])) { return true; } } return this.validator.getRule('required').validate(val); }, boolean: function(val) { return ( val === true || val === false || val === 0 || val === 1 || val === '0' || val === '1' || val === 'true' || val === 'false' ); }, // compares the size of strings // with numbers, compares the value size: function(val, req, attribute) { if (val) { req = parseFloat(req); var size = this.getSize(); return size === req; } return true; }, string: function(val, req, attribute) { return typeof val === 'string'; }, sometimes: function(val) { return true; }, /** * Compares the size of strings or the value of numbers if there is a truthy value */ min: function(val, req, attribute) { var size = this.getSize(); return size >= req; }, /** * Compares the size of strings or the value of numbers if there is a truthy value */ max: function(val, req, attribute) { var size = this.getSize(); return size <= req; }, between: function(val, req, attribute) { req = this.getParameters(); var size = this.getSize(); var min = parseFloat(req[0], 10); var max = parseFloat(req[1], 10); return size >= min && size <= max; }, email: function(val) { 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,}))$/; return re.test(val); }, numeric: function(val) { var num; num = Number(val); // tries to convert value to a number. useful if value is coming from form element if (typeof num === 'number' && !isNaN(num) && typeof val !== 'boolean') { return true; } else { return false; } }, array: function(val) { return val instanceof Array; }, url: function(url) { return /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)/i.test(url); }, alpha: function(val) { return /^[a-zA-Z]+$/.test(val); }, alpha_dash: function(val) { return /^[a-zA-Z0-9_\-]+$/.test(val); }, alpha_num: function(val) { return /^[a-zA-Z0-9]+$/.test(val); }, same: function(val, req) { var val1 = this.validator._flattenObject(this.validator.input)[req]; var val2 = val; if (val1 === val2) { return true; } return false; }, different: function(val, req) { var val1 = this.validator._flattenObject(this.validator.input)[req]; var val2 = val; if (val1 !== val2) { return true; } return false; }, in: function(val, req) { var list, i; if (val) { list = this.getParameters(); } if (val && !(val instanceof Array)) { var localValue = val; for (i = 0; i < list.length; i++) { if (typeof list[i] === 'string') { localValue = String(val); } if (localValue === list[i]) { return true; } } return false; } if (val && val instanceof Array) { for (i = 0; i < val.length; i++) { if (list.indexOf(val[i]) < 0) { return false; } } } return true; }, not_in: function(val, req) { var list = this.getParameters(); var len = list.length; var returnVal = true; for (var i = 0; i < len; i++) { var localValue = val; if (typeof list[i] === 'string') { localValue = String(val); } if (localValue === list[i]) { returnVal = false; break; } } return returnVal; }, accepted: function(val) { if (val === 'on' || val === 'yes' || val === 1 || val === '1' || val === true) { return true; } return false; }, confirmed: function(val, req, key) { var confirmedKey = key + '_confirmation'; if (this.validator.input[confirmedKey] === val) { return true; } return false; }, integer: function(val) { return String(parseInt(val, 10)) === String(val); }, digits: function(val, req) { var numericRule = this.validator.getRule('numeric'); if (numericRule.validate(val) && String(val).length === parseInt(req)) { return true; } return false; }, digits_between: function(val) { var numericRule = this.validator.getRule('numeric'); var req = this.getParameters(); var valueDigitsCount = String(val).length; var min = parseFloat(req[0], 10); var max = parseFloat(req[1], 10); if (numericRule.validate(val) && valueDigitsCount >= min && valueDigitsCount <= max) { return true; } return false; }, regex: function(val, req) { var mod = /[g|i|m]{1,3}$/; var flag = req.match(mod); flag = flag ? flag[0] : ''; req = req.replace(mod, '').slice(1, -1); req = new RegExp(req, flag); return !!req.test(val); }, date: function(val, format) { return isValidDate(val); }, present: function(val) { return typeof val !== 'undefined'; }, after: function(val, req) { var val1 = this.validator.input[req]; var val2 = val; if (!isValidDate(val1)) { return false; } if (!isValidDate(val2)) { return false; } if (new Date(val1).getTime() < new Date(val2).getTime()) { return true; } return false; }, after_or_equal: function(val, req) { var val1 = this.validator.input[req]; var val2 = val; if (!isValidDate(val1)) { return false; } if (!isValidDate(val2)) { return false; } if (new Date(val1).getTime() <= new Date(val2).getTime()) { return true; } return false; }, before: function(val, req) { var val1 = this.validator.input[req]; var val2 = val; if (!isValidDate(val1)) { return false; } if (!isValidDate(val2)) { return false; } if (new Date(val1).getTime() > new Date(val2).getTime()) { return true; } return false; }, before_or_equal: function(val, req) { var val1 = this.validator.input[req]; var val2 = val; if (!isValidDate(val1)) { return false; } if (!isValidDate(val2)) { return false; } if (new Date(val1).getTime() >= new Date(val2).getTime()) { return true; } return false; }, hex: function(val) { return /^[0-9a-f]+$/i.test(val); }, json:function (val){ return isJson(val); } }; var missedRuleValidator = function() { throw new Error('Validator `' + this.name + '` is not defined!'); }; var missedRuleMessage; function Rule(name, fn, async) { this.name = name; this.fn = fn; this.passes = null; this._customMessage = undefined; this.async = async; } Rule.prototype = { /** * Validate rule * * @param {mixed} inputValue * @param {mixed} ruleValue * @param {string} attribute * @param {function} callback * @return {boolean|undefined} */ validate: function(inputValue, ruleValue, attribute, callback) { var _this = this; this._setValidatingData(attribute, inputValue, ruleValue); if (typeof callback === 'function') { this.callback = callback; var handleResponse = function(passes, message) { _this.response(passes, message); }; if (this.async) { return this._apply(inputValue, ruleValue, attribute, handleResponse); } else { return handleResponse(this._apply(inputValue, ruleValue, attribute)); } } return this._apply(inputValue, ruleValue, attribute); }, /** * Apply validation function * * @param {mixed} inputValue * @param {mixed} ruleValue * @param {string} attribute * @param {function} callback * @return {boolean|undefined} */ _apply: function(inputValue, ruleValue, attribute, callback) { var fn = this.isMissed() ? missedRuleValidator : this.fn; return fn.apply(this, [inputValue, ruleValue, attribute, callback]); }, /** * Set validating data * * @param {string} attribute * @param {mixed} inputValue * @param {mixed} ruleValue * @return {void} */ _setValidatingData: function(attribute, inputValue, ruleValue) { this.attribute = attribute; this.inputValue = inputValue; this.ruleValue = ruleValue; }, /** * Get parameters * * @return {array} */ getParameters: function() { var value = []; if (typeof this.ruleValue === 'string') { value = this.ruleValue.split(','); } if (typeof this.ruleValue === 'number') { value.push(this.ruleValue); } if (this.ruleValue instanceof Array) { value = this.ruleValue; } return value; }, /** * Get true size of value * * @return {integer|float} */ getSize: function() { var value = this.inputValue; if (value instanceof Array) { return value.length; } if (typeof value === 'number') { return value; } if (this.validator._hasNumericRule(this.attribute)) { return parseFloat(value, 10); } return value.length; }, /** * Get the type of value being checked; numeric or string. * * @return {string} */ _getValueType: function() { if (typeof this.inputValue === 'number' || this.validator._hasNumericRule(this.attribute)) { return 'numeric'; } return 'string'; }, /** * Set the async callback response * * @param {boolean|undefined} passes Whether validation passed * @param {string|undefined} message Custom error message * @return {void} */ response: function(passes, message) { this.passes = passes === undefined || passes === true; this._customMessage = message; this.callback(this.passes, message); }, /** * Set validator instance * * @param {Validator} validator * @return {void} */ setValidator: function(validator) { this.validator = validator; }, /** * Check if rule is missed * * @return {boolean} */ isMissed: function() { return typeof this.fn !== 'function'; }, get customMessage() { return this.isMissed() ? missedRuleMessage : this._customMessage; } }; var manager = { /** * List of async rule names * * @type {Array} */ asyncRules: [], /** * Implicit rules (rules to always validate) * * @type {Array} */ implicitRules: [ 'required', 'required_if', 'required_unless', 'required_with', 'required_with_all', 'required_without', 'required_without_all', 'accepted', 'present' ], /** * Get rule by name * * @param {string} name * @param {Validator} * @return {Rule} */ make: function(name, validator) { var async = this.isAsync(name); var rule = new Rule(name, rules[name], async); rule.setValidator(validator); return rule; }, /** * Determine if given rule is async * * @param {string} name * @return {boolean} */ isAsync: function(name) { for (var i = 0, len = this.asyncRules.length; i < len; i++) { if (this.asyncRules[i] === name) { return true; } } return false; }, /** * Determine if rule is implicit (should always validate) * * @param {string} name * @return {boolean} */ isImplicit: function(name) { return this.implicitRules.indexOf(name) > -1; }, /** * Register new rule * * @param {string} name * @param {function} fn * @return {void} */ register: function(name, fn) { rules[name] = fn; }, /** * Register new implicit rule * * @param {string} name * @param {function} fn * @return {void} */ registerImplicit: function(name, fn) { this.register(name, fn); this.implicitRules.push(name); }, /** * Register async rule * * @param {string} name * @param {function} fn * @return {void} */ registerAsync: function(name, fn) { this.register(name, fn); this.asyncRules.push(name); }, /** * Register implicit async rule * * @param {string} name * @param {function} fn * @return {void} */ registerAsyncImplicit: function(name, fn) { this.registerImplicit(name, fn); this.asyncRules.push(name); }, registerMissedRuleValidator: function(fn, message) { missedRuleValidator = fn; missedRuleMessage = message; } }; module.exports = manager;