vendor/assets/javascripts/vue-validator.js in vuejs-1.0.31 vs vendor/assets/javascripts/vue-validator.js in vuejs-1.0.33

- old
+ new

@@ -1,28 +1,1828 @@ /*! - * vue-validator v2.1.3 + * vue-validator v3.0.0-alpha.1 * (c) 2016 kazuya kawaguchi * Released under the MIT License. */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.VueValidator = factory()); -}(this, function () { 'use strict'; +}(this, (function () { 'use strict'; - function plugin(Vue) { - var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; +/* */ - Vue.prototype.$add = function (a, b) { - return a + b; +var inBrowser = + typeof window !== 'undefined' && + Object.prototype.toString.call(window) !== '[object Object]'; +var UA = inBrowser && window.navigator.userAgent.toLowerCase(); +var isIE9 = UA && UA.indexOf('msie 9.0') > 0; + +var MODEL_NOTIFY_EVENT = '__VUE_VALIDATOR_MODEL_NOTIFY_EVENT__'; + +function warn (msg, err) { + if (window.console) { + console.warn('[vue-validator] ' + msg); + if (err) { + console.warn(err.stack); + } + } +} + +function looseEqual (a, b) { + return a === b || ( + isObject(a) && isObject(b) + ? JSON.stringify(a) === JSON.stringify(b) + : false + ) +} + +function getClass (el) { + var classname = el.className; + if (typeof classname === 'object') { + classname = classname.baseVal || ''; + } + return classname +} + +function setClass (el, cls) { + if (isIE9 && !/svg$/.test(el.namespaceURI)) { + el.className = cls; + } else { + el.setAttribute('class', cls); + } +} + +function addClass (el, cls) { + if (el.classList) { + el.classList.add(cls); + } else { + var cur = ' ' + getClass(el) + ' '; + if (cur.indexOf(' ' + cls + ' ') < 0) { + setClass(el, (cur + cls).trim()); + } + } +} + +function removeClass (el, cls) { + if (el.classList) { + el.classList.remove(cls); + } else { + var cur = ' ' + getClass(el) + ' '; + var tar = ' ' + cls + ' '; + while (cur.indexOf(tar) >= 0) { + cur = cur.replace(tar, ' '); + } + setClass(el, cur.trim()); + } + if (!el.className) { + el.removeAttribute('class'); + } +} + +function toggleClasses (el, key, fn) { + if (!el) { return } + + key = key.trim(); + if (key.indexOf(' ') === -1) { + fn(el, key); + return + } + + var keys = key.split(/\s+/); + for (var i = 0, l = keys.length; i < l; i++) { + fn(el, keys[i]); + } +} + +function triggerEvent (el, event, fn) { + var e = document.createEvent('HTMLEvents'); + e.initEvent(event, true, true); + fn && fn(e); + el.dispatchEvent(e); +} + +// TODO: should be defined strict type +function mapValidation (results) { + var res = {}; + + normalizeMap(results).forEach(function (ref) { + var key = ref.key; + var val = ref.val; + + res[key] = function mappedValidation () { + var validation = this.$validation; + if (!this._isMounted) { + return null + } + var paths = val.split('.'); + var first = paths.shift(); + if (first !== '$validation') { + warn(("unknown validation result path: " + val)); + return null + } + var path; + var value = validation; + do { + path = paths.shift(); + value = value[path]; + } while (paths.length > 0) + return value }; + }); + + return res +} + +function isObject (obj) { + return obj !== null && typeof obj === 'object' +} + +// TODO: should be defined strict type +function normalizeMap (map) { + return Array.isArray(map) + ? map.map(function (key) { return ({ key: key, val: key }); }) + : Object.keys(map).map(function (key) { return ({ key: key, val: map[key] }); }) +} + +/* */ + +// validator configrations +var validator = { + classes: {} +}; + +var Config = function (Vue) { + // define Vue.config.validator configration + // $FlowFixMe: https://github.com/facebook/flow/issues/285 + Object.defineProperty(Vue.config, 'validator', { + enumerable: true, + configurable: true, + get: function () { return validator }, + set: function (val) { validator = val; } + }); +}; + +/* */ +/** + * build-in validators + */ + +/** + * required + * This function validate whether the value has been filled out. + */ +function required (val) { + if (Array.isArray(val)) { + if (val.length !== 0) { + var valid = true; + for (var i = 0, l = val.length; i < l; i++) { + valid = required(val[i]); + if (!valid) { + break + } + } + return valid + } else { + return false + } + } else if (typeof val === 'number' || typeof val === 'function') { + return true + } else if (typeof val === 'boolean') { + return val + } else if (typeof val === 'string') { + return val.length > 0 + } else if (val !== null && typeof val === 'object') { + return Object.keys(val).length > 0 + } else if (val === null || val === undefined) { + return false + } else { + return false } +} - plugin.version = '2.1.3'; +/** + * pattern + * This function validate whether the value matches the regex pattern + */ +function pattern (val, pat) { + if (typeof pat !== 'string') { return false } - if (typeof window !== 'undefined' && window.Vue) { - window.Vue.use(plugin); + var match = pat.match(new RegExp('^/(.*?)/([gimy]*)$')); + if (!match) { return false } + + return new RegExp(match[1], match[2]).test(val) +} + +/** + * minlength + * This function validate whether the minimum length. + */ +function minlength (val, min) { + if (typeof val === 'string') { + return isInteger(min, 10) && val.length >= parseInt(min, 10) + } else if (Array.isArray(val)) { + return val.length >= parseInt(min, 10) + } else { + return false } +} - return plugin; +/** + * maxlength + * This function validate whether the maximum length. + */ +function maxlength (val, max) { + if (typeof val === 'string') { + return isInteger(max, 10) && val.length <= parseInt(max, 10) + } else if (Array.isArray(val)) { + return val.length <= parseInt(max, 10) + } else { + return false + } +} -})); \ No newline at end of file +/** + * min + * This function validate whether the minimum value of the numberable value. + */ +function min (val, arg) { + return !isNaN(+(val)) && !isNaN(+(arg)) && (+(val) >= +(arg)) +} + +/** + * max + * This function validate whether the maximum value of the numberable value. + */ +function max (val, arg) { + return !isNaN(+(val)) && !isNaN(+(arg)) && (+(val) <= +(arg)) +} + +/** + * isInteger + * This function check whether the value of the string is integer. + */ +function isInteger (val) { + return /^(-?[1-9]\d*|0)$/.test(val) +} + + +var validators = Object.freeze({ + required: required, + pattern: pattern, + minlength: minlength, + maxlength: maxlength, + min: min, + max: max +}); + +/* */ +var Asset = function (Vue) { + var ref = Vue.util; + var extend = ref.extend; + + // set global validators asset + var assets = Object.create(null); + extend(assets, validators); + Vue.options.validators = assets; + + // set option merge strategy + var strats = Vue.config.optionMergeStrategies; + if (strats) { + strats.validators = function (parent, child) { + if (!child) { return parent } + if (!parent) { return child } + var ret = Object.create(null); + extend(ret, parent); + var key; + for (key in child) { + ret[key] = child[key]; + } + return ret + }; + } + + /** + * Register or retrieve a global validator definition. + */ + function validator ( + id, + def + ) { + if (def === undefined) { + return Vue.options['validators'][id] + } else { + Vue.options['validators'][id] = def; + if (def === null) { + delete Vue.options['validators']['id']; + } + } + } + Vue['validator'] = validator; +}; + +/* */ + +var Group = function (Vue) { + var ref = Vue.util; + var extend = ref.extend; + + return { + data: function data () { + return { + valid: true, + dirty: false, + touched: false, + modified: false, + results: {} + } + }, + computed: { + invalid: function invalid () { return !this.valid }, + pristine: function pristine () { return !this.dirty }, + untouched: function untouched () { return !this.touched }, + result: function result () { + var ret = { + valid: this.valid, + invalid: this.invalid, + dirty: this.dirty, + pristine: this.pristine, + touched: this.touched, + untouched: this.untouched, + modified: this.modified + }; + var results = this.results; + this._validityKeys.forEach(function (key) { + ret[key] = results[key]; + }); + return ret + } + }, + watch: { + results: function results (val, old) { + var keys = this._validityKeys; + var results = this.results; + this.valid = this.checkResults(keys, results, 'valid', true); + this.dirty = this.checkResults(keys, results, 'dirty', false); + this.touched = this.checkResults(keys, results, 'touched', false); + this.modified = this.checkResults(keys, results, 'modified', false); + } + }, + created: function created () { + this._validities = Object.create(null); + this._validityWatchers = Object.create(null); + this._validityKeys = []; + this._committing = false; + }, + destroyed: function destroyed () { + var this$1 = this; + + this._validityKeys.forEach(function (key) { + this$1._validityWatchers[key](); + delete this$1._validityWatchers[key]; + delete this$1._validities[key]; + }); + delete this._validityWatchers; + delete this._validities; + delete this._validityKeys; + }, + methods: { + register: function register (name, validity) { + var this$1 = this; + + this._validities[name] = validity; + this._validityKeys = Object.keys(this._validities); + this.setResults(name, {}); + this.withCommit(function () { + this$1._validityWatchers[name] = validity.$watch('result', function (val, old) { + this$1.setResults(name, val); + }, { deep: true, immediate: true }); + }); + }, + unregister: function unregister (name) { + var this$1 = this; + + this._validityWatchers[name](); + delete this._validityWatchers[name]; + delete this._validities[name]; + this._validityKeys = Object.keys(this._validities); + this.withCommit(function () { + this$1.resetResults(name); + }); + }, + isRegistered: function isRegistered (name) { + return name in this._validities + }, + getValidityKeys: function getValidityKeys () { + return this._validityKeys + }, + checkResults: function checkResults ( + keys, + results, + prop, + checking + ) { + var ret = checking; + for (var i = 0; i < keys.length; i++) { + var result = results[keys[i]]; + if (result[prop] !== checking) { + ret = !checking; + break + } + } + return ret + }, + setResults: function setResults (name, val) { + var this$1 = this; + + var newVal = {}; + this._validityKeys.forEach(function (key) { + newVal[key] = extend({}, this$1.results[key]); + }); + newVal[name] = extend({}, val); + this.results = newVal; + }, + resetResults: function resetResults (ignore) { + var this$1 = this; + + var newVal = {}; + this._validityKeys.forEach(function (key) { + if (ignore && ignore !== key) { + newVal[key] = extend({}, this$1.results[key]); + } + }); + this.results = newVal; + }, + withCommit: function withCommit (fn) { + var committing = this._committing; + this._committing = true; + fn(); + this._committing = committing; + } + } + } +}; + +/* */ +var ValidationClass = function (Vue) { + var ValidityGroup = Group(Vue); + + var Validation = function Validation (options) { + if ( options === void 0 ) options = {}; + + this._result = {}; + this._host = options.host; + this._named = Object.create(null); + this._group = Object.create(null); + this._validities = Object.create(null); + this._beginDestroy = false; + Vue.util.defineReactive(this._host, '$validation', this._result); + }; + + Validation.prototype.register = function register ( + field, + validity, + options + ) { + if ( options === void 0 ) options = {}; + + // NOTE: lazy setup (in constructor, occured callstack recursive errors ...) + if (!this._validityManager) { + this._validityManager = new Vue(ValidityGroup); + this._watchValidityResult(); + } + + if (this._validities[field]) { + // TODO: should be output console.error + return + } + this._validities[field] = validity; + + var named = options.named; + var group = options.group; + var groupValidity = group + ? this._getValidityGroup('group', group) || this._registerValidityGroup('group', group) + : null; + var namedValidity = named + ? this._getValidityGroup('named', named) || this._registerValidityGroup('named', named) + : null; + if (named && group && namedValidity && groupValidity) { + groupValidity.register(field, validity); + !namedValidity.isRegistered(group) && namedValidity.register(group, groupValidity); + !this._validityManager.isRegistered(named) && this._validityManager.register(named, namedValidity); + } else if (namedValidity) { + namedValidity.register(field, validity); + !this._validityManager.isRegistered(named) && this._validityManager.register(named, namedValidity); + } else if (groupValidity) { + groupValidity.register(field, validity); + !this._validityManager.isRegistered(group) && this._validityManager.register(group, groupValidity); + } else { + this._validityManager.register(field, validity); + } + }; + + Validation.prototype.unregister = function unregister ( + field, + options + ) { + if ( options === void 0 ) options = {}; + + if (!this._validityManager) { + // TODO: should be output error + return + } + + if (!this._validities[field]) { + // TODO: should be output error + return + } + delete this._validities[field]; + + var named = options.named; + var group = options.group; + var groupValidity = group ? this._getValidityGroup('group', group) : null; + var namedValidity = named ? this._getValidityGroup('named', named) : null; + if (named && group && namedValidity && groupValidity) { + groupValidity.unregister(field); + namedValidity.isRegistered(group) && namedValidity.unregister(group); + this._validityManager.isRegistered(named) && this._validityManager.unregister(named); + } else if (namedValidity) { + namedValidity.unregister(field); + this._validityManager.isRegistered(named) && this._validityManager.unregister(named); + } else if (groupValidity) { + groupValidity.unregister(field); + this._validityManager.isRegistered(group) && this._validityManager.unregister(group); + } else { + this._validityManager.unregister(field); + } + + group && this._unregisterValidityGroup('group', group); + named && this._unregisterValidityGroup('named', named); + }; + + Validation.prototype.destroy = function destroy () { + var this$1 = this; + + var validityKeys = Object.keys(this._validities); + var namedKeys = Object.keys(this._named); + var groupKeys = Object.keys(this._group); + + // unregister validity + validityKeys.forEach(function (validityKey) { + groupKeys.forEach(function (groupKey) { + var group = this$1._getValidityGroup('group', groupKey); + if (group && group.isRegistered(groupKey)) { + group.unregister(validityKey); + } + }); + namedKeys.forEach(function (namedKey) { + var named = this$1._getValidityGroup('named', namedKey); + if (named && named.isRegistered(validityKey)) { + named.unregister(validityKey); + } + }); + if (this$1._validityManager.isRegistered(validityKey)) { + this$1._validityManager.unregister(validityKey); + } + delete this$1._validities[validityKey]; + }); + + // unregister grouped validity + groupKeys.forEach(function (groupKey) { + namedKeys.forEach(function (namedKey) { + var named = this$1._getValidityGroup('named', namedKey); + if (named && named.isRegistered(groupKey)) { + named.unregister(groupKey); + } + }); + if (this$1._validityManager.isRegistered(groupKey)) { + this$1._validityManager.unregister(groupKey); + } + this$1._unregisterValidityGroup('group', groupKey); + }); + + // unregister named validity + namedKeys.forEach(function (namedKey) { + if (this$1._validityManager.isRegistered(namedKey)) { + this$1._validityManager.unregister(namedKey); + } + this$1._unregisterValidityGroup('named', namedKey); + }); + + this._beginDestroy = true; + }; + + Validation.prototype._getValidityGroup = function _getValidityGroup (type, name) { + return type === 'named' ? this._named[name] : this._group[name] + }; + + Validation.prototype._registerValidityGroup = function _registerValidityGroup (type, name) { + var groups = type === 'named' ? this._named : this._group; + groups[name] = new Vue(ValidityGroup); + return groups[name] + }; + + Validation.prototype._unregisterValidityGroup = function _unregisterValidityGroup (type, name) { + var groups = type === 'named' ? this._named : this._group; + if (!groups[name]) { + // TODO: should be warn + return + } + + groups[name].$destroy(); + delete groups[name]; + }; + + Validation.prototype._watchValidityResult = function _watchValidityResult () { + var this$1 = this; + + this._watcher = this._validityManager.$watch('results', function (val, old) { + Vue.set(this$1._host, '$validation', val); + if (this$1._beginDestroy) { + this$1._destroyValidityMananger(); + } + }, { deep: true }); + }; + + Validation.prototype._unwatchValidityResult = function _unwatchValidityResult () { + this._watcher(); + delete this._watcher; + }; + + Validation.prototype._destroyValidityMananger = function _destroyValidityMananger () { + this._unwatchValidityResult(); + this._validityManager.$destroy(); + this._validityManager = null; + }; + + return Validation +}; + +/* */ + +var Mixin = function (Vue) { + var Validation = ValidationClass(Vue); + + return { + beforeCreate: function beforeCreate () { + this._validation = new Validation({ host: this }); + } + } +}; + +var baseProps = { + field: { + type: String, + required: true + }, + validators: { + type: [String, Array, Object], + required: true + }, + group: { + type: String + }, + multiple: { + type: Boolean + }, + classes: { + type: Object, + default: function () { + return { + valid: 'valid', + invalid: 'invalid', + touched: 'touched', + untouched: 'untouched', + pristine: 'pristine', + dirty: 'dirty', + modified: 'modified' + } + } + } +}; + +/* */ +var States = function (Vue) { + var ref = Vue.util; + var extend = ref.extend; + + var props = extend({ + child: { + type: Object, + required: true + } + }, baseProps); + + function data () { + var validators = nomalizeValidators(this.validators); + return { + results: getInitialResults(validators), + valid: true, + dirty: false, + touched: false, + modified: false, + progresses: getInitialProgresses(validators) + } + } + + return { + props: props, + data: data + } +}; + +function nomalizeValidators (target) { + var validators; + if (typeof target === 'string') { + validators = [target]; + } else if (Array.isArray(target)) { + validators = target; + } else { + validators = Object.keys(target); + } + return validators +} + +function getInitialResults (validators) { + var results = {}; + validators.forEach(function (validator) { + results[validator] = undefined; + }); + return results +} + +function getInitialProgresses (validators) { + var progresses = {}; + validators.forEach(function (validator) { + progresses[validator] = ''; + }); + return progresses +} + +/* */ + +var Computed = function (Vue) { + function invalid () { + return !this.valid + } + + function pristine () { + return !this.dirty + } + + function untouched () { + return !this.touched + } + + function result () { + var this$1 = this; + + var ret = { + valid: this.valid, + invalid: this.invalid, + dirty: this.dirty, + pristine: this.pristine, + touched: this.touched, + untouched: this.untouched, + modified: this.modified + }; + + var keys = this._keysCached(this._uid.toString(), this.results); + keys.forEach(function (validator) { + var result = getValidatorResult(validator, this$1.results[validator]); + if (result === false) { // success + ret[validator] = false; + } else { // failed + var error = { field: this$1.field, validator: validator }; + if (typeof result === 'string') { + error.message = result; + } + if (!ret.errors) { + ret.errors = []; + } + if (Array.isArray(ret.errors)) { + ret.errors.push(error); + } + ret[validator] = result; + } + }); + + return ret + } + + return { + invalid: invalid, + pristine: pristine, + untouched: untouched, + result: result + } +}; + +function getValidatorResult ( + validator, + result +) { + if (typeof result === 'boolean' && !result) { + return true + } + + if (typeof result === 'string' && result) { + return result + } + + return false +} + +/* */ + +var Render = function (Vue) { + return { + render: function render (h) { + this._interceptEvents(this.child, this.multiple); + return this.child + } + } +}; + +/* */ +function addEventInfo (e) { + e[MODEL_NOTIFY_EVENT] = 'DOM'; +} + +function modelValueEqual (vnode) { + var directives = (vnode.data && vnode.data.directives) || []; + var directive = directives.find(function (dir) { + return dir.name === 'model' + }); + return !directive + ? null + : looseEqual(directive.value, directive.oldValue) +} + +/* */ +var SingleElement = function SingleElement (vm, vnode) { + this._vm = vm; + this._vnode = vnode; + this.initValue = this.getValue(); + this.attachValidity(); +}; + +var prototypeAccessors = { _isBuiltIn: {},_isComponent: {} }; + +prototypeAccessors._isBuiltIn.get = function () { + var vnode = this._vnode; + return !vnode.child && + !vnode.componentOptions && + vnode.tag +}; + +prototypeAccessors._isComponent.get = function () { + var vnode = this._vnode; + return vnode.child && + vnode.componentOptions && + vnode.tag.match(/vue-component/) +}; + +SingleElement.prototype.attachValidity = function attachValidity () { + this._vm.$el.$validity = this._vm; +}; + +SingleElement.prototype.getValue = function getValue () { + if (this._isBuiltIn) { + var el = this._vm.$el; + if (el.tagName === 'SELECT') { + return getSelectValue(el) + } else { + if (el.type === 'checkbox') { + return el.checked + } else { + return el.value + } + } + } else if (this._isComponent) { + return this._vnode.child.value + } else { + // TODO: should be warn !! + return '' + } +}; + +SingleElement.prototype.checkModified = function checkModified () { + if (this._isBuiltIn) { + var el = this._vm.$el; + if (el.tagName === 'SELECT') { + return !looseEqual(this.initValue, getSelectValue(el)) + } else { + if (el.type === 'checkbox') { + return !looseEqual(this.initValue, el.checked) + } else { + return !looseEqual(this.initValue, el.value) + } + } + } else if (this._isComponent) { + return !looseEqual(this.initValue, this._vnode.child.value) + } else { + // TODO: should be warn !! + return false + } +}; + +SingleElement.prototype.listenToucheableEvent = function listenToucheableEvent () { + this._vm.$el.addEventListener('focusout', this._vm.willUpdateTouched); +}; + +SingleElement.prototype.unlistenToucheableEvent = function unlistenToucheableEvent () { + this._vm.$el.removeEventListener('focusout', this._vm.willUpdateTouched); +}; + +SingleElement.prototype.listenInputableEvent = function listenInputableEvent () { + var vm = this._vm; + if (this._isBuiltIn) { + var el = vm.$el; + if (el.tagName === 'SELECT') { + el.addEventListener('change', vm.handleInputable); + } else { + if (el.type === 'checkbox') { + el.addEventListener('change', vm.handleInputable); + } else { + el.addEventListener('input', vm.handleInputable); + } + } + } else if (this._isComponent) { + this._unwatchInputable = this._vnode.child.$watch('value', vm.watchInputable); + } else { + // TODO: should be warn !! + } +}; + +SingleElement.prototype.unlistenInputableEvent = function unlistenInputableEvent () { + var vm = this._vm; + if (this._isBuiltIn) { + var el = vm.$el; + if (el.tagName === 'SELECT') { + el.removeEventListener('change', vm.handleInputable); + } else { + if (el.type === 'checkbox') { + el.removeEventListener('change', vm.handleInputable); + } else { + el.removeEventListener('input', vm.handleInputable); + } + } + } else if (this._isComponent) { + if (this._unwatchInputable) { + this._unwatchInputable(); + this._unwatchInputable = undefined; + delete this._unwatchInputable; + } + } else { + // TODO: should be warn !! + } +}; + +SingleElement.prototype.fireInputableEvent = function fireInputableEvent () { + if (this._isBuiltIn) { + var el = this._vm.$el; + if (el.tagName === 'SELECT') { + triggerEvent(el, 'change', addEventInfo); + } else { + if (el.type === 'checkbox') { + triggerEvent(el, 'change', addEventInfo); + } else { + triggerEvent(el, 'input', addEventInfo); + } + } + } else if (this._isComponent) { + var args = { value: this.getValue() }; + args[MODEL_NOTIFY_EVENT] = 'COMPONENT'; + this._vnode.child.$emit('input', args); + } else { + // TODO: should be warn !! + } +}; + +SingleElement.prototype.modelValueEqual = function modelValueEqual$1 () { + return modelValueEqual(this._vnode) +}; + +Object.defineProperties( SingleElement.prototype, prototypeAccessors ); + +function getSelectValue (el) { + var value = []; + for (var i = 0, l = el.options.length; i < l; i++) { + var option = el.options[i]; + if (!option.disabled && option.selected) { + value.push(option.value); + } + } + return value +} + +/* */ +var MultiElement = function MultiElement (vm) { + // TODO: should be checked whether included radio or checkbox + this._vm = vm; + this.initValue = this.getValue(); + this.attachValidity(); +}; + +MultiElement.prototype.attachValidity = function attachValidity () { + var this$1 = this; + + this._vm.$el.$validity = this._vm; + this._eachItems(function (item) { + item.$validity = this$1._vm; + }); +}; + +MultiElement.prototype.getValue = function getValue () { + return this._getCheckedValue() +}; + +MultiElement.prototype.checkModified = function checkModified () { + return !looseEqual(this.initValue, this._getCheckedValue()) +}; + +MultiElement.prototype.listenToucheableEvent = function listenToucheableEvent () { + var this$1 = this; + + this._eachItems(function (item) { + item.addEventListener('focusout', this$1._vm.willUpdateTouched); + }); +}; + +MultiElement.prototype.unlistenToucheableEvent = function unlistenToucheableEvent () { + var this$1 = this; + + this._eachItems(function (item) { + item.removeEventListener('focusout', this$1._vm.willUpdateTouched); + }); +}; + +MultiElement.prototype.listenInputableEvent = function listenInputableEvent () { + var this$1 = this; + + this._eachItems(function (item) { + item.addEventListener('change', this$1._vm.handleInputable); + }); +}; + +MultiElement.prototype.unlistenInputableEvent = function unlistenInputableEvent () { + var this$1 = this; + + this._eachItems(function (item) { + item.removeEventListener('change', this$1._vm.handleInputable); + }); +}; + +MultiElement.prototype.fireInputableEvent = function fireInputableEvent () { + this._eachItems(function (item) { + triggerEvent(item, 'change', addEventInfo); + }); +}; + +MultiElement.prototype.modelValueEqual = function modelValueEqual$1 () { + var ret = null; + var children = (this._vm.child && this._vm.child.children) || []; + for (var i = 0; i < children.length; i++) { + if (!modelValueEqual(children[i])) { + ret = false; + break + } + } + return ret +}; + +MultiElement.prototype._getCheckedValue = function _getCheckedValue () { + var value = []; + this._eachItems(function (item) { + if (!item.disabled && item.checked) { + value.push(item.value); + } + }); + return value +}; + +MultiElement.prototype._getItems = function _getItems () { + return this._vm.$el.querySelectorAll('input[type="checkbox"], input[type="radio"]') +}; + +MultiElement.prototype._eachItems = function _eachItems (cb) { + var items = this._getItems(); + for (var i = 0; i < items.length; i++) { + cb(items[i]); + } +}; + +/* */ + +/* */ +var Lifecycles = function (Vue) { + function created () { + this._elementable = null; + + this._keysCached = memoize(function (results) { + return Object.keys(results) + }); + + // for event control flags + this._modified = false; + + // watch validation raw results + this._watchValidationRawResults(); + + var validation = this.$options.propsData ? this.$options.propsData.validation : null; + if (validation) { + var instance = validation.instance; + var name = validation.name; + var group = this.group; + instance.register(this.field, this, { named: name, group: group }); + } + } + + function destroyed () { + var validation = this.$options.propsData ? this.$options.propsData.validation : null; + if (validation) { + var instance = validation.instance; + var name = validation.name; + var group = this.group; + instance.unregister(this.field, this, { named: name, group: group }); + } + + this._unwatchValidationRawResults(); + + this._elementable.unlistenInputableEvent(); + this._elementable.unlistenToucheableEvent(); + this._elementable = null; + } + + function mounted () { + this._elementable = createValidityElement(this); + this._elementable.listenToucheableEvent(); + this._elementable.listenInputableEvent(); + + toggleClasses(this.$el, this.classes.untouched, addClass); + toggleClasses(this.$el, this.classes.pristine, addClass); + } + + function updated () { + var maybeChangeModel = this._elementable.modelValueEqual(); + if (!this._applyWithUserHandler && maybeChangeModel !== null && !maybeChangeModel) { + this._elementable.fireInputableEvent(); + } + delete this._applyWithUserHandler; + } + + return { + created: created, + destroyed: destroyed, + mounted: mounted, + updated: updated + } +}; + +function memoize (fn) { + var cache = Object.create(null); + return function memoizeFn (id) { + var args = [], len = arguments.length - 1; + while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; + + var hit = cache[id]; + return hit || (cache[id] = fn.apply(void 0, args)) + } +} + +function createValidityElement (vm) { + var vnode = vm.child; + return !vm.multiple + ? new SingleElement(vm, vnode) + : new MultiElement(vm) +} + +/* */ +var Event = function (Vue) { + var ref = Vue.util; + var toArray = ref.toArray; + + function _fireEvent (type) { + var args = [], len = arguments.length - 1; + while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; + + (ref = this).$emit.apply(ref, [ type ].concat( args )); + var ref; + } + + function _interceptEvents (child, multiple) { + var this$1 = this; + + (multiple ? (child.children || []) : [child]).forEach(function (child) { this$1._wrapEvent(child); }); + } + + function _wrapEvent (child) { + var this$1 = this; + + var ret = {}; + if (!child.tag || !child.data) { return ret } + + var dir = getModelDirective(child); + if (!dir) { return ret } + + var ref = getEventSources(child); + var type = ref.type; + var orgListeners = ref.orgListeners; + var listeners = ref.listeners; + if (!Array.isArray(orgListeners)) { return ret } + + var modelHandler = orgListeners[0]; + var userHandler = orgListeners[1]; + var modelApplyer = function (args) { + return function () { + this$1._applyWithUserHandler = true; + modelHandler.apply(child.context, args); + } + }; + var modifier = (dir.modifiers || {}).validity; + listeners[type] = function () { + var args = toArray(arguments, 0); + var event = args[0]; + if (event[MODEL_NOTIFY_EVENT] === 'DOM') { + delete event[MODEL_NOTIFY_EVENT]; + userHandler.apply(child.context, args); + return + } else if (event[MODEL_NOTIFY_EVENT] === 'COMPONENT') { + var value = event.value; + args[0] = value; + userHandler.apply(child.context, args); + return + } + + if (modifier) { + args.push(modelApplyer(args)); + userHandler.apply(child.context, args); + } else { + userHandler.apply(child.context, args); + modelHandler.apply(child.context, args); + } + }; + + ret.dir = dir; + return ret + } + + return { + _fireEvent: _fireEvent, + _interceptEvents: _interceptEvents, + _wrapEvent: _wrapEvent + } +}; + +function getModelDirective (child) { + return ((child.data && child.data.directives) || []).find(function (dir) { return dir.name === 'model' }) +} + +function getEventSources (child) { + var sources = {}; + var listeners = sources.listeners = child.componentOptions + ? child.componentOptions.listeners + : (child.data && child.data.on); + sources.type = + (child.tag === 'input' && (child.data && child.data.attrs && child.data.attrs.type) === 'text') || + (child.tag && child.tag.match(/vue-component/)) + ? 'input' + : 'change'; + if (listeners) { + sources.orgListeners = listeners[sources.type]; + } + return sources +} + +/* */ +var State = function (Vue) { + function getValue (options) { + return this._elementable.getValue() + } + + function checkModified () { + return this._elementable.checkModified() + } + + function willUpdateTouched (options) { + if (!this.touched) { + this.touched = true; + toggleClasses(this.$el, this.classes.touched, addClass); + toggleClasses(this.$el, this.classes.untouched, removeClass); + this._fireEvent('touched'); + } + } + + function willUpdateDirty () { + if (!this.dirty && this.checkModified()) { + this.dirty = true; + toggleClasses(this.$el, this.classes.dirty, addClass); + toggleClasses(this.$el, this.classes.pristine, removeClass); + this._fireEvent('dirty'); + } + } + + function willUpdateModified () { + var modified = this.modified = this.checkModified(); + if (this._modified !== modified) { + this._modified = modified; + toggleClasses(this.$el, this.classes.modified, modified ? addClass : removeClass); + this._fireEvent('modified', modified); + } + } + + function handleInputable (e) { + this.willUpdateDirty(); + this.willUpdateModified(); + } + + function watchInputable (val) { + this.willUpdateDirty(); + this.willUpdateModified(); + } + + function reset () { + var this$1 = this; + + this._unwatchValidationRawResults(); + var keys = this._keysCached(this._uid.toString(), this.results); + for (var i = 0; i < keys.length; i++) { + this$1.results[keys[i]] = undefined; + this$1.progresses[keys[i]] = ''; + } + toggleClasses(this.$el, this.classes.valid, removeClass); + toggleClasses(this.$el, this.classes.invalid, removeClass); + toggleClasses(this.$el, this.classes.touched, removeClass); + toggleClasses(this.$el, this.classes.untouched, addClass); + toggleClasses(this.$el, this.classes.dirty, removeClass); + toggleClasses(this.$el, this.classes.pristine, addClass); + toggleClasses(this.$el, this.classes.modified, removeClass); + this.valid = true; + this.dirty = false; + this.touched = false; + this.modified = false; + this._modified = false; + this._watchValidationRawResults(); + } + + function _watchValidationRawResults () { + var this$1 = this; + + this._unwatch = this.$watch('results', function (val) { + var valid = true; + var keys = this$1._keysCached(this$1._uid.toString(), this$1.results); + for (var i = 0; i < keys.length; i++) { + var result = this$1.results[keys[i]]; + if (typeof result === 'boolean' && !result) { + valid = false; + break + } + if (typeof result === 'string' && result) { + valid = false; + break + } + } + this$1.valid = valid; + + if (valid) { + toggleClasses(this$1.$el, this$1.classes.valid, addClass); + toggleClasses(this$1.$el, this$1.classes.invalid, removeClass); + } else { + toggleClasses(this$1.$el, this$1.classes.valid, removeClass); + toggleClasses(this$1.$el, this$1.classes.invalid, addClass); + } + + this$1._fireEvent(valid ? 'valid' : 'invalid'); + }, { deep: true }); + } + + function _unwatchValidationRawResults () { + this._unwatch(); + this._unwatch = undefined; + delete this._unwatch; + } + + return { + getValue: getValue, + checkModified: checkModified, + willUpdateTouched: willUpdateTouched, + willUpdateDirty: willUpdateDirty, + willUpdateModified: willUpdateModified, + handleInputable: handleInputable, + watchInputable: watchInputable, + reset: reset, + _watchValidationRawResults: _watchValidationRawResults, + _unwatchValidationRawResults: _unwatchValidationRawResults + } +}; + +/* */ + +/** + * Forgiving check for a promise + */ +function isPromise (p) { + return p && typeof p.then === 'function' +} + +var Validate = function (Vue) { + function _resolveValidator (name) { + var ref = Vue.util; + var resolveAsset = ref.resolveAsset; + var options = (this.child && this.child.context) + ? this.child.context.$options + : this.$options; + return resolveAsset(options, 'validators', name) + } + + function _getValidateDescriptor ( + validator, + field, + value + ) { + var ref = Vue.util; + var isPlainObject = ref.isPlainObject; + + var asset = this._resolveValidator(validator); + if (!asset) { + // TODO: should be warned + return null + } + + var fn = null; + var rule = null; + var msg = null; + if (isPlainObject(asset)) { + if (asset.check && typeof asset.check === 'function') { + fn = asset.check; + } + if (asset.message) { + msg = asset.message; + } + } else if (typeof asset === 'function') { + fn = asset; + } else { + // TODO: should be warned + return null + } + + if (!fn) { + // TODO: should be warned + return null + } + + if (isPlainObject(this.validators)) { + if (isPlainObject(this.validators[validator])) { + if (this.validators[validator].rule) { + rule = this.validators[validator].rule; + } + if (this.validators[validator].message) { + msg = this.validators[validator].message; + } + } else { + rule = this.validators[validator]; + } + } + + var descriptor = { fn: fn, value: value, field: field }; + if (rule) { + descriptor.rule = rule; + } + if (msg) { + descriptor.msg = msg; + } + + return descriptor + } + + function _resolveMessage ( + field, + msg, + override + ) { + if (override) { return override } + return msg + ? typeof msg === 'function' + ? msg(field) + : msg + : undefined + } + + function _invokeValidator ( + ref, + cb + ) { + var this$1 = this; + var fn = ref.fn; + var value = ref.value; + var field = ref.field; + var rule = ref.rule; + var msg = ref.msg; + + var future = fn.call(this, value, rule); + if (typeof future === 'function') { // function + future(function () { // resolve + cb(true); + }, function (err) { // reject + cb(false, this$1._resolveMessage(field, msg, err)); + }); + } else if (isPromise(future)) { // promise + future.then(function () { // resolve + cb(true); + }, function (err) { // reject + cb(false, this$1._resolveMessage(field, msg, err)); + }).catch(function (err) { + cb(false, this$1._resolveMessage(field, msg, err.message)); + }); + } else { // sync + cb(future, future === false ? this._resolveMessage(field, msg) : undefined); + } + } + + function _validate (validator, value, cb) { + var this$1 = this; + + var descriptor = this._getValidateDescriptor(validator, this.field, value); + if (descriptor) { + if (this.progresses[validator]) { return false } + this.progresses[validator] = 'running'; + this.$nextTick(function () { + this$1._invokeValidator(descriptor, function (ret, msg) { + this$1.progresses[validator] = ''; + this$1.results[validator] = msg || ret; + if (cb) { + this$1.$nextTick(function () { + cb.call(this$1, null, ret, msg); + }); + } else { + var e = { result: ret }; + if (msg) { + e['msg'] = msg; + } + this$1._fireEvent('validate', validator, e); + } + }); + }); + } else { + // TODO: + var err = new Error(); + cb ? cb.call(this, err) : this._fireEvent('validate', validator, err); + } + return true + } + + function validate () { + var this$1 = this; + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + + var validators; + var value; + var cb; + var ret = true; + + if (args.length === 3) { + validators = [args[0]]; + value = args[1]; + cb = args[2]; + } else if (args.length === 2) { + validators = this._keysCached(this._uid.toString(), this.results); + value = args[0]; + cb = args[1]; + } else if (args.length === 1) { + validators = this._keysCached(this._uid.toString(), this.results); + value = this.getValue(); + cb = args[0]; + } else { + validators = this._keysCached(this._uid.toString(), this.results); + value = this.getValue(); + cb = null; + } + + if (args.length === 3) { + ret = this._validate(validators[0], value, cb); + } else { + validators.forEach(function (validator) { + ret = this$1._validate(validator, value, cb); + }); + } + + return ret + } + + return { + _resolveValidator: _resolveValidator, + _getValidateDescriptor: _getValidateDescriptor, + _resolveMessage: _resolveMessage, + _invokeValidator: _invokeValidator, + _validate: _validate, + validate: validate + } +}; + +/* */ + +var Methods = function (Vue) { + var ref = Vue.util; + var extend = ref.extend; + + var methods = {}; + extend(methods, Event(Vue)); + extend(methods, State(Vue)); + extend(methods, Validate(Vue)); + + return methods +}; + +/* */ + +var ValidityControl = function (Vue) { + var ref = Vue.util; + var extend = ref.extend; + + var ref$1 = States(Vue); + var props = ref$1.props; + var data = ref$1.data; + var computed = Computed(Vue); + var lifecycles = Lifecycles(Vue); + var ref$2 = Render(Vue); + var render = ref$2.render; + var methods = Methods(Vue); + + var validity = { + props: props, + data: data, + render: render, + computed: computed, + methods: methods + }; + extend(validity, lifecycles); + + return validity +}; + +/* */ +var Validity = function (Vue) { + var ref = Vue.util; + var extend = ref.extend; + + return { + functional: true, + props: baseProps, + render: function render ( + h, + ref + ) { + var props = ref.props; + var data = ref.data; + var children = ref.children; + + return children.map(function (child) { + if (!child.tag) { return child } + var newData = extend({}, data); + newData.props = extend({}, props); + extend(newData.props.classes, Vue.config.validator.classes); + newData.props.child = child; + return h('validity-control', newData) + }) + } + } +}; + +/* */ +var ValidityGroup = function (Vue) { + var ref = Vue.util; + var extend = ref.extend; + + var props = extend({ + tag: { + type: String, + default: 'fieldset' + } + }, baseProps); + + return { + functional: true, + props: props, + render: function render ( + h, + ref + ) { + var props = ref.props; + var data = ref.data; + var children = ref.children; + + var child = h(props.tag, children); + var newData = extend({}, data); + newData.props = extend({}, props); + extend(newData.props.classes, Vue.config.validator.classes); + newData.props.child = child; + newData.props.multiple = true; + return h('validity-control', newData) + } + } +}; + +/* */ + +var Validation = function (Vue) { + return { + functional: true, + props: { + name: { + type: String + }, + tag: { + type: String, + default: 'form' + } + }, + render: function render ( + h, + ref + ) { + var props = ref.props; + var data = ref.data; + var parent = ref.parent; + var children = ref.children; + var slots = ref.slots; + + if (!parent._validation) { + // TODO: should be warned + return children + } + var tag = props.tag || 'form'; + walkChildren(parent._validation, props.name, children); + return h(tag, tag === 'form' ? { attrs: { novalidate: true }} : {}, children) + } + } +}; + +function walkChildren (validation, name, children) { + children.forEach(function (child) { + if (child && + child.componentOptions && + child.componentOptions.propsData && child.componentOptions.tag === 'validity-control') { + child.componentOptions.propsData.validation = { + instance: validation, + name: name + }; + } + child.children && walkChildren(validation, name, child.children); + }); +} + +/* */ +var Component = function (Vue) { + return { + 'validity-control': ValidityControl(Vue), + 'validity': Validity(Vue), + 'validity-group': ValidityGroup(Vue), + 'validation': Validation(Vue) + } +}; + +/* */ +var installed = false; + +function plugin (Vue, options) { + if ( options === void 0 ) options = {}; + + if (installed) { + warn('already installed.'); + return + } + + Config(Vue); + Asset(Vue); + installMixin(Vue); + installComponent(Vue); + installed = true; +} + +function installMixin (Vue) { + Vue.mixin(Mixin(Vue)); +} + +function installComponent (Vue) { + var components = Component(Vue); + Object.keys(components).forEach(function (id) { + Vue.component(id, components[id]); + }); +} + +plugin.mapValidation = mapValidation; // for standalone +plugin.version = '3.0.0-alpha.1'; + +if (typeof window !== 'undefined' && window.Vue) { + window.Vue.use(plugin); +} + +var index = { + install: plugin, + mapValidation: mapValidation +}; + +return index; + +})));