import { require_lib as require_lib2 } from "./chunk-WTIA47ZU.js"; import { require_lib } from "./chunk-WDCEFGHP.js"; import { attachTooltipToHighlight, createHighlight, getElementsPositionInDocument, positionHighlight } from "./chunk-LEAOZWS7.js"; import { escape } from "./chunk-PXGSXSC7.js"; import { closeOnOutsideClick } from "./chunk-WM2KMMIK.js"; import { settings } from "./chunk-LEX3GG7N.js"; import { __toESM } from "./chunk-5WRI5ZAA.js"; // node_modules/astro/dist/runtime/client/dev-toolbar/apps/audit/annotations.js var ELEMENT_ANNOTATIONS = /* @__PURE__ */ new WeakMap(); function getAnnotationsForElement(element) { return ELEMENT_ANNOTATIONS.get(element); } var ANNOTATION_MAP = { "data-astro-source-file": "file", "data-astro-source-loc": "location" }; function extractAnnotations(element) { const annotations = {}; for (const [attr, key] of Object.entries(ANNOTATION_MAP)) { annotations[key] = element.getAttribute(attr); } for (const attr of Object.keys(ANNOTATION_MAP)) { element.removeAttribute(attr); } return annotations; } function processAnnotations() { for (const element of document.querySelectorAll(`[data-astro-source-file]`)) { ELEMENT_ANNOTATIONS.set(element, extractAnnotations(element)); } } // node_modules/astro/dist/runtime/client/dev-toolbar/apps/audit/rules/a11y.js var import_aria_query = __toESM(require_lib(), 1); var import_axobject_query = __toESM(require_lib2(), 1); var WHITESPACE_REGEX = /\s+/; var a11y_required_attributes = { a: ["href"], area: ["alt", "aria-label", "aria-labelledby"], // html-has-lang html: ["lang"], // iframe-has-title iframe: ["title"], img: ["alt"], object: ["title", "aria-label", "aria-labelledby"] }; var MAYBE_INTERACTIVE = /* @__PURE__ */ new Map([ ["a", "href"], ["input", "type"], ["audio", "controls"], ["img", "usemap"], ["object", "usemap"], ["video", "controls"] ]); var interactiveElements = [ "button", "details", "embed", "iframe", "label", "select", "textarea", ...MAYBE_INTERACTIVE.keys() ]; var labelableElements = ["button", "input", "meter", "output", "progress", "select", "textarea"]; var aria_non_interactive_roles = [ "alert", "alertdialog", "application", "article", "banner", "cell", "columnheader", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "group", "heading", "image", "img", "list", "listitem", "log", "main", "marquee", "math", "navigation", "none", "note", "presentation", "region", "row", "rowgroup", "rowheader", "search", "status", "tabpanel", "term", "timer", "toolbar", "tooltip" ]; var roleless_elements = ["div", "span"]; var a11y_required_content = [ // anchor-has-content "a", // heading-has-content "h1", "h2", "h3", "h4", "h5", "h6" ]; var a11y_distracting_elements = ["blink", "marquee"]; var a11y_implicit_semantics = /* @__PURE__ */ new Map([ ["a", "link"], ["area", "link"], ["article", "article"], ["aside", "complementary"], ["body", "document"], ["button", "button"], ["datalist", "listbox"], ["dd", "definition"], ["dfn", "term"], ["dialog", "dialog"], ["details", "group"], ["dt", "term"], ["fieldset", "group"], ["figure", "figure"], ["form", "form"], ["h1", "heading"], ["h2", "heading"], ["h3", "heading"], ["h4", "heading"], ["h5", "heading"], ["h6", "heading"], ["hr", "separator"], ["img", "image"], ["li", "listitem"], ["link", "link"], ["main", "main"], ["menu", "list"], ["meter", "progressbar"], ["nav", "navigation"], ["ol", "list"], ["option", "option"], ["optgroup", "group"], ["output", "status"], ["progress", "progressbar"], ["section", "region"], ["summary", "button"], ["table", "table"], ["tbody", "rowgroup"], ["textarea", "textbox"], ["tfoot", "rowgroup"], ["thead", "rowgroup"], ["tr", "row"], ["ul", "list"] ]); var menuitem_type_to_implicit_role = /* @__PURE__ */ new Map([ ["command", "menuitem"], ["checkbox", "menuitemcheckbox"], ["radio", "menuitemradio"] ]); var input_type_to_implicit_role = /* @__PURE__ */ new Map([ ["button", "button"], ["image", "button"], ["reset", "button"], ["submit", "button"], ["checkbox", "checkbox"], ["radio", "radio"], ["range", "slider"], ["number", "spinbutton"], ["email", "textbox"], ["search", "searchbox"], ["tel", "textbox"], ["text", "textbox"], ["url", "textbox"] ]); var ariaAttributes = new Set( "activedescendant atomic autocomplete busy checked colcount colindex colspan controls current describedby details disabled dropeffect errormessage expanded flowto grabbed haspopup hidden invalid keyshortcuts label labelledby level live modal multiline multiselectable orientation owns placeholder posinset pressed readonly relevant required roledescription rowcount rowindex rowspan selected setsize sort valuemax valuemin valuenow valuetext".split( " " ) ); var ariaRoles = new Set( "alert alertdialog application article banner blockquote button caption cell checkbox code columnheader combobox command complementary composite contentinfo definition deletion dialog directory document emphasis feed figure form generic grid gridcell group heading image img input insertion landmark link list listbox listitem log main marquee math meter menu menubar menuitem menuitemcheckbox menuitemradio navigation none note option paragraph presentation progressbar radio radiogroup range region roletype row rowgroup rowheader scrollbar search searchbox section sectionhead select separator slider spinbutton status strong structure subscript superscript switch tab table tablist tabpanel term textbox time timer toolbar tooltip tree treegrid treeitem widget window".split( " " ) ); function isInteractive(element) { const attribute = MAYBE_INTERACTIVE.get(element.localName); if (attribute) { return element.hasAttribute(attribute); } return true; } var a11y = [ { code: "a11y-accesskey", title: "Avoid using `accesskey`", message: "The `accesskey` attribute can cause accessibility issues. The shortcuts can conflict with the browser's or operating system's shortcuts, and they are difficult for users to discover and use.", selector: "[accesskey]" }, { code: "a11y-aria-activedescendant-has-tabindex", title: "Elements with attribute `aria-activedescendant` must be tabbable", message: "Element with the `aria-activedescendant` attribute must either have an inherent `tabindex` or declare `tabindex` as an attribute.", selector: "[aria-activedescendant]", match(element) { if (!element.tabIndex && !element.hasAttribute("tabindex")) return true; } }, { code: "a11y-aria-attributes", title: "Element does not support ARIA roles.", message: "Elements like `meta`, `html`, `script`, `style` do not support having ARIA roles.", selector: ":is(meta, html, script, style)[role]", match(element) { for (const attribute of element.attributes) { if (attribute.name.startsWith("aria-")) return true; } } }, { code: "a11y-autofocus", title: "Avoid using `autofocus`", message: "The `autofocus` attribute can cause accessibility issues, as it can cause the focus to move around unexpectedly for screen reader users.", selector: "[autofocus]" }, { code: "a11y-distracting-elements", title: "Distracting elements should not be used", message: "Elements that can be visually distracting like `` or `` can cause accessibility issues for visually impaired users and should be avoided.", selector: `:is(${a11y_distracting_elements.join(",")})` }, { code: "a11y-hidden", title: "Certain DOM elements are useful for screen reader navigation and should not be hidden", message: (element) => `${element.localName} element should not be hidden.`, selector: "[aria-hidden]:is(h1,h2,h3,h4,h5,h6)" }, { code: "a11y-img-redundant-alt", title: "Redundant text in alt attribute", message: 'Screen readers already announce `img` elements as an image. There is no need to use words such as "image", "photo", and/or "picture".', selector: "img[alt]:not([aria-hidden])", match: (img) => /\b(?:image|picture|photo)\b/i.test(img.alt) }, { code: "a11y-incorrect-aria-attribute-type", title: "Incorrect value for ARIA attribute.", message: "`aria-hidden` should only receive a boolean.", selector: "[aria-hidden]", match(element) { const value = element.getAttribute("aria-hidden"); if (!value) return true; if (!["true", "false"].includes(value)) return true; } }, { code: "a11y-invalid-href", title: "Invalid `href` attribute", message: "`href` should not be empty, `'#'`, or `javascript:`.", selector: 'a[href]:is([href=""], [href="#"], [href^="javascript:" i])' }, { code: "a11y-invalid-label", title: "`label` element should have an associated control and a text content.", message: "The `label` element must be associated with a control either by using the `for` attribute or by containing a nested form element. Additionally, the `label` element must have text content.", selector: "label", match(element) { const hasFor = element.hasAttribute("for"); const nestedLabelableElement = element.querySelector(`${labelableElements.join(", ")}`); if (!hasFor && !nestedLabelableElement) return true; const innerText = element.innerText.trim(); if (innerText === "") return true; } }, { code: "a11y-media-has-caption", title: "Unmuted video elements should have captions", message: "Videos without captions can be difficult for deaf and hard-of-hearing users to follow along with. If the video does not need captions, add the `muted` attribute.", selector: "video:not([muted])", match(element) { const tracks = element.querySelectorAll("track"); if (!tracks.length) return true; const hasCaptionTrack = Array.from(tracks).some( (track) => track.getAttribute("kind") === "captions" ); return !hasCaptionTrack; } }, { code: "a11y-misplaced-scope", title: "The `scope` attribute should only be used on `` elements", message: "The `scope` attribute tells the browser and screen readers how to navigate tables. In HTML5, it should only be used on `` elements.", selector: ":not(th)[scope]" }, { code: "a11y-missing-attribute", title: "Required attributes missing.", description: "Some HTML elements require additional attributes for accessibility. For example, an `img` element requires an `alt` attribute, this attribute is used to describe the content of the image for screen readers.", message: (element) => { const requiredAttributes = a11y_required_attributes[element.localName]; const missingAttributes = requiredAttributes.filter( (attribute) => !element.hasAttribute(attribute) ); return `${element.localName} element is missing required attributes for accessibility: ${missingAttributes.join(", ")} `; }, selector: Object.keys(a11y_required_attributes).join(","), match(element) { const requiredAttributes = a11y_required_attributes[element.localName]; if (!requiredAttributes) return true; for (const attribute of requiredAttributes) { if (!element.hasAttribute(attribute)) return true; } return false; } }, { code: "a11y-missing-content", title: "Missing content", message: "Headings and anchors must have an accessible name, which can come from: inner text, aria-label, aria-labelledby, an img with alt property, or an svg with a tag .", selector: a11y_required_content.join(","), match(element) { const innerText = element.innerText?.trim(); if (innerText && innerText !== "") return false; const ariaLabel = element.getAttribute("aria-label")?.trim(); if (ariaLabel && ariaLabel !== "") return false; const ariaLabelledby = element.getAttribute("aria-labelledby")?.trim(); if (ariaLabelledby) { const ids = ariaLabelledby.split(" "); for (const id of ids) { const referencedElement = document.getElementById(id); if (referencedElement && referencedElement.innerText.trim() !== "") return false; } } const imgElements = element.querySelectorAll("img"); for (const img of imgElements) { const altAttribute = img.getAttribute("alt"); if (altAttribute && altAttribute.trim() !== "") return false; } const svgElements = element.querySelectorAll("svg"); for (const svg of svgElements) { const titleText = svg.querySelector("title"); if (titleText && titleText.textContent && titleText.textContent.trim() !== "") return false; } const inputElements = element.querySelectorAll("input"); for (const input of inputElements) { if (input.type === "image") { const altAttribute = input.getAttribute("alt"); if (altAttribute && altAttribute.trim() !== "") return false; } const inputAriaLabel = input.getAttribute("aria-label")?.trim(); if (inputAriaLabel && inputAriaLabel !== "") return false; const inputAriaLabelledby = input.getAttribute("aria-labelledby")?.trim(); if (inputAriaLabelledby) { const ids = inputAriaLabelledby.split(" "); for (const id of ids) { const referencedElement = document.getElementById(id); if (referencedElement && referencedElement.innerText.trim() !== "") return false; } } const title = input.getAttribute("title")?.trim(); if (title && title !== "") return false; } return true; } }, { code: "a11y-no-redundant-roles", title: "HTML element has redundant ARIA roles", message: "Giving these elements an ARIA role that is already set by the browser has no effect and is redundant.", selector: [...a11y_implicit_semantics.keys()].join(","), match(element) { const role = element.getAttribute("role"); if (element.localName === "input") { const type = element.getAttribute("type"); if (!type) return true; const implicitRoleForType = input_type_to_implicit_role.get(type); if (!implicitRoleForType) return true; if (role === implicitRoleForType) return false; } const implicitRole = a11y_implicit_semantics.get(element.localName); if (!implicitRole) return true; if (role === implicitRole) return false; } }, { code: "a11y-no-interactive-element-to-noninteractive-role", title: "Non-interactive ARIA role used on interactive HTML element.", message: "Interactive HTML elements like `` and ` `; } connectedCallback() { if (this.clickAction) { this.shadowRoot.getElementById("astro-overlay-card")?.addEventListener("click", this.clickAction); } } }; // node_modules/astro/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-list-window.js function createRoundedBadge(icon2) { const badge = document.createElement("astro-dev-toolbar-badge"); badge.shadowRoot.innerHTML += ` `; badge.innerHTML = `0`; return { badge, updateCount: (count) => { if (count === 0) { badge.badgeStyle = "green"; } else { badge.badgeStyle = "purple"; } badge.innerHTML = `${count}`; } }; } var DevToolbarAuditListWindow = class extends HTMLElement { _audits = []; shadowRoot; badges = {}; get audits() { return this._audits; } set audits(value) { this._audits = value; this.render(); } constructor() { super(); this.shadowRoot = this.attachShadow({ mode: "open" }); this.shadowRoot.innerHTML = `

Audit


`; const auditCounts = this.shadowRoot.getElementById("audit-counts"); if (auditCounts) { rulesCategories.forEach((category) => { const headerEntryContainer = document.createElement("div"); const auditCount = this.audits.filter( (audit) => getAuditCategory(audit.rule) === category.code ).length; const categoryBadge = createRoundedBadge(category.icon); categoryBadge.updateCount(auditCount); headerEntryContainer.append(categoryBadge.badge); auditCounts.append(headerEntryContainer); this.badges[category.code] = categoryBadge; }); } const backToListButton = this.shadowRoot.getElementById("back-to-list"); if (backToListButton) { backToListButton.addEventListener("click", () => { const activeAudit = this.shadowRoot.querySelector( "astro-dev-toolbar-audit-list-item[active]" ); if (activeAudit) { activeAudit.toggleAttribute("active", false); } }); } } connectedCallback() { this.render(); } updateAuditList() { const auditListContainer = this.shadowRoot.getElementById("audit-list"); if (auditListContainer) { auditListContainer.innerHTML = ""; if (this.audits.length > 0) { for (const category of rulesCategories) { const template = this.shadowRoot.getElementById( "category-template" ); if (!template) return; const clone = document.importNode(template.content, true); const categoryContainer = clone.querySelector("div"); const categoryHeader = clone.querySelector(".category-header"); categoryHeader.innerHTML = `

${category.name}

`; categoryContainer.append(categoryHeader); const categoryContent = clone.querySelector(".category-content"); const categoryAudits = this.audits.filter( (audit) => getAuditCategory(audit.rule) === category.code ); for (const audit of categoryAudits) { if (audit.card) categoryContent.append(audit.card); } categoryContainer.append(categoryContent); auditListContainer.append(categoryContainer); } } else { const noAuditContainer = document.createElement("div"); noAuditContainer.classList.add("no-audit-container"); noAuditContainer.innerHTML = `

No accessibility or performance issues detected.

Nice work! This app scans the page and highlights common accessibility and performance issues for you, like a missing "alt" attribute on an image, or an image not using performant attributes.

`; auditListContainer.append(noAuditContainer); } } } updateBadgeCounts() { for (const category of rulesCategories) { const auditCount = this.audits.filter( (audit) => getAuditCategory(audit.rule) === category.code ).length; this.badges[category.code].updateCount(auditCount); } } render() { this.updateAuditList(); this.updateBadgeCounts(); } }; // node_modules/astro/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-ui.js function truncate(val, maxLength) { return val.length > maxLength ? val.slice(0, maxLength - 1) + "…" : val; } function createAuditUI(audit, audits) { const rect = audit.auditedElement.getBoundingClientRect(); const highlight = createHighlight(rect, "warning", { "data-audit-code": audit.rule.code }); const resolvedAuditRule = resolveAuditRule(audit.rule, audit.auditedElement); const tooltip = buildAuditTooltip(resolvedAuditRule, audit.auditedElement); const card = buildAuditCard(resolvedAuditRule, highlight, audit.auditedElement, audits); ["focus", "mouseover"].forEach((event) => { const attribute = event === "focus" ? "active" : "hovered"; highlight.addEventListener(event, () => { if (event === "focus") { audits.forEach((adt) => { if (adt.card) adt.card.toggleAttribute("active", false); }); if (!card.isManualFocus) card.scrollIntoView(); card.toggleAttribute("active", true); } else { card.toggleAttribute(attribute, true); } }); }); highlight.addEventListener("mouseout", () => { card.toggleAttribute("hovered", false); }); const { isFixed } = getElementsPositionInDocument(audit.auditedElement); if (isFixed) { tooltip.style.position = highlight.style.position = "fixed"; } attachTooltipToHighlight(highlight, tooltip, audit.auditedElement); return { highlight, card }; } function buildAuditTooltip(rule, element) { const tooltip = document.createElement("astro-dev-toolbar-tooltip"); const { title, message } = rule; tooltip.sections = [ { icon: "warning", title: escape(title) }, { content: escape(message) } ]; const { file: elementFile, location: elementPosition } = getAnnotationsForElement(element) ?? {}; if (elementFile) { const elementFileWithPosition = elementFile + (elementPosition ? ":" + elementPosition : ""); tooltip.sections.push({ content: elementFileWithPosition.slice( window.__astro_dev_toolbar__.root.length - 1 // We want to keep the final slash, so minus one. ), clickDescription: "Click to go to file", async clickAction() { await fetch("/__open-in-editor?file=" + encodeURIComponent(elementFileWithPosition)); } }); } return tooltip; } function buildAuditCard(rule, highlightElement, auditedElement, audits) { const card = document.createElement( "astro-dev-toolbar-audit-list-item" ); card.clickAction = () => { if (card.hasAttribute("active")) return; audits.forEach((audit) => { audit.card?.toggleAttribute("active", false); }); highlightElement.scrollIntoView(); card.isManualFocus = true; highlightElement.focus(); card.isManualFocus = false; }; const selectorTitleContainer = document.createElement("section"); selectorTitleContainer.classList.add("selector-title-container"); const selector = document.createElement("span"); const selectorName = truncate(auditedElement.tagName.toLowerCase(), 8); selector.classList.add("audit-selector"); selector.innerHTML = escape(selectorName); const title = document.createElement("h3"); title.classList.add("audit-title"); title.innerText = rule.title; selectorTitleContainer.append(selector, title); card.append(selectorTitleContainer); const extendedInfo = document.createElement("div"); extendedInfo.classList.add("extended-info"); const selectorButton = document.createElement("button"); selectorButton.className = "audit-selector reset-button"; selectorButton.innerHTML = `${selectorName} `; selectorButton.addEventListener("click", () => { highlightElement.scrollIntoView(); highlightElement.focus(); }); extendedInfo.append(title.cloneNode(true)); extendedInfo.append(selectorButton); extendedInfo.append(document.createElement("hr")); const message = document.createElement("p"); message.classList.add("audit-message"); message.innerHTML = simpleRenderMarkdown(rule.message); extendedInfo.appendChild(message); const description = rule.description; if (description) { const descriptionElement = document.createElement("p"); descriptionElement.classList.add("audit-description"); descriptionElement.innerHTML = simpleRenderMarkdown(description); extendedInfo.appendChild(descriptionElement); } card.shadowRoot.appendChild(extendedInfo); return card; } var linkRegex = /\[([^[]+)\]\((.*)\)/g; var boldRegex = /\*\*(.+)\*\*/g; var codeRegex = /`([^`]+)`/g; function simpleRenderMarkdown(markdown) { return escape(markdown).replace(linkRegex, `
$1`).replace(boldRegex, "$1").replace(codeRegex, "$1"); } // node_modules/astro/dist/runtime/client/dev-toolbar/apps/audit/index.js var icon = ''; try { customElements.define("astro-dev-toolbar-audit-window", DevToolbarAuditListWindow); customElements.define("astro-dev-toolbar-audit-list-item", DevToolbarAuditListItem); } catch { } var showState = false; var audit_default = { id: "astro:audit", name: "Audit", icon, async init(canvas, eventTarget) { let audits = []; let auditWindow = document.createElement( "astro-dev-toolbar-audit-window" ); let hasCreatedUI = false; auditWindow.popover = ""; canvas.appendChild(auditWindow); await run(); let mutationDebounce; const observer = new MutationObserver(() => { if (mutationDebounce) { clearTimeout(mutationDebounce); } mutationDebounce = setTimeout(() => { settings.logger.verboseLog("Rerunning audit lints because the DOM has been updated."); if ("requestIdleCallback" in window) { window.requestIdleCallback( async () => { run().then(() => { if (showState) createAuditsUI(); }); }, { timeout: 300 } ); } else { setTimeout(async () => { run().then(() => { if (showState) createAuditsUI(); }); }, 150); } }, 250); }); setupObserver(); document.addEventListener("astro:before-preparation", () => { observer.disconnect(); }); document.addEventListener("astro:after-swap", async () => { run(); }); document.addEventListener("astro:page-load", async () => { refreshLintPositions(); setTimeout(() => { setupObserver(); }, 100); }); eventTarget.addEventListener("app-toggled", (event) => { if (event.detail.state === true) { showState = true; createAuditsUI(); } else { showState = false; } }); closeOnOutsideClick(eventTarget, () => { const activeAudits = audits.filter((audit) => audit.card?.hasAttribute("active")); if (activeAudits.length > 0) { activeAudits.forEach((audit) => { audit.card?.toggleAttribute("active", false); }); return true; } return false; }); async function createAuditsUI() { if (hasCreatedUI) return; const fragment = document.createDocumentFragment(); for (const audit of audits) { const { card, highlight } = createAuditUI(audit, audits); audit.card = card; audit.highlight = highlight; fragment.appendChild(highlight); } auditWindow.audits = audits; canvas.appendChild(fragment); hasCreatedUI = true; } async function run() { processAnnotations(); await lint(); } async function lint() { if (audits.length > 0) { audits.forEach((audit) => { audit.highlight?.remove(); audit.card?.remove(); }); audits = []; hasCreatedUI = false; } const selectorCache = /* @__PURE__ */ new Map(); for (const ruleCategory of rulesCategories) { for (const rule of ruleCategory.rules) { const elements = selectorCache.get(rule.selector) ?? document.querySelectorAll(rule.selector); let matches = []; if (typeof rule.match === "undefined") { matches = Array.from(elements); } else { for (const element of elements) { try { if (await rule.match(element)) { matches.push(element); } } catch (e) { settings.logger.error(`Error while running audit's match function: ${e}`); } } } for (const element of matches) { if (audits.some((audit) => audit.auditedElement === element)) continue; await createAuditProblem(rule, element); } } } eventTarget.dispatchEvent( new CustomEvent("toggle-notification", { detail: { state: audits.length > 0 } }) ); } async function createAuditProblem(rule, originalElement) { const computedStyle = window.getComputedStyle(originalElement); const targetedElement = originalElement.children[0] || originalElement; if (targetedElement.offsetParent === null || computedStyle.display === "none") { return; } if (originalElement.nodeName === "IMG" && !originalElement.complete) { return; } audits.push({ auditedElement: originalElement, rule, card: null, highlight: null }); } function refreshLintPositions() { audits.forEach(({ highlight, auditedElement }) => { const rect = auditedElement.getBoundingClientRect(); if (highlight) positionHighlight(highlight, rect); }); } ["scroll", "resize"].forEach((event) => { window.addEventListener(event, refreshLintPositions); }); function setupObserver() { observer.observe(document.body, { childList: true, subtree: true }); } } }; export { audit_default as default }; //# sourceMappingURL=audit-KGECZA7G.js.map