import $ from 'jquery';
import Utils from './utils';
import Defaults from './defaults';
import Validator from './validator';
var ValidatorRegistry = function (validators, catalog) {
this.__class__ = 'ValidatorRegistry';
import $ from 'jquery';
import Utils from './utils';
import Defaults from './defaults';
import Validator from './validator';
var ValidatorRegistry = function (validators, catalog) {
this.__class__ = 'ValidatorRegistry';
Default Parsley locale is en
this.locale = 'en';
this.init(validators || {}, catalog || {});
};
var typeTesters = {
email: /^((([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,
number: /^-?(\d*\.)?\d+(e[-+]?\d+)?$/i,
integer: /^-?\d+$/,
digits: /^\d+$/,
alphanum: /^\w+$/i,
date: {
test: value => Utils.parse.date(value) !== null
},
url: new RegExp(
"^" +
protocol identifier
"(?:(?:https?|ftp)://)?" + // ** mod: make scheme optional
user:pass authentication
"(?:\\S+(?::\\S*)?@)?" +
"(?:" +
IP address exclusion private & local networks “(?!(?:10|127)(?:\.\d{1,3}){3})” + // mod: allow local networks “(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})” + // mod: allow local networks “(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})” + // ** mod: allow local networks IP address dotted notation octets excludes loopback network 0.0.0.0 excludes reserved space >= 224.0.0.0 excludes network & broacast addresses (first & last IP address of each class)
"(?:[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]))" +
"|" +
host name
"(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
domain name
"(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
TLD identifier
"(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" +
")" +
port number
"(?::\\d{2,5})?" +
resource path
"(?:/\\S*)?" +
"$", 'i'
)
};
typeTesters.range = typeTesters.number;
var decimalPlaces = num => {
var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
if (!match) { return 0; }
return Math.max(
0,
Number of digits right of decimal point.
(match[1] ? match[1].length : 0) -
Adjust for scientific notation.
(match[2] ? +match[2] : 0));
};
parseArguments(‘number’, [‘1’, ‘2’]) => [1, 2]
let parseArguments = (type, args) => args.map(Utils.parse[type]);
operatorToValidator returns a validating function for an operator function, applied to the given type
let operatorToValidator = (type, operator) => {
return (value, ...requirementsAndInput) => {
requirementsAndInput.pop(); // Get rid of `input` argument
return operator(value, ...parseArguments(type, requirementsAndInput));
};
};
let comparisonOperator = operator => ({
validateDate: operatorToValidator('date', operator),
validateNumber: operatorToValidator('number', operator),
requirementType: operator.length <= 2 ? 'string' : ['string', 'string'], // Support operators with a 1 or 2 requirement(s)
priority: 30
});
ValidatorRegistry.prototype = {
init: function (validators, catalog) {
this.catalog = catalog;
Copy prototype’s validators:
this.validators = Object.assign({}, this.validators);
for (var name in validators)
this.addValidator(name, validators[name].fn, validators[name].priority);
window.Parsley.trigger('parsley:validator:init');
},
Set new messages locale if we have dictionary loaded in ParsleyConfig.i18n
setLocale: function (locale) {
if ('undefined' === typeof this.catalog[locale])
throw new Error(locale + ' is not available in the catalog');
this.locale = locale;
return this;
},
Add a new messages catalog for a given locale. Set locale for this catalog if set === true
addCatalog: function (locale, messages, set) {
if ('object' === typeof messages)
this.catalog[locale] = messages;
if (true === set)
return this.setLocale(locale);
return this;
},
Add a specific message for a given constraint in a given locale
addMessage: function (locale, name, message) {
if ('undefined' === typeof this.catalog[locale])
this.catalog[locale] = {};
this.catalog[locale][name] = message;
return this;
},
Add messages for a given locale
addMessages: function (locale, nameMessageObject) {
for (var name in nameMessageObject)
this.addMessage(locale, name, nameMessageObject[name]);
return this;
},
Add a new validator
addValidator(‘custom’, { requirementType: [‘integer’, ‘integer’], validateString: function(value, from, to) {}, priority: 22, messages: { en: “Hey, that’s no good”, fr: “Aye aye, pas bon du tout”, } })
Old API was addValidator(name, function, priority)
addValidator: function (name, arg1, arg2) {
if (this.validators[name])
Utils.warn('Validator "' + name + '" is already defined.');
else if (Defaults.hasOwnProperty(name)) {
Utils.warn('"' + name + '" is a restricted keyword and is not a valid validator name.');
return;
}
return this._setValidator(...arguments);
},
hasValidator: function (name) {
return !!this.validators[name];
},
updateValidator: function (name, arg1, arg2) {
if (!this.validators[name]) {
Utils.warn('Validator "' + name + '" is not already defined.');
return this.addValidator(...arguments);
}
return this._setValidator(...arguments);
},
removeValidator: function (name) {
if (!this.validators[name])
Utils.warn('Validator "' + name + '" is not defined.');
delete this.validators[name];
return this;
},
_setValidator: function (name, validator, priority) {
if ('object' !== typeof validator) {
Old style validator, with fn
and priority
validator = {
fn: validator,
priority: priority
};
}
if (!validator.validate) {
validator = new Validator(validator);
}
this.validators[name] = validator;
for (var locale in validator.messages || {})
this.addMessage(locale, name, validator.messages[locale]);
return this;
},
getErrorMessage: function (constraint) {
var message;
Type constraints are a bit different, we have to match their requirements too to find right error message
if ('type' === constraint.name) {
var typeMessages = this.catalog[this.locale][constraint.name] || {};
message = typeMessages[constraint.requirements];
} else
message = this.formatMessage(this.catalog[this.locale][constraint.name], constraint.requirements);
return message || this.catalog[this.locale].defaultMessage || this.catalog.en.defaultMessage;
},
Kind of light sprintf()
implementation
formatMessage: function (string, parameters) {
if ('object' === typeof parameters) {
for (var i in parameters)
string = this.formatMessage(string, parameters[i]);
return string;
}
return 'string' === typeof string ? string.replace(/%s/i, parameters) : '';
},
Here is the Parsley default validators list. A validator is an object with the following key values:
true
, false
or a promise
Alternatively, a validator can be a function that returns such an object validators: {
notblank: {
validateString: function(value) {
return /\S/.test(value);
},
priority: 2
},
required: {
validateMultiple: function(values) {
return values.length > 0;
},
validateString: function(value) {
return /\S/.test(value);
},
priority: 512
},
type: {
validateString: function(value, type, {step = 'any', base = 0} = {}) {
var tester = typeTesters[type];
if (!tester) {
throw new Error('validator type `' + type + '` is not supported');
}
if (!tester.test(value))
return false;
if ('number' === type) {
if (!/^any$/i.test(step || '')) {
var nb = Number(value);
var decimals = Math.max(decimalPlaces(step), decimalPlaces(base));
if (decimalPlaces(nb) > decimals) // Value can't have too many decimals
return false;
Be careful of rounding errors by using integers.
var toInt = f => Math.round(f * Math.pow(10, decimals));
if ((toInt(nb) - toInt(base)) % toInt(step) != 0)
return false;
}
}
return true;
},
requirementType: {
'': 'string',
step: 'string',
base: 'number'
},
priority: 256
},
pattern: {
validateString: function(value, regexp) {
return regexp.test(value);
},
requirementType: 'regexp',
priority: 64
},
minlength: {
validateString: function (value, requirement) {
return value.length >= requirement;
},
requirementType: 'integer',
priority: 30
},
maxlength: {
validateString: function (value, requirement) {
return value.length <= requirement;
},
requirementType: 'integer',
priority: 30
},
length: {
validateString: function (value, min, max) {
return value.length >= min && value.length <= max;
},
requirementType: ['integer', 'integer'],
priority: 30
},
mincheck: {
validateMultiple: function (values, requirement) {
return values.length >= requirement;
},
requirementType: 'integer',
priority: 30
},
maxcheck: {
validateMultiple: function (values, requirement) {
return values.length <= requirement;
},
requirementType: 'integer',
priority: 30
},
check: {
validateMultiple: function (values, min, max) {
return values.length >= min && values.length <= max;
},
requirementType: ['integer', 'integer'],
priority: 30
},
min: comparisonOperator((value, requirement) => value >= requirement),
max: comparisonOperator((value, requirement) => value <= requirement),
range: comparisonOperator((value, min, max) => value >= min && value <= max),
equalto: {
validateString: function (value, refOrValue) {
var $reference = $(refOrValue);
if ($reference.length)
return value === $reference.val();
else
return value === refOrValue;
},
priority: 256
}
}
};
export default ValidatorRegistry;