From fed8d94322ccdcfd68e71be8b05dbf8b73e94fe6 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Fri, 11 Nov 2016 15:17:55 +0100 Subject: [PATCH 01/30] add Dev notes in Readme and update global to --- README.md | 10 + example/example.build.js | 13800 +++++++++++++------------------------ example/example.js | 6 +- package.json | 2 +- src/vue-dragula.js | 2 +- yarn.lock | 2772 ++++++++ 6 files changed, 7611 insertions(+), 8981 deletions(-) create mode 100644 yarn.lock diff --git a/README.md b/README.md index 089d58c..1e93908 100644 --- a/README.md +++ b/README.md @@ -87,3 +87,13 @@ new Vue({ | removeModel | bagName, el, container, removeIndex | model was synced, removeIndex exposed | [1]: https://github.com/bevacqua/dragula + +## Development + +Following npm scripts are included: + +- `npm run build` to build new distribution in `/dist` +- `npm run dev` run example in dev mode +- `npm run lint` lint code using ESlint + +How to view the example!? WTF - I have node idea!!! Please document!!! :) \ No newline at end of file diff --git a/example/example.build.js b/example/example.build.js index 6a35425..1eea667 100644 --- a/example/example.build.js +++ b/example/example.build.js @@ -84,14 +84,14 @@ }, ready: function () { var _this = this - Vue.vueDragula.eventBus.$on( + Vue.$dragula.eventBus.$on( 'drop', function (args) { console.log('drop: ' + args[0]) console.log(_this.categories) } ) - Vue.vueDragula.eventBus.$on( + Vue.$dragula.eventBus.$on( 'dropModel', function (args) { console.log('dropModel: ' + args) @@ -101,7 +101,7 @@ }, methods: { onClick: function () { - console.log(Vue.vueDragula.find('first-bag')) + console.log(Vue.$dragula.find('first-bag')) window.alert('click event') }, testModify: function () { @@ -118,9944 +118,5783 @@ /* 1 */ /***/ function(module, exports, __webpack_require__) { - /* WEBPACK VAR INJECTION */(function(global, process) {/*! - * Vue.js v1.0.21 - * (c) 2016 Evan You + /* WEBPACK VAR INJECTION */(function(process) {/*! + * Vue.js v2.0.5 + * (c) 2014-2016 Evan You * Released under the MIT License. */ 'use strict'; - function set(obj, key, val) { - if (hasOwn(obj, key)) { - obj[key] = val; - return; - } - if (obj._isVue) { - set(obj._data, key, val); - return; - } - var ob = obj.__ob__; - if (!ob) { - obj[key] = val; - return; - } - ob.convert(key, val); - ob.dep.notify(); - if (ob.vms) { - var i = ob.vms.length; - while (i--) { - var vm = ob.vms[i]; - vm._proxy(key); - vm._digest(); - } - } - return val; - } + /* */ /** - * Delete a property and trigger change if necessary. - * - * @param {Object} obj - * @param {String} key + * Convert a value to a string that is actually rendered. */ - - function del(obj, key) { - if (!hasOwn(obj, key)) { - return; - } - delete obj[key]; - var ob = obj.__ob__; - if (!ob) { - return; - } - ob.dep.notify(); - if (ob.vms) { - var i = ob.vms.length; - while (i--) { - var vm = ob.vms[i]; - vm._unproxy(key); - vm._digest(); - } - } + function _toString (val) { + return val == null + ? '' + : typeof val === 'object' + ? JSON.stringify(val, null, 2) + : String(val) } - var hasOwnProperty = Object.prototype.hasOwnProperty; /** - * Check whether the object has the property. - * - * @param {Object} obj - * @param {String} key - * @return {Boolean} + * Convert a input value to a number for persistence. + * If the conversion fails, return original string. */ - - function hasOwn(obj, key) { - return hasOwnProperty.call(obj, key); + function toNumber (val) { + var n = parseFloat(val, 10); + return (n || n === 0) ? n : val } /** - * Check if an expression is a literal value. - * - * @param {String} exp - * @return {Boolean} + * Make a map and return a function for checking if a key + * is in that map. */ - - var literalValueRE = /^\s?(true|false|-?[\d\.]+|'[^']*'|"[^"]*")\s?$/; - - function isLiteral(exp) { - return literalValueRE.test(exp); + function makeMap ( + str, + expectsLowerCase + ) { + var map = Object.create(null); + var list = str.split(','); + for (var i = 0; i < list.length; i++) { + map[list[i]] = true; + } + return expectsLowerCase + ? function (val) { return map[val.toLowerCase()]; } + : function (val) { return map[val]; } } /** - * Check if a string starts with $ or _ - * - * @param {String} str - * @return {Boolean} + * Check if a tag is a built-in tag. */ - - function isReserved(str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F; - } + var isBuiltInTag = makeMap('slot,component', true); /** - * Guard text output, make sure undefined outputs - * empty string - * - * @param {*} value - * @return {String} + * Remove an item from an array */ - - function _toString(value) { - return value == null ? '' : value.toString(); + function remove$1 (arr, item) { + if (arr.length) { + var index = arr.indexOf(item); + if (index > -1) { + return arr.splice(index, 1) + } + } } /** - * Check and convert possible numeric strings to numbers - * before setting back to data - * - * @param {*} value - * @return {*|Number} + * Check whether the object has the property. */ - - function toNumber(value) { - if (typeof value !== 'string') { - return value; - } else { - var parsed = Number(value); - return isNaN(parsed) ? value : parsed; - } + var hasOwnProperty = Object.prototype.hasOwnProperty; + function hasOwn (obj, key) { + return hasOwnProperty.call(obj, key) } /** - * Convert string boolean literals into real booleans. - * - * @param {*} value - * @return {*|Boolean} + * Check if value is primitive */ - - function toBoolean(value) { - return value === 'true' ? true : value === 'false' ? false : value; + function isPrimitive (value) { + return typeof value === 'string' || typeof value === 'number' } /** - * Strip quotes from a string - * - * @param {String} str - * @return {String | false} + * Create a cached version of a pure function. */ - - function stripQuotes(str) { - var a = str.charCodeAt(0); - var b = str.charCodeAt(str.length - 1); - return a === b && (a === 0x22 || a === 0x27) ? str.slice(1, -1) : str; + function cached (fn) { + var cache = Object.create(null); + return function cachedFn (str) { + var hit = cache[str]; + return hit || (cache[str] = fn(str)) + } } /** * Camelize a hyphen-delmited string. - * - * @param {String} str - * @return {String} */ - var camelizeRE = /-(\w)/g; - - function camelize(str) { - return str.replace(camelizeRE, toUpper); - } - - function toUpper(_, c) { - return c ? c.toUpperCase() : ''; - } + var camelize = cached(function (str) { + return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) + }); /** - * Hyphenate a camelCase string. - * - * @param {String} str - * @return {String} + * Capitalize a string. */ - - var hyphenateRE = /([a-z\d])([A-Z])/g; - - function hyphenate(str) { - return str.replace(hyphenateRE, '$1-$2').toLowerCase(); - } + var capitalize = cached(function (str) { + return str.charAt(0).toUpperCase() + str.slice(1) + }); /** - * Converts hyphen/underscore/slash delimitered names into - * camelized classNames. - * - * e.g. my-component => MyComponent - * some_else => SomeElse - * some/comp => SomeComp - * - * @param {String} str - * @return {String} + * Hyphenate a camelCase string. */ - - var classifyRE = /(?:^|[-_\/])(\w)/g; - - function classify(str) { - return str.replace(classifyRE, toUpper); - } + var hyphenateRE = /([^-])([A-Z])/g; + var hyphenate = cached(function (str) { + return str + .replace(hyphenateRE, '$1-$2') + .replace(hyphenateRE, '$1-$2') + .toLowerCase() + }); /** * Simple bind, faster than native - * - * @param {Function} fn - * @param {Object} ctx - * @return {Function} */ - - function bind(fn, ctx) { - return function (a) { + function bind$1 (fn, ctx) { + function boundFn (a) { var l = arguments.length; - return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx); - }; + return l + ? l > 1 + ? fn.apply(ctx, arguments) + : fn.call(ctx, a) + : fn.call(ctx) + } + // record original fn length + boundFn._length = fn.length; + return boundFn } /** * Convert an Array-like object to a real Array. - * - * @param {Array-like} list - * @param {Number} [start] - start index - * @return {Array} */ - - function toArray(list, start) { + function toArray (list, start) { start = start || 0; var i = list.length - start; var ret = new Array(i); while (i--) { ret[i] = list[i + start]; } - return ret; + return ret } /** * Mix properties into target object. - * - * @param {Object} to - * @param {Object} from */ - - function extend(to, from) { - var keys = Object.keys(from); - var i = keys.length; - while (i--) { - to[keys[i]] = from[keys[i]]; + function extend (to, _from) { + for (var key in _from) { + to[key] = _from[key]; } - return to; + return to } /** * Quick object check - this is primarily used to tell * Objects from primitive values when we know the value * is a JSON-compliant type. - * - * @param {*} obj - * @return {Boolean} */ - - function isObject(obj) { - return obj !== null && typeof obj === 'object'; + function isObject (obj) { + return obj !== null && typeof obj === 'object' } /** * Strict object type check. Only returns true * for plain JavaScript objects. - * - * @param {*} obj - * @return {Boolean} */ - var toString = Object.prototype.toString; var OBJECT_STRING = '[object Object]'; - - function isPlainObject(obj) { - return toString.call(obj) === OBJECT_STRING; + function isPlainObject (obj) { + return toString.call(obj) === OBJECT_STRING } /** - * Array type check. - * - * @param {*} obj - * @return {Boolean} + * Merge an Array of Objects into a single Object. */ - - var isArray = Array.isArray; + function toObject (arr) { + var res = {}; + for (var i = 0; i < arr.length; i++) { + if (arr[i]) { + extend(res, arr[i]); + } + } + return res + } /** - * Define a property. - * - * @param {Object} obj - * @param {String} key - * @param {*} val - * @param {Boolean} [enumerable] + * Perform no operation. */ - - function def(obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); - } + function noop () {} /** - * Debounce a function so it only gets called after the - * input stops arriving after the given wait period. - * - * @param {Function} func - * @param {Number} wait - * @return {Function} - the debounced function + * Always return false. */ + var no = function () { return false; }; - function _debounce(func, wait) { - var timeout, args, context, timestamp, result; - var later = function later() { - var last = Date.now() - timestamp; - if (last < wait && last >= 0) { - timeout = setTimeout(later, wait - last); - } else { - timeout = null; - result = func.apply(context, args); - if (!timeout) context = args = null; - } - }; - return function () { - context = this; - args = arguments; - timestamp = Date.now(); - if (!timeout) { - timeout = setTimeout(later, wait); - } - return result; - }; + /** + * Generate a static keys string from compiler modules. + */ + function genStaticKeys (modules) { + return modules.reduce(function (keys, m) { + return keys.concat(m.staticKeys || []) + }, []).join(',') } /** - * Manual indexOf because it's slightly faster than - * native. - * - * @param {Array} arr - * @param {*} obj + * Check if two values are loosely equal - that is, + * if they are plain objects, do they have the same shape? */ + function looseEqual (a, b) { + /* eslint-disable eqeqeq */ + return a == b || ( + isObject(a) && isObject(b) + ? JSON.stringify(a) === JSON.stringify(b) + : false + ) + /* eslint-enable eqeqeq */ + } - function indexOf(arr, obj) { - var i = arr.length; - while (i--) { - if (arr[i] === obj) return i; + function looseIndexOf (arr, val) { + for (var i = 0; i < arr.length; i++) { + if (looseEqual(arr[i], val)) { return i } } - return -1; + return -1 } + /* */ + + var config = { + /** + * Option merge strategies (used in core/util/options) + */ + optionMergeStrategies: Object.create(null), + + /** + * Whether to suppress warnings. + */ + silent: false, + + /** + * Whether to enable devtools + */ + devtools: process.env.NODE_ENV !== 'production', + + /** + * Error handler for watcher errors + */ + errorHandler: null, + + /** + * Ignore certain custom elements + */ + ignoredElements: null, + + /** + * Custom user key aliases for v-on + */ + keyCodes: Object.create(null), + + /** + * Check if a tag is reserved so that it cannot be registered as a + * component. This is platform-dependent and may be overwritten. + */ + isReservedTag: no, + + /** + * Check if a tag is an unknown element. + * Platform-dependent. + */ + isUnknownElement: no, + + /** + * Get the namespace of an element + */ + getTagNamespace: noop, + + /** + * Check if an attribute must be bound using property, e.g. value + * Platform-dependent. + */ + mustUseProp: no, + + /** + * List of asset types that a component can own. + */ + _assetTypes: [ + 'component', + 'directive', + 'filter' + ], + + /** + * List of lifecycle hooks. + */ + _lifecycleHooks: [ + 'beforeCreate', + 'created', + 'beforeMount', + 'mounted', + 'beforeUpdate', + 'updated', + 'beforeDestroy', + 'destroyed', + 'activated', + 'deactivated' + ], + + /** + * Max circular updates allowed in a scheduler flush cycle. + */ + _maxUpdateCount: 100, + + /** + * Server rendering? + */ + _isServer: process.env.VUE_ENV === 'server' + }; + + /* */ + /** - * Make a cancellable version of an async callback. - * - * @param {Function} fn - * @return {Function} + * Check if a string starts with $ or _ */ - - function cancellable(fn) { - var cb = function cb() { - if (!cb.cancelled) { - return fn.apply(this, arguments); - } - }; - cb.cancel = function () { - cb.cancelled = true; - }; - return cb; + function isReserved (str) { + var c = (str + '').charCodeAt(0); + return c === 0x24 || c === 0x5F } /** - * Check if two values are loosely equal - that is, - * if they are plain objects, do they have the same shape? - * - * @param {*} a - * @param {*} b - * @return {Boolean} + * Define a property. */ + function def (obj, key, val, enumerable) { + Object.defineProperty(obj, key, { + value: val, + enumerable: !!enumerable, + writable: true, + configurable: true + }); + } - function looseEqual(a, b) { - /* eslint-disable eqeqeq */ - return a == b || (isObject(a) && isObject(b) ? JSON.stringify(a) === JSON.stringify(b) : false); - /* eslint-enable eqeqeq */ + /** + * Parse simple path. + */ + var bailRE = /[^\w.$]/; + function parsePath (path) { + if (bailRE.test(path)) { + return + } else { + var segments = path.split('.'); + return function (obj) { + for (var i = 0; i < segments.length; i++) { + if (!obj) { return } + obj = obj[segments[i]]; + } + return obj + } + } } - var hasProto = ('__proto__' in {}); + /* */ + /* globals MutationObserver */ - // Browser environment sniffing - var inBrowser = typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]'; + // can we use __proto__? + var hasProto = '__proto__' in {}; - // detect devtools - var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; + // Browser environment sniffing + var inBrowser = + typeof window !== 'undefined' && + Object.prototype.toString.call(window) !== '[object Object]'; - // UA sniffing for working around browser-specific quirks var UA = inBrowser && window.navigator.userAgent.toLowerCase(); + var isIE = UA && /msie|trident/.test(UA); var isIE9 = UA && UA.indexOf('msie 9.0') > 0; + var isEdge = UA && UA.indexOf('edge/') > 0; var isAndroid = UA && UA.indexOf('android') > 0; + var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); - var transitionProp = undefined; - var transitionEndEvent = undefined; - var animationProp = undefined; - var animationEndEvent = undefined; + // detect devtools + var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - // Transition property/event sniffing - if (inBrowser && !isIE9) { - var isWebkitTrans = window.ontransitionend === undefined && window.onwebkittransitionend !== undefined; - var isWebkitAnim = window.onanimationend === undefined && window.onwebkitanimationend !== undefined; - transitionProp = isWebkitTrans ? 'WebkitTransition' : 'transition'; - transitionEndEvent = isWebkitTrans ? 'webkitTransitionEnd' : 'transitionend'; - animationProp = isWebkitAnim ? 'WebkitAnimation' : 'animation'; - animationEndEvent = isWebkitAnim ? 'webkitAnimationEnd' : 'animationend'; + /* istanbul ignore next */ + function isNative (Ctor) { + return /native code/.test(Ctor.toString()) } /** - * Defer a task to execute it asynchronously. Ideally this - * should be executed as a microtask, so we leverage - * MutationObserver if it's available, and fallback to - * setTimeout(0). - * - * @param {Function} cb - * @param {Object} ctx + * Defer a task to execute it asynchronously. */ - var nextTick = (function () { var callbacks = []; var pending = false; var timerFunc; - function nextTickHandler() { + + function nextTickHandler () { pending = false; var copies = callbacks.slice(0); - callbacks = []; + callbacks.length = 0; for (var i = 0; i < copies.length; i++) { copies[i](); } } + // the nextTick behavior leverages the microtask queue, which can be accessed + // via either native Promise.then or MutationObserver. + // MutationObserver has wider support, however it is seriously bugged in + // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It + // completely stops working after triggering a few times... so, if native + // Promise is available, we will use it: /* istanbul ignore if */ - if (typeof MutationObserver !== 'undefined') { + if (typeof Promise !== 'undefined' && isNative(Promise)) { + var p = Promise.resolve(); + timerFunc = function () { + p.then(nextTickHandler); + // in problematic UIWebViews, Promise.then doesn't completely break, but + // it can get stuck in a weird state where callbacks are pushed into the + // microtask queue but the queue isn't being flushed, until the browser + // needs to do some other work, e.g. handle a timer. Therefore we can + // "force" the microtask queue to be flushed by adding an empty timer. + if (isIOS) { setTimeout(noop); } + }; + } else if (typeof MutationObserver !== 'undefined' && ( + isNative(MutationObserver) || + // PhantomJS and iOS 7.x + MutationObserver.toString() === '[object MutationObserverConstructor]' + )) { + // use MutationObserver where native Promise is not available, + // e.g. PhantomJS IE11, iOS7, Android 4.4 var counter = 1; var observer = new MutationObserver(nextTickHandler); - var textNode = document.createTextNode(counter); + var textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true }); timerFunc = function () { counter = (counter + 1) % 2; - textNode.data = counter; + textNode.data = String(counter); }; } else { - // webpack attempts to inject a shim for setImmediate - // if it is used as a global, so we have to work around that to - // avoid bundling unnecessary code. - var context = inBrowser ? window : typeof global !== 'undefined' ? global : {}; - timerFunc = context.setImmediate || setTimeout; - } - return function (cb, ctx) { - var func = ctx ? function () { - cb.call(ctx); - } : cb; + // fallback to setTimeout + /* istanbul ignore next */ + timerFunc = function () { + setTimeout(nextTickHandler, 0); + }; + } + + return function queueNextTick (cb, ctx) { + var func = ctx + ? function () { cb.call(ctx); } + : cb; callbacks.push(func); - if (pending) return; - pending = true; - timerFunc(nextTickHandler, 0); - }; + if (!pending) { + pending = true; + timerFunc(); + } + } })(); - function Cache(limit) { - this.size = 0; - this.limit = limit; - this.head = this.tail = undefined; - this._keymap = Object.create(null); + var _Set; + /* istanbul ignore if */ + if (typeof Set !== 'undefined' && isNative(Set)) { + // use native Set when available. + _Set = Set; + } else { + // a non-standard Set polyfill that only works with primitive keys. + _Set = (function () { + function Set () { + this.set = Object.create(null); + } + Set.prototype.has = function has (key) { + return this.set[key] !== undefined + }; + Set.prototype.add = function add (key) { + this.set[key] = 1; + }; + Set.prototype.clear = function clear () { + this.set = Object.create(null); + }; + + return Set; + }()); } - var p = Cache.prototype; + /* not type checking this file because flow doesn't play well with Proxy */ - /** - * Put into the cache associated with . - * Returns the entry which was removed to make room for - * the new entry. Otherwise undefined is returned. - * (i.e. if there was enough room already). - * - * @param {String} key - * @param {*} value - * @return {Entry|undefined} - */ + var hasProxy; + var proxyHandlers; + var initProxy; - p.put = function (key, value) { - var removed; - if (this.size === this.limit) { - removed = this.shift(); - } + if (process.env.NODE_ENV !== 'production') { + var allowedGlobals = makeMap( + 'Infinity,undefined,NaN,isFinite,isNaN,' + + 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + + 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + + 'require' // for Webpack/Browserify + ); + + hasProxy = + typeof Proxy !== 'undefined' && + Proxy.toString().match(/native code/); + + proxyHandlers = { + has: function has (target, key) { + var has = key in target; + var isAllowed = allowedGlobals(key) || key.charAt(0) === '_'; + if (!has && !isAllowed) { + warn( + "Property or method \"" + key + "\" is not defined on the instance but " + + "referenced during render. Make sure to declare reactive data " + + "properties in the data option.", + target + ); + } + return has || !isAllowed + } + }; - var entry = this.get(key, true); - if (!entry) { - entry = { - key: key - }; - this._keymap[key] = entry; - if (this.tail) { - this.tail.newer = entry; - entry.older = this.tail; + initProxy = function initProxy (vm) { + if (hasProxy) { + vm._renderProxy = new Proxy(vm, proxyHandlers); } else { - this.head = entry; + vm._renderProxy = vm; } - this.tail = entry; - this.size++; - } - entry.value = value; + }; + } - return removed; - }; + /* */ + + + var uid$2 = 0; /** - * Purge the least recently used (oldest) entry from the - * cache. Returns the removed entry or undefined if the - * cache was empty. + * A dep is an observable that can have multiple + * directives subscribing to it. */ + var Dep = function Dep () { + this.id = uid$2++; + this.subs = []; + }; - p.shift = function () { - var entry = this.head; - if (entry) { - this.head = this.head.newer; - this.head.older = undefined; - entry.newer = entry.older = undefined; - this._keymap[entry.key] = undefined; - this.size--; - } - return entry; + Dep.prototype.addSub = function addSub (sub) { + this.subs.push(sub); }; - /** - * Get and register recent use of . Returns the value - * associated with or undefined if not in cache. - * - * @param {String} key - * @param {Boolean} returnEntry - * @return {Entry|*} - */ + Dep.prototype.removeSub = function removeSub (sub) { + remove$1(this.subs, sub); + }; - p.get = function (key, returnEntry) { - var entry = this._keymap[key]; - if (entry === undefined) return; - if (entry === this.tail) { - return returnEntry ? entry : entry.value; - } - // HEAD--------------TAIL - // <.older .newer> - // <--- add direction -- - // A B C E - if (entry.newer) { - if (entry === this.head) { - this.head = entry.newer; - } - entry.newer.older = entry.older; // C <-- E. + Dep.prototype.depend = function depend () { + if (Dep.target) { + Dep.target.addDep(this); } - if (entry.older) { - entry.older.newer = entry.newer; // C. --> E - } - entry.newer = undefined; // D --x - entry.older = this.tail; // D. --> E - if (this.tail) { - this.tail.newer = entry; // E. <-- D - } - this.tail = entry; - return returnEntry ? entry : entry.value; }; - var cache$1 = new Cache(1000); - var filterTokenRE = /[^\s'"]+|'[^']*'|"[^"]*"/g; - var reservedArgRE = /^in$|^-?\d+/; + Dep.prototype.notify = function notify () { + // stablize the subscriber list first + var subs = this.subs.slice(); + for (var i = 0, l = subs.length; i < l; i++) { + subs[i].update(); + } + }; - /** - * Parser state - */ + // 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; + var targetStack = []; - var str; - var dir; - var c; - var prev; - var i; - var l; - var lastFilterIndex; - var inSingle; - var inDouble; - var curly; - var square; - var paren; - /** - * Push a filter to the current directive object - */ + function pushTarget (_target) { + if (Dep.target) { targetStack.push(Dep.target); } + Dep.target = _target; + } - function pushFilter() { - var exp = str.slice(lastFilterIndex, i).trim(); - var filter; - if (exp) { - filter = {}; - var tokens = exp.match(filterTokenRE); - filter.name = tokens[0]; - if (tokens.length > 1) { - filter.args = tokens.slice(1).map(processFilterArg); - } - } - if (filter) { - (dir.filters = dir.filters || []).push(filter); - } - lastFilterIndex = i + 1; + function popTarget () { + Dep.target = targetStack.pop(); } + /* */ + + + var queue = []; + var has$1 = {}; + var circular = {}; + var waiting = false; + var flushing = false; + var index = 0; + /** - * Check if an argument is dynamic and strip quotes. - * - * @param {String} arg - * @return {Object} + * Reset the scheduler's state. */ - - function processFilterArg(arg) { - if (reservedArgRE.test(arg)) { - return { - value: toNumber(arg), - dynamic: false - }; - } else { - var stripped = stripQuotes(arg); - var dynamic = stripped === arg; - return { - value: dynamic ? arg : stripped, - dynamic: dynamic - }; + function resetSchedulerState () { + queue.length = 0; + has$1 = {}; + if (process.env.NODE_ENV !== 'production') { + circular = {}; } + waiting = flushing = false; } /** - * Parse a directive value and extract the expression - * and its filters into a descriptor. - * - * Example: - * - * "a + 1 | uppercase" will yield: - * { - * expression: 'a + 1', - * filters: [ - * { name: 'uppercase', args: null } - * ] - * } - * - * @param {String} s - * @return {Object} + * Flush both queues and run the watchers. */ + function flushSchedulerQueue () { + flushing = true; - function parseDirective(s) { - var hit = cache$1.get(s); - if (hit) { - return hit; - } - - // reset parser state - str = s; - inSingle = inDouble = false; - curly = square = paren = 0; - lastFilterIndex = 0; - dir = {}; - - for (i = 0, l = str.length; i < l; i++) { - prev = c; - c = str.charCodeAt(i); - if (inSingle) { - // check single quote - if (c === 0x27 && prev !== 0x5C) inSingle = !inSingle; - } else if (inDouble) { - // check double quote - if (c === 0x22 && prev !== 0x5C) inDouble = !inDouble; - } else if (c === 0x7C && // pipe - str.charCodeAt(i + 1) !== 0x7C && str.charCodeAt(i - 1) !== 0x7C) { - if (dir.expression == null) { - // first filter, end of expression - lastFilterIndex = i + 1; - dir.expression = str.slice(0, i).trim(); - } else { - // already has filter - pushFilter(); - } - } else { - switch (c) { - case 0x22: - inDouble = true;break; // " - case 0x27: - inSingle = true;break; // ' - case 0x28: - paren++;break; // ( - case 0x29: - paren--;break; // ) - case 0x5B: - square++;break; // [ - case 0x5D: - square--;break; // ] - case 0x7B: - curly++;break; // { - case 0x7D: - curly--;break; // } + // Sort queue before flush. + // This ensures that: + // 1. Components are updated from parent to child. (because parent is always + // created before the child) + // 2. A component's user watchers are run before its render watcher (because + // user watchers are created before the render watcher) + // 3. If a component is destroyed during a parent component's watcher run, + // its watchers can be skipped. + queue.sort(function (a, b) { return a.id - b.id; }); + + // do not cache length because more watchers might be pushed + // as we run existing watchers + for (index = 0; index < queue.length; index++) { + var watcher = queue[index]; + var id = watcher.id; + has$1[id] = null; + watcher.run(); + // in dev build, check and stop circular updates. + if (process.env.NODE_ENV !== 'production' && has$1[id] != null) { + circular[id] = (circular[id] || 0) + 1; + if (circular[id] > config._maxUpdateCount) { + warn( + 'You may have an infinite update loop ' + ( + watcher.user + ? ("in watcher with expression \"" + (watcher.expression) + "\"") + : "in a component render function." + ), + watcher.vm + ); + break } } } - if (dir.expression == null) { - dir.expression = str.slice(0, i).trim(); - } else if (lastFilterIndex !== 0) { - pushFilter(); + // devtool hook + /* istanbul ignore if */ + if (devtools && config.devtools) { + devtools.emit('flush'); } - cache$1.put(s, dir); - return dir; + resetSchedulerState(); } - var directive = Object.freeze({ - parseDirective: parseDirective - }); - - var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; - var cache = undefined; - var tagRE = undefined; - var htmlRE = undefined; /** - * Escape a string so it can be used in a RegExp - * constructor. - * - * @param {String} str + * Push a watcher into the watcher queue. + * Jobs with duplicate IDs will be skipped unless it's + * pushed when the queue is being flushed. */ - - function escapeRegex(str) { - return str.replace(regexEscapeRE, '\\$&'); + function queueWatcher (watcher) { + var id = watcher.id; + if (has$1[id] == null) { + has$1[id] = true; + if (!flushing) { + queue.push(watcher); + } else { + // if already flushing, splice the watcher based on its id + // if already past its id, it will be run next immediately. + var i = queue.length - 1; + while (i >= 0 && queue[i].id > watcher.id) { + i--; + } + queue.splice(Math.max(i, index) + 1, 0, watcher); + } + // queue the flush + if (!waiting) { + waiting = true; + nextTick(flushSchedulerQueue); + } + } } - function compileRegex() { - var open = escapeRegex(config.delimiters[0]); - var close = escapeRegex(config.delimiters[1]); - var unsafeOpen = escapeRegex(config.unsafeDelimiters[0]); - var unsafeClose = escapeRegex(config.unsafeDelimiters[1]); - tagRE = new RegExp(unsafeOpen + '((?:.|\\n)+?)' + unsafeClose + '|' + open + '((?:.|\\n)+?)' + close, 'g'); - htmlRE = new RegExp('^' + unsafeOpen + '.*' + unsafeClose + '$'); - // reset cache - cache = new Cache(1000); - } + /* */ + + var uid$1 = 0; /** - * Parse a template text string into an array of tokens. - * - * @param {String} text - * @return {Array | null} - * - {String} type - * - {String} value - * - {Boolean} [html] - * - {Boolean} [oneTime] + * A watcher parses an expression, collects dependencies, + * and fires callback when the expression value changes. + * This is used for both the $watch() api and directives. */ + var Watcher = function Watcher ( + vm, + expOrFn, + cb, + options + ) { + if ( options === void 0 ) options = {}; - function parseText(text) { - if (!cache) { - compileRegex(); - } - var hit = cache.get(text); - if (hit) { - return hit; - } - if (!tagRE.test(text)) { - return null; - } - var tokens = []; - var lastIndex = tagRE.lastIndex = 0; - var match, index, html, value, first, oneTime; - /* eslint-disable no-cond-assign */ - while (match = tagRE.exec(text)) { - /* eslint-enable no-cond-assign */ - index = match.index; - // push text token - if (index > lastIndex) { - tokens.push({ - value: text.slice(lastIndex, index) - }); + this.vm = vm; + vm._watchers.push(this); + // options + this.deep = !!options.deep; + this.user = !!options.user; + this.lazy = !!options.lazy; + this.sync = !!options.sync; + this.expression = expOrFn.toString(); + this.cb = cb; + this.id = ++uid$1; // uid for batching + this.active = true; + this.dirty = this.lazy; // for lazy watchers + this.deps = []; + this.newDeps = []; + this.depIds = new _Set(); + this.newDepIds = new _Set(); + // parse expression for getter + if (typeof expOrFn === 'function') { + this.getter = expOrFn; + } else { + this.getter = parsePath(expOrFn); + if (!this.getter) { + this.getter = function () {}; + process.env.NODE_ENV !== 'production' && warn( + "Failed watching path: \"" + expOrFn + "\" " + + 'Watcher only accepts simple dot-delimited paths. ' + + 'For full control, use a function instead.', + vm + ); } - // tag token - html = htmlRE.test(match[0]); - value = html ? match[1] : match[2]; - first = value.charCodeAt(0); - oneTime = first === 42; // * - value = oneTime ? value.slice(1) : value; - tokens.push({ - tag: true, - value: value.trim(), - html: html, - oneTime: oneTime - }); - lastIndex = index + match[0].length; - } - if (lastIndex < text.length) { - tokens.push({ - value: text.slice(lastIndex) - }); } - cache.put(text, tokens); - return tokens; - } + this.value = this.lazy + ? undefined + : this.get(); + }; /** - * Format a list of tokens into an expression. - * e.g. tokens parsed from 'a {{b}} c' can be serialized - * into one single expression as '"a " + b + " c"'. - * - * @param {Array} tokens - * @param {Vue} [vm] - * @return {String} + * Evaluate the getter, and re-collect dependencies. */ - - function tokensToExp(tokens, vm) { - if (tokens.length > 1) { - return tokens.map(function (token) { - return formatToken(token, vm); - }).join('+'); - } else { - return formatToken(tokens[0], vm, true); + Watcher.prototype.get = function get () { + pushTarget(this); + var value = this.getter.call(this.vm, this.vm); + // "touch" every property so they are all tracked as + // dependencies for deep watching + if (this.deep) { + traverse(value); } - } + popTarget(); + this.cleanupDeps(); + return value + }; /** - * Format a single token. - * - * @param {Object} token - * @param {Vue} [vm] - * @param {Boolean} [single] - * @return {String} + * Add a dependency to this directive. */ - - function formatToken(token, vm, single) { - return token.tag ? token.oneTime && vm ? '"' + vm.$eval(token.value) + '"' : inlineFilters(token.value, single) : '"' + token.value + '"'; - } + Watcher.prototype.addDep = function addDep (dep) { + var id = dep.id; + if (!this.newDepIds.has(id)) { + this.newDepIds.add(id); + this.newDeps.push(dep); + if (!this.depIds.has(id)) { + dep.addSub(this); + } + } + }; /** - * For an attribute with multiple interpolation tags, - * e.g. attr="some-{{thing | filter}}", in order to combine - * the whole thing into a single watchable expression, we - * have to inline those filters. This function does exactly - * that. This is a bit hacky but it avoids heavy changes - * to directive parser and watcher mechanism. - * - * @param {String} exp - * @param {Boolean} single - * @return {String} + * Clean up for dependency collection. */ + Watcher.prototype.cleanupDeps = function cleanupDeps () { + var this$1 = this; - var filterRE = /[^|]\|[^|]/; - function inlineFilters(exp, single) { - if (!filterRE.test(exp)) { - return single ? exp : '(' + exp + ')'; - } else { - var dir = parseDirective(exp); - if (!dir.filters) { - return '(' + exp + ')'; - } else { - return 'this._applyFilters(' + dir.expression + // value - ',null,' + // oldValue (null for read) - JSON.stringify(dir.filters) + // filter descriptors - ',false)'; // write? + var i = this.deps.length; + while (i--) { + var dep = this$1.deps[i]; + if (!this$1.newDepIds.has(dep.id)) { + dep.removeSub(this$1); } } - } - - var text = Object.freeze({ - compileRegex: compileRegex, - parseText: parseText, - tokensToExp: tokensToExp - }); - - var delimiters = ['{{', '}}']; - var unsafeDelimiters = ['{{{', '}}}']; - - var config = Object.defineProperties({ - - /** - * Whether to print debug messages. - * Also enables stack trace for warnings. - * - * @type {Boolean} - */ - - debug: false, - - /** - * Whether to suppress warnings. - * - * @type {Boolean} - */ - - silent: false, - - /** - * Whether to use async rendering. - */ - - async: true, - - /** - * Whether to warn against errors caught when evaluating - * expressions. - */ - - warnExpressionErrors: true, - - /** - * Whether to allow devtools inspection. - * Disabled by default in production builds. - */ - - devtools: process.env.NODE_ENV !== 'production', - - /** - * Internal flag to indicate the delimiters have been - * changed. - * - * @type {Boolean} - */ - - _delimitersChanged: true, - - /** - * List of asset types that a component can own. - * - * @type {Array} - */ - - _assetTypes: ['component', 'directive', 'elementDirective', 'filter', 'transition', 'partial'], - - /** - * prop binding modes - */ - - _propBindingModes: { - ONE_WAY: 0, - TWO_WAY: 1, - ONE_TIME: 2 - }, - - /** - * Max circular updates allowed in a batcher flush cycle. - */ - - _maxUpdateCount: 100 - - }, { - delimiters: { /** - * Interpolation delimiters. Changing these would trigger - * the text parser to re-compile the regular expressions. - * - * @type {Array} - */ - - get: function get() { - return delimiters; - }, - set: function set(val) { - delimiters = val; - compileRegex(); - }, - configurable: true, - enumerable: true - }, - unsafeDelimiters: { - get: function get() { - return unsafeDelimiters; - }, - set: function set(val) { - unsafeDelimiters = val; - compileRegex(); - }, - configurable: true, - enumerable: true - } - }); - - var warn = undefined; - var formatComponentName = undefined; - - if (process.env.NODE_ENV !== 'production') { - (function () { - var hasConsole = typeof console !== 'undefined'; - - 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) + '>)' : ''; - }; - })(); - } + var tmp = this.depIds; + this.depIds = this.newDepIds; + this.newDepIds = tmp; + this.newDepIds.clear(); + tmp = this.deps; + this.deps = this.newDeps; + this.newDeps = tmp; + this.newDeps.length = 0; + }; /** - * Append with transition. - * - * @param {Element} el - * @param {Element} target - * @param {Vue} vm - * @param {Function} [cb] + * Subscriber interface. + * Will be called when a dependency changes. */ - - function appendWithTransition(el, target, vm, cb) { - applyTransition(el, 1, function () { - target.appendChild(el); - }, vm, cb); - } + Watcher.prototype.update = function update () { + /* istanbul ignore else */ + if (this.lazy) { + this.dirty = true; + } else if (this.sync) { + this.run(); + } else { + queueWatcher(this); + } + }; /** - * InsertBefore with transition. - * - * @param {Element} el - * @param {Element} target - * @param {Vue} vm - * @param {Function} [cb] + * Scheduler job interface. + * Will be called by the scheduler. */ - - function beforeWithTransition(el, target, vm, cb) { - applyTransition(el, 1, function () { - before(el, target); - }, vm, cb); - } + Watcher.prototype.run = function run () { + if (this.active) { + var value = this.get(); + if ( + value !== this.value || + // Deep watchers and watchers on Object/Arrays should fire even + // when the value is the same, because the value may + // have mutated. + isObject(value) || + this.deep + ) { + // set new value + var oldValue = this.value; + this.value = value; + if (this.user) { + try { + this.cb.call(this.vm, value, oldValue); + } catch (e) { + process.env.NODE_ENV !== 'production' && warn( + ("Error in watcher \"" + (this.expression) + "\""), + this.vm + ); + /* istanbul ignore else */ + if (config.errorHandler) { + config.errorHandler.call(null, e, this.vm); + } else { + throw e + } + } + } else { + this.cb.call(this.vm, value, oldValue); + } + } + } + }; /** - * Remove with transition. - * - * @param {Element} el - * @param {Vue} vm - * @param {Function} [cb] + * Evaluate the value of the watcher. + * This only gets called for lazy watchers. */ - - function removeWithTransition(el, vm, cb) { - applyTransition(el, -1, function () { - remove(el); - }, vm, cb); - } + Watcher.prototype.evaluate = function evaluate () { + this.value = this.get(); + this.dirty = false; + }; /** - * Apply transitions with an operation callback. - * - * @param {Element} el - * @param {Number} direction - * 1: enter - * -1: leave - * @param {Function} op - the actual DOM operation - * @param {Vue} vm - * @param {Function} [cb] + * Depend on all deps collected by this watcher. */ + Watcher.prototype.depend = function depend () { + var this$1 = this; - function applyTransition(el, direction, op, vm, cb) { - var transition = el.__v_trans; - if (!transition || - // skip if there are no js hooks and CSS transition is - // not supported - !transition.hooks && !transitionEndEvent || - // skip transitions for initial compile - !vm._isCompiled || - // if the vm is being manipulated by a parent directive - // during the parent's compilation phase, skip the - // animation. - vm.$parent && !vm.$parent._isCompiled) { - op(); - if (cb) cb(); - return; - } - var action = direction > 0 ? 'enter' : 'leave'; - transition[action](op, cb); - } - - var transition = Object.freeze({ - appendWithTransition: appendWithTransition, - beforeWithTransition: beforeWithTransition, - removeWithTransition: removeWithTransition, - applyTransition: applyTransition - }); + var i = this.deps.length; + while (i--) { + this$1.deps[i].depend(); + } + }; /** - * Query an element selector if it's not an element already. - * - * @param {String|Element} el - * @return {Element} + * Remove self from all dependencies' subscriber list. */ + Watcher.prototype.teardown = function teardown () { + var this$1 = this; - function query(el) { - if (typeof el === 'string') { - var selector = el; - el = document.querySelector(el); - if (!el) { - process.env.NODE_ENV !== 'production' && warn('Cannot find element: ' + selector); + if (this.active) { + // remove self from vm's watcher list + // this is a somewhat expensive operation so we skip it + // if the vm is being destroyed or is performing a v-for + // re-render (the watcher list is then filtered by v-for). + if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) { + remove$1(this.vm._watchers, this); + } + var i = this.deps.length; + while (i--) { + this$1.deps[i].removeSub(this$1); } + this.active = false; } - return el; - } + }; /** - * Check if a node is in the document. - * Note: document.documentElement.contains should work here - * but always returns false for comment nodes in phantomjs, - * making unit tests difficult. This is fixed by doing the - * contains() check on the node's parentNode instead of - * the node itself. - * - * @param {Node} node - * @return {Boolean} + * Recursively traverse an object to evoke all converted + * getters, so that every nested property inside the object + * is collected as a "deep" dependency. */ - - function inDoc(node) { - var doc = document.documentElement; - var parent = node && node.parentNode; - return doc === node || doc === parent || !!(parent && parent.nodeType === 1 && doc.contains(parent)); + var seenObjects = new _Set(); + function traverse (val) { + seenObjects.clear(); + _traverse(val, seenObjects); } - /** - * Get and remove an attribute from a node. - * - * @param {Node} node - * @param {String} _attr - */ - - function getAttr(node, _attr) { - var val = node.getAttribute(_attr); - if (val !== null) { - node.removeAttribute(_attr); + function _traverse (val, seen) { + var i, keys; + var isA = Array.isArray(val); + if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { + return + } + if (val.__ob__) { + var depId = val.__ob__.dep.id; + if (seen.has(depId)) { + return + } + seen.add(depId); + } + if (isA) { + i = val.length; + while (i--) { _traverse(val[i], seen); } + } else { + keys = Object.keys(val); + i = keys.length; + while (i--) { _traverse(val[keys[i]], seen); } } - return val; } - /** - * Get an attribute with colon or v-bind: prefix. - * - * @param {Node} node - * @param {String} name - * @return {String|null} - */ - - function getBindAttr(node, name) { - var val = getAttr(node, ':' + name); - if (val === null) { - val = getAttr(node, 'v-bind:' + name); - } - return val; - } - - /** - * Check the presence of a bind attribute. - * - * @param {Node} node - * @param {String} name - * @return {Boolean} + /* + * not type checking this file because flow doesn't play well with + * dynamically accessing methods on Array prototype */ - function hasBindAttr(node, name) { - return node.hasAttribute(name) || node.hasAttribute(':' + name) || node.hasAttribute('v-bind:' + name); - } - - /** - * Insert el before target - * - * @param {Element} el - * @param {Element} target - */ + var arrayProto = Array.prototype; + var arrayMethods = Object.create(arrayProto);[ + 'push', + 'pop', + 'shift', + 'unshift', + 'splice', + 'sort', + 'reverse' + ] + .forEach(function (method) { + // cache original method + var original = arrayProto[method]; + def(arrayMethods, method, function mutator () { + var arguments$1 = arguments; - function before(el, target) { - target.parentNode.insertBefore(el, target); - } + // avoid leaking arguments: + // http://jsperf.com/closure-with-arguments + var i = arguments.length; + var args = new Array(i); + while (i--) { + args[i] = arguments$1[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 + }); + }); - /** - * Insert el after target - * - * @param {Element} el - * @param {Element} target - */ + /* */ - function after(el, target) { - if (target.nextSibling) { - before(el, target.nextSibling); - } else { - target.parentNode.appendChild(el); - } - } + var arrayKeys = Object.getOwnPropertyNames(arrayMethods); /** - * Remove el from DOM - * - * @param {Element} el + * By default, when a reactive property is set, the new value is + * also converted to become reactive. However when passing down props, + * we don't want to force conversion because the value may be a nested value + * under a frozen data structure. Converting it would defeat the optimization. */ - - function remove(el) { - el.parentNode.removeChild(el); - } + var observerState = { + shouldConvert: true, + isSettingProps: false + }; /** - * Prepend el to target - * - * @param {Element} el - * @param {Element} target + * 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. */ - - function prepend(el, target) { - if (target.firstChild) { - before(el, target.firstChild); + var Observer = function Observer (value) { + this.value = value; + this.dep = new Dep(); + this.vmCount = 0; + def(value, '__ob__', this); + if (Array.isArray(value)) { + var augment = hasProto + ? protoAugment + : copyAugment; + augment(value, arrayMethods, arrayKeys); + this.observeArray(value); } else { - target.appendChild(el); + this.walk(value); } - } + }; /** - * Replace target with el - * - * @param {Element} target - * @param {Element} el + * Walk through each property and convert them into + * getter/setters. This method should only be called when + * value type is Object. */ - - function replace(target, el) { - var parent = target.parentNode; - if (parent) { - parent.replaceChild(el, target); + Observer.prototype.walk = function walk (obj) { + var keys = Object.keys(obj); + for (var i = 0; i < keys.length; i++) { + defineReactive$$1(obj, keys[i], obj[keys[i]]); } - } + }; /** - * Add event listener shorthand. - * - * @param {Element} el - * @param {String} event - * @param {Function} cb - * @param {Boolean} [useCapture] + * Observe a list of Array items. */ + Observer.prototype.observeArray = function observeArray (items) { + for (var i = 0, l = items.length; i < l; i++) { + observe(items[i]); + } + }; - function on(el, event, cb, useCapture) { - el.addEventListener(event, cb, useCapture); - } + // helpers /** - * Remove event listener shorthand. - * - * @param {Element} el - * @param {String} event - * @param {Function} cb + * Augment an target Object or Array by intercepting + * the prototype chain using __proto__ */ - - function off(el, event, cb) { - el.removeEventListener(event, cb); + function protoAugment (target, src) { + /* eslint-disable no-proto */ + target.__proto__ = src; + /* eslint-enable no-proto */ } /** - * For IE9 compat: when both class and :class are present - * getAttribute('class') returns wrong value... + * Augment an target Object or Array by defining + * hidden properties. * - * @param {Element} el - * @return {String} + * istanbul ignore next */ - - function getClass(el) { - var classname = el.className; - if (typeof classname === 'object') { - classname = classname.baseVal || ''; + function copyAugment (target, src, keys) { + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + def(target, key, src[key]); } - return classname; } /** - * In IE9, setAttribute('class') will result in empty class - * if the element also has the :class attribute; However in - * PhantomJS, setting `className` does not work on SVG elements... - * So we have to do a conditional check here. - * - * @param {Element} el - * @param {String} cls + * 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. */ - - function setClass(el, cls) { - /* istanbul ignore if */ - if (isIE9 && !/svg$/.test(el.namespaceURI)) { - el.className = cls; - } else { - el.setAttribute('class', cls); + function observe (value) { + if (!isObject(value)) { + return + } + var ob; + if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { + ob = value.__ob__; + } else if ( + observerState.shouldConvert && + !config._isServer && + (Array.isArray(value) || isPlainObject(value)) && + Object.isExtensible(value) && + !value._isVue + ) { + ob = new Observer(value); } + return ob } /** - * Add class with compatibility for IE & SVG - * - * @param {Element} el - * @param {String} cls + * Define a reactive property on an Object. */ + function defineReactive$$1 ( + obj, + key, + val, + customSetter + ) { + var dep = new Dep(); - 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()); - } + var property = Object.getOwnPropertyDescriptor(obj, key); + if (property && property.configurable === false) { + return } - } - /** - * Remove class with compatibility for IE & SVG - * - * @param {Element} el - * @param {String} cls - */ + // cater for pre-defined getter/setters + var getter = property && property.get; + var setter = property && property.set; - 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, ' '); + 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 (Array.isArray(value)) { + dependArray(value); + } + } + return value + }, + set: function reactiveSetter (newVal) { + var value = getter ? getter.call(obj) : val; + if (newVal === value) { + return + } + if (process.env.NODE_ENV !== 'production' && customSetter) { + customSetter(); + } + if (setter) { + setter.call(obj, newVal); + } else { + val = newVal; + } + childOb = observe(newVal); + dep.notify(); } - setClass(el, cur.trim()); - } - if (!el.className) { - el.removeAttribute('class'); - } + }); } /** - * Extract raw content inside an element into a temporary - * container div - * - * @param {Element} el - * @param {Boolean} asFragment - * @return {Element|DocumentFragment} + * Set a property on an object. Adds the new property and + * triggers change notification if the property doesn't + * already exist. */ - - function extractContent(el, asFragment) { - var child; - var rawContent; - /* istanbul ignore if */ - if (isTemplate(el) && isFragment(el.content)) { - el = el.content; + function set (obj, key, val) { + if (Array.isArray(obj)) { + obj.length = Math.max(obj.length, key); + obj.splice(key, 1, val); + return val } - if (el.hasChildNodes()) { - trimNode(el); - rawContent = asFragment ? document.createDocumentFragment() : document.createElement('div'); - /* eslint-disable no-cond-assign */ - while (child = el.firstChild) { - /* eslint-enable no-cond-assign */ - rawContent.appendChild(child); - } + if (hasOwn(obj, key)) { + obj[key] = val; + return + } + var ob = obj.__ob__; + if (obj._isVue || (ob && ob.vmCount)) { + process.env.NODE_ENV !== 'production' && warn( + 'Avoid adding reactive properties to a Vue instance or its root $data ' + + 'at runtime - declare it upfront in the data option.' + ); + return } - return rawContent; + if (!ob) { + obj[key] = val; + return + } + defineReactive$$1(ob.value, key, val); + ob.dep.notify(); + return val } /** - * Trim possible empty head/tail text and comment - * nodes inside a parent. - * - * @param {Node} node + * Delete a property and trigger change if necessary. */ - - function trimNode(node) { - var child; - /* eslint-disable no-sequences */ - while ((child = node.firstChild, isTrimmable(child))) { - node.removeChild(child); + function del (obj, key) { + var ob = obj.__ob__; + if (obj._isVue || (ob && ob.vmCount)) { + process.env.NODE_ENV !== 'production' && warn( + 'Avoid deleting properties on a Vue instance or its root $data ' + + '- just set it to null.' + ); + return } - while ((child = node.lastChild, isTrimmable(child))) { - node.removeChild(child); + if (!hasOwn(obj, key)) { + return } - /* eslint-enable no-sequences */ - } - - function isTrimmable(node) { - return node && (node.nodeType === 3 && !node.data.trim() || node.nodeType === 8); + delete obj[key]; + if (!ob) { + return + } + ob.dep.notify(); } /** - * Check if an element is a template tag. - * Note if the template appears inside an SVG its tagName - * will be in lowercase. - * - * @param {Element} el + * Collect dependencies on array elements when the array is touched, since + * we cannot intercept array element access like property getters. */ - - function isTemplate(el) { - return el.tagName && el.tagName.toLowerCase() === 'template'; + function dependArray (value) { + for (var e = void 0, i = 0, l = value.length; i < l; i++) { + e = value[i]; + e && e.__ob__ && e.__ob__.dep.depend(); + if (Array.isArray(e)) { + dependArray(e); + } + } } - /** - * Create an "anchor" for performing dom insertion/removals. - * This is used in a number of scenarios: - * - fragment instance - * - v-html - * - v-if - * - v-for - * - component - * - * @param {String} content - * @param {Boolean} persist - IE trashes empty textNodes on - * cloneNode(true), so in certain - * cases the anchor needs to be - * non-empty to be persisted in - * templates. - * @return {Comment|Text} - */ + /* */ - function createAnchor(content, persist) { - var anchor = config.debug ? document.createComment(content) : document.createTextNode(persist ? ' ' : ''); - anchor.__v_anchor = true; - return anchor; + function initState (vm) { + vm._watchers = []; + initProps(vm); + initData(vm); + initComputed(vm); + initMethods(vm); + initWatch(vm); } - /** - * Find a component ref attribute that starts with $. - * - * @param {Element} node - * @return {String|undefined} - */ - - var refRE = /^v-ref:/; - - function findRef(node) { - if (node.hasAttributes()) { - var attrs = node.attributes; - for (var i = 0, l = attrs.length; i < l; i++) { - var name = attrs[i].name; - if (refRE.test(name)) { - return camelize(name.replace(refRE, '')); + function initProps (vm) { + var props = vm.$options.props; + if (props) { + var propsData = vm.$options.propsData || {}; + var keys = vm.$options._propKeys = Object.keys(props); + var isRoot = !vm.$parent; + // root instance props should be converted + observerState.shouldConvert = isRoot; + var loop = function ( i ) { + var key = keys[i]; + /* istanbul ignore else */ + if (process.env.NODE_ENV !== 'production') { + defineReactive$$1(vm, key, validateProp(key, props, propsData, vm), function () { + if (vm.$parent && !observerState.isSettingProps) { + warn( + "Avoid mutating a prop directly since the value will be " + + "overwritten whenever the parent component re-renders. " + + "Instead, use a data or computed property based on the prop's " + + "value. Prop being mutated: \"" + key + "\"", + vm + ); + } + }); + } else { + defineReactive$$1(vm, key, validateProp(key, props, propsData, vm)); } - } + }; + + for (var i = 0; i < keys.length; i++) loop( i ); + observerState.shouldConvert = true; } } - /** - * Map a function to a range of nodes . - * - * @param {Node} node - * @param {Node} end - * @param {Function} op - */ - - function mapNodeRange(node, end, op) { - var next; - while (node !== end) { - next = node.nextSibling; - op(node); - node = next; + function initData (vm) { + var data = vm.$options.data; + data = vm._data = typeof data === 'function' + ? data.call(vm) + : data || {}; + if (!isPlainObject(data)) { + data = {}; + process.env.NODE_ENV !== 'production' && warn( + 'data functions should return an object.', + vm + ); + } + // proxy data on instance + var keys = Object.keys(data); + var props = vm.$options.props; + var i = keys.length; + while (i--) { + if (props && hasOwn(props, keys[i])) { + process.env.NODE_ENV !== 'production' && warn( + "The data property \"" + (keys[i]) + "\" is already declared as a prop. " + + "Use prop default value instead.", + vm + ); + } else { + proxy(vm, keys[i]); + } } - op(end); + // observe data + observe(data); + data.__ob__ && data.__ob__.vmCount++; } - /** - * Remove a range of nodes with transition, store - * the nodes in a fragment with correct ordering, - * and call callback when done. - * - * @param {Node} start - * @param {Node} end - * @param {Vue} vm - * @param {DocumentFragment} frag - * @param {Function} cb - */ + var computedSharedDefinition = { + enumerable: true, + configurable: true, + get: noop, + set: noop + }; - function removeNodeRange(start, end, vm, frag, cb) { - var done = false; - var removed = 0; - var nodes = []; - mapNodeRange(start, end, function (node) { - if (node === end) done = true; - nodes.push(node); - removeWithTransition(node, vm, onRemoved); - }); - function onRemoved() { - removed++; - if (done && removed >= nodes.length) { - for (var i = 0; i < nodes.length; i++) { - frag.appendChild(nodes[i]); + function initComputed (vm) { + var computed = vm.$options.computed; + if (computed) { + for (var key in computed) { + var userDef = computed[key]; + if (typeof userDef === 'function') { + computedSharedDefinition.get = makeComputedGetter(userDef, vm); + computedSharedDefinition.set = noop; + } else { + computedSharedDefinition.get = userDef.get + ? userDef.cache !== false + ? makeComputedGetter(userDef.get, vm) + : bind$1(userDef.get, vm) + : noop; + computedSharedDefinition.set = userDef.set + ? bind$1(userDef.set, vm) + : noop; } - cb && cb(); + Object.defineProperty(vm, key, computedSharedDefinition); } } } - /** - * Check if a node is a DocumentFragment. - * - * @param {Node} node - * @return {Boolean} - */ - - function isFragment(node) { - return node && node.nodeType === 11; - } - - /** - * Get outerHTML of elements, taking care - * of SVG elements in IE as well. - * - * @param {Element} el - * @return {String} - */ - - function getOuterHTML(el) { - if (el.outerHTML) { - return el.outerHTML; - } else { - var container = document.createElement('div'); - container.appendChild(el.cloneNode(true)); - return container.innerHTML; + function makeComputedGetter (getter, owner) { + var watcher = new Watcher(owner, getter, noop, { + lazy: true + }); + return function computedGetter () { + if (watcher.dirty) { + watcher.evaluate(); + } + if (Dep.target) { + watcher.depend(); + } + return watcher.value } } - 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; - - var isUnknownElement = undefined; - if (process.env.NODE_ENV !== 'production') { - isUnknownElement = function (el, tag) { - if (tag.indexOf('-') > -1) { - // http://stackoverflow.com/a/28210364/1070244 - return el.constructor === window.HTMLUnknownElement || el.constructor === window.HTMLElement; - } else { - return (/HTMLUnknownElement/.test(el.toString()) && - // Chrome returns unknown for several HTML5 elements. - // https://code.google.com/p/chromium/issues/detail?id=540526 - !/^(data|time|rtc|rb)$/.test(tag) - ); + function initMethods (vm) { + var methods = vm.$options.methods; + if (methods) { + for (var key in methods) { + vm[key] = methods[key] == null ? noop : bind$1(methods[key], vm); + if (process.env.NODE_ENV !== 'production') { + methods[key] == null && warn( + "method \"" + key + "\" has an undefined value in the component definition. " + + "Did you reference the function correctly?", + vm + ); + hasOwn(Vue$1.prototype, key) && warn( + ("Avoid overriding Vue's internal method \"" + key + "\"."), + vm + ); + } } - }; + } } - /** - * Check if an element is a component, if yes return its - * component id. - * - * @param {Element} el - * @param {Object} options - * @return {Object|undefined} - */ - - function checkComponentAttr(el, options) { - var tag = el.tagName.toLowerCase(); - var hasAttrs = el.hasAttributes(); - if (!commonTagRE.test(tag) && !reservedTagRE.test(tag)) { - if (resolveAsset(options, 'components', tag)) { - return { id: tag }; - } else { - var is = hasAttrs && getIsBinding(el); - if (is) { - return is; - } else if (process.env.NODE_ENV !== 'production') { - var expectedTag = options._componentNameMap && options._componentNameMap[tag]; - if (expectedTag) { - warn('Unknown custom element: <' + tag + '> - ' + 'did you mean <' + expectedTag + '>? ' + 'HTML is case-insensitive, remember to use kebab-case in templates.'); - } else if (isUnknownElement(el, tag)) { - warn('Unknown custom element: <' + tag + '> - did you ' + 'register the component correctly? For recursive components, ' + 'make sure to provide the "name" option.'); + function initWatch (vm) { + var watch = vm.$options.watch; + if (watch) { + for (var key in watch) { + var handler = watch[key]; + if (Array.isArray(handler)) { + for (var i = 0; i < handler.length; i++) { + createWatcher(vm, key, handler[i]); } + } else { + createWatcher(vm, key, handler); } } - } else if (hasAttrs) { - return getIsBinding(el); } } - /** - * Get "is" binding from an element. - * - * @param {Element} el - * @return {Object|undefined} - */ - - function getIsBinding(el) { - // dynamic syntax - var exp = getAttr(el, 'is'); - if (exp != null) { - return { id: exp }; - } else { - exp = getBindAttr(el, 'is'); - if (exp != null) { - return { id: exp, dynamic: true }; - } + function createWatcher (vm, key, handler) { + var options; + if (isPlainObject(handler)) { + options = handler; + handler = handler.handler; + } + if (typeof handler === 'string') { + handler = vm[handler]; } + vm.$watch(key, handler, options); } - /** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - * - * All strategy functions follow the same signature: - * - * @param {*} parentVal - * @param {*} childVal - * @param {Vue} [vm] - */ - - var strats = config.optionMergeStrategies = Object.create(null); - - /** - * Helper that recursively merges two data objects together. - */ - - function mergeData(to, from) { - var key, toVal, fromVal; - for (key in from) { - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isObject(toVal) && isObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to; - } - - /** - * Data - */ - - strats.data = function (parentVal, childVal, vm) { - if (!vm) { - // in a Vue.extend merge, both should be functions - if (!childVal) { - return parentVal; - } - if (typeof childVal !== 'function') { - process.env.NODE_ENV !== 'production' && warn('The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm); - return parentVal; - } - if (!parentVal) { - return childVal; - } - // when parentVal & childVal are both present, - // we need to return a function that returns the - // merged result of both functions... no need to - // check if parentVal is a function here because - // it has to be a function to pass previous merges. - return function mergedDataFn() { - return mergeData(childVal.call(this), parentVal.call(this)); - }; - } else if (parentVal || childVal) { - return function mergedInstanceDataFn() { - // instance merge - var instanceData = typeof childVal === 'function' ? childVal.call(vm) : childVal; - var defaultData = typeof parentVal === 'function' ? parentVal.call(vm) : undefined; - if (instanceData) { - return mergeData(instanceData, defaultData); - } else { - return defaultData; - } - }; - } - }; - - /** - * El - */ - - strats.el = function (parentVal, childVal, vm) { - if (!vm && childVal && typeof childVal !== 'function') { - process.env.NODE_ENV !== 'production' && warn('The "el" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm); - return; - } - var ret = childVal || parentVal; - // invoke the element factory if this is instance merge - return vm && typeof ret === 'function' ? ret.call(vm) : ret; - }; - - /** - * Hooks and param attributes are merged as arrays. - */ - - strats.init = strats.created = strats.ready = strats.attached = strats.detached = strats.beforeCompile = strats.compiled = strats.beforeDestroy = strats.destroyed = strats.activate = function (parentVal, childVal) { - return childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [childVal] : parentVal; - }; - - /** - * Assets - * - * When a vm is present (instance creation), we need to do - * a three-way merge between constructor options, instance - * options and parent options. - */ - - function mergeAssets(parentVal, childVal) { - var res = Object.create(parentVal); - return childVal ? extend(res, guardArrayAssets(childVal)) : res; - } - - config._assetTypes.forEach(function (type) { - strats[type + 's'] = mergeAssets; - }); - - /** - * Events & Watchers. - * - * Events & watchers hashes should not overwrite one - * another, so we merge them as arrays. - */ - - strats.watch = strats.events = function (parentVal, childVal) { - if (!childVal) return parentVal; - if (!parentVal) return childVal; - var ret = {}; - extend(ret, parentVal); - for (var key in childVal) { - var parent = ret[key]; - var child = childVal[key]; - if (parent && !isArray(parent)) { - parent = [parent]; - } - ret[key] = parent ? parent.concat(child) : [child]; - } - return ret; - }; - - /** - * Other object hashes. - */ - - strats.props = strats.methods = strats.computed = function (parentVal, childVal) { - if (!childVal) return parentVal; - if (!parentVal) return childVal; - var ret = Object.create(null); - extend(ret, parentVal); - extend(ret, childVal); - return ret; - }; - - /** - * Default strategy. - */ - - var defaultStrat = function defaultStrat(parentVal, childVal) { - return childVal === undefined ? parentVal : childVal; - }; - - /** - * Make sure component options get converted to actual - * constructors. - * - * @param {Object} options - */ - - function guardComponents(options) { - if (options.components) { - var components = options.components = guardArrayAssets(options.components); - var ids = Object.keys(components); - var def; - if (process.env.NODE_ENV !== 'production') { - var map = options._componentNameMap = {}; - } - for (var i = 0, l = ids.length; i < l; i++) { - var key = ids[i]; - if (commonTagRE.test(key) || reservedTagRE.test(key)) { - process.env.NODE_ENV !== 'production' && warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + key); - continue; - } - // record a all lowercase <-> kebab-case mapping for - // possible custom element case error warning - if (process.env.NODE_ENV !== 'production') { - map[key.replace(/-/g, '').toLowerCase()] = hyphenate(key); - } - def = components[key]; - if (isPlainObject(def)) { - components[key] = Vue.extend(def); - } - } - } - } - - /** - * Ensure all props option syntax are normalized into the - * Object-based format. - * - * @param {Object} options - */ - - function guardProps(options) { - var props = options.props; - var i, val; - if (isArray(props)) { - options.props = {}; - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - options.props[val] = null; - } else if (val.name) { - options.props[val.name] = val; - } - } - } else if (isPlainObject(props)) { - var keys = Object.keys(props); - i = keys.length; - while (i--) { - val = props[keys[i]]; - if (typeof val === 'function') { - props[keys[i]] = { type: val }; - } - } - } - } - - /** - * Guard an Array-format assets option and converted it - * into the key-value Object format. - * - * @param {Object|Array} assets - * @return {Object} - */ - - function guardArrayAssets(assets) { - if (isArray(assets)) { - var res = {}; - var i = assets.length; - var asset; - while (i--) { - asset = assets[i]; - var id = typeof asset === 'function' ? asset.options && asset.options.name || asset.id : asset.name || asset.id; - if (!id) { - process.env.NODE_ENV !== 'production' && warn('Array-syntax assets must provide a "name" or "id" field.'); - } else { - res[id] = asset; - } - } - return res; - } - return assets; - } - - /** - * Merge two option objects into a new one. - * Core utility used in both instantiation and inheritance. - * - * @param {Object} parent - * @param {Object} child - * @param {Vue} [vm] - if vm is present, indicates this is - * an instantiation merge. - */ - - function mergeOptions(parent, child, vm) { - guardComponents(child); - guardProps(child); - var options = {}; - var key; - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - for (key in parent) { - mergeField(key); - } - for (key in child) { - if (!hasOwn(parent, key)) { - mergeField(key); - } - } - function mergeField(key) { - var strat = strats[key] || defaultStrat; - options[key] = strat(parent[key], child[key], vm, key); - } - return options; - } - - /** - * Resolve an asset. - * This function is used because child instances need access - * to assets defined in its ancestor chain. - * - * @param {Object} options - * @param {String} type - * @param {String} id - * @param {Boolean} warnMissing - * @return {Object|Function} - */ - - function resolveAsset(options, type, id, warnMissing) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return; - } - var assets = options[type]; - var camelizedId; - var res = assets[id] || - // camelCase ID - assets[camelizedId = camelize(id)] || - // Pascal Case ID - assets[camelizedId.charAt(0).toUpperCase() + camelizedId.slice(1)]; - if (process.env.NODE_ENV !== '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; - } - - /** - * 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} 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, - del: del, - hasOwn: hasOwn, - isLiteral: isLiteral, - isReserved: isReserved, - _toString: _toString, - toNumber: toNumber, - toBoolean: toBoolean, - stripQuotes: stripQuotes, - camelize: camelize, - hyphenate: hyphenate, - classify: classify, - bind: bind, - toArray: toArray, - extend: extend, - isObject: isObject, - isPlainObject: isPlainObject, - def: def, - debounce: _debounce, - indexOf: indexOf, - cancellable: cancellable, - looseEqual: looseEqual, - isArray: isArray, - hasProto: hasProto, - inBrowser: inBrowser, - devtools: devtools, - isIE9: isIE9, - isAndroid: isAndroid, - get transitionProp () { return transitionProp; }, - get transitionEndEvent () { return transitionEndEvent; }, - get animationProp () { return animationProp; }, - get animationEndEvent () { return animationEndEvent; }, - nextTick: nextTick, - query: query, - inDoc: inDoc, - getAttr: getAttr, - getBindAttr: getBindAttr, - hasBindAttr: hasBindAttr, - before: before, - after: after, - remove: remove, - prepend: prepend, - replace: replace, - on: on, - off: off, - setClass: setClass, - addClass: addClass, - removeClass: removeClass, - extractContent: extractContent, - trimNode: trimNode, - isTemplate: isTemplate, - createAnchor: createAnchor, - findRef: findRef, - mapNodeRange: mapNodeRange, - removeNodeRange: removeNodeRange, - isFragment: isFragment, - getOuterHTML: getOuterHTML, - mergeOptions: mergeOptions, - resolveAsset: resolveAsset, - checkComponentAttr: checkComponentAttr, - commonTagRE: commonTagRE, - reservedTagRE: reservedTagRE, - get warn () { return warn; } - }); - - var uid = 0; - - function initMixin (Vue) { - /** - * The main init sequence. This is called for every - * instance, including ones that are created from extended - * constructors. - * - * @param {Object} options - this options object should be - * the result of merging class - * options and the options passed - * in to the constructor. - */ - - Vue.prototype._init = function (options) { - options = options || {}; - - this.$el = null; - this.$parent = options.parent; - this.$root = this.$parent ? this.$parent.$root : this; - this.$children = []; - this.$refs = {}; // child vm references - this.$els = {}; // element references - this._watchers = []; // all watchers as an array - this._directives = []; // all directives - - // a uid - this._uid = uid++; - - // a flag to avoid this being observed - this._isVue = true; - - // events bookkeeping - this._events = {}; // registered callbacks - this._eventsCount = {}; // for $broadcast optimization - - // fragment instance properties - this._isFragment = false; - this._fragment = // @type {DocumentFragment} - this._fragmentStart = // @type {Text|Comment} - this._fragmentEnd = null; // @type {Text|Comment} - - // lifecycle state - this._isCompiled = this._isDestroyed = this._isReady = this._isAttached = this._isBeingDestroyed = this._vForRemoving = false; - this._unlinkFn = null; - - // context: - // if this is a transcluded component, context - // will be the common parent vm of this instance - // and its host. - this._context = options._context || this.$parent; - - // scope: - // if this is inside an inline v-for, the scope - // will be the intermediate scope created for this - // repeat fragment. this is used for linking props - // and container directives. - this._scope = options._scope; - - // fragment: - // if this instance is compiled inside a Fragment, it - // needs to reigster itself as a child of that fragment - // for attach/detach to work properly. - this._frag = options._frag; - if (this._frag) { - this._frag.children.push(this); - } - - // push self into parent / transclusion host - if (this.$parent) { - this.$parent.$children.push(this); - } - - // merge options. - options = this.$options = mergeOptions(this.constructor.options, options, this); - - // set ref - this._updateRef(); - - // initialize data as empty object. - // it will be filled up in _initScope(). - this._data = {}; - - // save raw constructor data before merge - // so that we know which properties are provided at - // instantiation. - this._runtimeData = options.data; - - // call init hook - this._callHook('init'); - - // initialize data observation and scope inheritance. - this._initState(); - - // setup event system and option events. - this._initEvents(); - - // call created hook - this._callHook('created'); - - // if `el` option is passed, start compilation. - if (options.el) { - this.$mount(options.el); - } - }; - } - - var pathCache = new Cache(1000); - - // actions - var APPEND = 0; - var PUSH = 1; - var INC_SUB_PATH_DEPTH = 2; - var PUSH_SUB_PATH = 3; - - // states - var BEFORE_PATH = 0; - var IN_PATH = 1; - var BEFORE_IDENT = 2; - var IN_IDENT = 3; - var IN_SUB_PATH = 4; - var IN_SINGLE_QUOTE = 5; - var IN_DOUBLE_QUOTE = 6; - var AFTER_PATH = 7; - var ERROR = 8; - - var pathStateMachine = []; - - pathStateMachine[BEFORE_PATH] = { - 'ws': [BEFORE_PATH], - 'ident': [IN_IDENT, APPEND], - '[': [IN_SUB_PATH], - 'eof': [AFTER_PATH] - }; - - pathStateMachine[IN_PATH] = { - 'ws': [IN_PATH], - '.': [BEFORE_IDENT], - '[': [IN_SUB_PATH], - 'eof': [AFTER_PATH] - }; - - pathStateMachine[BEFORE_IDENT] = { - 'ws': [BEFORE_IDENT], - 'ident': [IN_IDENT, APPEND] - }; - - pathStateMachine[IN_IDENT] = { - 'ident': [IN_IDENT, APPEND], - '0': [IN_IDENT, APPEND], - 'number': [IN_IDENT, APPEND], - 'ws': [IN_PATH, PUSH], - '.': [BEFORE_IDENT, PUSH], - '[': [IN_SUB_PATH, PUSH], - 'eof': [AFTER_PATH, PUSH] - }; - - pathStateMachine[IN_SUB_PATH] = { - "'": [IN_SINGLE_QUOTE, APPEND], - '"': [IN_DOUBLE_QUOTE, APPEND], - '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH], - ']': [IN_PATH, PUSH_SUB_PATH], - 'eof': ERROR, - 'else': [IN_SUB_PATH, APPEND] - }; - - pathStateMachine[IN_SINGLE_QUOTE] = { - "'": [IN_SUB_PATH, APPEND], - 'eof': ERROR, - 'else': [IN_SINGLE_QUOTE, APPEND] - }; - - pathStateMachine[IN_DOUBLE_QUOTE] = { - '"': [IN_SUB_PATH, APPEND], - 'eof': ERROR, - 'else': [IN_DOUBLE_QUOTE, APPEND] - }; - - /** - * Determine the type of a character in a keypath. - * - * @param {Char} ch - * @return {String} type - */ - - function getPathCharType(ch) { - if (ch === undefined) { - return 'eof'; - } - - var code = ch.charCodeAt(0); - - switch (code) { - case 0x5B: // [ - case 0x5D: // ] - case 0x2E: // . - case 0x22: // " - case 0x27: // ' - case 0x30: - // 0 - return ch; - - case 0x5F: // _ - case 0x24: - // $ - return 'ident'; - - case 0x20: // Space - case 0x09: // Tab - case 0x0A: // Newline - case 0x0D: // Return - case 0xA0: // No-break space - case 0xFEFF: // Byte Order Mark - case 0x2028: // Line Separator - case 0x2029: - // Paragraph Separator - return 'ws'; - } - - // a-z, A-Z - if (code >= 0x61 && code <= 0x7A || code >= 0x41 && code <= 0x5A) { - return 'ident'; - } - - // 1-9 - if (code >= 0x31 && code <= 0x39) { - return 'number'; - } - - return 'else'; - } - - /** - * Format a subPath, return its plain form if it is - * a literal string or number. Otherwise prepend the - * dynamic indicator (*). - * - * @param {String} path - * @return {String} - */ - - function formatSubPath(path) { - var trimmed = path.trim(); - // invalid leading 0 - if (path.charAt(0) === '0' && isNaN(path)) { - return false; - } - return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed; - } - - /** - * Parse a string path into an array of segments - * - * @param {String} path - * @return {Array|undefined} - */ - - function parse(path) { - var keys = []; - var index = -1; - var mode = BEFORE_PATH; - var subPathDepth = 0; - var c, newChar, key, type, transition, action, typeMap; - - var actions = []; - - actions[PUSH] = function () { - if (key !== undefined) { - keys.push(key); - key = undefined; - } - }; - - actions[APPEND] = function () { - if (key === undefined) { - key = newChar; - } else { - key += newChar; - } - }; - - actions[INC_SUB_PATH_DEPTH] = function () { - actions[APPEND](); - subPathDepth++; - }; - - actions[PUSH_SUB_PATH] = function () { - if (subPathDepth > 0) { - subPathDepth--; - mode = IN_SUB_PATH; - actions[APPEND](); - } else { - subPathDepth = 0; - key = formatSubPath(key); - if (key === false) { - return false; - } else { - actions[PUSH](); - } - } - }; - - function maybeUnescapeQuote() { - var nextChar = path[index + 1]; - if (mode === IN_SINGLE_QUOTE && nextChar === "'" || mode === IN_DOUBLE_QUOTE && nextChar === '"') { - index++; - newChar = '\\' + nextChar; - actions[APPEND](); - return true; - } - } - - while (mode != null) { - index++; - c = path[index]; - - if (c === '\\' && maybeUnescapeQuote()) { - continue; - } - - type = getPathCharType(c); - typeMap = pathStateMachine[mode]; - transition = typeMap[type] || typeMap['else'] || ERROR; - - if (transition === ERROR) { - return; // parse error - } - - mode = transition[0]; - action = actions[transition[1]]; - if (action) { - newChar = transition[2]; - newChar = newChar === undefined ? c : newChar; - if (action() === false) { - return; - } - } - - if (mode === AFTER_PATH) { - keys.raw = path; - return keys; - } - } - } - - /** - * External parse that check for a cache hit first - * - * @param {String} path - * @return {Array|undefined} - */ - - function parsePath(path) { - var hit = pathCache.get(path); - if (!hit) { - hit = parse(path); - if (hit) { - pathCache.put(path, hit); - } - } - return hit; - } - - /** - * Get from an object from a path string - * - * @param {Object} obj - * @param {String} path - */ - - function getPath(obj, path) { - return parseExpression(path).get(obj); - } - - /** - * Warn against setting non-existent root path on a vm. - */ - - var warnNonExistent; - if (process.env.NODE_ENV !== 'production') { - 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); - }; - } - - /** - * Set on an object from a path - * - * @param {Object} obj - * @param {String | Array} path - * @param {*} val - */ - - function setPath(obj, path, val) { - var original = obj; - if (typeof path === 'string') { - path = parse(path); - } - if (!path || !isObject(obj)) { - return false; - } - var last, key; - for (var i = 0, l = path.length; i < l; i++) { - last = obj; - key = path[i]; - if (key.charAt(0) === '*') { - key = parseExpression(key.slice(1)).get.call(original, original); - } - if (i < l - 1) { - obj = obj[key]; - if (!isObject(obj)) { - obj = {}; - if (process.env.NODE_ENV !== 'production' && last._isVue) { - warnNonExistent(path, last); - } - set(last, key, obj); - } - } else { - if (isArray(obj)) { - obj.$set(key, val); - } else if (key in obj) { - obj[key] = val; - } else { - if (process.env.NODE_ENV !== 'production' && obj._isVue) { - warnNonExistent(path, obj); - } - set(obj, key, val); - } - } - } - return true; - } - - var path = Object.freeze({ - parsePath: parsePath, - getPath: getPath, - setPath: setPath - }); - - var expressionCache = new Cache(1000); - - var allowedKeywords = 'Math,Date,this,true,false,null,undefined,Infinity,NaN,' + 'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,' + 'encodeURIComponent,parseInt,parseFloat'; - var allowedKeywordsRE = new RegExp('^(' + allowedKeywords.replace(/,/g, '\\b|') + '\\b)'); - - // keywords that don't make sense inside expressions - var improperKeywords = 'break,case,class,catch,const,continue,debugger,default,' + 'delete,do,else,export,extends,finally,for,function,if,' + 'import,in,instanceof,let,return,super,switch,throw,try,' + 'var,while,with,yield,enum,await,implements,package,' + 'protected,static,interface,private,public'; - var improperKeywordsRE = new RegExp('^(' + improperKeywords.replace(/,/g, '\\b|') + '\\b)'); - - var wsRE = /\s/g; - var newlineRE = /\n/g; - var saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g; - var restoreRE = /"(\d+)"/g; - var pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/; - var identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g; - var booleanLiteralRE = /^(?:true|false)$/; - - /** - * Save / Rewrite / Restore - * - * When rewriting paths found in an expression, it is - * possible for the same letter sequences to be found in - * strings and Object literal property keys. Therefore we - * remove and store these parts in a temporary array, and - * restore them after the path rewrite. - */ - - var saved = []; - - /** - * Save replacer - * - * The save regex can match two possible cases: - * 1. An opening object literal - * 2. A string - * If matched as a plain string, we need to escape its - * newlines, since the string needs to be preserved when - * generating the function body. - * - * @param {String} str - * @param {String} isString - str if matched as a string - * @return {String} - placeholder with index - */ - - function save(str, isString) { - var i = saved.length; - saved[i] = isString ? str.replace(newlineRE, '\\n') : str; - return '"' + i + '"'; - } - - /** - * Path rewrite replacer - * - * @param {String} raw - * @return {String} - */ - - function rewrite(raw) { - var c = raw.charAt(0); - var path = raw.slice(1); - if (allowedKeywordsRE.test(path)) { - return raw; - } else { - path = path.indexOf('"') > -1 ? path.replace(restoreRE, restore) : path; - return c + 'scope.' + path; - } - } - - /** - * Restore replacer - * - * @param {String} str - * @param {String} i - matched save index - * @return {String} - */ - - function restore(str, i) { - return saved[i]; - } - - /** - * Rewrite an expression, prefixing all path accessors with - * `scope.` and generate getter/setter functions. - * - * @param {String} exp - * @return {Function} - */ - - function compileGetter(exp) { - if (improperKeywordsRE.test(exp)) { - process.env.NODE_ENV !== 'production' && warn('Avoid using reserved keywords in expression: ' + exp); - } - // reset state - saved.length = 0; - // save strings and object literal keys - var body = exp.replace(saveRE, save).replace(wsRE, ''); - // rewrite all paths - // pad 1 space here becaue the regex matches 1 extra char - body = (' ' + body).replace(identRE, rewrite).replace(restoreRE, restore); - return makeGetterFn(body); - } - - /** - * Build a getter function. Requires eval. - * - * We isolate the try/catch so it doesn't affect the - * optimization of the parse function when it is not called. - * - * @param {String} body - * @return {Function|undefined} - */ - - function makeGetterFn(body) { - try { - /* eslint-disable no-new-func */ - return new Function('scope', 'return ' + body + ';'); - /* eslint-enable no-new-func */ - } catch (e) { - process.env.NODE_ENV !== 'production' && warn('Invalid expression. ' + 'Generated function body: ' + body); - } - } - - /** - * Compile a setter function for the expression. - * - * @param {String} exp - * @return {Function|undefined} - */ - - function compileSetter(exp) { - var path = parsePath(exp); - if (path) { - return function (scope, val) { - setPath(scope, path, val); - }; - } else { - process.env.NODE_ENV !== 'production' && warn('Invalid setter expression: ' + exp); - } - } - - /** - * Parse an expression into re-written getter/setters. - * - * @param {String} exp - * @param {Boolean} needSet - * @return {Function} - */ - - function parseExpression(exp, needSet) { - exp = exp.trim(); - // try cache - var hit = expressionCache.get(exp); - if (hit) { - if (needSet && !hit.set) { - hit.set = compileSetter(hit.exp); - } - return hit; - } - var res = { exp: exp }; - res.get = isSimplePath(exp) && exp.indexOf('[') < 0 - // optimized super simple getter - ? makeGetterFn('scope.' + exp) - // dynamic getter - : compileGetter(exp); - if (needSet) { - res.set = compileSetter(exp); - } - expressionCache.put(exp, res); - return res; - } - - /** - * Check if an expression is a simple path. - * - * @param {String} exp - * @return {Boolean} - */ - - function isSimplePath(exp) { - return pathTestRE.test(exp) && - // don't treat true/false as paths - !booleanLiteralRE.test(exp) && - // Math constants e.g. Math.PI, Math.E etc. - exp.slice(0, 5) !== 'Math.'; - } - - var expression = Object.freeze({ - parseExpression: parseExpression, - isSimplePath: isSimplePath - }); - - // we have two separate queues: one for directive updates - // and one for user watcher registered via $watch(). - // we want to guarantee directive updates to be called - // before user watchers so that when user watchers are - // triggered, the DOM would have already been in updated - // state. - - var queueIndex; - var queue = []; - var userQueue = []; - var has = {}; - var circular = {}; - var waiting = false; - var internalQueueDepleted = false; - - /** - * Reset the batcher's state. - */ - - function resetBatcherState() { - queue = []; - userQueue = []; - has = {}; - circular = {}; - waiting = internalQueueDepleted = false; - } - - /** - * Flush both queues and run the watchers. - */ - - function flushBatcherQueue() { - runBatcherQueue(queue); - internalQueueDepleted = true; - runBatcherQueue(userQueue); - // dev tool hook - /* istanbul ignore if */ - if (devtools && config.devtools) { - devtools.emit('flush'); - } - resetBatcherState(); - } - - /** - * Run the watchers in a single queue. - * - * @param {Array} queue - */ - - function runBatcherQueue(queue) { - // do not cache length because more watchers might be pushed - // as we run existing watchers - for (queueIndex = 0; queueIndex < queue.length; queueIndex++) { - var watcher = queue[queueIndex]; - var id = watcher.id; - has[id] = null; - watcher.run(); - // in dev build, check and stop circular updates. - if (process.env.NODE_ENV !== 'production' && has[id] != null) { - circular[id] = (circular[id] || 0) + 1; - if (circular[id] > config._maxUpdateCount) { - warn('You may have an infinite update loop for watcher ' + 'with expression "' + watcher.expression + '"', watcher.vm); - break; - } - } - } - } - - /** - * Push a watcher into the watcher queue. - * Jobs with duplicate IDs will be skipped unless it's - * pushed when the queue is being flushed. - * - * @param {Watcher} watcher - * properties: - * - {Number} id - * - {Function} run - */ - - function pushWatcher(watcher) { - var id = watcher.id; - if (has[id] == null) { - if (internalQueueDepleted && !watcher.user) { - // an internal watcher triggered by a user watcher... - // let's run it immediately after current user watcher is done. - userQueue.splice(queueIndex + 1, 0, watcher); - } else { - // push watcher into appropriate queue - var q = watcher.user ? userQueue : queue; - has[id] = q.length; - q.push(watcher); - // queue the flush - if (!waiting) { - waiting = true; - nextTick(flushBatcherQueue); - } - } - } - } - - var uid$2 = 0; - - /** - * A watcher parses an expression, collects dependencies, - * and fires callback when the expression value changes. - * This is used for both the $watch() api and directives. - * - * @param {Vue} vm - * @param {String|Function} expOrFn - * @param {Function} cb - * @param {Object} options - * - {Array} filters - * - {Boolean} twoWay - * - {Boolean} deep - * - {Boolean} user - * - {Boolean} sync - * - {Boolean} lazy - * - {Function} [preProcess] - * - {Function} [postProcess] - * @constructor - */ - function Watcher(vm, expOrFn, cb, options) { - // mix in options - if (options) { - extend(this, options); - } - var isFn = typeof expOrFn === 'function'; - this.vm = vm; - vm._watchers.push(this); - this.expression = expOrFn; - this.cb = cb; - this.id = ++uid$2; // uid for batching - this.active = true; - this.dirty = this.lazy; // for lazy watchers - this.deps = []; - this.newDeps = []; - this.depIds = Object.create(null); - this.newDepIds = null; - this.prevError = null; // for async error stacks - // parse expression for getter/setter - if (isFn) { - this.getter = expOrFn; - this.setter = undefined; - } else { - var res = parseExpression(expOrFn, this.twoWay); - this.getter = res.get; - this.setter = res.set; - } - this.value = this.lazy ? undefined : this.get(); - // state for avoiding false triggers for deep and Array - // watchers during vm._digest() - this.queued = this.shallow = false; - } - - /** - * Evaluate the getter, and re-collect dependencies. - */ - - Watcher.prototype.get = function () { - this.beforeGet(); - var scope = this.scope || this.vm; - var value; - try { - value = this.getter.call(scope, scope); - } catch (e) { - if (process.env.NODE_ENV !== 'production' && config.warnExpressionErrors) { - warn('Error when evaluating expression ' + '"' + this.expression + '": ' + e.toString(), this.vm); - } - } - // "touch" every property so they are all tracked as - // dependencies for deep watching - if (this.deep) { - traverse(value); - } - if (this.preProcess) { - value = this.preProcess(value); - } - if (this.filters) { - value = scope._applyFilters(value, null, this.filters, false); - } - if (this.postProcess) { - value = this.postProcess(value); - } - this.afterGet(); - return value; - }; - - /** - * Set the corresponding value with the setter. - * - * @param {*} value - */ - - Watcher.prototype.set = function (value) { - var scope = this.scope || this.vm; - if (this.filters) { - value = scope._applyFilters(value, this.value, this.filters, true); - } - try { - this.setter.call(scope, scope, value); - } catch (e) { - if (process.env.NODE_ENV !== 'production' && config.warnExpressionErrors) { - 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) { - process.env.NODE_ENV !== '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 () { - if (scope.$key) { - // original is an object - forContext.rawValue[scope.$key] = value; - } else { - forContext.rawValue.$set(scope.$index, value); - } - }); - } - }; - - /** - * Prepare for dependency collection. - */ - - Watcher.prototype.beforeGet = function () { - Dep.target = this; - this.newDepIds = Object.create(null); - this.newDeps.length = 0; - }; - - /** - * Add a dependency to this directive. - * - * @param {Dep} dep - */ - - Watcher.prototype.addDep = function (dep) { - var id = dep.id; - if (!this.newDepIds[id]) { - this.newDepIds[id] = true; - this.newDeps.push(dep); - if (!this.depIds[id]) { - dep.addSub(this); - } - } - }; - - /** - * Clean up for dependency collection. - */ - - Watcher.prototype.afterGet = function () { - Dep.target = null; - var i = this.deps.length; - while (i--) { - var dep = this.deps[i]; - if (!this.newDepIds[dep.id]) { - dep.removeSub(this); - } - } - this.depIds = this.newDepIds; - var tmp = this.deps; - this.deps = this.newDeps; - this.newDeps = tmp; - }; - - /** - * Subscriber interface. - * Will be called when a dependency changes. - * - * @param {Boolean} shallow - */ - - Watcher.prototype.update = function (shallow) { - if (this.lazy) { - this.dirty = true; - } else if (this.sync || !config.async) { - this.run(); - } else { - // if queued, only overwrite shallow with non-shallow, - // but not the other way around. - this.shallow = this.queued ? shallow ? this.shallow : false : !!shallow; - this.queued = true; - // record before-push error stack in debug mode - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.debug) { - this.prevError = new Error('[vue] async stack trace'); - } - pushWatcher(this); - } - }; - - /** - * Batcher job interface. - * Will be called by the batcher. - */ - - Watcher.prototype.run = function () { - if (this.active) { - var value = this.get(); - if (value !== this.value || - // Deep watchers and watchers on Object/Arrays should fire even - // when the value is the same, because the value may - // have mutated; but only do so if this is a - // non-shallow update (caused by a vm digest). - (isObject(value) || this.deep) && !this.shallow) { - // set new value - var oldValue = this.value; - this.value = value; - // in debug + async mode, when a watcher callbacks - // throws, we also throw the saved before-push error - // so the full cross-tick stack trace is available. - var prevError = this.prevError; - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && config.debug && prevError) { - this.prevError = null; - try { - this.cb.call(this.vm, value, oldValue); - } catch (e) { - nextTick(function () { - throw prevError; - }, 0); - throw e; - } - } else { - this.cb.call(this.vm, value, oldValue); - } - } - this.queued = this.shallow = false; - } - }; - - /** - * Evaluate the value of the watcher. - * This only gets called for lazy watchers. - */ - - Watcher.prototype.evaluate = function () { - // avoid overwriting another watcher that is being - // collected. - var current = Dep.target; - this.value = this.get(); - this.dirty = false; - Dep.target = current; - }; - - /** - * Depend on all deps collected by this watcher. - */ - - Watcher.prototype.depend = function () { - var i = this.deps.length; - while (i--) { - this.deps[i].depend(); - } - }; - - /** - * Remove self from all dependencies' subcriber list. - */ - - Watcher.prototype.teardown = function () { - if (this.active) { - // remove self from vm's watcher list - // this is a somewhat expensive operation so we skip it - // if the vm is being destroyed or is performing a v-for - // re-render (the watcher list is then filtered by v-for). - if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) { - this.vm._watchers.$remove(this); - } - var i = this.deps.length; - while (i--) { - this.deps[i].removeSub(this); - } - this.active = false; - this.vm = this.cb = this.value = null; - } - }; - - /** - * Recrusively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - * - * @param {*} val - */ - - function traverse(val) { - var i, keys; - if (isArray(val)) { - i = val.length; - while (i--) traverse(val[i]); - } else if (isObject(val)) { - keys = Object.keys(val); - i = keys.length; - while (i--) traverse(val[keys[i]]); - } - } - - var text$1 = { - - bind: function bind() { - this.attr = this.el.nodeType === 3 ? 'data' : 'textContent'; - }, - - update: function update(value) { - this.el[this.attr] = _toString(value); - } - }; - - var templateCache = new Cache(1000); - var idSelectorCache = new Cache(1000); - - var map = { - efault: [0, '', ''], - legend: [1, '
', '
'], - tr: [2, '', '
'], - col: [2, '', '
'] - }; - - map.td = map.th = [3, '', '
']; - - map.option = map.optgroup = [1, '']; - - map.thead = map.tbody = map.colgroup = map.caption = map.tfoot = [1, '', '
']; - - map.g = map.defs = map.symbol = map.use = map.image = map.text = map.circle = map.ellipse = map.line = map.path = map.polygon = map.polyline = map.rect = [1, '', '']; - - /** - * Check if a node is a supported template node with a - * DocumentFragment content. - * - * @param {Node} node - * @return {Boolean} - */ - - function isRealTemplate(node) { - return isTemplate(node) && isFragment(node.content); - } - - var tagRE$1 = /<([\w:-]+)/; - var entityRE = /&#?\w+?;/; - - /** - * Convert a string template to a DocumentFragment. - * Determines correct wrapping by tag types. Wrapping - * strategy found in jQuery & component/domify. - * - * @param {String} templateString - * @param {Boolean} raw - * @return {DocumentFragment} - */ - - function stringToFragment(templateString, raw) { - // try a cache hit first - var cacheKey = raw ? templateString : templateString.trim(); - var hit = templateCache.get(cacheKey); - if (hit) { - return hit; - } - - var frag = document.createDocumentFragment(); - var tagMatch = templateString.match(tagRE$1); - var entityMatch = entityRE.test(templateString); - - if (!tagMatch && !entityMatch) { - // text only, return a single text node. - frag.appendChild(document.createTextNode(templateString)); - } else { - var tag = tagMatch && tagMatch[1]; - var wrap = map[tag] || map.efault; - var depth = wrap[0]; - var prefix = wrap[1]; - var suffix = wrap[2]; - var node = document.createElement('div'); - - node.innerHTML = prefix + templateString + suffix; - while (depth--) { - node = node.lastChild; - } - - var child; - /* eslint-disable no-cond-assign */ - while (child = node.firstChild) { - /* eslint-enable no-cond-assign */ - frag.appendChild(child); - } - } - if (!raw) { - trimNode(frag); - } - templateCache.put(cacheKey, frag); - return frag; - } - - /** - * Convert a template node to a DocumentFragment. - * - * @param {Node} node - * @return {DocumentFragment} - */ - - function nodeToFragment(node) { - // if its a template tag and the browser supports it, - // its content is already a document fragment. - if (isRealTemplate(node)) { - trimNode(node.content); - return node.content; - } - // script template - if (node.tagName === 'SCRIPT') { - return stringToFragment(node.textContent); - } - // normal node, clone it to avoid mutating the original - var clonedNode = cloneNode(node); - var frag = document.createDocumentFragment(); - var child; - /* eslint-disable no-cond-assign */ - while (child = clonedNode.firstChild) { - /* eslint-enable no-cond-assign */ - frag.appendChild(child); - } - trimNode(frag); - return frag; - } - - // Test for the presence of the Safari template cloning bug - // https://bugs.webkit.org/showug.cgi?id=137755 - var hasBrokenTemplate = (function () { - /* istanbul ignore else */ - if (inBrowser) { - var a = document.createElement('div'); - a.innerHTML = ''; - return !a.cloneNode(true).firstChild.innerHTML; - } else { - return false; - } - })(); - - // Test for IE10/11 textarea placeholder clone bug - var hasTextareaCloneBug = (function () { - /* istanbul ignore else */ - if (inBrowser) { - var t = document.createElement('textarea'); - t.placeholder = 't'; - return t.cloneNode(true).value === 't'; - } else { - return false; - } - })(); - - /** - * 1. Deal with Safari cloning nested