UNPKG

jsaction

Version:

Google's event delegation library

380 lines (352 loc) 13.5 kB
// Copyright 2011 Google Inc. All rights reserved. /** * * @fileoverview Functions for replaying events by the jsaction * Dispatcher. */ goog.provide('jsaction.createKeyboardEvent'); goog.provide('jsaction.createMouseEvent'); goog.provide('jsaction.createUiEvent'); goog.provide('jsaction.replayEvent'); goog.provide('jsaction.triggerEvent'); goog.require('goog.asserts'); goog.require('goog.functions'); goog.require('jsaction'); goog.require('jsaction.EventType'); goog.require('jsaction.event'); /** * Replays an event. * @param {!jsaction.EventInfo} eventInfo The event info record. */ jsaction.replayEvent = function(eventInfo) { var event = jsaction.createEvent(eventInfo['event'], eventInfo['eventType']); jsaction.triggerEvent(eventInfo['targetElement'], event); }; /** * Checks if a given event was triggered by the keyboard. * @param {string} eventType The event type. * @return {boolean} Whether it's a keyboard event. * @private */ jsaction.isKeyboardEvent_ = function(eventType) { return eventType == jsaction.EventType.KEYPRESS || eventType == jsaction.EventType.KEYDOWN || eventType == jsaction.EventType.KEYUP; }; /** * Checks if a given event was triggered by the mouse. * @param {string} eventType The event type. * @return {boolean} Whether it's a mouse event. * @private */ jsaction.isMouseEvent_ = function(eventType) { // TODO(ruilopes): Verify if Drag events should be bound here. return eventType == jsaction.EventType.CLICK || eventType == jsaction.EventType.DBLCLICK || eventType == jsaction.EventType.MOUSEDOWN || eventType == jsaction.EventType.MOUSEOVER || eventType == jsaction.EventType.MOUSEOUT || eventType == jsaction.EventType.MOUSEMOVE; }; /** * Checks if a given event is a general UI event. * @param {string} eventType The event type. * @return {boolean} Whether it's a focus event. * @private */ jsaction.isUiEvent_ = function(eventType) { // Almost nobody supports the W3C method of creating FocusEvents. // For now, we're going to use the UIEvent as a super-interface. return eventType == jsaction.EventType.FOCUS || eventType == jsaction.EventType.BLUR || eventType == jsaction.EventType.FOCUSIN || eventType == jsaction.EventType.FOCUSOUT || eventType == jsaction.EventType.SCROLL; }; /** * Create a whitespace-delineated list of modifier keys that should be * considered to be active on the event's key. See details at * https://developer.mozilla.org/en/DOM/KeyboardEvent. * @param {boolean} alt Alt pressed. * @param {boolean} ctrl Control pressed. * @param {boolean} meta Command pressed (OSX only). * @param {boolean} shift Shift pressed. * @return {string} The constructed modifier keys string. * @private */ jsaction.createKeyboardModifiersList_ = function(alt, ctrl, meta, shift) { var keys = []; if (alt) { keys.push('Alt'); } if (ctrl) { keys.push('Control'); } if (meta) { keys.push('Meta'); } if (shift) { keys.push('Shift'); } return keys.join(' '); }; /** * Creates a UI event object for replaying through the DOM. * @param {!Event} original The event to create a new event from. * @param {string=} opt_eventType The type this event is being handled as by * jsaction. E.g. blur events are handled as focusout * @return {!Event} The event object. */ jsaction.createUiEvent = function(original, opt_eventType) { var event; if (document.createEvent) { // Event creation as per W3C event model specification. This codepath // is used by most non-IE browsers and also by IE 9 and later. event = document.createEvent('UIEvent'); // On IE and Opera < 12, we must provide non-undefined values to // initEvent, otherwise it will fail. event.initUIEvent( opt_eventType || original.type, goog.isDef(original.bubbles) ? original.bubbles : true, original.cancelable || false, original.view || window, original.detail || 0 //detail ); } else { goog.asserts.assert(document.createEventObject); // Older versions of IE (up to version 8) do not support the // W3C event model. Use the IE specific function instead. event = document.createEventObject(); event.type = opt_eventType || original.type; event.bubbles = goog.isDef(original.bubbles) ? original.bubbles : true; event.cancelable = original.cancelable || false; event.view = original.view || window; event.detail = original.detail || 0; } // Some focus events also have a nullable relatedTarget value which isn't // directly supported in the initUIEvent() method. event.relatedTarget = original.relatedTarget || null; event.originalTimestamp = original.timeStamp; return event; }; /** * Creates a keyboard event object for replaying through the DOM. * @param {!Event} original The event to create a new event from. * @param {string=} opt_eventType The type this event is being handled as by * jsaction. E.g. a keypress is handled as click in some cases. * @return {!Event} The event object. */ jsaction.createKeyboardEvent = function(original, opt_eventType) { var event; if (jsaction.event.isSafari) { // We have to fall back to a generic event for Safari, which has the WebKit // keyCode bug noted below, but is also incapable of fixing it with // Object.defineProperty due to another bug: // https://bugs.webkit.org/show_bug.cgi?id=36423 event = jsaction.createGenericEvent_(original, opt_eventType); event.ctrlKey = original.ctrlKey; event.altKey = original.altKey; event.shiftKey = original.shiftKey; event.metaKey = original.metaKey; event.keyCode = original.keyCode; event.charCode = original.charCode; event.originalTimestamp = original.timeStamp; return event; } if (document.createEvent) { // Event creation as per W3C event model specification. This codepath // is used by most non-IE browsers and also by IE 9 and later. event = document.createEvent('KeyboardEvent'); if (event.initKeyboardEvent) { // W3C DOM Level 3 Events model. var modifiers = jsaction.createKeyboardModifiersList_(original.altKey, original.ctrlKey, original.metaKey, original.shiftKey); event.initKeyboardEvent( opt_eventType || original.type, true, true, window, original.charCode, original.keyCode, original.location, modifiers, original.repeat, original.locale); // Blink and Webkit have a long-standing bug that causes the 'keyCode' and // 'which' properties to always be set to 0 when synthesizing a keyboard // event. Details at: https://bugs.webkit.org/show_bug.cgi?id=16735 // It unfortunately looks like IE9+ also copied this behavior, when they // implemented DOM3 events. We work around it by redefining the noted // properties; a simple assignment here would fail because the native // properties are readonly. if (jsaction.event.isWebKit || jsaction.event.isIe) { var keyCodeGetter = goog.functions.constant(original.keyCode); Object.defineProperty(event, 'keyCode', { get: keyCodeGetter }); Object.defineProperty(event, 'which', { get: keyCodeGetter }); } } else { // Gecko only supports an older/deprecated version from DOM Level 2. See // https://developer.mozilla.org/en/DOM/event.initKeyEvent for details. goog.asserts.assert(event.initKeyEvent); event.initKeyEvent( opt_eventType || original.type, true, true, window, original.ctrlKey, original.altKey, original.shiftKey, original.metaKey, original.keyCode, original.charCode); } } else { // Older versions of IE (up to version 8) do not support the // W3C event model. Use the IE specific function instead. goog.asserts.assert(document.createEventObject); event = document.createEventObject(); event.type = opt_eventType || original.type; event.repeat = original.repeat; event.ctrlKey = original.ctrlKey; event.altKey = original.altKey; event.shiftKey = original.shiftKey; event.metaKey = original.metaKey; event.keyCode = original.keyCode; event.charCode = original.charCode; } event.originalTimestamp = original.timeStamp; return event; }; /** * Creates a mouse event object for replaying through the DOM. * @param {!Event} original The event to create a new event from. * @param {string=} opt_eventType The type this event is being handled as by * jsaction. E.g. a keypress is handled as click in some cases. * @return {!MouseEvent} The event object. */ jsaction.createMouseEvent = function(original, opt_eventType) { var event; if (document.createEvent) { // Event creation as per W3C event model specification. This codepath // is used by most non-IE browsers and also by IE 9 and later. event = document.createEvent('MouseEvent'); // On IE and Opera < 12, we must provide non-undefined values to // initMouseEvent, otherwise it will fail. event.initMouseEvent( opt_eventType || original.type, true, // canBubble true, // cancelable window, original.detail || 1, original.screenX || 0, original.screenY || 0, original.clientX || 0, original.clientY || 0, original.ctrlKey || false, original.altKey || false, original.shiftKey || false, original.metaKey || false, original.button || 0, original.relatedTarget || null); } else { goog.asserts.assert(document.createEventObject); // Older versions of IE (up to version 8) do not support the // W3C event model. Use the IE specific function instead. event = document.createEventObject(); event.type = opt_eventType || original.type; event.clientX = original.clientX; event.clientY = original.clientY; event.button = original.button; event.detail = original.detail; event.ctrlKey = original.ctrlKey; event.altKey = original.altKey; event.shiftKey = original.shiftKey; event.metaKey = original.metaKey; } event.originalTimestamp = original.timeStamp; return event; }; /** * Creates a generic event object for replaying through the DOM. * @param {!Event} original The event to create a new event from. * @param {string=} opt_eventType The type this event is being handled as by * jsaction. E.g. a keypress is handled as click in some cases. * @return {!Event} The event object. * @private */ jsaction.createGenericEvent_ = function(original, opt_eventType) { var event; if (document.createEvent) { // Event creation as per W3C event model specification. This codepath // is used by most non-IE browsers and also by IE 9 and later. event = document.createEvent('Event'); event.initEvent( opt_eventType || original.type, true, true); } else { // Older versions of IE (up to version 8) do not support the // W3C event model. Use the IE specific function instead. goog.asserts.assert(document.createEventObject); event = document.createEventObject(); event.type = opt_eventType || original.type; } event.originalTimestamp = original.timeStamp; return event; }; /** * Creates an event object for replaying through the DOM. * NOTE(ruilopes): This function is visible just for testing. Please don't use * it outside JsAction internal testing. * TODO(ruilopes): Add support for FocusEvent and WheelEvent. * @param {!Event} original The event to create a new event from. * @param {string=} opt_eventType The type this event is being handled as by * jsaction. E.g. a keypress is handled as click in some cases. * @return {!Event} The event object. */ jsaction.createEvent = function(original, opt_eventType) { var event; var eventType; if (original.type == jsaction.EventType.CUSTOM) { eventType = jsaction.EventType.CUSTOM; } else { eventType = opt_eventType || original.type; } if (jsaction.isKeyboardEvent_(eventType)) { event = jsaction.createKeyboardEvent(original, opt_eventType); } else if (jsaction.isMouseEvent_(eventType)) { event = jsaction.createMouseEvent(original, opt_eventType); } else if (jsaction.isUiEvent_(eventType)) { event = jsaction.createUiEvent(original, opt_eventType); } else if (eventType == jsaction.EventType.CUSTOM) { goog.asserts.assert(opt_eventType); event = jsaction.createCustomEvent( opt_eventType, original['detail']['data'], original['detail']['triggeringEvent']); event.originalTimestamp = original.timeStamp; } else { // This ensures we don't send an undefined event object to the replayer. event = jsaction.createGenericEvent_(original, opt_eventType); } return event; }; /** * Sends an event for replay to the DOM. * @param {!EventTarget} target The target for the event. * @param {!Event} event The event object. * @return {boolean} The return value of the event replay, i.e., whether * preventDefault() was called on it. */ jsaction.triggerEvent = function(target, event) { if (target.dispatchEvent) { return target.dispatchEvent(event); } else { goog.asserts.assert(target.fireEvent); return target.fireEvent('on' + event.type, event); } };