or
*
* - dynamic:
*
*/
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 {
("production") !== '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.
*/
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 (false) {
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 (false) {
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)) {
("production") !== '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 (false) {
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 (false) {
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 (false) {
// 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);
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)) {
("production") !== '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 (false) {
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)) {
("production") !== 'production' && warn('Invalid prop: custom validator check failed for prop "' + prop.name + '".', vm);
return false;
}
}
return true;
}
/**
* Force parsing value with coerce option.
*
* @param {*} value
* @param {Object} options
* @return {*}
*/
function coerceProp(prop, value) {
var coerce = prop.options.coerce;
if (!coerce) {
return value;
}
// coerce is a function
return coerce(value);
}
/**
* Assert the type of a value
*
* @param {*} value
* @param {Function} type
* @return {Object}
*/
function assertType(value, type) {
var valid;
var expectedType;
if (type === String) {
expectedType = 'string';
valid = typeof value === expectedType;
} else if (type === Number) {
expectedType = 'number';
valid = typeof value === expectedType;
} else if (type === Boolean) {
expectedType = 'boolean';
valid = typeof value === expectedType;
} else if (type === Function) {
expectedType = 'function';
valid = typeof value === expectedType;
} else if (type === Object) {
expectedType = 'object';
valid = isPlainObject(value);
} else if (type === Array) {
expectedType = 'array';
valid = isArray(value);
} else {
valid = value instanceof type;
}
return {
valid: valid,
expectedType: expectedType
};
}
/**
* Format type for output
*
* @param {String} type
* @return {String}
*/
function formatType(type) {
return type ? type.charAt(0).toUpperCase() + type.slice(1) : 'custom type';
}
/**
* Format value
*
* @param {*} value
* @return {String}
*/
function formatValue(val) {
return Object.prototype.toString.call(val).slice(8, -1);
}
var bindingModes = config._propBindingModes;
var propDef = {
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 (false) {
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';
el.__v_trans = new Transition(el, id, hooks, this.vm);
if (oldId) {
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 (true) {
// reset directives before every capture in production
// mode, so that when unlinking we don't need to splice
// them out (which turns out to be a perf hit).
// they are kept in development mode because they are
// useful for Vue's own tests.
vm._directives = [];
}
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 (false) {
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 (false) {
// 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