diff options
Diffstat (limited to 'node_modules/vue/src/platforms/web/runtime/modules')
7 files changed, 799 insertions, 0 deletions
diff --git a/node_modules/vue/src/platforms/web/runtime/modules/attrs.js b/node_modules/vue/src/platforms/web/runtime/modules/attrs.js new file mode 100644 index 00000000..78ef28a1 --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/modules/attrs.js @@ -0,0 +1,118 @@ +/* @flow */ + +import { isIE, isIE9, isEdge } from 'core/util/env' + +import { + extend, + isDef, + isUndef +} from 'shared/util' + +import { + isXlink, + xlinkNS, + getXlinkProp, + isBooleanAttr, + isEnumeratedAttr, + isFalsyAttrValue +} from 'web/util/index' + +function updateAttrs (oldVnode: VNodeWithData, vnode: VNodeWithData) { + const opts = vnode.componentOptions + if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) { + return + } + if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) { + return + } + let key, cur, old + const elm = vnode.elm + const oldAttrs = oldVnode.data.attrs || {} + let attrs: any = vnode.data.attrs || {} + // clone observed objects, as the user probably wants to mutate it + if (isDef(attrs.__ob__)) { + attrs = vnode.data.attrs = extend({}, attrs) + } + + for (key in attrs) { + cur = attrs[key] + old = oldAttrs[key] + if (old !== cur) { + setAttr(elm, key, cur) + } + } + // #4391: in IE9, setting type can reset value for input[type=radio] + // #6666: IE/Edge forces progress value down to 1 before setting a max + /* istanbul ignore if */ + if ((isIE || isEdge) && attrs.value !== oldAttrs.value) { + setAttr(elm, 'value', attrs.value) + } + for (key in oldAttrs) { + if (isUndef(attrs[key])) { + if (isXlink(key)) { + elm.removeAttributeNS(xlinkNS, getXlinkProp(key)) + } else if (!isEnumeratedAttr(key)) { + elm.removeAttribute(key) + } + } + } +} + +function setAttr (el: Element, key: string, value: any) { + if (el.tagName.indexOf('-') > -1) { + baseSetAttr(el, key, value) + } else if (isBooleanAttr(key)) { + // set attribute for blank value + // e.g. <option disabled>Select one</option> + if (isFalsyAttrValue(value)) { + el.removeAttribute(key) + } else { + // technically allowfullscreen is a boolean attribute for <iframe>, + // but Flash expects a value of "true" when used on <embed> tag + value = key === 'allowfullscreen' && el.tagName === 'EMBED' + ? 'true' + : key + el.setAttribute(key, value) + } + } else if (isEnumeratedAttr(key)) { + el.setAttribute(key, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true') + } else if (isXlink(key)) { + if (isFalsyAttrValue(value)) { + el.removeAttributeNS(xlinkNS, getXlinkProp(key)) + } else { + el.setAttributeNS(xlinkNS, key, value) + } + } else { + baseSetAttr(el, key, value) + } +} + +function baseSetAttr (el, key, value) { + if (isFalsyAttrValue(value)) { + el.removeAttribute(key) + } else { + // #7138: IE10 & 11 fires input event when setting placeholder on + // <textarea>... block the first input event and remove the blocker + // immediately. + /* istanbul ignore if */ + if ( + isIE && !isIE9 && + el.tagName === 'TEXTAREA' && + key === 'placeholder' && !el.__ieph + ) { + const blocker = e => { + e.stopImmediatePropagation() + el.removeEventListener('input', blocker) + } + el.addEventListener('input', blocker) + // $flow-disable-line + el.__ieph = true /* IE placeholder patched */ + } + el.setAttribute(key, value) + } +} + +export default { + create: updateAttrs, + update: updateAttrs +} diff --git a/node_modules/vue/src/platforms/web/runtime/modules/class.js b/node_modules/vue/src/platforms/web/runtime/modules/class.js new file mode 100644 index 00000000..29fd2c11 --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/modules/class.js @@ -0,0 +1,48 @@ +/* @flow */ + +import { + isDef, + isUndef +} from 'shared/util' + +import { + concat, + stringifyClass, + genClassForVnode +} from 'web/util/index' + +function updateClass (oldVnode: any, vnode: any) { + const el = vnode.elm + const data: VNodeData = vnode.data + const oldData: VNodeData = oldVnode.data + if ( + isUndef(data.staticClass) && + isUndef(data.class) && ( + isUndef(oldData) || ( + isUndef(oldData.staticClass) && + isUndef(oldData.class) + ) + ) + ) { + return + } + + let cls = genClassForVnode(vnode) + + // handle transition classes + const transitionClass = el._transitionClasses + if (isDef(transitionClass)) { + cls = concat(cls, stringifyClass(transitionClass)) + } + + // set the class + if (cls !== el._prevClass) { + el.setAttribute('class', cls) + el._prevClass = cls + } +} + +export default { + create: updateClass, + update: updateClass +} diff --git a/node_modules/vue/src/platforms/web/runtime/modules/dom-props.js b/node_modules/vue/src/platforms/web/runtime/modules/dom-props.js new file mode 100644 index 00000000..f3888aed --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/modules/dom-props.js @@ -0,0 +1,95 @@ +/* @flow */ + +import { isDef, isUndef, extend, toNumber } from 'shared/util' + +function updateDOMProps (oldVnode: VNodeWithData, vnode: VNodeWithData) { + if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) { + return + } + let key, cur + const elm: any = vnode.elm + const oldProps = oldVnode.data.domProps || {} + let props = vnode.data.domProps || {} + // clone observed objects, as the user probably wants to mutate it + if (isDef(props.__ob__)) { + props = vnode.data.domProps = extend({}, props) + } + + for (key in oldProps) { + if (isUndef(props[key])) { + elm[key] = '' + } + } + for (key in props) { + cur = props[key] + // ignore children if the node has textContent or innerHTML, + // as these will throw away existing DOM nodes and cause removal errors + // on subsequent patches (#3360) + if (key === 'textContent' || key === 'innerHTML') { + if (vnode.children) vnode.children.length = 0 + if (cur === oldProps[key]) continue + // #6601 work around Chrome version <= 55 bug where single textNode + // replaced by innerHTML/textContent retains its parentNode property + if (elm.childNodes.length === 1) { + elm.removeChild(elm.childNodes[0]) + } + } + + if (key === 'value') { + // store value as _value as well since + // non-string values will be stringified + elm._value = cur + // avoid resetting cursor position when value is the same + const strCur = isUndef(cur) ? '' : String(cur) + if (shouldUpdateValue(elm, strCur)) { + elm.value = strCur + } + } else { + elm[key] = cur + } + } +} + +// check platforms/web/util/attrs.js acceptValue +type acceptValueElm = HTMLInputElement | HTMLSelectElement | HTMLOptionElement; + +function shouldUpdateValue (elm: acceptValueElm, checkVal: string): boolean { + return (!elm.composing && ( + elm.tagName === 'OPTION' || + isNotInFocusAndDirty(elm, checkVal) || + isDirtyWithModifiers(elm, checkVal) + )) +} + +function isNotInFocusAndDirty (elm: acceptValueElm, checkVal: string): boolean { + // return true when textbox (.number and .trim) loses focus and its value is + // not equal to the updated value + let notInFocus = true + // #6157 + // work around IE bug when accessing document.activeElement in an iframe + try { notInFocus = document.activeElement !== elm } catch (e) {} + return notInFocus && elm.value !== checkVal +} + +function isDirtyWithModifiers (elm: any, newVal: string): boolean { + const value = elm.value + const modifiers = elm._vModifiers // injected by v-model runtime + if (isDef(modifiers)) { + if (modifiers.lazy) { + // inputs with lazy should only be updated when not in focus + return false + } + if (modifiers.number) { + return toNumber(value) !== toNumber(newVal) + } + if (modifiers.trim) { + return value.trim() !== newVal.trim() + } + } + return value !== newVal +} + +export default { + create: updateDOMProps, + update: updateDOMProps +} diff --git a/node_modules/vue/src/platforms/web/runtime/modules/events.js b/node_modules/vue/src/platforms/web/runtime/modules/events.js new file mode 100644 index 00000000..1e71b667 --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/modules/events.js @@ -0,0 +1,87 @@ +/* @flow */ + +import { isDef, isUndef } from 'shared/util' +import { updateListeners } from 'core/vdom/helpers/index' +import { withMacroTask, isIE, supportsPassive } from 'core/util/index' +import { RANGE_TOKEN, CHECKBOX_RADIO_TOKEN } from 'web/compiler/directives/model' + +// normalize v-model event tokens that can only be determined at runtime. +// it's important to place the event as the first in the array because +// the whole point is ensuring the v-model callback gets called before +// user-attached handlers. +function normalizeEvents (on) { + /* istanbul ignore if */ + if (isDef(on[RANGE_TOKEN])) { + // IE input[type=range] only supports `change` event + const event = isIE ? 'change' : 'input' + on[event] = [].concat(on[RANGE_TOKEN], on[event] || []) + delete on[RANGE_TOKEN] + } + // This was originally intended to fix #4521 but no longer necessary + // after 2.5. Keeping it for backwards compat with generated code from < 2.4 + /* istanbul ignore if */ + if (isDef(on[CHECKBOX_RADIO_TOKEN])) { + on.change = [].concat(on[CHECKBOX_RADIO_TOKEN], on.change || []) + delete on[CHECKBOX_RADIO_TOKEN] + } +} + +let target: any + +function createOnceHandler (handler, event, capture) { + const _target = target // save current target element in closure + return function onceHandler () { + const res = handler.apply(null, arguments) + if (res !== null) { + remove(event, onceHandler, capture, _target) + } + } +} + +function add ( + event: string, + handler: Function, + once: boolean, + capture: boolean, + passive: boolean +) { + handler = withMacroTask(handler) + if (once) handler = createOnceHandler(handler, event, capture) + target.addEventListener( + event, + handler, + supportsPassive + ? { capture, passive } + : capture + ) +} + +function remove ( + event: string, + handler: Function, + capture: boolean, + _target?: HTMLElement +) { + (_target || target).removeEventListener( + event, + handler._withTask || handler, + capture + ) +} + +function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) { + if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) { + return + } + const on = vnode.data.on || {} + const oldOn = oldVnode.data.on || {} + target = vnode.elm + normalizeEvents(on) + updateListeners(on, oldOn, add, remove, vnode.context) + target = undefined +} + +export default { + create: updateDOMListeners, + update: updateDOMListeners +} diff --git a/node_modules/vue/src/platforms/web/runtime/modules/index.js b/node_modules/vue/src/platforms/web/runtime/modules/index.js new file mode 100644 index 00000000..9f6ad8f9 --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/modules/index.js @@ -0,0 +1,15 @@ +import attrs from './attrs' +import klass from './class' +import events from './events' +import domProps from './dom-props' +import style from './style' +import transition from './transition' + +export default [ + attrs, + klass, + events, + domProps, + style, + transition +] diff --git a/node_modules/vue/src/platforms/web/runtime/modules/style.js b/node_modules/vue/src/platforms/web/runtime/modules/style.js new file mode 100644 index 00000000..29e1c3c1 --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/modules/style.js @@ -0,0 +1,93 @@ +/* @flow */ + +import { getStyle, normalizeStyleBinding } from 'web/util/style' +import { cached, camelize, extend, isDef, isUndef } from 'shared/util' + +const cssVarRE = /^--/ +const importantRE = /\s*!important$/ +const setProp = (el, name, val) => { + /* istanbul ignore if */ + if (cssVarRE.test(name)) { + el.style.setProperty(name, val) + } else if (importantRE.test(val)) { + el.style.setProperty(name, val.replace(importantRE, ''), 'important') + } else { + const normalizedName = normalize(name) + if (Array.isArray(val)) { + // Support values array created by autoprefixer, e.g. + // {display: ["-webkit-box", "-ms-flexbox", "flex"]} + // Set them one by one, and the browser will only set those it can recognize + for (let i = 0, len = val.length; i < len; i++) { + el.style[normalizedName] = val[i] + } + } else { + el.style[normalizedName] = val + } + } +} + +const vendorNames = ['Webkit', 'Moz', 'ms'] + +let emptyStyle +const normalize = cached(function (prop) { + emptyStyle = emptyStyle || document.createElement('div').style + prop = camelize(prop) + if (prop !== 'filter' && (prop in emptyStyle)) { + return prop + } + const capName = prop.charAt(0).toUpperCase() + prop.slice(1) + for (let i = 0; i < vendorNames.length; i++) { + const name = vendorNames[i] + capName + if (name in emptyStyle) { + return name + } + } +}) + +function updateStyle (oldVnode: VNodeWithData, vnode: VNodeWithData) { + const data = vnode.data + const oldData = oldVnode.data + + if (isUndef(data.staticStyle) && isUndef(data.style) && + isUndef(oldData.staticStyle) && isUndef(oldData.style) + ) { + return + } + + let cur, name + const el: any = vnode.elm + const oldStaticStyle: any = oldData.staticStyle + const oldStyleBinding: any = oldData.normalizedStyle || oldData.style || {} + + // if static style exists, stylebinding already merged into it when doing normalizeStyleData + const oldStyle = oldStaticStyle || oldStyleBinding + + const style = normalizeStyleBinding(vnode.data.style) || {} + + // store normalized style under a different key for next diff + // make sure to clone it if it's reactive, since the user likely wants + // to mutate it. + vnode.data.normalizedStyle = isDef(style.__ob__) + ? extend({}, style) + : style + + const newStyle = getStyle(vnode, true) + + for (name in oldStyle) { + if (isUndef(newStyle[name])) { + setProp(el, name, '') + } + } + for (name in newStyle) { + cur = newStyle[name] + if (cur !== oldStyle[name]) { + // ie9 setting to null has no effect, must use empty string + setProp(el, name, cur == null ? '' : cur) + } + } +} + +export default { + create: updateStyle, + update: updateStyle +} diff --git a/node_modules/vue/src/platforms/web/runtime/modules/transition.js b/node_modules/vue/src/platforms/web/runtime/modules/transition.js new file mode 100644 index 00000000..0796b5da --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/modules/transition.js @@ -0,0 +1,343 @@ +/* @flow */ + +import { inBrowser, isIE9, warn } from 'core/util/index' +import { mergeVNodeHook } from 'core/vdom/helpers/index' +import { activeInstance } from 'core/instance/lifecycle' + +import { + once, + isDef, + isUndef, + isObject, + toNumber +} from 'shared/util' + +import { + nextFrame, + resolveTransition, + whenTransitionEnds, + addTransitionClass, + removeTransitionClass +} from '../transition-util' + +export function enter (vnode: VNodeWithData, toggleDisplay: ?() => void) { + const el: any = vnode.elm + + // call leave callback now + if (isDef(el._leaveCb)) { + el._leaveCb.cancelled = true + el._leaveCb() + } + + const data = resolveTransition(vnode.data.transition) + if (isUndef(data)) { + return + } + + /* istanbul ignore if */ + if (isDef(el._enterCb) || el.nodeType !== 1) { + return + } + + const { + css, + type, + enterClass, + enterToClass, + enterActiveClass, + appearClass, + appearToClass, + appearActiveClass, + beforeEnter, + enter, + afterEnter, + enterCancelled, + beforeAppear, + appear, + afterAppear, + appearCancelled, + duration + } = data + + // activeInstance will always be the <transition> component managing this + // transition. One edge case to check is when the <transition> is placed + // as the root node of a child component. In that case we need to check + // <transition>'s parent for appear check. + let context = activeInstance + let transitionNode = activeInstance.$vnode + while (transitionNode && transitionNode.parent) { + transitionNode = transitionNode.parent + context = transitionNode.context + } + + const isAppear = !context._isMounted || !vnode.isRootInsert + + if (isAppear && !appear && appear !== '') { + return + } + + const startClass = isAppear && appearClass + ? appearClass + : enterClass + const activeClass = isAppear && appearActiveClass + ? appearActiveClass + : enterActiveClass + const toClass = isAppear && appearToClass + ? appearToClass + : enterToClass + + const beforeEnterHook = isAppear + ? (beforeAppear || beforeEnter) + : beforeEnter + const enterHook = isAppear + ? (typeof appear === 'function' ? appear : enter) + : enter + const afterEnterHook = isAppear + ? (afterAppear || afterEnter) + : afterEnter + const enterCancelledHook = isAppear + ? (appearCancelled || enterCancelled) + : enterCancelled + + const explicitEnterDuration: any = toNumber( + isObject(duration) + ? duration.enter + : duration + ) + + if (process.env.NODE_ENV !== 'production' && explicitEnterDuration != null) { + checkDuration(explicitEnterDuration, 'enter', vnode) + } + + const expectsCSS = css !== false && !isIE9 + const userWantsControl = getHookArgumentsLength(enterHook) + + const cb = el._enterCb = once(() => { + if (expectsCSS) { + removeTransitionClass(el, toClass) + removeTransitionClass(el, activeClass) + } + if (cb.cancelled) { + if (expectsCSS) { + removeTransitionClass(el, startClass) + } + enterCancelledHook && enterCancelledHook(el) + } else { + afterEnterHook && afterEnterHook(el) + } + el._enterCb = null + }) + + if (!vnode.data.show) { + // remove pending leave element on enter by injecting an insert hook + mergeVNodeHook(vnode, 'insert', () => { + const parent = el.parentNode + const pendingNode = parent && parent._pending && parent._pending[vnode.key] + if (pendingNode && + pendingNode.tag === vnode.tag && + pendingNode.elm._leaveCb + ) { + pendingNode.elm._leaveCb() + } + enterHook && enterHook(el, cb) + }) + } + + // start enter transition + beforeEnterHook && beforeEnterHook(el) + if (expectsCSS) { + addTransitionClass(el, startClass) + addTransitionClass(el, activeClass) + nextFrame(() => { + removeTransitionClass(el, startClass) + if (!cb.cancelled) { + addTransitionClass(el, toClass) + if (!userWantsControl) { + if (isValidDuration(explicitEnterDuration)) { + setTimeout(cb, explicitEnterDuration) + } else { + whenTransitionEnds(el, type, cb) + } + } + } + }) + } + + if (vnode.data.show) { + toggleDisplay && toggleDisplay() + enterHook && enterHook(el, cb) + } + + if (!expectsCSS && !userWantsControl) { + cb() + } +} + +export function leave (vnode: VNodeWithData, rm: Function) { + const el: any = vnode.elm + + // call enter callback now + if (isDef(el._enterCb)) { + el._enterCb.cancelled = true + el._enterCb() + } + + const data = resolveTransition(vnode.data.transition) + if (isUndef(data) || el.nodeType !== 1) { + return rm() + } + + /* istanbul ignore if */ + if (isDef(el._leaveCb)) { + return + } + + const { + css, + type, + leaveClass, + leaveToClass, + leaveActiveClass, + beforeLeave, + leave, + afterLeave, + leaveCancelled, + delayLeave, + duration + } = data + + const expectsCSS = css !== false && !isIE9 + const userWantsControl = getHookArgumentsLength(leave) + + const explicitLeaveDuration: any = toNumber( + isObject(duration) + ? duration.leave + : duration + ) + + if (process.env.NODE_ENV !== 'production' && isDef(explicitLeaveDuration)) { + checkDuration(explicitLeaveDuration, 'leave', vnode) + } + + const cb = el._leaveCb = once(() => { + if (el.parentNode && el.parentNode._pending) { + el.parentNode._pending[vnode.key] = null + } + if (expectsCSS) { + removeTransitionClass(el, leaveToClass) + removeTransitionClass(el, leaveActiveClass) + } + if (cb.cancelled) { + if (expectsCSS) { + removeTransitionClass(el, leaveClass) + } + leaveCancelled && leaveCancelled(el) + } else { + rm() + afterLeave && afterLeave(el) + } + el._leaveCb = null + }) + + if (delayLeave) { + delayLeave(performLeave) + } else { + performLeave() + } + + function performLeave () { + // the delayed leave may have already been cancelled + if (cb.cancelled) { + return + } + // record leaving element + if (!vnode.data.show) { + (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key: any)] = vnode + } + beforeLeave && beforeLeave(el) + if (expectsCSS) { + addTransitionClass(el, leaveClass) + addTransitionClass(el, leaveActiveClass) + nextFrame(() => { + removeTransitionClass(el, leaveClass) + if (!cb.cancelled) { + addTransitionClass(el, leaveToClass) + if (!userWantsControl) { + if (isValidDuration(explicitLeaveDuration)) { + setTimeout(cb, explicitLeaveDuration) + } else { + whenTransitionEnds(el, type, cb) + } + } + } + }) + } + leave && leave(el, cb) + if (!expectsCSS && !userWantsControl) { + cb() + } + } +} + +// only used in dev mode +function checkDuration (val, name, vnode) { + if (typeof val !== 'number') { + warn( + `<transition> explicit ${name} duration is not a valid number - ` + + `got ${JSON.stringify(val)}.`, + vnode.context + ) + } else if (isNaN(val)) { + warn( + `<transition> explicit ${name} duration is NaN - ` + + 'the duration expression might be incorrect.', + vnode.context + ) + } +} + +function isValidDuration (val) { + return typeof val === 'number' && !isNaN(val) +} + +/** + * Normalize a transition hook's argument length. The hook may be: + * - a merged hook (invoker) with the original in .fns + * - a wrapped component method (check ._length) + * - a plain function (.length) + */ +function getHookArgumentsLength (fn: Function): boolean { + if (isUndef(fn)) { + return false + } + const invokerFns = fn.fns + if (isDef(invokerFns)) { + // invoker + return getHookArgumentsLength( + Array.isArray(invokerFns) + ? invokerFns[0] + : invokerFns + ) + } else { + return (fn._length || fn.length) > 1 + } +} + +function _enter (_: any, vnode: VNodeWithData) { + if (vnode.data.show !== true) { + enter(vnode) + } +} + +export default inBrowser ? { + create: _enter, + activate: _enter, + remove (vnode: VNode, rm: Function) { + /* istanbul ignore else */ + if (vnode.data.show !== true) { + leave(vnode, rm) + } else { + rm() + } + } +} : {} |
