'use strict'; var isDisabled = require('../misc/isDisabled.js'); var isElementType = require('../misc/isElementType.js'); var isVisible = require('../misc/isVisible.js'); var selector = require('./selector.js'); function getTabDestination(activeElement, shift) { const document = activeElement.ownerDocument; const focusableElements = document.querySelectorAll(selector.FOCUSABLE_SELECTOR); const enabledElements = Array.from(focusableElements).filter((el)=>el === activeElement || !(Number(el.getAttribute('tabindex')) < 0 || isDisabled.isDisabled(el))); // tabindex has no effect if the active element has negative tabindex if (Number(activeElement.getAttribute('tabindex')) >= 0) { enabledElements.sort((a, b)=>{ const i = Number(a.getAttribute('tabindex')); const j = Number(b.getAttribute('tabindex')); if (i === j) { return 0; } else if (i === 0) { return 1; } else if (j === 0) { return -1; } return i - j; }); } const checkedRadio = {}; let prunedElements = [ document.body ]; const activeRadioGroup = isElementType.isElementType(activeElement, 'input', { type: 'radio' }) ? activeElement.name : undefined; enabledElements.forEach((currentElement)=>{ const el = currentElement; // For radio groups keep only the active radio // If there is no active radio, keep only the checked radio // If there is no checked radio, treat like everything else if (isElementType.isElementType(el, 'input', { type: 'radio' }) && el.name) { // If the active element is part of the group, add only that if (el === activeElement) { prunedElements.push(el); return; } else if (el.name === activeRadioGroup) { return; } // If we stumble upon a checked radio, remove the others if (el.checked) { prunedElements = prunedElements.filter((e)=>!isElementType.isElementType(e, 'input', { type: 'radio', name: el.name })); prunedElements.push(el); checkedRadio[el.name] = el; return; } // If we already found the checked one, skip if (typeof checkedRadio[el.name] !== 'undefined') { return; } } prunedElements.push(el); }); for(let index = prunedElements.findIndex((el)=>el === activeElement);;){ index += shift ? -1 : 1; // loop at overflow if (index === prunedElements.length) { index = 0; } else if (index === -1) { index = prunedElements.length - 1; } if (prunedElements[index] === activeElement || prunedElements[index] === document.body || isVisible.isVisible(prunedElements[index])) { return prunedElements[index]; } } } exports.getTabDestination = getTabDestination;