simulate-event
Version:
A library for triggering DOM events.
429 lines (400 loc) • 9.77 kB
JavaScript
var extend = require('xtend/mutable')
/**
* Set some default options.
*
* @type {Object}
*/
var eventOptions = {
UIEvent: function () {
return {
view: document.defaultView
}
},
FocusEvent: function () {
return eventOptions.UIEvent.apply(this, arguments)
},
MouseEvent: function (type) {
return {
button: 0,
bubbles: (type !== 'mouseenter' && type !== 'mouseleave'),
cancelable: (type !== 'mouseenter' && type !== 'mouseleave'),
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
clientX: 1,
clientY: 1,
screenX: 0,
screenY: 0,
view: document.defaultView,
relatedTarget: document.documentElement
}
},
WheelEvent: function (type) {
return eventOptions.MouseEvent.apply(this, arguments)
},
KeyboardEvent: function () {
return {
view: document.defaultView,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
keyCode: 0
}
}
}
/**
* Map event names to constructor names.
*
* @type {Object}
*/
var eventTypes = {
beforeprint: 'Event',
afterprint: 'Event',
beforeunload: 'Event',
abort: 'Event',
error: 'Event',
change: 'Event',
submit: 'Event',
reset: 'Event',
cached: 'Event',
canplay: 'Event',
canplaythrough: 'Event',
chargingchange: 'Event',
chargingtimechange: 'Event',
checking: 'Event',
close: 'Event',
downloading: 'Event',
durationchange: 'Event',
emptied: 'Event',
ended: 'Event',
fullscreenchange: 'Event',
fullscreenerror: 'Event',
invalid: 'Event',
levelchange: 'Event',
loadeddata: 'Event',
loadedmetadata: 'Event',
noupdate: 'Event',
obsolete: 'Event',
offline: 'Event',
online: 'Event',
open: 'Event',
orientationchange: 'Event',
pause: 'Event',
pointerlockchange: 'Event',
pointerlockerror: 'Event',
copy: 'Event',
cut: 'Event',
paste: 'Event',
play: 'Event',
playing: 'Event',
ratechange: 'Event',
readystatechange: 'Event',
seeked: 'Event',
seeking: 'Event',
stalled: 'Event',
success: 'Event',
suspend: 'Event',
timeupdate: 'Event',
updateready: 'Event',
visibilitychange: 'Event',
volumechange: 'Event',
waiting: 'Event',
load: 'UIEvent',
unload: 'UIEvent',
resize: 'UIEvent',
scroll: 'UIEvent',
select: 'UIEvent',
drag: 'MouseEvent',
dragenter: 'MouseEvent',
dragleave: 'MouseEvent',
dragover: 'MouseEvent',
dragstart: 'MouseEvent',
dragend: 'MouseEvent',
drop: 'MouseEvent',
touchcancel: 'UIEvent',
touchend: 'UIEvent',
touchenter: 'UIEvent',
touchleave: 'UIEvent',
touchmove: 'UIEvent',
touchstart: 'UIEvent',
blur: 'UIEvent',
focus: 'UIEvent',
focusin: 'UIEvent',
focusout: 'UIEvent',
input: 'UIEvent',
show: 'MouseEvent',
click: 'MouseEvent',
dblclick: 'MouseEvent',
mouseenter: 'MouseEvent',
mouseleave: 'MouseEvent',
mousedown: 'MouseEvent',
mouseup: 'MouseEvent',
mouseover: 'MouseEvent',
mousemove: 'MouseEvent',
mouseout: 'MouseEvent',
contextmenu: 'MouseEvent',
wheel: 'WheelEvent',
message: 'MessageEvent',
storage: 'StorageEvent',
timeout: 'StorageEvent',
keydown: 'KeyboardEvent',
keypress: 'KeyboardEvent',
keyup: 'KeyboardEvent',
progress: 'ProgressEvent',
loadend: 'ProgressEvent',
loadstart: 'ProgressEvent',
popstate: 'PopStateEvent',
hashchange: 'HashChangeEvent',
transitionend: 'TransitionEvent',
compositionend: 'CompositionEvent',
compositionstart: 'CompositionEvent',
compositionupdate: 'CompositionEvent',
pagehide: 'PageTransitionEvent',
pageshow: 'PageTransitionEvent'
}
/**
* Map the event type constructor to the initialization method.
*
* @type {Object}
*/
var eventInit = {
Event: 'initEvent',
UIEvent: 'initUIEvent',
FocusEvent: 'initUIEvent',
MouseEvent: 'initMouseEvent',
WheelEvent: 'initMouseEvent',
MessageEvent: 'initMessageEvent',
StorageEvent: 'initStorageEvent',
KeyboardEvent: 'initKeyboardEvent',
ProgressEvent: 'initEvent',
PopStateEvent: 'initEvent',
TransitionEvent: 'initEvent',
HashChangeEvent: 'initHashChangeEvent',
CompositionEvent: 'initCompositionEvent',
DeviceMotionEvent: 'initDeviceMotionEvent',
PageTransitionEvent: 'initEvent',
DeviceOrientationEvent: 'initDeviceOrientationEvent'
}
/**
* Map the options object to initialization parameters.
*
* @type {Object}
*/
var eventParameters = {
initEvent: [],
initUIEvent: [
'view',
'detail'
],
initKeyboardEvent: [
'view',
'char',
'key',
'location',
'modifiersList',
'repeat',
'locale'
],
initKeyEvent: [
'view',
'ctrlKey',
'altKey',
'shiftKey',
'metaKey',
'keyCode',
'charCode'
],
initMouseEvent: [
'view',
'detail',
'screenX',
'screenY',
'clientX',
'clientY',
'ctrlKey',
'altKey',
'shiftKey',
'metaKey',
'button',
'relatedTarget'
],
initHashChangeEvent: [
'oldURL',
'newURL'
],
initCompositionEvent: [
'view',
'data',
'locale'
],
initDeviceMotionEvent: [
'acceleration',
'accelerationIncludingGravity',
'rotationRate',
'interval'
],
initDeviceOrientationEvent: [
'alpha',
'beta',
'gamma',
'absolute'
],
initMessageEvent: [
'data',
'origin',
'lastEventId',
'source'
],
initStorageEvent: [
'key',
'oldValue',
'newValue',
'url',
'storageArea'
]
}
/**
* Map the event types to constructors.
*
* @type {Object}
*/
var eventConstructors = {
UIEvent: window.UIEvent,
FocusEvent: window.FocusEvent,
MouseEvent: window.MouseEvent,
WheelEvent: window.MouseEvent,
KeyboardEvent: window.KeyboardEvent
}
/**
* Get attributes which must be overriden manually.
*
* @param {String} eventType
* @param {Object} options.
*/
function getOverrides (eventType, options) {
if (eventType === 'KeyboardEvent' && options) {
return {
keyCode: options.keyCode || 0,
key: options.key || 0,
which: options.which || options.keyCode || 0
}
}
}
/**
* Generate an event.
*
* @param {String} type
* @param {Object} options
* @return {Event}
*/
exports.generate = function (type, options) {
// Immediately throw an error when the event name does not translate.
if (!eventTypes.hasOwnProperty(type)) {
throw new SyntaxError('Unsupported event type')
}
var eventType = eventTypes[type]
var event
var key
// Handle parameters which must be manually overridden using
// `Object.defineProperty`.
var overrides = getOverrides(eventType, options)
// Extend a new object with the default and passed in options.
// Existing events already have all of their defaults set.
if (!(options instanceof window.Event)) {
// Check for extra defaults to pass in.
if (eventType in eventOptions) {
options = extend({
bubbles: true,
cancelable: true
}, eventOptions[eventType](type, options), options)
} else {
options = extend({
bubbles: true,
cancelable: true
}, options)
}
}
// Attempt the Event Constructors DOM API.
var Constructor = eventConstructors[eventType] || window.Event
try {
event = new Constructor(type, options)
// Add the override properties.
for (key in overrides) {
Object.defineProperty(event, key, {
value: overrides[key]
})
}
return event
} catch (e) {
// Continue.
}
// In IE11, the Keyboard event does not allow setting the
// keyCode property, even with Object.defineProperty,
// so we have to use UIEvent.
var ua = window.navigator.userAgent.toLowerCase()
var msie = Math.max(ua.indexOf('msie'), ua.indexOf('trident'))
if (msie >= 0 && eventType === 'KeyboardEvent') {
eventType = 'UIEvent'
}
var initEvent = eventInit[eventType]
// In < IE9, the `createEvent` function is not available and we have to
// resort to using `fireEvent`.
if (!document.createEvent) {
event = extend(document.createEventObject(), options)
// Add the override properties.
for (key in overrides) {
Object.defineProperty(event, key, {
value: overrides[key]
})
}
return event
}
event = extend(document.createEvent(eventType), options)
// Handle differences between `initKeyboardEvent` and `initKeyEvent`.
if (initEvent === 'initKeyboardEvent') {
if (event[initEvent] === void 0) {
initEvent = 'initKeyEvent'
} else if (!('modifiersList' in options)) {
var mods = []
if (options.metaKey) mods.push('Meta')
if (options.altKey) mods.push('Alt')
if (options.shiftKey) mods.push('Shift')
if (options.ctrlKey) mods.push('Control')
options['modifiersList'] = mods.join(' ')
}
}
// Map argument names to the option values.
var args = eventParameters[initEvent].map(function (parameter) {
return options[parameter]
})
// Initialize the event using the built-in method.
event[initEvent].apply(
event, [type, options.bubbles, options.cancelable].concat(args)
)
// Add the override properties.
for (key in overrides) {
Object.defineProperty(event, key, {
value: overrides[key]
})
}
return event
}
/**
* Simulate an event which is dispatched on the given element.
*
* @param {Element} element
* @param {String} type
* @param {Object} options
* @return {Boolean}
*/
exports.simulate = function (element, type, options) {
var event = exports.generate(type, options)
// In < IE9, the `createEvent` function is not available and we have to
// resort to using `fireEvent`.
if (!document.createEvent) {
return element.fireEvent('on' + type, event)
}
return element.dispatchEvent(event)
}