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
194 lines (167 loc) • 4.53 kB
JavaScript
import getRootNode from './get-root-node';
import isOffscreen from './is-offscreen';
import findUp from './find-up';
import {
getScroll,
getNodeFromTree,
querySelectorAll,
escapeSelector
} from '../../core/utils';
const clipRegex = /rect\s*\(([0-9]+)px,?\s*([0-9]+)px,?\s*([0-9]+)px,?\s*([0-9]+)px\s*\)/;
const clipPathRegex = /(\w+)\((\d+)/;
/**
* Determines if an element is hidden with a clip or clip-path technique
* @method isClipped
* @memberof axe.commons.dom
* @private
* @param {CSSStyleDeclaration} style Computed style
* @return {Boolean}
*/
function isClipped(style) {
'use strict';
const matchesClip = style.getPropertyValue('clip').match(clipRegex);
const matchesClipPath = style
.getPropertyValue('clip-path')
.match(clipPathRegex);
if (matchesClip && matchesClip.length === 5) {
return (
matchesClip[3] - matchesClip[1] <= 0 &&
matchesClip[2] - matchesClip[4] <= 0
);
}
if (matchesClipPath) {
const type = matchesClipPath[1];
const value = parseInt(matchesClipPath[2], 10);
switch (type) {
case 'inset':
return value >= 50;
case 'circle':
return value === 0;
default:
}
}
return false;
}
/**
* Check `AREA` element is visible
* - validate if it is a child of `map`
* - ensure `map` is referred by `img` using the `usemap` attribute
* @param {Element} areaEl `AREA` element
* @retruns {Boolean}
*/
function isAreaVisible(el, screenReader, recursed) {
/**
* Note:
* - Verified that `map` element cannot refer to `area` elements across different document trees
* - Verified that `map` element does not get affected by altering `display` property
*/
const mapEl = findUp(el, 'map');
if (!mapEl) {
return false;
}
const mapElName = mapEl.getAttribute('name');
if (!mapElName) {
return false;
}
/**
* `map` element has to be in light DOM
*/
const mapElRootNode = getRootNode(el);
if (!mapElRootNode || mapElRootNode.nodeType !== 9) {
return false;
}
const refs = querySelectorAll(
// TODO: es-module-_tree
axe._tree,
`img[usemap="#${escapeSelector(mapElName)}"]`
);
if (!refs || !refs.length) {
return false;
}
return refs.some(({ actualNode }) =>
isVisible(actualNode, screenReader, recursed)
);
}
/**
* Determine whether an element is visible
* @method isVisible
* @memberof axe.commons.dom
* @instance
* @param {HTMLElement} el The HTMLElement
* @param {Boolean} screenReader When provided, will evaluate visibility from the perspective of a screen reader
* @param {Boolean} recursed
* @return {Boolean} The element's visibilty status
*/
function isVisible(el, screenReader, recursed) {
if (!el) {
throw new TypeError(
'Cannot determine if element is visible for non-DOM nodes'
);
}
const vNode = getNodeFromTree(el);
const cacheName = '_isVisible' + (screenReader ? 'ScreenReader' : '');
// 9 === Node.DOCUMENT
if (el.nodeType === 9) {
return true;
}
// 11 === Node.DOCUMENT_FRAGMENT_NODE
if (el.nodeType === 11) {
el = el.host; // grab the host Node
}
if (vNode && typeof vNode[cacheName] !== 'undefined') {
return vNode[cacheName];
}
const style = window.getComputedStyle(el, null);
if (style === null) {
return false;
}
const nodeName = el.nodeName.toUpperCase();
/**
* check visibility of `AREA`
* Note:
* Firefox's user-agent always sets `AREA` element to `display:none`
* hence excluding the edge case, for visibility computation
*/
if (nodeName === 'AREA') {
return isAreaVisible(el, screenReader, recursed);
}
// always hidden
if (
style.getPropertyValue('display') === 'none' ||
['STYLE', 'SCRIPT', 'NOSCRIPT', 'TEMPLATE'].includes(nodeName)
) {
return false;
}
// hidden from screen readers
if (screenReader && el.getAttribute('aria-hidden') === 'true') {
return false;
}
// hidden from visual users
if (
!screenReader &&
(isClipped(style) ||
style.getPropertyValue('opacity') === '0' ||
(getScroll(el) && parseInt(style.getPropertyValue('height')) === 0))
) {
return false;
}
// visibility is only accurate on the first element and
// position does not matter if it was already calculated
if (
!recursed &&
(style.getPropertyValue('visibility') === 'hidden' ||
(!screenReader && isOffscreen(el)))
) {
return false;
}
const parent = el.assignedSlot ? el.assignedSlot : el.parentNode;
let visible = false;
if (parent) {
visible = isVisible(parent, screenReader, true);
}
if (vNode) {
vNode[cacheName] = visible;
}
return visible;
}
export default isVisible;