/* 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) {
// #3029 only update when the value changes. This prevent
// browsers from overwriting values like selectionStart
value = _toString(value)
if (value !== this.el.value) this.el.value = 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)
}
}
}
|