提交 2ddd9bff 编写于 作者: A ascrutae

解决Application无法显示的问题

上级 b3177149
{
"name": "vue",
"main": "dist/vue.js",
"description": "Simple, Fast & Composable MVVM for building interative interfaces",
"authors": [
"Evan You <yyx990803@gmail.com>"
],
"license": "MIT",
"ignore": [
".*",
"examples",
"build",
"perf",
"test",
"grunt",
"gruntfile.js",
"*.json",
"*.md",
"*.yml"
],
"homepage": "https://github.com/vuejs/vue",
"version": "1.0.21",
"_release": "1.0.21",
"_resolution": {
"type": "version",
"tag": "v1.0.21",
"commit": "e552a45ecd95a095785d9d476d12596bd417a371"
},
"_source": "https://github.com/vuejs/vue.git",
"_target": "^1.0.21",
"_originalSource": "https://github.com/vuejs/vue.git",
"_direct": true
}
\ No newline at end of file
The MIT License (MIT)
Copyright (c) 2013-2016 Yuxi Evan You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
{
"name": "vue",
"main": "dist/vue.js",
"description": "Simple, Fast & Composable MVVM for building interative interfaces",
"authors": ["Evan You <yyx990803@gmail.com>"],
"license": "MIT",
"ignore": [
".*",
"examples",
"build",
"perf",
"test",
"grunt",
"gruntfile.js",
"*.json",
"*.md",
"*.yml"
]
}
import config from './config'
import {
warn,
nextTick,
devtools
} from './util/index'
// we have two separate queues: one for directive updates
// and one for user watcher registered via $watch().
// we want to guarantee directive updates to be called
// before user watchers so that when user watchers are
// triggered, the DOM would have already been in updated
// state.
var queueIndex
var queue = []
var userQueue = []
var has = {}
var circular = {}
var waiting = false
var internalQueueDepleted = false
/**
* Reset the batcher's state.
*/
function resetBatcherState () {
queue = []
userQueue = []
has = {}
circular = {}
waiting = internalQueueDepleted = false
}
/**
* Flush both queues and run the watchers.
*/
function flushBatcherQueue () {
runBatcherQueue(queue)
internalQueueDepleted = true
runBatcherQueue(userQueue)
// dev tool hook
/* istanbul ignore if */
if (devtools && config.devtools) {
devtools.emit('flush')
}
resetBatcherState()
}
/**
* Run the watchers in a single queue.
*
* @param {Array} queue
*/
function runBatcherQueue (queue) {
// do not cache length because more watchers might be pushed
// as we run existing watchers
for (queueIndex = 0; queueIndex < queue.length; queueIndex++) {
var watcher = queue[queueIndex]
var id = watcher.id
has[id] = null
watcher.run()
// in dev build, check and stop circular updates.
if (process.env.NODE_ENV !== 'production' && has[id] != null) {
circular[id] = (circular[id] || 0) + 1
if (circular[id] > config._maxUpdateCount) {
warn(
'You may have an infinite update loop for watcher ' +
'with expression "' + watcher.expression + '"',
watcher.vm
)
break
}
}
}
}
/**
* Push a watcher into the watcher queue.
* Jobs with duplicate IDs will be skipped unless it's
* pushed when the queue is being flushed.
*
* @param {Watcher} watcher
* properties:
* - {Number} id
* - {Function} run
*/
export function pushWatcher (watcher) {
var id = watcher.id
if (has[id] == null) {
if (internalQueueDepleted && !watcher.user) {
// an internal watcher triggered by a user watcher...
// let's run it immediately after current user watcher is done.
userQueue.splice(queueIndex + 1, 0, watcher)
} else {
// push watcher into appropriate queue
var q = watcher.user
? userQueue
: queue
has[id] = q.length
q.push(watcher)
// queue the flush
if (!waiting) {
waiting = true
nextTick(flushBatcherQueue)
}
}
}
}
/**
* A doubly linked list-based Least Recently Used (LRU)
* cache. Will keep most recently used items while
* discarding least recently used items when its limit is
* reached. This is a bare-bone version of
* Rasmus Andersson's js-lru:
*
* https://github.com/rsms/js-lru
*
* @param {Number} limit
* @constructor
*/
export default function Cache (limit) {
this.size = 0
this.limit = limit
this.head = this.tail = undefined
this._keymap = Object.create(null)
}
var p = Cache.prototype
/**
* Put <value> into the cache associated with <key>.
* Returns the entry which was removed to make room for
* the new entry. Otherwise undefined is returned.
* (i.e. if there was enough room already).
*
* @param {String} key
* @param {*} value
* @return {Entry|undefined}
*/
p.put = function (key, value) {
var removed
if (this.size === this.limit) {
removed = this.shift()
}
var entry = this.get(key, true)
if (!entry) {
entry = {
key: key
}
this._keymap[key] = entry
if (this.tail) {
this.tail.newer = entry
entry.older = this.tail
} else {
this.head = entry
}
this.tail = entry
this.size++
}
entry.value = value
return removed
}
/**
* Purge the least recently used (oldest) entry from the
* cache. Returns the removed entry or undefined if the
* cache was empty.
*/
p.shift = function () {
var entry = this.head
if (entry) {
this.head = this.head.newer
this.head.older = undefined
entry.newer = entry.older = undefined
this._keymap[entry.key] = undefined
this.size--
}
return entry
}
/**
* Get and register recent use of <key>. Returns the value
* associated with <key> or undefined if not in cache.
*
* @param {String} key
* @param {Boolean} returnEntry
* @return {Entry|*}
*/
p.get = function (key, returnEntry) {
var entry = this._keymap[key]
if (entry === undefined) return
if (entry === this.tail) {
return returnEntry
? entry
: entry.value
}
// HEAD--------------TAIL
// <.older .newer>
// <--- add direction --
// A B C <D> E
if (entry.newer) {
if (entry === this.head) {
this.head = entry.newer
}
entry.newer.older = entry.older // C <-- E.
}
if (entry.older) {
entry.older.newer = entry.newer // C. --> E
}
entry.newer = undefined // D --x
entry.older = this.tail // D. --> E
if (this.tail) {
this.tail.newer = entry // E. <-- D
}
this.tail = entry
return returnEntry
? entry
: entry.value
}
import config from '../config'
import { parseDirective } from '../parsers/directive'
import { isSimplePath } from '../parsers/expression'
import { defineReactive, withoutConversion } from '../observer/index'
import propDef from '../directives/internal/prop'
import {
warn,
camelize,
hyphenate,
getAttr,
getBindAttr,
isLiteral,
toBoolean,
toNumber,
stripQuotes,
isArray,
isPlainObject,
isObject,
hasOwn
} from '../util/index'
const propBindingModes = config._propBindingModes
const empty = {}
// regexes
const identRE = /^[$_a-zA-Z]+[\w$]*$/
const 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
*/
export 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 (process.env.NODE_ENV !== 'production' && name === '$data') {
warn('Do not use $data as prop.', vm)
continue
}
// props could contain dashes, which will be
// interpreted as minus calculations by the parser
// so we need to camelize the path here
path = camelize(name)
if (!identRE.test(path)) {
process.env.NODE_ENV !== '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 (process.env.NODE_ENV !== 'production' &&
prop.mode === propBindingModes.TWO_WAY &&
!settablePathRE.test(value)) {
prop.mode = propBindingModes.ONE_WAY
warn(
'Cannot bind two-way prop with non-settable ' +
'parent path: ' + value,
vm
)
}
}
prop.parentPath = value
// warn required two-way
if (
process.env.NODE_ENV !== 'production' &&
options.twoWay &&
prop.mode !== propBindingModes.TWO_WAY
) {
warn(
'Prop "' + name + '" expects a two-way binding type.',
vm
)
}
} else if ((value = getAttr(el, attr)) !== null) {
// has literal binding!
prop.raw = value
} else if (process.env.NODE_ENV !== 'production') {
// check possible camelCase prop usage
var lowerCaseName = path.toLowerCase()
value = /[A-Z\-]/.test(name) && (
el.getAttribute(lowerCaseName) ||
el.getAttribute(':' + lowerCaseName) ||
el.getAttribute('v-bind:' + lowerCaseName) ||
el.getAttribute(':' + lowerCaseName + '.once') ||
el.getAttribute('v-bind:' + lowerCaseName + '.once') ||
el.getAttribute(':' + lowerCaseName + '.sync') ||
el.getAttribute('v-bind:' + lowerCaseName + '.sync')
)
if (value) {
warn(
'Possible usage error for prop `' + lowerCaseName + '` - ' +
'did you mean `' + attr + '`? HTML is case-insensitive, remember to use ' +
'kebab-case for props in templates.',
vm
)
} else if (options.required) {
// warn missing required
warn('Missing required prop: ' + name, vm)
}
}
// push prop
props.push(prop)
}
return makePropsLinkFn(props)
}
/**
* Build a function that applies props to a vm.
*
* @param {Array} props
* @return {Function} propsLinkFn
*/
function makePropsLinkFn (props) {
return function propsLinkFn (vm, scope) {
// store resolved props info
vm._props = {}
var 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 (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) {
const isSimple = prop.dynamic && isSimplePath(prop.parentPath)
let value = rawValue
if (value === undefined) {
value = getPropDefaultValue(vm, prop)
}
value = coerceProp(prop, value)
const coerced = value !== rawValue
if (!assertProp(prop, value, vm)) {
value = undefined
}
if (isSimple && !coerced) {
withoutConversion(() => {
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
*/
export function initProp (vm, prop, value) {
processPropValue(vm, prop, value, value => {
defineReactive(vm, prop.path, value)
})
}
/**
* Update a prop's value on a vm.
*
* @param {Vue} vm
* @param {Object} prop
* @param {*} value
*/
export function updateProp (vm, prop, value) {
processPropValue(vm, prop, value, 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
const 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)) {
process.env.NODE_ENV !== '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 (process.env.NODE_ENV !== 'production') {
warn(
'Invalid prop: type check failed for prop "' + prop.name + '".' +
' Expected ' + expectedTypes.map(formatType).join(', ') +
', got ' + formatValue(value) + '.',
vm
)
}
return false
}
var validator = options.validator
if (validator) {
if (!validator(value)) {
process.env.NODE_ENV !== '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,
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)
}
export * from './compile'
export * from './transclude'
export * from './resolve-slots'
import { parseTemplate } from '../parsers/template'
import {
isTemplate,
toArray,
getBindAttr,
warn
} from '../util/index'
/**
* Scan and determine slot content distribution.
* We do this during transclusion instead at compile time so that
* the distribution is decoupled from the compilation order of
* the slots.
*
* @param {Element|DocumentFragment} template
* @param {Element} content
* @param {Vue} vm
*/
export function resolveSlots (vm, content) {
if (!content) {
return
}
var contents = vm._slotContents = Object.create(null)
var el, name
for (var i = 0, l = content.children.length; i < l; i++) {
el = content.children[i]
/* eslint-disable no-cond-assign */
if (name = el.getAttribute('slot')) {
(contents[name] || (contents[name] = [])).push(el)
}
/* eslint-enable no-cond-assign */
if (process.env.NODE_ENV !== 'production' && getBindAttr(el, 'slot')) {
warn('The "slot" attribute must be static.', vm.$parent)
}
}
for (name in contents) {
contents[name] = extractFragment(contents[name], content)
}
if (content.hasChildNodes()) {
contents['default'] = extractFragment(content.childNodes, content)
}
}
/**
* Extract qualified content nodes from a node list.
*
* @param {NodeList} nodes
* @return {DocumentFragment}
*/
function extractFragment (nodes, parent) {
var frag = document.createDocumentFragment()
nodes = toArray(nodes)
for (var i = 0, l = nodes.length; i < l; i++) {
var node = nodes[i]
if (
isTemplate(node) &&
!node.hasAttribute('v-if') &&
!node.hasAttribute('v-for')
) {
parent.removeChild(node)
node = parseTemplate(node)
}
frag.appendChild(node)
}
return frag
}
import { parseText } from '../parsers/text'
import { parseTemplate } from '../parsers/template'
import {
warn,
isTemplate,
isFragment,
prepend,
extractContent,
createAnchor,
resolveAsset,
toArray,
addClass,
hasBindAttr
} from '../util/index'
const specialCharRE = /[^\w\-:\.]/
/**
* Process an element or a DocumentFragment based on a
* instance option object. This allows us to transclude
* a template node/fragment before the instance is created,
* so the processed fragment can then be cloned and reused
* in v-for.
*
* @param {Element} el
* @param {Object} options
* @return {Element|DocumentFragment}
*/
export function transclude (el, options) {
// extract container attributes to pass them down
// to compiler, because they need to be compiled in
// parent scope. we are mutating the options object here
// assuming the same object will be used for compile
// right after this.
if (options) {
options._containerAttrs = extractAttrs(el)
}
// for template tags, what we want is its content as
// a documentFragment (for fragment instances)
if (isTemplate(el)) {
el = parseTemplate(el)
}
if (options) {
if (options._asComponent && !options.template) {
options.template = '<slot></slot>'
}
if (options.template) {
options._content = extractContent(el)
el = transcludeTemplate(el, options)
}
}
if (isFragment(el)) {
// anchors for fragment instance
// passing in `persist: true` to avoid them being
// discarded by IE during template cloning
prepend(createAnchor('v-start', true), el)
el.appendChild(createAnchor('v-end', true))
}
return el
}
/**
* Process the template option.
* If the replace option is true this will swap the $el.
*
* @param {Element} el
* @param {Object} options
* @return {Element|DocumentFragment}
*/
function transcludeTemplate (el, options) {
var template = options.template
var frag = parseTemplate(template, true)
if (frag) {
var replacer = frag.firstChild
var tag = replacer.tagName && replacer.tagName.toLowerCase()
if (options.replace) {
/* istanbul ignore if */
if (el === document.body) {
process.env.NODE_ENV !== 'production' && warn(
'You are mounting an instance with a template to ' +
'<body>. This will replace <body> entirely. You ' +
'should probably use `replace: false` here.'
)
}
// there are many cases where the instance must
// become a fragment instance: basically anything that
// can create more than 1 root nodes.
if (
// multi-children template
frag.childNodes.length > 1 ||
// non-element template
replacer.nodeType !== 1 ||
// single nested component
tag === 'component' ||
resolveAsset(options, 'components', tag) ||
hasBindAttr(replacer, 'is') ||
// element directive
resolveAsset(options, 'elementDirectives', tag) ||
// for block
replacer.hasAttribute('v-for') ||
// if block
replacer.hasAttribute('v-if')
) {
return frag
} else {
options._replacerAttrs = extractAttrs(replacer)
mergeAttrs(el, replacer)
return replacer
}
} else {
el.appendChild(frag)
return el
}
} else {
process.env.NODE_ENV !== 'production' && warn(
'Invalid template option: ' + template
)
}
}
/**
* Helper to extract a component container's attributes
* into a plain object array.
*
* @param {Element} el
* @return {Array}
*/
function extractAttrs (el) {
if (el.nodeType === 1 && el.hasAttributes()) {
return toArray(el.attributes)
}
}
/**
* Merge the attributes of two elements, and make sure
* the class names are merged properly.
*
* @param {Element} from
* @param {Element} to
*/
function mergeAttrs (from, to) {
var attrs = from.attributes
var i = attrs.length
var name, value
while (i--) {
name = attrs[i].name
value = attrs[i].value
if (!to.hasAttribute(name) && !specialCharRE.test(name)) {
to.setAttribute(name, value)
} else if (name === 'class' && !parseText(value)) {
value.trim().split(/\s+/).forEach(function (cls) {
addClass(to, cls)
})
}
}
}
import { compileRegex } from './parsers/text'
let delimiters = ['{{', '}}']
let unsafeDelimiters = ['{{{', '}}}']
const config = {
/**
* Whether to print debug messages.
* Also enables stack trace for warnings.
*
* @type {Boolean}
*/
debug: false,
/**
* Whether to suppress warnings.
*
* @type {Boolean}
*/
silent: false,
/**
* Whether to use async rendering.
*/
async: true,
/**
* Whether to warn against errors caught when evaluating
* expressions.
*/
warnExpressionErrors: true,
/**
* Whether to allow devtools inspection.
* Disabled by default in production builds.
*/
devtools: process.env.NODE_ENV !== 'production',
/**
* Internal flag to indicate the delimiters have been
* changed.
*
* @type {Boolean}
*/
_delimitersChanged: true,
/**
* List of asset types that a component can own.
*
* @type {Array}
*/
_assetTypes: [
'component',
'directive',
'elementDirective',
'filter',
'transition',
'partial'
],
/**
* prop binding modes
*/
_propBindingModes: {
ONE_WAY: 0,
TWO_WAY: 1,
ONE_TIME: 2
},
/**
* Max circular updates allowed in a batcher flush cycle.
*/
_maxUpdateCount: 100,
/**
* Interpolation delimiters. Changing these would trigger
* the text parser to re-compile the regular expressions.
*
* @type {Array<String>}
*/
get delimiters () {
return delimiters
},
set delimiters (val) {
delimiters = val
compileRegex()
},
get unsafeDelimiters () {
return unsafeDelimiters
},
set unsafeDelimiters (val) {
unsafeDelimiters = val
compileRegex()
}
}
export default config
import {
extend,
bind,
on,
off,
getAttr,
getBindAttr,
camelize,
hyphenate,
nextTick,
warn
} from './util/index'
import Watcher from './watcher'
import { parseExpression, isSimplePath } from './parsers/expression'
function noop () {}
/**
* A directive links a DOM element with a piece of data,
* which is the result of evaluating an expression.
* It registers a watcher with the expression and calls
* the DOM update function when a change is triggered.
*
* @param {Object} descriptor
* - {String} name
* - {Object} def
* - {String} expression
* - {Array<Object>} [filters]
* - {Object} [modifiers]
* - {Boolean} literal
* - {String} attr
* - {String} arg
* - {String} raw
* - {String} [ref]
* - {Array<Object>} [interp]
* - {Boolean} [hasOneTime]
* @param {Vue} vm
* @param {Node} el
* @param {Vue} [host] - transclusion host component
* @param {Object} [scope] - v-for scope
* @param {Fragment} [frag] - owner fragment
* @constructor
*/
export default function Directive (descriptor, vm, el, host, scope, frag) {
this.vm = vm
this.el = el
// copy descriptor properties
this.descriptor = descriptor
this.name = descriptor.name
this.expression = descriptor.expression
this.arg = descriptor.arg
this.modifiers = descriptor.modifiers
this.filters = descriptor.filters
this.literal = this.modifiers && this.modifiers.literal
// private
this._locked = false
this._bound = false
this._listeners = null
// link context
this._host = host
this._scope = scope
this._frag = frag
// store directives on node in dev mode
if (process.env.NODE_ENV !== 'production' && this.el) {
this.el._vue_directives = this.el._vue_directives || []
this.el._vue_directives.push(this)
}
}
/**
* Initialize the directive, mixin definition properties,
* setup the watcher, call definition bind() and update()
* if present.
*/
Directive.prototype._bind = function () {
var name = this.name
var descriptor = this.descriptor
// remove attribute
if (
(name !== 'cloak' || this.vm._isCompiled) &&
this.el && this.el.removeAttribute
) {
var attr = descriptor.attr || ('v-' + name)
this.el.removeAttribute(attr)
}
// copy def properties
var def = descriptor.def
if (typeof def === 'function') {
this.update = def
} else {
extend(this, def)
}
// setup directive params
this._setupParams()
// initial bind
if (this.bind) {
this.bind()
}
this._bound = true
if (this.literal) {
this.update && this.update(descriptor.raw)
} else if (
(this.expression || this.modifiers) &&
(this.update || this.twoWay) &&
!this._checkStatement()
) {
// wrapped updater for context
var dir = this
if (this.update) {
this._update = function (val, oldVal) {
if (!dir._locked) {
dir.update(val, oldVal)
}
}
} else {
this._update = noop
}
var preProcess = this._preProcess
? bind(this._preProcess, this)
: null
var postProcess = this._postProcess
? bind(this._postProcess, this)
: null
var watcher = this._watcher = new Watcher(
this.vm,
this.expression,
this._update, // callback
{
filters: this.filters,
twoWay: this.twoWay,
deep: this.deep,
preProcess: preProcess,
postProcess: postProcess,
scope: this._scope
}
)
// v-model with inital inline value need to sync back to
// model instead of update to DOM on init. They would
// set the afterBind hook to indicate that.
if (this.afterBind) {
this.afterBind()
} else if (this.update) {
this.update(watcher.value)
}
}
}
/**
* Setup all param attributes, e.g. track-by,
* transition-mode, etc...
*/
Directive.prototype._setupParams = function () {
if (!this.params) {
return
}
var params = this.params
// swap the params array with a fresh object.
this.params = Object.create(null)
var i = params.length
var key, val, mappedKey
while (i--) {
key = hyphenate(params[i])
mappedKey = camelize(key)
val = getBindAttr(this.el, key)
if (val != null) {
// dynamic
this._setupParamWatcher(mappedKey, val)
} else {
// static
val = getAttr(this.el, key)
if (val != null) {
this.params[mappedKey] = val === '' ? true : val
}
}
}
}
/**
* Setup a watcher for a dynamic param.
*
* @param {String} key
* @param {String} expression
*/
Directive.prototype._setupParamWatcher = function (key, expression) {
var self = this
var called = false
var unwatch = (this._scope || this.vm).$watch(expression, function (val, oldVal) {
self.params[key] = val
// since we are in immediate mode,
// only call the param change callbacks if this is not the first update.
if (called) {
var cb = self.paramWatchers && self.paramWatchers[key]
if (cb) {
cb.call(self, val, oldVal)
}
} else {
called = true
}
}, {
immediate: true,
user: false
})
;(this._paramUnwatchFns || (this._paramUnwatchFns = [])).push(unwatch)
}
/**
* Check if the directive is a function caller
* and if the expression is a callable one. If both true,
* we wrap up the expression and use it as the event
* handler.
*
* e.g. on-click="a++"
*
* @return {Boolean}
*/
Directive.prototype._checkStatement = function () {
var expression = this.expression
if (
expression && this.acceptStatement &&
!isSimplePath(expression)
) {
var fn = parseExpression(expression).get
var scope = this._scope || this.vm
var handler = function (e) {
scope.$event = e
fn.call(scope, scope)
scope.$event = null
}
if (this.filters) {
handler = scope._applyFilters(handler, null, this.filters)
}
this.update(handler)
return true
}
}
/**
* Set the corresponding value with the setter.
* This should only be used in two-way directives
* e.g. v-model.
*
* @param {*} value
* @public
*/
Directive.prototype.set = function (value) {
/* istanbul ignore else */
if (this.twoWay) {
this._withLock(function () {
this._watcher.set(value)
})
} else if (process.env.NODE_ENV !== 'production') {
warn(
'Directive.set() can only be used inside twoWay' +
'directives.'
)
}
}
/**
* Execute a function while preventing that function from
* triggering updates on this directive instance.
*
* @param {Function} fn
*/
Directive.prototype._withLock = function (fn) {
var self = this
self._locked = true
fn.call(self)
nextTick(function () {
self._locked = false
})
}
/**
* Convenience method that attaches a DOM event listener
* to the directive element and autometically tears it down
* during unbind.
*
* @param {String} event
* @param {Function} handler
* @param {Boolean} [useCapture]
*/
Directive.prototype.on = function (event, handler, useCapture) {
on(this.el, event, handler, useCapture)
;(this._listeners || (this._listeners = []))
.push([event, handler])
}
/**
* Teardown the watcher and call unbind.
*/
Directive.prototype._teardown = function () {
if (this._bound) {
this._bound = false
if (this.unbind) {
this.unbind()
}
if (this._watcher) {
this._watcher.teardown()
}
var listeners = this._listeners
var i
if (listeners) {
i = listeners.length
while (i--) {
off(this.el, listeners[i][0], listeners[i][1])
}
}
var unwatchFns = this._paramUnwatchFns
if (unwatchFns) {
i = unwatchFns.length
while (i--) {
unwatchFns[i]()
}
}
if (process.env.NODE_ENV !== 'production' && this.el) {
this.el._vue_directives.$remove(this)
}
this.vm = this.el = this._watcher = this._listeners = null
}
}
import slot from './slot'
import partial from './partial'
export default {
slot,
partial
}
import vIf from '../public/if'
import FragmentFactory from '../../fragment/factory'
import { PARTIAL } from '../priorities'
import {
createAnchor,
replace,
resolveAsset
} from '../../util/index'
export default {
priority: PARTIAL,
params: ['name'],
// watch changes to name for dynamic partials
paramWatchers: {
name (value) {
vIf.remove.call(this)
if (value) {
this.insert(value)
}
}
},
bind () {
this.anchor = createAnchor('v-partial')
replace(this.el, this.anchor)
this.insert(this.params.name)
},
insert (id) {
var partial = resolveAsset(this.vm.$options, 'partials', id, true)
if (partial) {
this.factory = new FragmentFactory(this.vm, partial)
vIf.insert.call(this)
}
},
unbind () {
if (this.frag) {
this.frag.destroy()
}
}
}
import { SLOT } from '../priorities'
import {
extractContent,
replace,
remove
} from '../../util/index'
export default {
priority: SLOT,
params: ['name'],
bind () {
// this was resolved during component transclusion
var name = this.params.name || 'default'
var content = this.vm._slotContents && this.vm._slotContents[name]
if (!content || !content.hasChildNodes()) {
this.fallback()
} else {
this.compile(content.cloneNode(true), this.vm._context, this.vm)
}
},
compile (content, context, host) {
if (content && context) {
if (
this.el.hasChildNodes() &&
content.childNodes.length === 1 &&
content.childNodes[0].nodeType === 1 &&
content.childNodes[0].hasAttribute('v-if')
) {
// if the inserted slot has v-if
// inject fallback content as the v-else
const elseBlock = document.createElement('template')
elseBlock.setAttribute('v-else', '')
elseBlock.innerHTML = this.el.innerHTML
// the else block should be compiled in child scope
elseBlock._context = this.vm
content.appendChild(elseBlock)
}
const scope = host
? host._scope
: this._scope
this.unlink = context.$compile(
content, host, scope, this._frag
)
}
if (content) {
replace(this.el, content)
} else {
remove(this.el)
}
},
fallback () {
this.compile(extractContent(this.el, true), this.vm)
},
unbind () {
if (this.unlink) {
this.unlink()
}
}
}
import {
addClass,
removeClass,
isArray,
isPlainObject
} from '../../util/index'
export default {
deep: true,
update (value) {
if (value && typeof value === 'string') {
this.handleObject(stringToObject(value))
} else if (isPlainObject(value)) {
this.handleObject(value)
} else if (isArray(value)) {
this.handleArray(value)
} else {
this.cleanup()
}
},
handleObject (value) {
this.cleanup(value)
this.prevKeys = Object.keys(value)
setObjectClasses(this.el, value)
},
handleArray (value) {
this.cleanup(value)
for (var i = 0, l = value.length; i < l; i++) {
var val = value[i]
if (val && isPlainObject(val)) {
setObjectClasses(this.el, val)
} else if (val && typeof val === 'string') {
addClass(this.el, val)
}
}
this.prevKeys = value.slice()
},
cleanup (value) {
if (!this.prevKeys) return
var i = this.prevKeys.length
while (i--) {
var key = this.prevKeys[i]
if (!key) continue
var keys = isPlainObject(key) ? Object.keys(key) : [key]
for (var j = 0, l = keys.length; j < l; j++) {
toggleClasses(this.el, keys[j], removeClass)
}
}
}
}
function setObjectClasses (el, obj) {
var keys = Object.keys(obj)
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i]
if (!obj[key]) continue
toggleClasses(el, key, addClass)
}
}
function stringToObject (value) {
var res = {}
var keys = value.trim().split(/\s+/)
for (var i = 0, l = keys.length; i < l; i++) {
res[keys[i]] = true
}
return res
}
/**
* Add or remove a class/classes on an element
*
* @param {Element} el
* @param {String} key The class name. This may or may not
* contain a space character, in such a
* case we'll deal with multiple class
* names at once.
* @param {Function} fn
*/
function toggleClasses (el, key, fn) {
key = key.trim()
if (key.indexOf(' ') === -1) {
fn(el, key)
return
}
// The key contains one or more space characters.
// Since a class name doesn't accept such characters, we
// treat it as multiple classes.
var keys = key.split(/\s+/)
for (var i = 0, l = keys.length; i < l; i++) {
fn(el, keys[i])
}
}
import { cloneNode } from '../../parsers/template'
import { COMPONENT } from '../priorities'
import {
extractContent,
createAnchor,
replace,
hyphenate,
warn,
cancellable,
extend
} from '../../util/index'
export default {
priority: COMPONENT,
params: [
'keep-alive',
'transition-mode',
'inline-template'
],
/**
* Setup. Two possible usages:
*
* - static:
* <comp> or <div v-component="comp">
*
* - dynamic:
* <component :is="view">
*/
bind () {
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')
// 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 {
process.env.NODE_ENV !== 'production' && warn(
'cannot mount component "' + this.expression + '" ' +
'on already mounted element: ' + this.el
)
}
},
/**
* Public update, called by the watcher in the dynamic
* literal scenario, e.g. <component :is="view">
*/
update (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 (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 (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 (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 () {
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 (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 (process.env.NODE_ENV !== 'production' &&
this.el.hasAttribute('transition') &&
child._isFragment) {
warn(
'Transitions will not work on a fragment instance. ' +
'Template: ' + child.$options.template,
child
)
}
return child
}
},
/**
* Try to get a cached instance of the current component.
*
* @return {Vue|undefined}
*/
getCached () {
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 (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 (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 (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 () {
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)
}
}
}
import style from './style'
import vClass from './class'
import component from './component'
import prop from './prop'
import transition from './transition'
export default {
style,
'class': vClass,
component,
prop,
transition
}
// NOTE: the prop internal directive is compiled and linked
// during _initScope(), before the created hook is called.
// The purpose is to make the initial prop values available
// inside `created` hooks and `data` functions.
import Watcher from '../../watcher'
import config from '../../config'
import { initProp, updateProp } from '../../compiler/compile-props'
const bindingModes = config._propBindingModes
export default {
bind () {
const child = this.vm
const parent = child._context
// passed in from compiler directly
const prop = this.descriptor.prop
const childKey = prop.path
const parentKey = prop.parentPath
const twoWay = prop.mode === bindingModes.TWO_WAY
const 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 () {
this.parentWatcher.teardown()
if (this.childWatcher) {
this.childWatcher.teardown()
}
}
}
import {
extend,
isArray,
hyphenate,
camelize,
warn
} from '../../util/index'
const prefixes = ['-webkit-', '-moz-', '-ms-']
const camelPrefixes = ['Webkit', 'Moz', 'ms']
const importantRE = /!important;?$/
const propCache = Object.create(null)
let testEl = null
export default {
deep: true,
update (value) {
if (typeof value === 'string') {
this.el.style.cssText = value
} else if (isArray(value)) {
this.handleObject(value.reduce(extend, {}))
} else {
this.handleObject(value || {})
}
},
handleObject (value) {
// cache object styles so that only changed props
// are actually updated.
var cache = this.cache || (this.cache = {})
var name, val
for (name in cache) {
if (!(name in value)) {
this.handleSingle(name, null)
delete cache[name]
}
}
for (name in value) {
val = value[name]
if (val !== cache[name]) {
cache[name] = val
this.handleSingle(name, val)
}
}
},
handleSingle (prop, value) {
prop = normalize(prop)
if (!prop) return // unsupported prop
// cast possible numbers/booleans into strings
if (value != null) value += ''
if (value) {
var isImportant = importantRE.test(value)
? 'important'
: ''
if (isImportant) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production') {
warn(
'It\'s probably a bad idea to use !important with inline rules. ' +
'This feature will be deprecated in a future version of Vue.'
)
}
value = value.replace(importantRE, '').trim()
this.el.style.setProperty(prop.kebab, value, isImportant)
} else {
this.el.style[prop.camel] = value
}
} else {
this.el.style[prop.camel] = ''
}
}
}
/**
* Normalize a CSS property name.
* - cache result
* - auto prefix
* - camelCase -> dash-case
*
* @param {String} prop
* @return {String}
*/
function normalize (prop) {
if (propCache[prop]) {
return propCache[prop]
}
var res = prefix(prop)
propCache[prop] = propCache[res] = res
return res
}
/**
* Auto detect the appropriate prefix for a CSS property.
* https://gist.github.com/paulirish/523692
*
* @param {String} prop
* @return {String}
*/
function prefix (prop) {
prop = hyphenate(prop)
var camel = camelize(prop)
var upper = camel.charAt(0).toUpperCase() + camel.slice(1)
if (!testEl) {
testEl = document.createElement('div')
}
var i = prefixes.length
var prefixed
while (i--) {
prefixed = camelPrefixes[i] + upper
if (prefixed in testEl.style) {
return {
kebab: prefixes[i] + prop,
camel: prefixed
}
}
}
if (camel in testEl.style) {
return {
kebab: prop,
camel: camel
}
}
}
import { resolveAsset, addClass, removeClass } from '../../util/index'
import { TRANSITION } from '../priorities'
import Transition from '../../transition/transition'
export default {
priority: TRANSITION,
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')
}
}
export const ON = 700
export const MODEL = 800
export const BIND = 850
export const TRANSITION = 1100
export const EL = 1500
export const COMPONENT = 1500
export const PARTIAL = 1750
export const IF = 2100
export const FOR = 2200
export const SLOT = 2300
import { warn, setClass, camelize } from '../../util/index'
import { BIND } from '../priorities'
import vStyle from '../internal/style'
import { tokensToExp } from '../../parsers/text'
// xlink
const xlinkNS = 'http://www.w3.org/1999/xlink'
const xlinkRE = /^xlink:/
// check for attributes that prohibit interpolations
const 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
const attrWithPropsRE = /^(?:value|checked|selected|muted)$/
// these attributes expect enumrated values of "true" or "false"
// but are not boolean attributes
const enumeratedAttrRE = /^(?:draggable|contenteditable|spellcheck)$/
// these attributes should set a hidden property for
// binding v-model to object values
const modelProps = {
value: '_value',
'true-value': '_trueValue',
'false-value': '_falseValue'
}
export default {
priority: BIND,
bind () {
var attr = this.arg
var tag = this.el.tagName
// should be deep watch on object mode
if (!attr) {
this.deep = true
}
// handle interpolation bindings
const descriptor = this.descriptor
const 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'))
) {
process.env.NODE_ENV !== 'production' && warn(
attr + '="' + descriptor.raw + '": ' +
'attribute interpolation is not allowed in Vue.js ' +
'directives and special attributes.',
this.vm
)
this.el.removeAttribute(attr)
this.invalid = true
}
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production') {
var raw = attr + '="' + descriptor.raw + '": '
// warn src
if (attr === 'src') {
warn(
raw + 'interpolation in "src" attribute will cause ' +
'a 404 request. Use v-bind:src instead.',
this.vm
)
}
// warn style
if (attr === 'style') {
warn(
raw + 'interpolation in "style" attribute will cause ' +
'the attribute to be discarded in Internet Explorer. ' +
'Use v-bind:style instead.',
this.vm
)
}
}
}
},
update (value) {
if (this.invalid) {
return
}
var attr = this.arg
if (this.arg) {
this.handleSingle(attr, value)
} else {
this.handleObject(value || {})
}
},
// share object handler with v-bind:class
handleObject: vStyle.handleObject,
handleSingle (attr, value) {
const el = this.el
const interp = this.descriptor.interp
if (this.modifiers.camel) {
attr = camelize(attr)
}
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 (!interp && modelProp) {
el[modelProp] = value
// update v-model if present
var model = el.__v_model
if (model) {
model.listener()
}
}
// do not set value attribute for textarea
if (attr === 'value' && el.tagName === 'TEXTAREA') {
el.removeAttribute(attr)
return
}
// update attribute
if (enumeratedAttrRE.test(attr)) {
el.setAttribute(attr, value ? 'true' : 'false')
} else if (value != null && value !== false) {
if (attr === 'class') {
// handle edge case #1960:
// class interpolation should not overwrite Vue transition class
if (el.__v_trans) {
value += ' ' + el.__v_trans.id + '-transition'
}
setClass(el, value)
} else if (xlinkRE.test(attr)) {
el.setAttributeNS(xlinkNS, attr, value === true ? '' : value)
} else {
el.setAttribute(attr, value === true ? '' : value)
}
} else {
el.removeAttribute(attr)
}
}
}
export default {
bind () {
var el = this.el
this.vm.$once('pre-hook:compiled', function () {
el.removeAttribute('v-cloak')
})
}
}
import { camelize, hasOwn, defineReactive } from '../../util/index'
import { EL } from '../priorities'
export default {
priority: EL,
bind () {
/* istanbul ignore if */
if (!this.arg) {
return
}
var id = this.id = camelize(this.arg)
var refs = (this._scope || this.vm).$els
if (hasOwn(refs, id)) {
refs[id] = this.el
} else {
defineReactive(refs, id, this.el)
}
},
unbind () {
var refs = (this._scope || this.vm).$els
if (refs[this.id] === this.el) {
refs[this.id] = null
}
}
}
import FragmentFactory from '../../fragment/factory'
import { FOR } from '../priorities'
import { withoutConversion } from '../../observer/index'
import { getPath } from '../../parsers/path'
import {
isObject,
warn,
createAnchor,
replace,
before,
after,
remove,
hasOwn,
inDoc,
defineReactive,
def,
cancellable,
isArray,
isPlainObject
} from '../../util/index'
let uid = 0
const vFor = {
priority: FOR,
terminal: true,
params: [
'track-by',
'stagger',
'enter-stagger',
'leave-stagger'
],
bind () {
// support "item in/of items" syntax
var inMatch = this.expression.match(/(.*) (?:in|of) (.*)/)
if (inMatch) {
var itMatch = inMatch[1].match(/\((.*),(.*)\)/)
if (itMatch) {
this.iterator = itMatch[1].trim()
this.alias = itMatch[2].trim()
} else {
this.alias = inMatch[1].trim()
}
this.expression = inMatch[2]
}
if (!this.alias) {
process.env.NODE_ENV !== 'production' && warn(
'Invalid v-for expression "' + this.descriptor.raw + '": ' +
'alias is required.',
this.vm
)
return
}
// uid as a cache identifier
this.id = '__v-for__' + (++uid)
// check if this is an option list,
// so that we know if we need to update the <select>'s
// v-model when the option list has changed.
// because v-model has a lower priority than v-for,
// the v-model is not bound here yet, so we have to
// retrive it in the actual updateModel() function.
var tag = this.el.tagName
this.isOption =
(tag === 'OPTION' || tag === 'OPTGROUP') &&
this.el.parentNode.tagName === 'SELECT'
// setup anchor nodes
this.start = createAnchor('v-for-start')
this.end = createAnchor('v-for-end')
replace(this.el, this.end)
before(this.start, this.end)
// cache
this.cache = Object.create(null)
// fragment factory
this.factory = new FragmentFactory(this.vm, this.el)
},
update (data) {
this.diff(data)
this.updateRef()
this.updateModel()
},
/**
* Diff, based on new data and old data, determine the
* minimum amount of DOM manipulations needed to make the
* DOM reflect the new data Array.
*
* The algorithm diffs the new data Array by storing a
* hidden reference to an owner vm instance on previously
* seen data. This allows us to achieve O(n) which is
* better than a levenshtein distance based algorithm,
* which is O(m * n).
*
* @param {Array} data
*/
diff (data) {
// check if the Array was converted from an Object
var item = data[0]
var convertedFromObject = this.fromObject =
isObject(item) &&
hasOwn(item, '$key') &&
hasOwn(item, '$value')
var trackByKey = this.params.trackBy
var oldFrags = this.frags
var frags = this.frags = new Array(data.length)
var alias = this.alias
var iterator = this.iterator
var start = this.start
var end = this.end
var inDocument = inDoc(start)
var init = !oldFrags
var i, l, frag, key, value, primitive
// First pass, go through the new Array and fill up
// the new frags array. If a piece of data has a cached
// instance for it, we reuse it. Otherwise build a new
// instance.
for (i = 0, l = data.length; i < l; i++) {
item = data[i]
key = convertedFromObject ? item.$key : null
value = convertedFromObject ? item.$value : item
primitive = !isObject(value)
frag = !init && this.getCachedFrag(value, i, key)
if (frag) { // reusable fragment
frag.reused = true
// update $index
frag.scope.$index = i
// update $key
if (key) {
frag.scope.$key = key
}
// update iterator
if (iterator) {
frag.scope[iterator] = key !== null ? key : i
}
// update data for track-by, object repeat &
// primitive values.
if (trackByKey || convertedFromObject || primitive) {
withoutConversion(() => {
frag.scope[alias] = value
})
}
} else { // new isntance
frag = this.create(value, alias, i, key)
frag.fresh = !init
}
frags[i] = frag
if (init) {
frag.before(end)
}
}
// we're done for the initial render.
if (init) {
return
}
// Second pass, go through the old fragments and
// destroy those who are not reused (and remove them
// from cache)
var removalIndex = 0
var totalRemoved = oldFrags.length - frags.length
// when removing a large number of fragments, watcher removal
// turns out to be a perf bottleneck, so we batch the watcher
// removals into a single filter call!
this.vm._vForRemoving = true
for (i = 0, l = oldFrags.length; i < l; i++) {
frag = oldFrags[i]
if (!frag.reused) {
this.deleteCachedFrag(frag)
this.remove(frag, removalIndex++, totalRemoved, inDocument)
}
}
this.vm._vForRemoving = false
if (removalIndex) {
this.vm._watchers = this.vm._watchers.filter(w => w.active)
}
// Final pass, move/insert new fragments into the
// right place.
var targetPrev, prevEl, currentPrev
var insertionIndex = 0
for (i = 0, l = frags.length; i < l; i++) {
frag = frags[i]
// this is the frag that we should be after
targetPrev = frags[i - 1]
prevEl = targetPrev
? targetPrev.staggerCb
? targetPrev.staggerAnchor
: targetPrev.end || targetPrev.node
: start
if (frag.reused && !frag.staggerCb) {
currentPrev = findPrevFrag(frag, start, this.id)
if (
currentPrev !== targetPrev && (
!currentPrev ||
// optimization for moving a single item.
// thanks to suggestions by @livoras in #1807
findPrevFrag(currentPrev, start, this.id) !== targetPrev
)
) {
this.move(frag, prevEl)
}
} else {
// new instance, or still in stagger.
// insert with updated stagger index.
this.insert(frag, insertionIndex++, prevEl, inDocument)
}
frag.reused = frag.fresh = false
}
},
/**
* Create a new fragment instance.
*
* @param {*} value
* @param {String} alias
* @param {Number} index
* @param {String} [key]
* @return {Fragment}
*/
create (value, alias, index, key) {
var host = this._host
// create iteration scope
var parentScope = this._scope || this.vm
var scope = Object.create(parentScope)
// ref holder for the scope
scope.$refs = Object.create(parentScope.$refs)
scope.$els = Object.create(parentScope.$els)
// make sure point $parent to parent scope
scope.$parent = parentScope
// for two-way binding on alias
scope.$forContext = this
// define scope properties
// important: define the scope alias without forced conversion
// so that frozen data structures remain non-reactive.
withoutConversion(() => {
defineReactive(scope, alias, value)
})
defineReactive(scope, '$index', index)
if (key) {
defineReactive(scope, '$key', key)
} else if (scope.$key) {
// avoid accidental fallback
def(scope, '$key', null)
}
if (this.iterator) {
defineReactive(scope, this.iterator, key !== null ? key : index)
}
var frag = this.factory.create(host, scope, this._frag)
frag.forId = this.id
this.cacheFrag(value, frag, index, key)
return frag
},
/**
* Update the v-ref on owner vm.
*/
updateRef () {
var ref = this.descriptor.ref
if (!ref) return
var hash = (this._scope || this.vm).$refs
var refs
if (!this.fromObject) {
refs = this.frags.map(findVmFromFrag)
} else {
refs = {}
this.frags.forEach(function (frag) {
refs[frag.scope.$key] = findVmFromFrag(frag)
})
}
hash[ref] = refs
},
/**
* For option lists, update the containing v-model on
* parent <select>.
*/
updateModel () {
if (this.isOption) {
var parent = this.start.parentNode
var model = parent && parent.__v_model
if (model) {
model.forceUpdate()
}
}
},
/**
* Insert a fragment. Handles staggering.
*
* @param {Fragment} frag
* @param {Number} index
* @param {Node} prevEl
* @param {Boolean} inDocument
*/
insert (frag, index, prevEl, inDocument) {
if (frag.staggerCb) {
frag.staggerCb.cancel()
frag.staggerCb = null
}
var staggerAmount = this.getStagger(frag, index, null, 'enter')
if (inDocument && staggerAmount) {
// create an anchor and insert it synchronously,
// so that we can resolve the correct order without
// worrying about some elements not inserted yet
var anchor = frag.staggerAnchor
if (!anchor) {
anchor = frag.staggerAnchor = createAnchor('stagger-anchor')
anchor.__v_frag = frag
}
after(anchor, prevEl)
var op = frag.staggerCb = cancellable(function () {
frag.staggerCb = null
frag.before(anchor)
remove(anchor)
})
setTimeout(op, staggerAmount)
} else {
frag.before(prevEl.nextSibling)
}
},
/**
* Remove a fragment. Handles staggering.
*
* @param {Fragment} frag
* @param {Number} index
* @param {Number} total
* @param {Boolean} inDocument
*/
remove (frag, index, total, inDocument) {
if (frag.staggerCb) {
frag.staggerCb.cancel()
frag.staggerCb = null
// it's not possible for the same frag to be removed
// twice, so if we have a pending stagger callback,
// it means this frag is queued for enter but removed
// before its transition started. Since it is already
// destroyed, we can just leave it in detached state.
return
}
var staggerAmount = this.getStagger(frag, index, total, 'leave')
if (inDocument && staggerAmount) {
var op = frag.staggerCb = cancellable(function () {
frag.staggerCb = null
frag.remove()
})
setTimeout(op, staggerAmount)
} else {
frag.remove()
}
},
/**
* Move a fragment to a new position.
* Force no transition.
*
* @param {Fragment} frag
* @param {Node} prevEl
*/
move (frag, prevEl) {
// fix a common issue with Sortable:
// if prevEl doesn't have nextSibling, this means it's
// been dragged after the end anchor. Just re-position
// the end anchor to the end of the container.
/* istanbul ignore if */
if (!prevEl.nextSibling) {
this.end.parentNode.appendChild(this.end)
}
frag.before(prevEl.nextSibling, false)
},
/**
* Cache a fragment using track-by or the object key.
*
* @param {*} value
* @param {Fragment} frag
* @param {Number} index
* @param {String} [key]
*/
cacheFrag (value, frag, index, key) {
var trackByKey = this.params.trackBy
var cache = this.cache
var primitive = !isObject(value)
var id
if (key || trackByKey || primitive) {
id = trackByKey
? trackByKey === '$index'
? index
: getPath(value, trackByKey)
: (key || value)
if (!cache[id]) {
cache[id] = frag
} else if (trackByKey !== '$index') {
process.env.NODE_ENV !== 'production' &&
this.warnDuplicate(value)
}
} else {
id = this.id
if (hasOwn(value, id)) {
if (value[id] === null) {
value[id] = frag
} else {
process.env.NODE_ENV !== 'production' &&
this.warnDuplicate(value)
}
} else {
def(value, id, frag)
}
}
frag.raw = value
},
/**
* Get a cached fragment from the value/index/key
*
* @param {*} value
* @param {Number} index
* @param {String} key
* @return {Fragment}
*/
getCachedFrag (value, index, key) {
var trackByKey = this.params.trackBy
var primitive = !isObject(value)
var frag
if (key || trackByKey || primitive) {
var id = trackByKey
? trackByKey === '$index'
? index
: getPath(value, trackByKey)
: (key || value)
frag = this.cache[id]
} else {
frag = value[this.id]
}
if (frag && (frag.reused || frag.fresh)) {
process.env.NODE_ENV !== 'production' &&
this.warnDuplicate(value)
}
return frag
},
/**
* Delete a fragment from cache.
*
* @param {Fragment} frag
*/
deleteCachedFrag (frag) {
var value = frag.raw
var trackByKey = this.params.trackBy
var scope = frag.scope
var index = scope.$index
// fix #948: avoid accidentally fall through to
// a parent repeater which happens to have $key.
var key = hasOwn(scope, '$key') && scope.$key
var primitive = !isObject(value)
if (trackByKey || key || primitive) {
var id = trackByKey
? trackByKey === '$index'
? index
: getPath(value, trackByKey)
: (key || value)
this.cache[id] = null
} else {
value[this.id] = null
frag.raw = null
}
},
/**
* Get the stagger amount for an insertion/removal.
*
* @param {Fragment} frag
* @param {Number} index
* @param {Number} total
* @param {String} type
*/
getStagger (frag, index, total, type) {
type = type + 'Stagger'
var trans = frag.node.__v_trans
var hooks = trans && trans.hooks
var hook = hooks && (hooks[type] || hooks.stagger)
return hook
? hook.call(frag, index, total)
: index * parseInt(this.params[type] || this.params.stagger, 10)
},
/**
* Pre-process the value before piping it through the
* filters. This is passed to and called by the watcher.
*/
_preProcess (value) {
// regardless of type, store the un-filtered raw value.
this.rawValue = value
return value
},
/**
* Post-process the value after it has been piped through
* the filters. This is passed to and called by the watcher.
*
* It is necessary for this to be called during the
* wathcer's dependency collection phase because we want
* the v-for to update when the source Object is mutated.
*/
_postProcess (value) {
if (isArray(value)) {
return value
} else if (isPlainObject(value)) {
// convert plain object to array.
var keys = Object.keys(value)
var i = keys.length
var res = new Array(i)
var key
while (i--) {
key = keys[i]
res[i] = {
$key: key,
$value: value[key]
}
}
return res
} else {
if (typeof value === 'number' && !isNaN(value)) {
value = range(value)
}
return value || []
}
},
unbind () {
if (this.descriptor.ref) {
(this._scope || this.vm).$refs[this.descriptor.ref] = null
}
if (this.frags) {
var i = this.frags.length
var frag
while (i--) {
frag = this.frags[i]
this.deleteCachedFrag(frag)
frag.destroy()
}
}
}
}
/**
* Helper to find the previous element that is a fragment
* anchor. This is necessary because a destroyed frag's
* element could still be lingering in the DOM before its
* leaving transition finishes, but its inserted flag
* should have been set to false so we can skip them.
*
* If this is a block repeat, we want to make sure we only
* return frag that is bound to this v-for. (see #929)
*
* @param {Fragment} frag
* @param {Comment|Text} anchor
* @param {String} id
* @return {Fragment}
*/
function findPrevFrag (frag, anchor, id) {
var el = frag.node.previousSibling
/* istanbul ignore if */
if (!el) return
frag = el.__v_frag
while (
(!frag || frag.forId !== id || !frag.inserted) &&
el !== anchor
) {
el = el.previousSibling
/* istanbul ignore if */
if (!el) return
frag = el.__v_frag
}
return frag
}
/**
* Find a vm from a fragment.
*
* @param {Fragment} frag
* @return {Vue|undefined}
*/
function findVmFromFrag (frag) {
let node = frag.node
// handle multi-node frag
if (frag.end) {
while (!node.__vue__ && node !== frag.end && node.nextSibling) {
node = node.nextSibling
}
}
return node.__vue__
}
/**
* Create a range array from given number.
*
* @param {Number} n
* @return {Array}
*/
function range (n) {
var i = -1
var ret = new Array(Math.floor(n))
while (++i < n) {
ret[i] = i
}
return ret
}
if (process.env.NODE_ENV !== 'production') {
vFor.warnDuplicate = function (value) {
warn(
'Duplicate value found in v-for="' + this.descriptor.raw + '": ' +
JSON.stringify(value) + '. Use track-by="$index" if ' +
'you are expecting duplicate values.',
this.vm
)
}
}
export default vFor
import { parseTemplate } from '../../parsers/template'
import {
createAnchor,
before,
replace,
remove,
_toString,
toArray
} from '../../util/index'
export default {
bind () {
// a comment node means this is a binding for
// {{{ inline unescaped html }}}
if (this.el.nodeType === 8) {
// hold nodes
this.nodes = []
// replace the placeholder with proper anchor
this.anchor = createAnchor('v-html')
replace(this.el, this.anchor)
}
},
update (value) {
value = _toString(value)
if (this.nodes) {
this.swap(value)
} else {
this.el.innerHTML = value
}
},
swap (value) {
// remove old nodes
var i = this.nodes.length
while (i--) {
remove(this.nodes[i])
}
// convert new value to a fragment
// do not attempt to retrieve from id selector
var frag = parseTemplate(value, true, true)
// save a reference to these nodes so we can remove later
this.nodes = toArray(frag.childNodes)
before(frag, this.anchor)
}
}
import FragmentFactory from '../../fragment/factory'
import { IF } from '../priorities'
import {
getAttr,
remove,
replace,
createAnchor,
warn
} from '../../util/index'
export default {
priority: IF,
terminal: true,
bind () {
var el = this.el
if (!el.__vue__) {
// check else block
var next = el.nextElementSibling
if (next && getAttr(next, 'v-else') !== null) {
remove(next)
this.elseEl = next
}
// check main block
this.anchor = createAnchor('v-if')
replace(el, this.anchor)
} else {
process.env.NODE_ENV !== 'production' && warn(
'v-if="' + this.expression + '" cannot be ' +
'used on an instance root element.',
this.vm
)
this.invalid = true
}
},
update (value) {
if (this.invalid) return
if (value) {
if (!this.frag) {
this.insert()
}
} else {
this.remove()
}
},
insert () {
if (this.elseFrag) {
this.elseFrag.remove()
this.elseFrag = null
}
// lazy init factory
if (!this.factory) {
this.factory = new FragmentFactory(this.vm, this.el)
}
this.frag = this.factory.create(this._host, this._scope, this._frag)
this.frag.before(this.anchor)
},
remove () {
if (this.frag) {
this.frag.remove()
this.frag = null
}
if (this.elseEl && !this.elseFrag) {
if (!this.elseFactory) {
this.elseFactory = new FragmentFactory(
this.elseEl._context || this.vm,
this.elseEl
)
}
this.elseFrag = this.elseFactory.create(this._host, this._scope, this._frag)
this.elseFrag.before(this.anchor)
}
},
unbind () {
if (this.frag) {
this.frag.destroy()
}
if (this.elseFrag) {
this.elseFrag.destroy()
}
}
}
// text & html
import text from './text'
import html from './html'
// logic control
import vFor from './for'
import vIf from './if'
import show from './show'
// two-way binding
import model from './model/index'
// event handling
import on from './on'
// attributes
import bind from './bind'
// ref & el
import el from './el'
import ref from './ref'
// cloak
import cloak from './cloak'
// must export plain object
export default {
text,
html,
'for': vFor,
'if': vIf,
show,
model,
on,
bind,
el,
ref,
cloak
}
import {
toNumber,
isArray,
indexOf,
looseEqual
} from '../../../util/index'
export default {
bind () {
var self = this
var el = this.el
this.getValue = function () {
return el.hasOwnProperty('_value')
? el._value
: self.params.number
? toNumber(el.value)
: el.value
}
function getBooleanValue () {
var val = el.checked
if (val && el.hasOwnProperty('_trueValue')) {
return el._trueValue
}
if (!val && el.hasOwnProperty('_falseValue')) {
return el._falseValue
}
return val
}
this.listener = function () {
var model = self._watcher.value
if (isArray(model)) {
var val = self.getValue()
if (el.checked) {
if (indexOf(model, val) < 0) {
model.push(val)
}
} else {
model.$remove(val)
}
} else {
self.set(getBooleanValue())
}
}
this.on('change', this.listener)
if (el.hasAttribute('checked')) {
this.afterBind = this.listener
}
},
update (value) {
var el = this.el
if (isArray(value)) {
el.checked = indexOf(value, this.getValue()) > -1
} else {
if (el.hasOwnProperty('_trueValue')) {
el.checked = looseEqual(value, el._trueValue)
} else {
el.checked = !!value
}
}
}
}
import { warn, resolveAsset } from '../../../util/index'
import { MODEL } from '../../priorities'
import text from './text'
import radio from './radio'
import select from './select'
import checkbox from './checkbox'
const handlers = {
text,
radio,
select,
checkbox
}
export default {
priority: MODEL,
twoWay: true,
handlers: handlers,
params: ['lazy', 'number', 'debounce'],
/**
* Possible elements:
* <select>
* <textarea>
* <input type="*">
* - text
* - checkbox
* - radio
* - number
*/
bind () {
// friendly warning...
this.checkFilters()
if (this.hasRead && !this.hasWrite) {
process.env.NODE_ENV !== 'production' && warn(
'It seems you are using a read-only filter with ' +
'v-model="' + this.descriptor.raw + '". ' +
'You might want to use a two-way filter to ensure correct behavior.',
this.vm
)
}
var el = this.el
var tag = el.tagName
var handler
if (tag === 'INPUT') {
handler = handlers[el.type] || handlers.text
} else if (tag === 'SELECT') {
handler = handlers.select
} else if (tag === 'TEXTAREA') {
handler = handlers.text
} else {
process.env.NODE_ENV !== 'production' && warn(
'v-model does not support element type: ' + tag,
this.vm
)
return
}
el.__v_model = this
handler.bind.call(this)
this.update = handler.update
this._unbind = handler.unbind
},
/**
* Check read/write filter stats.
*/
checkFilters () {
var filters = this.filters
if (!filters) return
var i = filters.length
while (i--) {
var filter = resolveAsset(this.vm.$options, 'filters', filters[i].name)
if (typeof filter === 'function' || filter.read) {
this.hasRead = true
}
if (filter.write) {
this.hasWrite = true
}
}
},
unbind () {
this.el.__v_model = null
this._unbind && this._unbind()
}
}
import { toNumber, looseEqual } from '../../../util/index'
export default {
bind () {
var self = this
var el = this.el
this.getValue = function () {
// value overwrite via v-bind:value
if (el.hasOwnProperty('_value')) {
return el._value
}
var val = el.value
if (self.params.number) {
val = toNumber(val)
}
return val
}
this.listener = function () {
self.set(self.getValue())
}
this.on('change', this.listener)
if (el.hasAttribute('checked')) {
this.afterBind = this.listener
}
},
update (value) {
this.el.checked = looseEqual(value, this.getValue())
}
}
import { isArray, toNumber, looseEqual } from '../../../util/index'
export default {
bind () {
var self = this
var el = this.el
// method to force update DOM using latest value.
this.forceUpdate = function () {
if (self._watcher) {
self.update(self._watcher.get())
}
}
// check if this is a multiple select
var multiple = this.multiple = el.hasAttribute('multiple')
// attach listener
this.listener = function () {
var value = getValue(el, multiple)
value = self.params.number
? isArray(value)
? value.map(toNumber)
: toNumber(value)
: value
self.set(value)
}
this.on('change', this.listener)
// if has initial value, set afterBind
var initValue = getValue(el, multiple, true)
if ((multiple && initValue.length) ||
(!multiple && initValue !== null)) {
this.afterBind = this.listener
}
// All major browsers except Firefox resets
// selectedIndex with value -1 to 0 when the element
// is appended to a new parent, therefore we have to
// force a DOM update whenever that happens...
this.vm.$on('hook:attached', this.forceUpdate)
},
update (value) {
var el = this.el
el.selectedIndex = -1
var multi = this.multiple && isArray(value)
var options = el.options
var i = options.length
var op, val
while (i--) {
op = options[i]
val = op.hasOwnProperty('_value')
? op._value
: op.value
/* eslint-disable eqeqeq */
op.selected = multi
? indexOf(value, val) > -1
: looseEqual(value, val)
/* eslint-enable eqeqeq */
}
},
unbind () {
/* istanbul ignore next */
this.vm.$off('hook:attached', this.forceUpdate)
}
}
/**
* Get select value
*
* @param {SelectElement} el
* @param {Boolean} multi
* @param {Boolean} init
* @return {Array|*}
*/
function getValue (el, multi, init) {
var res = multi ? [] : null
var op, val, selected
for (var i = 0, l = el.options.length; i < l; i++) {
op = el.options[i]
selected = init
? op.hasAttribute('selected')
: op.selected
if (selected) {
val = op.hasOwnProperty('_value')
? op._value
: op.value
if (multi) {
res.push(val)
} else {
return val
}
}
}
return res
}
/**
* Native Array.indexOf uses strict equal, but in this
* case we need to match string/numbers with custom equal.
*
* @param {Array} arr
* @param {*} val
*/
function indexOf (arr, val) {
var i = arr.length
while (i--) {
if (looseEqual(arr[i], val)) {
return i
}
}
return -1
}
/* global jQuery */
import {
isIE9,
isAndroid,
toNumber,
_toString,
nextTick,
debounce as _debounce
} from '../../../util/index'
export default {
bind () {
var self = this
var el = this.el
var isRange = el.type === 'range'
var lazy = this.params.lazy
var number = this.params.number
var debounce = this.params.debounce
// handle composition events.
// http://blog.evanyou.me/2014/01/03/composition-event/
// skip this for Android because it handles composition
// events quite differently. Android doesn't trigger
// composition events for language input methods e.g.
// Chinese, but instead triggers them for spelling
// suggestions... (see Discussion/#162)
var composing = false
if (!isAndroid && !isRange) {
this.on('compositionstart', function () {
composing = true
})
this.on('compositionend', function () {
composing = false
// in IE11 the "compositionend" event fires AFTER
// the "input" event, so the input handler is blocked
// at the end... have to call it here.
//
// #1327: in lazy mode this is unecessary.
if (!lazy) {
self.listener()
}
})
}
// prevent messing with the input when user is typing,
// and force update on blur.
this.focused = false
if (!isRange && !lazy) {
this.on('focus', function () {
self.focused = true
})
this.on('blur', function () {
self.focused = false
// do not sync value after fragment removal (#2017)
if (!self._frag || self._frag.inserted) {
self.rawListener()
}
})
}
// Now attach the main listener
this.listener = this.rawListener = function () {
if (composing || !self._bound) {
return
}
var val = number || isRange
? toNumber(el.value)
: el.value
self.set(val)
// force update on next tick to avoid lock & same value
// also only update when user is not typing
nextTick(function () {
if (self._bound && !self.focused) {
self.update(self._watcher.value)
}
})
}
// apply debounce
if (debounce) {
this.listener = _debounce(this.listener, debounce)
}
// Support jQuery events, since jQuery.trigger() doesn't
// trigger native events in some cases and some plugins
// rely on $.trigger()
//
// We want to make sure if a listener is attached using
// jQuery, it is also removed with jQuery, that's why
// we do the check for each directive instance and
// store that check result on itself. This also allows
// easier test coverage control by unsetting the global
// jQuery variable in tests.
this.hasjQuery = typeof jQuery === 'function'
if (this.hasjQuery) {
const method = jQuery.fn.on ? 'on' : 'bind'
jQuery(el)[method]('change', this.rawListener)
if (!lazy) {
jQuery(el)[method]('input', this.listener)
}
} else {
this.on('change', this.rawListener)
if (!lazy) {
this.on('input', this.listener)
}
}
// IE9 doesn't fire input event on backspace/del/cut
if (!lazy && isIE9) {
this.on('cut', function () {
nextTick(self.listener)
})
this.on('keyup', function (e) {
if (e.keyCode === 46 || e.keyCode === 8) {
self.listener()
}
})
}
// set initial value if present
if (
el.hasAttribute('value') ||
(el.tagName === 'TEXTAREA' && el.value.trim())
) {
this.afterBind = this.listener
}
},
update (value) {
this.el.value = _toString(value)
},
unbind () {
var el = this.el
if (this.hasjQuery) {
const method = jQuery.fn.off ? 'off' : 'unbind'
jQuery(el)[method]('change', this.listener)
jQuery(el)[method]('input', this.listener)
}
}
}
import { on, off, warn } from '../../util/index'
import { ON } from '../priorities'
// keyCode aliases
const keyCodes = {
esc: 27,
tab: 9,
enter: 13,
space: 32,
'delete': [8, 46],
up: 38,
left: 37,
right: 39,
down: 40
}
function keyFilter (handler, keys) {
var codes = keys.map(function (key) {
var charCode = key.charCodeAt(0)
if (charCode > 47 && charCode < 58) {
return parseInt(key, 10)
}
if (key.length === 1) {
charCode = key.toUpperCase().charCodeAt(0)
if (charCode > 64 && charCode < 91) {
return charCode
}
}
return keyCodes[key]
})
codes = [].concat.apply([], codes)
return function keyHandler (e) {
if (codes.indexOf(e.keyCode) > -1) {
return handler.call(this, e)
}
}
}
function stopFilter (handler) {
return function stopHandler (e) {
e.stopPropagation()
return handler.call(this, e)
}
}
function preventFilter (handler) {
return function preventHandler (e) {
e.preventDefault()
return handler.call(this, e)
}
}
function selfFilter (handler) {
return function selfHandler (e) {
if (e.target === e.currentTarget) {
return handler.call(this, e)
}
}
}
export default {
priority: ON,
acceptStatement: true,
keyCodes,
bind () {
// deal with iframes
if (
this.el.tagName === 'IFRAME' &&
this.arg !== 'load'
) {
var self = this
this.iframeBind = function () {
on(
self.el.contentWindow,
self.arg,
self.handler,
self.modifiers.capture
)
}
this.on('load', this.iframeBind)
}
},
update (handler) {
// stub a noop for v-on with no value,
// e.g. @mousedown.prevent
if (!this.descriptor.raw) {
handler = function () {}
}
if (typeof handler !== 'function') {
process.env.NODE_ENV !== 'production' && warn(
'v-on:' + this.arg + '="' +
this.expression + '" expects a function value, ' +
'got ' + handler,
this.vm
)
return
}
// apply modifiers
if (this.modifiers.stop) {
handler = stopFilter(handler)
}
if (this.modifiers.prevent) {
handler = preventFilter(handler)
}
if (this.modifiers.self) {
handler = selfFilter(handler)
}
// key filter
var keys = Object.keys(this.modifiers)
.filter(function (key) {
return key !== 'stop' && key !== 'prevent' && key !== 'self'
})
if (keys.length) {
handler = keyFilter(handler, keys)
}
this.reset()
this.handler = handler
if (this.iframeBind) {
this.iframeBind()
} else {
on(
this.el,
this.arg,
this.handler,
this.modifiers.capture
)
}
},
reset () {
var el = this.iframeBind
? this.el.contentWindow
: this.el
if (this.handler) {
off(el, this.arg, this.handler)
}
},
unbind () {
this.reset()
}
}
import { warn } from '../../util/index'
export default {
bind () {
process.env.NODE_ENV !== 'production' && warn(
'v-ref:' + this.arg + ' must be used on a child ' +
'component. Found on <' + this.el.tagName.toLowerCase() + '>.',
this.vm
)
}
}
import { getAttr, inDoc } from '../../util/index'
import { applyTransition } from '../../transition/index'
export default {
bind () {
// check else block
var next = this.el.nextElementSibling
if (next && getAttr(next, 'v-else') !== null) {
this.elseEl = next
}
},
update (value) {
this.apply(this.el, value)
if (this.elseEl) {
this.apply(this.elseEl, !value)
}
},
apply (el, value) {
if (inDoc(el)) {
applyTransition(el, value ? 1 : -1, toggle, this.vm)
} else {
toggle()
}
function toggle () {
el.style.display = value ? '' : 'none'
}
}
}
import { _toString } from '../../util/index'
export default {
bind () {
this.attr = this.el.nodeType === 3
? 'data'
: 'textContent'
},
update (value) {
this.el[this.attr] = _toString(value)
}
}
import { getPath } from '../parsers/path'
import vFor from '../directives/public/for'
import {
toArray,
toNumber,
isArray,
isObject,
isPlainObject
} from '../util/index'
const convertArray = vFor._postProcess
/**
* Limit filter for arrays
*
* @param {Number} n
* @param {Number} offset (Decimal expected)
*/
export function limitBy (arr, n, offset) {
offset = offset ? parseInt(offset, 10) : 0
n = toNumber(n)
return typeof n === 'number'
? arr.slice(offset, offset + n)
: arr
}
/**
* Filter filter for arrays
*
* @param {String} search
* @param {String} [delimiter]
* @param {String} ...dataKeys
*/
export function filterBy (arr, search, delimiter) {
arr = convertArray(arr)
if (search == null) {
return arr
}
if (typeof search === 'function') {
return arr.filter(search)
}
// cast to lowercase string
search = ('' + search).toLowerCase()
// allow optional `in` delimiter
// because why not
var n = delimiter === 'in' ? 3 : 2
// extract and flatten keys
var keys = Array.prototype.concat.apply([], toArray(arguments, n))
var res = []
var item, key, val, j
for (var i = 0, l = arr.length; i < l; i++) {
item = arr[i]
val = (item && item.$value) || item
j = keys.length
if (j) {
while (j--) {
key = keys[j]
if ((key === '$key' && contains(item.$key, search)) ||
contains(getPath(val, key), search)) {
res.push(item)
break
}
}
} else if (contains(item, search)) {
res.push(item)
}
}
return res
}
/**
* Filter filter for arrays
*
* @param {String|Array<String>|Function} ...sortKeys
* @param {Number} [order]
*/
export function orderBy (arr) {
let comparator = null
let sortKeys
arr = convertArray(arr)
// determine order (last argument)
let args = toArray(arguments, 1)
let order = args[args.length - 1]
if (typeof order === 'number') {
order = order < 0 ? -1 : 1
args = args.length > 1 ? args.slice(0, -1) : args
} else {
order = 1
}
// determine sortKeys & comparator
let firstArg = args[0]
if (!firstArg) {
return arr
} else if (typeof firstArg === 'function') {
// custom comparator
comparator = function (a, b) {
return firstArg(a, b) * order
}
} else {
// string keys. flatten first
sortKeys = Array.prototype.concat.apply([], args)
comparator = function (a, b, i) {
i = i || 0
return i >= sortKeys.length - 1
? baseCompare(a, b, i)
: baseCompare(a, b, i) || comparator(a, b, i + 1)
}
}
function baseCompare (a, b, sortKeyIndex) {
const sortKey = sortKeys[sortKeyIndex]
if (sortKey) {
if (sortKey !== '$key') {
if (isObject(a) && '$value' in a) a = a.$value
if (isObject(b) && '$value' in b) b = b.$value
}
a = isObject(a) ? getPath(a, sortKey) : a
b = isObject(b) ? getPath(b, sortKey) : b
}
return a === b ? 0 : a > b ? order : -order
}
// sort on a copy to avoid mutating original array
return arr.slice().sort(comparator)
}
/**
* String contain helper
*
* @param {*} val
* @param {String} search
*/
function contains (val, search) {
var i
if (isPlainObject(val)) {
var keys = Object.keys(val)
i = keys.length
while (i--) {
if (contains(val[keys[i]], search)) {
return true
}
}
} else if (isArray(val)) {
i = val.length
while (i--) {
if (contains(val[i], search)) {
return true
}
}
} else if (val != null) {
return val.toString().toLowerCase().indexOf(search) > -1
}
}
import { compile } from '../compiler/index'
import { isTemplate, getOuterHTML } from '../util/index'
import { parseTemplate, cloneNode } from '../parsers/template'
import Fragment from './fragment'
import Cache from '../cache'
const linkerCache = new Cache(5000)
/**
* A factory that can be used to create instances of a
* fragment. Caches the compiled linker if possible.
*
* @param {Vue} vm
* @param {Element|String} el
*/
export default function FragmentFactory (vm, el) {
this.vm = vm
var template
var isString = typeof el === 'string'
if (isString || isTemplate(el)) {
template = parseTemplate(el, true)
} else {
template = document.createDocumentFragment()
template.appendChild(el)
}
this.template = template
// linker can be cached, but only for components
var linker
var cid = vm.constructor.cid
if (cid > 0) {
var cacheId = cid + (isString ? el : getOuterHTML(el))
linker = linkerCache.get(cacheId)
if (!linker) {
linker = compile(template, vm.$options, true)
linkerCache.put(cacheId, linker)
}
} else {
linker = compile(template, vm.$options, true)
}
this.linker = linker
}
/**
* Create a fragment instance with given host and scope.
*
* @param {Vue} host
* @param {Object} scope
* @param {Fragment} parentFrag
*/
FragmentFactory.prototype.create = function (host, scope, parentFrag) {
var frag = cloneNode(this.template)
return new Fragment(this.linker, this.vm, frag, host, scope, parentFrag)
}
import config from '../config'
import { hyphenate } from './lang'
let warn
let formatComponentName
if (process.env.NODE_ENV !== 'production') {
const hasConsole = typeof console !== 'undefined'
warn = (msg, vm) => {
if (hasConsole && (!config.silent)) {
console.error('[Vue warn]: ' + msg + (vm ? formatComponentName(vm) : ''))
}
}
formatComponentName = vm => {
var name = vm._isVue ? vm.$options.name : vm.name
return name
? ' (found in component: <' + hyphenate(name) + '>)'
: ''
}
}
export { warn }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册