|
@@ -1,776 +0,0 @@
|
|
|
-var _ = require('../util')
|
|
|
|
|
-var config = require('../config')
|
|
|
|
|
-var isObject = _.isObject
|
|
|
|
|
-var isPlainObject = _.isPlainObject
|
|
|
|
|
-var textParser = require('../parsers/text')
|
|
|
|
|
-var expParser = require('../parsers/expression')
|
|
|
|
|
-var templateParser = require('../parsers/template')
|
|
|
|
|
-var compiler = require('../compiler')
|
|
|
|
|
-var uid = 0
|
|
|
|
|
-
|
|
|
|
|
-// async component resolution states
|
|
|
|
|
-var UNRESOLVED = 0
|
|
|
|
|
-var PENDING = 1
|
|
|
|
|
-var RESOLVED = 2
|
|
|
|
|
-var ABORTED = 3
|
|
|
|
|
-
|
|
|
|
|
-module.exports = {
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Setup.
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- bind: function () {
|
|
|
|
|
-
|
|
|
|
|
- // some helpful tips...
|
|
|
|
|
- /* istanbul ignore if */
|
|
|
|
|
- if (
|
|
|
|
|
- process.env.NODE_ENV !== 'production' &&
|
|
|
|
|
- this.el.tagName === 'OPTION' &&
|
|
|
|
|
- this.el.parentNode && this.el.parentNode.__v_model
|
|
|
|
|
- ) {
|
|
|
|
|
- _.warn(
|
|
|
|
|
- 'Don\'t use v-repeat for v-model options; ' +
|
|
|
|
|
- 'use the `options` param instead: ' +
|
|
|
|
|
- 'http://vuejs.org/guide/forms.html#Dynamic_Select_Options'
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (process.env.NODE_ENV !== 'production') {
|
|
|
|
|
- _.deprecation.REPEAT()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // support for item in array syntax
|
|
|
|
|
- var inMatch = this.expression.match(/(.*) in (.*)/)
|
|
|
|
|
- if (inMatch) {
|
|
|
|
|
- this.arg = inMatch[1]
|
|
|
|
|
- this._watcherExp = inMatch[2]
|
|
|
|
|
- }
|
|
|
|
|
- // uid as a cache identifier
|
|
|
|
|
- this.id = '__v_repeat_' + (++uid)
|
|
|
|
|
-
|
|
|
|
|
- // setup anchor nodes
|
|
|
|
|
- this.start = _.createAnchor('v-repeat-start')
|
|
|
|
|
- this.end = _.createAnchor('v-repeat-end')
|
|
|
|
|
- _.replace(this.el, this.end)
|
|
|
|
|
- _.before(this.start, this.end)
|
|
|
|
|
-
|
|
|
|
|
- // check if this is a block repeat
|
|
|
|
|
- this.template = _.isTemplate(this.el)
|
|
|
|
|
- ? templateParser.parse(this.el, true)
|
|
|
|
|
- : this.el
|
|
|
|
|
-
|
|
|
|
|
- // check for trackby param
|
|
|
|
|
- this.idKey = this.param('track-by')
|
|
|
|
|
- // check for transition stagger
|
|
|
|
|
- var stagger = +this.param('stagger')
|
|
|
|
|
- this.enterStagger = +this.param('enter-stagger') || stagger
|
|
|
|
|
- this.leaveStagger = +this.param('leave-stagger') || stagger
|
|
|
|
|
-
|
|
|
|
|
- // check for v-ref/v-el
|
|
|
|
|
- this.refId = this.param(config.prefix + 'ref')
|
|
|
|
|
- this.elID = this.param(config.prefix + 'el')
|
|
|
|
|
-
|
|
|
|
|
- if (process.env.NODE_ENV !== 'production') {
|
|
|
|
|
- if (this.refId) _.deprecation.V_REF()
|
|
|
|
|
- if (this.elID) _.deprecation.V_EL()
|
|
|
|
|
- }
|
|
|
|
|
- this.refId = this.refId || this.param('ref')
|
|
|
|
|
-
|
|
|
|
|
- // check other directives that need to be handled
|
|
|
|
|
- // at v-repeat level
|
|
|
|
|
- this.checkIf()
|
|
|
|
|
- this.checkComponent()
|
|
|
|
|
-
|
|
|
|
|
- // create cache object
|
|
|
|
|
- this.cache = Object.create(null)
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Warn against v-if usage.
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- checkIf: function () {
|
|
|
|
|
- if (_.attr(this.el, 'if') !== null) {
|
|
|
|
|
- process.env.NODE_ENV !== 'production' && _.warn(
|
|
|
|
|
- 'Don\'t use v-if with v-repeat. ' +
|
|
|
|
|
- 'Use v-show or the "filterBy" filter instead.'
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Check the component constructor to use for repeated
|
|
|
|
|
- * instances. If static we resolve it now, otherwise it
|
|
|
|
|
- * needs to be resolved at build time with actual data.
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- checkComponent: function () {
|
|
|
|
|
- this.componentState = UNRESOLVED
|
|
|
|
|
- var options = this.vm.$options
|
|
|
|
|
- var id = _.checkComponent(this.el, options)
|
|
|
|
|
- if (!id) {
|
|
|
|
|
- // default constructor
|
|
|
|
|
- this.Component = _.Vue
|
|
|
|
|
- // inline repeats should inherit
|
|
|
|
|
- this.inline = true
|
|
|
|
|
- // important: transclude with no options, just
|
|
|
|
|
- // to ensure block start and block end
|
|
|
|
|
- this.template = compiler.transclude(this.template)
|
|
|
|
|
- var copy = _.extend({}, options)
|
|
|
|
|
- copy._asComponent = false
|
|
|
|
|
- this._linkFn = compiler.compile(this.template, copy)
|
|
|
|
|
- } else {
|
|
|
|
|
- this.Component = null
|
|
|
|
|
- this.asComponent = true
|
|
|
|
|
- // check inline-template
|
|
|
|
|
- if (this.param('inline-template') !== null) {
|
|
|
|
|
- // extract inline template as a DocumentFragment
|
|
|
|
|
- this.inlineTemplate = _.extractContent(this.el, true)
|
|
|
|
|
- }
|
|
|
|
|
- var tokens = textParser.parse(id)
|
|
|
|
|
- if (tokens) {
|
|
|
|
|
- // dynamic component to be resolved later
|
|
|
|
|
- var componentExp = textParser.tokensToExp(tokens)
|
|
|
|
|
- this.componentGetter = expParser.parse(componentExp).get
|
|
|
|
|
- } else {
|
|
|
|
|
- // static
|
|
|
|
|
- this.componentId = id
|
|
|
|
|
- this.pendingData = null
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- resolveComponent: function () {
|
|
|
|
|
- this.componentState = PENDING
|
|
|
|
|
- this.vm._resolveComponent(this.componentId, _.bind(function (Component) {
|
|
|
|
|
- if (this.componentState === ABORTED) {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- this.Component = Component
|
|
|
|
|
- this.componentState = RESOLVED
|
|
|
|
|
- this.realUpdate(this.pendingData)
|
|
|
|
|
- this.pendingData = null
|
|
|
|
|
- }, this))
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Resolve a dynamic component to use for an instance.
|
|
|
|
|
- * The tricky part here is that there could be dynamic
|
|
|
|
|
- * components depending on instance data.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Object} data
|
|
|
|
|
- * @param {Object} meta
|
|
|
|
|
- * @return {Function}
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- resolveDynamicComponent: function (data, meta) {
|
|
|
|
|
- // create a temporary context object and copy data
|
|
|
|
|
- // and meta properties onto it.
|
|
|
|
|
- // use _.define to avoid accidentally overwriting scope
|
|
|
|
|
- // properties.
|
|
|
|
|
- var context = Object.create(this.vm)
|
|
|
|
|
- var key
|
|
|
|
|
- for (key in data) {
|
|
|
|
|
- _.define(context, key, data[key])
|
|
|
|
|
- }
|
|
|
|
|
- for (key in meta) {
|
|
|
|
|
- _.define(context, key, meta[key])
|
|
|
|
|
- }
|
|
|
|
|
- var id = this.componentGetter.call(context, context)
|
|
|
|
|
- var Component = _.resolveAsset(this.vm.$options, 'components', id)
|
|
|
|
|
- if (process.env.NODE_ENV !== 'production') {
|
|
|
|
|
- _.assertAsset(Component, 'component', id)
|
|
|
|
|
- }
|
|
|
|
|
- if (!Component.options) {
|
|
|
|
|
- process.env.NODE_ENV !== 'production' && _.warn(
|
|
|
|
|
- 'Async resolution is not supported for v-repeat ' +
|
|
|
|
|
- '+ dynamic component. (component: ' + id + ')'
|
|
|
|
|
- )
|
|
|
|
|
- return _.Vue
|
|
|
|
|
- }
|
|
|
|
|
- return Component
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Update.
|
|
|
|
|
- * This is called whenever the Array mutates. If we have
|
|
|
|
|
- * a component, we might need to wait for it to resolve
|
|
|
|
|
- * asynchronously.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Array|Number|String} data
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- update: function (data) {
|
|
|
|
|
- if (this.componentId) {
|
|
|
|
|
- var state = this.componentState
|
|
|
|
|
- if (state === UNRESOLVED) {
|
|
|
|
|
- this.pendingData = data
|
|
|
|
|
- // once resolved, it will call realUpdate
|
|
|
|
|
- this.resolveComponent()
|
|
|
|
|
- } else if (state === PENDING) {
|
|
|
|
|
- this.pendingData = data
|
|
|
|
|
- } else if (state === RESOLVED) {
|
|
|
|
|
- this.realUpdate(data)
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- this.realUpdate(data)
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * The real update that actually modifies the DOM.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Array|Number|String} data
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- realUpdate: function (data) {
|
|
|
|
|
- this.vms = this.diff(data, this.vms)
|
|
|
|
|
- // update v-ref
|
|
|
|
|
- if (this.refId) {
|
|
|
|
|
- this.vm.$[this.refId] = this.converted
|
|
|
|
|
- ? toRefObject(this.vms)
|
|
|
|
|
- : this.vms
|
|
|
|
|
- }
|
|
|
|
|
- if (this.elID) {
|
|
|
|
|
- this.vm.$$[this.elID] = this.vms.map(function (vm) {
|
|
|
|
|
- return vm.$el
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 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
|
|
|
|
|
- * @param {Array} oldVms
|
|
|
|
|
- * @return {Array}
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- diff: function (data, oldVms) {
|
|
|
|
|
- var idKey = this.idKey
|
|
|
|
|
- var converted = this.converted
|
|
|
|
|
- var start = this.start
|
|
|
|
|
- var end = this.end
|
|
|
|
|
- var inDoc = _.inDoc(start)
|
|
|
|
|
- var alias = this.arg
|
|
|
|
|
- var init = !oldVms
|
|
|
|
|
- var vms = new Array(data.length)
|
|
|
|
|
- var obj, raw, vm, i, l, primitive
|
|
|
|
|
- // First pass, go through the new Array and fill up
|
|
|
|
|
- // the new vms 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++) {
|
|
|
|
|
- obj = data[i]
|
|
|
|
|
- raw = converted ? obj.$value : obj
|
|
|
|
|
- primitive = !isObject(raw)
|
|
|
|
|
- vm = !init && this.getVm(raw, i, converted ? obj.$key : null)
|
|
|
|
|
- if (vm) { // reusable instance
|
|
|
|
|
-
|
|
|
|
|
- if (process.env.NODE_ENV !== 'production' && vm._reused) {
|
|
|
|
|
- _.warn(
|
|
|
|
|
- 'Duplicate objects found in v-repeat="' + this.expression + '": ' +
|
|
|
|
|
- JSON.stringify(raw)
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- vm._reused = true
|
|
|
|
|
- vm.$index = i // update $index
|
|
|
|
|
- // update data for track-by or object repeat,
|
|
|
|
|
- // since in these two cases the data is replaced
|
|
|
|
|
- // rather than mutated.
|
|
|
|
|
- if (idKey || converted || primitive) {
|
|
|
|
|
- if (alias) {
|
|
|
|
|
- vm[alias] = raw
|
|
|
|
|
- } else if (_.isPlainObject(raw)) {
|
|
|
|
|
- vm.$data = raw
|
|
|
|
|
- } else {
|
|
|
|
|
- vm.$value = raw
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else { // new instance
|
|
|
|
|
- vm = this.build(obj, i, true)
|
|
|
|
|
- vm._reused = false
|
|
|
|
|
- }
|
|
|
|
|
- vms[i] = vm
|
|
|
|
|
- // insert if this is first run
|
|
|
|
|
- if (init) {
|
|
|
|
|
- vm.$before(end)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // if this is the first run, we're done.
|
|
|
|
|
- if (init) {
|
|
|
|
|
- return vms
|
|
|
|
|
- }
|
|
|
|
|
- // Second pass, go through the old vm instances and
|
|
|
|
|
- // destroy those who are not reused (and remove them
|
|
|
|
|
- // from cache)
|
|
|
|
|
- var removalIndex = 0
|
|
|
|
|
- var totalRemoved = oldVms.length - vms.length
|
|
|
|
|
- for (i = 0, l = oldVms.length; i < l; i++) {
|
|
|
|
|
- vm = oldVms[i]
|
|
|
|
|
- if (!vm._reused) {
|
|
|
|
|
- this.uncacheVm(vm)
|
|
|
|
|
- vm.$destroy(false, true) // defer cleanup until removal
|
|
|
|
|
- this.remove(vm, removalIndex++, totalRemoved, inDoc)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // final pass, move/insert new instances into the
|
|
|
|
|
- // right place.
|
|
|
|
|
- var targetPrev, prevEl, currentPrev
|
|
|
|
|
- var insertionIndex = 0
|
|
|
|
|
- for (i = 0, l = vms.length; i < l; i++) {
|
|
|
|
|
- vm = vms[i]
|
|
|
|
|
- // this is the vm that we should be after
|
|
|
|
|
- targetPrev = vms[i - 1]
|
|
|
|
|
- prevEl = targetPrev
|
|
|
|
|
- ? targetPrev._staggerCb
|
|
|
|
|
- ? targetPrev._staggerAnchor
|
|
|
|
|
- : targetPrev._fragmentEnd || targetPrev.$el
|
|
|
|
|
- : start
|
|
|
|
|
- if (vm._reused && !vm._staggerCb) {
|
|
|
|
|
- currentPrev = findPrevVm(vm, start, this.id)
|
|
|
|
|
- if (currentPrev !== targetPrev) {
|
|
|
|
|
- this.move(vm, prevEl)
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // new instance, or still in stagger.
|
|
|
|
|
- // insert with updated stagger index.
|
|
|
|
|
- this.insert(vm, insertionIndex++, prevEl, inDoc)
|
|
|
|
|
- }
|
|
|
|
|
- vm._reused = false
|
|
|
|
|
- }
|
|
|
|
|
- return vms
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Build a new instance and cache it.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Object} data
|
|
|
|
|
- * @param {Number} index
|
|
|
|
|
- * @param {Boolean} needCache
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- build: function (data, index, needCache) {
|
|
|
|
|
- var meta = { $index: index }
|
|
|
|
|
- if (this.converted) {
|
|
|
|
|
- meta.$key = data.$key
|
|
|
|
|
- }
|
|
|
|
|
- var raw = this.converted ? data.$value : data
|
|
|
|
|
- var alias = this.arg
|
|
|
|
|
- if (alias) {
|
|
|
|
|
- data = {}
|
|
|
|
|
- data[alias] = raw
|
|
|
|
|
- } else if (!isPlainObject(raw)) {
|
|
|
|
|
- // non-object values
|
|
|
|
|
- data = {}
|
|
|
|
|
- meta.$value = raw
|
|
|
|
|
- } else {
|
|
|
|
|
- // default
|
|
|
|
|
- data = raw
|
|
|
|
|
- }
|
|
|
|
|
- // resolve constructor
|
|
|
|
|
- var Component = this.Component || this.resolveDynamicComponent(data, meta)
|
|
|
|
|
- var parent = this._host || this.vm
|
|
|
|
|
- var vm = parent.$addChild({
|
|
|
|
|
- el: templateParser.clone(this.template),
|
|
|
|
|
- data: data,
|
|
|
|
|
- inherit: this.inline,
|
|
|
|
|
- template: this.inlineTemplate,
|
|
|
|
|
- // repeater meta, e.g. $index, $key
|
|
|
|
|
- _meta: meta,
|
|
|
|
|
- // mark this as an inline-repeat instance
|
|
|
|
|
- _repeat: this.inline,
|
|
|
|
|
- // is this a component?
|
|
|
|
|
- _asComponent: this.asComponent,
|
|
|
|
|
- // linker cachable if no inline-template
|
|
|
|
|
- _linkerCachable: !this.inlineTemplate && Component !== _.Vue,
|
|
|
|
|
- // pre-compiled linker for simple repeats
|
|
|
|
|
- _linkFn: this._linkFn,
|
|
|
|
|
- // identifier, shows that this vm belongs to this collection
|
|
|
|
|
- _repeatId: this.id,
|
|
|
|
|
- // transclusion content owner
|
|
|
|
|
- _context: this.vm,
|
|
|
|
|
- // cotnext fragment
|
|
|
|
|
- _frag: this._frag
|
|
|
|
|
- }, Component)
|
|
|
|
|
- // cache instance
|
|
|
|
|
- if (needCache) {
|
|
|
|
|
- this.cacheVm(raw, vm, index, this.converted ? meta.$key : null)
|
|
|
|
|
- }
|
|
|
|
|
- // sync back changes for two-way bindings of primitive values
|
|
|
|
|
- var dir = this
|
|
|
|
|
- if (this.rawType === 'object' && isPrimitive(raw)) {
|
|
|
|
|
- vm.$watch(alias || '$value', function (val) {
|
|
|
|
|
- if (dir.filters) {
|
|
|
|
|
- process.env.NODE_ENV !== 'production' && _.warn(
|
|
|
|
|
- 'You seem to be mutating the $value reference of ' +
|
|
|
|
|
- 'a v-repeat instance (likely through v-model) ' +
|
|
|
|
|
- 'and filtering the v-repeat at the same time. ' +
|
|
|
|
|
- 'This will not work properly with an Array of ' +
|
|
|
|
|
- 'primitive values. Please use an Array of ' +
|
|
|
|
|
- 'Objects instead.'
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- dir._withLock(function () {
|
|
|
|
|
- if (dir.converted) {
|
|
|
|
|
- dir.rawValue[vm.$key] = val
|
|
|
|
|
- } else {
|
|
|
|
|
- dir.rawValue.$set(vm.$index, val)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- return vm
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Unbind, teardown everything
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- unbind: function () {
|
|
|
|
|
- this.componentState = ABORTED
|
|
|
|
|
- if (this.refId) {
|
|
|
|
|
- this.vm.$[this.refId] = null
|
|
|
|
|
- }
|
|
|
|
|
- if (this.vms) {
|
|
|
|
|
- var i = this.vms.length
|
|
|
|
|
- var vm
|
|
|
|
|
- while (i--) {
|
|
|
|
|
- vm = this.vms[i]
|
|
|
|
|
- this.uncacheVm(vm)
|
|
|
|
|
- vm.$destroy()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Cache a vm instance based on its data.
|
|
|
|
|
- *
|
|
|
|
|
- * If the data is an object, we save the vm's reference on
|
|
|
|
|
- * the data object as a hidden property. Otherwise we
|
|
|
|
|
- * cache them in an object and for each primitive value
|
|
|
|
|
- * there is an array in case there are duplicates.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Object} data
|
|
|
|
|
- * @param {Vue} vm
|
|
|
|
|
- * @param {Number} index
|
|
|
|
|
- * @param {String} [key]
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- cacheVm: function (data, vm, index, key) {
|
|
|
|
|
- var idKey = this.idKey
|
|
|
|
|
- var cache = this.cache
|
|
|
|
|
- var primitive = !isObject(data)
|
|
|
|
|
- var id
|
|
|
|
|
- if (key || idKey || primitive) {
|
|
|
|
|
- id = idKey
|
|
|
|
|
- ? idKey === '$index'
|
|
|
|
|
- ? index
|
|
|
|
|
- : data[idKey]
|
|
|
|
|
- : (key || index)
|
|
|
|
|
- if (!cache[id]) {
|
|
|
|
|
- cache[id] = vm
|
|
|
|
|
- } else if (!primitive && idKey !== '$index') {
|
|
|
|
|
- process.env.NODE_ENV !== 'production' && _.warn(
|
|
|
|
|
- 'Duplicate objects with the same track-by key in v-repeat: ' + id
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- id = this.id
|
|
|
|
|
- if (data.hasOwnProperty(id)) {
|
|
|
|
|
- if (data[id] === null) {
|
|
|
|
|
- data[id] = vm
|
|
|
|
|
- } else {
|
|
|
|
|
- process.env.NODE_ENV !== 'production' && _.warn(
|
|
|
|
|
- 'Duplicate objects found in v-repeat="' + this.expression + '": ' +
|
|
|
|
|
- JSON.stringify(data)
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- _.define(data, id, vm)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- vm._raw = data
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Try to get a cached instance from a piece of data.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Object} data
|
|
|
|
|
- * @param {Number} index
|
|
|
|
|
- * @param {String} [key]
|
|
|
|
|
- * @return {Vue|undefined}
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- getVm: function (data, index, key) {
|
|
|
|
|
- var idKey = this.idKey
|
|
|
|
|
- var primitive = !isObject(data)
|
|
|
|
|
- if (key || idKey || primitive) {
|
|
|
|
|
- var id = idKey
|
|
|
|
|
- ? idKey === '$index'
|
|
|
|
|
- ? index
|
|
|
|
|
- : data[idKey]
|
|
|
|
|
- : (key || index)
|
|
|
|
|
- return this.cache[id]
|
|
|
|
|
- } else {
|
|
|
|
|
- return data[this.id]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Delete a cached vm instance.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Vue} vm
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- uncacheVm: function (vm) {
|
|
|
|
|
- var data = vm._raw
|
|
|
|
|
- var idKey = this.idKey
|
|
|
|
|
- var index = vm.$index
|
|
|
|
|
- // fix #948: avoid accidentally fall through to
|
|
|
|
|
- // a parent repeater which happens to have $key.
|
|
|
|
|
- var key = vm.hasOwnProperty('$key') && vm.$key
|
|
|
|
|
- var primitive = !isObject(data)
|
|
|
|
|
- if (idKey || key || primitive) {
|
|
|
|
|
- var id = idKey
|
|
|
|
|
- ? idKey === '$index'
|
|
|
|
|
- ? index
|
|
|
|
|
- : data[idKey]
|
|
|
|
|
- : (key || index)
|
|
|
|
|
- this.cache[id] = null
|
|
|
|
|
- } else {
|
|
|
|
|
- data[this.id] = null
|
|
|
|
|
- vm._raw = null
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Insert an instance.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Vue} vm
|
|
|
|
|
- * @param {Number} index
|
|
|
|
|
- * @param {Node} prevEl
|
|
|
|
|
- * @param {Boolean} inDoc
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- insert: function (vm, index, prevEl, inDoc) {
|
|
|
|
|
- if (vm._staggerCb) {
|
|
|
|
|
- vm._staggerCb.cancel()
|
|
|
|
|
- vm._staggerCb = null
|
|
|
|
|
- }
|
|
|
|
|
- var staggerAmount = this.getStagger(vm, index, null, 'enter')
|
|
|
|
|
- if (inDoc && 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 = vm._staggerAnchor
|
|
|
|
|
- if (!anchor) {
|
|
|
|
|
- anchor = vm._staggerAnchor = _.createAnchor('stagger-anchor')
|
|
|
|
|
- anchor.__vue__ = vm
|
|
|
|
|
- }
|
|
|
|
|
- _.after(anchor, prevEl)
|
|
|
|
|
- var op = vm._staggerCb = _.cancellable(function () {
|
|
|
|
|
- vm._staggerCb = null
|
|
|
|
|
- vm.$before(anchor)
|
|
|
|
|
- _.remove(anchor)
|
|
|
|
|
- })
|
|
|
|
|
- setTimeout(op, staggerAmount)
|
|
|
|
|
- } else {
|
|
|
|
|
- vm.$after(prevEl)
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Move an already inserted instance.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Vue} vm
|
|
|
|
|
- * @param {Node} prevEl
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- move: function (vm, prevEl) {
|
|
|
|
|
- vm.$after(prevEl, null, false)
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Remove an instance.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Vue} vm
|
|
|
|
|
- * @param {Number} index
|
|
|
|
|
- * @param {Boolean} inDoc
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- remove: function (vm, index, total, inDoc) {
|
|
|
|
|
- if (vm._staggerCb) {
|
|
|
|
|
- vm._staggerCb.cancel()
|
|
|
|
|
- vm._staggerCb = null
|
|
|
|
|
- // it's not possible for the same vm to be removed
|
|
|
|
|
- // twice, so if we have a pending stagger callback,
|
|
|
|
|
- // it means this vm 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(vm, index, total, 'leave')
|
|
|
|
|
- if (inDoc && staggerAmount) {
|
|
|
|
|
- var op = vm._staggerCb = _.cancellable(function () {
|
|
|
|
|
- vm._staggerCb = null
|
|
|
|
|
- remove()
|
|
|
|
|
- })
|
|
|
|
|
- setTimeout(op, staggerAmount)
|
|
|
|
|
- } else {
|
|
|
|
|
- remove()
|
|
|
|
|
- }
|
|
|
|
|
- function remove () {
|
|
|
|
|
- vm.$remove(function () {
|
|
|
|
|
- vm._cleanup()
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Get the stagger amount for an insertion/removal.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Vue} vm
|
|
|
|
|
- * @param {Number} index
|
|
|
|
|
- * @param {String} type
|
|
|
|
|
- * @param {Number} total
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- getStagger: function (vm, index, total, type) {
|
|
|
|
|
- type = type + 'Stagger'
|
|
|
|
|
- var trans = vm.$el.__v_trans
|
|
|
|
|
- var hooks = trans && trans.hooks
|
|
|
|
|
- var hook = hooks && (hooks[type] || hooks.stagger)
|
|
|
|
|
- return hook
|
|
|
|
|
- ? hook.call(vm, index, total)
|
|
|
|
|
- : index * this[type]
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Pre-process the value before piping it through the
|
|
|
|
|
- * filters, and convert non-Array objects to arrays.
|
|
|
|
|
- *
|
|
|
|
|
- * This function will be bound to this directive instance
|
|
|
|
|
- * and passed into the watcher.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {*} value
|
|
|
|
|
- * @return {Array}
|
|
|
|
|
- * @private
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- _preProcess: function (value) {
|
|
|
|
|
- // regardless of type, store the un-filtered raw value.
|
|
|
|
|
- this.rawValue = value
|
|
|
|
|
- var type = this.rawType = typeof value
|
|
|
|
|
- if (!isPlainObject(value)) {
|
|
|
|
|
- this.converted = false
|
|
|
|
|
- if (type === 'number') {
|
|
|
|
|
- value = range(value)
|
|
|
|
|
- } else if (type === 'string') {
|
|
|
|
|
- value = _.toArray(value)
|
|
|
|
|
- }
|
|
|
|
|
- return value || []
|
|
|
|
|
- } else {
|
|
|
|
|
- // 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]
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- this.converted = true
|
|
|
|
|
- return res
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Helper to find the previous element that is an instance
|
|
|
|
|
- * root node. This is necessary because a destroyed vm's
|
|
|
|
|
- * element could still be lingering in the DOM before its
|
|
|
|
|
- * leaving transition finishes, but its __vue__ reference
|
|
|
|
|
- * should have been removed so we can skip them.
|
|
|
|
|
- *
|
|
|
|
|
- * If this is a block repeat, we want to make sure we only
|
|
|
|
|
- * return vm that is bound to this v-repeat. (see #929)
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Vue} vm
|
|
|
|
|
- * @param {Comment|Text} anchor
|
|
|
|
|
- * @return {Vue}
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
-function findPrevVm (vm, anchor, id) {
|
|
|
|
|
- var el = vm.$el.previousSibling
|
|
|
|
|
- /* istanbul ignore if */
|
|
|
|
|
- if (!el) return
|
|
|
|
|
- while (
|
|
|
|
|
- (!el.__vue__ || el.__vue__.$options._repeatId !== id) &&
|
|
|
|
|
- el !== anchor
|
|
|
|
|
- ) {
|
|
|
|
|
- el = el.previousSibling
|
|
|
|
|
- }
|
|
|
|
|
- return el.__vue__
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Create a range array from given number.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Number} n
|
|
|
|
|
- * @return {Array}
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
-function range (n) {
|
|
|
|
|
- var i = -1
|
|
|
|
|
- var ret = new Array(n)
|
|
|
|
|
- while (++i < n) {
|
|
|
|
|
- ret[i] = i
|
|
|
|
|
- }
|
|
|
|
|
- return ret
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Convert a vms array to an object ref for v-ref on an
|
|
|
|
|
- * Object value.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {Array} vms
|
|
|
|
|
- * @return {Object}
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
-function toRefObject (vms) {
|
|
|
|
|
- var ref = {}
|
|
|
|
|
- for (var i = 0, l = vms.length; i < l; i++) {
|
|
|
|
|
- ref[vms[i].$key] = vms[i]
|
|
|
|
|
- }
|
|
|
|
|
- return ref
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Check if a value is a primitive one:
|
|
|
|
|
- * String, Number, Boolean, null or undefined.
|
|
|
|
|
- *
|
|
|
|
|
- * @param {*} value
|
|
|
|
|
- * @return {Boolean}
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
-function isPrimitive (value) {
|
|
|
|
|
- var type = typeof value
|
|
|
|
|
- return value == null ||
|
|
|
|
|
- type === 'string' ||
|
|
|
|
|
- type === 'number' ||
|
|
|
|
|
- type === 'boolean'
|
|
|
|
|
-}
|
|
|