šŸš€ Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more →
Socket
Book a DemoInstallSign in
Socket

arrow-key-navigation

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

arrow-key-navigation - npm Package Compare versions

Comparing version

to
1.2.0

136

dist-node/index.js

@@ -11,33 +11,28 @@ 'use strict';

/* global document, addEventListener, removeEventListener, getSelection */
// This query is adapted from a11y-dialog
// https://github.com/edenspiekermann/a11y-dialog/blob/cf4ed81/a11y-dialog.js#L6-L18
var focusablesQuery = 'a[href], area[href], input, select, textarea, ' + 'button, iframe, object, embed, [contenteditable], [tabindex], ' + 'video[controls], audio[controls], summary'; // TODO: email/number types are a special type, in that they return selectionStart/selectionEnd as null
// TODO: email/number types are a special type, in that they return selectionStart/selectionEnd as null
// As far as I can tell, there is no way to actually get the caret position from these inputs. So we
// don't do the proper caret handling for those inputs, unfortunately.
// https://html.spec.whatwg.org/multipage/input.html#do-not-apply
var textInputTypes = ['text', 'search', 'url', 'password', 'tel'];
var checkboxRadioInputTypes = ['checkbox', 'radio'];
var focusTrapTest = undefined;
var focusTrapTest = undefined; // This query is adapted from a11y-dialog
// https://github.com/edenspiekermann/a11y-dialog/blob/cf4ed81/a11y-dialog.js#L6-L18
function getFocusableElements(activeElement) {
// Respect focus trap inside of dialogs
var dialogParent = getFocusTrapParent(activeElement);
var root = dialogParent || document;
var res = [];
var elements = root.querySelectorAll(focusablesQuery);
var len = elements.length;
var focusablesQuery = 'a[href], area[href], input, select, textarea, ' + 'button, iframe, object, embed, [contenteditable], [tabindex], ' + 'video[controls], audio[controls], summary';
for (var i = 0; i < len; i++) {
var element = elements[i];
function getActiveElement() {
var activeElement = document.activeElement;
if (element === activeElement || !element.disabled && !/^-/.test(element.getAttribute('tabindex') || '') && !element.hasAttribute('inert') && ( // see https://github.com/GoogleChrome/inert-polyfill
element.offsetWidth > 0 || element.offsetHeight > 0)) {
res.push(element);
}
while (activeElement.shadowRoot) {
activeElement = activeElement.shadowRoot.activeElement;
}
return res;
return activeElement;
}
function isFocusable(element) {
return element.matches(focusablesQuery) && !element.disabled && !/^-/.test(element.getAttribute('tabindex') || '') && !element.hasAttribute('inert') && ( // see https://github.com/GoogleChrome/inert-polyfill
element.offsetWidth > 0 || element.offsetHeight > 0);
}
function getFocusTrapParent(element) {

@@ -59,3 +54,3 @@ if (!focusTrapTest) {

function shouldIgnoreEvent(activeElement, key) {
function shouldIgnoreEvent(activeElement, forwardDirection) {
var tagName = activeElement.tagName;

@@ -87,5 +82,5 @@ var isTextarea = tagName === 'TEXTAREA';

if (key === 'ArrowLeft' && selectionStart === selectionEnd && selectionStart === 0) {
if (!forwardDirection && selectionStart === selectionEnd && selectionStart === 0) {
return false;
} else if (key === 'ArrowRight' && selectionStart === selectionEnd && selectionStart === len) {
} else if (forwardDirection && selectionStart === selectionEnd && selectionStart === len) {
return false;

@@ -97,31 +92,94 @@ }

function focusNextOrPrevious(event, key) {
var activeElement = document.activeElement;
function getNextCandidateNodeForShadowDomPolyfill(root, targetElement, forwardDirection, filter) {
// When the shadydom polyfill is running, we can't use TreeWalker on ShadowRoots because
// they aren't real Nodes. So we do this workaround where we run TreeWalker on the
// children instead.
var nodes = Array.prototype.slice.call(root.querySelectorAll('*'));
var idx = nodes.indexOf(targetElement);
if (shouldIgnoreEvent(activeElement, key)) {
return;
if (forwardDirection) {
nodes = nodes.slice(idx + 1);
} else {
if (idx === -1) {
idx = nodes.length;
}
nodes = nodes.slice(0, idx);
nodes.reverse();
}
var focusableElements = getFocusableElements(activeElement);
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (!focusableElements.length) {
return;
if (node instanceof HTMLElement && filter.acceptNode(node) === NodeFilter.FILTER_ACCEPT) {
return node;
}
}
var index = focusableElements.indexOf(activeElement);
var element;
return undefined;
}
if (key === 'ArrowLeft') {
element = focusableElements[index - 1] || focusableElements[0];
} else {
// ArrowRight
element = focusableElements[index + 1] || focusableElements[focusableElements.length - 1];
function getNextCandidateNode(root, targetElement, forwardDirection, filter) {
var walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filter);
if (targetElement) {
walker.currentNode = targetElement;
}
element.focus();
event.preventDefault();
if (forwardDirection) {
return walker.nextNode();
} else if (targetElement) {
return walker.previousNode();
} // iterating backwards through shadow root, use last child
return walker.lastChild();
}
function isShadowDomPolyfill() {
return typeof ShadowRoot !== 'undefined' && ( // ShadowRoot.polyfill is just a hack for our unit tests
'polyfill' in ShadowRoot || !ShadowRoot.toString().includes('[native code]'));
}
function getNextNode(root, targetElement, forwardDirection) {
var filter = {
acceptNode: function (node) {
return node === targetElement || node.shadowRoot || isFocusable(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
}
}; // TODO: remove this when we don't need to support the Shadow DOM polyfill
var nextNode = isShadowDomPolyfill() && root instanceof ShadowRoot ? getNextCandidateNodeForShadowDomPolyfill(root, targetElement, forwardDirection, filter) : getNextCandidateNode(root, targetElement, forwardDirection, filter);
if (nextNode && nextNode.shadowRoot) {
// push into the shadow DOM
return getNextNode(nextNode.shadowRoot, null, forwardDirection);
}
if (!nextNode && root.host) {
// pop out of the shadow DOM
return getNextNode(root.host.getRootNode(), root.host, forwardDirection);
}
return nextNode;
}
function focusNextOrPrevious(event, key) {
var activeElement = getActiveElement();
var forwardDirection = key === 'ArrowRight';
if (shouldIgnoreEvent(activeElement, forwardDirection)) {
return;
}
var root = getFocusTrapParent(activeElement) || activeElement.getRootNode();
var nextNode = getNextNode(root, activeElement, forwardDirection);
if (nextNode && nextNode !== activeElement) {
nextNode.focus();
event.preventDefault();
}
}
function handleEnter(event) {
var activeElement = document.activeElement;
var activeElement = getActiveElement();

@@ -128,0 +186,0 @@ if (activeElement.tagName === 'INPUT' && checkboxRadioInputTypes.indexOf(activeElement.getAttribute('type').toLowerCase()) !== -1) {

@@ -6,7 +6,2 @@ /**

/* global document, addEventListener, removeEventListener, getSelection */
// This query is adapted from a11y-dialog
// https://github.com/edenspiekermann/a11y-dialog/blob/cf4ed81/a11y-dialog.js#L6-L18
var focusablesQuery = 'a[href], area[href], input, select, textarea, ' +
'button, iframe, object, embed, [contenteditable], [tabindex], ' +
'video[controls], audio[controls], summary';
// TODO: email/number types are a special type, in that they return selectionStart/selectionEnd as null

@@ -19,20 +14,21 @@ // As far as I can tell, there is no way to actually get the caret position from these inputs. So we

var focusTrapTest = undefined;
function getFocusableElements(activeElement) {
// Respect focus trap inside of dialogs
var dialogParent = getFocusTrapParent(activeElement);
var root = dialogParent || document;
var res = [];
var elements = root.querySelectorAll(focusablesQuery);
var len = elements.length;
for (var i = 0; i < len; i++) {
var element = elements[i];
if (element === activeElement || (!element.disabled &&
!/^-/.test(element.getAttribute('tabindex') || '') &&
!element.hasAttribute('inert') && // see https://github.com/GoogleChrome/inert-polyfill
(element.offsetWidth > 0 || element.offsetHeight > 0))) {
res.push(element);
}
// This query is adapted from a11y-dialog
// https://github.com/edenspiekermann/a11y-dialog/blob/cf4ed81/a11y-dialog.js#L6-L18
var focusablesQuery = 'a[href], area[href], input, select, textarea, ' +
'button, iframe, object, embed, [contenteditable], [tabindex], ' +
'video[controls], audio[controls], summary';
function getActiveElement() {
var activeElement = document.activeElement;
while (activeElement.shadowRoot) {
activeElement = activeElement.shadowRoot.activeElement;
}
return res;
return activeElement;
}
function isFocusable(element) {
return element.matches(focusablesQuery) &&
!element.disabled &&
!/^-/.test(element.getAttribute('tabindex') || '') &&
!element.hasAttribute('inert') && // see https://github.com/GoogleChrome/inert-polyfill
(element.offsetWidth > 0 || element.offsetHeight > 0);
}
function getFocusTrapParent(element) {

@@ -50,3 +46,3 @@ if (!focusTrapTest) {

}
function shouldIgnoreEvent(activeElement, key) {
function shouldIgnoreEvent(activeElement, forwardDirection) {
var tagName = activeElement.tagName;

@@ -76,6 +72,6 @@ var isTextarea = tagName === 'TEXTAREA';

// unless the cursor is at the beginning or the end
if (key === 'ArrowLeft' && selectionStart === selectionEnd && selectionStart === 0) {
if (!forwardDirection && selectionStart === selectionEnd && selectionStart === 0) {
return false;
}
else if (key === 'ArrowRight' && selectionStart === selectionEnd && selectionStart === len) {
else if (forwardDirection && selectionStart === selectionEnd && selectionStart === len) {
return false;

@@ -85,24 +81,80 @@ }

}
function focusNextOrPrevious(event, key) {
var activeElement = document.activeElement;
if (shouldIgnoreEvent(activeElement, key)) {
return;
function getNextCandidateNodeForShadowDomPolyfill(root, targetElement, forwardDirection, filter) {
// When the shadydom polyfill is running, we can't use TreeWalker on ShadowRoots because
// they aren't real Nodes. So we do this workaround where we run TreeWalker on the
// children instead.
var nodes = Array.prototype.slice.call(root.querySelectorAll('*'));
var idx = nodes.indexOf(targetElement);
if (forwardDirection) {
nodes = nodes.slice(idx + 1);
}
var focusableElements = getFocusableElements(activeElement);
if (!focusableElements.length) {
return;
else {
if (idx === -1) {
idx = nodes.length;
}
nodes = nodes.slice(0, idx);
nodes.reverse();
}
var index = focusableElements.indexOf(activeElement);
var element;
if (key === 'ArrowLeft') {
element = focusableElements[index - 1] || focusableElements[0];
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node instanceof HTMLElement && filter.acceptNode(node) === NodeFilter.FILTER_ACCEPT) {
return node;
}
}
else { // ArrowRight
element = focusableElements[index + 1] || focusableElements[focusableElements.length - 1];
return undefined;
}
function getNextCandidateNode(root, targetElement, forwardDirection, filter) {
var walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filter);
if (targetElement) {
walker.currentNode = targetElement;
}
element.focus();
event.preventDefault();
if (forwardDirection) {
return walker.nextNode();
}
else if (targetElement) {
return walker.previousNode();
}
// iterating backwards through shadow root, use last child
return walker.lastChild();
}
function isShadowDomPolyfill() {
return typeof ShadowRoot !== 'undefined' &&
// ShadowRoot.polyfill is just a hack for our unit tests
('polyfill' in ShadowRoot || !ShadowRoot.toString().includes('[native code]'));
}
function getNextNode(root, targetElement, forwardDirection) {
var filter = {
acceptNode: function (node) {
return (node === targetElement || node.shadowRoot || isFocusable(node))
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_SKIP;
}
};
// TODO: remove this when we don't need to support the Shadow DOM polyfill
var nextNode = isShadowDomPolyfill() && root instanceof ShadowRoot
? getNextCandidateNodeForShadowDomPolyfill(root, targetElement, forwardDirection, filter)
: getNextCandidateNode(root, targetElement, forwardDirection, filter);
if (nextNode && nextNode.shadowRoot) { // push into the shadow DOM
return getNextNode(nextNode.shadowRoot, null, forwardDirection);
}
if (!nextNode && root.host) { // pop out of the shadow DOM
return getNextNode(root.host.getRootNode(), root.host, forwardDirection);
}
return nextNode;
}
function focusNextOrPrevious(event, key) {
var activeElement = getActiveElement();
var forwardDirection = key === 'ArrowRight';
if (shouldIgnoreEvent(activeElement, forwardDirection)) {
return;
}
var root = getFocusTrapParent(activeElement) || activeElement.getRootNode();
var nextNode = getNextNode(root, activeElement, forwardDirection);
if (nextNode && nextNode !== activeElement) {
nextNode.focus();
event.preventDefault();
}
}
function handleEnter(event) {
var activeElement = document.activeElement;
var activeElement = getActiveElement();
if (activeElement.tagName === 'INPUT' &&

@@ -109,0 +161,0 @@ checkboxRadioInputTypes.indexOf(activeElement.getAttribute('type').toLowerCase()) !== -1) {

@@ -13,33 +13,28 @@ (function (global, factory) {

/* global document, addEventListener, removeEventListener, getSelection */
// This query is adapted from a11y-dialog
// https://github.com/edenspiekermann/a11y-dialog/blob/cf4ed81/a11y-dialog.js#L6-L18
var focusablesQuery = 'a[href], area[href], input, select, textarea, ' + 'button, iframe, object, embed, [contenteditable], [tabindex], ' + 'video[controls], audio[controls], summary'; // TODO: email/number types are a special type, in that they return selectionStart/selectionEnd as null
// TODO: email/number types are a special type, in that they return selectionStart/selectionEnd as null
// As far as I can tell, there is no way to actually get the caret position from these inputs. So we
// don't do the proper caret handling for those inputs, unfortunately.
// https://html.spec.whatwg.org/multipage/input.html#do-not-apply
var textInputTypes = ['text', 'search', 'url', 'password', 'tel'];
var checkboxRadioInputTypes = ['checkbox', 'radio'];
var focusTrapTest = undefined;
var focusTrapTest = undefined; // This query is adapted from a11y-dialog
// https://github.com/edenspiekermann/a11y-dialog/blob/cf4ed81/a11y-dialog.js#L6-L18
function getFocusableElements(activeElement) {
// Respect focus trap inside of dialogs
var dialogParent = getFocusTrapParent(activeElement);
var root = dialogParent || document;
var res = [];
var elements = root.querySelectorAll(focusablesQuery);
var len = elements.length;
var focusablesQuery = 'a[href], area[href], input, select, textarea, ' + 'button, iframe, object, embed, [contenteditable], [tabindex], ' + 'video[controls], audio[controls], summary';
for (var i = 0; i < len; i++) {
var element = elements[i];
function getActiveElement() {
var activeElement = document.activeElement;
if (element === activeElement || !element.disabled && !/^-/.test(element.getAttribute('tabindex') || '') && !element.hasAttribute('inert') && ( // see https://github.com/GoogleChrome/inert-polyfill
element.offsetWidth > 0 || element.offsetHeight > 0)) {
res.push(element);
}
while (activeElement.shadowRoot) {
activeElement = activeElement.shadowRoot.activeElement;
}
return res;
return activeElement;
}
function isFocusable(element) {
return element.matches(focusablesQuery) && !element.disabled && !/^-/.test(element.getAttribute('tabindex') || '') && !element.hasAttribute('inert') && ( // see https://github.com/GoogleChrome/inert-polyfill
element.offsetWidth > 0 || element.offsetHeight > 0);
}
function getFocusTrapParent(element) {

@@ -61,3 +56,3 @@ if (!focusTrapTest) {

function shouldIgnoreEvent(activeElement, key) {
function shouldIgnoreEvent(activeElement, forwardDirection) {
var tagName = activeElement.tagName;

@@ -89,5 +84,5 @@ var isTextarea = tagName === 'TEXTAREA';

if (key === 'ArrowLeft' && selectionStart === selectionEnd && selectionStart === 0) {
if (!forwardDirection && selectionStart === selectionEnd && selectionStart === 0) {
return false;
} else if (key === 'ArrowRight' && selectionStart === selectionEnd && selectionStart === len) {
} else if (forwardDirection && selectionStart === selectionEnd && selectionStart === len) {
return false;

@@ -99,31 +94,94 @@ }

function focusNextOrPrevious(event, key) {
var activeElement = document.activeElement;
function getNextCandidateNodeForShadowDomPolyfill(root, targetElement, forwardDirection, filter) {
// When the shadydom polyfill is running, we can't use TreeWalker on ShadowRoots because
// they aren't real Nodes. So we do this workaround where we run TreeWalker on the
// children instead.
var nodes = Array.prototype.slice.call(root.querySelectorAll('*'));
var idx = nodes.indexOf(targetElement);
if (shouldIgnoreEvent(activeElement, key)) {
return;
if (forwardDirection) {
nodes = nodes.slice(idx + 1);
} else {
if (idx === -1) {
idx = nodes.length;
}
nodes = nodes.slice(0, idx);
nodes.reverse();
}
var focusableElements = getFocusableElements(activeElement);
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (!focusableElements.length) {
return;
if (node instanceof HTMLElement && filter.acceptNode(node) === NodeFilter.FILTER_ACCEPT) {
return node;
}
}
var index = focusableElements.indexOf(activeElement);
var element;
return undefined;
}
if (key === 'ArrowLeft') {
element = focusableElements[index - 1] || focusableElements[0];
} else {
// ArrowRight
element = focusableElements[index + 1] || focusableElements[focusableElements.length - 1];
function getNextCandidateNode(root, targetElement, forwardDirection, filter) {
var walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filter);
if (targetElement) {
walker.currentNode = targetElement;
}
element.focus();
event.preventDefault();
if (forwardDirection) {
return walker.nextNode();
} else if (targetElement) {
return walker.previousNode();
} // iterating backwards through shadow root, use last child
return walker.lastChild();
}
function isShadowDomPolyfill() {
return typeof ShadowRoot !== 'undefined' && ( // ShadowRoot.polyfill is just a hack for our unit tests
'polyfill' in ShadowRoot || !ShadowRoot.toString().includes('[native code]'));
}
function getNextNode(root, targetElement, forwardDirection) {
var filter = {
acceptNode: function acceptNode(node) {
return node === targetElement || node.shadowRoot || isFocusable(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
}
}; // TODO: remove this when we don't need to support the Shadow DOM polyfill
var nextNode = isShadowDomPolyfill() && root instanceof ShadowRoot ? getNextCandidateNodeForShadowDomPolyfill(root, targetElement, forwardDirection, filter) : getNextCandidateNode(root, targetElement, forwardDirection, filter);
if (nextNode && nextNode.shadowRoot) {
// push into the shadow DOM
return getNextNode(nextNode.shadowRoot, null, forwardDirection);
}
if (!nextNode && root.host) {
// pop out of the shadow DOM
return getNextNode(root.host.getRootNode(), root.host, forwardDirection);
}
return nextNode;
}
function focusNextOrPrevious(event, key) {
var activeElement = getActiveElement();
var forwardDirection = key === 'ArrowRight';
if (shouldIgnoreEvent(activeElement, forwardDirection)) {
return;
}
var root = getFocusTrapParent(activeElement) || activeElement.getRootNode();
var nextNode = getNextNode(root, activeElement, forwardDirection);
if (nextNode && nextNode !== activeElement) {
nextNode.focus();
event.preventDefault();
}
}
function handleEnter(event) {
var activeElement = document.activeElement;
var activeElement = getActiveElement();

@@ -130,0 +188,0 @@ if (activeElement.tagName === 'INPUT' && checkboxRadioInputTypes.indexOf(activeElement.getAttribute('type').toLowerCase()) !== -1) {

@@ -6,7 +6,2 @@ /**

/* global document, addEventListener, removeEventListener, getSelection */
// This query is adapted from a11y-dialog
// https://github.com/edenspiekermann/a11y-dialog/blob/cf4ed81/a11y-dialog.js#L6-L18
var focusablesQuery = 'a[href], area[href], input, select, textarea, ' +
'button, iframe, object, embed, [contenteditable], [tabindex], ' +
'video[controls], audio[controls], summary';
// TODO: email/number types are a special type, in that they return selectionStart/selectionEnd as null

@@ -19,20 +14,21 @@ // As far as I can tell, there is no way to actually get the caret position from these inputs. So we

var focusTrapTest = undefined;
function getFocusableElements(activeElement) {
// Respect focus trap inside of dialogs
var dialogParent = getFocusTrapParent(activeElement);
var root = dialogParent || document;
var res = [];
var elements = root.querySelectorAll(focusablesQuery);
var len = elements.length;
for (var i = 0; i < len; i++) {
var element = elements[i];
if (element === activeElement || (!element.disabled &&
!/^-/.test(element.getAttribute('tabindex') || '') &&
!element.hasAttribute('inert') && // see https://github.com/GoogleChrome/inert-polyfill
(element.offsetWidth > 0 || element.offsetHeight > 0))) {
res.push(element);
}
// This query is adapted from a11y-dialog
// https://github.com/edenspiekermann/a11y-dialog/blob/cf4ed81/a11y-dialog.js#L6-L18
var focusablesQuery = 'a[href], area[href], input, select, textarea, ' +
'button, iframe, object, embed, [contenteditable], [tabindex], ' +
'video[controls], audio[controls], summary';
function getActiveElement() {
var activeElement = document.activeElement;
while (activeElement.shadowRoot) {
activeElement = activeElement.shadowRoot.activeElement;
}
return res;
return activeElement;
}
function isFocusable(element) {
return element.matches(focusablesQuery) &&
!element.disabled &&
!/^-/.test(element.getAttribute('tabindex') || '') &&
!element.hasAttribute('inert') && // see https://github.com/GoogleChrome/inert-polyfill
(element.offsetWidth > 0 || element.offsetHeight > 0);
}
function getFocusTrapParent(element) {

@@ -50,3 +46,3 @@ if (!focusTrapTest) {

}
function shouldIgnoreEvent(activeElement, key) {
function shouldIgnoreEvent(activeElement, forwardDirection) {
var tagName = activeElement.tagName;

@@ -76,6 +72,6 @@ var isTextarea = tagName === 'TEXTAREA';

// unless the cursor is at the beginning or the end
if (key === 'ArrowLeft' && selectionStart === selectionEnd && selectionStart === 0) {
if (!forwardDirection && selectionStart === selectionEnd && selectionStart === 0) {
return false;
}
else if (key === 'ArrowRight' && selectionStart === selectionEnd && selectionStart === len) {
else if (forwardDirection && selectionStart === selectionEnd && selectionStart === len) {
return false;

@@ -85,24 +81,80 @@ }

}
function focusNextOrPrevious(event, key) {
var activeElement = document.activeElement;
if (shouldIgnoreEvent(activeElement, key)) {
return;
function getNextCandidateNodeForShadowDomPolyfill(root, targetElement, forwardDirection, filter) {
// When the shadydom polyfill is running, we can't use TreeWalker on ShadowRoots because
// they aren't real Nodes. So we do this workaround where we run TreeWalker on the
// children instead.
var nodes = Array.prototype.slice.call(root.querySelectorAll('*'));
var idx = nodes.indexOf(targetElement);
if (forwardDirection) {
nodes = nodes.slice(idx + 1);
}
var focusableElements = getFocusableElements(activeElement);
if (!focusableElements.length) {
return;
else {
if (idx === -1) {
idx = nodes.length;
}
nodes = nodes.slice(0, idx);
nodes.reverse();
}
var index = focusableElements.indexOf(activeElement);
var element;
if (key === 'ArrowLeft') {
element = focusableElements[index - 1] || focusableElements[0];
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node instanceof HTMLElement && filter.acceptNode(node) === NodeFilter.FILTER_ACCEPT) {
return node;
}
}
else { // ArrowRight
element = focusableElements[index + 1] || focusableElements[focusableElements.length - 1];
return undefined;
}
function getNextCandidateNode(root, targetElement, forwardDirection, filter) {
var walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filter);
if (targetElement) {
walker.currentNode = targetElement;
}
element.focus();
event.preventDefault();
if (forwardDirection) {
return walker.nextNode();
}
else if (targetElement) {
return walker.previousNode();
}
// iterating backwards through shadow root, use last child
return walker.lastChild();
}
function isShadowDomPolyfill() {
return typeof ShadowRoot !== 'undefined' &&
// ShadowRoot.polyfill is just a hack for our unit tests
('polyfill' in ShadowRoot || !ShadowRoot.toString().includes('[native code]'));
}
function getNextNode(root, targetElement, forwardDirection) {
var filter = {
acceptNode: function (node) {
return (node === targetElement || node.shadowRoot || isFocusable(node))
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_SKIP;
}
};
// TODO: remove this when we don't need to support the Shadow DOM polyfill
var nextNode = isShadowDomPolyfill() && root instanceof ShadowRoot
? getNextCandidateNodeForShadowDomPolyfill(root, targetElement, forwardDirection, filter)
: getNextCandidateNode(root, targetElement, forwardDirection, filter);
if (nextNode && nextNode.shadowRoot) { // push into the shadow DOM
return getNextNode(nextNode.shadowRoot, null, forwardDirection);
}
if (!nextNode && root.host) { // pop out of the shadow DOM
return getNextNode(root.host.getRootNode(), root.host, forwardDirection);
}
return nextNode;
}
function focusNextOrPrevious(event, key) {
var activeElement = getActiveElement();
var forwardDirection = key === 'ArrowRight';
if (shouldIgnoreEvent(activeElement, forwardDirection)) {
return;
}
var root = getFocusTrapParent(activeElement) || activeElement.getRootNode();
var nextNode = getNextNode(root, activeElement, forwardDirection);
if (nextNode && nextNode !== activeElement) {
nextNode.focus();
event.preventDefault();
}
}
function handleEnter(event) {
var activeElement = document.activeElement;
var activeElement = getActiveElement();
if (activeElement.tagName === 'INPUT' &&

@@ -109,0 +161,0 @@ checkboxRadioInputTypes.indexOf(activeElement.getAttribute('type').toLowerCase()) !== -1) {

{
"name": "arrow-key-navigation",
"description": "Add left/right key navigation to a KaiOS app or web app",
"version": "1.1.0",
"version": "1.2.0",
"license": "Apache-2.0",

@@ -34,14 +34,26 @@ "files": [

"@pika/pack": "^0.5.0",
"@pika/plugin-build-node": "^0.7.1",
"@pika/plugin-build-umd": "^0.7.1",
"@pika/plugin-build-web": "^0.7.1",
"@pika/plugin-ts-standard-pkg": "^0.7.1",
"@pika/plugin-build-node": "^0.9.2",
"@pika/plugin-build-umd": "^0.9.2",
"@pika/plugin-build-web": "^0.9.2",
"@pika/plugin-ts-standard-pkg": "^0.9.2",
"@rollup/plugin-commonjs": "^13.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^8.0.1",
"assert": "^2.0.0",
"jsdom": "^15.2.0",
"jsdom": "^16.2.2",
"jsdom-global": "^3.0.2",
"mocha": "^6.2.2",
"standard": "^14.3.1",
"tslint": "^5.20.0",
"tslint-config-standard": "^8.0.1",
"typescript": "^3.6.4"
"karma": "^5.1.0",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.0.2",
"karma-mocha": "^2.0.1",
"karma-rollup-preprocessor": "^7.0.5",
"mocha": "^8.0.1",
"nyc": "^15.1.0",
"rollup": "^2.17.1",
"rollup-plugin-istanbul": "^2.0.1",
"rollup-plugin-node-polyfills": "^0.2.1",
"standard": "^14.3.4",
"tslint": "^6.1.2",
"tslint-config-standard": "^9.0.0",
"typescript": "^3.9.5"
},

@@ -48,0 +60,0 @@ "source": "dist-src/index.js",

@@ -14,3 +14,3 @@ arrow-key-navigation [![Build Status](https://travis-ci.org/nolanlawson/arrow-key-navigation.svg)](https://travis-ci.org/nolanlawson/arrow-key-navigation)

It will also listen for the <kbd>Enter</kbd> key for certain special cases like checkbox/radio buttons.
It will also listen for the <kbd>Enter</kbd> key for certain special cases like checkbox/radio buttons. `contenteditable` and Shadow DOM are also supported.

@@ -78,2 +78,6 @@ ## Install

### Code coverage
npm run cover
### Manual KaiOS app test

@@ -80,0 +84,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet