lib/events.js
import { getElementClientPageCenter } from './dom';
import { promiseResolveReject } from './general';
/**
* @param {string} type
* @param {number} clickCount
* @return {number}
* @private
*/
function __generateDetailPropForMouseEvent(type, clickCount) {
switch (type) {
case 'click':
case 'dblclick':
return clickCount;
case 'mousedown':
case 'mouseup':
return clickCount + 1;
default:
return 0;
}
}
/**
* @param {string} type
* @param {boolean} [buttons]
* @return {number}
* @private
*/
function __getWhichButtonForEvent(type, buttons) {
switch (type) {
case 'click':
case 'dblclick':
case 'mousedown':
case 'mouseup':
return buttons ? 1 : 0;
default:
return 0;
}
}
/**
* @typedef {Object} CreateMouseEventOptions
* @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent
* @property {string} type - The type of the mouse event to be created
* @property {Element} elem - The element the mouse event will be fired on used to calculate mouse position
* @property {Object} [position] - An object specifying the events position
* @property {Object} [eventOpts] - Additional options to be used
* @property {Window} [view] - The view (window)
* @property {number} [clickCount] - How many clicks have been done before this event
*/
/**
* Creates and returns a new mouse event configured using the supplied options
* @param {CreateMouseEventOptions} config
* @return {MouseEvent}
*/
export function createMouseEvent(config) {
let eventPosition;
if (config.elem && config.position == null) {
eventPosition = getElementClientPageCenter(config.elem);
// my internet skills did not turn up anything on how to fake
// event cords in screen space.... so we just put something here
eventPosition.screenX = eventPosition.clientX + eventPosition.pageX;
eventPosition.screenY = eventPosition.clientY + eventPosition.pageY;
} else {
eventPosition = config.position;
}
const defaultOpts = {
button: __getWhichButtonForEvent(config.type),
buttons: __getWhichButtonForEvent(config.type, true),
bubbles: true,
cancelable: true,
composed: true,
ctrlKey: false,
shiftKey: false,
altKey: false,
metaKey: false,
view: config.view || window,
detail: __generateDetailPropForMouseEvent(
config.type,
config.clickCount || 0
),
};
const eventOpts = Object.assign(defaultOpts, eventPosition, config.eventOpts);
return config.view != null
? new config.view.MouseEvent(config.type, eventOpts)
: new MouseEvent(config.type, eventOpts);
}
/**
* @typedef {Object} FireMouseEventsOnOptions
* @property {Element} elem - The element the mouse event will be fired on used to calculate mouse position
* @property {Array<string>} eventNames - The event types to be fired
* @property {Window} [view] - The view (window)
* @property {Object} [position] - An object specifying the events position
* @property {Object} [eventOpts] - Additional options to be used
* @property {number} [clickCount] - How many clicks have been done before this event
*/
/**
* Creates mouse events and fires them in order configured using the supplied options
* @param {FireMouseEventsOnOptions} config
*/
export function fireMouseEventsOnElement(config) {
const { elem, eventNames, view, position, eventOpts, clickCount } = config;
if (elem != null) {
const numEvents = eventNames.length;
for (var eventIdx = 0; eventIdx < numEvents; ++eventIdx) {
elem.dispatchEvent(
createMouseEvent({
type: eventNames[eventIdx],
elem,
view,
position,
eventOpts,
clickCount,
})
);
}
}
}
/**
* Fires the supplied event on the supplied element
* @param {Element} elem - The element to have an event fired on
* @param {Event} event - The event to be fired
*/
export function fireEventOn(elem, event) {
if (elem != null) {
elem.dispatchEvent(event);
}
}
/**
* Returns a promise that resolves to true once the supplied event is fired from the supplied
* event target. If `max` is supplied and the event is not fired by `max` the promise resolves to false.
* @param {EventTarget} eventTarget - The event target that should fire `event`
* @param {string} event - The event that should be fired from `eventTarget`
* @param {number} [max] - Optional amount of time in milliseconds that defines
* a maximum time to be waited for `event`
* @return {Promise<boolean>}
*/
export function waitForEventTargetToFireEvent(eventTarget, event, max) {
const promResolveReject = promiseResolveReject();
const listener = fromSafety => {
eventTarget.removeEventListener(event, listener);
promResolveReject.resolve(!fromSafety);
};
eventTarget.addEventListener(event, listener);
if (max) {
setTimeout(listener, max, true);
}
return promResolveReject.promise;
}