PHP Classes

File: public/js/lib/vue/dist/vue.js

Recommend this page to a friend!
  Classes of Sergey Beskorovayniy   Silex MVC Blog   public/js/lib/vue/dist/vue.js   Download  
File: public/js/lib/vue/dist/vue.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Silex MVC Blog
MVC based blog using on the Silex micro-framework
Author: By
Last change:
Date: 8 years ago
Size: 272,834 bytes
 

Contents

Class file image Download
/*! * Vue.js v1.0.26 * (c) 2016 Evan You * Released under the MIT License. */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.Vue = factory()); }(this, function () { '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 */ function del(obj, key) { if (!hasOwn(obj, key)) { return; } delete obj[key]; var ob = obj.__ob__; if (!ob) { if (obj._isVue) { delete obj._data[key]; obj._digest(); } return; } ob.dep.notify(); if (ob.vms) { var i = ob.vms.length; while (i--) { var vm = ob.vms[i]; vm._unproxy(key); vm._digest(); } } } var hasOwnProperty = Object.prototype.hasOwnProperty; /** * Check whether the object has the property. * * @param {Object} obj * @param {String} key * @return {Boolean} */ function hasOwn(obj, key) { return hasOwnProperty.call(obj, key); } /** * Check if an expression is a literal value. * * @param {String} exp * @return {Boolean} */ var literalValueRE = /^\s?(true|false|-?[\d\.]+|'[^']*'|"[^"]*")\s?$/; function isLiteral(exp) { return literalValueRE.test(exp); } /** * Check if a string starts with $ or _ * * @param {String} str * @return {Boolean} */ function isReserved(str) { var c = (str + '').charCodeAt(0); return c === 0x24 || c === 0x5F; } /** * Guard text output, make sure undefined outputs * empty string * * @param {*} value * @return {String} */ function _toString(value) { return value == null ? '' : value.toString(); } /** * Check and convert possible numeric strings to numbers * before setting back to data * * @param {*} value * @return {*|Number} */ function toNumber(value) { if (typeof value !== 'string') { return value; } else { var parsed = Number(value); return isNaN(parsed) ? value : parsed; } } /** * Convert string boolean literals into real booleans. * * @param {*} value * @return {*|Boolean} */ function toBoolean(value) { return value === 'true' ? true : value === 'false' ? false : value; } /** * Strip quotes from a string * * @param {String} str * @return {String | false} */ 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; } /** * 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() : ''; } /** * Hyphenate a camelCase string. * * @param {String} str * @return {String} */ var hyphenateRE = /([a-z\d])([A-Z])/g; function hyphenate(str) { return str.replace(hyphenateRE, '$1-$2').toLowerCase(); } /** * 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} */ var classifyRE = /(?:^|[-_\/])(\w)/g; function classify(str) { return str.replace(classifyRE, toUpper); } /** * Simple bind, faster than native * * @param {Function} fn * @param {Object} ctx * @return {Function} */ function bind(fn, ctx) { return function (a) { var l = arguments.length; return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx); }; } /** * Convert an Array-like object to a real Array. * * @param {Array-like} list * @param {Number} [start] - start index * @return {Array} */ 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; } /** * 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]]; } 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'; } /** * 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; } /** * Array type check. * * @param {*} obj * @return {Boolean} */ var isArray = Array.isArray; /** * Define a property. * * @param {Object} obj * @param {String} key * @param {*} val * @param {Boolean} [enumerable] */ function def(obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true }); } /** * 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 */ 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; }; } /** * Manual indexOf because it's slightly faster than * native. * * @param {Array} arr * @param {*} obj */ function indexOf(arr, obj) { var i = arr.length; while (i--) { if (arr[i] === obj) return i; } return -1; } /** * Make a cancellable version of an async callback. * * @param {Function} fn * @return {Function} */ function cancellable(fn) { var cb = function cb() { if (!cb.cancelled) { return fn.apply(this, arguments); } }; cb.cancel = function () { cb.cancelled = true; }; return cb; } /** * 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} */ function looseEqual(a, b) { /* eslint-disable eqeqeq */ return a == b || (isObject(a) && isObject(b) ? JSON.stringify(a) === JSON.stringify(b) : false); /* eslint-enable eqeqeq */ } var hasProto = ('__proto__' in {}); // Browser environment sniffing var inBrowser = typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]'; // detect devtools var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; // UA sniffing for working around browser-specific quirks var UA = inBrowser && window.navigator.userAgent.toLowerCase(); var isIE = UA && UA.indexOf('trident') > 0; var isIE9 = UA && UA.indexOf('msie 9.0') > 0; var isAndroid = UA && UA.indexOf('android') > 0; var isIos = UA && /(iphone|ipad|ipod|ios)/i.test(UA); var iosVersionMatch = isIos && UA.match(/os ([\d_]+)/); var iosVersion = iosVersionMatch && iosVersionMatch[1].split('_'); // detecting iOS UIWebView by indexedDB var hasMutationObserverBug = iosVersion && Number(iosVersion[0]) >= 9 && Number(iosVersion[1]) >= 3 && !window.indexedDB; var transitionProp = undefined; var transitionEndEvent = undefined; var animationProp = undefined; var animationEndEvent = undefined; // 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'; } /** * 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 */ var nextTick = (function () { var callbacks = []; var pending = false; var timerFunc; function nextTickHandler() { pending = false; var copies = callbacks.slice(0); callbacks = []; for (var i = 0; i < copies.length; i++) { copies[i](); } } /* istanbul ignore if */ if (typeof MutationObserver !== 'undefined' && !hasMutationObserverBug) { var counter = 1; var observer = new MutationObserver(nextTickHandler); var textNode = document.createTextNode(counter); observer.observe(textNode, { characterData: true }); timerFunc = function () { counter = (counter + 1) % 2; textNode.data = 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; callbacks.push(func); if (pending) return; pending = true; timerFunc(nextTickHandler, 0); }; })(); var _Set = undefined; /* istanbul ignore if */ if (typeof Set !== 'undefined' && Set.toString().match(/native code/)) { // use native Set when available. _Set = Set; } else { // a non-standard Set polyfill that only works with primitive keys. _Set = function () { this.set = Object.create(null); }; _Set.prototype.has = function (key) { return this.set[key] !== undefined; }; _Set.prototype.add = function (key) { this.set[key] = 1; }; _Set.prototype.clear = function () { this.set = Object.create(null); }; } function Cache(limit) { this.size = 0; this.limit = limit; this.head = this.tail = undefined; this._keymap = Object.create(null); } var p = Cache.prototype; /** * Put <value> into the cache associated with <key>. * 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} */ p.put = function (key, value) { var removed; var entry = this.get(key, true); if (!entry) { if (this.size === this.limit) { removed = this.shift(); } entry = { key: key }; this._keymap[key] = entry; if (this.tail) { this.tail.newer = entry; entry.older = this.tail; } else { this.head = entry; } this.tail = entry; this.size++; } entry.value = value; return removed; }; /** * Purge the least recently used (oldest) entry from the * cache. Returns the removed entry or undefined if the * cache was empty. */ 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; }; /** * Get and register recent use of <key>. Returns the value * associated with <key> or undefined if not in cache. * * @param {String} key * @param {Boolean} returnEntry * @return {Entry|*} */ 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 <D> E if (entry.newer) { if (entry === this.head) { this.head = entry.newer; } entry.newer.older = entry.older; // C <-- E. } 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+/; /** * Parser state */ 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 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; } /** * Check if an argument is dynamic and strip quotes. * * @param {String} arg * @return {Object} */ 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 }; } } /** * 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} */ 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; // } } } } if (dir.expression == null) { dir.expression = str.slice(0, i).trim(); } else if (lastFilterIndex !== 0) { pushFilter(); } cache$1.put(s, dir); return dir; } 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 */ function escapeRegex(str) { return str.replace(regexEscapeRE, '\\$&'); } 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 + '((?:.|\\n)+?)' + unsafeClose + '$'); // reset cache cache = new Cache(1000); } /** * Parse a template text string into an array of tokens. * * @param {String} text * @return {Array<Object> | null} * - {String} type * - {String} value * - {Boolean} [html] * - {Boolean} [oneTime] */ 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) }); } // 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; } /** * 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} */ 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); } } /** * Format a single token. * * @param {Object} token * @param {Vue} [vm] * @param {Boolean} [single] * @return {String} */ function formatToken(token, vm, single) { return token.tag ? token.oneTime && vm ? '"' + vm.$eval(token.value) + '"' : inlineFilters(token.value, single) : '"' + token.value + '"'; } /** * 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} */ 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 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: 'development' !== '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<String>} */ 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 ('development' !== '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) + '>)' : ''; }; })(); } /** * Append with transition. * * @param {Element} el * @param {Element} target * @param {Vue} vm * @param {Function} [cb] */ function appendWithTransition(el, target, vm, cb) { applyTransition(el, 1, function () { target.appendChild(el); }, vm, cb); } /** * InsertBefore with transition. * * @param {Element} el * @param {Element} target * @param {Vue} vm * @param {Function} [cb] */ function beforeWithTransition(el, target, vm, cb) { applyTransition(el, 1, function () { before(el, target); }, vm, cb); } /** * Remove with transition. * * @param {Element} el * @param {Vue} vm * @param {Function} [cb] */ function removeWithTransition(el, vm, cb) { applyTransition(el, -1, function () { remove(el); }, vm, cb); } /** * 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] */ 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 }); /** * Query an element selector if it's not an element already. * * @param {String|Element} el * @return {Element} */ function query(el) { if (typeof el === 'string') { var selector = el; el = document.querySelector(el); if (!el) { 'development' !== 'production' && warn('Cannot find element: ' + selector); } } 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} */ function inDoc(node) { if (!node) return false; var doc = node.ownerDocument.documentElement; var parent = node.parentNode; return doc === node || doc === parent || !!(parent && parent.nodeType === 1 && doc.contains(parent)); } /** * 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); } 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} */ 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 */ function before(el, target) { target.parentNode.insertBefore(el, target); } /** * 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); } } /** * Remove el from DOM * * @param {Element} el */ function remove(el) { el.parentNode.removeChild(el); } /** * Prepend el to target * * @param {Element} el * @param {Element} target */ function prepend(el, target) { if (target.firstChild) { before(el, target.firstChild); } else { target.appendChild(el); } } /** * Replace target with el * * @param {Element} target * @param {Element} el */ function replace(target, el) { var parent = target.parentNode; if (parent) { parent.replaceChild(el, target); } } /** * Add event listener shorthand. * * @param {Element} el * @param {String} event * @param {Function} cb * @param {Boolean} [useCapture] */ function on(el, event, cb, useCapture) { el.addEventListener(event, cb, useCapture); } /** * Remove event listener shorthand. * * @param {Element} el * @param {String} event * @param {Function} cb */ function off(el, event, cb) { el.removeEventListener(event, cb); } /** * For IE9 compat: when both class and :class are present * getAttribute('class') returns wrong value... * * @param {Element} el * @return {String} */ function getClass(el) { var classname = el.className; if (typeof classname === 'object') { classname = classname.baseVal || ''; } return classname; } /** * In IE9, setAttribute('class') will result in empty class * if the element also has the :class attribute; However in * PhantomJS, setting `className` does not work on SVG elements... * So we have to do a conditional check here. * * @param {Element} el * @param {String} cls */ function setClass(el, cls) { /* istanbul ignore if */ if (isIE9 && !/svg$/.test(el.namespaceURI)) { el.className = cls; } else { el.setAttribute('class', cls); } } /** * Add class with compatibility for IE & SVG * * @param {Element} el * @param {String} cls */ function addClass(el, cls) { if (el.classList) { el.classList.add(cls); } else { var cur = ' ' + getClass(el) + ' '; if (cur.indexOf(' ' + cls + ' ') < 0) { setClass(el, (cur + cls).trim()); } } } /** * Remove class with compatibility for IE & SVG * * @param {Element} el * @param {String} cls */ function removeClass(el, cls) { if (el.classList) { el.classList.remove(cls); } else { var cur = ' ' + getClass(el) + ' '; var tar = ' ' + cls + ' '; while (cur.indexOf(tar) >= 0) { cur = cur.replace(tar, ' '); } setClass(el, cur.trim()); } if (!el.className) { el.removeAttribute('class'); } } /** * Extract raw content inside an element into a temporary * container div * * @param {Element} el * @param {Boolean} asFragment * @return {Element|DocumentFragment} */ function extractContent(el, asFragment) { var child; var rawContent; /* istanbul ignore if */ if (isTemplate(el) && isFragment(el.content)) { el = el.content; } 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); } } return rawContent; } /** * Trim possible empty head/tail text and comment * nodes inside a parent. * * @param {Node} node */ function trimNode(node) { var child; /* eslint-disable no-sequences */ while ((child = node.firstChild, isTrimmable(child))) { node.removeChild(child); } while ((child = node.lastChild, isTrimmable(child))) { node.removeChild(child); } /* eslint-enable no-sequences */ } function isTrimmable(node) { return node && (node.nodeType === 3 && !node.data.trim() || node.nodeType === 8); } /** * 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 */ function isTemplate(el) { return el.tagName && el.tagName.toLowerCase() === 'template'; } /** * 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; } /** * 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, '')); } } } } /** * 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; } op(end); } /** * 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 */ 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]); } cb && cb(); } } } /** * 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; } } 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 ('development' !== '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 // Firefox returns unknown for some "Interactive elements." !/^(data|time|rtc|rb|details|dialog|summary)$/.test(tag) ); } }; } /** * 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, options); if (is) { return is; } else if ('development' !== '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.'); } } } } else if (hasAttrs) { return getIsBinding(el, options); } } /** * Get "is" binding from an element. * * @param {Element} el * @param {Object} options * @return {Object|undefined} */ function getIsBinding(el, options) { // dynamic syntax var exp = el.getAttribute('is'); if (exp != null) { if (resolveAsset(options, 'components', exp)) { el.removeAttribute('is'); return { id: exp }; } } else { exp = getBindAttr(el, 'is'); if (exp != null) { return { id: exp, dynamic: true }; } } } /** * 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') { 'development' !== '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') { 'development' !== 'production' && warn('The "el" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm); return; } var ret = childVal || parentVal; // 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 || null); 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 ('development' !== '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)) { 'development' !== '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 ('development' !== '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) { 'development' !== '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); if ('development' !== 'production') { if (child.propsData && !vm) { warn('propsData can only be used as an instantiation option.'); } } var options = {}; var key; if (child['extends']) { parent = typeof child['extends'] === 'function' ? mergeOptions(parent, child['extends'].options, vm) : mergeOptions(parent, child['extends'], vm); } if (child.mixins) { for (var i = 0, l = child.mixins.length; i < l; i++) { var mixin = child.mixins[i]; var mixinOptions = mixin.prototype instanceof Vue ? mixin.options : mixin; parent = mergeOptions(parent, mixinOptions, 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 ('development' !== 'production' && warnMissing && !res) { warn('Failed to resolve ' + type.slice(0, -1) + ': ' + id, options); } return res; } var uid$1 = 0; /** * A dep is an observable that can have multiple * directives subscribing to it. * * @constructor */ function Dep() { this.id = uid$1++; this.subs = []; } // the current target watcher being evaluated. // this is globally unique because there could be only one // watcher being evaluated at any time. Dep.target = null; /** * Add a directive subscriber. * * @param {Directive} sub */ Dep.prototype.addSub = function (sub) { this.subs.push(sub); }; /** * Remove a directive subscriber. * * @param {Directive} sub */ Dep.prototype.removeSub = function (sub) { this.subs.$remove(sub); }; /** * Add self as a dependency to the target watcher. */ Dep.prototype.depend = function () { Dep.target.addDep(this); }; /** * Notify all subscribers of a new value. */ Dep.prototype.notify = function () { // stablize the subscriber list first var subs = toArray(this.subs); for (var i = 0, l = subs.length; i < l; i++) { subs[i].update(); } }; var arrayProto = Array.prototype; var arrayMethods = Object.create(arrayProto) /** * Intercept mutating methods and emit events */ ;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) { // cache original method var original = arrayProto[method]; def(arrayMethods, method, function mutator() { // avoid leaking arguments: // http://jsperf.com/closure-with-arguments var i = arguments.length; var args = new Array(i); while (i--) { args[i] = arguments[i]; } var result = original.apply(this, args); var ob = this.__ob__; var inserted; switch (method) { case 'push': inserted = args; break; case 'unshift': inserted = args; break; case 'splice': inserted = args.slice(2); break; } if (inserted) ob.observeArray(inserted); // notify change ob.dep.notify(); return result; }); }); /** * Swap the element at the given index with a new value * and emits corresponding event. * * @param {Number} index * @param {*} val * @return {*} - replaced element */ def(arrayProto, '$set', function $set(index, val) { if (index >= this.length) { this.length = Number(index) + 1; } return this.splice(index, 1, val)[0]; }); /** * Convenience method to remove the element at given index or target element reference. * * @param {*} item */ def(arrayProto, '$remove', function $remove(item) { /* istanbul ignore if */ if (!this.length) return; var index = indexOf(this, item); if (index > -1) { return this.splice(index, 1); } }); var arrayKeys = Object.getOwnPropertyNames(arrayMethods); /** * By default, when a reactive property is set, the new value is * also converted to become reactive. However in certain cases, e.g. * v-for scope alias and props, we don't want to force conversion * because the value may be a nested value under a frozen data structure. * * So whenever we want to set a reactive property without forcing * conversion on the new value, we wrap that call inside this function. */ var shouldConvert = true; function withoutConversion(fn) { shouldConvert = false; fn(); shouldConvert = true; } /** * 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, isIE: isIE, isIE9: isIE9, isAndroid: isAndroid, isIos: isIos, iosVersionMatch: iosVersionMatch, iosVersion: iosVersion, hasMutationObserverBug: hasMutationObserverBug, get transitionProp () { return transitionProp; }, get transitionEndEvent () { return transitionEndEvent; }, get animationProp () { return animationProp; }, get animationEndEvent () { return animationEndEvent; }, nextTick: nextTick, get _Set () { return _Set; }, 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 _initData(). this._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 ('development' !== '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 ('development' !== '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 ('development' !== '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 literalValueRE$1 = /^(?:true|false|null|undefined|Infinity|NaN)$/; function noop() {} /** * 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)) { 'development' !== '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 because 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) { if ('development' !== 'production') { /* istanbul ignore if */ if (e.toString().match(/unsafe-eval|CSP/)) { warn('It seems you are using the default build of Vue.js in an environment ' + 'with Content Security Policy that prohibits unsafe-eval. ' + 'Use the CSP-compliant build instead: ' + 'http://vuejs.org/guide/installation.html#CSP-compliant-build'); } else { warn('Invalid expression. ' + 'Generated function body: ' + body); } } return noop; } } /** * 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 { 'development' !== '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 literal values as paths !literalValueRE$1.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 queue = []; var userQueue = []; var has = {}; var circular = {}; var waiting = false; /** * Reset the batcher's state. */ function resetBatcherState() { queue.length = 0; userQueue.length = 0; has = {}; circular = {}; waiting = false; } /** * Flush both queues and run the watchers. */ function flushBatcherQueue() { var _again = true; _function: while (_again) { _again = false; runBatcherQueue(queue); runBatcherQueue(userQueue); // user watchers triggered more watchers, // keep flushing until it depletes if (queue.length) { _again = true; continue _function; } // 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 (var i = 0; i < queue.length; i++) { var watcher = queue[i]; var id = watcher.id; has[id] = null; watcher.run(); // in dev build, check and stop circular updates. if ('development' !== '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; } } } queue.length = 0; } /** * 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) { // 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 = new _Set(); this.newDepIds = new _Set(); 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 ('development' !== '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 ('development' !== '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) { 'development' !== 'production' && warn('It seems you are using two-way binding on ' + 'a v-for alias (' + this.expression + '), and the ' + 'v-for has filters. This will not work properly. ' + 'Either remove the filters or use an array of ' + 'objects and bind to object properties instead.', this.vm); return; } forContext._withLock(function () { 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; }; /** * Add a dependency to this directive. * * @param {Dep} dep */ Watcher.prototype.addDep = function (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); } } }; /** * 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.has(dep.id)) { dep.removeSub(this); } } 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; }; /** * 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 ('development' !== '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 ('development' !== '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 */ var seenObjects = new _Set(); function traverse(val, seen) { var i = undefined, keys = undefined; if (!seen) { seen = seenObjects; seen.clear(); } var isA = isArray(val); var isO = isObject(val); if ((isA || isO) && Object.isExtensible(val)) { if (val.__ob__) { var depId = val.__ob__.dep.id; if (seen.has(depId)) { return; } else { seen.add(depId); } } if (isA) { i = val.length; while (i--) traverse(val[i], seen); } else if (isO) { keys = Object.keys(val); i = keys.length; while (i--) traverse(val[keys[i]], seen); } } } 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, '<fieldset>', '</fieldset>'], tr: [2, '<table><tbody>', '</tbody></table>'], col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'] }; map.td = map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>']; map.option = map.optgroup = [1, '<select multiple="multiple">', '</select>']; map.thead = map.tbody = map.colgroup = map.caption = map.tfoot = [1, '<table>', '</table>']; 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, '<svg ' + 'xmlns="http://www.w3.org/2000/svg" ' + 'xmlns:xlink="http://www.w3.org/1999/xlink" ' + 'xmlns:ev="http://www.w3.org/2001/xml-events"' + 'version="1.1">', '</svg>']; /** * 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+?;/; var commentRE = /<!--/; /** * 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); var commentMatch = commentRE.test(templateString); if (!tagMatch && !entityMatch && !commentMatch) { // 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. However, iOS Safari has // bug when using directly cloned template content with touch // events and can cause crashes when the nodes are removed from DOM, so we // have to treat template elements as string templates. (#2805) /* istanbul ignore if */ if (isRealTemplate(node)) { return stringToFragment(node.innerHTML); } // 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 = '<template>1</template>'; 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 <template> bug by * manually cloning all template instances. * 2. Deal with IE10/11 textarea placeholder bug by setting * the correct value after cloning. * * @param {Element|DocumentFragment} node * @return {Element|DocumentFragment} */ function cloneNode(node) { /* istanbul ignore if */ if (!node.querySelectorAll) { return node.cloneNode(); } var res = node.cloneNode(true); var i, original, cloned; /* istanbul ignore if */ if (hasBrokenTemplate) { var tempClone = res; if (isRealTemplate(node)) { node = node.content; tempClone = res.content; } original = node.querySelectorAll('template'); if (original.length) { cloned = tempClone.querySelectorAll('template'); i = cloned.length; while (i--) { cloned[i].parentNode.replaceChild(cloneNode(original[i]), cloned[i]); } } } /* istanbul ignore if */ if (hasTextareaCloneBug) { if (node.tagName === 'TEXTAREA') { res.value = node.value; } else { original = node.querySelectorAll('textarea'); if (original.length) { cloned = res.querySelectorAll('textarea'); i = cloned.length; while (i--) { cloned[i].value = original[i].value; } } } } return res; } /** * Process the template option and normalizes it into a * a DocumentFragment that can be used as a partial or a * instance template. * * @param {*} template * Possible values include: * - DocumentFragment object * - Node object of type Template * - id selector: '#some-template-id' * - template string: '<div><span>{{msg}}</span></div>' * @param {Boolean} shouldClone * @param {Boolean} raw * inline HTML interpolation. Do not check for id * selector and keep whitespace in the string. * @return {DocumentFragment|undefined} */ function parseTemplate(template, shouldClone, raw) { var node, frag; // if the template is already a document fragment, // do nothing if (isFragment(template)) { trimNode(template); return shouldClone ? cloneNode(template) : template; } if (typeof template === 'string') { // id selector if (!raw && template.charAt(0) === '#') { // id selector can be cached too frag = idSelectorCache.get(template); if (!frag) { node = document.getElementById(template.slice(1)); if (node) { frag = nodeToFragment(node); // save selector to cache idSelectorCache.put(template, frag); } } } else { // normal string template frag = stringToFragment(template, raw); } } else if (template.nodeType) { // a direct node frag = nodeToFragment(template); } return frag && shouldClone ? cloneNode(frag) : frag; } var template = Object.freeze({ cloneNode: cloneNode, parseTemplate: parseTemplate }); var html = { bind: function bind() { // a comment node means this is a binding for // {{{ inline unescaped html }}} if (this.el.nodeType === 8) { // hold nodes this.nodes = []; // replace the placeholder with proper anchor this.anchor = createAnchor('v-html'); replace(this.el, this.anchor); } }, update: function update(value) { value = _toString(value); if (this.nodes) { this.swap(value); } else { this.el.innerHTML = value; } }, swap: function swap(value) { // remove old nodes var i = this.nodes.length; while (i--) { remove(this.nodes[i]); } // convert new value to a fragment // do not attempt to retrieve from id selector var frag = parseTemplate(value, true, true); // save a reference to these nodes so we can remove later this.nodes = toArray(frag.childNodes); before(frag, this.anchor); } }; /** * Abstraction for a partially-compiled fragment. * Can optionally compile content with a child scope. * * @param {Function} linker * @param {Vue} vm * @param {DocumentFragment} frag * @param {Vue} [host] * @param {Object} [scope] * @param {Fragment} [parentFrag] */ function Fragment(linker, vm, frag, host, scope, parentFrag) { this.children = []; this.childFrags = []; this.vm = vm; this.scope = scope; this.inserted = false; this.parentFrag = parentFrag; if (parentFrag) { parentFrag.childFrags.push(this); } this.unlink = linker(vm, frag, host, scope, this); var single = this.single = frag.childNodes.length === 1 && // do not go single mode if the only node is an anchor !frag.childNodes[0].__v_anchor; if (single) { this.node = frag.childNodes[0]; this.before = singleBefore; this.remove = singleRemove; } else { this.node = createAnchor('fragment-start'); this.end = createAnchor('fragment-end'); this.frag = frag; prepend(this.node, frag); frag.appendChild(this.end); this.before = multiBefore; this.remove = multiRemove; } this.node.__v_frag = this; } /** * Call attach/detach for all components contained within * this fragment. Also do so recursively for all child * fragments. * * @param {Function} hook */ Fragment.prototype.callHook = function (hook) { var i, l; for (i = 0, l = this.childFrags.length; i < l; i++) { this.childFrags[i].callHook(hook); } for (i = 0, l = this.children.length; i < l; i++) { hook(this.children[i]); } }; /** * Insert fragment before target, single node version * * @param {Node} target * @param {Boolean} withTransition */ function singleBefore(target, withTransition) { this.inserted = true; var method = withTransition !== false ? beforeWithTransition : before; method(this.node, target, this.vm); if (inDoc(this.node)) { this.callHook(attach); } } /** * Remove fragment, single node version */ function singleRemove() { this.inserted = false; var shouldCallRemove = inDoc(this.node); var self = this; this.beforeRemove(); removeWithTransition(this.node, this.vm, function () { if (shouldCallRemove) { self.callHook(detach); } self.destroy(); }); } /** * Insert fragment before target, multi-nodes version * * @param {Node} target * @param {Boolean} withTransition */ function multiBefore(target, withTransition) { this.inserted = true; var vm = this.vm; var method = withTransition !== false ? beforeWithTransition : before; mapNodeRange(this.node, this.end, function (node) { method(node, target, vm); }); if (inDoc(this.node)) { this.callHook(attach); } } /** * Remove fragment, multi-nodes version */ function multiRemove() { this.inserted = false; var self = this; var shouldCallRemove = inDoc(this.node); this.beforeRemove(); removeNodeRange(this.node, this.end, this.vm, this.frag, function () { if (shouldCallRemove) { self.callHook(detach); } self.destroy(); }); } /** * Prepare the fragment for removal. */ Fragment.prototype.beforeRemove = function () { var i, l; for (i = 0, l = this.childFrags.length; i < l; i++) { // call the same method recursively on child // fragments, depth-first this.childFrags[i].beforeRemove(false); } for (i = 0, l = this.children.length; i < l; i++) { // Call destroy for all contained instances, // with remove:false and defer:true. // Defer is necessary because we need to // keep the children to call detach hooks // on them. this.children[i].$destroy(false, true); } var dirs = this.unlink.dirs; for (i = 0, l = dirs.length; i < l; i++) { // disable the watchers on all the directives // so that the rendered content stays the same // during removal. dirs[i]._watcher && dirs[i]._watcher.teardown(); } }; /** * Destroy the fragment. */ Fragment.prototype.destroy = function () { if (this.parentFrag) { this.parentFrag.childFrags.$remove(this); } this.node.__v_frag = null; this.unlink(); }; /** * Call attach hook for a Vue instance. * * @param {Vue} child */ function attach(child) { if (!child._isAttached && inDoc(child.$el)) { child._callHook('attached'); } } /** * Call detach hook for a Vue instance. * * @param {Vue} child */ function detach(child) { if (child._isAttached && !inDoc(child.$el)) { child._callHook('detached'); } } var linkerCache = new Cache(5000); /** * A factory that can be used to create instances of a * fragment. Caches the compiled linker if possible. * * @param {Vue} vm * @param {Element|String} el */ function FragmentFactory(vm, el) { this.vm = vm; var template; var isString = typeof el === 'string'; if (isString || isTemplate(el) && !el.hasAttribute('v-if')) { template = parseTemplate(el, true); } else { template = document.createDocumentFragment(); template.appendChild(el); } this.template = template; // linker can be cached, but only for components var linker; var cid = vm.constructor.cid; if (cid > 0) { var cacheId = cid + (isString ? el : getOuterHTML(el)); linker = linkerCache.get(cacheId); if (!linker) { linker = compile(template, vm.$options, true); linkerCache.put(cacheId, linker); } } else { linker = compile(template, vm.$options, true); } this.linker = linker; } /** * Create a fragment instance with given host and scope. * * @param {Vue} host * @param {Object} scope * @param {Fragment} parentFrag */ FragmentFactory.prototype.create = function (host, scope, parentFrag) { var frag = cloneNode(this.template); return new Fragment(this.linker, this.vm, frag, host, scope, parentFrag); }; var ON = 700; var MODEL = 800; var BIND = 850; var TRANSITION = 1100; var EL = 1500; var COMPONENT = 1500; var PARTIAL = 1750; var IF = 2100; var FOR = 2200; var SLOT = 2300; var uid$3 = 0; var vFor = { priority: FOR, terminal: true, params: ['track-by', 'stagger', 'enter-stagger', 'leave-stagger'], bind: function bind() { // support "item in/of items" syntax var inMatch = this.expression.match(/(.*) (?:in|of) (.*)/); if (inMatch) { var itMatch = inMatch[1].match(/\((.*),(.*)\)/); if (itMatch) { this.iterator = itMatch[1].trim(); this.alias = itMatch[2].trim(); } else { this.alias = inMatch[1].trim(); } this.expression = inMatch[2]; } if (!this.alias) { 'development' !== 'production' && warn('Invalid v-for expression "' + this.descriptor.raw + '": ' + 'alias is required.', this.vm); return; } // uid as a cache identifier this.id = '__v-for__' + ++uid$3; // check if this is an option list, // so that we know if we need to update the <select>'s // v-model when the option list has changed. // because v-model has a lower priority than v-for, // the v-model is not bound here yet, so we have to // retrive it in the actual updateModel() function. var tag = this.el.tagName; this.isOption = (tag === 'OPTION' || tag === 'OPTGROUP') && this.el.parentNode.tagName === 'SELECT'; // setup anchor nodes this.start = createAnchor('v-for-start'); this.end = createAnchor('v-for-end'); replace(this.el, this.end); before(this.start, this.end); // cache this.cache = Object.create(null); // fragment factory this.factory = new FragmentFactory(this.vm, this.el); }, update: function update(data) { this.diff(data); this.updateRef(); this.updateModel(); }, /** * Diff, based on new data and old data, determine the * minimum amount of DOM manipulations needed to make the * DOM reflect the new data Array. * * The algorithm diffs the new data Array by storing a * hidden reference to an owner vm instance on previously * seen data. This allows us to achieve O(n) which is * better than a levenshtein distance based algorithm, * which is O(m * n). * * @param {Array} data */ diff: function diff(data) { // check if the Array was converted from an Object var item = data[0]; var convertedFromObject = this.fromObject = isObject(item) && hasOwn(item, '$key') && hasOwn(item, '$value'); var trackByKey = this.params.trackBy; var oldFrags = this.frags; var frags = this.frags = new Array(data.length); var alias = this.alias; var iterator = this.iterator; var start = this.start; var end = this.end; var inDocument = inDoc(start); var init = !oldFrags; var i, l, frag, key, value, primitive; // First pass, go through the new Array and fill up // the new frags array. If a piece of data has a cached // instance for it, we reuse it. Otherwise build a new // instance. for (i = 0, l = data.length; i < l; i++) { item = data[i]; key = convertedFromObject ? item.$key : null; value = convertedFromObject ? item.$value : item; primitive = !isObject(value); frag = !init && this.getCachedFrag(value, i, key); if (frag) { // reusable fragment frag.reused = true; // update $index frag.scope.$index = i; // update $key if (key) { frag.scope.$key = key; } // update iterator if (iterator) { frag.scope[iterator] = key !== null ? key : i; } // update data for track-by, object repeat & // primitive values. if (trackByKey || convertedFromObject || primitive) { withoutConversion(function () { frag.scope[alias] = value; }); } } else { // new isntance frag = this.create(value, alias, i, key); frag.fresh = !init; } frags[i] = frag; if (init) { frag.before(end); } } // we're done for the initial render. if (init) { return; } // Second pass, go through the old fragments and // destroy those who are not reused (and remove them // from cache) var removalIndex = 0; var totalRemoved = oldFrags.length - frags.length; // when removing a large number of fragments, watcher removal // turns out to be a perf bottleneck, so we batch the watcher // removals into a single filter call! this.vm._vForRemoving = true; for (i = 0, l = oldFrags.length; i < l; i++) { frag = oldFrags[i]; if (!frag.reused) { this.deleteCachedFrag(frag); this.remove(frag, removalIndex++, totalRemoved, inDocument); } } this.vm._vForRemoving = false; if (removalIndex) { this.vm._watchers = this.vm._watchers.filter(function (w) { return w.active; }); } // Final pass, move/insert new fragments into the // right place. var targetPrev, prevEl, currentPrev; var insertionIndex = 0; for (i = 0, l = frags.length; i < l; i++) { frag = frags[i]; // this is the frag that we should be after targetPrev = frags[i - 1]; prevEl = targetPrev ? targetPrev.staggerCb ? targetPrev.staggerAnchor : targetPrev.end || targetPrev.node : start; if (frag.reused && !frag.staggerCb) { currentPrev = findPrevFrag(frag, start, this.id); if (currentPrev !== targetPrev && (!currentPrev || // optimization for moving a single item. // thanks to suggestions by @livoras in #1807 findPrevFrag(currentPrev, start, this.id) !== targetPrev)) { this.move(frag, prevEl); } } else { // new instance, or still in stagger. // insert with updated stagger index. this.insert(frag, insertionIndex++, prevEl, inDocument); } frag.reused = frag.fresh = false; } }, /** * Create a new fragment instance. * * @param {*} value * @param {String} alias * @param {Number} index * @param {String} [key] * @return {Fragment} */ create: function create(value, alias, index, key) { var host = this._host; // create iteration scope var parentScope = this._scope || this.vm; var scope = Object.create(parentScope); // ref holder for the scope scope.$refs = Object.create(parentScope.$refs); scope.$els = Object.create(parentScope.$els); // make sure point $parent to parent scope scope.$parent = parentScope; // for two-way binding on alias scope.$forContext = this; // define scope properties // important: define the scope alias without forced conversion // so that frozen data structures remain non-reactive. withoutConversion(function () { defineReactive(scope, alias, value); }); defineReactive(scope, '$index', index); if (key) { defineReactive(scope, '$key', key); } else if (scope.$key) { // avoid accidental fallback def(scope, '$key', null); } if (this.iterator) { defineReactive(scope, this.iterator, key !== null ? key : index); } var frag = this.factory.create(host, scope, this._frag); frag.forId = this.id; this.cacheFrag(value, frag, index, key); return frag; }, /** * Update the v-ref on owner vm. */ updateRef: function updateRef() { var ref = this.descriptor.ref; if (!ref) return; var hash = (this._scope || this.vm).$refs; var refs; if (!this.fromObject) { refs = this.frags.map(findVmFromFrag); } else { refs = {}; this.frags.forEach(function (frag) { refs[frag.scope.$key] = findVmFromFrag(frag); }); } hash[ref] = refs; }, /** * For option lists, update the containing v-model on * parent <select>. */ updateModel: function updateModel() { if (this.isOption) { var parent = this.start.parentNode; var model = parent && parent.__v_model; if (model) { model.forceUpdate(); } } }, /** * Insert a fragment. Handles staggering. * * @param {Fragment} frag * @param {Number} index * @param {Node} prevEl * @param {Boolean} inDocument */ insert: function insert(frag, index, prevEl, inDocument) { if (frag.staggerCb) { frag.staggerCb.cancel(); frag.staggerCb = null; } var staggerAmount = this.getStagger(frag, index, null, 'enter'); if (inDocument && staggerAmount) { // create an anchor and insert it synchronously, // so that we can resolve the correct order without // worrying about some elements not inserted yet var anchor = frag.staggerAnchor; if (!anchor) { anchor = frag.staggerAnchor = createAnchor('stagger-anchor'); anchor.__v_frag = frag; } after(anchor, prevEl); var op = frag.staggerCb = cancellable(function () { frag.staggerCb = null; frag.before(anchor); remove(anchor); }); setTimeout(op, staggerAmount); } else { var target = prevEl.nextSibling; /* istanbul ignore if */ if (!target) { // reset end anchor position in case the position was messed up // by an external drag-n-drop library. after(this.end, prevEl); target = this.end; } frag.before(target); } }, /** * Remove a fragment. Handles staggering. * * @param {Fragment} frag * @param {Number} index * @param {Number} total * @param {Boolean} inDocument */ remove: function remove(frag, index, total, inDocument) { if (frag.staggerCb) { frag.staggerCb.cancel(); frag.staggerCb = null; // it's not possible for the same frag to be removed // twice, so if we have a pending stagger callback, // it means this frag is queued for enter but removed // before its transition started. Since it is already // destroyed, we can just leave it in detached state. return; } var staggerAmount = this.getStagger(frag, index, total, 'leave'); if (inDocument && staggerAmount) { var op = frag.staggerCb = cancellable(function () { frag.staggerCb = null; frag.remove(); }); setTimeout(op, staggerAmount); } else { frag.remove(); } }, /** * Move a fragment to a new position. * Force no transition. * * @param {Fragment} frag * @param {Node} prevEl */ move: function move(frag, prevEl) { // fix a common issue with Sortable: // if prevEl doesn't have nextSibling, this means it's // been dragged after the end anchor. Just re-position // the end anchor to the end of the container. /* istanbul ignore if */ if (!prevEl.nextSibling) { this.end.parentNode.appendChild(this.end); } frag.before(prevEl.nextSibling, false); }, /** * Cache a fragment using track-by or the object key. * * @param {*} value * @param {Fragment} frag * @param {Number} index * @param {String} [key] */ cacheFrag: function cacheFrag(value, frag, index, key) { var trackByKey = this.params.trackBy; var cache = this.cache; var primitive = !isObject(value); var id; if (key || trackByKey || primitive) { id = getTrackByKey(index, key, value, trackByKey); if (!cache[id]) { cache[id] = frag; } else if (trackByKey !== '$index') { 'development' !== 'production' && this.warnDuplicate(value); } } else { id = this.id; if (hasOwn(value, id)) { if (value[id] === null) { value[id] = frag; } else { 'development' !== 'production' && this.warnDuplicate(value); } } else if (Object.isExtensible(value)) { def(value, id, frag); } else if ('development' !== 'production') { warn('Frozen v-for objects cannot be automatically tracked, make sure to ' + 'provide a track-by key.'); } } frag.raw = value; }, /** * Get a cached fragment from the value/index/key * * @param {*} value * @param {Number} index * @param {String} key * @return {Fragment} */ getCachedFrag: function getCachedFrag(value, index, key) { var trackByKey = this.params.trackBy; var primitive = !isObject(value); var frag; if (key || trackByKey || primitive) { var id = getTrackByKey(index, key, value, trackByKey); frag = this.cache[id]; } else { frag = value[this.id]; } if (frag && (frag.reused || frag.fresh)) { 'development' !== 'production' && this.warnDuplicate(value); } return frag; }, /** * Delete a fragment from cache. * * @param {Fragment} frag */ deleteCachedFrag: function deleteCachedFrag(frag) { var value = frag.raw; var trackByKey = this.params.trackBy; var scope = frag.scope; var index = scope.$index; // fix #948: avoid accidentally fall through to // a parent repeater which happens to have $key. var key = hasOwn(scope, '$key') && scope.$key; var primitive = !isObject(value); if (trackByKey || key || primitive) { var id = getTrackByKey(index, key, value, trackByKey); this.cache[id] = null; } else { value[this.id] = null; frag.raw = null; } }, /** * Get the stagger amount for an insertion/removal. * * @param {Fragment} frag * @param {Number} index * @param {Number} total * @param {String} type */ getStagger: function getStagger(frag, index, total, type) { type = type + 'Stagger'; var trans = frag.node.__v_trans; var hooks = trans && trans.hooks; var hook = hooks && (hooks[type] || hooks.stagger); return hook ? hook.call(frag, index, total) : index * parseInt(this.params[type] || this.params.stagger, 10); }, /** * Pre-process the value before piping it through the * filters. This is passed to and called by the watcher. */ _preProcess: function _preProcess(value) { // regardless of type, store the un-filtered raw value. this.rawValue = value; return value; }, /** * Post-process the value after it has been piped through * the filters. This is passed to and called by the watcher. * * It is necessary for this to be called during the * watcher's dependency collection phase because we want * the v-for to update when the source Object is mutated. */ _postProcess: function _postProcess(value) { if (isArray(value)) { return value; } else if (isPlainObject(value)) { // convert plain object to array. var keys = Object.keys(value); var i = keys.length; var res = new Array(i); var key; while (i--) { key = keys[i]; res[i] = { $key: key, $value: value[key] }; } return res; } else { if (typeof value === 'number' && !isNaN(value)) { value = range(value); } return value || []; } }, unbind: function unbind() { if (this.descriptor.ref) { (this._scope || this.vm).$refs[this.descriptor.ref] = null; } if (this.frags) { var i = this.frags.length; var frag; while (i--) { frag = this.frags[i]; this.deleteCachedFrag(frag); frag.destroy(); } } } }; /** * Helper to find the previous element that is a fragment * anchor. This is necessary because a destroyed frag's * element could still be lingering in the DOM before its * leaving transition finishes, but its inserted flag * should have been set to false so we can skip them. * * If this is a block repeat, we want to make sure we only * return frag that is bound to this v-for. (see #929) * * @param {Fragment} frag * @param {Comment|Text} anchor * @param {String} id * @return {Fragment} */ function findPrevFrag(frag, anchor, id) { var el = frag.node.previousSibling; /* istanbul ignore if */ if (!el) return; frag = el.__v_frag; while ((!frag || frag.forId !== id || !frag.inserted) && el !== anchor) { el = el.previousSibling; /* istanbul ignore if */ if (!el) return; frag = el.__v_frag; } return frag; } /** * Find a vm from a fragment. * * @param {Fragment} frag * @return {Vue|undefined} */ function findVmFromFrag(frag) { var node = frag.node; // handle multi-node frag if (frag.end) { while (!node.__vue__ && node !== frag.end && node.nextSibling) { node = node.nextSibling; } } return node.__vue__; } /** * Create a range array from given number. * * @param {Number} n * @return {Array} */ function range(n) { var i = -1; var ret = new Array(Math.floor(n)); while (++i < n) { ret[i] = i; } return ret; } /** * Get the track by key for an item. * * @param {Number} index * @param {String} key * @param {*} value * @param {String} [trackByKey] */ function getTrackByKey(index, key, value, trackByKey) { return trackByKey ? trackByKey === '$index' ? index : trackByKey.charAt(0).match(/\w/) ? getPath(value, trackByKey) : value[trackByKey] : key || value; } if ('development' !== 'production') { vFor.warnDuplicate = function (value) { warn('Duplicate value found in v-for="' + this.descriptor.raw + '": ' + JSON.stringify(value) + '. Use track-by="$index" if ' + 'you are expecting duplicate values.', this.vm); }; } var vIf = { priority: IF, terminal: true, bind: function bind() { var el = this.el; if (!el.__vue__) { // check else block var next = el.nextElementSibling; if (next && getAttr(next, 'v-else') !== null) { remove(next); this.elseEl = next; } // check main block this.anchor = createAnchor('v-if'); replace(el, this.anchor); } else { 'development' !== 'production' && warn('v-if="' + this.expression + '" cannot be ' + 'used on an instance root element.', this.vm); this.invalid = true; } }, update: function update(value) { if (this.invalid) return; if (value) { if (!this.frag) { this.insert(); } } else { this.remove(); } }, insert: function insert() { if (this.elseFrag) { this.elseFrag.remove(); this.elseFrag = null; } // lazy init factory if (!this.factory) { this.factory = new FragmentFactory(this.vm, this.el); } this.frag = this.factory.create(this._host, this._scope, this._frag); this.frag.before(this.anchor); }, remove: function remove() { if (this.frag) { this.frag.remove(); this.frag = null; } if (this.elseEl && !this.elseFrag) { if (!this.elseFactory) { this.elseFactory = new FragmentFactory(this.elseEl._context || this.vm, this.elseEl); } this.elseFrag = this.elseFactory.create(this._host, this._scope, this._frag); this.elseFrag.before(this.anchor); } }, unbind: function unbind() { if (this.frag) { this.frag.destroy(); } if (this.elseFrag) { this.elseFrag.destroy(); } } }; var show = { bind: function bind() { // check else block var next = this.el.nextElementSibling; if (next && getAttr(next, 'v-else') !== null) { this.elseEl = next; } }, update: function update(value) { this.apply(this.el, value); if (this.elseEl) { this.apply(this.elseEl, !value); } }, apply: function apply(el, value) { if (inDoc(el)) { applyTransition(el, value ? 1 : -1, toggle, this.vm); } else { toggle(); } function toggle() { el.style.display = value ? '' : 'none'; } } }; var text$2 = { bind: function bind() { var self = this; var el = this.el; var isRange = el.type === 'range'; var lazy = this.params.lazy; var number = this.params.number; var debounce = this.params.debounce; // handle composition events. // http://blog.evanyou.me/2014/01/03/composition-event/ // skip this for Android because it handles composition // events quite differently. Android doesn't trigger // composition events for language input methods e.g. // Chinese, but instead triggers them for spelling // suggestions... (see Discussion/#162) var composing = false; if (!isAndroid && !isRange) { this.on('compositionstart', function () { composing = true; }); this.on('compositionend', function () { composing = false; // in IE11 the "compositionend" event fires AFTER // the "input" event, so the input handler is blocked // at the end... have to call it here. // // #1327: in lazy mode this is unecessary. if (!lazy) { self.listener(); } }); } // prevent messing with the input when user is typing, // and force update on blur. this.focused = false; if (!isRange && !lazy) { this.on('focus', function () { self.focused = true; }); this.on('blur', function () { self.focused = false; // do not sync value after fragment removal (#2017) if (!self._frag || self._frag.inserted) { self.rawListener(); } }); } // Now attach the main listener this.listener = this.rawListener = function () { if (composing || !self._bound) { return; } var val = number || isRange ? toNumber(el.value) : el.value; self.set(val); // force update on next tick to avoid lock & same value // also only update when user is not typing nextTick(function () { if (self._bound && !self.focused) { self.update(self._watcher.value); } }); }; // apply debounce if (debounce) { this.listener = _debounce(this.listener, debounce); } // Support jQuery events, since jQuery.trigger() doesn't // trigger native events in some cases and some plugins // rely on $.trigger() // // We want to make sure if a listener is attached using // jQuery, it is also removed with jQuery, that's why // we do the check for each directive instance and // store that check result on itself. This also allows // easier test coverage control by unsetting the global // jQuery variable in tests. this.hasjQuery = typeof jQuery === 'function'; if (this.hasjQuery) { var method = jQuery.fn.on ? 'on' : 'bind'; jQuery(el)[method]('change', this.rawListener); if (!lazy) { jQuery(el)[method]('input', this.listener); } } else { this.on('change', this.rawListener); if (!lazy) { this.on('input', this.listener); } } // IE9 doesn't fire input event on backspace/del/cut if (!lazy && isIE9) { this.on('cut', function () { nextTick(self.listener); }); this.on('keyup', function (e) { if (e.keyCode === 46 || e.keyCode === 8) { self.listener(); } }); } // set initial value if present if (el.hasAttribute('value') || el.tagName === 'TEXTAREA' && el.value.trim()) { this.afterBind = this.listener; } }, update: function update(value) { // #3029 only update when the value changes. This prevent // browsers from overwriting values like selectionStart value = _toString(value); if (value !== this.el.value) this.el.value = value; }, unbind: function unbind() { var el = this.el; if (this.hasjQuery) { var method = jQuery.fn.off ? 'off' : 'unbind'; jQuery(el)[method]('change', this.listener); jQuery(el)[method]('input', this.listener); } } }; var radio = { bind: function bind() { var self = this; var el = this.el; this.getValue = function () { // value overwrite via v-bind:value if (el.hasOwnProperty('_value')) { return el._value; } var val = el.value; if (self.params.number) { val = toNumber(val); } return val; }; this.listener = function () { self.set(self.getValue()); }; this.on('change', this.listener); if (el.hasAttribute('checked')) { this.afterBind = this.listener; } }, update: function update(value) { this.el.checked = looseEqual(value, this.getValue()); } }; var select = { bind: function bind() { var _this = this; var self = this; var el = this.el; // method to force update DOM using latest value. this.forceUpdate = function () { if (self._watcher) { self.update(self._watcher.get()); } }; // check if this is a multiple select var multiple = this.multiple = el.hasAttribute('multiple'); // attach listener this.listener = function () { var value = getValue(el, multiple); value = self.params.number ? isArray(value) ? value.map(toNumber) : toNumber(value) : value; self.set(value); }; this.on('change', this.listener); // if has initial value, set afterBind var initValue = getValue(el, multiple, true); if (multiple && initValue.length || !multiple && initValue !== null) { this.afterBind = this.listener; } // All major browsers except Firefox resets // selectedIndex with value -1 to 0 when the element // is appended to a new parent, therefore we have to // force a DOM update whenever that happens... this.vm.$on('hook:attached', function () { nextTick(_this.forceUpdate); }); if (!inDoc(el)) { nextTick(this.forceUpdate); } }, update: function update(value) { var el = this.el; el.selectedIndex = -1; var multi = this.multiple && isArray(value); var options = el.options; var i = options.length; var op, val; while (i--) { op = options[i]; val = op.hasOwnProperty('_value') ? op._value : op.value; /* eslint-disable eqeqeq */ op.selected = multi ? indexOf$1(value, val) > -1 : looseEqual(value, val); /* eslint-enable eqeqeq */ } }, unbind: function unbind() { /* istanbul ignore next */ this.vm.$off('hook:attached', this.forceUpdate); } }; /** * Get select value * * @param {SelectElement} el * @param {Boolean} multi * @param {Boolean} init * @return {Array|*} */ function getValue(el, multi, init) { var res = multi ? [] : null; var op, val, selected; for (var i = 0, l = el.options.length; i < l; i++) { op = el.options[i]; selected = init ? op.hasAttribute('selected') : op.selected; if (selected) { val = op.hasOwnProperty('_value') ? op._value : op.value; if (multi) { res.push(val); } else { return val; } } } return res; } /** * Native Array.indexOf uses strict equal, but in this * case we need to match string/numbers with custom equal. * * @param {Array} arr * @param {*} val */ function indexOf$1(arr, val) { var i = arr.length; while (i--) { if (looseEqual(arr[i], val)) { return i; } } return -1; } var checkbox = { bind: function bind() { var self = this; var el = this.el; this.getValue = function () { return el.hasOwnProperty('_value') ? el._value : self.params.number ? toNumber(el.value) : el.value; }; function getBooleanValue() { var val = el.checked; if (val && el.hasOwnProperty('_trueValue')) { return el._trueValue; } if (!val && el.hasOwnProperty('_falseValue')) { return el._falseValue; } return val; } this.listener = function () { var model = self._watcher.value; if (isArray(model)) { var val = self.getValue(); if (el.checked) { if (indexOf(model, val) < 0) { model.push(val); } } else { model.$remove(val); } } else { self.set(getBooleanValue()); } }; this.on('change', this.listener); if (el.hasAttribute('checked')) { this.afterBind = this.listener; } }, update: function update(value) { var el = this.el; if (isArray(value)) { el.checked = indexOf(value, this.getValue()) > -1; } else { if (el.hasOwnProperty('_trueValue')) { el.checked = looseEqual(value, el._trueValue); } else { el.checked = !!value; } } } }; var handlers = { text: text$2, radio: radio, select: select, checkbox: checkbox }; var model = { priority: MODEL, twoWay: true, handlers: handlers, params: ['lazy', 'number', 'debounce'], /** * Possible elements: * <select> * <textarea> * <input type="*"> * - text * - checkbox * - radio * - number */ bind: function bind() { // friendly warning... this.checkFilters(); if (this.hasRead && !this.hasWrite) { 'development' !== 'production' && warn('It seems you are using a read-only filter with ' + 'v-model="' + this.descriptor.raw + '". ' + 'You might want to use a two-way filter to ensure correct behavior.', this.vm); } var el = this.el; var tag = el.tagName; var handler; if (tag === 'INPUT') { handler = handlers[el.type] || handlers.text; } else if (tag === 'SELECT') { handler = handlers.select; } else if (tag === 'TEXTAREA') { handler = handlers.text; } else { 'development' !== 'production' && warn('v-model does not support element type: ' + tag, this.vm); return; } el.__v_model = this; handler.bind.call(this); this.update = handler.update; this._unbind = handler.unbind; }, /** * Check read/write filter stats. */ checkFilters: function checkFilters() { var filters = this.filters; if (!filters) return; var i = filters.length; while (i--) { var filter = resolveAsset(this.vm.$options, 'filters', filters[i].name); if (typeof filter === 'function' || filter.read) { this.hasRead = true; } if (filter.write) { this.hasWrite = true; } } }, unbind: function unbind() { this.el.__v_model = null; this._unbind && this._unbind(); } }; // keyCode aliases var keyCodes = { esc: 27, tab: 9, enter: 13, space: 32, 'delete': [8, 46], up: 38, left: 37, right: 39, down: 40 }; function keyFilter(handler, keys) { var codes = keys.map(function (key) { var charCode = key.charCodeAt(0); if (charCode > 47 && charCode < 58) { return parseInt(key, 10); } if (key.length === 1) { charCode = key.toUpperCase().charCodeAt(0); if (charCode > 64 && charCode < 91) { return charCode; } } return keyCodes[key]; }); codes = [].concat.apply([], codes); return function keyHandler(e) { if (codes.indexOf(e.keyCode) > -1) { return handler.call(this, e); } }; } function stopFilter(handler) { return function stopHandler(e) { e.stopPropagation(); return handler.call(this, e); }; } function preventFilter(handler) { return function preventHandler(e) { e.preventDefault(); return handler.call(this, e); }; } function selfFilter(handler) { return function selfHandler(e) { if (e.target === e.currentTarget) { return handler.call(this, e); } }; } var on$1 = { priority: ON, acceptStatement: true, keyCodes: keyCodes, bind: function bind() { // deal with iframes if (this.el.tagName === 'IFRAME' && this.arg !== 'load') { var self = this; this.iframeBind = function () { on(self.el.contentWindow, self.arg, self.handler, self.modifiers.capture); }; this.on('load', this.iframeBind); } }, update: function update(handler) { // stub a noop for v-on with no value, // e.g. @mousedown.prevent if (!this.descriptor.raw) { handler = function () {}; } if (typeof handler !== 'function') { 'development' !== 'production' && warn('v-on:' + this.arg + '="' + this.expression + '" expects a function value, ' + 'got ' + handler, this.vm); return; } // apply modifiers if (this.modifiers.stop) { handler = stopFilter(handler); } if (this.modifiers.prevent) { handler = preventFilter(handler); } if (this.modifiers.self) { handler = selfFilter(handler); } // key filter var keys = Object.keys(this.modifiers).filter(function (key) { return key !== 'stop' && key !== 'prevent' && key !== 'self' && key !== 'capture'; }); if (keys.length) { handler = keyFilter(handler, keys); } this.reset(); this.handler = handler; if (this.iframeBind) { this.iframeBind(); } else { on(this.el, this.arg, this.handler, this.modifiers.capture); } }, reset: function reset() { var el = this.iframeBind ? this.el.contentWindow : this.el; if (this.handler) { off(el, this.arg, this.handler); } }, unbind: function unbind() { this.reset(); } }; var prefixes = ['-webkit-', '-moz-', '-ms-']; var camelPrefixes = ['Webkit', 'Moz', 'ms']; var importantRE = /!important;?$/; var propCache = Object.create(null); var testEl = null; var style = { deep: true, update: function update(value) { if (typeof value === 'string') { this.el.style.cssText = value; } else if (isArray(value)) { this.handleObject(value.reduce(extend, {})); } else { this.handleObject(value || {}); } }, handleObject: function handleObject(value) { // cache object styles so that only changed props // are actually updated. var cache = this.cache || (this.cache = {}); var name, val; for (name in cache) { if (!(name in value)) { this.handleSingle(name, null); delete cache[name]; } } for (name in value) { val = value[name]; if (val !== cache[name]) { cache[name] = val; this.handleSingle(name, val); } } }, handleSingle: function handleSingle(prop, value) { prop = normalize(prop); if (!prop) return; // unsupported prop // cast possible numbers/booleans into strings if (value != null) value += ''; if (value) { var isImportant = importantRE.test(value) ? 'important' : ''; if (isImportant) { /* istanbul ignore if */ if ('development' !== 'production') { warn('It\'s probably a bad idea to use !important with inline rules. ' + 'This feature will be deprecated in a future version of Vue.'); } value = value.replace(importantRE, '').trim(); this.el.style.setProperty(prop.kebab, value, isImportant); } else { this.el.style[prop.camel] = value; } } else { this.el.style[prop.camel] = ''; } } }; /** * Normalize a CSS property name. * - cache result * - auto prefix * - camelCase -> dash-case * * @param {String} prop * @return {String} */ function normalize(prop) { if (propCache[prop]) { return propCache[prop]; } var res = prefix(prop); propCache[prop] = propCache[res] = res; return res; } /** * Auto detect the appropriate prefix for a CSS property. * https://gist.github.com/paulirish/523692 * * @param {String} prop * @return {String} */ function prefix(prop) { prop = hyphenate(prop); var camel = camelize(prop); var upper = camel.charAt(0).toUpperCase() + camel.slice(1); if (!testEl) { testEl = document.createElement('div'); } var i = prefixes.length; var prefixed; if (camel !== 'filter' && camel in testEl.style) { return { kebab: prop, camel: camel }; } while (i--) { prefixed = camelPrefixes[i] + upper; if (prefixed in testEl.style) { return { kebab: prefixes[i] + prop, camel: prefixed }; } } } // xlink var xlinkNS = 'http://www.w3.org/1999/xlink'; var xlinkRE = /^xlink:/; // check for attributes that prohibit interpolations var disallowedInterpAttrRE = /^v-|^:|^@|^(?:is|transition|transition-mode|debounce|track-by|stagger|enter-stagger|leave-stagger)$/; // these attributes should also set their corresponding properties // because they only affect the initial state of the element var attrWithPropsRE = /^(?:value|checked|selected|muted)$/; // these attributes expect enumrated values of "true" or "false" // but are not boolean attributes var enumeratedAttrRE = /^(?:draggable|contenteditable|spellcheck)$/; // these attributes should set a hidden property for // binding v-model to object values var modelProps = { value: '_value', 'true-value': '_trueValue', 'false-value': '_falseValue' }; var bind$1 = { priority: BIND, bind: function bind() { var attr = this.arg; var tag = this.el.tagName; // should be deep watch on object mode if (!attr) { this.deep = true; } // handle interpolation bindings var descriptor = this.descriptor; var tokens = descriptor.interp; if (tokens) { // handle interpolations with one-time tokens if (descriptor.hasOneTime) { this.expression = tokensToExp(tokens, this._scope || this.vm); } // only allow binding on native attributes if (disallowedInterpAttrRE.test(attr) || attr === 'name' && (tag === 'PARTIAL' || tag === 'SLOT')) { 'development' !== 'production' && warn(attr + '="' + descriptor.raw + '": ' + 'attribute interpolation is not allowed in Vue.js ' + 'directives and special attributes.', this.vm); this.el.removeAttribute(attr); this.invalid = true; } /* istanbul ignore if */ if ('development' !== 'production') { var raw = attr + '="' + descriptor.raw + '": '; // warn src if (attr === 'src') { warn(raw + 'interpolation in "src" attribute will cause ' + 'a 404 request. Use v-bind:src instead.', this.vm); } // warn style if (attr === 'style') { warn(raw + 'interpolation in "style" attribute will cause ' + 'the attribute to be discarded in Internet Explorer. ' + 'Use v-bind:style instead.', this.vm); } } } }, update: function update(value) { if (this.invalid) { return; } var attr = this.arg; if (this.arg) { this.handleSingle(attr, value); } else { this.handleObject(value || {}); } }, // share object handler with v-bind:class handleObject: style.handleObject, handleSingle: function handleSingle(attr, value) { var el = this.el; var interp = this.descriptor.interp; if (this.modifiers.camel) { attr = camelize(attr); } if (!interp && attrWithPropsRE.test(attr) && attr in el) { var attrValue = attr === 'value' ? value == null // IE9 will set input.value to "null" for null... ? '' : value : value; if (el[attr] !== attrValue) { el[attr] = attrValue; } } // set model props var modelProp = modelProps[attr]; if (!interp && modelProp) { el[modelProp] = value; // update v-model if present var model = el.__v_model; if (model) { model.listener(); } } // do not set value attribute for textarea if (attr === 'value' && el.tagName === 'TEXTAREA') { el.removeAttribute(attr); return; } // update attribute if (enumeratedAttrRE.test(attr)) { el.setAttribute(attr, value ? 'true' : 'false'); } else if (value != null && value !== false) { if (attr === 'class') { // handle edge case #1960: // class interpolation should not overwrite Vue transition class if (el.__v_trans) { value += ' ' + el.__v_trans.id + '-transition'; } setClass(el, value); } else if (xlinkRE.test(attr)) { el.setAttributeNS(xlinkNS, attr, value === true ? '' : value); } else { el.setAttribute(attr, value === true ? '' : value); } } else { el.removeAttribute(attr); } } }; var el = { priority: EL, bind: function bind() { /* istanbul ignore if */ if (!this.arg) { return; } var id = this.id = camelize(this.arg); var refs = (this._scope || this.vm).$els; if (hasOwn(refs, id)) { refs[id] = this.el; } else { defineReactive(refs, id, this.el); } }, unbind: function unbind() { var refs = (this._scope || this.vm).$els; if (refs[this.id] === this.el) { refs[this.id] = null; } } }; var ref = { bind: function bind() { 'development' !== 'production' && warn('v-ref:' + this.arg + ' must be used on a child ' + 'component. Found on <' + this.el.tagName.toLowerCase() + '>.', this.vm); } }; var cloak = { bind: function bind() { var el = this.el; this.vm.$once('pre-hook:compiled', function () { el.removeAttribute('v-cloak'); }); } }; // must export plain object var directives = { text: text$1, html: html, 'for': vFor, 'if': vIf, show: show, model: model, on: on$1, bind: bind$1, el: el, ref: ref, cloak: cloak }; var vClass = { deep: true, update: function update(value) { if (!value) { this.cleanup(); } else if (typeof value === 'string') { this.setClass(value.trim().split(/\s+/)); } else { this.setClass(normalize$1(value)); } }, setClass: function setClass(value) { this.cleanup(value); for (var i = 0, l = value.length; i < l; i++) { var val = value[i]; if (val) { apply(this.el, val, addClass); } } this.prevKeys = value; }, cleanup: function cleanup(value) { var prevKeys = this.prevKeys; if (!prevKeys) return; var i = prevKeys.length; while (i--) { var key = prevKeys[i]; if (!value || value.indexOf(key) < 0) { apply(this.el, key, removeClass); } } } }; /** * Normalize objects and arrays (potentially containing objects) * into array of strings. * * @param {Object|Array<String|Object>} value * @return {Array<String>} */ function normalize$1(value) { var res = []; if (isArray(value)) { for (var i = 0, l = value.length; i < l; i++) { var _key = value[i]; if (_key) { if (typeof _key === 'string') { res.push(_key); } else { for (var k in _key) { if (_key[k]) res.push(k); } } } } } else if (isObject(value)) { for (var key in value) { if (value[key]) res.push(key); } } return res; } /** * Add or remove a class/classes on an element * * @param {Element} el * @param {String} key The class name. This may or may not * contain a space character, in such a * case we'll deal with multiple class * names at once. * @param {Function} fn */ function apply(el, key, fn) { key = key.trim(); if (key.indexOf(' ') === -1) { fn(el, key); return; } // The key contains one or more space characters. // Since a class name doesn't accept such characters, we // treat it as multiple classes. var keys = key.split(/\s+/); for (var i = 0, l = keys.length; i < l; i++) { fn(el, keys[i]); } } var component = { priority: COMPONENT, params: ['keep-alive', 'transition-mode', 'inline-template'], /** * Setup. Two possible usages: * * - static: * <comp> or <div v-component="comp"> * * - dynamic: * <component :is="view"> */ bind: function bind() { if (!this.el.__vue__) { // keep-alive cache this.keepAlive = this.params.keepAlive; if (this.keepAlive) { this.cache = {}; } // check inline-template if (this.params.inlineTemplate) { // extract inline template as a DocumentFragment this.inlineTemplate = extractContent(this.el, true); } // component resolution related state this.pendingComponentCb = this.Component = null; // transition related state this.pendingRemovals = 0; this.pendingRemovalCb = null; // create a ref anchor this.anchor = createAnchor('v-component'); replace(this.el, this.anchor); // remove is attribute. // this is removed during compilation, but because compilation is // cached, when the component is used elsewhere this attribute // will remain at link time. this.el.removeAttribute('is'); this.el.removeAttribute(':is'); // remove ref, same as above if (this.descriptor.ref) { this.el.removeAttribute('v-ref:' + hyphenate(this.descriptor.ref)); } // if static, build right now. if (this.literal) { this.setComponent(this.expression); } } else { 'development' !== 'production' && warn('cannot mount component "' + this.expression + '" ' + 'on already mounted element: ' + this.el); } }, /** * Public update, called by the watcher in the dynamic * literal scenario, e.g. <component :is="view"> */ update: function update(value) { if (!this.literal) { this.setComponent(value); } }, /** * Switch dynamic components. May resolve the component * asynchronously, and perform transition based on * specified transition mode. Accepts a few additional * arguments specifically for vue-router. * * The callback is called when the full transition is * finished. * * @param {String} value * @param {Function} [cb] */ setComponent: function setComponent(value, cb) { this.invalidatePending(); if (!value) { // just remove current this.unbuild(true); this.remove(this.childVM, cb); this.childVM = null; } else { var self = this; this.resolveComponent(value, function () { self.mountComponent(cb); }); } }, /** * Resolve the component constructor to use when creating * the child vm. * * @param {String|Function} value * @param {Function} cb */ resolveComponent: function resolveComponent(value, cb) { var self = this; this.pendingComponentCb = cancellable(function (Component) { self.ComponentName = Component.options.name || (typeof value === 'string' ? value : null); self.Component = Component; cb(); }); this.vm._resolveComponent(value, this.pendingComponentCb); }, /** * Create a new instance using the current constructor and * replace the existing instance. This method doesn't care * whether the new component and the old one are actually * the same. * * @param {Function} [cb] */ mountComponent: function mountComponent(cb) { // actual mount this.unbuild(true); var self = this; var activateHooks = this.Component.options.activate; var cached = this.getCached(); var newComponent = this.build(); if (activateHooks && !cached) { this.waitingFor = newComponent; callActivateHooks(activateHooks, newComponent, function () { if (self.waitingFor !== newComponent) { return; } self.waitingFor = null; self.transition(newComponent, cb); }); } else { // update ref for kept-alive component if (cached) { newComponent._updateRef(); } this.transition(newComponent, cb); } }, /** * When the component changes or unbinds before an async * constructor is resolved, we need to invalidate its * pending callback. */ invalidatePending: function invalidatePending() { if (this.pendingComponentCb) { this.pendingComponentCb.cancel(); this.pendingComponentCb = null; } }, /** * Instantiate/insert a new child vm. * If keep alive and has cached instance, insert that * instance; otherwise build a new one and cache it. * * @param {Object} [extraOptions] * @return {Vue} - the created instance */ build: function build(extraOptions) { var cached = this.getCached(); if (cached) { return cached; } if (this.Component) { // default options var options = { name: this.ComponentName, el: cloneNode(this.el), template: this.inlineTemplate, // make sure to add the child with correct parent // if this is a transcluded component, its parent // should be the transclusion host. parent: this._host || this.vm, // if no inline-template, then the compiled // linker can be cached for better performance. _linkerCachable: !this.inlineTemplate, _ref: this.descriptor.ref, _asComponent: true, _isRouterView: this._isRouterView, // if this is a transcluded component, context // will be the common parent vm of this instance // and its host. _context: this.vm, // 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. _scope: this._scope, // pass in the owner fragment of this component. // this is necessary so that the fragment can keep // track of its contained components in order to // call attach/detach hooks for them. _frag: this._frag }; // extra options // in 1.0.0 this is used by vue-router only /* istanbul ignore if */ if (extraOptions) { extend(options, extraOptions); } var child = new this.Component(options); if (this.keepAlive) { this.cache[this.Component.cid] = child; } /* istanbul ignore if */ if ('development' !== 'production' && this.el.hasAttribute('transition') && child._isFragment) { warn('Transitions will not work on a fragment instance. ' + 'Template: ' + child.$options.template, child); } return child; } }, /** * Try to get a cached instance of the current component. * * @return {Vue|undefined} */ getCached: function getCached() { return this.keepAlive && this.cache[this.Component.cid]; }, /** * Teardown the current child, but defers cleanup so * that we can separate the destroy and removal steps. * * @param {Boolean} defer */ unbuild: function unbuild(defer) { if (this.waitingFor) { if (!this.keepAlive) { this.waitingFor.$destroy(); } this.waitingFor = null; } var child = this.childVM; if (!child || this.keepAlive) { if (child) { // remove ref child._inactive = true; child._updateRef(true); } return; } // the sole purpose of `deferCleanup` is so that we can // "deactivate" the vm right now and perform DOM removal // later. child.$destroy(false, defer); }, /** * Remove current destroyed child and manually do * the cleanup after removal. * * @param {Function} cb */ remove: function remove(child, cb) { var keepAlive = this.keepAlive; if (child) { // we may have a component switch when a previous // component is still being transitioned out. // we want to trigger only one lastest insertion cb // when the existing transition finishes. (#1119) this.pendingRemovals++; this.pendingRemovalCb = cb; var self = this; child.$remove(function () { self.pendingRemovals--; if (!keepAlive) child._cleanup(); if (!self.pendingRemovals && self.pendingRemovalCb) { self.pendingRemovalCb(); self.pendingRemovalCb = null; } }); } else if (cb) { cb(); } }, /** * Actually swap the components, depending on the * transition mode. Defaults to simultaneous. * * @param {Vue} target * @param {Function} [cb] */ transition: function transition(target, cb) { var self = this; var current = this.childVM; // for devtool inspection if (current) current._inactive = true; target._inactive = false; this.childVM = target; switch (self.params.transitionMode) { case 'in-out': target.$before(self.anchor, function () { self.remove(current, cb); }); break; case 'out-in': self.remove(current, function () { target.$before(self.anchor, cb); }); break; default: self.remove(current); target.$before(self.anchor, cb); } }, /** * Unbind. */ unbind: function unbind() { this.invalidatePending(); // Do not defer cleanup when unbinding this.unbuild(); // destroy all keep-alive cached instances if (this.cache) { for (var key in this.cache) { this.cache[key].$destroy(); } this.cache = null; } } }; /** * Call activate hooks in order (asynchronous) * * @param {Array} hooks * @param {Vue} vm * @param {Function} cb */ function callActivateHooks(hooks, vm, cb) { var total = hooks.length; var called = 0; hooks[0].call(vm, next); function next() { if (++called >= total) { cb(); } else { hooks[called].call(vm, next); } } } var propBindingModes = config._propBindingModes; var empty = {}; // regexes var identRE$1 = /^[$_a-zA-Z]+[\w$]*$/; var settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/; /** * Compile props on a root element and return * a props link function. * * @param {Element|DocumentFragment} el * @param {Array} propOptions * @param {Vue} vm * @return {Function} propsLinkFn */ function compileProps(el, propOptions, vm) { var props = []; var names = Object.keys(propOptions); var i = names.length; var options, name, attr, value, path, parsed, prop; while (i--) { name = names[i]; options = propOptions[name] || empty; if ('development' !== 'production' && name === '$data') { warn('Do not use $data as prop.', vm); continue; } // props could contain dashes, which will be // interpreted as minus calculations by the parser // so we need to camelize the path here path = camelize(name); if (!identRE$1.test(path)) { 'development' !== 'production' && warn('Invalid prop key: "' + name + '". Prop keys ' + 'must be valid identifiers.', vm); continue; } prop = { name: name, path: path, options: options, mode: propBindingModes.ONE_WAY, raw: null }; attr = hyphenate(name); // first check dynamic version if ((value = getBindAttr(el, attr)) === null) { if ((value = getBindAttr(el, attr + '.sync')) !== null) { prop.mode = propBindingModes.TWO_WAY; } else if ((value = getBindAttr(el, attr + '.once')) !== null) { prop.mode = propBindingModes.ONE_TIME; } } if (value !== null) { // has dynamic binding! prop.raw = value; parsed = parseDirective(value); value = parsed.expression; prop.filters = parsed.filters; // check binding type if (isLiteral(value) && !parsed.filters) { // for expressions containing literal numbers and // booleans, there's no need to setup a prop binding, // so we can optimize them as a one-time set. prop.optimizedLiteral = true; } else { prop.dynamic = true; // check non-settable path for two-way bindings if ('development' !== 'production' && prop.mode === propBindingModes.TWO_WAY && !settablePathRE.test(value)) { prop.mode = propBindingModes.ONE_WAY; warn('Cannot bind two-way prop with non-settable ' + 'parent path: ' + value, vm); } } prop.parentPath = value; // warn required two-way if ('development' !== 'production' && options.twoWay && prop.mode !== propBindingModes.TWO_WAY) { warn('Prop "' + name + '" expects a two-way binding type.', vm); } } else if ((value = getAttr(el, attr)) !== null) { // has literal binding! prop.raw = value; } else if ('development' !== 'production') { // check possible camelCase prop usage var lowerCaseName = path.toLowerCase(); value = /[A-Z\-]/.test(name) && (el.getAttribute(lowerCaseName) || el.getAttribute(':' + lowerCaseName) || el.getAttribute('v-bind:' + lowerCaseName) || el.getAttribute(':' + lowerCaseName + '.once') || el.getAttribute('v-bind:' + lowerCaseName + '.once') || el.getAttribute(':' + lowerCaseName + '.sync') || el.getAttribute('v-bind:' + lowerCaseName + '.sync')); if (value) { warn('Possible usage error for prop `' + lowerCaseName + '` - ' + 'did you mean `' + attr + '`? HTML is case-insensitive, remember to use ' + 'kebab-case for props in templates.', vm); } else if (options.required) { // warn missing required warn('Missing required prop: ' + name, vm); } } // push prop props.push(prop); } return makePropsLinkFn(props); } /** * Build a function that applies props to a vm. * * @param {Array} props * @return {Function} propsLinkFn */ function makePropsLinkFn(props) { return function propsLinkFn(vm, scope) { // store resolved props info vm._props = {}; var inlineProps = vm.$options.propsData; var i = props.length; var prop, path, options, value, raw; while (i--) { prop = props[i]; raw = prop.raw; path = prop.path; options = prop.options; vm._props[path] = prop; if (inlineProps && hasOwn(inlineProps, path)) { initProp(vm, prop, inlineProps[path]); }if (raw === null) { // initialize absent prop initProp(vm, prop, undefined); } else if (prop.dynamic) { // dynamic prop if (prop.mode === propBindingModes.ONE_TIME) { // one time binding value = (scope || vm._context || vm).$get(prop.parentPath); initProp(vm, prop, value); } else { if (vm._context) { // dynamic binding vm._bindDir({ name: 'prop', def: propDef, prop: prop }, null, null, scope); // el, host, scope } else { // root instance initProp(vm, prop, vm.$get(prop.parentPath)); } } } else if (prop.optimizedLiteral) { // optimized literal, cast it and just set once var stripped = stripQuotes(raw); value = stripped === raw ? toBoolean(toNumber(raw)) : stripped; initProp(vm, prop, value); } else { // string literal, but we need to cater for // Boolean props with no value, or with same // literal value (e.g. disabled="disabled") // see https://github.com/vuejs/vue-loader/issues/182 value = options.type === Boolean && (raw === '' || raw === hyphenate(prop.name)) ? true : raw; initProp(vm, prop, value); } } }; } /** * Process a prop with a rawValue, applying necessary coersions, * default values & assertions and call the given callback with * processed value. * * @param {Vue} vm * @param {Object} prop * @param {*} rawValue * @param {Function} fn */ function processPropValue(vm, prop, rawValue, fn) { var isSimple = prop.dynamic && isSimplePath(prop.parentPath); var value = rawValue; if (value === undefined) { value = getPropDefaultValue(vm, prop); } value = coerceProp(prop, value, vm); var coerced = value !== rawValue; if (!assertProp(prop, value, vm)) { value = undefined; } if (isSimple && !coerced) { withoutConversion(function () { fn(value); }); } else { fn(value); } } /** * Set a prop's initial value on a vm and its data object. * * @param {Vue} vm * @param {Object} prop * @param {*} value */ function initProp(vm, prop, value) { processPropValue(vm, prop, value, function (value) { defineReactive(vm, prop.path, value); }); } /** * Update a prop's value on a vm. * * @param {Vue} vm * @param {Object} prop * @param {*} value */ function updateProp(vm, prop, value) { processPropValue(vm, prop, value, function (value) { vm[prop.path] = value; }); } /** * Get the default value of a prop. * * @param {Vue} vm * @param {Object} prop * @return {*} */ function getPropDefaultValue(vm, prop) { // no default, return undefined var options = prop.options; if (!hasOwn(options, 'default')) { // absent boolean value defaults to false return options.type === Boolean ? false : undefined; } var def = options['default']; // warn against non-factory defaults for Object & Array if (isObject(def)) { 'development' !== 'production' && warn('Invalid default value for prop "' + prop.name + '": ' + 'Props with type Object/Array must use a factory function ' + 'to return the default value.', vm); } // call factory function for non-Function types return typeof def === 'function' && options.type !== Function ? def.call(vm) : def; } /** * Assert whether a prop is valid. * * @param {Object} prop * @param {*} value * @param {Vue} vm */ function assertProp(prop, value, vm) { if (!prop.options.required && ( // non-required prop.raw === null || // abscent value == null) // null or undefined ) { return true; } var options = prop.options; var type = options.type; var valid = !type; var expectedTypes = []; if (type) { if (!isArray(type)) { type = [type]; } for (var i = 0; i < type.length && !valid; i++) { var assertedType = assertType(value, type[i]); expectedTypes.push(assertedType.expectedType); valid = assertedType.valid; } } if (!valid) { if ('development' !== 'production') { warn('Invalid prop: type check failed for prop "' + prop.name + '".' + ' Expected ' + expectedTypes.map(formatType).join(', ') + ', got ' + formatValue(value) + '.', vm); } return false; } var validator = options.validator; if (validator) { if (!validator(value)) { 'development' !== 'production' && warn('Invalid prop: custom validator check failed for prop "' + prop.name + '".', vm); return false; } } return true; } /** * Force parsing value with coerce option. * * @param {*} value * @param {Object} options * @return {*} */ function coerceProp(prop, value, vm) { var coerce = prop.options.coerce; if (!coerce) { return value; } if (typeof coerce === 'function') { return coerce(value); } else { 'development' !== 'production' && warn('Invalid coerce for prop "' + prop.name + '": expected function, got ' + typeof coerce + '.', vm); return value; } } /** * Assert the type of a value * * @param {*} value * @param {Function} type * @return {Object} */ function assertType(value, type) { var valid; var expectedType; if (type === String) { expectedType = 'string'; valid = typeof value === expectedType; } else if (type === Number) { expectedType = 'number'; valid = typeof value === expectedType; } else if (type === Boolean) { expectedType = 'boolean'; valid = typeof value === expectedType; } else if (type === Function) { expectedType = 'function'; valid = typeof value === expectedType; } else if (type === Object) { expectedType = 'object'; valid = isPlainObject(value); } else if (type === Array) { expectedType = 'array'; valid = isArray(value); } else { valid = value instanceof type; } return { valid: valid, expectedType: expectedType }; } /** * Format type for output * * @param {String} type * @return {String} */ function formatType(type) { return type ? type.charAt(0).toUpperCase() + type.slice(1) : 'custom type'; } /** * Format value * * @param {*} value * @return {String} */ function formatValue(val) { return Object.prototype.toString.call(val).slice(8, -1); } var bindingModes = config._propBindingModes; var propDef = { bind: function bind() { var child = this.vm; var parent = child._context; // passed in from compiler directly var prop = this.descriptor.prop; var childKey = prop.path; var parentKey = prop.parentPath; var twoWay = prop.mode === bindingModes.TWO_WAY; var parentWatcher = this.parentWatcher = new Watcher(parent, parentKey, function (val) { updateProp(child, prop, val); }, { twoWay: twoWay, filters: prop.filters, // important: props need to be observed on the // v-for scope if present scope: this._scope }); // set the child initial value. initProp(child, prop, parentWatcher.value); // setup two-way binding if (twoWay) { // important: defer the child watcher creation until // the created hook (after data observation) var self = this; child.$once('pre-hook:created', function () { self.childWatcher = new Watcher(child, childKey, function (val) { parentWatcher.set(val); }, { // ensure sync upward before parent sync down. // this is necessary in cases e.g. the child // mutates a prop array, then replaces it. (#1683) sync: true }); }); } }, unbind: function unbind() { this.parentWatcher.teardown(); if (this.childWatcher) { this.childWatcher.teardown(); } } }; var queue$1 = []; var queued = false; /** * Push a job into the queue. * * @param {Function} job */ function pushJob(job) { queue$1.push(job); if (!queued) { queued = true; nextTick(flush); } } /** * Flush the queue, and do one forced reflow before * triggering transitions. */ function flush() { // Force layout var f = document.documentElement.offsetHeight; for (var i = 0; i < queue$1.length; i++) { queue$1[i](); } queue$1 = []; queued = false; // dummy return, so js linters don't complain about // unused variable f return f; } var TYPE_TRANSITION = 'transition'; var TYPE_ANIMATION = 'animation'; var transDurationProp = transitionProp + 'Duration'; var animDurationProp = animationProp + 'Duration'; /** * If a just-entered element is applied the * leave class while its enter transition hasn't started yet, * and the transitioned property has the same value for both * enter/leave, then the leave transition will be skipped and * the transitionend event never fires. This function ensures * its callback to be called after a transition has started * by waiting for double raf. * * It falls back to setTimeout on devices that support CSS * transitions but not raf (e.g. Android 4.2 browser) - since * these environments are usually slow, we are giving it a * relatively large timeout. */ var raf = inBrowser && window.requestAnimationFrame; var waitForTransitionStart = raf /* istanbul ignore next */ ? function (fn) { raf(function () { raf(fn); }); } : function (fn) { setTimeout(fn, 50); }; /** * A Transition object that encapsulates the state and logic * of the transition. * * @param {Element} el * @param {String} id * @param {Object} hooks * @param {Vue} vm */ function Transition(el, id, hooks, vm) { this.id = id; this.el = el; this.enterClass = hooks && hooks.enterClass || id + '-enter'; this.leaveClass = hooks && hooks.leaveClass || id + '-leave'; this.hooks = hooks; this.vm = vm; // async state this.pendingCssEvent = this.pendingCssCb = this.cancel = this.pendingJsCb = this.op = this.cb = null; this.justEntered = false; this.entered = this.left = false; this.typeCache = {}; // check css transition type this.type = hooks && hooks.type; /* istanbul ignore if */ if ('development' !== 'production') { if (this.type && this.type !== TYPE_TRANSITION && this.type !== TYPE_ANIMATION) { warn('invalid CSS transition type for transition="' + this.id + '": ' + this.type, vm); } } // bind var self = this;['enterNextTick', 'enterDone', 'leaveNextTick', 'leaveDone'].forEach(function (m) { self[m] = bind(self[m], self); }); } var p$1 = Transition.prototype; /** * Start an entering transition. * * 1. enter transition triggered * 2. call beforeEnter hook * 3. add enter class * 4. insert/show element * 5. call enter hook (with possible explicit js callback) * 6. reflow * 7. based on transition type: * - transition: * remove class now, wait for transitionend, * then done if there's no explicit js callback. * - animation: * wait for animationend, remove class, * then done if there's no explicit js callback. * - no css transition: * done now if there's no explicit js callback. * 8. wait for either done or js callback, then call * afterEnter hook. * * @param {Function} op - insert/show the element * @param {Function} [cb] */ p$1.enter = function (op, cb) { this.cancelPending(); this.callHook('beforeEnter'); this.cb = cb; addClass(this.el, this.enterClass); op(); this.entered = false; this.callHookWithCb('enter'); if (this.entered) { return; // user called done synchronously. } this.cancel = this.hooks && this.hooks.enterCancelled; pushJob(this.enterNextTick); }; /** * The "nextTick" phase of an entering transition, which is * to be pushed into a queue and executed after a reflow so * that removing the class can trigger a CSS transition. */ p$1.enterNextTick = function () { var _this = this; // prevent transition skipping this.justEntered = true; waitForTransitionStart(function () { _this.justEntered = false; }); var enterDone = this.enterDone; var type = this.getCssTransitionType(this.enterClass); if (!this.pendingJsCb) { if (type === TYPE_TRANSITION) { // trigger transition by removing enter class now removeClass(this.el, this.enterClass); this.setupCssCb(transitionEndEvent, enterDone); } else if (type === TYPE_ANIMATION) { this.setupCssCb(animationEndEvent, enterDone); } else { enterDone(); } } else if (type === TYPE_TRANSITION) { removeClass(this.el, this.enterClass); } }; /** * The "cleanup" phase of an entering transition. */ p$1.enterDone = function () { this.entered = true; this.cancel = this.pendingJsCb = null; removeClass(this.el, this.enterClass); this.callHook('afterEnter'); if (this.cb) this.cb(); }; /** * Start a leaving transition. * * 1. leave transition triggered. * 2. call beforeLeave hook * 3. add leave class (trigger css transition) * 4. call leave hook (with possible explicit js callback) * 5. reflow if no explicit js callback is provided * 6. based on transition type: * - transition or animation: * wait for end event, remove class, then done if * there's no explicit js callback. * - no css transition: * done if there's no explicit js callback. * 7. wait for either done or js callback, then call * afterLeave hook. * * @param {Function} op - remove/hide the element * @param {Function} [cb] */ p$1.leave = function (op, cb) { this.cancelPending(); this.callHook('beforeLeave'); this.op = op; this.cb = cb; addClass(this.el, this.leaveClass); this.left = false; this.callHookWithCb('leave'); if (this.left) { return; // user called done synchronously. } this.cancel = this.hooks && this.hooks.leaveCancelled; // only need to handle leaveDone if // 1. the transition is already done (synchronously called // by the user, which causes this.op set to null) // 2. there's no explicit js callback if (this.op && !this.pendingJsCb) { // if a CSS transition leaves immediately after enter, // the transitionend event never fires. therefore we // detect such cases and end the leave immediately. if (this.justEntered) { this.leaveDone(); } else { pushJob(this.leaveNextTick); } } }; /** * The "nextTick" phase of a leaving transition. */ p$1.leaveNextTick = function () { var type = this.getCssTransitionType(this.leaveClass); if (type) { var event = type === TYPE_TRANSITION ? transitionEndEvent : animationEndEvent; this.setupCssCb(event, this.leaveDone); } else { this.leaveDone(); } }; /** * The "cleanup" phase of a leaving transition. */ p$1.leaveDone = function () { this.left = true; this.cancel = this.pendingJsCb = null; this.op(); removeClass(this.el, this.leaveClass); this.callHook('afterLeave'); if (this.cb) this.cb(); this.op = null; }; /** * Cancel any pending callbacks from a previously running * but not finished transition. */ p$1.cancelPending = function () { this.op = this.cb = null; var hasPending = false; if (this.pendingCssCb) { hasPending = true; off(this.el, this.pendingCssEvent, this.pendingCssCb); this.pendingCssEvent = this.pendingCssCb = null; } if (this.pendingJsCb) { hasPending = true; this.pendingJsCb.cancel(); this.pendingJsCb = null; } if (hasPending) { removeClass(this.el, this.enterClass); removeClass(this.el, this.leaveClass); } if (this.cancel) { this.cancel.call(this.vm, this.el); this.cancel = null; } }; /** * Call a user-provided synchronous hook function. * * @param {String} type */ p$1.callHook = function (type) { if (this.hooks && this.hooks[type]) { this.hooks[type].call(this.vm, this.el); } }; /** * Call a user-provided, potentially-async hook function. * We check for the length of arguments to see if the hook * expects a `done` callback. If true, the transition's end * will be determined by when the user calls that callback; * otherwise, the end is determined by the CSS transition or * animation. * * @param {String} type */ p$1.callHookWithCb = function (type) { var hook = this.hooks && this.hooks[type]; if (hook) { if (hook.length > 1) { this.pendingJsCb = cancellable(this[type + 'Done']); } hook.call(this.vm, this.el, this.pendingJsCb); } }; /** * Get an element's transition type based on the * calculated styles. * * @param {String} className * @return {Number} */ p$1.getCssTransitionType = function (className) { /* istanbul ignore if */ if (!transitionEndEvent || // skip CSS transitions if page is not visible - // this solves the issue of transitionend events not // firing until the page is visible again. // pageVisibility API is supported in IE10+, same as // CSS transitions. document.hidden || // explicit js-only transition this.hooks && this.hooks.css === false || // element is hidden isHidden(this.el)) { return; } var type = this.type || this.typeCache[className]; if (type) return type; var inlineStyles = this.el.style; var computedStyles = window.getComputedStyle(this.el); var transDuration = inlineStyles[transDurationProp] || computedStyles[transDurationProp]; if (transDuration && transDuration !== '0s') { type = TYPE_TRANSITION; } else { var animDuration = inlineStyles[animDurationProp] || computedStyles[animDurationProp]; if (animDuration && animDuration !== '0s') { type = TYPE_ANIMATION; } } if (type) { this.typeCache[className] = type; } return type; }; /** * Setup a CSS transitionend/animationend callback. * * @param {String} event * @param {Function} cb */ p$1.setupCssCb = function (event, cb) { this.pendingCssEvent = event; var self = this; var el = this.el; var onEnd = this.pendingCssCb = function (e) { if (e.target === el) { off(el, event, onEnd); self.pendingCssEvent = self.pendingCssCb = null; if (!self.pendingJsCb && cb) { cb(); } } }; on(el, event, onEnd); }; /** * Check if an element is hidden - in that case we can just * skip the transition alltogether. * * @param {Element} el * @return {Boolean} */ function isHidden(el) { if (/svg$/.test(el.namespaceURI)) { // SVG elements do not have offset(Width|Height) // so we need to check the client rect var rect = el.getBoundingClientRect(); return !(rect.width || rect.height); } else { return !(el.offsetWidth || el.offsetHeight || el.getClientRects().length); } } var transition$1 = { priority: TRANSITION, update: function update(id, oldId) { var el = this.el; // resolve on owner vm var hooks = resolveAsset(this.vm.$options, 'transitions', id); id = id || 'v'; oldId = oldId || 'v'; el.__v_trans = new Transition(el, id, hooks, this.vm); removeClass(el, oldId + '-transition'); addClass(el, id + '-transition'); } }; var internalDirectives = { style: style, 'class': vClass, component: component, prop: propDef, transition: transition$1 }; // special binding prefixes var bindRE = /^v-bind:|^:/; var onRE = /^v-on:|^@/; var dirAttrRE = /^v-([^:]+)(?:$|:(.*)$)/; var modifierRE = /\.[^\.]+/g; var transitionRE = /^(v-bind:|:)?transition$/; // default directive priority var DEFAULT_PRIORITY = 1000; var DEFAULT_TERMINAL_PRIORITY = 2000; /** * Compile a template and return a reusable composite link * function, which recursively contains more link functions * inside. This top level compile function would normally * be called on instance root nodes, but can also be used * for partial compilation if the partial argument is true. * * The returned composite link function, when called, will * return an unlink function that tearsdown all directives * created during the linking phase. * * @param {Element|DocumentFragment} el * @param {Object} options * @param {Boolean} partial * @return {Function} */ function compile(el, options, partial) { // link function for the node itself. var nodeLinkFn = partial || !options._asComponent ? compileNode(el, options) : null; // link function for the childNodes var childLinkFn = !(nodeLinkFn && nodeLinkFn.terminal) && !isScript(el) && el.hasChildNodes() ? compileNodeList(el.childNodes, options) : null; /** * A composite linker function to be called on a already * compiled piece of DOM, which instantiates all directive * instances. * * @param {Vue} vm * @param {Element|DocumentFragment} el * @param {Vue} [host] - host vm of transcluded content * @param {Object} [scope] - v-for scope * @param {Fragment} [frag] - link context fragment * @return {Function|undefined} */ return function compositeLinkFn(vm, el, host, scope, frag) { // cache childNodes before linking parent, fix #657 var childNodes = toArray(el.childNodes); // link var dirs = linkAndCapture(function compositeLinkCapturer() { if (nodeLinkFn) nodeLinkFn(vm, el, host, scope, frag); if (childLinkFn) childLinkFn(vm, childNodes, host, scope, frag); }, vm); return makeUnlinkFn(vm, dirs); }; } /** * Apply a linker to a vm/element pair and capture the * directives created during the process. * * @param {Function} linker * @param {Vue} vm */ function linkAndCapture(linker, vm) { /* istanbul ignore if */ if ('development' === 'production') {} var originalDirCount = vm._directives.length; linker(); var dirs = vm._directives.slice(originalDirCount); dirs.sort(directiveComparator); for (var i = 0, l = dirs.length; i < l; i++) { dirs[i]._bind(); } return dirs; } /** * Directive priority sort comparator * * @param {Object} a * @param {Object} b */ function directiveComparator(a, b) { a = a.descriptor.def.priority || DEFAULT_PRIORITY; b = b.descriptor.def.priority || DEFAULT_PRIORITY; return a > b ? -1 : a === b ? 0 : 1; } /** * Linker functions return an unlink function that * tearsdown all directives instances generated during * the process. * * We create unlink functions with only the necessary * information to avoid retaining additional closures. * * @param {Vue} vm * @param {Array} dirs * @param {Vue} [context] * @param {Array} [contextDirs] * @return {Function} */ function makeUnlinkFn(vm, dirs, context, contextDirs) { function unlink(destroying) { teardownDirs(vm, dirs, destroying); if (context && contextDirs) { teardownDirs(context, contextDirs); } } // expose linked directives unlink.dirs = dirs; return unlink; } /** * Teardown partial linked directives. * * @param {Vue} vm * @param {Array} dirs * @param {Boolean} destroying */ function teardownDirs(vm, dirs, destroying) { var i = dirs.length; while (i--) { dirs[i]._teardown(); if ('development' !== 'production' && !destroying) { vm._directives.$remove(dirs[i]); } } } /** * Compile link props on an instance. * * @param {Vue} vm * @param {Element} el * @param {Object} props * @param {Object} [scope] * @return {Function} */ function compileAndLinkProps(vm, el, props, scope) { var propsLinkFn = compileProps(el, props, vm); var propDirs = linkAndCapture(function () { propsLinkFn(vm, scope); }, vm); return makeUnlinkFn(vm, propDirs); } /** * Compile the root element of an instance. * * 1. attrs on context container (context scope) * 2. attrs on the component template root node, if * replace:true (child scope) * * If this is a fragment instance, we only need to compile 1. * * @param {Element} el * @param {Object} options * @param {Object} contextOptions * @return {Function} */ function compileRoot(el, options, contextOptions) { var containerAttrs = options._containerAttrs; var replacerAttrs = options._replacerAttrs; var contextLinkFn, replacerLinkFn; // only need to compile other attributes for // non-fragment instances if (el.nodeType !== 11) { // for components, container and replacer need to be // compiled separately and linked in different scopes. if (options._asComponent) { // 2. container attributes if (containerAttrs && contextOptions) { contextLinkFn = compileDirectives(containerAttrs, contextOptions); } if (replacerAttrs) { // 3. replacer attributes replacerLinkFn = compileDirectives(replacerAttrs, options); } } else { // non-component, just compile as a normal element. replacerLinkFn = compileDirectives(el.attributes, options); } } else if ('development' !== 'production' && containerAttrs) { // warn container directives for fragment instances var names = containerAttrs.filter(function (attr) { // allow vue-loader/vueify scoped css attributes return attr.name.indexOf('_v-') < 0 && // allow event listeners !onRE.test(attr.name) && // allow slots attr.name !== 'slot'; }).map(function (attr) { return '"' + attr.name + '"'; }); if (names.length) { var plural = names.length > 1; warn('Attribute' + (plural ? 's ' : ' ') + names.join(', ') + (plural ? ' are' : ' is') + ' ignored on component ' + '<' + options.el.tagName.toLowerCase() + '> because ' + 'the component is a fragment instance: ' + 'http://vuejs.org/guide/components.html#Fragment-Instance'); } } options._containerAttrs = options._replacerAttrs = null; return function rootLinkFn(vm, el, scope) { // link context scope dirs var context = vm._context; var contextDirs; if (context && contextLinkFn) { contextDirs = linkAndCapture(function () { contextLinkFn(context, el, null, scope); }, context); } // link self var selfDirs = linkAndCapture(function () { if (replacerLinkFn) replacerLinkFn(vm, el); }, vm); // return the unlink function that tearsdown context // container directives. return makeUnlinkFn(vm, selfDirs, context, contextDirs); }; } /** * Compile a node and return a nodeLinkFn based on the * node type. * * @param {Node} node * @param {Object} options * @return {Function|null} */ function compileNode(node, options) { var type = node.nodeType; if (type === 1 && !isScript(node)) { return compileElement(node, options); } else if (type === 3 && node.data.trim()) { return compileTextNode(node, options); } else { return null; } } /** * Compile an element and return a nodeLinkFn. * * @param {Element} el * @param {Object} options * @return {Function|null} */ function compileElement(el, options) { // preprocess textareas. // textarea treats its text content as the initial value. // just bind it as an attr directive for value. if (el.tagName === 'TEXTAREA') { var tokens = parseText(el.value); if (tokens) { el.setAttribute(':value', tokensToExp(tokens)); el.value = ''; } } var linkFn; var hasAttrs = el.hasAttributes(); var attrs = hasAttrs && toArray(el.attributes); // check terminal directives (for & if) if (hasAttrs) { linkFn = checkTerminalDirectives(el, attrs, options); } // check element directives if (!linkFn) { linkFn = checkElementDirectives(el, options); } // check component if (!linkFn) { linkFn = checkComponent(el, options); } // normal directives if (!linkFn && hasAttrs) { linkFn = compileDirectives(attrs, options); } return linkFn; } /** * Compile a textNode and return a nodeLinkFn. * * @param {TextNode} node * @param {Object} options * @return {Function|null} textNodeLinkFn */ function compileTextNode(node, options) { // skip marked text nodes if (node._skip) { return removeText; } var tokens = parseText(node.wholeText); if (!tokens) { return null; } // mark adjacent text nodes as skipped, // because we are using node.wholeText to compile // all adjacent text nodes together. This fixes // issues in IE where sometimes it splits up a single // text node into multiple ones. var next = node.nextSibling; while (next && next.nodeType === 3) { next._skip = true; next = next.nextSibling; } var frag = document.createDocumentFragment(); var el, token; for (var i = 0, l = tokens.length; i < l; i++) { token = tokens[i]; el = token.tag ? processTextToken(token, options) : document.createTextNode(token.value); frag.appendChild(el); } return makeTextNodeLinkFn(tokens, frag, options); } /** * Linker for an skipped text node. * * @param {Vue} vm * @param {Text} node */ function removeText(vm, node) { remove(node); } /** * Process a single text token. * * @param {Object} token * @param {Object} options * @return {Node} */ function processTextToken(token, options) { var el; if (token.oneTime) { el = document.createTextNode(token.value); } else { if (token.html) { el = document.createComment('v-html'); setTokenType('html'); } else { // IE will clean up empty textNodes during // frag.cloneNode(true), so we have to give it // something here... el = document.createTextNode(' '); setTokenType('text'); } } function setTokenType(type) { if (token.descriptor) return; var parsed = parseDirective(token.value); token.descriptor = { name: type, def: directives[type], expression: parsed.expression, filters: parsed.filters }; } return el; } /** * Build a function that processes a textNode. * * @param {Array<Object>} tokens * @param {DocumentFragment} frag */ function makeTextNodeLinkFn(tokens, frag) { return function textNodeLinkFn(vm, el, host, scope) { var fragClone = frag.cloneNode(true); var childNodes = toArray(fragClone.childNodes); var token, value, node; for (var i = 0, l = tokens.length; i < l; i++) { token = tokens[i]; value = token.value; if (token.tag) { node = childNodes[i]; if (token.oneTime) { value = (scope || vm).$eval(value); if (token.html) { replace(node, parseTemplate(value, true)); } else { node.data = _toString(value); } } else { vm._bindDir(token.descriptor, node, host, scope); } } } replace(el, fragClone); }; } /** * Compile a node list and return a childLinkFn. * * @param {NodeList} nodeList * @param {Object} options * @return {Function|undefined} */ function compileNodeList(nodeList, options) { var linkFns = []; var nodeLinkFn, childLinkFn, node; for (var i = 0, l = nodeList.length; i < l; i++) { node = nodeList[i]; nodeLinkFn = compileNode(node, options); childLinkFn = !(nodeLinkFn && nodeLinkFn.terminal) && node.tagName !== 'SCRIPT' && node.hasChildNodes() ? compileNodeList(node.childNodes, options) : null; linkFns.push(nodeLinkFn, childLinkFn); } return linkFns.length ? makeChildLinkFn(linkFns) : null; } /** * Make a child link function for a node's childNodes. * * @param {Array<Function>} linkFns * @return {Function} childLinkFn */ function makeChildLinkFn(linkFns) { return function childLinkFn(vm, nodes, host, scope, frag) { var node, nodeLinkFn, childrenLinkFn; for (var i = 0, n = 0, l = linkFns.length; i < l; n++) { node = nodes[n]; nodeLinkFn = linkFns[i++]; childrenLinkFn = linkFns[i++]; // cache childNodes before linking parent, fix #657 var childNodes = toArray(node.childNodes); if (nodeLinkFn) { nodeLinkFn(vm, node, host, scope, frag); } if (childrenLinkFn) { childrenLinkFn(vm, childNodes, host, scope, frag); } } }; } /** * Check for element directives (custom elements that should * be resovled as terminal directives). * * @param {Element} el * @param {Object} options */ function checkElementDirectives(el, options) { var tag = el.tagName.toLowerCase(); if (commonTagRE.test(tag)) { return; } var def = resolveAsset(options, 'elementDirectives', tag); if (def) { return makeTerminalNodeLinkFn(el, tag, '', options, def); } } /** * Check if an element is a component. If yes, return * a component link function. * * @param {Element} el * @param {Object} options * @return {Function|undefined} */ function checkComponent(el, options) { var component = checkComponentAttr(el, options); if (component) { var ref = findRef(el); var descriptor = { name: 'component', ref: ref, expression: component.id, def: internalDirectives.component, modifiers: { literal: !component.dynamic } }; var componentLinkFn = function componentLinkFn(vm, el, host, scope, frag) { if (ref) { defineReactive((scope || vm).$refs, ref, null); } vm._bindDir(descriptor, el, host, scope, frag); }; componentLinkFn.terminal = true; return componentLinkFn; } } /** * Check an element for terminal directives in fixed order. * If it finds one, return a terminal link function. * * @param {Element} el * @param {Array} attrs * @param {Object} options * @return {Function} terminalLinkFn */ function checkTerminalDirectives(el, attrs, options) { // skip v-pre if (getAttr(el, 'v-pre') !== null) { return skip; } // skip v-else block, but only if following v-if if (el.hasAttribute('v-else')) { var prev = el.previousElementSibling; if (prev && prev.hasAttribute('v-if')) { return skip; } } var attr, name, value, modifiers, matched, dirName, rawName, arg, def, termDef; for (var i = 0, j = attrs.length; i < j; i++) { attr = attrs[i]; name = attr.name.replace(modifierRE, ''); if (matched = name.match(dirAttrRE)) { def = resolveAsset(options, 'directives', matched[1]); if (def && def.terminal) { if (!termDef || (def.priority || DEFAULT_TERMINAL_PRIORITY) > termDef.priority) { termDef = def; rawName = attr.name; modifiers = parseModifiers(attr.name); value = attr.value; dirName = matched[1]; arg = matched[2]; } } } } if (termDef) { return makeTerminalNodeLinkFn(el, dirName, value, options, termDef, rawName, arg, modifiers); } } function skip() {} skip.terminal = true; /** * Build a node link function for a terminal directive. * A terminal link function terminates the current * compilation recursion and handles compilation of the * subtree in the directive. * * @param {Element} el * @param {String} dirName * @param {String} value * @param {Object} options * @param {Object} def * @param {String} [rawName] * @param {String} [arg] * @param {Object} [modifiers] * @return {Function} terminalLinkFn */ function makeTerminalNodeLinkFn(el, dirName, value, options, def, rawName, arg, modifiers) { var parsed = parseDirective(value); var descriptor = { name: dirName, arg: arg, expression: parsed.expression, filters: parsed.filters, raw: value, attr: rawName, modifiers: modifiers, def: def }; // check ref for v-for and router-view if (dirName === 'for' || dirName === 'router-view') { descriptor.ref = findRef(el); } var fn = function terminalNodeLinkFn(vm, el, host, scope, frag) { if (descriptor.ref) { defineReactive((scope || vm).$refs, descriptor.ref, null); } vm._bindDir(descriptor, el, host, scope, frag); }; fn.terminal = true; return fn; } /** * Compile the directives on an element and return a linker. * * @param {Array|NamedNodeMap} attrs * @param {Object} options * @return {Function} */ function compileDirectives(attrs, options) { var i = attrs.length; var dirs = []; var attr, name, value, rawName, rawValue, dirName, arg, modifiers, dirDef, tokens, matched; while (i--) { attr = attrs[i]; name = rawName = attr.name; value = rawValue = attr.value; tokens = parseText(value); // reset arg arg = null; // check modifiers modifiers = parseModifiers(name); name = name.replace(modifierRE, ''); // attribute interpolations if (tokens) { value = tokensToExp(tokens); arg = name; pushDir('bind', directives.bind, tokens); // warn against mixing mustaches with v-bind if ('development' !== 'production') { if (name === 'class' && Array.prototype.some.call(attrs, function (attr) { return attr.name === ':class' || attr.name === 'v-bind:class'; })) { warn('class="' + rawValue + '": Do not mix mustache interpolation ' + 'and v-bind for "class" on the same element. Use one or the other.', options); } } } else // special attribute: transition if (transitionRE.test(name)) { modifiers.literal = !bindRE.test(name); pushDir('transition', internalDirectives.transition); } else // event handlers if (onRE.test(name)) { arg = name.replace(onRE, ''); pushDir('on', directives.on); } else // attribute bindings if (bindRE.test(name)) { dirName = name.replace(bindRE, ''); if (dirName === 'style' || dirName === 'class') { pushDir(dirName, internalDirectives[dirName]); } else { arg = dirName; pushDir('bind', directives.bind); } } else // normal directives if (matched = name.match(dirAttrRE)) { dirName = matched[1]; arg = matched[2]; // skip v-else (when used with v-show) if (dirName === 'else') { continue; } dirDef = resolveAsset(options, 'directives', dirName, true); if (dirDef) { pushDir(dirName, dirDef); } } } /** * Push a directive. * * @param {String} dirName * @param {Object|Function} def * @param {Array} [interpTokens] */ function pushDir(dirName, def, interpTokens) { var hasOneTimeToken = interpTokens && hasOneTime(interpTokens); var parsed = !hasOneTimeToken && parseDirective(value); dirs.push({ name: dirName, attr: rawName, raw: rawValue, def: def, arg: arg, modifiers: modifiers, // conversion from interpolation strings with one-time token // to expression is differed until directive bind time so that we // have access to the actual vm context for one-time bindings. expression: parsed && parsed.expression, filters: parsed && parsed.filters, interp: interpTokens, hasOneTime: hasOneTimeToken }); } if (dirs.length) { return makeNodeLinkFn(dirs); } } /** * Parse modifiers from directive attribute name. * * @param {String} name * @return {Object} */ function parseModifiers(name) { var res = Object.create(null); var match = name.match(modifierRE); if (match) { var i = match.length; while (i--) { res[match[i].slice(1)] = true; } } return res; } /** * Build a link function for all directives on a single node. * * @param {Array} directives * @return {Function} directivesLinkFn */ function makeNodeLinkFn(directives) { return function nodeLinkFn(vm, el, host, scope, frag) { // reverse apply because it's sorted low to high var i = directives.length; while (i--) { vm._bindDir(directives[i], el, host, scope, frag); } }; } /** * Check if an interpolation string contains one-time tokens. * * @param {Array} tokens * @return {Boolean} */ function hasOneTime(tokens) { var i = tokens.length; while (i--) { if (tokens[i].oneTime) return true; } } function isScript(el) { return el.tagName === 'SCRIPT' && (!el.hasAttribute('type') || el.getAttribute('type') === 'text/javascript'); } var specialCharRE = /[^\w\-:\.]/; /** * Process an element or a DocumentFragment based on a * instance option object. This allows us to transclude * a template node/fragment before the instance is created, * so the processed fragment can then be cloned and reused * in v-for. * * @param {Element} el * @param {Object} options * @return {Element|DocumentFragment} */ function transclude(el, options) { // extract container attributes to pass them down // to compiler, because they need to be compiled in // parent scope. we are mutating the options object here // assuming the same object will be used for compile // right after this. if (options) { options._containerAttrs = extractAttrs(el); } // for template tags, what we want is its content as // a documentFragment (for fragment instances) if (isTemplate(el)) { el = parseTemplate(el); } if (options) { if (options._asComponent && !options.template) { options.template = '<slot></slot>'; } if (options.template) { options._content = extractContent(el); el = transcludeTemplate(el, options); } } if (isFragment(el)) { // anchors for fragment instance // passing in `persist: true` to avoid them being // discarded by IE during template cloning prepend(createAnchor('v-start', true), el); el.appendChild(createAnchor('v-end', true)); } return el; } /** * Process the template option. * If the replace option is true this will swap the $el. * * @param {Element} el * @param {Object} options * @return {Element|DocumentFragment} */ function transcludeTemplate(el, options) { var template = options.template; var frag = parseTemplate(template, true); if (frag) { var replacer = frag.firstChild; var tag = replacer.tagName && replacer.tagName.toLowerCase(); if (options.replace) { /* istanbul ignore if */ if (el === document.body) { 'development' !== 'production' && warn('You are mounting an instance with a template to ' + '<body>. This will replace <body> entirely. You ' + 'should probably use `replace: false` here.'); } // there are many cases where the instance must // become a fragment instance: basically anything that // can create more than 1 root nodes. if ( // multi-children template frag.childNodes.length > 1 || // non-element template replacer.nodeType !== 1 || // single nested component tag === 'component' || resolveAsset(options, 'components', tag) || hasBindAttr(replacer, 'is') || // element directive resolveAsset(options, 'elementDirectives', tag) || // for block replacer.hasAttribute('v-for') || // if block replacer.hasAttribute('v-if')) { return frag; } else { options._replacerAttrs = extractAttrs(replacer); mergeAttrs(el, replacer); return replacer; } } else { el.appendChild(frag); return el; } } else { 'development' !== 'production' && warn('Invalid template option: ' + template); } } /** * Helper to extract a component container's attributes * into a plain object array. * * @param {Element} el * @return {Array} */ function extractAttrs(el) { if (el.nodeType === 1 && el.hasAttributes()) { return toArray(el.attributes); } } /** * Merge the attributes of two elements, and make sure * the class names are merged properly. * * @param {Element} from * @param {Element} to */ function mergeAttrs(from, to) { var attrs = from.attributes; var i = attrs.length; var name, value; while (i--) { name = attrs[i].name; value = attrs[i].value; if (!to.hasAttribute(name) && !specialCharRE.test(name)) { to.setAttribute(name, value); } else if (name === 'class' && !parseText(value) && (value = value.trim())) { value.split(/\s+/).forEach(function (cls) { addClass(to, cls); }); } } } /** * Scan and determine slot content distribution. * We do this during transclusion instead at compile time so that * the distribution is decoupled from the compilation order of * the slots. * * @param {Element|DocumentFragment} template * @param {Element} content * @param {Vue} vm */ function resolveSlots(vm, content) { if (!content) { return; } var contents = vm._slotContents = Object.create(null); var el, name; for (var i = 0, l = content.children.length; i < l; i++) { el = content.children[i]; /* eslint-disable no-cond-assign */ if (name = el.getAttribute('slot')) { (contents[name] || (contents[name] = [])).push(el); } /* eslint-enable no-cond-assign */ if ('development' !== 'production' && getBindAttr(el, 'slot')) { warn('The "slot" attribute must be static.', vm.$parent); } } for (name in contents) { contents[name] = extractFragment(contents[name], content); } if (content.hasChildNodes()) { var nodes = content.childNodes; if (nodes.length === 1 && nodes[0].nodeType === 3 && !nodes[0].data.trim()) { return; } contents['default'] = extractFragment(content.childNodes, content); } } /** * Extract qualified content nodes from a node list. * * @param {NodeList} nodes * @return {DocumentFragment} */ function extractFragment(nodes, parent) { var frag = document.createDocumentFragment(); nodes = toArray(nodes); for (var i = 0, l = nodes.length; i < l; i++) { var node = nodes[i]; if (isTemplate(node) && !node.hasAttribute('v-if') && !node.hasAttribute('v-for')) { parent.removeChild(node); node = parseTemplate(node, true); } frag.appendChild(node); } return frag; } var compiler = Object.freeze({ compile: compile, compileAndLinkProps: compileAndLinkProps, compileRoot: compileRoot, transclude: transclude, resolveSlots: resolveSlots }); function stateMixin (Vue) { /** * Accessor for `$data` property, since setting $data * requires observing the new object and updating * proxied properties. */ Object.defineProperty(Vue.prototype, '$data', { get: function get() { return this._data; }, set: function set(newData) { if (newData !== this._data) { this._setData(newData); } } }); /** * Setup the scope of an instance, which contains: * - observed data * - computed properties * - user methods * - meta properties */ Vue.prototype._initState = function () { this._initProps(); this._initMeta(); this._initMethods(); this._initData(); this._initComputed(); }; /** * Initialize props. */ Vue.prototype._initProps = function () { var options = this.$options; var el = options.el; var props = options.props; if (props && !el) { 'development' !== 'production' && warn('Props will not be compiled if no `el` option is ' + 'provided at instantiation.', this); } // make sure to convert string selectors into element now el = options.el = query(el); this._propsUnlinkFn = el && el.nodeType === 1 && props // props must be linked in proper scope if inside v-for ? compileAndLinkProps(this, el, props, this._scope) : null; }; /** * Initialize the data. */ Vue.prototype._initData = function () { var dataFn = this.$options.data; var data = this._data = dataFn ? dataFn() : {}; if (!isPlainObject(data)) { data = {}; 'development' !== 'production' && warn('data functions should return an object.', this); } var props = this._props; // proxy data on instance var keys = Object.keys(data); var i, key; i = keys.length; while (i--) { key = keys[i]; // there are two scenarios where we can proxy a data key: // 1. it's not already defined as a prop // 2. it's provided via a instantiation option AND there are no // template prop present if (!props || !hasOwn(props, key)) { this._proxy(key); } else if ('development' !== 'production') { warn('Data field "' + key + '" is already defined ' + 'as a prop. To provide default value for a prop, use the "default" ' + 'prop option; if you want to pass prop values to an instantiation ' + 'call, use the "propsData" option.', this); } } // observe data observe(data, this); }; /** * Swap the instance's $data. Called in $data's setter. * * @param {Object} newData */ Vue.prototype._setData = function (newData) { newData = newData || {}; var oldData = this._data; this._data = newData; var keys, key, i; // unproxy keys not present in new data keys = Object.keys(oldData); i = keys.length; while (i--) { key = keys[i]; if (!(key in newData)) { this._unproxy(key); } } // proxy keys not already proxied, // and trigger change for changed values keys = Object.keys(newData); i = keys.length; while (i--) { key = keys[i]; if (!hasOwn(this, key)) { // new property this._proxy(key); } } oldData.__ob__.removeVm(this); observe(newData, this); this._digest(); }; /** * Proxy a property, so that * vm.prop === vm._data.prop * * @param {String} key */ Vue.prototype._proxy = function (key) { if (!isReserved(key)) { // need to store ref to self here // because these getter/setters might // be called by child scopes via // prototype inheritance. var self = this; Object.defineProperty(self, key, { configurable: true, enumerable: true, get: function proxyGetter() { return self._data[key]; }, set: function proxySetter(val) { self._data[key] = val; } }); } }; /** * Unproxy a property. * * @param {String} key */ Vue.prototype._unproxy = function (key) { if (!isReserved(key)) { delete this[key]; } }; /** * Force update on every watcher in scope. */ Vue.prototype._digest = function () { for (var i = 0, l = this._watchers.length; i < l; i++) { this._watchers[i].update(true); // shallow updates } }; /** * Setup computed properties. They are essentially * special getter/setters */ function noop() {} Vue.prototype._initComputed = function () { var computed = this.$options.computed; if (computed) { for (var key in computed) { var userDef = computed[key]; var def = { enumerable: true, configurable: true }; if (typeof userDef === 'function') { def.get = makeComputedGetter(userDef, this); def.set = noop; } else { def.get = userDef.get ? userDef.cache !== false ? makeComputedGetter(userDef.get, this) : bind(userDef.get, this) : noop; def.set = userDef.set ? bind(userDef.set, this) : noop; } Object.defineProperty(this, key, def); } } }; function makeComputedGetter(getter, owner) { var watcher = new Watcher(owner, getter, null, { lazy: true }); return function computedGetter() { if (watcher.dirty) { watcher.evaluate(); } if (Dep.target) { watcher.depend(); } return watcher.value; }; } /** * Setup instance methods. Methods must be bound to the * instance since they might be passed down as a prop to * child components. */ Vue.prototype._initMethods = function () { var methods = this.$options.methods; if (methods) { for (var key in methods) { this[key] = bind(methods[key], this); } } }; /** * Initialize meta information like $index, $key & $value. */ Vue.prototype._initMeta = function () { var metas = this.$options._meta; if (metas) { for (var key in metas) { defineReactive(this, key, metas[key]); } } }; } var eventRE = /^v-on:|^@/; function eventsMixin (Vue) { /** * Setup the instance's option events & watchers. * If the value is a string, we pull it from the * instance's methods by name. */ Vue.prototype._initEvents = function () { var options = this.$options; if (options._asComponent) { registerComponentEvents(this, options.el); } registerCallbacks(this, '$on', options.events); registerCallbacks(this, '$watch', options.watch); }; /** * Register v-on events on a child component * * @param {Vue} vm * @param {Element} el */ function registerComponentEvents(vm, el) { var attrs = el.attributes; var name, value, handler; for (var i = 0, l = attrs.length; i < l; i++) { name = attrs[i].name; if (eventRE.test(name)) { name = name.replace(eventRE, ''); // force the expression into a statement so that // it always dynamically resolves the method to call (#2670) // kinda ugly hack, but does the job. value = attrs[i].value; if (isSimplePath(value)) { value += '.apply(this, $arguments)'; } handler = (vm._scope || vm._context).$eval(value, true); handler._fromParent = true; vm.$on(name.replace(eventRE), handler); } } } /** * Register callbacks for option events and watchers. * * @param {Vue} vm * @param {String} action * @param {Object} hash */ function registerCallbacks(vm, action, hash) { if (!hash) return; var handlers, key, i, j; for (key in hash) { handlers = hash[key]; if (isArray(handlers)) { for (i = 0, j = handlers.length; i < j; i++) { register(vm, action, key, handlers[i]); } } else { register(vm, action, key, handlers); } } } /** * Helper to register an event/watch callback. * * @param {Vue} vm * @param {String} action * @param {String} key * @param {Function|String|Object} handler * @param {Object} [options] */ function register(vm, action, key, handler, options) { var type = typeof handler; if (type === 'function') { vm[action](key, handler, options); } else if (type === 'string') { var methods = vm.$options.methods; var method = methods && methods[handler]; if (method) { vm[action](key, method, options); } else { 'development' !== 'production' && warn('Unknown method: "' + handler + '" when ' + 'registering callback for ' + action + ': "' + key + '".', vm); } } else if (handler && type === 'object') { register(vm, action, key, handler.handler, handler); } } /** * Setup recursive attached/detached calls */ Vue.prototype._initDOMHooks = function () { this.$on('hook:attached', onAttached); this.$on('hook:detached', onDetached); }; /** * Callback to recursively call attached hook on children */ function onAttached() { if (!this._isAttached) { this._isAttached = true; this.$children.forEach(callAttach); } } /** * Iterator to call attached hook * * @param {Vue} child */ function callAttach(child) { if (!child._isAttached && inDoc(child.$el)) { child._callHook('attached'); } } /** * Callback to recursively call detached hook on children */ function onDetached() { if (this._isAttached) { this._isAttached = false; this.$children.forEach(callDetach); } } /** * Iterator to call detached hook * * @param {Vue} child */ function callDetach(child) { if (child._isAttached && !inDoc(child.$el)) { child._callHook('detached'); } } /** * Trigger all handlers for a hook * * @param {String} hook */ Vue.prototype._callHook = function (hook) { this.$emit('pre-hook:' + hook); var handlers = this.$options[hook]; if (handlers) { for (var i = 0, j = handlers.length; i < j; i++) { handlers[i].call(this); } } this.$emit('hook:' + hook); }; } function noop$1() {} /** * A directive links a DOM element with a piece of data, * which is the result of evaluating an expression. * It registers a watcher with the expression and calls * the DOM update function when a change is triggered. * * @param {Object} descriptor * - {String} name * - {Object} def * - {String} expression * - {Array<Object>} [filters] * - {Object} [modifiers] * - {Boolean} literal * - {String} attr * - {String} arg * - {String} raw * - {String} [ref] * - {Array<Object>} [interp] * - {Boolean} [hasOneTime] * @param {Vue} vm * @param {Node} el * @param {Vue} [host] - transclusion host component * @param {Object} [scope] - v-for scope * @param {Fragment} [frag] - owner fragment * @constructor */ function Directive(descriptor, vm, el, host, scope, frag) { this.vm = vm; this.el = el; // copy descriptor properties this.descriptor = descriptor; this.name = descriptor.name; this.expression = descriptor.expression; this.arg = descriptor.arg; this.modifiers = descriptor.modifiers; this.filters = descriptor.filters; this.literal = this.modifiers && this.modifiers.literal; // private this._locked = false; this._bound = false; this._listeners = null; // link context this._host = host; this._scope = scope; this._frag = frag; // store directives on node in dev mode if ('development' !== 'production' && this.el) { this.el._vue_directives = this.el._vue_directives || []; this.el._vue_directives.push(this); } } /** * Initialize the directive, mixin definition properties, * setup the watcher, call definition bind() and update() * if present. */ Directive.prototype._bind = function () { var name = this.name; var descriptor = this.descriptor; // remove attribute if ((name !== 'cloak' || this.vm._isCompiled) && this.el && this.el.removeAttribute) { var attr = descriptor.attr || 'v-' + name; this.el.removeAttribute(attr); } // copy def properties var def = descriptor.def; if (typeof def === 'function') { this.update = def; } else { extend(this, def); } // setup directive params this._setupParams(); // initial bind if (this.bind) { this.bind(); } this._bound = true; if (this.literal) { this.update && this.update(descriptor.raw); } else if ((this.expression || this.modifiers) && (this.update || this.twoWay) && !this._checkStatement()) { // wrapped updater for context var dir = this; if (this.update) { this._update = function (val, oldVal) { if (!dir._locked) { dir.update(val, oldVal); } }; } else { this._update = noop$1; } var preProcess = this._preProcess ? bind(this._preProcess, this) : null; var postProcess = this._postProcess ? bind(this._postProcess, this) : null; var watcher = this._watcher = new Watcher(this.vm, this.expression, this._update, // callback { filters: this.filters, twoWay: this.twoWay, deep: this.deep, preProcess: preProcess, postProcess: postProcess, scope: this._scope }); // v-model with inital inline value need to sync back to // model instead of update to DOM on init. They would // set the afterBind hook to indicate that. if (this.afterBind) { this.afterBind(); } else if (this.update) { this.update(watcher.value); } } }; /** * Setup all param attributes, e.g. track-by, * transition-mode, etc... */ Directive.prototype._setupParams = function () { if (!this.params) { return; } var params = this.params; // swap the params array with a fresh object. this.params = Object.create(null); var i = params.length; var key, val, mappedKey; while (i--) { key = hyphenate(params[i]); mappedKey = camelize(key); val = getBindAttr(this.el, key); if (val != null) { // dynamic this._setupParamWatcher(mappedKey, val); } else { // static val = getAttr(this.el, key); if (val != null) { this.params[mappedKey] = val === '' ? true : val; } } } }; /** * Setup a watcher for a dynamic param. * * @param {String} key * @param {String} expression */ Directive.prototype._setupParamWatcher = function (key, expression) { var self = this; var called = false; var unwatch = (this._scope || this.vm).$watch(expression, function (val, oldVal) { self.params[key] = val; // since we are in immediate mode, // only call the param change callbacks if this is not the first update. if (called) { var cb = self.paramWatchers && self.paramWatchers[key]; if (cb) { cb.call(self, val, oldVal); } } else { called = true; } }, { immediate: true, user: false });(this._paramUnwatchFns || (this._paramUnwatchFns = [])).push(unwatch); }; /** * Check if the directive is a function caller * and if the expression is a callable one. If both true, * we wrap up the expression and use it as the event * handler. * * e.g. on-click="a++" * * @return {Boolean} */ Directive.prototype._checkStatement = function () { var expression = this.expression; if (expression && this.acceptStatement && !isSimplePath(expression)) { var fn = parseExpression(expression).get; var scope = this._scope || this.vm; var handler = function handler(e) { scope.$event = e; fn.call(scope, scope); scope.$event = null; }; if (this.filters) { handler = scope._applyFilters(handler, null, this.filters); } this.update(handler); return true; } }; /** * Set the corresponding value with the setter. * This should only be used in two-way directives * e.g. v-model. * * @param {*} value * @public */ Directive.prototype.set = function (value) { /* istanbul ignore else */ if (this.twoWay) { this._withLock(function () { this._watcher.set(value); }); } else if ('development' !== 'production') { warn('Directive.set() can only be used inside twoWay' + 'directives.'); } }; /** * Execute a function while preventing that function from * triggering updates on this directive instance. * * @param {Function} fn */ Directive.prototype._withLock = function (fn) { var self = this; self._locked = true; fn.call(self); nextTick(function () { self._locked = false; }); }; /** * Convenience method that attaches a DOM event listener * to the directive element and autometically tears it down * during unbind. * * @param {String} event * @param {Function} handler * @param {Boolean} [useCapture] */ Directive.prototype.on = function (event, handler, useCapture) { on(this.el, event, handler, useCapture);(this._listeners || (this._listeners = [])).push([event, handler]); }; /** * Teardown the watcher and call unbind. */ Directive.prototype._teardown = function () { if (this._bound) { this._bound = false; if (this.unbind) { this.unbind(); } if (this._watcher) { this._watcher.teardown(); } var listeners = this._listeners; var i; if (listeners) { i = listeners.length; while (i--) { off(this.el, listeners[i][0], listeners[i][1]); } } var unwatchFns = this._paramUnwatchFns; if (unwatchFns) { i = unwatchFns.length; while (i--) { unwatchFns[i](); } } if ('development' !== 'production' && this.el) { this.el._vue_directives.$remove(this); } this.vm = this.el = this._watcher = this._listeners = null; } }; function lifecycleMixin (Vue) { /** * Update v-ref for component. * * @param {Boolean} remove */ Vue.prototype._updateRef = function (remove) { var ref = this.$options._ref; if (ref) { var refs = (this._scope || this._context).$refs; if (remove) { if (refs[ref] === this) { refs[ref] = null; } } else { refs[ref] = this; } } }; /** * Transclude, compile and link element. * * If a pre-compiled linker is available, that means the * passed in element will be pre-transcluded and compiled * as well - all we need to do is to call the linker. * * Otherwise we need to call transclude/compile/link here. * * @param {Element} el */ Vue.prototype._compile = function (el) { var options = this.$options; // transclude and init element // transclude can potentially replace original // so we need to keep reference; this step also injects // the template and caches the original attributes // on the container node and replacer node. var original = el; el = transclude(el, options); this._initElement(el); // handle v-pre on root node (#2026) if (el.nodeType === 1 && getAttr(el, 'v-pre') !== null) { return; } // root is always compiled per-instance, because // container attrs and props can be different every time. var contextOptions = this._context && this._context.$options; var rootLinker = compileRoot(el, options, contextOptions); // resolve slot distribution resolveSlots(this, options._content); // compile and link the rest var contentLinkFn; var ctor = this.constructor; // component compilation can be cached // as long as it's not using inline-template if (options._linkerCachable) { contentLinkFn = ctor.linker; if (!contentLinkFn) { contentLinkFn = ctor.linker = compile(el, options); } } // link phase // make sure to link root with prop scope! var rootUnlinkFn = rootLinker(this, el, this._scope); var contentUnlinkFn = contentLinkFn ? contentLinkFn(this, el) : compile(el, options)(this, el); // register composite unlink function // to be called during instance destruction this._unlinkFn = function () { rootUnlinkFn(); // passing destroying: true to avoid searching and // splicing the directives contentUnlinkFn(true); }; // finally replace original if (options.replace) { replace(original, el); } this._isCompiled = true; this._callHook('compiled'); }; /** * Initialize instance element. Called in the public * $mount() method. * * @param {Element} el */ Vue.prototype._initElement = function (el) { if (isFragment(el)) { this._isFragment = true; this.$el = this._fragmentStart = el.firstChild; this._fragmentEnd = el.lastChild; // set persisted text anchors to empty if (this._fragmentStart.nodeType === 3) { this._fragmentStart.data = this._fragmentEnd.data = ''; } this._fragment = el; } else { this.$el = el; } this.$el.__vue__ = this; this._callHook('beforeCompile'); }; /** * Create and bind a directive to an element. * * @param {Object} descriptor - parsed directive descriptor * @param {Node} node - target node * @param {Vue} [host] - transclusion host component * @param {Object} [scope] - v-for scope * @param {Fragment} [frag] - owner fragment */ Vue.prototype._bindDir = function (descriptor, node, host, scope, frag) { this._directives.push(new Directive(descriptor, this, node, host, scope, frag)); }; /** * Teardown an instance, unobserves the data, unbind all the * directives, turn off all the event listeners, etc. * * @param {Boolean} remove - whether to remove the DOM node. * @param {Boolean} deferCleanup - if true, defer cleanup to * be called later */ Vue.prototype._destroy = function (remove, deferCleanup) { if (this._isBeingDestroyed) { if (!deferCleanup) { this._cleanup(); } return; } var destroyReady; var pendingRemoval; var self = this; // Cleanup should be called either synchronously or asynchronoysly as // callback of this.$remove(), or if remove and deferCleanup are false. // In any case it should be called after all other removing, unbinding and // turning of is done var cleanupIfPossible = function cleanupIfPossible() { if (destroyReady && !pendingRemoval && !deferCleanup) { self._cleanup(); } }; // remove DOM element if (remove && this.$el) { pendingRemoval = true; this.$remove(function () { pendingRemoval = false; cleanupIfPossible(); }); } this._callHook('beforeDestroy'); this._isBeingDestroyed = true; var i; // remove self from parent. only necessary // if parent is not being destroyed as well. var parent = this.$parent; if (parent && !parent._isBeingDestroyed) { parent.$children.$remove(this); // unregister ref (remove: true) this._updateRef(true); } // destroy all children. i = this.$children.length; while (i--) { this.$children[i].$destroy(); } // teardown props if (this._propsUnlinkFn) { this._propsUnlinkFn(); } // teardown all directives. this also tearsdown all // directive-owned watchers. if (this._unlinkFn) { this._unlinkFn(); } i = this._watchers.length; while (i--) { this._watchers[i].teardown(); } // remove reference to self on $el if (this.$el) { this.$el.__vue__ = null; } destroyReady = true; cleanupIfPossible(); }; /** * Clean up to ensure garbage collection. * This is called after the leave transition if there * is any. */ Vue.prototype._cleanup = function () { if (this._isDestroyed) { return; } // remove self from owner fragment // do it in cleanup so that we can call $destroy with // defer right when a fragment is about to be removed. if (this._frag) { this._frag.children.$remove(this); } // remove reference from data ob // frozen object may not have observer. if (this._data && this._data.__ob__) { this._data.__ob__.removeVm(this); } // Clean up references to private properties and other // instances. preserve reference to _data so that proxy // accessors still work. The only potential side effect // here is that mutating the instance after it's destroyed // may affect the state of other components that are still // observing the same object, but that seems to be a // reasonable responsibility for the user rather than // always throwing an error on them. this.$el = this.$parent = this.$root = this.$children = this._watchers = this._context = this._scope = this._directives = null; // call the last hook... this._isDestroyed = true; this._callHook('destroyed'); // turn off all instance listeners. this.$off(); }; } function miscMixin (Vue) { /** * Apply a list of filter (descriptors) to a value. * Using plain for loops here because this will be called in * the getter of any watcher with filters so it is very * performance sensitive. * * @param {*} value * @param {*} [oldValue] * @param {Array} filters * @param {Boolean} write * @return {*} */ Vue.prototype._applyFilters = function (value, oldValue, filters, write) { var filter, fn, args, arg, offset, i, l, j, k; for (i = 0, l = filters.length; i < l; i++) { filter = filters[write ? l - i - 1 : i]; fn = resolveAsset(this.$options, 'filters', filter.name, true); if (!fn) continue; fn = write ? fn.write : fn.read || fn; if (typeof fn !== 'function') continue; args = write ? [value, oldValue] : [value]; offset = write ? 2 : 1; if (filter.args) { for (j = 0, k = filter.args.length; j < k; j++) { arg = filter.args[j]; args[j + offset] = arg.dynamic ? this.$get(arg.value) : arg.value; } } value = fn.apply(this, args); } return value; }; /** * Resolve a component, depending on whether the component * is defined normally or using an async factory function. * Resolves synchronously if already resolved, otherwise * resolves asynchronously and caches the resolved * constructor on the factory. * * @param {String|Function} value * @param {Function} cb */ Vue.prototype._resolveComponent = function (value, cb) { var factory; if (typeof value === 'function') { factory = value; } else { factory = resolveAsset(this.$options, 'components', value, true); } /* istanbul ignore if */ if (!factory) { return; } // async component factory if (!factory.options) { if (factory.resolved) { // cached cb(factory.resolved); } else if (factory.requested) { // pool callbacks factory.pendingCallbacks.push(cb); } else { factory.requested = true; var cbs = factory.pendingCallbacks = [cb]; factory.call(this, function resolve(res) { if (isPlainObject(res)) { res = Vue.extend(res); } // cache resolved factory.resolved = res; // invoke callbacks for (var i = 0, l = cbs.length; i < l; i++) { cbs[i](res); } }, function reject(reason) { 'development' !== 'production' && warn('Failed to resolve async component' + (typeof value === 'string' ? ': ' + value : '') + '. ' + (reason ? '\nReason: ' + reason : '')); }); } } else { // normal component cb(factory); } }; } var filterRE$1 = /[^|]\|[^|]/; function dataAPI (Vue) { /** * Get the value from an expression on this vm. * * @param {String} exp * @param {Boolean} [asStatement] * @return {*} */ Vue.prototype.$get = function (exp, asStatement) { var res = parseExpression(exp); if (res) { if (asStatement) { var self = this; return function statementHandler() { self.$arguments = toArray(arguments); var result = res.get.call(self, self); self.$arguments = null; return result; }; } else { try { return res.get.call(this, this); } catch (e) {} } } }; /** * Set the value from an expression on this vm. * The expression must be a valid left-hand * expression in an assignment. * * @param {String} exp * @param {*} val */ Vue.prototype.$set = function (exp, val) { var res = parseExpression(exp, true); if (res && res.set) { res.set.call(this, this, val); } }; /** * Delete a property on the VM * * @param {String} key */ Vue.prototype.$delete = function (key) { del(this._data, key); }; /** * Watch an expression, trigger callback when its * value changes. * * @param {String|Function} expOrFn * @param {Function} cb * @param {Object} [options] * - {Boolean} deep * - {Boolean} immediate * @return {Function} - unwatchFn */ Vue.prototype.$watch = function (expOrFn, cb, options) { var vm = this; var parsed; if (typeof expOrFn === 'string') { parsed = parseDirective(expOrFn); expOrFn = parsed.expression; } var watcher = new Watcher(vm, expOrFn, cb, { deep: options && options.deep, sync: options && options.sync, filters: parsed && parsed.filters, user: !options || options.user !== false }); if (options && options.immediate) { cb.call(vm, watcher.value); } return function unwatchFn() { watcher.teardown(); }; }; /** * Evaluate a text directive, including filters. * * @param {String} text * @param {Boolean} [asStatement] * @return {String} */ Vue.prototype.$eval = function (text, asStatement) { // check for filters. if (filterRE$1.test(text)) { var dir = parseDirective(text); // the filter regex check might give false positive // for pipes inside strings, so it's possible that // we don't get any filters here var val = this.$get(dir.expression, asStatement); return dir.filters ? this._applyFilters(val, null, dir.filters) : val; } else { // no filter return this.$get(text, asStatement); } }; /** * Interpolate a piece of template text. * * @param {String} text * @return {String} */ Vue.prototype.$interpolate = function (text) { var tokens = parseText(text); var vm = this; if (tokens) { if (tokens.length === 1) { return vm.$eval(tokens[0].value) + ''; } else { return tokens.map(function (token) { return token.tag ? vm.$eval(token.value) : token.value; }).join(''); } } else { return text; } }; /** * Log instance data as a plain JS object * so that it is easier to inspect in console. * This method assumes console is available. * * @param {String} [path] */ Vue.prototype.$log = function (path) { var data = path ? getPath(this._data, path) : this._data; if (data) { data = clean(data); } // include computed fields if (!path) { var key; for (key in this.$options.computed) { data[key] = clean(this[key]); } if (this._props) { for (key in this._props) { data[key] = clean(this[key]); } } } console.log(data); }; /** * "clean" a getter/setter converted object into a plain * object copy. * * @param {Object} - obj * @return {Object} */ function clean(obj) { return JSON.parse(JSON.stringify(obj)); } } function domAPI (Vue) { /** * Convenience on-instance nextTick. The callback is * auto-bound to the instance, and this avoids component * modules having to rely on the global Vue. * * @param {Function} fn */ Vue.prototype.$nextTick = function (fn) { nextTick(fn, this); }; /** * Append instance to target * * @param {Node} target * @param {Function} [cb] * @param {Boolean} [withTransition] - defaults to true */ Vue.prototype.$appendTo = function (target, cb, withTransition) { return insert(this, target, cb, withTransition, append, appendWithTransition); }; /** * Prepend instance to target * * @param {Node} target * @param {Function} [cb] * @param {Boolean} [withTransition] - defaults to true */ Vue.prototype.$prependTo = function (target, cb, withTransition) { target = query(target); if (target.hasChildNodes()) { this.$before(target.firstChild, cb, withTransition); } else { this.$appendTo(target, cb, withTransition); } return this; }; /** * Insert instance before target * * @param {Node} target * @param {Function} [cb] * @param {Boolean} [withTransition] - defaults to true */ Vue.prototype.$before = function (target, cb, withTransition) { return insert(this, target, cb, withTransition, beforeWithCb, beforeWithTransition); }; /** * Insert instance after target * * @param {Node} target * @param {Function} [cb] * @param {Boolean} [withTransition] - defaults to true */ Vue.prototype.$after = function (target, cb, withTransition) { target = query(target); if (target.nextSibling) { this.$before(target.nextSibling, cb, withTransition); } else { this.$appendTo(target.parentNode, cb, withTransition); } return this; }; /** * Remove instance from DOM * * @param {Function} [cb] * @param {Boolean} [withTransition] - defaults to true */ Vue.prototype.$remove = function (cb, withTransition) { if (!this.$el.parentNode) { return cb && cb(); } var inDocument = this._isAttached && inDoc(this.$el); // if we are not in document, no need to check // for transitions if (!inDocument) withTransition = false; var self = this; var realCb = function realCb() { if (inDocument) self._callHook('detached'); if (cb) cb(); }; if (this._isFragment) { removeNodeRange(this._fragmentStart, this._fragmentEnd, this, this._fragment, realCb); } else { var op = withTransition === false ? removeWithCb : removeWithTransition; op(this.$el, this, realCb); } return this; }; /** * Shared DOM insertion function. * * @param {Vue} vm * @param {Element} target * @param {Function} [cb] * @param {Boolean} [withTransition] * @param {Function} op1 - op for non-transition insert * @param {Function} op2 - op for transition insert * @return vm */ function insert(vm, target, cb, withTransition, op1, op2) { target = query(target); var targetIsDetached = !inDoc(target); var op = withTransition === false || targetIsDetached ? op1 : op2; var shouldCallHook = !targetIsDetached && !vm._isAttached && !inDoc(vm.$el); if (vm._isFragment) { mapNodeRange(vm._fragmentStart, vm._fragmentEnd, function (node) { op(node, target, vm); }); cb && cb(); } else { op(vm.$el, target, vm, cb); } if (shouldCallHook) { vm._callHook('attached'); } return vm; } /** * Check for selectors * * @param {String|Element} el */ function query(el) { return typeof el === 'string' ? document.querySelector(el) : el; } /** * Append operation that takes a callback. * * @param {Node} el * @param {Node} target * @param {Vue} vm - unused * @param {Function} [cb] */ function append(el, target, vm, cb) { target.appendChild(el); if (cb) cb(); } /** * InsertBefore operation that takes a callback. * * @param {Node} el * @param {Node} target * @param {Vue} vm - unused * @param {Function} [cb] */ function beforeWithCb(el, target, vm, cb) { before(el, target); if (cb) cb(); } /** * Remove operation that takes a callback. * * @param {Node} el * @param {Vue} vm - unused * @param {Function} [cb] */ function removeWithCb(el, vm, cb) { remove(el); if (cb) cb(); } } function eventsAPI (Vue) { /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn */ Vue.prototype.$on = function (event, fn) { (this._events[event] || (this._events[event] = [])).push(fn); modifyListenerCount(this, event, 1); return this; }; /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn */ Vue.prototype.$once = function (event, fn) { var self = this; function on() { self.$off(event, on); fn.apply(this, arguments); } on.fn = fn; this.$on(event, on); return this; }; /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn */ Vue.prototype.$off = function (event, fn) { var cbs; // all if (!arguments.length) { if (this.$parent) { for (event in this._events) { cbs = this._events[event]; if (cbs) { modifyListenerCount(this, event, -cbs.length); } } } this._events = {}; return this; } // specific event cbs = this._events[event]; if (!cbs) { return this; } if (arguments.length === 1) { modifyListenerCount(this, event, -cbs.length); this._events[event] = null; return this; } // specific handler var cb; var i = cbs.length; while (i--) { cb = cbs[i]; if (cb === fn || cb.fn === fn) { modifyListenerCount(this, event, -1); cbs.splice(i, 1); break; } } return this; }; /** * Trigger an event on self. * * @param {String|Object} event * @return {Boolean} shouldPropagate */ Vue.prototype.$emit = function (event) { var isSource = typeof event === 'string'; event = isSource ? event : event.name; var cbs = this._events[event]; var shouldPropagate = isSource || !cbs; if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs; // this is a somewhat hacky solution to the question raised // in #2102: for an inline component listener like <comp @test="doThis">, // the propagation handling is somewhat broken. Therefore we // need to treat these inline callbacks differently. var hasParentCbs = isSource && cbs.some(function (cb) { return cb._fromParent; }); if (hasParentCbs) { shouldPropagate = false; } var args = toArray(arguments, 1); for (var i = 0, l = cbs.length; i < l; i++) { var cb = cbs[i]; var res = cb.apply(this, args); if (res === true && (!hasParentCbs || cb._fromParent)) { shouldPropagate = true; } } } return shouldPropagate; }; /** * Recursively broadcast an event to all children instances. * * @param {String|Object} event * @param {...*} additional arguments */ Vue.prototype.$broadcast = function (event) { var isSource = typeof event === 'string'; event = isSource ? event : event.name; // if no child has registered for this event, // then there's no need to broadcast. if (!this._eventsCount[event]) return; var children = this.$children; var args = toArray(arguments); if (isSource) { // use object event to indicate non-source emit // on children args[0] = { name: event, source: this }; } for (var i = 0, l = children.length; i < l; i++) { var child = children[i]; var shouldPropagate = child.$emit.apply(child, args); if (shouldPropagate) { child.$broadcast.apply(child, args); } } return this; }; /** * Recursively propagate an event up the parent chain. * * @param {String} event * @param {...*} additional arguments */ Vue.prototype.$dispatch = function (event) { var shouldPropagate = this.$emit.apply(this, arguments); if (!shouldPropagate) return; var parent = this.$parent; var args = toArray(arguments); // use object event to indicate non-source emit // on parents args[0] = { name: event, source: this }; while (parent) { shouldPropagate = parent.$emit.apply(parent, args); parent = shouldPropagate ? parent.$parent : null; } return this; }; /** * Modify the listener counts on all parents. * This bookkeeping allows $broadcast to return early when * no child has listened to a certain event. * * @param {Vue} vm * @param {String} event * @param {Number} count */ var hookRE = /^hook:/; function modifyListenerCount(vm, event, count) { var parent = vm.$parent; // hooks do not get broadcasted so no need // to do bookkeeping for them if (!parent || !count || hookRE.test(event)) return; while (parent) { parent._eventsCount[event] = (parent._eventsCount[event] || 0) + count; parent = parent.$parent; } } } function lifecycleAPI (Vue) { /** * Set instance target element and kick off the compilation * process. The passed in `el` can be a selector string, an * existing Element, or a DocumentFragment (for block * instances). * * @param {Element|DocumentFragment|string} el * @public */ Vue.prototype.$mount = function (el) { if (this._isCompiled) { 'development' !== 'production' && warn('$mount() should be called only once.', this); return; } el = query(el); if (!el) { el = document.createElement('div'); } this._compile(el); this._initDOMHooks(); if (inDoc(this.$el)) { this._callHook('attached'); ready.call(this); } else { this.$once('hook:attached', ready); } return this; }; /** * Mark an instance as ready. */ function ready() { this._isAttached = true; this._isReady = true; this._callHook('ready'); } /** * Teardown the instance, simply delegate to the internal * _destroy. * * @param {Boolean} remove * @param {Boolean} deferCleanup */ Vue.prototype.$destroy = function (remove, deferCleanup) { this._destroy(remove, deferCleanup); }; /** * Partially compile a piece of DOM and return a * decompile function. * * @param {Element|DocumentFragment} el * @param {Vue} [host] * @param {Object} [scope] * @param {Fragment} [frag] * @return {Function} */ Vue.prototype.$compile = function (el, host, scope, frag) { return compile(el, this.$options, true)(this, el, host, scope, frag); }; } /** * The exposed Vue constructor. * * API conventions: * - public API methods/properties are prefixed with `$` * - internal methods/properties are prefixed with `_` * - non-prefixed properties are assumed to be proxied user * data. * * @constructor * @param {Object} [options] * @public */ function Vue(options) { this._init(options); } // install internals initMixin(Vue); stateMixin(Vue); eventsMixin(Vue); lifecycleMixin(Vue); miscMixin(Vue); // install instance APIs dataAPI(Vue); domAPI(Vue); eventsAPI(Vue); lifecycleAPI(Vue); var slot = { priority: SLOT, params: ['name'], bind: function bind() { // this was resolved during component transclusion var name = this.params.name || 'default'; var content = this.vm._slotContents && this.vm._slotContents[name]; if (!content || !content.hasChildNodes()) { this.fallback(); } else { this.compile(content.cloneNode(true), this.vm._context, this.vm); } }, compile: function compile(content, context, host) { if (content && context) { if (this.el.hasChildNodes() && content.childNodes.length === 1 && content.childNodes[0].nodeType === 1 && content.childNodes[0].hasAttribute('v-if')) { // if the inserted slot has v-if // inject fallback content as the v-else var elseBlock = document.createElement('template'); elseBlock.setAttribute('v-else', ''); elseBlock.innerHTML = this.el.innerHTML; // the else block should be compiled in child scope elseBlock._context = this.vm; content.appendChild(elseBlock); } var scope = host ? host._scope : this._scope; this.unlink = context.$compile(content, host, scope, this._frag); } if (content) { replace(this.el, content); } else { remove(this.el); } }, fallback: function fallback() { this.compile(extractContent(this.el, true), this.vm); }, unbind: function unbind() { if (this.unlink) { this.unlink(); } } }; var partial = { priority: PARTIAL, params: ['name'], // watch changes to name for dynamic partials paramWatchers: { name: function name(value) { vIf.remove.call(this); if (value) { this.insert(value); } } }, bind: function bind() { this.anchor = createAnchor('v-partial'); replace(this.el, this.anchor); this.insert(this.params.name); }, insert: function insert(id) { var partial = resolveAsset(this.vm.$options, 'partials', id, true); if (partial) { this.factory = new FragmentFactory(this.vm, partial); vIf.insert.call(this); } }, unbind: function unbind() { if (this.frag) { this.frag.destroy(); } } }; var elementDirectives = { slot: slot, partial: partial }; var convertArray = vFor._postProcess; /** * Limit filter for arrays * * @param {Number} n * @param {Number} offset (Decimal expected) */ function limitBy(arr, n, offset) { offset = offset ? parseInt(offset, 10) : 0; n = toNumber(n); return typeof n === 'number' ? arr.slice(offset, offset + n) : arr; } /** * Filter filter for arrays * * @param {String} search * @param {String} [delimiter] * @param {String} ...dataKeys */ function filterBy(arr, search, delimiter) { arr = convertArray(arr); if (search == null) { return arr; } if (typeof search === 'function') { return arr.filter(search); } // cast to lowercase string search = ('' + search).toLowerCase(); // allow optional `in` delimiter // because why not var n = delimiter === 'in' ? 3 : 2; // extract and flatten keys var keys = Array.prototype.concat.apply([], toArray(arguments, n)); var res = []; var item, key, val, j; for (var i = 0, l = arr.length; i < l; i++) { item = arr[i]; val = item && item.$value || item; j = keys.length; if (j) { while (j--) { key = keys[j]; if (key === '$key' && contains(item.$key, search) || contains(getPath(val, key), search)) { res.push(item); break; } } } else if (contains(item, search)) { res.push(item); } } return res; } /** * Filter filter for arrays * * @param {String|Array<String>|Function} ...sortKeys * @param {Number} [order] */ function orderBy(arr) { var comparator = null; var sortKeys = undefined; arr = convertArray(arr); // determine order (last argument) var args = toArray(arguments, 1); var order = args[args.length - 1]; if (typeof order === 'number') { order = order < 0 ? -1 : 1; args = args.length > 1 ? args.slice(0, -1) : args; } else { order = 1; } // determine sortKeys & comparator var firstArg = args[0]; if (!firstArg) { return arr; } else if (typeof firstArg === 'function') { // custom comparator comparator = function (a, b) { return firstArg(a, b) * order; }; } else { // string keys. flatten first sortKeys = Array.prototype.concat.apply([], args); comparator = function (a, b, i) { i = i || 0; return i >= sortKeys.length - 1 ? baseCompare(a, b, i) : baseCompare(a, b, i) || comparator(a, b, i + 1); }; } function baseCompare(a, b, sortKeyIndex) { var sortKey = sortKeys[sortKeyIndex]; if (sortKey) { if (sortKey !== '$key') { if (isObject(a) && '$value' in a) a = a.$value; if (isObject(b) && '$value' in b) b = b.$value; } a = isObject(a) ? getPath(a, sortKey) : a; b = isObject(b) ? getPath(b, sortKey) : b; } return a === b ? 0 : a > b ? order : -order; } // sort on a copy to avoid mutating original array return arr.slice().sort(comparator); } /** * String contain helper * * @param {*} val * @param {String} search */ function contains(val, search) { var i; if (isPlainObject(val)) { var keys = Object.keys(val); i = keys.length; while (i--) { if (contains(val[keys[i]], search)) { return true; } } } else if (isArray(val)) { i = val.length; while (i--) { if (contains(val[i], search)) { return true; } } } else if (val != null) { return val.toString().toLowerCase().indexOf(search) > -1; } } var digitsRE = /(\d{3})(?=\d)/g; // asset collections must be a plain object. var filters = { orderBy: orderBy, filterBy: filterBy, limitBy: limitBy, /** * Stringify value. * * @param {Number} indent */ json: { read: function read(value, indent) { return typeof value === 'string' ? value : JSON.stringify(value, null, arguments.length > 1 ? indent : 2); }, write: function write(value) { try { return JSON.parse(value); } catch (e) { return value; } } }, /** * 'abc' => 'Abc' */ capitalize: function capitalize(value) { if (!value && value !== 0) return ''; value = value.toString(); return value.charAt(0).toUpperCase() + value.slice(1); }, /** * 'abc' => 'ABC' */ uppercase: function uppercase(value) { return value || value === 0 ? value.toString().toUpperCase() : ''; }, /** * 'AbC' => 'abc' */ lowercase: function lowercase(value) { return value || value === 0 ? value.toString().toLowerCase() : ''; }, /** * 12345 => $12,345.00 * * @param {String} sign * @param {Number} decimals Decimal places */ currency: function currency(value, _currency, decimals) { value = parseFloat(value); if (!isFinite(value) || !value && value !== 0) return ''; _currency = _currency != null ? _currency : '$'; decimals = decimals != null ? decimals : 2; var stringified = Math.abs(value).toFixed(decimals); var _int = decimals ? stringified.slice(0, -1 - decimals) : stringified; var i = _int.length % 3; var head = i > 0 ? _int.slice(0, i) + (_int.length > 3 ? ',' : '') : ''; var _float = decimals ? stringified.slice(-1 - decimals) : ''; var sign = value < 0 ? '-' : ''; return sign + _currency + head + _int.slice(i).replace(digitsRE, '$1,') + _float; }, /** * 'item' => 'items' * * @params * an array of strings corresponding to * the single, double, triple ... forms of the word to * be pluralized. When the number to be pluralized * exceeds the length of the args, it will use the last * entry in the array. * * e.g. ['single', 'double', 'triple', 'multiple'] */ pluralize: function pluralize(value) { var args = toArray(arguments, 1); var length = args.length; if (length > 1) { var index = value % 10 - 1; return index in args ? args[index] : args[length - 1]; } else { return args[0] + (value === 1 ? '' : 's'); } }, /** * Debounce a handler function. * * @param {Function} handler * @param {Number} delay = 300 * @return {Function} */ debounce: function debounce(handler, delay) { if (!handler) return; if (!delay) { delay = 300; } return _debounce(handler, delay); } }; function installGlobalAPI (Vue) { /** * Vue and every constructor that extends Vue has an * associated options object, which can be accessed during * compilation steps as `this.constructor.options`. * * These can be seen as the default options of every * Vue instance. */ Vue.options = { directives: directives, elementDirectives: elementDirectives, filters: filters, transitions: {}, components: {}, partials: {}, replace: true }; /** * Expose useful internals */ Vue.util = util; Vue.config = config; Vue.set = set; Vue['delete'] = del; Vue.nextTick = nextTick; /** * The following are exposed for advanced usage / plugins */ Vue.compiler = compiler; Vue.FragmentFactory = FragmentFactory; Vue.internalDirectives = internalDirectives; Vue.parsers = { path: path, text: text, template: template, directive: directive, expression: expression }; /** * Each instance constructor, including Vue, has a unique * cid. This enables us to create wrapped "child * constructors" for prototypal inheritance and cache them. */ Vue.cid = 0; var cid = 1; /** * Class inheritance * * @param {Object} extendOptions */ Vue.extend = function (extendOptions) { extendOptions = extendOptions || {}; var Super = this; var isFirstExtend = Super.cid === 0; if (isFirstExtend && extendOptions._Ctor) { return extendOptions._Ctor; } var name = extendOptions.name || Super.options.name; if ('development' !== 'production') { if (!/^[a-zA-Z][\w-]*$/.test(name)) { warn('Invalid component name: "' + name + '". Component names ' + 'can only contain alphanumeric characaters and the hyphen.'); name = null; } } var Sub = createClass(name || 'VueComponent'); Sub.prototype = Object.create(Super.prototype); Sub.prototype.constructor = Sub; Sub.cid = cid++; Sub.options = mergeOptions(Super.options, extendOptions); Sub['super'] = Super; // allow further extension Sub.extend = Super.extend; // create asset registers, so extended classes // can have their private assets too. config._assetTypes.forEach(function (type) { Sub[type] = Super[type]; }); // enable recursive self-lookup if (name) { Sub.options.components[name] = Sub; } // cache constructor if (isFirstExtend) { extendOptions._Ctor = Sub; } return Sub; }; /** * A function that returns a sub-class constructor with the * given name. This gives us much nicer output when * logging instances in the console. * * @param {String} name * @return {Function} */ function createClass(name) { /* eslint-disable no-new-func */ return new Function('return function ' + classify(name) + ' (options) { this._init(options) }')(); /* eslint-enable no-new-func */ } /** * Plugin system * * @param {Object} plugin */ Vue.use = function (plugin) { /* istanbul ignore if */ if (plugin.installed) { return; } // additional parameters var args = toArray(arguments, 1); args.unshift(this); if (typeof plugin.install === 'function') { plugin.install.apply(plugin, args); } else { plugin.apply(null, args); } plugin.installed = true; return this; }; /** * Apply a global mixin by merging it into the default * options. */ Vue.mixin = function (mixin) { Vue.options = mergeOptions(Vue.options, mixin); }; /** * Create asset registration methods with the following * signature: * * @param {String} id * @param {*} definition */ config._assetTypes.forEach(function (type) { Vue[type] = function (id, definition) { if (!definition) { return this.options[type + 's'][id]; } else { /* istanbul ignore if */ if ('development' !== 'production') { if (type === 'component' && (commonTagRE.test(id) || reservedTagRE.test(id))) { warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + id); } } if (type === 'component' && isPlainObject(definition)) { if (!definition.name) { definition.name = id; } definition = Vue.extend(definition); } this.options[type + 's'][id] = definition; return definition; } }; }); // expose internal transition API extend(Vue.transition, transition); } installGlobalAPI(Vue); Vue.version = '1.0.26'; // devtools global hook /* istanbul ignore next */ setTimeout(function () { if (config.devtools) { if (devtools) { devtools.emit('init', Vue); } else if ('development' !== 'production' && inBrowser && /Chrome\/\d+/.test(window.navigator.userAgent)) { console.log('Download the Vue Devtools for a better development experience:\n' + 'https://github.com/vuejs/vue-devtools'); } } }, 0); return Vue; }));