accessify-widget 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/accessify.min.js +1 -1
- package/dist/accessify.min.js.map +1 -1
- package/dist/accessify.mjs +1 -1
- package/dist/{index-B6b-ij4T.js → index-i-tNypvI.js} +1867 -1114
- package/dist/index-i-tNypvI.js.map +1 -0
- package/dist/{keyboard-nav-YtijlLYi.js → keyboard-nav-C9zLmfrJ.js} +2 -2
- package/dist/{keyboard-nav-YtijlLYi.js.map → keyboard-nav-C9zLmfrJ.js.map} +1 -1
- package/dist/{page-structure-WFqy5QjQ.js → page-structure-GqhCQsl3.js} +2 -2
- package/dist/{page-structure-WFqy5QjQ.js.map → page-structure-GqhCQsl3.js.map} +1 -1
- package/dist/text-simplify-C9gzE3t0.js +632 -0
- package/dist/text-simplify-C9gzE3t0.js.map +1 -0
- package/dist/{tts-02b9iV0h.js → tts-CjszLRnb.js} +58 -12
- package/dist/tts-CjszLRnb.js.map +1 -0
- package/dist/widget.js +1 -1
- package/dist/widget.js.map +1 -1
- package/package.json +10 -10
- package/dist/alt-text-CrPRUX83.js +0 -567
- package/dist/alt-text-CrPRUX83.js.map +0 -1
- package/dist/auto-scan-pg-09o7A.js +0 -885
- package/dist/auto-scan-pg-09o7A.js.map +0 -1
- package/dist/focus-highlight-CjERyyUF.js +0 -93
- package/dist/focus-highlight-CjERyyUF.js.map +0 -1
- package/dist/index-B6b-ij4T.js.map +0 -1
- package/dist/text-simplify-CELklw5A.js +0 -223
- package/dist/text-simplify-CELklw5A.js.map +0 -1
- package/dist/tts-02b9iV0h.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auto-scan-pg-09o7A.js","sources":["../src/features/auto-scan.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Accessify – WCAG Auto-Scan Feature\n// ---------------------------------------------------------------------------\n// Integrates axe-core for on-demand WCAG 2.1 accessibility scanning.\n// Lazy-loads axe-core (~300 KB) only when the user triggers a scan.\n// Displays results grouped by impact, provides safe auto-fix for common\n// issues, and allows exporting results as JSON.\n// ---------------------------------------------------------------------------\n\nimport type { FeatureModule, FeatureState, ScanResult, Violation } from '../types';\n\n// axe-core types (minimal, to avoid requiring the full @types/axe-core dep)\ninterface AxeResult {\n violations: AxeViolation[];\n passes: { id: string }[];\n incomplete: { id: string }[];\n inapplicable: { id: string }[];\n}\n\ninterface AxeViolation {\n id: string;\n impact?: 'critical' | 'serious' | 'moderate' | 'minor';\n description: string;\n helpUrl: string;\n nodes: { html: string; target: string[] }[];\n tags: string[];\n}\n\n// Global axe reference once loaded\nlet axe: any = null;\n\nexport default function createAutoScanModule(\n lang: string = 'de',\n): FeatureModule {\n let enabled = false;\n let styleEl: HTMLStyleElement | null = null;\n let panelEl: HTMLDivElement | null = null;\n let lastResult: ScanResult | null = null;\n let isScanning = false;\n\n const STYLE_ID = 'accessify-scan-styles';\n const PANEL_CLASS = 'accessify-scan-panel';\n\n // -------------------------------------------------------------------------\n // Styles\n // -------------------------------------------------------------------------\n\n function getStyles(): string {\n return `\n .${PANEL_CLASS} {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 2147483646;\n width: 560px;\n max-width: 92vw;\n max-height: 80vh;\n background: #1a1a2e;\n color: #f0f0f0;\n border-radius: 14px;\n box-shadow: 0 12px 48px rgba(0,0,0,0.5);\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n display: flex;\n flex-direction: column;\n animation: accessify-scan-fade-in 0.25s ease;\n }\n\n .${PANEL_CLASS}-backdrop {\n position: fixed;\n inset: 0;\n z-index: 2147483645;\n background: rgba(0,0,0,0.4);\n animation: accessify-scan-fade-in 0.2s ease;\n }\n\n .${PANEL_CLASS}-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid rgba(255,255,255,0.08);\n flex-shrink: 0;\n }\n .${PANEL_CLASS}-header h2 {\n margin: 0;\n font-size: 16px;\n font-weight: 700;\n color: #f0f0f0;\n }\n .${PANEL_CLASS}-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: #888;\n font-size: 18px;\n cursor: pointer;\n }\n .${PANEL_CLASS}-close:hover { color: #fff; background: rgba(255,255,255,0.1); }\n .${PANEL_CLASS}-close:focus-visible { outline: 2px solid #4ea8de; outline-offset: 1px; }\n\n .${PANEL_CLASS}-body {\n flex: 1;\n overflow-y: auto;\n padding: 16px 20px;\n }\n\n .${PANEL_CLASS}-score {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n background: rgba(255,255,255,0.04);\n border-radius: 10px;\n margin-bottom: 16px;\n }\n .${PANEL_CLASS}-score-ring {\n position: relative;\n width: 56px;\n height: 56px;\n flex-shrink: 0;\n }\n .${PANEL_CLASS}-score-ring svg {\n transform: rotate(-90deg);\n }\n .${PANEL_CLASS}-score-ring .score-text {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: 700;\n }\n .${PANEL_CLASS}-score-details {\n font-size: 13px;\n color: #aaa;\n }\n .${PANEL_CLASS}-score-details strong {\n color: #f0f0f0;\n font-weight: 600;\n }\n\n .${PANEL_CLASS}-group {\n margin-bottom: 14px;\n }\n .${PANEL_CLASS}-group-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 600;\n margin-bottom: 6px;\n cursor: pointer;\n user-select: none;\n }\n .${PANEL_CLASS}-group-header:hover {\n opacity: 0.9;\n }\n .${PANEL_CLASS}-group-header .count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 700;\n }\n .${PANEL_CLASS}-group[data-impact=\"critical\"] .${PANEL_CLASS}-group-header {\n background: rgba(220,53,69,0.12);\n color: #f0a0a8;\n }\n .${PANEL_CLASS}-group[data-impact=\"critical\"] .count {\n background: rgba(220,53,69,0.3);\n }\n .${PANEL_CLASS}-group[data-impact=\"serious\"] .${PANEL_CLASS}-group-header {\n background: rgba(255,152,0,0.12);\n color: #ffcc80;\n }\n .${PANEL_CLASS}-group[data-impact=\"serious\"] .count {\n background: rgba(255,152,0,0.3);\n }\n .${PANEL_CLASS}-group[data-impact=\"moderate\"] .${PANEL_CLASS}-group-header {\n background: rgba(255,193,7,0.12);\n color: #ffd54f;\n }\n .${PANEL_CLASS}-group[data-impact=\"moderate\"] .count {\n background: rgba(255,193,7,0.3);\n }\n .${PANEL_CLASS}-group[data-impact=\"minor\"] .${PANEL_CLASS}-group-header {\n background: rgba(78,168,222,0.12);\n color: #81d4fa;\n }\n .${PANEL_CLASS}-group[data-impact=\"minor\"] .count {\n background: rgba(78,168,222,0.3);\n }\n\n .${PANEL_CLASS}-violation {\n padding: 10px 14px;\n margin-bottom: 4px;\n background: rgba(255,255,255,0.03);\n border-radius: 6px;\n border-left: 3px solid rgba(255,255,255,0.1);\n }\n .${PANEL_CLASS}-violation-desc {\n font-size: 13px;\n margin-bottom: 4px;\n }\n .${PANEL_CLASS}-violation-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n font-size: 11px;\n color: #888;\n }\n .${PANEL_CLASS}-violation-meta a {\n color: #4ea8de;\n text-decoration: none;\n }\n .${PANEL_CLASS}-violation-meta a:hover { text-decoration: underline; }\n .${PANEL_CLASS}-violation-meta a:focus-visible {\n outline: 2px solid #4ea8de;\n outline-offset: 1px;\n }\n\n .${PANEL_CLASS}-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 20px;\n border-top: 1px solid rgba(255,255,255,0.08);\n flex-shrink: 0;\n gap: 8px;\n flex-wrap: wrap;\n }\n .${PANEL_CLASS}-footer button {\n padding: 7px 16px;\n border: 1px solid rgba(255,255,255,0.15);\n border-radius: 7px;\n background: rgba(255,255,255,0.08);\n color: #f0f0f0;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s ease;\n white-space: nowrap;\n }\n .${PANEL_CLASS}-footer button:hover { background: rgba(255,255,255,0.18); }\n .${PANEL_CLASS}-footer button:focus-visible { outline: 2px solid #4ea8de; outline-offset: 1px; }\n .${PANEL_CLASS}-footer button.primary {\n background: rgba(78,168,222,0.2);\n color: #4ea8de;\n border-color: rgba(78,168,222,0.3);\n }\n .${PANEL_CLASS}-footer button.primary:hover { background: rgba(78,168,222,0.35); }\n .${PANEL_CLASS}-footer button.fix {\n background: rgba(42,157,143,0.2);\n color: #2a9d8f;\n border-color: rgba(42,157,143,0.3);\n }\n .${PANEL_CLASS}-footer button.fix:hover { background: rgba(42,157,143,0.35); }\n\n .${PANEL_CLASS}-spinner {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 14px;\n padding: 40px 20px;\n color: #aaa;\n font-size: 14px;\n }\n .${PANEL_CLASS}-spinner::before {\n content: '';\n width: 28px;\n height: 28px;\n border: 3px solid rgba(78,168,222,0.2);\n border-top-color: #4ea8de;\n border-radius: 50%;\n animation: accessify-scan-spin 0.7s linear infinite;\n }\n\n .${PANEL_CLASS}-empty {\n text-align: center;\n padding: 32px 20px;\n color: #2a9d8f;\n font-size: 15px;\n font-weight: 600;\n }\n .${PANEL_CLASS}-empty .checkmark {\n display: block;\n font-size: 40px;\n margin-bottom: 8px;\n }\n\n .${PANEL_CLASS}-fix-report {\n padding: 12px 14px;\n margin: 12px 0;\n background: rgba(42,157,143,0.1);\n border: 1px solid rgba(42,157,143,0.25);\n border-radius: 8px;\n font-size: 13px;\n color: #81cec5;\n }\n .${PANEL_CLASS}-fix-report strong { color: #2a9d8f; }\n\n @keyframes accessify-scan-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes accessify-scan-spin {\n to { transform: rotate(360deg); }\n }\n `;\n }\n\n function injectStyles() {\n if (document.getElementById(STYLE_ID)) return;\n styleEl = document.createElement('style');\n styleEl.id = STYLE_ID;\n styleEl.textContent = getStyles();\n document.head.appendChild(styleEl);\n }\n\n function removeStyles() {\n styleEl?.remove();\n styleEl = null;\n document.getElementById(STYLE_ID)?.remove();\n }\n\n // -------------------------------------------------------------------------\n // Lazy-load axe-core\n // -------------------------------------------------------------------------\n\n async function loadAxe(): Promise<void> {\n if (axe) return;\n\n // Check if already on the page\n if ((window as any).axe) {\n axe = (window as any).axe;\n return;\n }\n\n return new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js';\n script.crossOrigin = 'anonymous';\n script.onload = () => {\n axe = (window as any).axe;\n if (!axe) {\n reject(new Error('axe-core loaded but not available on window'));\n return;\n }\n resolve();\n };\n script.onerror = () => reject(new Error('Failed to load axe-core'));\n document.head.appendChild(script);\n });\n }\n\n // -------------------------------------------------------------------------\n // Run scan\n // -------------------------------------------------------------------------\n\n async function runScan(): Promise<ScanResult> {\n await loadAxe();\n\n // Run axe against the host page, excluding the widget Shadow DOM\n const results: AxeResult = await axe.run(document, {\n exclude: ['#accessify-root'],\n runOnly: {\n type: 'tag',\n values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice'],\n },\n });\n\n const violations: Violation[] = results.violations.map((v: AxeViolation) => ({\n id: v.id,\n impact: v.impact || 'minor',\n description: v.description,\n helpUrl: v.helpUrl,\n nodes: v.nodes.length,\n wcagCriteria: extractWcagTags(v.tags),\n }));\n\n const scanResult: ScanResult = {\n violations,\n passes: results.passes.length,\n count: results.passes.length + results.violations.length,\n timestamp: Date.now(),\n };\n\n lastResult = scanResult;\n return scanResult;\n }\n\n function extractWcagTags(tags: string[]): string[] {\n return tags.filter((t) =>\n /^wcag\\d/.test(t) || t.startsWith('best-practice'),\n );\n }\n\n // -------------------------------------------------------------------------\n // Panel UI\n // -------------------------------------------------------------------------\n\n function showPanel() {\n removePanel();\n injectStyles();\n\n // Backdrop\n const backdrop = document.createElement('div');\n backdrop.className = `${PANEL_CLASS}-backdrop`;\n backdrop.addEventListener('click', () => removePanel());\n document.body.appendChild(backdrop);\n\n // Panel\n const panel = document.createElement('div');\n panel.className = PANEL_CLASS;\n panel.setAttribute('role', 'dialog');\n panel.setAttribute('aria-label', lang.startsWith('de') ? 'WCAG-Pr\\u00fcfergebnisse' : 'WCAG Scan Results');\n panel.setAttribute('aria-modal', 'true');\n\n // Header\n const header = document.createElement('div');\n header.className = `${PANEL_CLASS}-header`;\n\n const title = document.createElement('h2');\n title.textContent = lang.startsWith('de') ? 'WCAG-Pr\\u00fcfung' : 'WCAG Accessibility Scan';\n\n const closeBtn = document.createElement('button');\n closeBtn.className = `${PANEL_CLASS}-close`;\n closeBtn.setAttribute('aria-label', lang.startsWith('de') ? 'Schlie\\u00dfen' : 'Close');\n closeBtn.innerHTML = '×';\n closeBtn.addEventListener('click', () => removePanel());\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n panel.appendChild(header);\n\n // Body\n const body = document.createElement('div');\n body.className = `${PANEL_CLASS}-body`;\n panel.appendChild(body);\n\n document.body.appendChild(panel);\n panelEl = panel;\n\n // Run the scan\n showLoading(body);\n performScan(body, panel);\n\n // Close on Escape\n const handleEsc = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n removePanel();\n document.removeEventListener('keydown', handleEsc);\n }\n };\n document.addEventListener('keydown', handleEsc);\n }\n\n function showLoading(body: HTMLDivElement) {\n body.innerHTML = '';\n const spinner = document.createElement('div');\n spinner.className = `${PANEL_CLASS}-spinner`;\n\n const text = document.createElement('span');\n text.textContent = lang.startsWith('de')\n ? 'Seite wird auf Barrierefreiheit gepr\\u00fcft...'\n : 'Scanning page for accessibility issues...';\n spinner.appendChild(text);\n\n const hint = document.createElement('span');\n hint.style.fontSize = '12px';\n hint.style.color = '#666';\n hint.textContent = lang.startsWith('de')\n ? 'axe-core wird geladen und analysiert die Seite'\n : 'Loading axe-core and analyzing the page';\n spinner.appendChild(hint);\n\n body.appendChild(spinner);\n }\n\n async function performScan(body: HTMLDivElement, panel: HTMLDivElement) {\n isScanning = true;\n\n try {\n const result = await runScan();\n isScanning = false;\n renderResults(body, panel, result);\n } catch (err) {\n isScanning = false;\n body.innerHTML = '';\n\n const errorEl = document.createElement('div');\n errorEl.style.cssText =\n 'padding: 20px; text-align: center; color: #f0a0a8;';\n errorEl.textContent = lang.startsWith('de')\n ? `Fehler beim Scannen: ${(err as Error).message}`\n : `Scan error: ${(err as Error).message}`;\n body.appendChild(errorEl);\n\n addFooter(panel, null);\n }\n }\n\n function renderResults(\n body: HTMLDivElement,\n panel: HTMLDivElement,\n result: ScanResult,\n ) {\n body.innerHTML = '';\n\n // No violations\n if (result.violations.length === 0) {\n const empty = document.createElement('div');\n empty.className = `${PANEL_CLASS}-empty`;\n empty.innerHTML = `\n <span class=\"checkmark\" aria-hidden=\"true\">✓</span>\n ${lang.startsWith('de') ? 'Keine Probleme gefunden!' : 'No issues found!'}\n `;\n body.appendChild(empty);\n renderScore(body, result);\n addFooter(panel, result);\n return;\n }\n\n // Score\n renderScore(body, result);\n\n // Group violations by impact\n const groups: Record<string, Violation[]> = {\n critical: [],\n serious: [],\n moderate: [],\n minor: [],\n };\n\n result.violations.forEach((v) => {\n const impact = v.impact || 'minor';\n if (groups[impact]) {\n groups[impact].push(v);\n } else {\n groups.minor.push(v);\n }\n });\n\n const impactLabels: Record<string, Record<string, string>> = {\n critical: { de: 'Kritisch', en: 'Critical' },\n serious: { de: 'Schwerwiegend', en: 'Serious' },\n moderate: { de: 'Mittel', en: 'Moderate' },\n minor: { de: 'Gering', en: 'Minor' },\n };\n\n for (const [impact, violations] of Object.entries(groups)) {\n if (violations.length === 0) continue;\n\n const group = document.createElement('div');\n group.className = `${PANEL_CLASS}-group`;\n group.setAttribute('data-impact', impact);\n\n const groupHeader = document.createElement('div');\n groupHeader.className = `${PANEL_CLASS}-group-header`;\n\n const chevron = document.createElement('span');\n chevron.textContent = '\\u25BC';\n chevron.style.fontSize = '10px';\n chevron.style.transition = 'transform 0.15s ease';\n\n const label = document.createElement('span');\n const resolvedLang = lang.startsWith('de') ? 'de' : 'en';\n label.textContent = impactLabels[impact]?.[resolvedLang] || impact;\n\n const count = document.createElement('span');\n count.className = 'count';\n count.textContent = String(violations.length);\n\n groupHeader.appendChild(chevron);\n groupHeader.appendChild(label);\n groupHeader.appendChild(count);\n\n const groupBody = document.createElement('div');\n let collapsed = false;\n groupHeader.addEventListener('click', () => {\n collapsed = !collapsed;\n groupBody.style.display = collapsed ? 'none' : 'block';\n chevron.style.transform = collapsed ? 'rotate(-90deg)' : 'rotate(0)';\n });\n\n violations.forEach((v) => {\n const item = document.createElement('div');\n item.className = `${PANEL_CLASS}-violation`;\n\n const desc = document.createElement('div');\n desc.className = `${PANEL_CLASS}-violation-desc`;\n desc.textContent = v.description;\n\n const meta = document.createElement('div');\n meta.className = `${PANEL_CLASS}-violation-meta`;\n\n const nodeCount = document.createElement('span');\n nodeCount.textContent = lang.startsWith('de')\n ? `${v.nodes} ${v.nodes === 1 ? 'Element' : 'Elemente'}`\n : `${v.nodes} ${v.nodes === 1 ? 'element' : 'elements'}`;\n\n meta.appendChild(nodeCount);\n\n if (v.wcagCriteria.length > 0) {\n const criteria = document.createElement('span');\n criteria.textContent = v.wcagCriteria.join(', ');\n meta.appendChild(criteria);\n }\n\n const learnMore = document.createElement('a');\n learnMore.href = v.helpUrl;\n learnMore.target = '_blank';\n learnMore.rel = 'noopener noreferrer';\n learnMore.textContent = lang.startsWith('de') ? 'Mehr erfahren' : 'Learn more';\n meta.appendChild(learnMore);\n\n item.appendChild(desc);\n item.appendChild(meta);\n groupBody.appendChild(item);\n });\n\n group.appendChild(groupHeader);\n group.appendChild(groupBody);\n body.appendChild(group);\n }\n\n addFooter(panel, result);\n }\n\n function renderScore(body: HTMLDivElement, result: ScanResult) {\n const scoreBlock = document.createElement('div');\n scoreBlock.className = `${PANEL_CLASS}-score`;\n\n const total = result.count;\n const passed = result.passes;\n const percentage = total > 0 ? Math.round((passed / total) * 100) : 100;\n\n // Color based on score\n let color = '#2a9d8f'; // green\n if (percentage < 60) color = '#e63946'; // red\n else if (percentage < 80) color = '#f77f00'; // orange\n else if (percentage < 95) color = '#ffd166'; // yellow\n\n // SVG ring\n const ring = document.createElement('div');\n ring.className = `${PANEL_CLASS}-score-ring`;\n const circumference = 2 * Math.PI * 22;\n const offset = circumference - (percentage / 100) * circumference;\n\n ring.innerHTML = `\n <svg width=\"56\" height=\"56\" viewBox=\"0 0 56 56\" aria-hidden=\"true\">\n <circle cx=\"28\" cy=\"28\" r=\"22\" fill=\"none\" stroke=\"rgba(255,255,255,0.08)\" stroke-width=\"4\" />\n <circle cx=\"28\" cy=\"28\" r=\"22\" fill=\"none\" stroke=\"${color}\" stroke-width=\"4\"\n stroke-dasharray=\"${circumference}\" stroke-dashoffset=\"${offset}\" stroke-linecap=\"round\" />\n </svg>\n <div class=\"score-text\" style=\"color:${color}\">${percentage}%</div>\n `;\n\n const details = document.createElement('div');\n details.className = `${PANEL_CLASS}-score-details`;\n details.innerHTML = lang.startsWith('de')\n ? `<strong>${passed}</strong> von <strong>${total}</strong> Pr\\u00fcfungen bestanden<br>${result.violations.length} ${result.violations.length === 1 ? 'Problem' : 'Probleme'} gefunden`\n : `<strong>${passed}</strong> of <strong>${total}</strong> checks passed<br>${result.violations.length} ${result.violations.length === 1 ? 'issue' : 'issues'} found`;\n\n scoreBlock.appendChild(ring);\n scoreBlock.appendChild(details);\n\n // Insert at the top of body\n body.insertBefore(scoreBlock, body.firstChild);\n }\n\n function addFooter(panel: HTMLDivElement, result: ScanResult | null) {\n // Remove existing footer\n panel.querySelector(`.${PANEL_CLASS}-footer`)?.remove();\n\n const footer = document.createElement('div');\n footer.className = `${PANEL_CLASS}-footer`;\n\n const leftGroup = document.createElement('div');\n leftGroup.style.display = 'flex';\n leftGroup.style.gap = '6px';\n\n // Rescan button\n const rescanBtn = document.createElement('button');\n rescanBtn.classList.add('primary');\n rescanBtn.textContent = lang.startsWith('de') ? 'Erneut pr\\u00fcfen' : 'Rescan';\n rescanBtn.addEventListener('click', () => {\n const body = panel.querySelector(`.${PANEL_CLASS}-body`) as HTMLDivElement;\n if (body) {\n showLoading(body);\n performScan(body, panel);\n }\n });\n leftGroup.appendChild(rescanBtn);\n\n // Auto-fix button (only if there are fixable violations)\n if (result && result.violations.length > 0) {\n const fixableCount = countFixableIssues(result.violations);\n if (fixableCount > 0) {\n const fixBtn = document.createElement('button');\n fixBtn.classList.add('fix');\n fixBtn.textContent = lang.startsWith('de')\n ? `${fixableCount} automatisch beheben`\n : `Auto-fix ${fixableCount} ${fixableCount === 1 ? 'issue' : 'issues'}`;\n fixBtn.addEventListener('click', () => {\n const report = applyAutoFixes(result.violations);\n const body = panel.querySelector(`.${PANEL_CLASS}-body`) as HTMLDivElement;\n if (body) {\n showFixReport(body, report);\n }\n });\n leftGroup.appendChild(fixBtn);\n }\n }\n\n const rightGroup = document.createElement('div');\n rightGroup.style.display = 'flex';\n rightGroup.style.gap = '6px';\n\n // Export JSON button\n if (result) {\n const exportBtn = document.createElement('button');\n exportBtn.textContent = lang.startsWith('de') ? 'JSON exportieren' : 'Export JSON';\n exportBtn.addEventListener('click', () => {\n exportResultsAsJson(result);\n });\n rightGroup.appendChild(exportBtn);\n }\n\n // Close button\n const closeBtn = document.createElement('button');\n closeBtn.textContent = lang.startsWith('de') ? 'Schlie\\u00dfen' : 'Close';\n closeBtn.addEventListener('click', () => removePanel());\n rightGroup.appendChild(closeBtn);\n\n footer.appendChild(leftGroup);\n footer.appendChild(rightGroup);\n panel.appendChild(footer);\n }\n\n // -------------------------------------------------------------------------\n // Auto-fix logic\n // -------------------------------------------------------------------------\n\n interface FixReport {\n fixed: string[];\n skipped: string[];\n }\n\n const FIXABLE_RULES: Record<\n string,\n { label: string; labelDe: string; fix: () => boolean }\n > = {\n 'html-has-lang': {\n label: 'Added lang attribute to <html>',\n labelDe: 'lang-Attribut zu <html> hinzugef\\u00fcgt',\n fix: () => fixMissingLang(),\n },\n 'document-title': {\n label: 'Added page title',\n labelDe: 'Seitentitel hinzugef\\u00fcgt',\n fix: () => fixMissingTitle(),\n },\n bypass: {\n label: 'Added skip-to-content link',\n labelDe: '\\u00dcberspringen-Link hinzugef\\u00fcgt',\n fix: () => fixMissingSkipLink(),\n },\n 'link-name': {\n label: 'Added aria-labels to empty links',\n labelDe: 'aria-label zu leeren Links hinzugef\\u00fcgt',\n fix: () => fixEmptyLinks(),\n },\n 'button-name': {\n label: 'Added aria-labels to empty buttons',\n labelDe: 'aria-label zu leeren Buttons hinzugef\\u00fcgt',\n fix: () => fixEmptyButtons(),\n },\n 'focus-order-semantics': {\n label: 'Added focus indicators CSS',\n labelDe: 'CSS-Fokus-Indikatoren hinzugef\\u00fcgt',\n fix: () => fixMissingFocusIndicators(),\n },\n };\n\n function countFixableIssues(violations: Violation[]): number {\n return violations.filter((v) => FIXABLE_RULES[v.id]).length;\n }\n\n function applyAutoFixes(violations: Violation[]): FixReport {\n const report: FixReport = { fixed: [], skipped: [] };\n const resolvedLang = lang.startsWith('de') ? 'de' : 'en';\n\n violations.forEach((v) => {\n const rule = FIXABLE_RULES[v.id];\n if (!rule) return;\n\n try {\n const success = rule.fix();\n if (success) {\n report.fixed.push(resolvedLang === 'de' ? rule.labelDe : rule.label);\n } else {\n report.skipped.push(\n resolvedLang === 'de' ? rule.labelDe : rule.label,\n );\n }\n } catch {\n report.skipped.push(resolvedLang === 'de' ? rule.labelDe : rule.label);\n }\n });\n\n return report;\n }\n\n function showFixReport(body: HTMLDivElement, report: FixReport) {\n // Remove existing fix report\n body.querySelectorAll(`.${PANEL_CLASS}-fix-report`).forEach((el) => el.remove());\n\n const el = document.createElement('div');\n el.className = `${PANEL_CLASS}-fix-report`;\n el.setAttribute('role', 'status');\n el.setAttribute('aria-live', 'polite');\n\n let html = '';\n if (report.fixed.length > 0) {\n html += `<strong>${lang.startsWith('de') ? 'Behoben:' : 'Fixed:'}</strong><br>`;\n html += report.fixed.map((f) => `✓ ${f}`).join('<br>');\n }\n if (report.skipped.length > 0) {\n if (html) html += '<br><br>';\n html += `<strong>${lang.startsWith('de') ? '\\u00dcbersprungen:' : 'Skipped:'}</strong><br>`;\n html += report.skipped.map((s) => `• ${s}`).join('<br>');\n }\n if (report.fixed.length === 0 && report.skipped.length === 0) {\n html = lang.startsWith('de')\n ? 'Keine automatisch behebbaren Probleme gefunden.'\n : 'No auto-fixable issues found.';\n }\n\n el.innerHTML = html;\n body.insertBefore(el, body.firstChild);\n }\n\n // -------------------------------------------------------------------------\n // Individual fix implementations\n // -------------------------------------------------------------------------\n\n function fixMissingLang(): boolean {\n const html = document.documentElement;\n if (html.hasAttribute('lang') && html.lang.trim() !== '') return false;\n\n // Attempt to detect language from page content\n const detectedLang = detectContentLanguage();\n html.setAttribute('lang', detectedLang);\n return true;\n }\n\n function detectContentLanguage(): string {\n // Check meta tags\n const metaLang = document.querySelector<HTMLMetaElement>(\n 'meta[http-equiv=\"content-language\"]',\n );\n if (metaLang?.content) return metaLang.content;\n\n // Check Open Graph locale\n const ogLocale = document.querySelector<HTMLMetaElement>(\n 'meta[property=\"og:locale\"]',\n );\n if (ogLocale?.content) return ogLocale.content.replace('_', '-');\n\n // Simple heuristic: check for common German words in visible text\n const bodyText = (document.body.innerText || '').toLowerCase().slice(0, 2000);\n const germanIndicators = ['und', 'der', 'die', 'das', 'ist', 'nicht', 'ein', 'eine', 'mit', 'auf'];\n const englishIndicators = ['the', 'and', 'is', 'are', 'for', 'not', 'with', 'this', 'that', 'was'];\n\n let germanScore = 0;\n let englishScore = 0;\n\n germanIndicators.forEach((w) => {\n const regex = new RegExp(`\\\\b${w}\\\\b`, 'gi');\n germanScore += (bodyText.match(regex) || []).length;\n });\n englishIndicators.forEach((w) => {\n const regex = new RegExp(`\\\\b${w}\\\\b`, 'gi');\n englishScore += (bodyText.match(regex) || []).length;\n });\n\n return germanScore > englishScore ? 'de' : 'en';\n }\n\n function fixMissingTitle(): boolean {\n let titleEl = document.querySelector('title');\n if (titleEl && titleEl.textContent?.trim()) return false;\n\n // Generate a title from the first h1 or the domain\n const h1 = document.querySelector('h1');\n const titleText =\n h1?.textContent?.trim() || window.location.hostname || 'Untitled Page';\n\n if (!titleEl) {\n titleEl = document.createElement('title');\n document.head.appendChild(titleEl);\n }\n titleEl.textContent = titleText;\n return true;\n }\n\n function fixMissingSkipLink(): boolean {\n // Check if a skip link already exists\n const existing = document.querySelector(\n 'a[href=\"#main\"], a[href=\"#content\"], a[href=\"#main-content\"], .skip-link, .skip-to-content',\n );\n if (existing) return false;\n\n // Find the main content area\n const main =\n document.querySelector('main') ||\n document.getElementById('main') ||\n document.getElementById('content') ||\n document.getElementById('main-content');\n\n if (!main) {\n // Create an anchor on the first heading\n const firstH = document.querySelector('h1, h2');\n if (firstH && !firstH.id) {\n firstH.id = 'accessify-main-content';\n }\n }\n\n const targetId =\n main?.id ||\n (main ? (main.id = 'accessify-main-content') : 'accessify-main-content');\n\n const skipLink = document.createElement('a');\n skipLink.href = `#${targetId}`;\n skipLink.textContent = lang.startsWith('de')\n ? 'Zum Inhalt springen'\n : 'Skip to content';\n skipLink.className = 'accessify-skip-link';\n\n Object.assign(skipLink.style, {\n position: 'absolute',\n top: '-100px',\n left: '0',\n padding: '8px 16px',\n background: '#1a1a2e',\n color: '#4ea8de',\n fontSize: '14px',\n fontWeight: '600',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n textDecoration: 'none',\n zIndex: '2147483647',\n borderRadius: '0 0 8px 0',\n transition: 'top 0.2s ease',\n });\n\n // Show on focus\n skipLink.addEventListener('focus', () => {\n skipLink.style.top = '0';\n });\n skipLink.addEventListener('blur', () => {\n skipLink.style.top = '-100px';\n });\n\n document.body.insertBefore(skipLink, document.body.firstChild);\n return true;\n }\n\n function fixEmptyLinks(): boolean {\n const emptyLinks = document.querySelectorAll<HTMLAnchorElement>(\n 'a:not([aria-label]):not([aria-labelledby])',\n );\n let fixedCount = 0;\n\n emptyLinks.forEach((link) => {\n // Skip links that already have accessible text\n const text = (link.textContent || '').trim();\n const img = link.querySelector('img[alt]');\n if (text || (img && (img as HTMLImageElement).alt)) return;\n\n // Try to derive a label\n const title = link.title?.trim();\n const href = link.href;\n const imgInLink = link.querySelector('img');\n const ariaLabel =\n title ||\n (imgInLink ? (imgInLink as HTMLImageElement).alt : '') ||\n (href ? new URL(href, window.location.href).hostname : '') ||\n 'Link';\n\n if (ariaLabel) {\n link.setAttribute('aria-label', ariaLabel);\n fixedCount++;\n }\n });\n\n return fixedCount > 0;\n }\n\n function fixEmptyButtons(): boolean {\n const emptyButtons = document.querySelectorAll<HTMLButtonElement>(\n 'button:not([aria-label]):not([aria-labelledby])',\n );\n let fixedCount = 0;\n\n emptyButtons.forEach((btn) => {\n const text = (btn.textContent || '').trim();\n if (text) return;\n\n // Try to derive a label from title, inner img alt, or class name\n const title = btn.title?.trim();\n const img = btn.querySelector('img[alt]');\n const svg = btn.querySelector('svg');\n const ariaLabel =\n title ||\n (img ? (img as HTMLImageElement).alt : '') ||\n (svg ? deriveNameFromSvg(svg as SVGElement) : '') ||\n deriveNameFromClass(btn.className) ||\n 'Button';\n\n btn.setAttribute('aria-label', ariaLabel);\n fixedCount++;\n });\n\n return fixedCount > 0;\n }\n\n function fixMissingFocusIndicators(): boolean {\n const FOCUS_STYLE_ID = 'accessify-focus-fix';\n if (document.getElementById(FOCUS_STYLE_ID)) return false;\n\n const style = document.createElement('style');\n style.id = FOCUS_STYLE_ID;\n style.textContent = `\n *:focus-visible {\n outline: 3px solid #4ea8de !important;\n outline-offset: 2px !important;\n }\n `;\n document.head.appendChild(style);\n return true;\n }\n\n // -------------------------------------------------------------------------\n // Helper utilities for fix logic\n // -------------------------------------------------------------------------\n\n function deriveNameFromSvg(svg: SVGElement): string {\n // Check for <title> inside SVG\n const svgTitle = svg.querySelector('title');\n if (svgTitle?.textContent?.trim()) return svgTitle.textContent.trim();\n\n // Check aria-label on SVG itself\n const ariaLabel = svg.getAttribute('aria-label');\n if (ariaLabel?.trim()) return ariaLabel.trim();\n\n return '';\n }\n\n function deriveNameFromClass(className: string): string {\n if (!className) return '';\n // Extract meaningful words from class names\n const words = className\n .replace(/[_-]/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .split(/\\s+/)\n .filter(\n (w) =>\n w.length > 2 &&\n !['btn', 'button', 'icon', 'svg', 'img', 'cls', 'mod'].includes(\n w.toLowerCase(),\n ),\n );\n\n if (words.length > 0) {\n return words.slice(0, 3).join(' ');\n }\n return '';\n }\n\n // -------------------------------------------------------------------------\n // Export results\n // -------------------------------------------------------------------------\n\n function exportResultsAsJson(result: ScanResult) {\n const exportData = {\n ...result,\n url: window.location.href,\n generatedBy: 'Accessify WCAG Scanner',\n exportedAt: new Date().toISOString(),\n };\n\n const json = JSON.stringify(exportData, null, 2);\n const blob = new Blob([json], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n\n const a = document.createElement('a');\n a.href = url;\n a.download = `accessify-scan-${Date.now()}.json`;\n a.click();\n URL.revokeObjectURL(url);\n }\n\n // -------------------------------------------------------------------------\n // Panel management\n // -------------------------------------------------------------------------\n\n function removePanel() {\n panelEl?.remove();\n panelEl = null;\n document.querySelector(`.${PANEL_CLASS}-backdrop`)?.remove();\n }\n\n // -------------------------------------------------------------------------\n // Module lifecycle\n // -------------------------------------------------------------------------\n\n function activate() {\n if (enabled) return;\n enabled = true;\n showPanel();\n }\n\n function deactivate() {\n enabled = false;\n removePanel();\n removeStyles();\n }\n\n // -------------------------------------------------------------------------\n // FeatureModule interface\n // -------------------------------------------------------------------------\n\n return {\n id: 'auto-scan',\n name: () => (lang.startsWith('de') ? 'WCAG-Pr\\u00fcfung' : 'WCAG Scan'),\n description: lang.startsWith('de')\n ? 'Seite auf Barrierefreiheit pr\\u00fcfen'\n : 'Scan page for accessibility issues',\n icon: 'scan',\n category: 'ai',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'auto-scan',\n enabled,\n value: lastResult\n ? {\n violations: lastResult.violations.length,\n passes: lastResult.passes,\n total: lastResult.count,\n timestamp: lastResult.timestamp,\n }\n : null,\n }),\n };\n}\n"],"names":["el"],"mappings":"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"}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
function createFocusHighlightModule() {
|
|
2
|
-
let enabled = false;
|
|
3
|
-
const STYLE_ID = "accessify-focus-highlight";
|
|
4
|
-
const STORAGE_KEY = "accessify-focus-highlight";
|
|
5
|
-
function getStyles() {
|
|
6
|
-
return `
|
|
7
|
-
/* accessify focus highlight - WCAG 2.4.7 / 2.4.13 compliance */
|
|
8
|
-
*:focus-visible {
|
|
9
|
-
outline: 3px solid #1a73e8 !important;
|
|
10
|
-
outline-offset: 2px !important;
|
|
11
|
-
box-shadow: 0 0 0 6px rgba(26, 115, 232, 0.3) !important;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/* Override any outline:none that pages may set */
|
|
15
|
-
a:focus-visible,
|
|
16
|
-
button:focus-visible,
|
|
17
|
-
input:focus-visible,
|
|
18
|
-
select:focus-visible,
|
|
19
|
-
textarea:focus-visible,
|
|
20
|
-
[tabindex]:focus-visible,
|
|
21
|
-
[role="button"]:focus-visible,
|
|
22
|
-
[role="link"]:focus-visible,
|
|
23
|
-
[role="checkbox"]:focus-visible,
|
|
24
|
-
[role="radio"]:focus-visible,
|
|
25
|
-
[role="tab"]:focus-visible,
|
|
26
|
-
[role="menuitem"]:focus-visible,
|
|
27
|
-
[role="option"]:focus-visible,
|
|
28
|
-
[role="switch"]:focus-visible,
|
|
29
|
-
[contenteditable="true"]:focus-visible,
|
|
30
|
-
summary:focus-visible,
|
|
31
|
-
details:focus-visible {
|
|
32
|
-
outline: 3px solid #1a73e8 !important;
|
|
33
|
-
outline-offset: 2px !important;
|
|
34
|
-
box-shadow: 0 0 0 6px rgba(26, 115, 232, 0.3) !important;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/* High contrast mode: use yellow outline for dark backgrounds */
|
|
38
|
-
@media (prefers-color-scheme: dark) {
|
|
39
|
-
*:focus-visible {
|
|
40
|
-
outline-color: #ffdd00 !important;
|
|
41
|
-
box-shadow: 0 0 0 6px rgba(255, 221, 0, 0.3) !important;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
`;
|
|
45
|
-
}
|
|
46
|
-
function injectStyles() {
|
|
47
|
-
let styleEl = document.getElementById(STYLE_ID);
|
|
48
|
-
if (!styleEl) {
|
|
49
|
-
styleEl = document.createElement("style");
|
|
50
|
-
styleEl.id = STYLE_ID;
|
|
51
|
-
document.head.appendChild(styleEl);
|
|
52
|
-
}
|
|
53
|
-
styleEl.textContent = getStyles();
|
|
54
|
-
}
|
|
55
|
-
function removeStyles() {
|
|
56
|
-
const styleEl = document.getElementById(STYLE_ID);
|
|
57
|
-
styleEl?.remove();
|
|
58
|
-
}
|
|
59
|
-
function activate() {
|
|
60
|
-
enabled = true;
|
|
61
|
-
injectStyles();
|
|
62
|
-
localStorage.setItem(STORAGE_KEY, "true");
|
|
63
|
-
}
|
|
64
|
-
function deactivate() {
|
|
65
|
-
enabled = false;
|
|
66
|
-
removeStyles();
|
|
67
|
-
localStorage.removeItem(STORAGE_KEY);
|
|
68
|
-
}
|
|
69
|
-
return {
|
|
70
|
-
id: "focus-highlight",
|
|
71
|
-
name: () => "Focus Highlight",
|
|
72
|
-
description: "Add strong visible focus indicators on all focusable elements (WCAG 2.4.7/2.4.13)",
|
|
73
|
-
icon: "focus-highlight",
|
|
74
|
-
category: "motor",
|
|
75
|
-
activate,
|
|
76
|
-
deactivate,
|
|
77
|
-
getState: () => ({
|
|
78
|
-
id: "focus-highlight",
|
|
79
|
-
enabled
|
|
80
|
-
}),
|
|
81
|
-
setState: (state) => {
|
|
82
|
-
if (state.enabled) {
|
|
83
|
-
activate();
|
|
84
|
-
} else {
|
|
85
|
-
deactivate();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
export {
|
|
91
|
-
createFocusHighlightModule as default
|
|
92
|
-
};
|
|
93
|
-
//# sourceMappingURL=focus-highlight-CjERyyUF.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"focus-highlight-CjERyyUF.js","sources":["../src/features/focus-highlight.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\nexport default function createFocusHighlightModule(): FeatureModule {\n let enabled = false;\n const STYLE_ID = 'accessify-focus-highlight';\n const STORAGE_KEY = 'accessify-focus-highlight';\n\n function getStyles(): string {\n return `\n /* accessify focus highlight - WCAG 2.4.7 / 2.4.13 compliance */\n *:focus-visible {\n outline: 3px solid #1a73e8 !important;\n outline-offset: 2px !important;\n box-shadow: 0 0 0 6px rgba(26, 115, 232, 0.3) !important;\n }\n\n /* Override any outline:none that pages may set */\n a:focus-visible,\n button:focus-visible,\n input:focus-visible,\n select:focus-visible,\n textarea:focus-visible,\n [tabindex]:focus-visible,\n [role=\"button\"]:focus-visible,\n [role=\"link\"]:focus-visible,\n [role=\"checkbox\"]:focus-visible,\n [role=\"radio\"]:focus-visible,\n [role=\"tab\"]:focus-visible,\n [role=\"menuitem\"]:focus-visible,\n [role=\"option\"]:focus-visible,\n [role=\"switch\"]:focus-visible,\n [contenteditable=\"true\"]:focus-visible,\n summary:focus-visible,\n details:focus-visible {\n outline: 3px solid #1a73e8 !important;\n outline-offset: 2px !important;\n box-shadow: 0 0 0 6px rgba(26, 115, 232, 0.3) !important;\n }\n\n /* High contrast mode: use yellow outline for dark backgrounds */\n @media (prefers-color-scheme: dark) {\n *:focus-visible {\n outline-color: #ffdd00 !important;\n box-shadow: 0 0 0 6px rgba(255, 221, 0, 0.3) !important;\n }\n }\n `;\n }\n\n function injectStyles() {\n let styleEl = document.getElementById(STYLE_ID);\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = STYLE_ID;\n document.head.appendChild(styleEl);\n }\n styleEl.textContent = getStyles();\n }\n\n function removeStyles() {\n const styleEl = document.getElementById(STYLE_ID);\n styleEl?.remove();\n }\n\n function activate() {\n enabled = true;\n injectStyles();\n localStorage.setItem(STORAGE_KEY, 'true');\n }\n\n function deactivate() {\n enabled = false;\n removeStyles();\n localStorage.removeItem(STORAGE_KEY);\n }\n\n return {\n id: 'focus-highlight',\n name: () => 'Focus Highlight',\n description: 'Add strong visible focus indicators on all focusable elements (WCAG 2.4.7/2.4.13)',\n icon: 'focus-highlight',\n category: 'motor',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'focus-highlight',\n enabled,\n }),\n setState: (state: { enabled: boolean }) => {\n if (state.enabled) {\n activate();\n } else {\n deactivate();\n }\n },\n };\n}\n"],"names":[],"mappings":"AAEA,SAAwB,6BAA4C;AAClE,MAAI,UAAU;AACd,QAAM,WAAW;AACjB,QAAM,cAAc;AAEpB,WAAS,YAAoB;AAC3B,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuCT;AAEA,WAAS,eAAe;AACtB,QAAI,UAAU,SAAS,eAAe,QAAQ;AAC9C,QAAI,CAAC,SAAS;AACZ,gBAAU,SAAS,cAAc,OAAO;AACxC,cAAQ,KAAK;AACb,eAAS,KAAK,YAAY,OAAO;AAAA,IACnC;AACA,YAAQ,cAAc,UAAA;AAAA,EACxB;AAEA,WAAS,eAAe;AACtB,UAAM,UAAU,SAAS,eAAe,QAAQ;AAChD,aAAS,OAAA;AAAA,EACX;AAEA,WAAS,WAAW;AAClB,cAAU;AACV,iBAAA;AACA,iBAAa,QAAQ,aAAa,MAAM;AAAA,EAC1C;AAEA,WAAS,aAAa;AACpB,cAAU;AACV,iBAAA;AACA,iBAAa,WAAW,WAAW;AAAA,EACrC;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM,MAAM;AAAA,IACZ,aAAa;AAAA,IACb,MAAM;AAAA,IACN,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU,OAAqB;AAAA,MAC7B,IAAI;AAAA,MACJ;AAAA,IAAA;AAAA,IAEF,UAAU,CAAC,UAAgC;AACzC,UAAI,MAAM,SAAS;AACjB,iBAAA;AAAA,MACF,OAAO;AACL,mBAAA;AAAA,MACF;AAAA,IACF;AAAA,EAAA;AAEJ;"}
|