diff --git a/static/js/vue/vue.js b/static/js/vue/vue.js index 14d2359..eb13aa4 100644 --- a/static/js/vue/vue.js +++ b/static/js/vue/vue.js @@ -1,5 +1,5 @@ /*! - * Vue.js v1.0.18 + * Vue.js v1.0.21 * (c) 2016 Evan You * Released under the MIT License. */ @@ -652,7 +652,7 @@ * ] * } * - * @param {String} str + * @param {String} s * @return {Object} */ @@ -753,7 +753,7 @@ var close = escapeRegex(config.delimiters[1]); var unsafeOpen = escapeRegex(config.unsafeDelimiters[0]); var unsafeClose = escapeRegex(config.unsafeDelimiters[1]); - tagRE = new RegExp(unsafeOpen + '(.+?)' + unsafeClose + '|' + open + '(.+?)' + close, 'g'); + tagRE = new RegExp(unsafeOpen + '((?:.|\\n)+?)' + unsafeClose + '|' + open + '((?:.|\\n)+?)' + close, 'g'); htmlRE = new RegExp('^' + unsafeOpen + '.*' + unsafeClose + '$'); // reset cache cache = new Cache(1000); @@ -778,7 +778,6 @@ if (hit) { return hit; } - text = text.replace(/\n/g, ''); if (!tagRE.test(text)) { return null; } @@ -996,23 +995,22 @@ }); var warn = undefined; + var formatComponentName = undefined; if ('development' !== 'production') { (function (){ var hasConsole = typeof console !== 'undefined'; - warn = function (msg, e){ - if (hasConsole && (!config.silent || config.debug)) { - console.warn('[Vue warn]: ' + msg); - /* istanbul ignore if */ - if (config.debug) { - if (e) { - throw e; - } else { - console.warn(new Error('Warning Stack Trace').stack); - } - } + + warn = function (msg, vm){ + if (hasConsole && !config.silent) { + console.error('[Vue warn]: ' + msg + (vm ? formatComponentName(vm) : '')); } }; + + formatComponentName = function (vm){ + var name = vm._isVue ? vm.$options.name : vm.name; + return name ? ' (found in component: <' + hyphenate(name) + '>)' : ''; + }; })(); } @@ -1268,6 +1266,22 @@ el.removeEventListener(event, cb); } + /** + * For IE9 compat: when both class and :class are present + * getAttribute('class') returns wrong value... + * + * @param {Element} el + * @return {String} + */ + + function getClass(el){ + var classname = el.className; + if (typeof classname === 'object') { + classname = classname.baseVal || ''; + } + return classname; + } + /** * In IE9, setAttribute('class') will result in empty class * if the element also has the :class attribute; However in @@ -1298,7 +1312,7 @@ if (el.classList) { el.classList.add(cls); } else { - var cur = ' ' + (el.getAttribute('class') || '') + ' '; + var cur = ' ' + getClass(el) + ' '; if (cur.indexOf(' ' + cls + ' ') < 0) { setClass(el, (cur + cls).trim()); } @@ -1316,7 +1330,7 @@ if (el.classList) { el.classList.remove(cls); } else { - var cur = ' ' + (el.getAttribute('class') || '') + ' '; + var cur = ' ' + getClass(el) + ' '; var tar = ' ' + cls + ' '; while (cur.indexOf(tar) >= 0) { cur = cur.replace(tar, ' '); @@ -1515,345 +1529,6 @@ } } - var uid$1 = 0; - - /** - * A dep is an observable that can have multiple - * directives subscribing to it. - * - * @constructor - */ - function Dep(){ - this.id = uid$1++; - this.subs = []; - } - - // the current target watcher being evaluated. - // this is globally unique because there could be only one - // watcher being evaluated at any time. - Dep.target = null; - - /** - * Add a directive subscriber. - * - * @param {Directive} sub - */ - - Dep.prototype.addSub = function (sub){ - this.subs.push(sub); - }; - - /** - * Remove a directive subscriber. - * - * @param {Directive} sub - */ - - Dep.prototype.removeSub = function (sub){ - this.subs.$remove(sub); - }; - - /** - * Add self as a dependency to the target watcher. - */ - - Dep.prototype.depend = function (){ - Dep.target.addDep(this); - }; - - /** - * Notify all subscribers of a new value. - */ - - Dep.prototype.notify = function (){ - // stablize the subscriber list first - var subs = toArray(this.subs); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } - }; - - var arrayProto = Array.prototype; - var arrayMethods = Object.create(arrayProto) - - /** - * Intercept mutating methods and emit events - */ - - ; - ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method){ - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator(){ - // avoid leaking arguments: - // http://jsperf.com/closure-with-arguments - var i = arguments.length; - var args = new Array(i); - while (i--) { - args[i] = arguments[i]; - } - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - inserted = args; - break; - case 'unshift': - inserted = args; - break; - case 'splice': - inserted = args.slice(2); - break; - } - if (inserted) ob.observeArray(inserted); - // notify change - ob.dep.notify(); - return result; - }); - }); - - /** - * Swap the element at the given index with a new value - * and emits corresponding event. - * - * @param {Number} index - * @param {*} val - * @return {*} - replaced element - */ - - def(arrayProto, '$set', function $set(index, val){ - if (index >= this.length) { - this.length = Number(index) + 1; - } - return this.splice(index, 1, val)[0]; - }); - - /** - * Convenience method to remove the element at given index. - * - * @param {Number} index - * @param {*} val - */ - - def(arrayProto, '$remove', function $remove(item){ - /* istanbul ignore if */ - if (!this.length) return; - var index = indexOf(this, item); - if (index > -1) { - return this.splice(index, 1); - } - }); - - var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - - /** - * Observer class that are attached to each observed - * object. Once attached, the observer converts target - * object's property keys into getter/setters that - * collect dependencies and dispatches updates. - * - * @param {Array|Object} value - * @constructor - */ - - function Observer(value){ - this.value = value; - this.dep = new Dep(); - def(value, '__ob__', this); - if (isArray(value)) { - var augment = hasProto ? protoAugment : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } - } - - // Instance methods - - /** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - * - * @param {Object} obj - */ - - Observer.prototype.walk = function (obj){ - var keys = Object.keys(obj); - for (var i = 0, l = keys.length; i < l; i++) { - this.convert(keys[i], obj[keys[i]]); - } - }; - - /** - * Observe a list of Array items. - * - * @param {Array} items - */ - - Observer.prototype.observeArray = function (items){ - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } - }; - - /** - * Convert a property into getter/setter so we can emit - * the events when the property is accessed/changed. - * - * @param {String} key - * @param {*} val - */ - - Observer.prototype.convert = function (key, val){ - defineReactive(this.value, key, val); - }; - - /** - * Add an owner vm, so that when $set/$delete mutations - * happen we can notify owner vms to proxy the keys and - * digest the watchers. This is only called when the object - * is observed as an instance's root $data. - * - * @param {Vue} vm - */ - - Observer.prototype.addVm = function (vm){ - (this.vms || (this.vms = [])).push(vm); - }; - - /** - * Remove an owner vm. This is called when the object is - * swapped out as an instance's $data object. - * - * @param {Vue} vm - */ - - Observer.prototype.removeVm = function (vm){ - this.vms.$remove(vm); - }; - - // helpers - - /** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - * - * @param {Object|Array} target - * @param {Object} proto - */ - - function protoAugment(target, src){ - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ - } - - /** - * Augment an target Object or Array by defining - * hidden properties. - * - * @param {Object|Array} target - * @param {Object} proto - */ - - function copyAugment(target, src, keys){ - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - def(target, key, src[key]); - } - } - - /** - * Attempt to create an observer instance for a value, - * returns the new observer if successfully observed, - * or the existing observer if the value already has one. - * - * @param {*} value - * @param {Vue} [vm] - * @return {Observer|undefined} - * @static - */ - - function observe(value, vm){ - if (!value || typeof value !== 'object') { - return; - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ((isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue) { - ob = new Observer(value); - } - if (ob && vm) { - ob.addVm(vm); - } - return ob; - } - - /** - * Define a reactive property on an Object. - * - * @param {Object} obj - * @param {String} key - * @param {*} val - * @param {Boolean} doNotObserve - */ - - function defineReactive(obj, key, val, doNotObserve){ - var dep = new Dep(); - - var property = Object.getOwnPropertyDescriptor(obj, key); - if (property && property.configurable === false) { - return; - } - - // cater for pre-defined getter/setters - var getter = property && property.get; - var setter = property && property.set; - - // if doNotObserve is true, only use the child value observer - // if it already exists, and do not attempt to create it. - // this allows freezing a large object from the root and - // avoid unnecessary observation inside v-for fragments. - var childOb = doNotObserve ? isObject(val) && val.__ob__ : observe(val); - Object.defineProperty(obj, key, { - enumerable: true, - configurable: true, - get: function reactiveGetter(){ - var value = getter ? getter.call(obj) : val; - if (Dep.target) { - dep.depend(); - if (childOb) { - childOb.dep.depend(); - } - if (isArray(value)) { - for (var e, i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - } - } - } - return value; - }, - set: function reactiveSetter(newVal){ - var value = getter ? getter.call(obj) : val; - if (newVal === value) { - return; - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = doNotObserve ? isObject(newVal) && newVal.__ob__ : observe(newVal); - dep.notify(); - } - }); - } - var commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/i; var reservedTagRE = /^(slot|partial|component)$/i; @@ -1926,128 +1601,6 @@ } } - /** - * Set a prop's initial value on a vm and its data object. - * - * @param {Vue} vm - * @param {Object} prop - * @param {*} value - */ - - function initProp(vm, prop, value){ - var key = prop.path; - value = coerceProp(prop, value); - if (value === undefined) { - value = getPropDefaultValue(vm, prop.options); - } - if (assertProp(prop, value)) { - defineReactive(vm, key, value, true /* doNotObserve */); - } - } - - /** - * Get the default value of a prop. - * - * @param {Vue} vm - * @param {Object} options - * @return {*} - */ - - function getPropDefaultValue(vm, options){ - // no default, return undefined - if (!hasOwn(options, 'default')) { - // absent boolean value defaults to false - return options.type === Boolean ? false : undefined; - } - var def = options['default']; - // warn against non-factory defaults for Object & Array - if (isObject(def)) { - 'development' !== 'production' && warn('Object/Array as default prop values will be shared ' + 'across multiple instances. Use a factory function ' + 'to return the default value instead.'); - } - // call factory function for non-Function types - return typeof def === 'function' && options.type !== Function ? def.call(vm) : def; - } - - /** - * Assert whether a prop is valid. - * - * @param {Object} prop - * @param {*} value - */ - - function assertProp(prop, value){ - if (!prop.options.required && ( // non-required - prop.raw === null || // abscent - value == null) // null or undefined - ) { - return true; - } - var options = prop.options; - var type = options.type; - var valid = true; - var expectedType; - if (type) { - if (type === String) { - expectedType = 'string'; - valid = typeof value === expectedType; - } else if (type === Number) { - expectedType = 'number'; - valid = typeof value === 'number'; - } else if (type === Boolean) { - expectedType = 'boolean'; - valid = typeof value === 'boolean'; - } else if (type === Function) { - expectedType = 'function'; - valid = typeof value === 'function'; - } else if (type === Object) { - expectedType = 'object'; - valid = isPlainObject(value); - } else if (type === Array) { - expectedType = 'array'; - valid = isArray(value); - } else { - valid = value instanceof type; - } - } - if (!valid) { - 'development' !== 'production' && warn('Invalid prop: type check failed for ' + prop.path + '="' + prop.raw + '".' + ' Expected ' + formatType(expectedType) + ', got ' + formatValue(value) + '.'); - return false; - } - var validator = options.validator; - if (validator) { - if (!validator(value)) { - 'development' !== 'production' && warn('Invalid prop: custom validator check failed for ' + prop.path + '="' + prop.raw + '"'); - return false; - } - } - return true; - } - - /** - * Force parsing value with coerce option. - * - * @param {*} value - * @param {Object} options - * @return {*} - */ - - function coerceProp(prop, value){ - var coerce = prop.options.coerce; - if (!coerce) { - return value; - } - // coerce is a function - return coerce(value); - } - - function formatType(val){ - return val ? val.charAt(0).toUpperCase() + val.slice(1) : 'custom type'; - } - - function formatValue(val){ - return Object.prototype.toString.call(val).slice(8, -1); - } - /** * Option overwriting strategies are functions that handle * how to merge a parent option value and a child option @@ -2091,7 +1644,7 @@ return parentVal; } if (typeof childVal !== 'function') { - 'development' !== 'production' && warn('The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.'); + 'development' !== 'production' && warn('The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm); return parentVal; } if (!parentVal) { @@ -2125,7 +1678,7 @@ strats.el = function (parentVal, childVal, vm){ if (!vm && childVal && typeof childVal !== 'function') { - 'development' !== 'production' && warn('The "el" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.'); + 'development' !== 'production' && warn('The "el" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm); return; } var ret = childVal || parentVal; @@ -2141,15 +1694,6 @@ return childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [childVal] : parentVal; }; - /** - * 0.11 deprecation warning - */ - - strats.paramAttributes = function (){ - /* istanbul ignore next */ - 'development' !== 'production' && warn('"paramAttributes" option has been deprecated in 0.12. ' + 'Use "props" instead.'); - }; - /** * Assets * @@ -2349,33 +1893,379 @@ * @param {Object} options * @param {String} type * @param {String} id + * @param {Boolean} warnMissing * @return {Object|Function} */ - function resolveAsset(options, type, id){ + function resolveAsset(options, type, id, warnMissing){ /* istanbul ignore if */ if (typeof id !== 'string') { return; } var assets = options[type]; var camelizedId; - return assets[id] || + var res = assets[id] || // camelCase ID assets[camelizedId = camelize(id)] || // Pascal Case ID assets[camelizedId.charAt(0).toUpperCase() + camelizedId.slice(1)]; + if ('development' !== 'production' && warnMissing && !res) { + warn('Failed to resolve ' + type.slice(0, -1) + ': ' + id, options); + } + return res; + } + + var uid$1 = 0; + + /** + * A dep is an observable that can have multiple + * directives subscribing to it. + * + * @constructor + */ + function Dep(){ + this.id = uid$1++; + this.subs = []; + } + + // the current target watcher being evaluated. + // this is globally unique because there could be only one + // watcher being evaluated at any time. + Dep.target = null; + + /** + * Add a directive subscriber. + * + * @param {Directive} sub + */ + + Dep.prototype.addSub = function (sub){ + this.subs.push(sub); + }; + + /** + * Remove a directive subscriber. + * + * @param {Directive} sub + */ + + Dep.prototype.removeSub = function (sub){ + this.subs.$remove(sub); + }; + + /** + * Add self as a dependency to the target watcher. + */ + + Dep.prototype.depend = function (){ + Dep.target.addDep(this); + }; + + /** + * Notify all subscribers of a new value. + */ + + Dep.prototype.notify = function (){ + // stablize the subscriber list first + var subs = toArray(this.subs); + for (var i = 0, l = subs.length; i < l; i++) { + subs[i].update(); + } + }; + + var arrayProto = Array.prototype; + var arrayMethods = Object.create(arrayProto) + + /** + * Intercept mutating methods and emit events + */ + + ; + ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method){ + // cache original method + var original = arrayProto[method]; + def(arrayMethods, method, function mutator(){ + // avoid leaking arguments: + // http://jsperf.com/closure-with-arguments + var i = arguments.length; + var args = new Array(i); + while (i--) { + args[i] = arguments[i]; + } + var result = original.apply(this, args); + var ob = this.__ob__; + var inserted; + switch (method) { + case 'push': + inserted = args; + break; + case 'unshift': + inserted = args; + break; + case 'splice': + inserted = args.slice(2); + break; + } + if (inserted) ob.observeArray(inserted); + // notify change + ob.dep.notify(); + return result; + }); + }); + + /** + * Swap the element at the given index with a new value + * and emits corresponding event. + * + * @param {Number} index + * @param {*} val + * @return {*} - replaced element + */ + + def(arrayProto, '$set', function $set(index, val){ + if (index >= this.length) { + this.length = Number(index) + 1; + } + return this.splice(index, 1, val)[0]; + }); + + /** + * Convenience method to remove the element at given index or target element reference. + * + * @param {*} item + */ + + def(arrayProto, '$remove', function $remove(item){ + /* istanbul ignore if */ + if (!this.length) return; + var index = indexOf(this, item); + if (index > -1) { + return this.splice(index, 1); + } + }); + + var arrayKeys = Object.getOwnPropertyNames(arrayMethods); + + /** + * By default, when a reactive property is set, the new value is + * also converted to become reactive. However in certain cases, e.g. + * v-for scope alias and props, we don't want to force conversion + * because the value may be a nested value under a frozen data structure. + * + * So whenever we want to set a reactive property without forcing + * conversion on the new value, we wrap that call inside this function. + */ + + var shouldConvert = true; + + function withoutConversion(fn){ + shouldConvert = false; + fn(); + shouldConvert = true; } /** - * Assert asset exists + * Observer class that are attached to each observed + * object. Once attached, the observer converts target + * object's property keys into getter/setters that + * collect dependencies and dispatches updates. + * + * @param {Array|Object} value + * @constructor */ - function assertAsset(val, type, id){ - if (!val) { - 'development' !== 'production' && warn('Failed to resolve ' + type + ': ' + id); + function Observer(value){ + this.value = value; + this.dep = new Dep(); + def(value, '__ob__', this); + if (isArray(value)) { + var augment = hasProto ? protoAugment : copyAugment; + augment(value, arrayMethods, arrayKeys); + this.observeArray(value); + } else { + this.walk(value); } } + // Instance methods + + /** + * Walk through each property and convert them into + * getter/setters. This method should only be called when + * value type is Object. + * + * @param {Object} obj + */ + + Observer.prototype.walk = function (obj){ + var keys = Object.keys(obj); + for (var i = 0, l = keys.length; i < l; i++) { + this.convert(keys[i], obj[keys[i]]); + } + }; + + /** + * Observe a list of Array items. + * + * @param {Array} items + */ + + Observer.prototype.observeArray = function (items){ + for (var i = 0, l = items.length; i < l; i++) { + observe(items[i]); + } + }; + + /** + * Convert a property into getter/setter so we can emit + * the events when the property is accessed/changed. + * + * @param {String} key + * @param {*} val + */ + + Observer.prototype.convert = function (key, val){ + defineReactive(this.value, key, val); + }; + + /** + * Add an owner vm, so that when $set/$delete mutations + * happen we can notify owner vms to proxy the keys and + * digest the watchers. This is only called when the object + * is observed as an instance's root $data. + * + * @param {Vue} vm + */ + + Observer.prototype.addVm = function (vm){ + (this.vms || (this.vms = [])).push(vm); + }; + + /** + * Remove an owner vm. This is called when the object is + * swapped out as an instance's $data object. + * + * @param {Vue} vm + */ + + Observer.prototype.removeVm = function (vm){ + this.vms.$remove(vm); + }; + + // helpers + + /** + * Augment an target Object or Array by intercepting + * the prototype chain using __proto__ + * + * @param {Object|Array} target + * @param {Object} src + */ + + function protoAugment(target, src){ + /* eslint-disable no-proto */ + target.__proto__ = src; + /* eslint-enable no-proto */ + } + + /** + * Augment an target Object or Array by defining + * hidden properties. + * + * @param {Object|Array} target + * @param {Object} proto + */ + + function copyAugment(target, src, keys){ + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + def(target, key, src[key]); + } + } + + /** + * Attempt to create an observer instance for a value, + * returns the new observer if successfully observed, + * or the existing observer if the value already has one. + * + * @param {*} value + * @param {Vue} [vm] + * @return {Observer|undefined} + * @static + */ + + function observe(value, vm){ + if (!value || typeof value !== 'object') { + return; + } + var ob; + if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { + ob = value.__ob__; + } else if (shouldConvert && (isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue) { + ob = new Observer(value); + } + if (ob && vm) { + ob.addVm(vm); + } + return ob; + } + + /** + * Define a reactive property on an Object. + * + * @param {Object} obj + * @param {String} key + * @param {*} val + */ + + function defineReactive(obj, key, val){ + var dep = new Dep(); + + var property = Object.getOwnPropertyDescriptor(obj, key); + if (property && property.configurable === false) { + return; + } + + // cater for pre-defined getter/setters + var getter = property && property.get; + var setter = property && property.set; + + var childOb = observe(val); + Object.defineProperty(obj, key, { + enumerable: true, + configurable: true, + get: function reactiveGetter(){ + var value = getter ? getter.call(obj) : val; + if (Dep.target) { + dep.depend(); + if (childOb) { + childOb.dep.depend(); + } + if (isArray(value)) { + for (var e, i = 0, l = value.length; i < l; i++) { + e = value[i]; + e && e.__ob__ && e.__ob__.dep.depend(); + } + } + } + return value; + }, + set: function reactiveSetter(newVal){ + var value = getter ? getter.call(obj) : val; + if (newVal === value) { + return; + } + if (setter) { + setter.call(obj, newVal); + } else { + val = newVal; + } + childOb = observe(newVal); + dep.notify(); + } + }); + } + var util = Object.freeze({ defineReactive: defineReactive, set: set, @@ -2437,11 +2327,7 @@ getOuterHTML: getOuterHTML, mergeOptions: mergeOptions, resolveAsset: resolveAsset, - assertAsset: assertAsset, checkComponentAttr: checkComponentAttr, - initProp: initProp, - assertProp: assertProp, - coerceProp: coerceProp, commonTagRE: commonTagRE, reservedTagRE: reservedTagRE, get warn(){ return warn; } @@ -2827,8 +2713,8 @@ var warnNonExistent; if ('development' !== 'production') { - warnNonExistent = function (path){ - warn('You are setting a non-existent path "' + path.raw + '" ' + 'on a vm instance. Consider pre-initializing the property ' + 'with the "data" option for more reliable reactivity ' + 'and better performance.'); + warnNonExistent = function (path, vm){ + warn('You are setting a non-existent path "' + path.raw + '" ' + 'on a vm instance. Consider pre-initializing the property ' + 'with the "data" option for more reliable reactivity ' + 'and better performance.', vm); }; } @@ -2860,7 +2746,7 @@ if (!isObject(obj)) { obj = {}; if ('development' !== 'production' && last._isVue) { - warnNonExistent(path); + warnNonExistent(path, last); } set(last, key, obj); } @@ -2871,7 +2757,7 @@ obj[key] = val; } else { if ('development' !== 'production' && obj._isVue) { - warnNonExistent(path); + warnNonExistent(path, obj); } set(obj, key, val); } @@ -3138,8 +3024,8 @@ if ('development' !== 'production' && has[id] != null) { circular[id] = (circular[id] || 0) + 1; if (circular[id] > config._maxUpdateCount) { - queue.splice(has[id], 1); - warn('You may have an infinite update loop for watcher ' + 'with expression: ' + watcher.expression); + warn('You may have an infinite update loop for watcher ' + 'with expression "' + watcher.expression + '"', watcher.vm); + break; } } } @@ -3185,7 +3071,7 @@ * This is used for both the $watch() api and directives. * * @param {Vue} vm - * @param {String} expression + * @param {String|Function} expOrFn * @param {Function} cb * @param {Object} options * - {Array} filters @@ -3243,7 +3129,7 @@ value = this.getter.call(scope, scope); } catch (e) { if ('development' !== 'production' && config.warnExpressionErrors) { - warn('Error when evaluating expression "' + this.expression + '". ' + (config.debug ? '' : 'Turn on debug mode to see stack trace.'), e); + warn('Error when evaluating expression ' + '"' + this.expression + '": ' + e.toString(), this.vm); } } // "touch" every property so they are all tracked as @@ -3279,14 +3165,14 @@ this.setter.call(scope, scope, value); } catch (e) { if ('development' !== 'production' && config.warnExpressionErrors) { - warn('Error when evaluating setter "' + this.expression + '"', e); + warn('Error when evaluating setter ' + '"' + this.expression + '": ' + e.toString(), this.vm); } } // two-way sync for v-for alias var forContext = scope.$forContext; if (forContext && forContext.alias === this.expression) { if (forContext.filters) { - 'development' !== 'production' && warn('It seems you are using two-way binding on ' + 'a v-for alias (' + this.expression + '), and the ' + 'v-for has filters. This will not work properly. ' + 'Either remove the filters or use an array of ' + 'objects and bind to object properties instead.'); + 'development' !== 'production' && warn('It seems you are using two-way binding on ' + 'a v-for alias (' + this.expression + '), and the ' + 'v-for has filters. This will not work properly. ' + 'Either remove the filters or use an array of ' + 'objects and bind to object properties instead.', this.vm); return; } forContext._withLock(function (){ @@ -3787,6 +3673,7 @@ * @param {DocumentFragment} frag * @param {Vue} [host] * @param {Object} [scope] + * @param {Fragment} [parentFrag] */ function Fragment(linker, vm, frag, host, scope, parentFrag){ this.children = []; @@ -4025,15 +3912,16 @@ var EL = 1500; var COMPONENT = 1500; var PARTIAL = 1750; - var FOR = 2000; - var IF = 2000; - var SLOT = 2100; + var IF = 2100; + var FOR = 2200; + var SLOT = 2300; var uid$3 = 0; var vFor = { priority: FOR, + terminal: true, params: ['track-by', 'stagger', 'enter-stagger', 'leave-stagger'], @@ -4052,7 +3940,7 @@ } if (!this.alias) { - 'development' !== 'production' && warn('Alias is required in v-for.'); + 'development' !== 'production' && warn('Invalid v-for expression "' + this.descriptor.raw + '": ' + 'alias is required.', this.vm); return; } @@ -4143,7 +4031,9 @@ // update data for track-by, object repeat & // primitive values. if (trackByKey || convertedFromObject || primitive) { - frag.scope[alias] = value; + withoutConversion(function (){ + frag.scope[alias] = value; + }); } } else { // new isntance @@ -4233,7 +4123,11 @@ // for two-way binding on alias scope.$forContext = this; // define scope properties - defineReactive(scope, alias, value, true /* do not observe */); + // important: define the scope alias without forced conversion + // so that frozen data structures remain non-reactive. + withoutConversion(function (){ + defineReactive(scope, alias, value); + }); defineReactive(scope, '$index', index); if (key) { defineReactive(scope, '$key', key); @@ -4388,7 +4282,7 @@ var primitive = !isObject(value); var id; if (key || trackByKey || primitive) { - id = trackByKey ? trackByKey === '$index' ? index : value[trackByKey] : key || value; + id = trackByKey ? trackByKey === '$index' ? index : getPath(value, trackByKey) : key || value; if (!cache[id]) { cache[id] = frag; } else if (trackByKey !== '$index') { @@ -4423,7 +4317,7 @@ var primitive = !isObject(value); var frag; if (key || trackByKey || primitive) { - var id = trackByKey ? trackByKey === '$index' ? index : value[trackByKey] : key || value; + var id = trackByKey ? trackByKey === '$index' ? index : getPath(value, trackByKey) : key || value; frag = this.cache[id]; } else { frag = value[this.id]; @@ -4450,7 +4344,7 @@ var key = hasOwn(scope, '$key') && scope.$key; var primitive = !isObject(value); if (trackByKey || key || primitive) { - var id = trackByKey ? trackByKey === '$index' ? index : value[trackByKey] : key || value; + var id = trackByKey ? trackByKey === '$index' ? index : getPath(value, trackByKey) : key || value; this.cache[id] = null; } else { value[this.id] = null; @@ -4602,13 +4496,14 @@ if ('development' !== 'production') { vFor.warnDuplicate = function (value){ - warn('Duplicate value found in v-for="' + this.descriptor.raw + '": ' + JSON.stringify(value) + '. Use track-by="$index" if ' + 'you are expecting duplicate values.'); + warn('Duplicate value found in v-for="' + this.descriptor.raw + '": ' + JSON.stringify(value) + '. Use track-by="$index" if ' + 'you are expecting duplicate values.', this.vm); }; } var vIf = { priority: IF, + terminal: true, bind: function bind(){ var el = this.el; @@ -4623,7 +4518,7 @@ this.anchor = createAnchor('v-if'); replace(el, this.anchor); } else { - 'development' !== 'production' && warn('v-if="' + this.expression + '" cannot be ' + 'used on an instance root element.'); + 'development' !== 'production' && warn('v-if="' + this.expression + '" cannot be ' + 'used on an instance root element.', this.vm); this.invalid = true; } }, @@ -5056,7 +4951,7 @@ // friendly warning... this.checkFilters(); if (this.hasRead && !this.hasWrite) { - 'development' !== 'production' && warn('It seems you are using a read-only filter with ' + 'v-model. You might want to use a two-way filter ' + 'to ensure correct behavior.'); + 'development' !== 'production' && warn('It seems you are using a read-only filter with ' + 'v-model="' + this.descriptor.raw + '". ' + 'You might want to use a two-way filter to ensure correct behavior.', this.vm); } var el = this.el; var tag = el.tagName; @@ -5068,7 +4963,7 @@ } else if (tag === 'TEXTAREA') { handler = handlers.text; } else { - 'development' !== 'production' && warn('v-model does not support element type: ' + tag); + 'development' !== 'production' && warn('v-model does not support element type: ' + tag, this.vm); return; } el.__v_model = this; @@ -5184,7 +5079,7 @@ } if (typeof handler !== 'function') { - 'development' !== 'production' && warn('v-on:' + this.arg + '="' + this.expression + '" expects a function value, ' + 'got ' + handler); + 'development' !== 'production' && warn('v-on:' + this.arg + '="' + this.expression + '" expects a function value, ' + 'got ' + handler, this.vm); return; } @@ -5277,11 +5172,17 @@ if (value) { var isImportant = importantRE.test(value) ? 'important' : ''; if (isImportant) { + /* istanbul ignore if */ + if ('development' !== 'production') { + warn('It\'s probably a bad idea to use !important with inline rules. ' + 'This feature will be deprecated in a future version of Vue.'); + } value = value.replace(importantRE, '').trim(); + this.el.style.setProperty(prop.kebab, value, isImportant); + } else { + this.el.style[prop.camel] = value; } - this.el.style.setProperty(prop, value, isImportant); } else { - this.el.style.removeProperty(prop); + this.el.style[prop.camel] = ''; } } @@ -5326,11 +5227,17 @@ while (i--) { prefixed = camelPrefixes[i] + upper; if (prefixed in testEl.style) { - return prefixes[i] + prop; + return { + kebab: prefixes[i] + prop, + camel: prefixed + }; } } if (camel in testEl.style) { - return prop; + return { + kebab: prop, + camel: camel + }; } } @@ -5377,7 +5284,7 @@ // only allow binding on native attributes if (disallowedInterpAttrRE.test(attr) || attr === 'name' && (tag === 'PARTIAL' || tag === 'SLOT')) { - 'development' !== 'production' && warn(attr + '="' + descriptor.raw + '": ' + 'attribute interpolation is not allowed in Vue.js ' + 'directives and special attributes.'); + 'development' !== 'production' && warn(attr + '="' + descriptor.raw + '": ' + 'attribute interpolation is not allowed in Vue.js ' + 'directives and special attributes.', this.vm); this.el.removeAttribute(attr); this.invalid = true; } @@ -5387,12 +5294,12 @@ var raw = attr + '="' + descriptor.raw + '": '; // warn src if (attr === 'src') { - warn(raw + 'interpolation in "src" attribute will cause ' + 'a 404 request. Use v-bind:src instead.'); + warn(raw + 'interpolation in "src" attribute will cause ' + 'a 404 request. Use v-bind:src instead.', this.vm); } // warn style if (attr === 'style') { - warn(raw + 'interpolation in "style" attribute will cause ' + 'the attribute to be discarded in Internet Explorer. ' + 'Use v-bind:style instead.'); + warn(raw + 'interpolation in "style" attribute will cause ' + 'the attribute to be discarded in Internet Explorer. ' + 'Use v-bind:style instead.', this.vm); } } } @@ -5488,7 +5395,7 @@ var ref = { bind: function bind(){ - 'development' !== 'production' && warn('v-ref:' + this.arg + ' must be used on a child ' + 'component. Found on <' + this.el.tagName.toLowerCase() + '>.'); + 'development' !== 'production' && warn('v-ref:' + this.arg + ' must be used on a child ' + 'component. Found on <' + this.el.tagName.toLowerCase() + '>.', this.vm); } }; @@ -5534,52 +5441,83 @@ handleObject: function handleObject(value){ this.cleanup(value); - var keys = this.prevKeys = Object.keys(value); - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - if (value[key]) { - addClass(this.el, key); - } else { - removeClass(this.el, key); - } - } + this.prevKeys = Object.keys(value); + setObjectClasses(this.el, value); }, handleArray: function handleArray(value){ this.cleanup(value); for (var i = 0, l = value.length; i < l; i++) { - if (value[i]) { - addClass(this.el, value[i]); + var val = value[i]; + if (val && isPlainObject(val)) { + setObjectClasses(this.el, val); + } else if (val && typeof val === 'string') { + addClass(this.el, val); } } this.prevKeys = value.slice(); }, cleanup: function cleanup(value){ - if (this.prevKeys) { - var i = this.prevKeys.length; - while (i--) { - var key = this.prevKeys[i]; - if (key && (!value || !contains(value, key))) { - removeClass(this.el, key); - } + if (!this.prevKeys) return; + + var i = this.prevKeys.length; + while (i--) { + var key = this.prevKeys[i]; + if (!key) continue; + + var keys = isPlainObject(key) ? Object.keys(key) : [key]; + for (var j = 0, l = keys.length; j < l; j++) { + toggleClasses(this.el, keys[j], removeClass); } } } }; + function setObjectClasses(el, obj){ + var keys = Object.keys(obj); + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + if (!obj[key]) continue; + toggleClasses(el, key, addClass); + } + } + function stringToObject(value){ var res = {}; var keys = value.trim().split(/\s+/); - var i = keys.length; - while (i--) { + for (var i = 0, l = keys.length; i < l; i++) { res[keys[i]] = true; } return res; } - function contains(value, key){ - return isArray(value) ? value.indexOf(key) > -1 : hasOwn(value, key); + /** + * Add or remove a class/classes on an element + * + * @param {Element} el + * @param {String} key The class name. This may or may not + * contain a space character, in such a + * case we'll deal with multiple class + * names at once. + * @param {Function} fn + */ + + function toggleClasses(el, key, fn){ + key = key.trim(); + + if (key.indexOf(' ') === -1) { + fn(el, key); + return; + } + + // The key contains one or more space characters. + // Since a class name doesn't accept such characters, we + // treat it as multiple classes. + var keys = key.split(/\s+/); + for (var i = 0, l = keys.length; i < l; i++) { + fn(el, keys[i]); + } } var component = { @@ -5678,16 +5616,19 @@ /** * Resolve the component constructor to use when creating * the child vm. + * + * @param {String|Function} value + * @param {Function} cb */ - resolveComponent: function resolveComponent(id, cb){ + resolveComponent: function resolveComponent(value, cb){ var self = this; this.pendingComponentCb = cancellable(function (Component){ - self.ComponentName = Component.options.name || id; + self.ComponentName = Component.options.name || (typeof value === 'string' ? value : null); self.Component = Component; cb(); }); - this.vm._resolveComponent(id, this.pendingComponentCb); + this.vm._resolveComponent(value, this.pendingComponentCb); }, /** @@ -5794,7 +5735,7 @@ } /* istanbul ignore if */ if ('development' !== 'production' && this.el.hasAttribute('transition') && child._isFragment) { - warn('Transitions will not work on a fragment instance. ' + 'Template: ' + child.$options.template); + warn('Transitions will not work on a fragment instance. ' + 'Template: ' + child.$options.template, child); } return child; } @@ -5819,7 +5760,9 @@ unbuild: function unbuild(defer){ if (this.waitingFor) { - this.waitingFor.$destroy(); + if (!this.keepAlive) { + this.waitingFor.$destroy(); + } this.waitingFor = null; } var child = this.childVM; @@ -5938,6 +5881,372 @@ } } + var propBindingModes = config._propBindingModes; + var empty = {}; + + // regexes + var identRE$1 = /^[$_a-zA-Z]+[\w$]*$/; + var settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/; + + /** + * Compile props on a root element and return + * a props link function. + * + * @param {Element|DocumentFragment} el + * @param {Array} propOptions + * @param {Vue} vm + * @return {Function} propsLinkFn + */ + + function compileProps(el, propOptions, vm){ + var props = []; + var names = Object.keys(propOptions); + var i = names.length; + var options, name, attr, value, path, parsed, prop; + while (i--) { + name = names[i]; + options = propOptions[name] || empty; + + if ('development' !== 'production' && name === '$data') { + warn('Do not use $data as prop.', vm); + continue; + } + + // props could contain dashes, which will be + // interpreted as minus calculations by the parser + // so we need to camelize the path here + path = camelize(name); + if (!identRE$1.test(path)) { + 'development' !== 'production' && warn('Invalid prop key: "' + name + '". Prop keys ' + 'must be valid identifiers.', vm); + continue; + } + + prop = { + name: name, + path: path, + options: options, + mode: propBindingModes.ONE_WAY, + raw: null + }; + + attr = hyphenate(name); + // first check dynamic version + if ((value = getBindAttr(el, attr)) === null) { + if ((value = getBindAttr(el, attr + '.sync')) !== null) { + prop.mode = propBindingModes.TWO_WAY; + } else if ((value = getBindAttr(el, attr + '.once')) !== null) { + prop.mode = propBindingModes.ONE_TIME; + } + } + if (value !== null) { + // has dynamic binding! + prop.raw = value; + parsed = parseDirective(value); + value = parsed.expression; + prop.filters = parsed.filters; + // check binding type + if (isLiteral(value) && !parsed.filters) { + // for expressions containing literal numbers and + // booleans, there's no need to setup a prop binding, + // so we can optimize them as a one-time set. + prop.optimizedLiteral = true; + } else { + prop.dynamic = true; + // check non-settable path for two-way bindings + if ('development' !== 'production' && prop.mode === propBindingModes.TWO_WAY && !settablePathRE.test(value)) { + prop.mode = propBindingModes.ONE_WAY; + warn('Cannot bind two-way prop with non-settable ' + 'parent path: ' + value, vm); + } + } + prop.parentPath = value; + + // warn required two-way + if ('development' !== 'production' && options.twoWay && prop.mode !== propBindingModes.TWO_WAY) { + warn('Prop "' + name + '" expects a two-way binding type.', vm); + } + } else if ((value = getAttr(el, attr)) !== null) { + // has literal binding! + prop.raw = value; + } else if ('development' !== 'production') { + // check possible camelCase prop usage + var lowerCaseName = path.toLowerCase(); + value = /[A-Z\-]/.test(name) && (el.getAttribute(lowerCaseName) || el.getAttribute(':' + lowerCaseName) || el.getAttribute('v-bind:' + lowerCaseName) || el.getAttribute(':' + lowerCaseName + '.once') || el.getAttribute('v-bind:' + lowerCaseName + '.once') || el.getAttribute(':' + lowerCaseName + '.sync') || el.getAttribute('v-bind:' + lowerCaseName + '.sync')); + if (value) { + warn('Possible usage error for prop `' + lowerCaseName + '` - ' + 'did you mean `' + attr + '`? HTML is case-insensitive, remember to use ' + 'kebab-case for props in templates.', vm); + } else if (options.required) { + // warn missing required + warn('Missing required prop: ' + name, vm); + } + } + // push prop + props.push(prop); + } + return makePropsLinkFn(props); + } + + /** + * Build a function that applies props to a vm. + * + * @param {Array} props + * @return {Function} propsLinkFn + */ + + function makePropsLinkFn(props){ + return function propsLinkFn(vm, scope){ + // store resolved props info + vm._props = {}; + var i = props.length; + var prop, path, options, value, raw; + while (i--) { + prop = props[i]; + raw = prop.raw; + path = prop.path; + options = prop.options; + vm._props[path] = prop; + if (raw === null) { + // initialize absent prop + initProp(vm, prop, undefined); + } else if (prop.dynamic) { + // dynamic prop + if (prop.mode === propBindingModes.ONE_TIME) { + // one time binding + value = (scope || vm._context || vm).$get(prop.parentPath); + initProp(vm, prop, value); + } else { + if (vm._context) { + // dynamic binding + vm._bindDir({ + name: 'prop', + def: propDef, + prop: prop + }, null, null, scope); // el, host, scope + } else { + // root instance + initProp(vm, prop, vm.$get(prop.parentPath)); + } + } + } else if (prop.optimizedLiteral) { + // optimized literal, cast it and just set once + var stripped = stripQuotes(raw); + value = stripped === raw ? toBoolean(toNumber(raw)) : stripped; + initProp(vm, prop, value); + } else { + // string literal, but we need to cater for + // Boolean props with no value, or with same + // literal value (e.g. disabled="disabled") + // see https://github.com/vuejs/vue-loader/issues/182 + value = options.type === Boolean && (raw === '' || raw === hyphenate(prop.name)) ? true : raw; + initProp(vm, prop, value); + } + } + }; + } + + /** + * Process a prop with a rawValue, applying necessary coersions, + * default values & assertions and call the given callback with + * processed value. + * + * @param {Vue} vm + * @param {Object} prop + * @param {*} rawValue + * @param {Function} fn + */ + + function processPropValue(vm, prop, rawValue, fn){ + var isSimple = prop.dynamic && isSimplePath(prop.parentPath); + var value = rawValue; + if (value === undefined) { + value = getPropDefaultValue(vm, prop); + } + value = coerceProp(prop, value); + var coerced = value !== rawValue; + if (!assertProp(prop, value, vm)) { + value = undefined; + } + if (isSimple && !coerced) { + withoutConversion(function (){ + fn(value); + }); + } else { + fn(value); + } + } + + /** + * Set a prop's initial value on a vm and its data object. + * + * @param {Vue} vm + * @param {Object} prop + * @param {*} value + */ + + function initProp(vm, prop, value){ + processPropValue(vm, prop, value, function (value){ + defineReactive(vm, prop.path, value); + }); + } + + /** + * Update a prop's value on a vm. + * + * @param {Vue} vm + * @param {Object} prop + * @param {*} value + */ + + function updateProp(vm, prop, value){ + processPropValue(vm, prop, value, function (value){ + vm[prop.path] = value; + }); + } + + /** + * Get the default value of a prop. + * + * @param {Vue} vm + * @param {Object} prop + * @return {*} + */ + + function getPropDefaultValue(vm, prop){ + // no default, return undefined + var options = prop.options; + if (!hasOwn(options, 'default')) { + // absent boolean value defaults to false + return options.type === Boolean ? false : undefined; + } + var def = options['default']; + // warn against non-factory defaults for Object & Array + if (isObject(def)) { + 'development' !== 'production' && warn('Invalid default value for prop "' + prop.name + '": ' + 'Props with type Object/Array must use a factory function ' + 'to return the default value.', vm); + } + // call factory function for non-Function types + return typeof def === 'function' && options.type !== Function ? def.call(vm) : def; + } + + /** + * Assert whether a prop is valid. + * + * @param {Object} prop + * @param {*} value + * @param {Vue} vm + */ + + function assertProp(prop, value, vm){ + if (!prop.options.required && ( // non-required + prop.raw === null || // abscent + value == null) // null or undefined + ) { + return true; + } + var options = prop.options; + var type = options.type; + var valid = !type; + var expectedTypes = []; + if (type) { + if (!isArray(type)) { + type = [type]; + } + for (var i = 0; i < type.length && !valid; i++) { + var assertedType = assertType(value, type[i]); + expectedTypes.push(assertedType.expectedType); + valid = assertedType.valid; + } + } + if (!valid) { + if ('development' !== 'production') { + warn('Invalid prop: type check failed for prop "' + prop.name + '".' + ' Expected ' + expectedTypes.map(formatType).join(', ') + ', got ' + formatValue(value) + '.', vm); + } + return false; + } + var validator = options.validator; + if (validator) { + if (!validator(value)) { + 'development' !== 'production' && warn('Invalid prop: custom validator check failed for prop "' + prop.name + '".', vm); + return false; + } + } + return true; + } + + /** + * Force parsing value with coerce option. + * + * @param {*} value + * @param {Object} options + * @return {*} + */ + + function coerceProp(prop, value){ + var coerce = prop.options.coerce; + if (!coerce) { + return value; + } + // coerce is a function + return coerce(value); + } + + /** + * Assert the type of a value + * + * @param {*} value + * @param {Function} type + * @return {Object} + */ + + function assertType(value, type){ + var valid; + var expectedType; + if (type === String) { + expectedType = 'string'; + valid = typeof value === expectedType; + } else if (type === Number) { + expectedType = 'number'; + valid = typeof value === expectedType; + } else if (type === Boolean) { + expectedType = 'boolean'; + valid = typeof value === expectedType; + } else if (type === Function) { + expectedType = 'function'; + valid = typeof value === expectedType; + } else if (type === Object) { + expectedType = 'object'; + valid = isPlainObject(value); + } else if (type === Array) { + expectedType = 'array'; + valid = isArray(value); + } else { + valid = value instanceof type; + } + return { + valid: valid, + expectedType: expectedType + }; + } + + /** + * Format type for output + * + * @param {String} type + * @return {String} + */ + + function formatType(type){ + return type ? type.charAt(0).toUpperCase() + type.slice(1) : 'custom type'; + } + + /** + * Format value + * + * @param {*} value + * @return {String} + */ + + function formatValue(val){ + return Object.prototype.toString.call(val).slice(8, -1); + } + var bindingModes = config._propBindingModes; var propDef = { @@ -5952,10 +6261,7 @@ var twoWay = prop.mode === bindingModes.TWO_WAY; var parentWatcher = this.parentWatcher = new Watcher(parent, parentKey, function (val){ - val = coerceProp(prop, val); - if (assertProp(prop, val)) { - child[childKey] = val; - } + updateProp(child, prop, val); }, { twoWay: twoWay, filters: prop.filters, @@ -6033,6 +6339,32 @@ var transDurationProp = transitionProp + 'Duration'; var animDurationProp = animationProp + 'Duration'; + /** + * If a just-entered element is applied the + * leave class while its enter transition hasn't started yet, + * and the transitioned property has the same value for both + * enter/leave, then the leave transition will be skipped and + * the transitionend event never fires. This function ensures + * its callback to be called after a transition has started + * by waiting for double raf. + * + * It falls back to setTimeout on devices that support CSS + * transitions but not raf (e.g. Android 4.2 browser) - since + * these environments are usually slow, we are giving it a + * relatively large timeout. + */ + + var raf = inBrowser && window.requestAnimationFrame; + var waitForTransitionStart = raf + /* istanbul ignore next */ + ? function (fn){ + raf(function (){ + raf(fn); + }); + } : function (fn){ + setTimeout(fn, 50); + }; + /** * A Transition object that encapsulates the state and logic * of the transition. @@ -6059,7 +6391,7 @@ /* istanbul ignore if */ if ('development' !== 'production') { if (this.type && this.type !== TYPE_TRANSITION && this.type !== TYPE_ANIMATION) { - warn('invalid CSS transition type for transition="' + this.id + '": ' + this.type); + warn('invalid CSS transition type for transition="' + this.id + '": ' + this.type, vm); } } // bind @@ -6118,19 +6450,13 @@ */ p$1.enterNextTick = function (){ - // Important hack: - // in Chrome, if a just-entered element is applied the - // leave class while its interpolated property still has - // a very small value (within one frame), Chrome will - // skip the leave transition entirely and not firing the - // transtionend event. Therefore we need to protected - // against such cases using a one-frame timeout. - this.justEntered = true; - var self = this; - setTimeout(function (){ - self.justEntered = false; - }, 17); + var _this = this; + // prevent transition skipping + this.justEntered = true; + waitForTransitionStart(function (){ + _this.justEntered = false; + }); var enterDone = this.enterDone; var type = this.getCssTransitionType(this.enterClass); if (!this.pendingJsCb) { @@ -6407,164 +6733,6 @@ transition: transition$1 }; - var propBindingModes = config._propBindingModes; - var empty = {}; - - // regexes - var identRE$1 = /^[$_a-zA-Z]+[\w$]*$/; - var settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/; - - /** - * Compile props on a root element and return - * a props link function. - * - * @param {Element|DocumentFragment} el - * @param {Array} propOptions - * @return {Function} propsLinkFn - */ - - function compileProps(el, propOptions){ - var props = []; - var names = Object.keys(propOptions); - var i = names.length; - var options, name, attr, value, path, parsed, prop; - while (i--) { - name = names[i]; - options = propOptions[name] || empty; - - if ('development' !== 'production' && name === '$data') { - warn('Do not use $data as prop.'); - continue; - } - - // props could contain dashes, which will be - // interpreted as minus calculations by the parser - // so we need to camelize the path here - path = camelize(name); - if (!identRE$1.test(path)) { - 'development' !== 'production' && warn('Invalid prop key: "' + name + '". Prop keys ' + 'must be valid identifiers.'); - continue; - } - - prop = { - name: name, - path: path, - options: options, - mode: propBindingModes.ONE_WAY, - raw: null - }; - - attr = hyphenate(name); - // first check dynamic version - if ((value = getBindAttr(el, attr)) === null) { - if ((value = getBindAttr(el, attr + '.sync')) !== null) { - prop.mode = propBindingModes.TWO_WAY; - } else if ((value = getBindAttr(el, attr + '.once')) !== null) { - prop.mode = propBindingModes.ONE_TIME; - } - } - if (value !== null) { - // has dynamic binding! - prop.raw = value; - parsed = parseDirective(value); - value = parsed.expression; - prop.filters = parsed.filters; - // check binding type - if (isLiteral(value) && !parsed.filters) { - // for expressions containing literal numbers and - // booleans, there's no need to setup a prop binding, - // so we can optimize them as a one-time set. - prop.optimizedLiteral = true; - } else { - prop.dynamic = true; - // check non-settable path for two-way bindings - if ('development' !== 'production' && prop.mode === propBindingModes.TWO_WAY && !settablePathRE.test(value)) { - prop.mode = propBindingModes.ONE_WAY; - warn('Cannot bind two-way prop with non-settable ' + 'parent path: ' + value); - } - } - prop.parentPath = value; - - // warn required two-way - if ('development' !== 'production' && options.twoWay && prop.mode !== propBindingModes.TWO_WAY) { - warn('Prop "' + name + '" expects a two-way binding type.'); - } - } else if ((value = getAttr(el, attr)) !== null) { - // has literal binding! - prop.raw = value; - } else if ('development' !== 'production') { - // check possible camelCase prop usage - var lowerCaseName = path.toLowerCase(); - value = /[A-Z\-]/.test(name) && (el.getAttribute(lowerCaseName) || el.getAttribute(':' + lowerCaseName) || el.getAttribute('v-bind:' + lowerCaseName) || el.getAttribute(':' + lowerCaseName + '.once') || el.getAttribute('v-bind:' + lowerCaseName + '.once') || el.getAttribute(':' + lowerCaseName + '.sync') || el.getAttribute('v-bind:' + lowerCaseName + '.sync')); - if (value) { - warn('Possible usage error for prop `' + lowerCaseName + '` - ' + 'did you mean `' + attr + '`? HTML is case-insensitive, remember to use ' + 'kebab-case for props in templates.'); - } else if (options.required) { - // warn missing required - warn('Missing required prop: ' + name); - } - } - // push prop - props.push(prop); - } - return makePropsLinkFn(props); - } - - /** - * Build a function that applies props to a vm. - * - * @param {Array} props - * @return {Function} propsLinkFn - */ - - function makePropsLinkFn(props){ - return function propsLinkFn(vm, scope){ - // store resolved props info - vm._props = {}; - var i = props.length; - var prop, path, options, value, raw; - while (i--) { - prop = props[i]; - raw = prop.raw; - path = prop.path; - options = prop.options; - vm._props[path] = prop; - if (raw === null) { - // initialize absent prop - initProp(vm, prop, undefined); - } else if (prop.dynamic) { - // dynamic prop - if (prop.mode === propBindingModes.ONE_TIME) { - // one time binding - value = (scope || vm._context || vm).$get(prop.parentPath); - initProp(vm, prop, value); - } else { - if (vm._context) { - // dynamic binding - vm._bindDir({ - name: 'prop', - def: propDef, - prop: prop - }, null, null, scope); // el, host, scope - } else { - // root instance - initProp(vm, prop, vm.$get(prop.parentPath)); - } - } - } else if (prop.optimizedLiteral) { - // optimized literal, cast it and just set once - var stripped = stripQuotes(raw); - value = stripped === raw ? toBoolean(toNumber(raw)) : stripped; - initProp(vm, prop, value); - } else { - // string literal, but we need to cater for - // Boolean props with no value - value = options.type === Boolean && raw === '' ? true : raw; - initProp(vm, prop, value); - } - } - }; - } - // special binding prefixes var bindRE = /^v-bind:|^:/; var onRE = /^v-on:|^@/; @@ -6572,11 +6740,9 @@ var modifierRE = /\.[^\.]+/g; var transitionRE = /^(v-bind:|:)?transition$/; - // terminal directives - var terminalDirectives = ['for', 'if']; - // default directive priority var DEFAULT_PRIORITY = 1000; + var DEFAULT_TERMINAL_PRIORITY = 2000; /** * Compile a template and return a reusable composite link @@ -6717,7 +6883,7 @@ */ function compileAndLinkProps(vm, el, props, scope){ - var propsLinkFn = compileProps(el, props); + var propsLinkFn = compileProps(el, props, vm); var propDirs = linkAndCapture(function (){ propsLinkFn(vm, scope); }, vm); @@ -6843,9 +7009,10 @@ } var linkFn; var hasAttrs = el.hasAttributes(); + var attrs = hasAttrs && toArray(el.attributes); // check terminal directives (for & if) if (hasAttrs) { - linkFn = checkTerminalDirectives(el, options); + linkFn = checkTerminalDirectives(el, attrs, options); } // check element directives if (!linkFn) { @@ -6857,7 +7024,7 @@ } // normal directives if (!linkFn && hasAttrs) { - linkFn = compileDirectives(el.attributes, options); + linkFn = compileDirectives(attrs, options); } return linkFn; } @@ -7087,11 +7254,12 @@ * If it finds one, return a terminal link function. * * @param {Element} el + * @param {Array} attrs * @param {Object} options * @return {Function} terminalLinkFn */ - function checkTerminalDirectives(el, options){ + function checkTerminalDirectives(el, attrs, options){ // skip v-pre if (getAttr(el, 'v-pre') !== null) { return skip; @@ -7103,14 +7271,29 @@ return skip; } } - var value, dirName; - for (var i = 0, l = terminalDirectives.length; i < l; i++) { - dirName = terminalDirectives[i]; - value = el.getAttribute('v-' + dirName); - if (value != null) { - return makeTerminalNodeLinkFn(el, dirName, value, options); + + var attr, name, value, modifiers, matched, dirName, rawName, arg, def, termDef; + for (var i = 0, j = attrs.length; i < j; i++) { + attr = attrs[i]; + modifiers = parseModifiers(attr.name); + name = attr.name.replace(modifierRE, ''); + if (matched = name.match(dirAttrRE)) { + def = resolveAsset(options, 'directives', matched[1]); + if (def && def.terminal) { + if (!termDef || (def.priority || DEFAULT_TERMINAL_PRIORITY) > termDef.priority) { + termDef = def; + rawName = attr.name; + value = attr.value; + dirName = matched[1]; + arg = matched[2]; + } + } } } + + if (termDef) { + return makeTerminalNodeLinkFn(el, dirName, value, options, termDef, rawName, arg, modifiers); + } } function skip(){} @@ -7127,20 +7310,24 @@ * @param {String} dirName * @param {String} value * @param {Object} options - * @param {Object} [def] + * @param {Object} def + * @param {String} [rawName] + * @param {String} [arg] + * @param {Object} [modifiers] * @return {Function} terminalLinkFn */ - function makeTerminalNodeLinkFn(el, dirName, value, options, def){ + function makeTerminalNodeLinkFn(el, dirName, value, options, def, rawName, arg, modifiers){ var parsed = parseDirective(value); var descriptor = { name: dirName, + arg: arg, expression: parsed.expression, filters: parsed.filters, raw: value, - // either an element directive, or if/for - // #2366 or custom terminal directive - def: def || resolveAsset(options, 'directives', dirName) + attr: rawName, + modifiers: modifiers, + def: def }; // check ref for v-for and router-view if (dirName === 'for' || dirName === 'router-view') { @@ -7189,7 +7376,7 @@ if (name === 'class' && Array.prototype.some.call(attrs, function (attr){ return attr.name === ':class' || attr.name === 'v-bind:class'; })) { - warn('class="' + rawValue + '": Do not mix mustache interpolation ' + 'and v-bind for "class" on the same element. Use one or the other.'); + warn('class="' + rawValue + '": Do not mix mustache interpolation ' + 'and v-bind for "class" on the same element. Use one or the other.', options); } } } else @@ -7227,12 +7414,7 @@ continue; } - dirDef = resolveAsset(options, 'directives', dirName); - - if ('development' !== 'production') { - assertAsset(dirDef, 'directive', dirName); - } - + dirDef = resolveAsset(options, 'directives', dirName, true); if (dirDef) { pushDir(dirName, dirDef); } @@ -7483,6 +7665,9 @@ (contents[name] || (contents[name] = [])).push(el); } /* eslint-enable no-cond-assign */ + if ('development' !== 'production' && getBindAttr(el, 'slot')) { + warn('The "slot" attribute must be static.', vm.$parent); + } } for (name in contents) { contents[name] = extractFragment(contents[name], content); @@ -7517,7 +7702,6 @@ compile: compile, compileAndLinkProps: compileAndLinkProps, compileRoot: compileRoot, - terminalDirectives: terminalDirectives, transclude: transclude, resolveSlots: resolveSlots }); @@ -7565,7 +7749,7 @@ var el = options.el; var props = options.props; if (props && !el) { - 'development' !== 'production' && warn('Props will not be compiled if no `el` option is ' + 'provided at instantiation.'); + 'development' !== 'production' && warn('Props will not be compiled if no `el` option is ' + 'provided at instantiation.', this); } // make sure to convert string selectors into element now el = options.el = query(el); @@ -7581,6 +7765,10 @@ Vue.prototype._initData = function (){ var dataFn = this.$options.data; var data = this._data = dataFn ? dataFn() : {}; + if (!isPlainObject(data)) { + data = {}; + 'development' !== 'production' && warn('data functions should return an object.', this); + } var props = this._props; var runtimeData = this._runtimeData ? typeof this._runtimeData === 'function' ? this._runtimeData() : this._runtimeData : null; // proxy data on instance @@ -7596,7 +7784,7 @@ if (!props || !hasOwn(props, key) || runtimeData && hasOwn(runtimeData, key) && props[key].raw === null) { this._proxy(key); } else if ('development' !== 'production') { - warn('Data field "' + key + '" is already defined ' + 'as a prop. Use prop default value instead.'); + warn('Data field "' + key + '" is already defined ' + 'as a prop. Use prop default value instead.', this); } } // observe data @@ -7797,7 +7985,7 @@ handler._fromParent = true; vm.$on(name.replace(eventRE), handler); } else if ('development' !== 'production') { - warn('v-on:' + name + '="' + attrs[i].value + '"' + (vm.$options.name ? ' on component <' + vm.$options.name + '>' : '') + ' expects a function value, got ' + handler); + warn('v-on:' + name + '="' + attrs[i].value + '" ' + 'expects a function value, got ' + handler, vm); } } } @@ -7846,7 +8034,7 @@ if (method) { vm[action](key, method, options); } else { - 'development' !== 'production' && warn('Unknown method: "' + handler + '" when ' + 'registering callback for ' + action + ': "' + key + '".'); + 'development' !== 'production' && warn('Unknown method: "' + handler + '" when ' + 'registering callback for ' + action + ': "' + key + '".', vm); } } else if (handler && type === 'object') { register(vm, action, key, handler.handler, handler); @@ -7934,18 +8122,21 @@ * It registers a watcher with the expression and calls * the DOM update function when a change is triggered. * - * @param {String} name - * @param {Node} el - * @param {Vue} vm * @param {Object} descriptor * - {String} name * - {Object} def * - {String} expression * - {Array} [filters] + * - {Object} [modifiers] * - {Boolean} literal * - {String} attr + * - {String} arg * - {String} raw - * @param {Object} def - directive definition object + * - {String} [ref] + * - {Array} [interp] + * - {Boolean} [hasOneTime] + * @param {Vue} vm + * @param {Node} el * @param {Vue} [host] - transclusion host component * @param {Object} [scope] - v-for scope * @param {Fragment} [frag] - owner fragment @@ -7981,8 +8172,6 @@ * Initialize the directive, mixin definition properties, * setup the watcher, call definition bind() and update() * if present. - * - * @param {Object} def */ Directive.prototype._bind = function (){ @@ -8063,7 +8252,7 @@ var i = params.length; var key, val, mappedKey; while (i--) { - key = params[i]; + key = hyphenate(params[i]); mappedKey = camelize(key); val = getBindAttr(this.el, key); if (val != null) { @@ -8343,10 +8532,8 @@ /** * Create and bind a directive to an element. * - * @param {String} name - directive name + * @param {Object} descriptor - parsed directive descriptor * @param {Node} node - target node - * @param {Object} desc - parsed directive descriptor - * @param {Object} def - directive definition object * @param {Vue} [host] - transclusion host component * @param {Object} [scope] - v-for scope * @param {Fragment} [frag] - owner fragment @@ -8489,11 +8676,8 @@ Vue.prototype._applyFilters = function (value, oldValue, filters, write){ var filter, fn, args, arg, offset, i, l, j, k; for (i = 0, l = filters.length; i < l; i++) { - filter = filters[i]; - fn = resolveAsset(this.$options, 'filters', filter.name); - if ('development' !== 'production') { - assertAsset(fn, 'filter', filter.name); - } + filter = filters[write ? l - i - 1 : i]; + fn = resolveAsset(this.$options, 'filters', filter.name, true); if (!fn) continue; fn = write ? fn.write : fn.read || fn; if (typeof fn !== 'function') continue; @@ -8517,14 +8701,16 @@ * resolves asynchronously and caches the resolved * constructor on the factory. * - * @param {String} id + * @param {String|Function} value * @param {Function} cb */ - Vue.prototype._resolveComponent = function (id, cb){ - var factory = resolveAsset(this.$options, 'components', id); - if ('development' !== 'production') { - assertAsset(factory, 'component', id); + Vue.prototype._resolveComponent = function (value, cb){ + var factory; + if (typeof value === 'function') { + factory = value; + } else { + factory = resolveAsset(this.$options, 'components', value, true); } if (!factory) { return; @@ -8551,7 +8737,7 @@ cbs[i](res); } }, function reject(reason){ - 'development' !== 'production' && warn('Failed to resolve async component: ' + id + '. ' + (reason ? '\nReason: ' + reason : '')); + 'development' !== 'production' && warn('Failed to resolve async component' + (typeof value === 'string' ? ': ' + value : '') + '. ' + (reason ? '\nReason: ' + reason : '')); }); } } else { @@ -9129,7 +9315,7 @@ Vue.prototype.$mount = function (el){ if (this._isCompiled) { - 'development' !== 'production' && warn('$mount() should be called only once.'); + 'development' !== 'production' && warn('$mount() should be called only once.', this); return; } el = query(el); @@ -9160,6 +9346,9 @@ /** * Teardown the instance, simply delegate to the internal * _destroy. + * + * @param {Boolean} remove + * @param {Boolean} deferCleanup */ Vue.prototype.$destroy = function (remove, deferCleanup){ @@ -9172,6 +9361,8 @@ * * @param {Element|DocumentFragment} el * @param {Vue} [host] + * @param {Object} [scope] + * @param {Fragment} [frag] * @return {Function} */ @@ -9283,10 +9474,7 @@ }, insert: function insert(id){ - var partial = resolveAsset(this.vm.$options, 'partials', id); - if ('development' !== 'production') { - assertAsset(partial, 'partial', id); - } + var partial = resolveAsset(this.vm.$options, 'partials', id, true); if (partial) { this.factory = new FragmentFactory(this.vm, partial); vIf.insert.call(this); @@ -9342,9 +9530,7 @@ // because why not var n = delimiter === 'in' ? 3 : 2; // extract and flatten keys - var keys = toArray(arguments, n).reduce(function (prev, cur){ - return prev.concat(cur); - }, []); + var keys = Array.prototype.concat.apply([], toArray(arguments, n)); var res = []; var item, key, val, j; for (var i = 0, l = arr.length; i < l; i++) { @@ -9354,12 +9540,12 @@ if (j) { while (j--) { key = keys[j]; - if (key === '$key' && contains$1(item.$key, search) || contains$1(getPath(val, key), search)) { + if (key === '$key' && contains(item.$key, search) || contains(getPath(val, key), search)) { res.push(item); break; } } - } else if (contains$1(item, search)) { + } else if (contains(item, search)) { res.push(item); } } @@ -9369,26 +9555,58 @@ /** * Filter filter for arrays * - * @param {String} sortKey - * @param {String} reverse + * @param {String|Array|Function} ...sortKeys + * @param {Number} [order] */ - function orderBy(arr, sortKey, reverse){ + function orderBy(arr){ + var comparator = null; + var sortKeys = undefined; arr = convertArray(arr); - if (!sortKey) { - return arr; + + // determine order (last argument) + var args = toArray(arguments, 1); + var order = args[args.length - 1]; + if (typeof order === 'number') { + order = order < 0 ? -1 : 1; + args = args.length > 1 ? args.slice(0, -1) : args; + } else { + order = 1; } - var order = reverse && reverse < 0 ? -1 : 1; - // sort on a copy to avoid mutating original array - return arr.slice().sort(function (a, b){ - if (sortKey !== '$key') { - if (isObject(a) && '$value' in a) a = a.$value; - if (isObject(b) && '$value' in b) b = b.$value; + + // determine sortKeys & comparator + var firstArg = args[0]; + if (!firstArg) { + return arr; + } else if (typeof firstArg === 'function') { + // custom comparator + comparator = function (a, b){ + return firstArg(a, b) * order; + }; + } else { + // string keys. flatten first + sortKeys = Array.prototype.concat.apply([], args); + comparator = function (a, b, i){ + i = i || 0; + return i >= sortKeys.length - 1 ? baseCompare(a, b, i) : baseCompare(a, b, i) || comparator(a, b, i + 1); + }; + } + + function baseCompare(a, b, sortKeyIndex){ + var sortKey = sortKeys[sortKeyIndex]; + if (sortKey) { + if (sortKey !== '$key') { + if (isObject(a) && '$value' in a) a = a.$value; + if (isObject(b) && '$value' in b) b = b.$value; + } + a = isObject(a) ? getPath(a, sortKey) : a; + b = isObject(b) ? getPath(b, sortKey) : b; } - a = isObject(a) ? getPath(a, sortKey) : a; - b = isObject(b) ? getPath(b, sortKey) : b; return a === b ? 0 : a > b ? order : -order; - }); + } + + // sort on a copy to avoid mutating original array + return arr.slice().sort(comparator); } /** @@ -9398,20 +9616,20 @@ * @param {String} search */ - function contains$1(val, search){ + function contains(val, search){ var i; if (isPlainObject(val)) { var keys = Object.keys(val); i = keys.length; while (i--) { - if (contains$1(val[keys[i]], search)) { + if (contains(val[keys[i]], search)) { return true; } } } else if (isArray(val)) { i = val.length; while (i--) { - if (contains$1(val[i], search)) { + if (contains(val[i], search)) { return true; } } @@ -9708,17 +9926,19 @@ installGlobalAPI(Vue); - Vue.version = '1.0.18'; + Vue.version = '1.0.21'; // devtools global hook /* istanbul ignore next */ - if (config.devtools) { - if (devtools) { - devtools.emit('init', Vue); - } else if ('development' !== 'production' && inBrowser && /Chrome\/\d+/.test(window.navigator.userAgent)) { - console.log('Download the Vue Devtools for a better development experience:\n' + 'https://github.com/vuejs/vue-devtools'); + setTimeout(function (){ + if (config.devtools) { + if (devtools) { + devtools.emit('init', Vue); + } else if ('development' !== 'production' && inBrowser && /Chrome\/\d+/.test(window.navigator.userAgent)) { + console.log('Download the Vue Devtools for a better development experience:\n' + 'https://github.com/vuejs/vue-devtools'); + } } - } + }, 0); return Vue; }));