vee-validate
Version:
Simple Vue.js input validation plugin
2,018 lines (1,671 loc) • 70.4 kB
JavaScript
/**
* vee-validate v2.0.0-rc.21
* (c) 2017 Abdelrahman Awad
* @license MIT
*/
//
/**
* Gets the data attribute. the name must be kebab-case.
*/
var getDataAttribute = function (el, name) { return el.getAttribute(("data-vv-" + name)); };
/**
* Checks if the value is either null or undefined.
*/
var isNullOrUndefined = function (value) {
return value === null || value === undefined;
};
/**
* Sets the data attribute.
*/
var setDataAttribute = function (el, name, value) { return el.setAttribute(("data-vv-" + name), value); };
/**
* Creates a proxy object if available in the environment.
*/
var createProxy = function (target, handler) {
if (typeof Proxy === 'undefined') {
return target;
}
return new Proxy(target, handler);
};
/**
* Creates the default flags object.
*/
var createFlags = function () { return ({
untouched: true,
touched: false,
dirty: false,
pristine: true,
valid: null,
invalid: null,
validated: false,
pending: false,
required: false
}); };
/**
* Shallow object comparison.
*/
var isEqual = function (lhs, rhs) {
if (lhs instanceof RegExp && rhs instanceof RegExp) {
return isEqual(lhs.source, rhs.source) && isEqual(lhs.flags, rhs.flags);
}
if (Array.isArray(lhs) && Array.isArray(rhs)) {
if (lhs.length !== rhs.length) { return false; }
for (var i = 0; i < lhs.length; i++) {
if (!isEqual(lhs[i], rhs[i])) {
return false;
}
}
return true;
}
// if both are objects, compare each key recursively.
if (isObject(lhs) && isObject(rhs)) {
return Object.keys(lhs).every(function (key) {
return isEqual(lhs[key], rhs[key]);
}) && Object.keys(rhs).every(function (key) {
return isEqual(lhs[key], rhs[key]);
});
}
return lhs === rhs;
};
/**
* Determines the input field scope.
*/
var getScope = function (el) {
var scope = getDataAttribute(el, 'scope');
if (isNullOrUndefined(scope) && el.form) {
scope = getDataAttribute(el.form, 'scope');
}
return !isNullOrUndefined(scope) ? scope : null;
};
/**
* Gets the value in an object safely.
*/
var getPath = function (path, target, def) {
if ( def === void 0 ) def = undefined;
if (!path || !target) { return def; }
var value = target;
path.split('.').every(function (prop) {
if (! Object.prototype.hasOwnProperty.call(value, prop) && value[prop] === undefined) {
value = def;
return false;
}
value = value[prop];
return true;
});
return value;
};
/**
* Checks if path exists within an object.
*/
var hasPath = function (path, target) {
var obj = target;
return path.split('.').every(function (prop) {
if (! Object.prototype.hasOwnProperty.call(obj, prop)) {
return false;
}
obj = obj[prop];
return true;
});
};
/**
* Parses a rule string expression.
*/
var parseRule = function (rule) {
var params = [];
var name = rule.split(':')[0];
if (~rule.indexOf(':')) {
params = rule.split(':').slice(1).join(':').split(',');
}
return { name: name, params: params };
};
/**
* Debounces a function.
*/
var debounce = function (fn, wait, immediate) {
if ( wait === void 0 ) wait = 0;
if ( immediate === void 0 ) immediate = false;
if (wait === 0) {
return fn;
}
var timeout;
return function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
var later = function () {
timeout = null;
if (!immediate) { fn.apply(void 0, args); }
};
/* istanbul ignore next */
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
/* istanbul ignore next */
if (callNow) { fn.apply(void 0, args); }
};
};
/**
* Normalizes the given rules expression.
*/
var normalizeRules = function (rules) {
// if falsy value return an empty object.
if (!rules) {
return {};
}
if (isObject(rules)) {
// $FlowFixMe
return Object.keys(rules).reduce(function (prev, curr) {
var params = [];
// $FlowFixMe
if (rules[curr] === true) {
params = [];
} else if (Array.isArray(rules[curr])) {
params = rules[curr];
} else {
params = [rules[curr]];
}
// $FlowFixMe
if (rules[curr] !== false) {
prev[curr] = params;
}
return prev;
}, {});
}
if (typeof rules !== 'string') {
warn('rules must be either a string or an object.');
return {};
}
return rules.split('|').reduce(function (prev, rule) {
var parsedRule = parseRule(rule);
if (!parsedRule.name) {
return prev;
}
prev[parsedRule.name] = parsedRule.params;
return prev;
}, {});
};
/**
* Emits a warning to the console.
*/
var warn = function (message) {
console.warn(("[vee-validate] " + message)); // eslint-disable-line
};
/**
* Creates a branded error object.
*/
var createError = function (message) { return new Error(("[vee-validate] " + message)); };
/**
* Checks if the value is an object.
*/
var isObject = function (obj) { return obj !== null && obj && typeof obj === 'object' && ! Array.isArray(obj); };
/**
* Checks if a function is callable.
*/
var isCallable = function (func) { return typeof func === 'function'; };
/**
* Check if element has the css class on it.
*/
var hasClass = function (el, className) {
if (el.classList) {
return el.classList.contains(className);
}
return !!el.className.match(new RegExp(("(\\s|^)" + className + "(\\s|$)")));
};
/**
* Adds the provided css className to the element.
*/
var addClass = function (el, className) {
if (el.classList) {
el.classList.add(className);
return;
}
if (!hasClass(el, className)) {
el.className += " " + className;
}
};
/**
* Remove the provided css className from the element.
*/
var removeClass = function (el, className) {
if (el.classList) {
el.classList.remove(className);
return;
}
if (hasClass(el, className)) {
var reg = new RegExp(("(\\s|^)" + className + "(\\s|$)"));
el.className = el.className.replace(reg, ' ');
}
};
/**
* Adds or removes a class name on the input depending on the status flag.
*/
var toggleClass = function (el, className, status) {
if (!el || !className) { return; }
if (status) {
return addClass(el, className);
}
removeClass(el, className);
};
/**
* Converts an array-like object to array, provides a simple polyfill for Array.from
*/
var toArray = function (arrayLike) {
if (isCallable(Array.from)) {
return Array.from(arrayLike);
}
var array = [];
var length = arrayLike.length;
for (var i = 0; i < length; i++) {
array.push(arrayLike[i]);
}
return array;
};
/**
* Assign polyfill from the mdn.
*/
var assign = function (target) {
var others = [], len = arguments.length - 1;
while ( len-- > 0 ) others[ len ] = arguments[ len + 1 ];
/* istanbul ignore else */
if (isCallable(Object.assign)) {
return Object.assign.apply(Object, [ target ].concat( others ));
}
/* istanbul ignore next */
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
/* istanbul ignore next */
var to = Object(target);
/* istanbul ignore next */
others.forEach(function (arg) {
// Skip over if undefined or null
if (arg != null) {
Object.keys(arg).forEach(function (key) {
to[key] = arg[key];
});
}
});
/* istanbul ignore next */
return to;
};
/**
* Generates a unique id.
*/
var uniqId = function () { return ("_" + (Math.random().toString(36).substr(2, 9))); };
/**
* finds the first element that satisfies the predicate callback, polyfills array.find
*/
var find = function (arrayLike, predicate) {
var array = toArray(arrayLike);
if (isCallable(array.find)) {
return array.find(predicate);
}
var result;
array.some(function (item) {
if (predicate(item)) {
result = item;
return true;
}
return false;
});
return result;
};
/**
* Returns a suitable event name for the input element.
*/
var getInputEventName = function (el) {
if (el && (el.tagName === 'SELECT' || ~['radio', 'checkbox', 'file'].indexOf(el.type))) {
return 'change';
}
return 'input';
};
var isBuiltInComponent = function (vnode) {
if (!vnode) {
return false;
}
var tag = vnode.componentOptions.tag;
return /keep-alive|transition|transition-group/.test(tag);
};
//
var ErrorBag = function ErrorBag () {
this.items = [];
};
/**
* Adds an error to the internal array.
*/
ErrorBag.prototype.add = function add (error) {
// handle old signature.
if (arguments.length > 1) {
error = {
field: arguments[0],
msg: arguments[1],
rule: arguments[2],
scope: !isNullOrUndefined(arguments[3]) ? arguments[3] : null
};
}
error.scope = !isNullOrUndefined(error.scope) ? error.scope : null;
this.items.push(error);
};
/**
* Updates a field error with the new field scope.
*/
ErrorBag.prototype.update = function update (id, error) {
var item = find(this.items, function (i) { return i.id === id; });
if (!item) {
return;
}
var idx = this.items.indexOf(item);
this.items.splice(idx, 1);
item.scope = error.scope;
this.items.push(item);
};
/**
* Gets all error messages from the internal array.
*/
ErrorBag.prototype.all = function all (scope) {
if (isNullOrUndefined(scope)) {
return this.items.map(function (e) { return e.msg; });
}
return this.items.filter(function (e) { return e.scope === scope; }).map(function (e) { return e.msg; });
};
/**
* Checks if there are any errors in the internal array.
*/
ErrorBag.prototype.any = function any (scope) {
if (isNullOrUndefined(scope)) {
return !!this.items.length;
}
return !!this.items.filter(function (e) { return e.scope === scope; }).length;
};
/**
* Removes all items from the internal array.
*/
ErrorBag.prototype.clear = function clear (scope) {
var this$1 = this;
if (isNullOrUndefined(scope)) {
scope = null;
}
for (var i = 0; i < this.items.length; ++i) {
if (this$1.items[i].scope === scope) {
this$1.items.splice(i, 1);
--i;
}
}
};
/**
* Collects errors into groups or for a specific field.
*/
ErrorBag.prototype.collect = function collect (field, scope, map) {
if ( map === void 0 ) map = true;
if (!field) {
var collection = {};
this.items.forEach(function (e) {
if (! collection[e.field]) {
collection[e.field] = [];
}
collection[e.field].push(map ? e.msg : e);
});
return collection;
}
field = !isNullOrUndefined(field) ? String(field) : field;
if (isNullOrUndefined(scope)) {
return this.items.filter(function (e) { return e.field === field; }).map(function (e) { return (map ? e.msg : e); });
}
return this.items.filter(function (e) { return e.field === field && e.scope === scope; })
.map(function (e) { return (map ? e.msg : e); });
};
/**
* Gets the internal array length.
*/
ErrorBag.prototype.count = function count () {
return this.items.length;
};
/**
* Finds and fetches the first error message for the specified field id.
*/
ErrorBag.prototype.firstById = function firstById (id) {
var error = find(this.items, function (i) { return i.id === id; });
return error ? error.msg : null;
};
/**
* Gets the first error message for a specific field.
*/
ErrorBag.prototype.first = function first (field, scope) {
var this$1 = this;
if ( scope === void 0 ) scope = null;
field = !isNullOrUndefined(field) ? String(field) : field;
var selector = this._selector(field);
var scoped = this._scope(field);
if (scoped) {
var result = this.first(scoped.name, scoped.scope);
// if such result exist, return it. otherwise it could be a field.
// with dot in its name.
if (result) {
return result;
}
}
if (selector) {
return this.firstByRule(selector.name, selector.rule, scope);
}
for (var i = 0; i < this.items.length; ++i) {
if (this$1.items[i].field === field && (this$1.items[i].scope === scope)) {
return this$1.items[i].msg;
}
}
return null;
};
/**
* Returns the first error rule for the specified field
*/
ErrorBag.prototype.firstRule = function firstRule (field, scope) {
var errors = this.collect(field, scope, false);
return (errors.length && errors[0].rule) || null;
};
/**
* Checks if the internal array has at least one error for the specified field.
*/
ErrorBag.prototype.has = function has (field, scope) {
if ( scope === void 0 ) scope = null;
return !!this.first(field, scope);
};
/**
* Gets the first error message for a specific field and a rule.
*/
ErrorBag.prototype.firstByRule = function firstByRule (name, rule, scope) {
if ( scope === void 0 ) scope = null;
var error = this.collect(name, scope, false).filter(function (e) { return e.rule === rule; })[0];
return (error && error.msg) || null;
};
/**
* Gets the first error message for a specific field that not match the rule.
*/
ErrorBag.prototype.firstNot = function firstNot (name, rule, scope) {
if ( rule === void 0 ) rule = 'required';
if ( scope === void 0 ) scope = null;
var error = this.collect(name, scope, false).filter(function (e) { return e.rule !== rule; })[0];
return (error && error.msg) || null;
};
/**
* Removes errors by matching against the id.
*/
ErrorBag.prototype.removeById = function removeById (id) {
var this$1 = this;
for (var i = 0; i < this.items.length; ++i) {
if (this$1.items[i].id === id) {
this$1.items.splice(i, 1);
--i;
}
}
};
/**
* Removes all error messages associated with a specific field.
*/
ErrorBag.prototype.remove = function remove (field, scope, id) {
var this$1 = this;
field = !isNullOrUndefined(field) ? String(field) : field;
var removeCondition = function (e) {
if (e.id && id) {
return e.id === id;
}
if (!isNullOrUndefined(scope)) {
return e.field === field && e.scope === scope;
}
return e.field === field && e.scope === null;
};
for (var i = 0; i < this.items.length; ++i) {
if (removeCondition(this$1.items[i])) {
this$1.items.splice(i, 1);
--i;
}
}
};
/**
* Get the field attributes if there's a rule selector.
*/
ErrorBag.prototype._selector = function _selector (field) {
if (field.indexOf(':') > -1) {
var ref = field.split(':');
var name = ref[0];
var rule = ref[1];
return { name: name, rule: rule };
}
return null;
};
/**
* Get the field scope if specified using dot notation.
*/
ErrorBag.prototype._scope = function _scope (field) {
if (field.indexOf('.') > -1) {
var ref = field.split('.');
var scope = ref[0];
var name = ref.slice(1);
return { name: name.join('.'), scope: scope };
}
return null;
};
//
var Dictionary = function Dictionary (dictionary) {
if ( dictionary === void 0 ) dictionary = {};
this.container = {};
this.merge(dictionary);
};
Dictionary.prototype.hasLocale = function hasLocale (locale) {
return !!this.container[locale];
};
Dictionary.prototype.setDateFormat = function setDateFormat (locale, format) {
if (!this.container[locale]) {
this.container[locale] = {};
}
this.container[locale].dateFormat = format;
};
Dictionary.prototype.getDateFormat = function getDateFormat (locale) {
if (!this.container[locale]) {
return undefined;
}
return this.container[locale].dateFormat;
};
Dictionary.prototype.getMessage = function getMessage (locale, key, fallback) {
if (!this.hasMessage(locale, key)) {
return fallback || this._getDefaultMessage(locale);
}
return this.container[locale].messages[key];
};
/**
* Gets a specific message for field. falls back to the rule message.
*/
Dictionary.prototype.getFieldMessage = function getFieldMessage (locale, field, key) {
if (!this.hasLocale(locale)) {
return this.getMessage(locale, key);
}
var dict = this.container[locale].custom && this.container[locale].custom[field];
if (!dict || !dict[key]) {
return this.getMessage(locale, key);
}
return dict[key];
};
Dictionary.prototype._getDefaultMessage = function _getDefaultMessage (locale) {
if (this.hasMessage(locale, '_default')) {
return this.container[locale].messages._default;
}
return this.container.en.messages._default;
};
Dictionary.prototype.getAttribute = function getAttribute (locale, key, fallback) {
if ( fallback === void 0 ) fallback = '';
if (!this.hasAttribute(locale, key)) {
return fallback;
}
return this.container[locale].attributes[key];
};
Dictionary.prototype.hasMessage = function hasMessage (locale, key) {
return !! (
this.hasLocale(locale) &&
this.container[locale].messages &&
this.container[locale].messages[key]
);
};
Dictionary.prototype.hasAttribute = function hasAttribute (locale, key) {
return !! (
this.hasLocale(locale) &&
this.container[locale].attributes &&
this.container[locale].attributes[key]
);
};
Dictionary.prototype.merge = function merge (dictionary) {
this._merge(this.container, dictionary);
};
Dictionary.prototype.setMessage = function setMessage (locale, key, message) {
if (! this.hasLocale(locale)) {
this.container[locale] = {
messages: {},
attributes: {}
};
}
this.container[locale].messages[key] = message;
};
Dictionary.prototype.setAttribute = function setAttribute (locale, key, attribute) {
if (! this.hasLocale(locale)) {
this.container[locale] = {
messages: {},
attributes: {}
};
}
this.container[locale].attributes[key] = attribute;
};
Dictionary.prototype._merge = function _merge (target, source) {
var this$1 = this;
if (! (isObject(target) && isObject(source))) {
return target;
}
Object.keys(source).forEach(function (key) {
if (isObject(source[key])) {
if (! target[key]) {
assign(target, ( obj = {}, obj[key] = {}, obj ));
var obj;
}
this$1._merge(target[key], source[key]);
return;
}
assign(target, ( obj$1 = {}, obj$1[key] = source[key], obj$1 ));
var obj$1;
});
return target;
};
//
var defaultConfig = {
locale: 'en',
delay: 0,
errorBagName: 'errors',
dictionary: null,
strict: true,
fieldsBagName: 'fields',
classes: false,
classNames: null,
events: 'input|blur',
inject: true,
fastExit: true,
aria: true,
validity: false
};
var currentConfig = assign({}, defaultConfig);
var Config = function Config () {};
var staticAccessors$1 = { default: {},current: {} };
staticAccessors$1.default.get = function () {
return defaultConfig;
};
staticAccessors$1.current.get = function () {
return currentConfig;
};
/**
* Merges the config with a new one.
*/
Config.merge = function merge (config) {
currentConfig = assign({}, currentConfig, config);
};
/**
* Resolves the working config from a Vue instance.
*/
Config.resolve = function resolve (context) {
var selfConfig = getPath('$options.$_veeValidate', context, {});
return assign({}, Config.current, selfConfig);
};
Object.defineProperties( Config, staticAccessors$1 );
/**
* Generates the options required to construct a field.
*/
var Generator = function Generator () {};
Generator.generate = function generate (el, binding, vnode) {
var model = Generator.resolveModel(binding, vnode);
var options = Config.resolve(vnode.context);
return {
name: Generator.resolveName(el, vnode),
el: el,
listen: !binding.modifiers.disable,
scope: Generator.resolveScope(el, binding, vnode),
vm: Generator.makeVM(vnode.context),
expression: binding.value,
component: vnode.child,
classes: options.classes,
classNames: options.classNames,
getter: Generator.resolveGetter(el, vnode, model),
events: Generator.resolveEvents(el, vnode) || options.events,
model: model,
delay: Generator.resolveDelay(el, vnode, options),
rules: Generator.resolveRules(el, binding),
initial: !!binding.modifiers.initial,
alias: Generator.resolveAlias(el, vnode),
validity: options.validity,
aria: options.aria,
initialValue: Generator.resolveInitialValue(vnode)
};
};
Generator.getCtorConfig = function getCtorConfig (vnode) {
if (!vnode.child) { return null; }
var config = getPath('child.$options.$_veeValidate', vnode);
return config;
};
/**
*
* @param {*} el
* @param {*} binding
*/
Generator.resolveRules = function resolveRules (el, binding) {
if (!binding || !binding.expression) {
return getDataAttribute(el, 'rules');
}
if (typeof binding.value === 'string') {
return binding.value;
}
if (~['string', 'object'].indexOf(typeof binding.value.rules)) {
return binding.value.rules;
}
return binding.value;
};
/**
* @param {*} vnode
*/
Generator.resolveInitialValue = function resolveInitialValue (vnode) {
var model = vnode.data.model || find(vnode.data.directives, function (d) { return d.name === 'model'; });
return model && model.value;
};
/**
* Creates a non-circular partial VM instance from a Vue instance.
* @param {*} vm
*/
Generator.makeVM = function makeVM (vm) {
return {
get $el () {
return vm.$el;
},
get $refs () {
return vm.$refs;
},
$watch: vm.$watch ? vm.$watch.bind(vm) : function () {},
$validator: vm.$validator ? {
errors: vm.$validator.errors,
validate: vm.$validator.validate.bind(vm.$validator),
update: vm.$validator.update.bind(vm.$validator)
} : null
};
};
/**
* Resolves the delay value.
* @param {*} el
* @param {*} vnode
* @param {Object} options
*/
Generator.resolveDelay = function resolveDelay (el, vnode, options) {
if ( options === void 0 ) options = {};
return getDataAttribute(el, 'delay') || (vnode.child && vnode.child.$attrs && vnode.child.$attrs['data-vv-delay']) || options.delay;
};
/**
* Resolves the alias for the field.
* @param {*} el
* @param {*} vnode
* @return {Function} alias getter
*/
Generator.resolveAlias = function resolveAlias (el, vnode) {
return function () { return getDataAttribute(el, 'as') || (vnode.child && vnode.child.$attrs && vnode.child.$attrs['data-vv-as']) || el.title || null; };
};
/**
* Resolves the events to validate in response to.
* @param {*} el
* @param {*} vnode
*/
Generator.resolveEvents = function resolveEvents (el, vnode) {
var events = getDataAttribute(el, 'validate-on');
if (!events && vnode.child && vnode.child.$attrs) {
events = vnode.child.$attrs['data-vv-validate-on'];
}
if (!events && vnode.child) {
var config = Generator.getCtorConfig(vnode);
events = config && config.events;
}
return events;
};
/**
* Resolves the scope for the field.
* @param {*} el
* @param {*} binding
*/
Generator.resolveScope = function resolveScope (el, binding, vnode) {
if ( vnode === void 0 ) vnode = {};
var scope = null;
if (isObject(binding.value)) {
scope = binding.value.scope;
}
if (vnode.child && isNullOrUndefined(scope)) {
scope = vnode.child.$attrs && vnode.child.$attrs['data-vv-scope'];
}
return !isNullOrUndefined(scope) ? scope : getScope(el);
};
/**
* Checks if the node directives contains a v-model or a specified arg.
* Args take priority over models.
*
* @return {Object}
*/
Generator.resolveModel = function resolveModel (binding, vnode) {
if (binding.arg) {
return binding.arg;
}
if (isObject(binding.value) && binding.value.arg) {
return binding.value.arg;
}
var model = vnode.data.model || find(vnode.data.directives, function (d) { return d.name === 'model'; });
if (!model) {
return null;
}
var watchable = /^[a-z_]+[0-9]*(\w*\.[a-z_]\w*)*$/i.test(model.expression) && hasPath(model.expression, vnode.context);
if (!watchable) {
return null;
}
return model.expression;
};
/**
* Resolves the field name to trigger validations.
* @return {String} The field name.
*/
Generator.resolveName = function resolveName (el, vnode) {
var name = getDataAttribute(el, 'name');
if (!name && !vnode.child) {
return el.name;
}
if (!name && vnode.child && vnode.child.$attrs) {
name = vnode.child.$attrs['data-vv-name'] || vnode.child.$attrs['name'];
}
if (!name && vnode.child) {
var config = Generator.getCtorConfig(vnode);
if (config && isCallable(config.name)) {
var boundGetter = config.name.bind(vnode.child);
return boundGetter();
}
return vnode.child.name;
}
return name;
};
/**
* Returns a value getter input type.
*/
Generator.resolveGetter = function resolveGetter (el, vnode, model) {
if (model) {
return function () {
return getPath(model, vnode.context);
};
}
if (vnode.child) {
var path = getDataAttribute(el, 'value-path') || (vnode.child.$attrs && vnode.child.$attrs['data-vv-value-path']);
if (path) {
return function () {
return getPath(path, vnode.child);
};
}
var config = Generator.getCtorConfig(vnode);
if (config && isCallable(config.value)) {
var boundGetter = config.value.bind(vnode.child);
return function () {
return boundGetter();
};
}
return function () {
return vnode.child.value;
};
}
switch (el.type) {
case 'checkbox': return function () {
var els = document.querySelectorAll(("input[name=\"" + (el.name) + "\"]"));
els = toArray(els).filter(function (el) { return el.checked; });
if (!els.length) { return undefined; }
return els.map(function (checkbox) { return checkbox.value; });
};
case 'radio': return function () {
var els = document.querySelectorAll(("input[name=\"" + (el.name) + "\"]"));
var elm = find(els, function (el) { return el.checked; });
return elm && elm.value;
};
case 'file': return function (context) {
return toArray(el.files);
};
case 'select-multiple': return function () {
return toArray(el.options).filter(function (opt) { return opt.selected; }).map(function (opt) { return opt.value; });
};
default: return function () {
return el && el.value;
};
}
};
//
var DEFAULT_OPTIONS = {
targetOf: null,
initial: false,
scope: null,
listen: true,
name: null,
active: true,
required: false,
rules: {},
vm: null,
classes: false,
validity: true,
aria: true,
events: 'input|blur',
delay: 0,
classNames: {
touched: 'touched', // the control has been blurred
untouched: 'untouched', // the control hasn't been blurred
valid: 'valid', // model is valid
invalid: 'invalid', // model is invalid
pristine: 'pristine', // control has not been interacted with
dirty: 'dirty' // control has been interacted with
}
};
var Field = function Field (el, options) {
if ( options === void 0 ) options = {};
this.id = uniqId();
this.el = el;
this.updated = false;
this.dependencies = [];
this.watchers = [];
this.events = [];
this.rules = {};
if (!this.isHeadless && !options.targetOf) {
setDataAttribute(this.el, 'id', this.id); // cache field id if it is independent and has a root element.
}
options = assign({}, DEFAULT_OPTIONS, options);
this.validity = options.validity;
this.aria = options.aria;
this.flags = createFlags();
this.vm = options.vm;
this.component = options.component;
this.ctorConfig = this.component ? getPath('$options.$_veeValidate', this.component) : undefined;
this.update(options);
this.updated = false;
};
var prototypeAccessors$1 = { isVue: {},validator: {},isRequired: {},isDisabled: {},isHeadless: {},displayName: {},value: {},rejectsFalse: {} };
prototypeAccessors$1.isVue.get = function () {
return !!this.component;
};
prototypeAccessors$1.validator.get = function () {
if (!this.vm || !this.vm.$validator) {
warn('No validator instance detected.');
return { validate: function () {} };
}
return this.vm.$validator;
};
prototypeAccessors$1.isRequired.get = function () {
return !!this.rules.required;
};
prototypeAccessors$1.isDisabled.get = function () {
return !!(this.component && this.component.disabled) || !!(this.el && this.el.disabled);
};
prototypeAccessors$1.isHeadless.get = function () {
return !this.el;
};
/**
* Gets the display name (user-friendly name).
*/
prototypeAccessors$1.displayName.get = function () {
return isCallable(this.alias) ? this.alias() : this.alias;
};
/**
* Gets the input value.
*/
prototypeAccessors$1.value.get = function () {
if (!isCallable(this.getter)) {
return undefined;
}
return this.getter();
};
/**
* If the field rejects false as a valid value for the required rule.
*/
prototypeAccessors$1.rejectsFalse.get = function () {
if (this.isVue && this.ctorConfig) {
return !!this.ctorConfig.rejectsFalse;
}
if (this.isHeadless) {
return false;
}
return this.el.type === 'checkbox';
};
/**
* Determines if the instance matches the options provided.
*/
Field.prototype.matches = function matches (options) {
if (options.id) {
return this.id === options.id;
}
if (options.name === undefined && options.scope === undefined) {
return true;
}
if (options.scope === undefined) {
return this.name === options.name;
}
if (options.name === undefined) {
return this.scope === options.scope;
}
return options.name === this.name && options.scope === this.scope;
};
/**
* Updates the field with changed data.
*/
Field.prototype.update = function update (options) {
this.targetOf = options.targetOf || null;
this.initial = options.initial || this.initial || false;
// update errors scope if the field scope was changed.
if (this.updated && !isNullOrUndefined(options.scope) && options.scope !== this.scope && isCallable(this.validator.update)) {
this.validator.update(this.id, { scope: options.scope });
}
this.scope = !isNullOrUndefined(options.scope) ? options.scope
: !isNullOrUndefined(this.scope) ? this.scope : null;
this.name = (!isNullOrUndefined(options.name) ? String(options.name) : options.name) || this.name || null;
this.rules = options.rules !== undefined ? normalizeRules(options.rules) : this.rules;
this.model = options.model || this.model;
this.listen = options.listen !== undefined ? options.listen : this.listen;
this.classes = options.classes || this.classes || false;
this.classNames = options.classNames || this.classNames || DEFAULT_OPTIONS.classNames;
this.alias = options.alias || this.alias;
this.getter = isCallable(options.getter) ? options.getter : this.getter;
this.delay = options.delay || this.delay || 0;
this.events = typeof options.events === 'string' && options.events.length ? options.events.split('|') : this.events;
this.updateDependencies();
this.addActionListeners();
// update required flag flags
if (options.rules !== undefined) {
this.flags.required = this.isRequired;
}
// validate if it was validated before and field was updated and there was a rules mutation.
if (this.flags.validated && options.rules !== undefined && this.updated) {
this.validator.validate(("#" + (this.id)));
}
this.updated = true;
// no need to continue.
if (this.isHeadless) {
return;
}
this.updateClasses();
this.addValueListeners();
this.updateAriaAttrs();
};
/**
* Resets field flags and errors.
*/
Field.prototype.reset = function reset () {
var this$1 = this;
var def = createFlags();
Object.keys(this.flags).forEach(function (flag) {
this$1.flags[flag] = def[flag];
});
this.addActionListeners();
this.updateClasses();
this.updateAriaAttrs();
this.updateCustomValidity();
};
/**
* Sets the flags and their negated counterparts, and updates the classes and re-adds action listeners.
*/
Field.prototype.setFlags = function setFlags (flags) {
var this$1 = this;
var negated = {
pristine: 'dirty',
dirty: 'pristine',
valid: 'invalid',
invalid: 'valid',
touched: 'untouched',
untouched: 'touched'
};
Object.keys(flags).forEach(function (flag) {
this$1.flags[flag] = flags[flag];
// if it has a negation and was not specified, set it as well.
if (negated[flag] && flags[negated[flag]] === undefined) {
this$1.flags[negated[flag]] = !flags[flag];
}
});
if (
flags.untouched !== undefined ||
flags.touched !== undefined ||
flags.dirty !== undefined ||
flags.pristine !== undefined
) {
this.addActionListeners();
}
this.updateClasses();
this.updateAriaAttrs();
this.updateCustomValidity();
};
/**
* Determines if the field requires references to target fields.
*/
Field.prototype.updateDependencies = function updateDependencies () {
var this$1 = this;
// reset dependencies.
this.dependencies.forEach(function (d) { return d.field.destroy(); });
this.dependencies = [];
// we get the selectors for each field.
var fields = Object.keys(this.rules).reduce(function (prev, r) {
if (r === 'confirmed') {
prev.push({ selector: this$1.rules[r][0] || ((this$1.name) + "_confirmation"), name: r });
} else if (/after|before/.test(r)) {
prev.push({ selector: this$1.rules[r][0], name: r });
}
return prev;
}, []);
if (!fields.length || !this.vm || !this.vm.$el) { return; }
// must be contained within the same component, so we use the vm root element constrain our dom search.
fields.forEach(function (ref) {
var selector = ref.selector;
var name = ref.name;
var el = null;
// vue ref selector.
if (selector[0] === '$') {
el = this$1.vm.$refs[selector.slice(1)];
} else {
try {
// try query selector
el = this$1.vm.$el.querySelector(selector);
} catch (err) {
el = null;
}
}
if (!el) {
try {
el = this$1.vm.$el.querySelector(("input[name=\"" + selector + "\"]"));
} catch (err) {
el = null;
}
}
if (!el) {
return;
}
var options = {
vm: this$1.vm,
classes: this$1.classes,
classNames: this$1.classNames,
delay: this$1.delay,
scope: this$1.scope,
events: this$1.events.join('|'),
initial: this$1.initial,
targetOf: this$1.id
};
// probably a component.
if (isCallable(el.$watch)) {
options.component = el;
options.el = el.$el;
options.alias = Generator.resolveAlias(el.$el, { child: el });
options.getter = Generator.resolveGetter(el.$el, { child: el });
} else {
options.el = el;
options.alias = Generator.resolveAlias(el, {});
options.getter = Generator.resolveGetter(el, {});
}
this$1.dependencies.push({ name: name, field: new Field(options.el, options) });
});
};
/**
* Removes listeners.
*/
Field.prototype.unwatch = function unwatch (tag) {
if ( tag === void 0 ) tag = null;
if (!tag) {
this.watchers.forEach(function (w) { return w.unwatch(); });
this.watchers = [];
return;
}
this.watchers.filter(function (w) { return tag.test(w.tag); }).forEach(function (w) { return w.unwatch(); });
this.watchers = this.watchers.filter(function (w) { return !tag.test(w.tag); });
};
/**
* Updates the element classes depending on each field flag status.
*/
Field.prototype.updateClasses = function updateClasses () {
if (!this.classes) { return; }
toggleClass(this.el, this.classNames.dirty, this.flags.dirty);
toggleClass(this.el, this.classNames.pristine, this.flags.pristine);
toggleClass(this.el, this.classNames.valid, !!this.flags.valid);
toggleClass(this.el, this.classNames.invalid, !!this.flags.invalid);
toggleClass(this.el, this.classNames.touched, this.flags.touched);
toggleClass(this.el, this.classNames.untouched, this.flags.untouched);
};
/**
* Adds the listeners required for automatic classes and some flags.
*/
Field.prototype.addActionListeners = function addActionListeners () {
var this$1 = this;
// remove previous listeners.
this.unwatch(/class/);
var onBlur = function () {
this$1.flags.touched = true;
this$1.flags.untouched = false;
if (this$1.classes) {
toggleClass(this$1.el, this$1.classNames.touched, true);
toggleClass(this$1.el, this$1.classNames.untouched, false);
}
// only needed once.
this$1.unwatch(/^class_blur$/);
};
var inputEvent = getInputEventName(this.el);
var onInput = function () {
this$1.flags.dirty = true;
this$1.flags.pristine = false;
if (this$1.classes) {
toggleClass(this$1.el, this$1.classNames.pristine, false);
toggleClass(this$1.el, this$1.classNames.dirty, true);
}
// only needed once.
this$1.unwatch(/^class_input$/);
};
if (this.isVue && isCallable(this.component.$once)) {
this.component.$once('input', onInput);
this.component.$once('blur', onBlur);
this.watchers.push({
tag: 'class_input',
unwatch: function () {
this$1.component.$off('input', onInput);
}
});
this.watchers.push({
tag: 'class_blur',
unwatch: function () {
this$1.component.$off('blur', onBlur);
}
});
return;
}
if (this.isHeadless) { return; }
this.el.addEventListener(inputEvent, onInput);
// Checkboxes and radio buttons on Mac don't emit blur naturally, so we listen on click instead.
var blurEvent = ['radio', 'checkbox'].indexOf(this.el.type) === -1 ? 'blur' : 'click';
this.el.addEventListener(blurEvent, onBlur);
this.watchers.push({
tag: 'class_input',
unwatch: function () {
this$1.el.removeEventListener(inputEvent, onInput);
}
});
this.watchers.push({
tag: 'class_blur',
unwatch: function () {
this$1.el.removeEventListener(blurEvent, onBlur);
}
});
};
/**
* Adds the listeners required for validation.
*/
Field.prototype.addValueListeners = function addValueListeners () {
var this$1 = this;
this.unwatch(/^input_.+/);
if (!this.listen) { return; }
var fn = this.targetOf ? function () {
this$1.validator.validate(("#" + (this$1.targetOf)));
} : function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
// if its a DOM event, resolve the value, otherwise use the first parameter as the value.
if (args.length === 0 || (isCallable(Event) && args[0] instanceof Event) || (args[0] && args[0].srcElement)) {
args[0] = this$1.value;
}
this$1.validator.validate(("#" + (this$1.id)), args[0]);
};
var validate = debounce(fn, this.delay);
var inputEvent = getInputEventName(this.el);
// replace input event with suitable one.
var events = this.events.map(function (e) {
return e === 'input' ? inputEvent : e;
});
// if there is a watchable model and an on input validation is requested.
if (this.model && events.indexOf(inputEvent) !== -1) {
var unwatch = this.vm.$watch(this.model, validate);
this.watchers.push({
tag: 'input_model',
unwatch: unwatch
});
// filter out input event as it is already handled by the watcher API.
events = events.filter(function (e) { return e !== inputEvent; });
}
// Add events.
events.forEach(function (e) {
if (this$1.isVue) {
this$1.component.$on(e, validate);
this$1.watchers.push({
tag: 'input_vue',
unwatch: function () {
this$1.component.$off(e, validate);
}
});
return;
}
if (~['radio', 'checkbox'].indexOf(this$1.el.type)) {
var els = document.querySelectorAll(("input[name=\"" + (this$1.el.name) + "\"]"));
toArray(els).forEach(function (el) {
el.addEventListener(e, validate);
this$1.watchers.push({
tag: 'input_native',
unwatch: function () {
el.removeEventListener(e, validate);
}
});
});
return;
}
this$1.el.addEventListener(e, validate);
this$1.watchers.push({
tag: 'input_native',
unwatch: function () {
this$1.el.removeEventListener(e, validate);
}
});
});
};
/**
* Updates aria attributes on the element.
*/
Field.prototype.updateAriaAttrs = function updateAriaAttrs () {
if (!this.aria || this.isHeadless || !isCallable(this.el.setAttribute)) { return; }
this.el.setAttribute('aria-required', this.isRequired ? 'true' : 'false');
this.el.setAttribute('aria-invalid', this.flags.invalid ? 'true' : 'false');
};
/**
* Updates the custom validity for the field.
*/
Field.prototype.updateCustomValidity = function updateCustomValidity () {
if (!this.validity || this.isHeadless || !isCallable(this.el.setCustomValidity)) { return; }
this.el.setCustomValidity(this.flags.valid ? '' : (this.validator.errors.firstById(this.id) || ''));
};
/**
* Removes all listeners.
*/
Field.prototype.destroy = function destroy () {
this.watchers.forEach(function (w) { return w.unwatch(); });
this.watchers = [];
this.dependencies.forEach(function (d) { return d.field.destroy(); });
this.dependencies = [];
};
Object.defineProperties( Field.prototype, prototypeAccessors$1 );
//
var FieldBag = function FieldBag () {
this.items = [];
};
var prototypeAccessors$2 = { length: {} };
/**
* Gets the current items length.
*/
prototypeAccessors$2.length.get = function () {
return this.items.length;
};
/**
* Finds the first field that matches the provided matcher object.
*/
FieldBag.prototype.find = function find$1 (matcher) {
return find(this.items, function (item) { return item.matches(matcher); });
};
/**
* Filters the items down to the matched fields.
*/
FieldBag.prototype.filter = function filter (matcher) {
// multiple matchers to be tried.
if (Array.isArray(matcher)) {
return this.items.filter(function (item) { return matcher.some(function (m) { return item.matches(m); }); });
}
return this.items.filter(function (item) { return item.matches(matcher); });
};
/**
* Maps the field items using the mapping function.
*/
FieldBag.prototype.map = function map (mapper) {
return this.items.map(mapper);
};
/**
* Finds and removes the first field that matches the provided matcher object, returns the removed item.
*/
FieldBag.prototype.remove = function remove (matcher) {
var item = null;
if (matcher instanceof Field) {
item = matcher;
} else {
item = this.find(matcher);
}
if (!item) { return null; }
var index = this.items.indexOf(item);
this.items.splice(index, 1);
return item;
};
/**
* Adds a field item to the list.
*/
FieldBag.prototype.push = function push (item) {
if (! (item instanceof Field)) {
throw createError('FieldBag only accepts instances of Field that has an id defined.');
}
if (!item.id) {
throw createError('Field id must be defined.');
}
if (this.find({ id: item.id })) {
throw createError(("Field with id " + (item.id) + " is already added."));
}
this.items.push(item);
};
Object.defineProperties( FieldBag.prototype, prototypeAccessors$2 );
//
var RULES = {};
var LOCALE = 'en';
var STRICT_MODE = true;
var DICTIONARY = new Dictionary({
en: {
messages: {},
attributes: {},
custom: {}
}
});
var Validator = function Validator (validations, options) {
var this$1 = this;
if ( options === void 0 ) options = { vm: null, fastExit: true };
this.strict = STRICT_MODE;
this.errors = new ErrorBag();
this.fields = new FieldBag();
this.flags = {};
this._createFields(validations);
this.paused = false;
this.fastExit = options.fastExit || false;
this.ownerId = options.vm && options.vm._uid;
// create it statically since we don't need constant access to the vm.
this.reset = options.vm && isCallable(options.vm.$nextTick) ? function () {
return new Promise(function (resolve, reject) {
options.vm.$nextTick(function () {
this$1.fields.items.forEach(function (i) { return i.reset(); });
this$1.errors.clear();
resolve();
});
});
} : function () {
return new Promise(function (resolve, reject) {
this$1.fields.items.forEach(function (i) { return i.reset(); });
this$1.errors.clear();
resolve();
});
};
/* istanbul ignore next */
this.clean = function () {
warn('validator.clean is marked for deprecation, please use validator.reset instead.');
this$1.reset();
};
};
var prototypeAccessors = { dictionary: {},locale: {},rules: {} };
var staticAccessors = { dictionary: {},locale: {},rules: {} };
/**
* Getter for the dictionary.
*/
prototypeAccessors.dictionary.get = function () {
return DICTIONARY;
};
/**
* Static Getter for the dictionary.
*/
staticAccessors.dictionary.get = function () {
return DICTIONARY;
};
/**
* Getter for the current locale.
*/
prototypeAccessors.locale.get = function () {
return LOCALE;
};
/**
* Setter for the validator locale.
*/
prototypeAccessors.locale.set = function (value) {
Validator.locale = value;
};
/**
* Static getter for the validator locale.
*/
staticAccessors.locale.get = function () {
return LOCALE;
};
/**
* Static setter for the validator locale.
*/
staticAccessors.locale.set = function (value) {
/* istanbul ignore if */
if (!DICTIONARY.hasLocale(value)) {
// eslint-disable-next-line
warn('You are setting the validator locale to a locale that is not defined in the dictionary. English messages may still be generated.');
}
LOCALE = value;
};
/**
* Getter for the rules object.
*/
prototypeAccessors.rules.get = function () {
return RULES;
};
/**
* Static Getter for the rules object.
*/
staticAccessors.rules.get = function () {
return RULES;
};
/**
* Static constructor.
*/
Validator.create = function create (validations, options) {
return new Validator(validations, options);
};
/**
* Adds a custom validator to the list of validation rules.
*/
Validator.extend = function extend (name, validator) {
Validator._guardExtend(name, validator);
Validator._merge(name, validator);
};
/**
* Removes a rule from the list of validators.
*/
Validator.remove = function remove (name) {
delete RULES[name];
};
/**
* Sets the default locale for all validators.
* @deprecated
*/
Validator.setLocale = function setLocale (language) {
if ( language === void 0 ) language = 'en';
Validator.locale = language;
};
/**
* @deprecated
*/
Validator.installDateTimeValidators = function installDateTimeValidators () {
/* istanbul ignore next */
warn('Date validations are now installed by default, you no longer need to install it.');
};
/**
* @deprecated
*/
Validator.prototype.installDateTimeValidators = function installDateTimeValidators () {
/* istanbul ignore next */
warn('Date validations are now installed by default, you no longer need to install it.');
};
/**
* Sets the operating mode for all newly created validators.
* strictMode = true: Values without a rule are invalid and cause failure.
* strictMode = false: Values without a rule are valid and are skipped.
*/
Validator.setStrictMode = function setStrictMode (strictMode) {
if ( strictMode === void 0 ) strictMode = true;
STRICT_MODE = strictMode;
};
/**
* Updates the dictionary, overwriting existing values and adding new ones.
* @deprecated
*/
Validator.updateDictionary = function updateDictionary (data) {
DICTIONARY.merge(data);
};
/**
* Adds a locale object to the dictionary.
* @deprecated
*/
Validator.addLocale = function addLocale (locale) {
if (! locale.name) {
warn('Your locale must have a name property');
return;
}
this.updateDictionary(( obj = {}, obj[locale.name] = locale, obj ));
var obj;
};
/**
* Adds a locale object to the dictionary.
* @deprecated
* @param {Object} locale
*/
Validator.prototype.addLocale = function addLocale (locale) {
Validator.addLocale(locale);
};
/**
* Adds and sets the current locale for the validator.
*/
Validator.prototype.localize = function localize (lang, dictionary) {
Validator.localize(lang, dictionary);
};
/**
* Adds and sets the current locale for the validator.
*/
Validator.localize = function localize (lang, dictionary) {
// merge the dictionary.
if (dictionary) {
dictionary = assign({}, dictionary, { name: lang });
Validator.addLocale(dictionary);
}
// set the locale.
Validator.locale = lang;
};
/**
* Registers a field to be validated.
*/
Validator.prototype.attach = function attach (field) {
// deprecate: handle old signature.
if (arguments.length > 1) {
field = assign({}, {
name: arguments[0],
rules: arguments[1]
}, arguments[2] || { vm: { $validator: this } });
}
// fixes initial value detection with v-model and select elements.
var value = field.initialValue;
if (!(field instanceof Field)) {
field = new Field(field.el || null, field);
}
this.fields.push(field);
// validate the field initially
if (field.initial) {
this.validate(("#" + (field.id)), value || field.value);
} else {
this._validate(field, value || field.value, true).then(function (valid) {
field.flags.valid = valid;
field.flags.invalid = !valid;
});
}
this._addFlag(field, field.scope);
return field;
};
/**
* Sets the flags on a field.
*/
Validator.prototype.flag = function flag (name, flags) {
var field = this._resolveField(name);
if (! field || !flags) {
return;
}
field.setFlags(flags);
};
/**
* Removes a field from the validator.
*/
Validator.prototype.detach = function detach (name, scope) {
var field = name instanceof Field ? name : this._resolveField(name, scope);
if (!field) { return; }
field.destroy();
this.errors.remove(field.name, field.scope, field.id);
this.fields.remove(field);
var flags = this.flags;
if (!isNullOrUndefined(field.scope) && flags[("$" + (field.scope))]) {
delete flags[("$" + (field.scope))