UNPKG

kayle

Version:

Extremely fast and accurate accessibility engine built for any headless tool like playwright or puppeteer.

271 lines 9.98 kB
((exports) => { const issueCodeMap = { unknown: 0, error: 1, warning: 2, notice: 3, }; const issueCodeReverseMap = { 0: "unknown", 1: "error", 2: "warning", 3: "notice", }; const A_1 = "color-contrast"; const H_1 = "Principle1.Guideline1_4.1_4_3.G18.Fail"; const A_2 = "has-alt"; const H_2 = "Principle1.Guideline1_1.1_1_1.H37"; const A_3 = "empty-heading"; const H_3 = "Principle1.Guideline1_3.1_3_1.H42.2"; const A_4 = "frame-title"; const H_4 = "Principle2.Guideline2_4.2_4_1.H64.1"; const A_5 = "link-name"; const H_5 = "Principle4.Guideline4_1.4_1_2.H91.A.EmptyNoId"; const A_6 = "heading-order"; const H_6 = "Principle1.Guideline1_3.1_3_1_A.G141"; const scoreMap = new Map([ [A_1, [20, H_1]], [H_1, [20, A_1]], [A_2, [20, H_2]], [H_2, [20, A_2]], [A_3, [20, H_3]], [H_3, [20, A_3]], [A_4, [10, H_4]], [H_4, [10, A_4]], [A_5, [20, H_5]], [H_5, [20, A_5]], [A_6, [10, H_6]], [H_6, [10, A_6]], ]); let rootElement = null; let hiddenElements = null; const shapeIssue = (issue, cliped) => { let context = ""; let selector = ""; let clip; if (issue.element) { context = getElementContext(issue.element); selector = getElementSelector(issue.element); if (cliped && !issue.bounds && typeof issue.element.getBoundingClientRect === "function") { const { x, y, width, height } = issue.element.getBoundingClientRect(); clip = { x, y, width, height, }; } } const typeCode = issueCodeMap[issue.type || (issue.value && Array.isArray(issue.value) && issue.value.length && issue.value[0])]; return { context: context || issue.snippet, selector: selector || (issue.path && issue.path.dom ? issue.path.dom : ""), code: issue.code || issue.ruleId, type: issue.type || issueCodeReverseMap[typeCode], typeCode: typeCode || 0, message: issue.message, runner: issue.runner ? issue.runner : "kayle", runnerExtras: issue.runnerExtras, recurrence: issue.recurrence || 0, clip: issue.bounds ? { x: issue.bounds.left || 0, y: issue.bounds.top || 0, height: issue.bounds.height || 0, width: issue.bounds.width || 0, } : clip, }; }; const getElementContext = (element) => { let outerHTML = element.outerHTML; if (!outerHTML) { return ""; } if (element.innerHTML.length > 31) { outerHTML = outerHTML.replace(element.innerHTML, `${element.innerHTML.substring(0, 32)}...`); } if (outerHTML.length > 251) { outerHTML = `${outerHTML.substring(0, 251)}...`; } return outerHTML; }; const siblingPosition = (node) => { let i = 1; while ((node = node.previousSibling)) { if (node.nodeType == 1) { i += 1; } } return i; }; const getElementSelector = (element, initial) => { if (!element || element.nodeName == "HTML") { return ""; } if (element.id) { return "#" + element.id; } if (element.nodeName == "BODY") { return "body"; } const elementSelector = getElementSelector(element.parentNode, true); return `${!elementSelector && initial ? "html" : elementSelector}>:nth-child(${siblingPosition(element)})`; }; const runA11y = async (options) => { const isIssueNotIgnored = (issue) => { return !options.ignore.some((element) => { if (element === issue.type) { return true; } const _code = issue.code?.toLowerCase(); if (element === _code) { return _code; } if (element.length >= 8) { const ignoreSplit = element.split("."); if (ignoreSplit.length) { const codeSplit = _code.split("."); const ruleMatch = ignoreSplit[0] === codeSplit[1] && ignoreSplit[1] === codeSplit[2]; if (ignoreSplit.length === 2) { return true; } if (ruleMatch) { if (ignoreSplit[ignoreSplit.length - 1] === codeSplit[codeSplit.length - 1] || ignoreSplit[ignoreSplit.length - 1] === codeSplit[codeSplit.length - 2]) { return true; } } } } return false; }); }; const isElementInTestArea = (element) => { if (!rootElement) { rootElement = window.document.querySelector(options.rootElement); } return rootElement ? rootElement.contains(element) : true; }; const isElementOutsideHiddenArea = (element) => { if (!hiddenElements && typeof options.hideElements === "string") { hiddenElements = window.document.querySelectorAll(options.hideElements); } let found = true; if (hiddenElements && hiddenElements.length) { found = false; for (const ele of hiddenElements) { if (ele.contains(element)) { found = true; break; } } } return found; }; const validateIssue = (issue) => (options.rootElement && !isElementInTestArea(issue.element)) || (options.hideElements && !isElementOutsideHiddenArea(issue.element)) || !isIssueNotIgnored(issue) || (issue.value && Array.isArray(issue.value) && issue.value.length >= 2 && issue.value[1] === "PASS"); const processIssues = (issues, acc, tracker, meta, missingAltIndexs) => { for (const is of issues) { if (validateIssue(is)) { continue; } const issue = shapeIssue(is, options.clip); const errorType = issue.type === "error"; if (issue.type === "warning") { meta.warningCount += issue.recurrence + 1; } if (issue.type === "notice") { meta.noticeCount += issue.recurrence + 1; } const code = issue.code[0] === "W" ? issue.code.substring(issue.code.indexOf(".") + 1) : issue.code; if (scoreMap.has(code)) { const [accessScore, ref] = scoreMap.get(code); meta.accessScore -= accessScore; scoreMap.delete(ref); scoreMap.delete(code); } if (errorType && !tracker.errorPointer) { acc[tracker.ic] = acc[0]; acc[0] = issue; } else if (tracker.errorPointer) { const right = acc[tracker.errorPointer]; if (right && right.type === "warning" && errorType) { acc[tracker.ic] = right; acc[tracker.errorPointer] = issue; } else { acc[tracker.ic] = issue; } } else { acc[tracker.ic] = issue; } if (errorType) { meta.errorCount += issue.recurrence + 1; if (issue.code === "Principle1.Guideline1_1.1_1_1.H37" || issue.code === "image-alt") { missingAltIndexs.push(tracker.errorPointer); } tracker.errorPointer++; } tracker.ic++; } }; const runnerIssues = await Promise.all(options.runners.map((runner) => { return kayle.runners[runner](options, kayle).catch((e) => { if (e instanceof Error) { console.error(e); } return []; }); })); const meta = { errorCount: 0, warningCount: 0, noticeCount: 0, accessScore: 100, }; const missingAltIndexs = []; const issues = new Array(runnerIssues.reduce((r, x) => r + x.length, 0)); const tracker = { errorPointer: 0, ic: 0, }; for (const runnerIssue of runnerIssues) { processIssues(runnerIssue, issues, tracker, meta, missingAltIndexs); } issues.length = tracker.ic; return { documentTitle: window.document.title, pageUrl: window.location.href, issues, meta, automateable: { missingAltIndexs, }, }; }; const kayle = (exports.__a11y = { run: runA11y, runners: {}, }); })(this); //# sourceMappingURL=runner.js.map