From 566bb67ea1a526c7ac4457d58f4b1fadb0b7f919 Mon Sep 17 00:00:00 2001 From: nuintun Date: Thu, 14 Jan 2016 14:01:27 +0800 Subject: [PATCH] update files --- static/js/vue/vue.js | 583 +++++++++++++++++++++++++++++-------------- 1 file changed, 397 insertions(+), 186 deletions(-) diff --git a/static/js/vue/vue.js b/static/js/vue/vue.js index b5a7d42..faf04d5 100644 --- a/static/js/vue/vue.js +++ b/static/js/vue/vue.js @@ -1,11 +1,12 @@ /*! - * Vue.js v1.0.10 - * (c) 2015 Evan You + * Vue.js v1.0.14 + * (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(); + typeof define === 'function' && define.amd ? define(factory) : + global.Vue = factory(); }(this, function (){ 'use strict'; @@ -33,6 +34,7 @@ vm._digest(); } } + return val; } /** @@ -567,6 +569,7 @@ var str; var dir; var c; + var prev; var i; var l; var lastFilterIndex; @@ -653,13 +656,14 @@ dir = {}; for (i = 0, l = str.length; i < l; i++) { + prev = c; c = str.charCodeAt(i); if (inSingle) { // check single quote - if (c === 0x27) inSingle = !inSingle; + if (c === 0x27 && prev !== 0x5C) inSingle = !inSingle; } else if (inDouble) { // check double quote - if (c === 0x22) inDouble = !inDouble; + 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) { @@ -806,16 +810,17 @@ * into one single expression as '"a " + b + " c"'. * * @param {Array} tokens + * @param {Vue} [vm] * @return {String} */ - function tokensToExp(tokens){ + function tokensToExp(tokens, vm){ if (tokens.length > 1) { return tokens.map(function (token){ - return formatToken(token); + return formatToken(token, vm); }).join('+'); } else { - return formatToken(tokens[0], true); + return formatToken(tokens[0], vm, true); } } @@ -823,12 +828,13 @@ * Format a single token. * * @param {Object} token - * @param {Boolean} single + * @param {Vue} [vm] + * @param {Boolean} [single] * @return {String} */ - function formatToken(token, single){ - return token.tag ? inlineFilters(token.value, single) : '"' + token.value + '"'; + function formatToken(token, vm, single){ + return token.tag ? token.oneTime && vm ? '"' + vm.$eval(token.value) + '"' : inlineFilters(token.value, single) : '"' + token.value + '"'; } /** @@ -1140,6 +1146,18 @@ 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 * @@ -1229,11 +1247,30 @@ el.removeEventListener(event, cb); } + /** + * 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 && !(el instanceof SVGElement)) { + el.className = cls; + } else { + el.setAttribute('class', cls); + } + } + /** * Add class with compatibility for IE & SVG * * @param {Element} el - * @param {Strong} cls + * @param {String} cls */ function addClass(el, cls){ @@ -1242,7 +1279,7 @@ } else { var cur = ' ' + (el.getAttribute('class') || '') + ' '; if (cur.indexOf(' ' + cls + ' ') < 0) { - el.setAttribute('class', (cur + cls).trim()); + setClass(el, (cur + cls).trim()); } } } @@ -1251,7 +1288,7 @@ * Remove class with compatibility for IE & SVG * * @param {Element} el - * @param {Strong} cls + * @param {String} cls */ function removeClass(el, cls){ @@ -1263,7 +1300,7 @@ while (cur.indexOf(tar) >= 0) { cur = cur.replace(tar, ' '); } - el.setAttribute('class', cur.trim()); + setClass(el, cur.trim()); } if (!el.className) { el.removeAttribute('class'); @@ -1423,6 +1460,7 @@ } 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)$/; + var reservedTagRE = /^(slot|partial|component)$/; /** * Check if an element is a component, if yes return its @@ -1436,7 +1474,7 @@ function checkComponentAttr(el, options){ var tag = el.tagName.toLowerCase(); var hasAttrs = el.hasAttributes(); - if (!commonTagRE.test(tag) && tag !== 'component') { + if (!commonTagRE.test(tag) && !reservedTagRE.test(tag)) { if (resolveAsset(options, 'components', tag)) { return { id: tag }; } else { @@ -1487,6 +1525,7 @@ function initProp(vm, prop, value){ var key = prop.path; + value = coerceProp(prop, value); vm[key] = vm._data[key] = assertProp(prop, value) ? value : undefined; } @@ -1544,6 +1583,23 @@ return true; } + /** + * Force parsing value with coerce option. + * + * @param {*} value + * @param {Object} options + * @return {*} + */ + + function coerceProp(prop, value){ + var coerce = prop.options.coerce; + if (!coerce) { + return value; + } + // coerce is a function + return coerce(value); + } + function formatType(val){ return val ? val.charAt(0).toUpperCase() + val.slice(1) : 'custom type'; } @@ -1729,8 +1785,8 @@ var ids = Object.keys(components); for (var i = 0, l = ids.length; i < l; i++) { var key = ids[i]; - if (commonTagRE.test(key)) { - 'development' !== 'production' && warn('Do not use built-in HTML elements as component ' + 'id: ' + key); + if (commonTagRE.test(key) || reservedTagRE.test(key)) { + 'development' !== 'production' && warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + key); continue; } def = components[key]; @@ -1919,7 +1975,7 @@ def(arrayProto, '$set', function $set(index, val){ if (index >= this.length) { - this.length = index + 1; + this.length = Number(index) + 1; } return this.splice(index, 1, val)[0]; }); @@ -2035,8 +2091,7 @@ Observer.prototype.walk = function (obj){ var keys = Object.keys(obj); - var i = keys.length; - while (i--) { + for (var i = 0, l = keys.length; i < l; i++) { this.convert(keys[i], obj[keys[i]]); } }; @@ -2048,8 +2103,7 @@ */ Observer.prototype.observeArray = function (items){ - var i = items.length; - while (i--) { + for (var i = 0, l = items.length; i < l; i++) { observe(items[i]); } }; @@ -2113,10 +2167,8 @@ */ function copyAugment(target, src, keys){ - var i = keys.length; - var key; - while (i--) { - key = keys[i]; + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; def(target, key, src[key]); } } @@ -2139,7 +2191,7 @@ var ob; if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__; - } else if ((isArray(value) || isPlainObject(value)) && !Object.isFrozen(value) && !value._isVue) { + } else if ((isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue) { ob = new Observer(value); } if (ob && vm) { @@ -2244,6 +2296,7 @@ inDoc: inDoc, getAttr: getAttr, getBindAttr: getBindAttr, + hasBindAttr: hasBindAttr, before: before, after: after, remove: remove, @@ -2251,6 +2304,7 @@ replace: replace, on: on$1, off: off, + setClass: setClass, addClass: addClass, removeClass: removeClass, extractContent: extractContent, @@ -2266,7 +2320,9 @@ checkComponentAttr: checkComponentAttr, initProp: initProp, assertProp: assertProp, + coerceProp: coerceProp, commonTagRE: commonTagRE, + reservedTagRE: reservedTagRE, get warn(){ return warn; } }); @@ -2717,11 +2773,11 @@ var wsRE = /\s/g; var newlineRE = /\n/g; - var saveRE = /[\{,]\s*[\w\$_]+\s*:|('[^']*'|"[^"]*")|new |typeof |void /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 pathReplaceRE = /[^\w$\.]([A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\])*)/g; - var booleanLiteralRE = /^(true|false)$/; + var pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/; + var identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g; + var booleanLiteralRE = /^(?:true|false)$/; /** * Save / Rewrite / Restore @@ -2804,7 +2860,7 @@ var body = exp.replace(saveRE, save).replace(wsRE, ''); // rewrite all paths // pad 1 space here becaue the regex matches 1 extra char - body = (' ' + body).replace(pathReplaceRE, rewrite).replace(restoreRE, restore); + body = (' ' + body).replace(identRE, rewrite).replace(restoreRE, restore); return makeGetterFn(body); } @@ -3194,11 +3250,11 @@ if (this.active) { var value = this.get(); if (value !== this.value || - // Deep watchers and Array watchers should fire even + // 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). - (isArray(value) || this.deep) && !this.shallow) { + (isObject(value) || this.deep) && !this.shallow) { // set new value var oldValue = this.value; this.value = value; @@ -3296,7 +3352,7 @@ var cloak = { bind: function bind(){ var el = this.el; - this.vm.$once('hook:compiled', function (){ + this.vm.$once('pre-hook:compiled', function (){ el.removeAttribute('v-cloak'); }); } @@ -3308,9 +3364,20 @@ } }; + var ON = 700; + var MODEL = 800; + var BIND = 850; + var TRANSITION = 1100; + var EL = 1500; + var COMPONENT = 1500; + var PARTIAL = 1750; + var SLOT = 1750; + var FOR = 2000; + var IF = 2000; + var el = { - priority: 1500, + priority: EL, bind: function bind(){ /* istanbul ignore if */ @@ -3444,13 +3511,12 @@ var xlinkNS = 'http://www.w3.org/1999/xlink'; var xlinkRE = /^xlink:/; - // these input element attributes should also set their - // corresponding properties - var inputProps = { - value: 1, - checked: 1, - selected: 1 - }; + // 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 should set a hidden property for // binding v-model to object values @@ -3460,12 +3526,9 @@ 'false-value': '_falseValue' }; - // check for attributes that prohibit interpolations - var disallowedInterpAttrRE = /^v-|^:|^@|^(is|transition|transition-mode|debounce|track-by|stagger|enter-stagger|leave-stagger)$/; - var bind = { - priority: 850, + priority: BIND, bind: function bind(){ var attr = this.arg; @@ -3475,17 +3538,24 @@ this.deep = true; } // handle interpolation bindings - if (this.descriptor.interp) { + 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 + '="' + this.descriptor.raw + '": ' + 'attribute interpolation is not allowed in Vue.js ' + 'directives and special attributes.'); + 'development' !== 'production' && warn(attr + '="' + descriptor.raw + '": ' + 'attribute interpolation is not allowed in Vue.js ' + 'directives and special attributes.'); this.el.removeAttribute(attr); this.invalid = true; } /* istanbul ignore if */ if ('development' !== 'production') { - var raw = attr + '="' + this.descriptor.raw + '": '; + 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.'); @@ -3515,34 +3585,43 @@ handleObject: style.handleObject, handleSingle: function handleSingle(attr, value){ - if (inputProps[attr] && attr in this.el) { - this.el[attr] = attr === 'value' ? value || '' : // IE9 will set input.value to "null" for null... - value; + var el = this.el; + var interp = this.descriptor.interp; + if (!interp && attrWithPropsRE.test(attr) && attr in el) { + el[attr] = attr === 'value' ? value == null // IE9 will set input.value to "null" for null... + ? '' : value : value; } // set model props var modelProp = modelProps[attr]; - if (modelProp) { - this.el[modelProp] = value; + if (!interp && modelProp) { + el[modelProp] = value; // update v-model if present - var model = this.el.__v_model; + var model = el.__v_model; if (model) { model.listener(); } } // do not set value attribute for textarea - if (attr === 'value' && this.el.tagName === 'TEXTAREA') { - this.el.removeAttribute(attr); + if (attr === 'value' && el.tagName === 'TEXTAREA') { + el.removeAttribute(attr); return; } // update attribute if (value != null && value !== false) { - if (xlinkRE.test(attr)) { - this.el.setAttributeNS(xlinkNS, attr, value); + 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); } else { - this.el.setAttribute(attr, value); + el.setAttribute(attr, value); } } else { - this.el.removeAttribute(attr); + el.removeAttribute(attr); } } }; @@ -3598,7 +3677,7 @@ var on = { acceptStatement: true, - priority: 700, + priority: ON, bind: function bind(){ // deal with iframes @@ -3698,7 +3777,7 @@ }; this.on('change', this.listener); - if (el.checked) { + if (el.hasAttribute('checked')) { this.afterBind = this.listener; } }, @@ -3844,7 +3923,7 @@ }; this.on('change', this.listener); - if (el.checked) { + if (el.hasAttribute('checked')) { this.afterBind = this.listener; } }, @@ -3892,19 +3971,24 @@ // prevent messing with the input when user is typing, // and force update on blur. this.focused = false; - if (!isRange) { + if (!isRange && !lazy) { this.on('focus', function (){ self.focused = true; }); this.on('blur', function (){ self.focused = false; - self.listener(); + // do not sync value after fragment removal (#2017) + if (!self._frag || self._frag.inserted) { + self.rawListener(); + } }); } // Now attach the main listener - this.listener = function (){ - if (composing) return; + 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 @@ -3984,7 +4068,7 @@ var model = { - priority: 800, + priority: MODEL, twoWay: true, handlers: handlers, params: ['lazy', 'number', 'debounce'], @@ -4068,9 +4152,14 @@ }, apply: function apply(el, value){ - applyTransition(el, value ? 1 : -1, function (){ + if (inDoc(el)) { + applyTransition(el, value ? 1 : -1, toggle, this.vm); + } else { + toggle(); + } + function toggle(){ el.style.display = value ? '' : 'none'; - }, this.vm); + } } }; @@ -4105,7 +4194,7 @@ } var tagRE$1 = /<([\w:]+)/; - var entityRE = /&\w+;|&#\d+;|&#x[\dA-F]+;/; + var entityRE = /&#?\w+?;/; /** * Convert a string template to a DocumentFragment. @@ -4374,23 +4463,12 @@ Fragment.prototype.callHook = function (hook){ var i, l; - for (i = 0, l = this.children.length; i < l; i++) { - hook(this.children[i]); - } for (i = 0, l = this.childFrags.length; i < l; i++) { this.childFrags[i].callHook(hook); } - }; - - /** - * Destroy the fragment. - */ - - Fragment.prototype.destroy = function (){ - if (this.parentFrag) { - this.parentFrag.childFrags.$remove(this); + for (i = 0, l = this.children.length; i < l; i++) { + hook(this.children[i]); } - this.unlink(); }; /** @@ -4417,7 +4495,7 @@ this.inserted = false; var shouldCallRemove = inDoc(this.node); var self = this; - self.callHook(destroyChild); + this.beforeRemove(); removeWithTransition(this.node, this.vm, function (){ if (shouldCallRemove) { self.callHook(detach); @@ -4453,7 +4531,7 @@ this.inserted = false; var self = this; var shouldCallRemove = inDoc(this.node); - self.callHook(destroyChild); + this.beforeRemove(); removeNodeRange(this.node, this.end, this.vm, this.frag, function (){ if (shouldCallRemove) { self.callHook(detach); @@ -4462,6 +4540,45 @@ }); } + /** + * 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.unlink(); + }; + /** * Call attach hook for a Vue instance. * @@ -4474,20 +4591,6 @@ } } - /** - * 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. - * - * @param {Vue} child - */ - - function destroyChild(child){ - child.$destroy(false, true); - } - /** * Call detach hook for a Vue instance. * @@ -4551,7 +4654,7 @@ var vIf = { - priority: 2000, + priority: IF, bind: function bind(){ var el = this.el; @@ -4614,7 +4717,7 @@ var vFor = { - priority: 2000, + priority: FOR, params: ['track-by', 'stagger', 'enter-stagger', 'leave-stagger'], @@ -5268,8 +5371,8 @@ return f; } - var TYPE_TRANSITION = 1; - var TYPE_ANIMATION = 2; + var TYPE_TRANSITION = 'transition'; + var TYPE_ANIMATION = 'animation'; var transDurationProp = transitionProp + 'Duration'; var animDurationProp = animationProp + 'Duration'; @@ -5285,8 +5388,8 @@ function Transition(el, id, hooks, vm){ this.id = id; this.el = el; - this.enterClass = id + '-enter'; - this.leaveClass = id + '-leave'; + this.enterClass = hooks && hooks.enterClass || id + '-enter'; + this.leaveClass = hooks && hooks.leaveClass || id + '-leave'; this.hooks = hooks; this.vm = vm; // async state @@ -5294,6 +5397,14 @@ 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); + } + } // bind var self = this; ['enterNextTick', 'enterDone', 'leaveNextTick', 'leaveDone'].forEach(function (m){ @@ -5554,7 +5665,7 @@ isHidden(this.el)) { return; } - var type = this.typeCache[className]; + var type = this.type || this.typeCache[className]; if (type) return type; var inlineStyles = this.el.style; var computedStyles = window.getComputedStyle(this.el); @@ -5610,7 +5721,7 @@ var transition = { - priority: 1100, + priority: TRANSITION, update: function update(id, oldId){ var el = this.el; @@ -5641,6 +5752,7 @@ var twoWay = prop.mode === bindingModes.TWO_WAY; var parentWatcher = this.parentWatcher = new Watcher(parent, parentKey, function (val){ + val = coerceProp(prop, val); if (assertProp(prop, val)) { child[childKey] = val; } @@ -5660,7 +5772,7 @@ // important: defer the child watcher creation until // the created hook (after data observation) var self = this; - child.$once('hook:created', function (){ + child.$once('pre-hook:created', function (){ self.childWatcher = new Watcher(child, childKey, function (val){ parentWatcher.set(val); }, { @@ -5683,7 +5795,7 @@ var component = { - priority: 1500, + priority: COMPONENT, params: ['keep-alive', 'transition-mode', 'inline-template'], @@ -5808,6 +5920,9 @@ if (activateHook && !cached) { this.waitingFor = newComponent; activateHook.call(newComponent, function (){ + if (self.waitingFor !== newComponent) { + return; + } self.waitingFor = null; self.transition(newComponent, cb); }); @@ -6092,7 +6207,7 @@ var empty = {}; // regexes - var identRE = /^[$_a-zA-Z]+[\w$]*$/; + var identRE$1 = /^[$_a-zA-Z]+[\w$]*$/; var settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/; /** @@ -6122,7 +6237,7 @@ // interpreted as minus calculations by the parser // so we need to camelize the path here path = camelize(name); - if (!identRE.test(path)) { + if (!identRE$1.test(path)) { 'development' !== 'production' && warn('Invalid prop key: "' + name + '". Prop keys ' + 'must be valid identifiers.'); continue; } @@ -6151,7 +6266,7 @@ value = parsed.expression; prop.filters = parsed.filters; // check binding type - if (isLiteral(value)) { + 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. @@ -6370,12 +6485,16 @@ */ function makeUnlinkFn(vm, dirs, context, contextDirs){ - return function unlink(destroying){ + function unlink(destroying){ teardownDirs(vm, dirs, destroying); if (context && contextDirs) { teardownDirs(context, contextDirs); } - }; + } + + // expose linked directives + unlink.dirs = dirs; + return unlink; } /** @@ -6471,6 +6590,7 @@ } } + options._containerAttrs = options._replacerAttrs = null; return function rootLinkFn(vm, el, scope){ // link context scope dirs var context = vm._context; @@ -6731,6 +6851,11 @@ function checkElementDirectives(el, options){ var tag = el.tagName.toLowerCase(); if (commonTagRE.test(tag)) return; + // special case: give named slot a higher priority + // than unnamed slots + if (tag === 'slot' && hasBindAttr(el, 'name')) { + tag = '_namedSlot'; + } var def = resolveAsset(options, 'elementDirectives', tag); if (def) { return makeTerminalNodeLinkFn(el, tag, '', options, def); @@ -6794,11 +6919,10 @@ var value, dirName; for (var i = 0, l = terminalDirectives.length; i < l; i++) { dirName = terminalDirectives[i]; - /* eslint-disable no-cond-assign */ - if (value = el.getAttribute('v-' + dirName)) { + value = el.getAttribute('v-' + dirName); + if (value != null) { return makeTerminalNodeLinkFn(el, dirName, value, options); } - /* eslint-enable no-cond-assign */ } } @@ -6871,7 +6995,7 @@ if (tokens) { value = tokensToExp(tokens); arg = name; - pushDir('bind', publicDirectives.bind, true); + pushDir('bind', publicDirectives.bind, tokens); // warn against mixing mustaches with v-bind if ('development' !== 'production') { if (name === 'class' && Array.prototype.some.call(attrs, function (attr){ @@ -6937,11 +7061,12 @@ * * @param {String} dirName * @param {Object|Function} def - * @param {Boolean} [interp] + * @param {Array} [interpTokens] */ - function pushDir(dirName, def, interp){ - var parsed = parseDirective(value); + function pushDir(dirName, def, interpTokens){ + var hasOneTimeToken = interpTokens && hasOneTime(interpTokens); + var parsed = !hasOneTimeToken && parseDirective(value); dirs.push({ name: dirName, attr: rawName, @@ -6949,9 +7074,13 @@ def: def, arg: arg, modifiers: modifiers, - expression: parsed.expression, - filters: parsed.filters, - interp: interp + // 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 }); } @@ -6996,6 +7125,20 @@ }; } + /** + * 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; + } + } + var specialCharRE = /[^\w\-:\.]/; /** @@ -7072,7 +7215,7 @@ // non-element template replacer.nodeType !== 1 || // single nested component - tag === 'component' || resolveAsset(options, 'components', tag) || replacer.hasAttribute('is') || replacer.hasAttribute(':is') || replacer.hasAttribute('v-bind:is') || + tag === 'component' || resolveAsset(options, 'components', tag) || hasBindAttr(replacer, 'is') || // element directive resolveAsset(options, 'elementDirectives', tag) || // for block @@ -7125,7 +7268,7 @@ value = attrs[i].value; if (!to.hasAttribute(name) && !specialCharRE.test(name)) { to.setAttribute(name, value); - } else if (name === 'class') { + } else if (name === 'class' && !parseText(value)) { value.split(/\s+/).forEach(function (cls){ addClass(to, cls); }); @@ -7137,6 +7280,7 @@ compile: compile, compileAndLinkProps: compileAndLinkProps, compileRoot: compileRoot, + terminalDirectives: terminalDirectives, transclude: transclude }); @@ -7416,6 +7560,7 @@ if (eventRE.test(name)) { name = name.replace(eventRE, ''); handler = (vm._scope || vm._context).$eval(attrs[i].value, true); + handler._fromParent = true; vm.$on(name.replace(eventRE), handler); } } @@ -7533,6 +7678,7 @@ */ 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++) { @@ -7627,6 +7773,7 @@ if (this.bind) { this.bind(); } + this._bound = true; if (this.literal) { this.update && this.update(descriptor.raw); @@ -7662,7 +7809,6 @@ this.update(watcher.value); } } - this._bound = true; }; /** @@ -7719,7 +7865,8 @@ called = true; } }, { - immediate: true + immediate: true, + user: false }); (this._paramUnwatchFns || (this._paramUnwatchFns = [])).push(unwatch); }; @@ -7885,6 +8032,11 @@ 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; @@ -7982,6 +8134,30 @@ } 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; @@ -8015,15 +8191,9 @@ if (this.$el) { this.$el.__vue__ = null; } - // remove DOM element - var self = this; - if (remove && this.$el) { - this.$remove(function (){ - self._cleanup(); - }); - } else if (!deferCleanup) { - this._cleanup(); - } + + destroyReady = true; + cleanupIfPossible(); }; /** @@ -8204,6 +8374,12 @@ 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); + name = null; + } + } var Sub = createClass(name || 'VueComponent'); Sub.prototype = Object.create(Super.prototype); Sub.prototype.constructor = Sub; @@ -8288,8 +8464,8 @@ } else { /* istanbul ignore if */ if ('development' !== 'production') { - if (type === 'component' && commonTagRE.test(id)) { - warn('Do not use built-in HTML elements as component ' + 'id: ' + id); + 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)) { @@ -8321,7 +8497,10 @@ if (asStatement && !isSimplePath(exp)) { var self = this; return function statementHandler(){ - res.get.call(self, self); + self.$arguments = toArray(arguments); + var result = res.get.call(self, self); + self.$arguments = null; + return result; }; } else { try { @@ -8378,7 +8557,9 @@ } var watcher = new Watcher(vm, expOrFn, cb, { deep: options && options.deep, - filters: parsed && parsed.filters + sync: options && options.sync, + filters: parsed && parsed.filters, + user: !options || options.user !== false }); if (options && options.immediate) { cb.call(vm, watcher.value); @@ -8741,19 +8922,32 @@ /** * Trigger an event on self. * - * @param {String} event + * @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 = !cbs; + 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 , + // 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 res = cbs[i].apply(this, args); - if (res === true) { + var cb = cbs[i]; + var res = cb.apply(this, args); + if (res === true && (!hasParentCbs || cb._fromParent)) { shouldPropagate = true; } } @@ -8764,20 +8958,28 @@ /** * Recursively broadcast an event to all children instances. * - * @param {String} event + * @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, arguments); + var shouldPropagate = child.$emit.apply(child, args); if (shouldPropagate) { - child.$broadcast.apply(child, arguments); + child.$broadcast.apply(child, args); } } return this; @@ -8790,11 +8992,16 @@ * @param {...*} additional arguments */ - Vue.prototype.$dispatch = function (){ - this.$emit.apply(this, 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) { - var shouldPropagate = parent.$emit.apply(parent, arguments); + shouldPropagate = parent.$emit.apply(parent, args); parent = shouldPropagate ? parent.$parent : null; } return this; @@ -8932,6 +9139,7 @@ function limitBy(arr, n, offset){ offset = offset ? parseInt(offset, 10) : 0; + n = toNumber(n); return typeof n === 'number' ? arr.slice(offset, offset + n) : arr; } @@ -9145,7 +9353,7 @@ var partial = { - priority: 1750, + priority: PARTIAL, params: ['name'], @@ -9188,55 +9396,46 @@ // instance being stored as `$options._content` during // the transclude phase. + // We are exporting two versions, one for named and one + // for unnamed, because the unnamed slots must be compiled + // AFTER all named slots have selected their content. So + // we need to give them different priorities in the compilation + // process. (See #1965) + var slot = { - priority: 1750, - - params: ['name'], + priority: SLOT, bind: function bind(){ var host = this.vm; var raw = host.$options._content; - var content; if (!raw) { this.fallback(); return; } var context = host._context; - var slotName = this.params.name; + var slotName = this.params && this.params.name; if (!slotName) { - // Default content - var self = this; - var compileDefaultContent = function compileDefaultContent(){ - self.compile(extractFragment(raw.childNodes, raw, true), context, host); - }; - if (!host._isCompiled) { - // defer until the end of instance compilation, - // because the default outlet must wait until all - // other possible outlets with selectors have picked - // out their contents. - host.$once('hook:compiled', compileDefaultContent); - } else { - compileDefaultContent(); - } + // Default slot + this.tryCompile(extractFragment(raw.childNodes, raw, true), context, host); } else { + // Named slot var selector = '[slot="' + slotName + '"]'; var nodes = raw.querySelectorAll(selector); if (nodes.length) { - content = extractFragment(nodes, raw); - if (content.hasChildNodes()) { - this.compile(content, context, host); - } else { - this.fallback(); - } + this.tryCompile(extractFragment(nodes, raw), context, host); } else { this.fallback(); } } }, - fallback: function fallback(){ - this.compile(extractContent(this.el, true), this.vm); + tryCompile: function tryCompile(content, context, host){ + if (content.hasChildNodes()) { + this.compile(content, context, host); + } else { + this.fallback(); + } }, compile: function compile(content, context, host){ @@ -9251,6 +9450,10 @@ } }, + fallback: function fallback(){ + this.compile(extractContent(this.el, true), this.vm); + }, + unbind: function unbind(){ if (this.unlink) { this.unlink(); @@ -9258,6 +9461,11 @@ } }; + var namedSlot = extend(extend({}, slot), { + priority: slot.priority + 1, + params: ['name'] + }); + /** * Extract qualified content nodes from a node list. * @@ -9297,10 +9505,11 @@ var elementDirectives = { slot: slot, + _namedSlot: namedSlot, // same as slot but with higher priority partial: partial }; - Vue.version = '1.0.10'; + Vue.version = '1.0.14'; /** * Vue and every constructor that extends Vue has an @@ -9323,9 +9532,11 @@ // devtools global hook /* istanbul ignore if */ - if ('development' !== 'production') { - if (inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__) { + if ('development' !== 'production' && inBrowser) { + if (window.__VUE_DEVTOOLS_GLOBAL_HOOK__) { window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit('init', Vue); + } else if (/Chrome\/\d+/.test(navigator.userAgent)) { + console.log('Download the Vue Devtools for a better development experience:\n' + 'https://github.com/vuejs/vue-devtools'); } }