| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 | "use strict";Object.defineProperty(exports, "__esModule", {  value: true});exports.computeAriaChecked = computeAriaChecked;exports.computeAriaCurrent = computeAriaCurrent;exports.computeAriaExpanded = computeAriaExpanded;exports.computeAriaPressed = computeAriaPressed;exports.computeAriaSelected = computeAriaSelected;exports.computeHeadingLevel = computeHeadingLevel;exports.getImplicitAriaRoles = getImplicitAriaRoles;exports.getRoles = getRoles;exports.isInaccessible = isInaccessible;exports.isSubtreeInaccessible = isSubtreeInaccessible;exports.logRoles = void 0;exports.prettyRoles = prettyRoles;var _ariaQuery = require("aria-query");var _domAccessibilityApi = require("dom-accessibility-api");var _prettyDom = require("./pretty-dom");var _config = require("./config");const elementRoleList = buildElementRoleList(_ariaQuery.elementRoles);/** * @param {Element} element - * @returns {boolean} - `true` if `element` and its subtree are inaccessible */function isSubtreeInaccessible(element) {  if (element.hidden === true) {    return true;  }  if (element.getAttribute('aria-hidden') === 'true') {    return true;  }  const window = element.ownerDocument.defaultView;  if (window.getComputedStyle(element).display === 'none') {    return true;  }  return false;}/** * Partial implementation https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion * which should only be used for elements with a non-presentational role i.e. * `role="none"` and `role="presentation"` will not be excluded. * * Implements aria-hidden semantics (i.e. parent overrides child) * Ignores "Child Presentational: True" characteristics * * @param {Element} element - * @param {object} [options] - * @param {function (element: Element): boolean} options.isSubtreeInaccessible - * can be used to return cached results from previous isSubtreeInaccessible calls * @returns {boolean} true if excluded, otherwise false */function isInaccessible(element, options = {}) {  const {    isSubtreeInaccessible: isSubtreeInaccessibleImpl = isSubtreeInaccessible  } = options;  const window = element.ownerDocument.defaultView;  // since visibility is inherited we can exit early  if (window.getComputedStyle(element).visibility === 'hidden') {    return true;  }  let currentElement = element;  while (currentElement) {    if (isSubtreeInaccessibleImpl(currentElement)) {      return true;    }    currentElement = currentElement.parentElement;  }  return false;}function getImplicitAriaRoles(currentNode) {  // eslint bug here:  // eslint-disable-next-line no-unused-vars  for (const {    match,    roles  } of elementRoleList) {    if (match(currentNode)) {      return [...roles];    }  }  return [];}function buildElementRoleList(elementRolesMap) {  function makeElementSelector({    name,    attributes  }) {    return `${name}${attributes.map(({      name: attributeName,      value,      constraints = []    }) => {      const shouldNotExist = constraints.indexOf('undefined') !== -1;      if (shouldNotExist) {        return `:not([${attributeName}])`;      } else if (value) {        return `[${attributeName}="${value}"]`;      } else {        return `[${attributeName}]`;      }    }).join('')}`;  }  function getSelectorSpecificity({    attributes = []  }) {    return attributes.length;  }  function bySelectorSpecificity({    specificity: leftSpecificity  }, {    specificity: rightSpecificity  }) {    return rightSpecificity - leftSpecificity;  }  function match(element) {    let {      attributes = []    } = element;    // https://github.com/testing-library/dom-testing-library/issues/814    const typeTextIndex = attributes.findIndex(attribute => attribute.value && attribute.name === 'type' && attribute.value === 'text');    if (typeTextIndex >= 0) {      // not using splice to not mutate the attributes array      attributes = [...attributes.slice(0, typeTextIndex), ...attributes.slice(typeTextIndex + 1)];    }    const selector = makeElementSelector({      ...element,      attributes    });    return node => {      if (typeTextIndex >= 0 && node.type !== 'text') {        return false;      }      return node.matches(selector);    };  }  let result = [];  // eslint bug here:  // eslint-disable-next-line no-unused-vars  for (const [element, roles] of elementRolesMap.entries()) {    result = [...result, {      match: match(element),      roles: Array.from(roles),      specificity: getSelectorSpecificity(element)    }];  }  return result.sort(bySelectorSpecificity);}function getRoles(container, {  hidden = false} = {}) {  function flattenDOM(node) {    return [node, ...Array.from(node.children).reduce((acc, child) => [...acc, ...flattenDOM(child)], [])];  }  return flattenDOM(container).filter(element => {    return hidden === false ? isInaccessible(element) === false : true;  }).reduce((acc, node) => {    let roles = [];    // TODO: This violates html-aria which does not allow any role on every element    if (node.hasAttribute('role')) {      roles = node.getAttribute('role').split(' ').slice(0, 1);    } else {      roles = getImplicitAriaRoles(node);    }    return roles.reduce((rolesAcc, role) => Array.isArray(rolesAcc[role]) ? {      ...rolesAcc,      [role]: [...rolesAcc[role], node]    } : {      ...rolesAcc,      [role]: [node]    }, acc);  }, {});}function prettyRoles(dom, {  hidden,  includeDescription}) {  const roles = getRoles(dom, {    hidden  });  // We prefer to skip generic role, we don't recommend it  return Object.entries(roles).filter(([role]) => role !== 'generic').map(([role, elements]) => {    const delimiterBar = '-'.repeat(50);    const elementsString = elements.map(el => {      const nameString = `Name "${(0, _domAccessibilityApi.computeAccessibleName)(el, {        computedStyleSupportsPseudoElements: (0, _config.getConfig)().computedStyleSupportsPseudoElements      })}":\n`;      const domString = (0, _prettyDom.prettyDOM)(el.cloneNode(false));      if (includeDescription) {        const descriptionString = `Description "${(0, _domAccessibilityApi.computeAccessibleDescription)(el, {          computedStyleSupportsPseudoElements: (0, _config.getConfig)().computedStyleSupportsPseudoElements        })}":\n`;        return `${nameString}${descriptionString}${domString}`;      }      return `${nameString}${domString}`;    }).join('\n\n');    return `${role}:\n\n${elementsString}\n\n${delimiterBar}`;  }).join('\n');}const logRoles = (dom, {  hidden = false} = {}) => console.log(prettyRoles(dom, {  hidden}));/** * @param {Element} element - * @returns {boolean | undefined} - false/true if (not)selected, undefined if not selectable */exports.logRoles = logRoles;function computeAriaSelected(element) {  // implicit value from html-aam mappings: https://www.w3.org/TR/html-aam-1.0/#html-attribute-state-and-property-mappings  // https://www.w3.org/TR/html-aam-1.0/#details-id-97  if (element.tagName === 'OPTION') {    return element.selected;  }  // explicit value  return checkBooleanAttribute(element, 'aria-selected');}/** * @param {Element} element - * @returns {boolean | undefined} - false/true if (not)checked, undefined if not checked-able */function computeAriaChecked(element) {  // implicit value from html-aam mappings: https://www.w3.org/TR/html-aam-1.0/#html-attribute-state-and-property-mappings  // https://www.w3.org/TR/html-aam-1.0/#details-id-56  // https://www.w3.org/TR/html-aam-1.0/#details-id-67  if ('indeterminate' in element && element.indeterminate) {    return undefined;  }  if ('checked' in element) {    return element.checked;  }  // explicit value  return checkBooleanAttribute(element, 'aria-checked');}/** * @param {Element} element - * @returns {boolean | undefined} - false/true if (not)pressed, undefined if not press-able */function computeAriaPressed(element) {  // https://www.w3.org/TR/wai-aria-1.1/#aria-pressed  return checkBooleanAttribute(element, 'aria-pressed');}/** * @param {Element} element - * @returns {boolean | string | null} - */function computeAriaCurrent(element) {  var _ref, _checkBooleanAttribut;  // https://www.w3.org/TR/wai-aria-1.1/#aria-current  return (_ref = (_checkBooleanAttribut = checkBooleanAttribute(element, 'aria-current')) != null ? _checkBooleanAttribut : element.getAttribute('aria-current')) != null ? _ref : false;}/** * @param {Element} element - * @returns {boolean | undefined} - false/true if (not)expanded, undefined if not expand-able */function computeAriaExpanded(element) {  // https://www.w3.org/TR/wai-aria-1.1/#aria-expanded  return checkBooleanAttribute(element, 'aria-expanded');}function checkBooleanAttribute(element, attribute) {  const attributeValue = element.getAttribute(attribute);  if (attributeValue === 'true') {    return true;  }  if (attributeValue === 'false') {    return false;  }  return undefined;}/** * @param {Element} element - * @returns {number | undefined} - number if implicit heading or aria-level present, otherwise undefined */function computeHeadingLevel(element) {  // https://w3c.github.io/html-aam/#el-h1-h6  // https://w3c.github.io/html-aam/#el-h1-h6  const implicitHeadingLevels = {    H1: 1,    H2: 2,    H3: 3,    H4: 4,    H5: 5,    H6: 6  };  // explicit aria-level value  // https://www.w3.org/TR/wai-aria-1.2/#aria-level  const ariaLevelAttribute = element.getAttribute('aria-level') && Number(element.getAttribute('aria-level'));  return ariaLevelAttribute || implicitHeadingLevels[element.tagName];}
 |