aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vue/src/platforms/web/runtime/directives/model.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/vue/src/platforms/web/runtime/directives/model.js')
-rw-r--r--node_modules/vue/src/platforms/web/runtime/directives/model.js147
1 files changed, 147 insertions, 0 deletions
diff --git a/node_modules/vue/src/platforms/web/runtime/directives/model.js b/node_modules/vue/src/platforms/web/runtime/directives/model.js
new file mode 100644
index 00000000..cba8ded0
--- /dev/null
+++ b/node_modules/vue/src/platforms/web/runtime/directives/model.js
@@ -0,0 +1,147 @@
+/**
+ * Not type checking this file because flow doesn't like attaching
+ * properties to Elements.
+ */
+
+import { isTextInputType } from 'web/util/element'
+import { looseEqual, looseIndexOf } from 'shared/util'
+import { mergeVNodeHook } from 'core/vdom/helpers/index'
+import { warn, isIE9, isIE, isEdge } from 'core/util/index'
+
+/* istanbul ignore if */
+if (isIE9) {
+ // http://www.matts411.com/post/internet-explorer-9-oninput/
+ document.addEventListener('selectionchange', () => {
+ const el = document.activeElement
+ if (el && el.vmodel) {
+ trigger(el, 'input')
+ }
+ })
+}
+
+const directive = {
+ inserted (el, binding, vnode, oldVnode) {
+ if (vnode.tag === 'select') {
+ // #6903
+ if (oldVnode.elm && !oldVnode.elm._vOptions) {
+ mergeVNodeHook(vnode, 'postpatch', () => {
+ directive.componentUpdated(el, binding, vnode)
+ })
+ } else {
+ setSelected(el, binding, vnode.context)
+ }
+ el._vOptions = [].map.call(el.options, getValue)
+ } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
+ el._vModifiers = binding.modifiers
+ if (!binding.modifiers.lazy) {
+ el.addEventListener('compositionstart', onCompositionStart)
+ el.addEventListener('compositionend', onCompositionEnd)
+ // Safari < 10.2 & UIWebView doesn't fire compositionend when
+ // switching focus before confirming composition choice
+ // this also fixes the issue where some browsers e.g. iOS Chrome
+ // fires "change" instead of "input" on autocomplete.
+ el.addEventListener('change', onCompositionEnd)
+ /* istanbul ignore if */
+ if (isIE9) {
+ el.vmodel = true
+ }
+ }
+ }
+ },
+
+ componentUpdated (el, binding, vnode) {
+ if (vnode.tag === 'select') {
+ setSelected(el, binding, vnode.context)
+ // in case the options rendered by v-for have changed,
+ // it's possible that the value is out-of-sync with the rendered options.
+ // detect such cases and filter out values that no longer has a matching
+ // option in the DOM.
+ const prevOptions = el._vOptions
+ const curOptions = el._vOptions = [].map.call(el.options, getValue)
+ if (curOptions.some((o, i) => !looseEqual(o, prevOptions[i]))) {
+ // trigger change event if
+ // no matching option found for at least one value
+ const needReset = el.multiple
+ ? binding.value.some(v => hasNoMatchingOption(v, curOptions))
+ : binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, curOptions)
+ if (needReset) {
+ trigger(el, 'change')
+ }
+ }
+ }
+ }
+}
+
+function setSelected (el, binding, vm) {
+ actuallySetSelected(el, binding, vm)
+ /* istanbul ignore if */
+ if (isIE || isEdge) {
+ setTimeout(() => {
+ actuallySetSelected(el, binding, vm)
+ }, 0)
+ }
+}
+
+function actuallySetSelected (el, binding, vm) {
+ const value = binding.value
+ const isMultiple = el.multiple
+ if (isMultiple && !Array.isArray(value)) {
+ process.env.NODE_ENV !== 'production' && warn(
+ `<select multiple v-model="${binding.expression}"> ` +
+ `expects an Array value for its binding, but got ${
+ Object.prototype.toString.call(value).slice(8, -1)
+ }`,
+ vm
+ )
+ return
+ }
+ let selected, option
+ for (let i = 0, l = el.options.length; i < l; i++) {
+ option = el.options[i]
+ if (isMultiple) {
+ selected = looseIndexOf(value, getValue(option)) > -1
+ if (option.selected !== selected) {
+ option.selected = selected
+ }
+ } else {
+ if (looseEqual(getValue(option), value)) {
+ if (el.selectedIndex !== i) {
+ el.selectedIndex = i
+ }
+ return
+ }
+ }
+ }
+ if (!isMultiple) {
+ el.selectedIndex = -1
+ }
+}
+
+function hasNoMatchingOption (value, options) {
+ return options.every(o => !looseEqual(o, value))
+}
+
+function getValue (option) {
+ return '_value' in option
+ ? option._value
+ : option.value
+}
+
+function onCompositionStart (e) {
+ e.target.composing = true
+}
+
+function onCompositionEnd (e) {
+ // prevent triggering an input event for no reason
+ if (!e.target.composing) return
+ e.target.composing = false
+ trigger(e.target, 'input')
+}
+
+function trigger (el, type) {
+ const e = document.createEvent('HTMLEvents')
+ e.initEvent(type, true, true)
+ el.dispatchEvent(e)
+}
+
+export default directive