emulate-key-in-browser
Version:
emulate browsers reactions on special keys
591 lines (576 loc) • 23.4 kB
JavaScript
var emulateKey = (function () {
'use strict';
function unwrapExports (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
function createCommonjsModule(fn, basedir, module) {
return module = {
path: basedir,
exports: {},
require: function (path, base) {
return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
}
}, fn(module, module.exports), module.exports;
}
function commonjsRequire () {
throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
}
var emulateTab_1 = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.emulateTab = void 0;
var possibleSizeAttributeKeys = new Array('offsetHeight', 'scrollHeight', 'clientHeight');
var shiftModifier = true;
/**
* emulate tab keyboard events and default action (switch to next element)
*
* fist:
* - dispatch tab keydown event at active element
*
* then emulate default action (if not prevented):
* - blur on active element
* - skip keypress action (it's between element switch so there is no target)
* - find next element in selectable elemetns
* - focus on next element
* - tab keyup on next element
*
* or (if default action is prevented):
* - tab keypress on active element
* - tab keyup on active elment
*
* @returns true if switched elemenet and false if default action had been prevented
*/
function emulateTab() {
var source = activeElement() || document.body;
var target = getNextElement(source);
return emulateTabFromSourceToTarget(source, target);
}
exports.emulateTab = emulateTab;
(function (emulateTab) {
/**
* emulate tab from a given element (default is active element)
*
* @returns methods to select tab target
*/
emulateTab.from = emulateTabFrom;
/**
* emulate tab from active element to a given target
*
* @param target element to switch to
* @param sendKeyEventsWithShiftModifier like shift tab would do
* @returns true if switched elemenet and false if default action had been prevented
*/
emulateTab.to = function (target, sendKeyEventsWithShiftModifier) {
if (sendKeyEventsWithShiftModifier === void 0) { sendKeyEventsWithShiftModifier = false; }
return emulateTabFrom(activeElement()).to(target, sendKeyEventsWithShiftModifier);
};
/**
* emulate tab to previous element
*
* @returns true if switched elemenet and false if default action had been prevented
*/
emulateTab.backwards = function () { return emulateTab.to(getPreviousElement(activeElement()), shiftModifier); };
/**
* alias for emulateTab() that is more precise about it's direction
*
* @returns true if switched elemenet and false if default action had been prevented
*/
emulateTab.forwards = function () { return emulateTab(); };
/**
* find all elements that are selectable by tab
*/
emulateTab.findSelectableElements = findAllElementsSelectableByTab;
})(emulateTab = exports.emulateTab || (exports.emulateTab = {}));
function emulateTabFrom(source) {
if (source === void 0) { source = document.body; }
return {
/**
* emulate tab to the element before the starting element (which is the active element by default)
*
* @returns true if switched elemenet and false if default action had been prevented
*/
toPreviousElement: function () { return emulateTabFromSourceToTarget(source, getPreviousElement(), shiftModifier); },
/**
* emulate tab to the element after the starting element (which is the active element by default)
*
* @returns true if switched elemenet and false if default action had been prevented
*/
toNextElement: function () { return emulateTabFromSourceToTarget(source, getNextElement(source)); },
/**
* emulate tab to a custom given element
*
* @param target element after tab
* @param sendKeyEventsWithShiftModifier like shift tab would do
* @returns true if switched elemenet and false if default action had been prevented
*/
to: function (target, sendKeyEventsWithShiftModifier) {
if (sendKeyEventsWithShiftModifier === void 0) { sendKeyEventsWithShiftModifier = false; }
return emulateTabFromSourceToTarget(source, target, sendKeyEventsWithShiftModifier);
},
};
}
function emulateTabFromSourceToTarget(source, target, sendKeyEventsWithShiftModifier) {
if (sendKeyEventsWithShiftModifier === void 0) { sendKeyEventsWithShiftModifier = false; }
var executeDefaultAction = source.dispatchEvent(createTabEvent('keydown', sendKeyEventsWithShiftModifier));
if (executeDefaultAction) {
source.blur();
target.focus();
if (document.activeElement !== target) {
try {
document.activeElement = target;
}
catch (e) {
console.warn('could not switch active element');
}
}
if (target instanceof HTMLInputElement) {
target.selectionStart = 0;
}
var tabKeyup = createTabEvent('keyup', sendKeyEventsWithShiftModifier);
target.dispatchEvent(tabKeyup);
return true;
}
else {
source.dispatchEvent(createTabEvent('keypress', sendKeyEventsWithShiftModifier));
source.dispatchEvent(createTabEvent('keyup', sendKeyEventsWithShiftModifier));
return false;
}
}
function createTabEvent(type, sendKeyEventsWithShiftModifier) {
if (sendKeyEventsWithShiftModifier === void 0) { sendKeyEventsWithShiftModifier = false; }
return new KeyboardEvent(type, {
code: 'Tab',
key: 'Tab',
cancelable: true,
bubbles: true,
shiftKey: sendKeyEventsWithShiftModifier,
});
}
function activeElement() {
var element = document.activeElement;
return element instanceof HTMLElement ? element : undefined;
}
function getPreviousElement(lastElement) {
if (lastElement === void 0) { lastElement = document.body; }
var selectableElements = findAllElementsSelectableByTab();
if (selectableElements.length < 1) {
throw new Error('no selectable elements found');
}
var currentIndex = selectableElements.indexOf(lastElement);
var previousIndex = (currentIndex > 0 ? currentIndex : selectableElements.length) - 1;
var previousElement = selectableElements[previousIndex];
return previousElement;
}
function getNextElement(lastElement) {
if (lastElement === void 0) { lastElement = document.body; }
var selectableElements = findAllElementsSelectableByTab();
if (selectableElements.length < 1) {
throw new Error('no selectable elements found');
}
var currentIndex = selectableElements.indexOf(lastElement);
var nextIndex = currentIndex + 1 < selectableElements.length ? currentIndex + 1 : 0;
var nextElement = selectableElements[nextIndex];
return nextElement;
}
function findAllElementsSelectableByTab() {
var allElements = Array.from(document.querySelectorAll('*')).filter(isHtmlElement);
initIsVisibleOnce();
var tabGroups = allElements
.filter(testAll(hasValidTabIndex, isVisible, isNotDisabledInput, isNotSkippableAnchor, isNotCollapsed))
.reduce(function (grouped, element) {
var tabIndex = element.tabIndex;
var tabGroup = grouped['' + tabIndex] || { tabIndex: tabIndex, elements: [] };
tabGroup.elements.push(element);
grouped[tabIndex] = tabGroup;
return grouped;
}, {});
var selectableElements = Object.values(tabGroups)
.sort(byComparingTabIndex)
.reduce(function (all, more) { return all.concat(more.elements); }, new Array());
return selectableElements;
}
var isHtmlElement = function (element) { return element instanceof HTMLElement; };
var isVisible;
var initIsVisibleOnce = function () {
var sizeAttributeKey = findSizeAttributeKey();
if (sizeAttributeKey) {
// console.log('use isVisible by size attribute: ' + sizeAttributeKey);
isVisible = isVisibleBySize(sizeAttributeKey);
}
else {
// console.log('use isVisible by parents');
isVisible = isVisibleByParents;
}
initIsVisibleOnce = function () { };
};
var isVisibleBySize = function (sizeAttribute) { return function (element) {
var size = element[sizeAttribute];
return !!size && typeof size === 'number' && size > 0;
}; };
function findSizeAttributeKey(element) {
if (element === void 0) { element = document.body; }
var htmlElement = element;
var sizeAttributeKey = possibleSizeAttributeKeys
.find(function (key) {
var value = htmlElement[key];
return value && typeof value === 'number' && value > 0;
});
if (sizeAttributeKey) {
return sizeAttributeKey;
}
var childNodes = element.children;
for (var _i = 0, _a = Array.from(childNodes); _i < _a.length; _i++) {
var childNode = _a[_i];
var childAttribute = findSizeAttributeKey(childNode);
if (childAttribute) {
return childAttribute;
}
}
return undefined;
}
var isVisibleByParents = function (element) {
if (!element.isConnected) {
return false;
}
if (element.tagName === 'BODY') {
return true;
}
var style = getComputedStyle(element);
if (style.display === 'none') {
return false;
}
if (style.visibility === 'collapse') {
return false;
}
var parent = element.parentElement;
if (!parent) {
return false;
}
return isVisibleByParents(parent);
};
function testAll() {
var elementFilter = [];
for (var _i = 0; _i < arguments.length; _i++) {
elementFilter[_i] = arguments[_i];
}
return function (element) { return !elementFilter.some(function (f) { return !f(element); }); };
}
function hasValidTabIndex(element) {
return typeof element.tabIndex === 'number' && element.tabIndex >= 0;
}
var isNotDisabledInput = function (element) { return !element.disabled; };
function isNotSkippableAnchor(element) {
return !(element instanceof HTMLAnchorElement)
|| !!element.href
|| element.getAttribute('tabIndex') !== null;
}
var isNotCollapsed = function (element) {
return getComputedStyle(element).visibility !== 'collapse';
};
function byComparingTabIndex(a, b) {
if (a.tabIndex > 0 && b.tabIndex > 0) {
return a.tabIndex - b.tabIndex;
}
if (a.tabIndex > 0) {
return -a.tabIndex;
}
if (b.tabIndex > 0) {
return b.tabIndex;
}
throw new Error('same tab index for two groups');
}
});
var emulateTab = /*@__PURE__*/unwrapExports(emulateTab_1);
function isTextInputElement(element) {
return (element instanceof HTMLInputElement)
|| (element instanceof HTMLTextAreaElement);
}
function getValueLength(target) {
return target.value && target.value.length || 0;
}
function findSelectionStart(target, valueLength) {
if (valueLength === void 0) { valueLength = getValueLength(target); }
if (typeof target.selectionStart === 'number')
return target.selectionStart;
if (typeof target.selectionEnd === 'number')
return target.selectionEnd;
return valueLength;
}
function findSelectionEnd(target, valueLength) {
if (valueLength === void 0) { valueLength = getValueLength(target); }
if (typeof target.selectionEnd === 'number')
return target.selectionEnd;
if (typeof target.selectionStart === 'number')
return target.selectionStart;
return valueLength;
}
function findKeyTarget() {
return document.activeElement || document.body;
}
function emulateKeyEvents(key, textInputAction, target, options) {
if (target === void 0) { target = findKeyTarget(); }
var executeDefaultActions = target.dispatchEvent(new KeyboardEvent('keydown', { key: key, cancelable: true, bubbles: true }));
executeDefaultActions = target.dispatchEvent(new KeyboardEvent('keypress', { key: key, cancelable: true, bubbles: options === null || options === void 0 ? void 0 : options.keypressBubbles })) && executeDefaultActions;
if (executeDefaultActions && textInputAction && isTextInputElement(target)) {
executeDefaultActions = textInputAction(target);
}
executeDefaultActions = target.dispatchEvent(new KeyboardEvent('keyup', { key: key, cancelable: true })) && executeDefaultActions;
}
var noEventThatCouldGetCanceled = true;
var emulateArrow = {
up: function () { return emulateArrowUp(); },
right: function () { return emulateArrowRight(); },
down: function () { return emulateArrowDown(); },
left: function () { return emulateArrowLeft(); },
shift: {
up: function () { return emulateShiftArrowUp(); },
right: function () { return emulateShiftArrowRight(); },
down: function () { return emulateShiftArrowDown(); },
left: function () { return emulateShiftArrowLeft(); }
}
};
function emulateArrowUp() {
return emulateKeyEvents('ArrowUp', function (target) {
var lineLength = lineLengthOf(target);
var selectionStart = findSelectionStart(target);
setCursorOf(target, Math.max(selectionStart - lineLength, 0));
return noEventThatCouldGetCanceled;
});
}
function setCursorOf(target, position) {
target.setSelectionRange(position, position, 'none');
}
function lineLengthOf(target, valueLength) {
if (valueLength === void 0) { valueLength = valueLengthOf(target); }
return target instanceof HTMLTextAreaElement ? target.cols : valueLength;
}
function valueLengthOf(target) {
return target.value && target.value.length || 0;
}
function emulateArrowRight() {
return emulateKeyEvents('ArrowRight', function (target) {
if (target.selectionStart !== target.selectionEnd) {
setCursorOf(target, findSelectionEnd(target));
return noEventThatCouldGetCanceled;
}
var valueLength = valueLengthOf(target);
var selectionEnd = findSelectionEnd(target);
if (selectionEnd < valueLength) {
setCursorOf(target, selectionEnd + 1);
}
return noEventThatCouldGetCanceled;
});
}
function emulateArrowDown() {
return emulateKeyEvents('ArrowDown', function (target) {
var valueLength = valueLengthOf(target);
var lineLength = lineLengthOf(target, valueLength);
var selectionEnd = findSelectionEnd(target);
setCursorOf(target, Math.min(selectionEnd + lineLength, valueLength));
return noEventThatCouldGetCanceled;
});
}
function emulateArrowLeft() {
return emulateKeyEvents('ArrowLeft', function (target) {
if (target.selectionStart !== target.selectionEnd) {
setCursorOf(target, findSelectionStart(target));
target.selectionEnd = target.selectionStart;
return noEventThatCouldGetCanceled;
}
var selectionStart = findSelectionStart(target);
if (selectionStart > 0) {
setCursorOf(target, selectionStart - 1);
}
return noEventThatCouldGetCanceled;
});
}
function emulateShiftArrowUp() {
return emulateKeyEvents('ArrowUp', function (target) {
var lineLength = lineLengthOf(target);
if (isSelectionDirectionOf(target, 'forward')) {
reduceSelectionAtSelectionEnd(target, lineLength);
}
else {
extendSelectionAtSelectionStart(target, lineLength);
}
return noEventThatCouldGetCanceled;
});
}
function isSelectionDirectionOf(target, direction) {
return target.selectionDirection === direction && (target.selectionStart !== target.selectionEnd);
}
function extendSelectionAtSelectionStart(target, lengthToExtend) {
var selectionStart = findSelectionStart(target);
var newSelectionStart = Math.max(selectionStart - lengthToExtend, 0);
target.selectionStart = newSelectionStart;
if (target.selectionDirection !== 'backward') {
target.selectionDirection = 'backward';
}
}
function reduceSelectionAtSelectionEnd(target, lengthToReduce) {
var selectionStart = findSelectionStart(target);
var selectionEnd = findSelectionEnd(target);
var newSelectionEnd = selectionEnd - lengthToReduce;
if (selectionStart < newSelectionEnd) {
target.selectionEnd = newSelectionEnd;
}
else {
target.selectionEnd = selectionStart;
target.selectionDirection = 'none';
}
}
function emulateShiftArrowRight() {
return emulateKeyEvents('ArrowRight', function (target) {
if (isSelectionDirectionOf(target, 'backward')) {
reduceSelectionAtSelectionStart(target, 1);
}
else {
extendSelectionAtSelectionEnd(target, 1);
}
return noEventThatCouldGetCanceled;
});
}
function extendSelectionAtSelectionEnd(target, lengthToExtend) {
var valueLength = valueLengthOf(target);
var selectionEnd = findSelectionEnd(target, valueLength);
target.selectionEnd = Math.min(selectionEnd + lengthToExtend, valueLength);
if (target.selectionDirection !== 'forward') {
target.selectionDirection = 'forward';
}
}
function reduceSelectionAtSelectionStart(target, lengthToReduce) {
var selectionStart = findSelectionStart(target);
var selectionEnd = findSelectionEnd(target);
var newSelectionStart = selectionStart + lengthToReduce;
if (newSelectionStart < selectionEnd) {
target.selectionStart = newSelectionStart;
}
else {
target.selectionStart = selectionEnd;
target.selectionDirection = 'none';
}
}
function emulateShiftArrowDown() {
return emulateKeyEvents('ArrowDown', function (target) {
var lineLength = lineLengthOf(target);
if (isSelectionDirectionOf(target, 'backward')) {
reduceSelectionAtSelectionStart(target, lineLength);
}
else {
extendSelectionAtSelectionEnd(target, lineLength);
}
return noEventThatCouldGetCanceled;
});
}
function emulateShiftArrowLeft() {
return emulateKeyEvents('ArrowLeft', function (target) {
if (isSelectionDirectionOf(target, 'forward')) {
reduceSelectionAtSelectionEnd(target, 1);
}
else {
extendSelectionAtSelectionStart(target, 1);
}
return noEventThatCouldGetCanceled;
});
}
function emulateBackspace() {
return emulateKeyEvents('Backspace', function (target) {
var value = target.value || '';
var valueLength = value.length;
if (valueLength > 0) {
var selectionStart = findSelectionStart(target, valueLength);
var selectionEnd = findSelectionEnd(target, valueLength);
if (selectionStart !== selectionEnd) {
target.value = value.substr(0, selectionStart) + value.substr(selectionEnd);
target.setSelectionRange(selectionStart, selectionStart, 'none');
}
else if (selectionStart > 0) {
var newSelectionStart = selectionStart - 1;
target.value = value.substr(0, newSelectionStart) + value.substr(selectionEnd);
target.setSelectionRange(newSelectionStart, newSelectionStart, 'none');
}
}
return target.dispatchEvent(new InputEvent('input'));
});
}
function emulateDelete() {
return emulateKeyEvents('Delete', function (target) {
var value = target.value || '';
var valueLength = value.length;
if (valueLength > 0) {
var selectionStart = findSelectionStart(target, valueLength);
var selectionEnd = findSelectionEnd(target, valueLength);
if (selectionStart !== selectionEnd) {
target.value = value.substr(0, selectionStart) + value.substr(selectionEnd);
target.setSelectionRange(selectionStart, selectionStart, 'none');
}
else if (selectionEnd < valueLength) {
target.value = value.substr(0, selectionEnd) + value.substr(selectionEnd + 1);
target.setSelectionRange(selectionEnd, selectionEnd, 'none');
}
}
return target.dispatchEvent(new InputEvent('input'));
});
}
var emulateMouse;
(function (emulateMouse) {
emulateMouse.hover = triggerMouseover;
})(emulateMouse || (emulateMouse = {}));
function triggerMouseover(element) {
var offsetBox = getOffsetBox(element);
var clientX = offsetBox.left + Math.floor(offsetBox.width / 2);
var clientY = offsetBox.top + Math.floor(offsetBox.height / 2);
return element.dispatchEvent(new MouseEvent('mouseover', { clientX: clientX, clientY: clientY, button: 0, buttons: 0 }));
}
function getOffsetBox(element) {
var pos = getOffsetPosition(element);
var left = pos.left;
var top = pos.top;
var width = element.offsetWidth;
var height = element.offsetHeight;
var right = left + width;
var bottom = top + height;
return { top: top, right: right, bottom: bottom, left: left, width: width, height: height };
}
function getOffsetPosition(element) {
var left = element.offsetLeft;
var top = element.offsetTop;
var parent = element.offsetParent;
if (parent instanceof HTMLElement) {
var parentPos = getOffsetPosition(parent);
left += parentPos.left;
top += parentPos.top;
}
return { left: left, top: top };
}
function emulateWriteText(keys) {
keys.split('').forEach(writeKey);
}
function writeKey(key) {
return emulateKeyEvents(key, function (target) {
var value = target.value || '';
var valueLength = value.length;
var selectionStart = findSelectionStart(target, valueLength);
if (selectionStart !== valueLength) {
var selectionEnd = findSelectionEnd(target, valueLength);
target.value = value.substr(0, selectionStart) + key + value.substr(selectionEnd);
target.selectionStart = target.selectionEnd = selectionStart + 1;
target.selectionDirection = 'none';
}
else {
target.value += key;
}
return target.dispatchEvent(new InputEvent('input', { bubbles: true }));
}, undefined, { keypressBubbles: true });
}
var emulateKey = {
tab: emulateTab.emulateTab,
shiftTab: emulateTab.emulateTab.backwards,
backspace: emulateBackspace,
"delete": emulateDelete,
mouse: emulateMouse,
arrow: emulateArrow,
shiftArrow: emulateArrow.shift,
writeText: emulateWriteText
};
return emulateKey;
}());