luhn-generator
Version:
A generator of numbers that passes the validation of Luhn algorithm or Luhn formula, also known as the 'modulus 10' or 'mod 10' algorithm
153 lines (136 loc) • 3.85 kB
JavaScript
import arialabelledbyText from '../aria/arialabelledby-text';
import arialabelText from '../aria/arialabel-text';
import nativeTextAlternative from './native-text-alternative';
import formControlValue from './form-control-value';
import subtreeText from './subtree-text';
import titleText from './title-text';
import sanitize from './sanitize';
import isVisible from '../dom/is-visible';
/**
* Finds virtual node and calls accessibleTextVirtual()
* IMPORTANT: This method requires the composed tree at axe._tree
*
* @param {HTMLElement} element The HTMLElement
* @param {Object} context
* @property {Bool} inControlContext
* @property {Bool} inLabelledByContext
* @return {string}
*/
function accessibleTextVirtual(virtualNode, context = {}) {
const { actualNode } = virtualNode;
context = prepareContext(virtualNode, context);
// Step 2A, check visibility
if (shouldIgnoreHidden(virtualNode, context)) {
return '';
}
const computationSteps = [
arialabelledbyText, // Step 2B.1
arialabelText, // Step 2C
nativeTextAlternative, // Step 2D
formControlValue, // Step 2E
subtreeText, // Step 2F + Step 2H
textNodeValue, // Step 2G (order with 2H does not matter)
titleText // Step 2I
];
// Find the first step that returns a non-empty string
let accName = computationSteps.reduce((accName, step) => {
if (context.startNode === virtualNode) {
accName = sanitize(accName);
}
if (accName !== '') {
// yes, whitespace only a11y names halt the algorithm
return accName;
}
return step(virtualNode, context);
}, '');
if (context.debug) {
axe.log(accName || '{empty-value}', actualNode, context);
}
return accName;
}
/**
* Return the nodeValue of a node
* @param {VirtualNode} element
* @return {String} nodeValue value
*/
function textNodeValue(virtualNode) {
if (virtualNode.props.nodeType !== 3) {
return '';
}
return virtualNode.props.nodeValue;
}
/**
* Check if the
* @param {VirtualNode} element
* @param {Object} context
* @property {VirtualNode[]} processed
* @return {Boolean}
*/
function shouldIgnoreHidden({ actualNode }, context) {
if (!actualNode) {
return false;
}
if (
// If the parent isn't ignored, the text node should not be either
actualNode.nodeType !== 1 ||
// If the target of aria-labelledby is hidden, ignore all descendents
context.includeHidden
) {
return false;
}
return !isVisible(actualNode, true);
}
/**
* Apply defaults to the context
* @param {VirtualNode} element
* @param {Object} context
* @return {Object} context object with defaults applied
*/
function prepareContext(virtualNode, context) {
const { actualNode } = virtualNode;
if (!context.startNode) {
context = { startNode: virtualNode, ...context };
}
if (!actualNode) {
return context;
}
/**
* When `aria-labelledby` directly references a `hidden` element
* the element needs to be included in the accessible name.
*
* When a descendent of the `aria-labelledby` reference is `hidden`
* the element should not be included in the accessible name.
*
* This is done by setting `includeHidden` for the `aria-labelledby` reference.
*/
if (
actualNode.nodeType === 1 &&
context.inLabelledByContext &&
context.includeHidden === undefined
) {
context = {
includeHidden: !isVisible(actualNode, true),
...context
};
}
return context;
}
/**
* Check if the node is processed with this context before
* @param {VirtualNode} element
* @param {Object} context
* @property {VirtualNode[]} processed
* @return {Boolean}
*/
accessibleTextVirtual.alreadyProcessed = function alreadyProcessed(
virtualnode,
context
) {
context.processed = context.processed || [];
if (context.processed.includes(virtualnode)) {
return true;
}
context.processed.push(virtualnode);
return false;
};
export default accessibleTextVirtual;