UNPKG

emulate-key-in-browser

Version:
591 lines (576 loc) 23.4 kB
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; }());