Spaces:
Runtime error
Runtime error
| (() => { | |
| if (globalThis.__darkPatternAuditorLoaded) { | |
| return; | |
| } | |
| globalThis.__darkPatternAuditorLoaded = true; | |
| const ATTRIBUTE = "data-dp-audit-id"; | |
| const HIGHLIGHT_CLASS = "dp-audit-highlight"; | |
| const STYLE_ID = "dp-audit-style"; | |
| const CANDIDATE_SELECTOR = [ | |
| "button", | |
| "a", | |
| "label", | |
| "input", | |
| "textarea", | |
| "select", | |
| "p", | |
| "h1", | |
| "h2", | |
| "h3", | |
| "h4", | |
| "h5", | |
| "h6", | |
| "[role='button']", | |
| "[aria-label]", | |
| ].join(","); | |
| function installStyles() { | |
| if (document.getElementById(STYLE_ID)) { | |
| return; | |
| } | |
| const style = document.createElement("style"); | |
| style.id = STYLE_ID; | |
| style.textContent = ` | |
| .${HIGHLIGHT_CLASS} { | |
| outline: 3px solid #f97316 !important; | |
| outline-offset: 3px !important; | |
| box-shadow: 0 0 0 6px rgba(249, 115, 22, 0.2) !important; | |
| } | |
| .${HIGHLIGHT_CLASS}[data-dp-audit-severity="critical"] { | |
| outline-color: #e11d48 !important; | |
| box-shadow: 0 0 0 6px rgba(225, 29, 72, 0.22) !important; | |
| } | |
| .${HIGHLIGHT_CLASS}[data-dp-audit-severity="medium"] { | |
| outline-color: #eab308 !important; | |
| box-shadow: 0 0 0 6px rgba(234, 179, 8, 0.2) !important; | |
| } | |
| .${HIGHLIGHT_CLASS}[data-dp-audit-severity="low"] { | |
| outline-color: #0ea5e9 !important; | |
| box-shadow: 0 0 0 6px rgba(14, 165, 233, 0.18) !important; | |
| } | |
| .dp-audit-focus { | |
| animation: dp-audit-pulse 0.8s ease-in-out 3 !important; | |
| } | |
| @keyframes dp-audit-pulse { | |
| 50% { filter: brightness(1.4); transform: scale(1.01); } | |
| } | |
| `; | |
| document.documentElement.appendChild(style); | |
| } | |
| function normalizeText(value) { | |
| return String(value || "").replace(/\s+/g, " ").trim(); | |
| } | |
| function isVisible(element) { | |
| const style = getComputedStyle(element); | |
| const rect = element.getBoundingClientRect(); | |
| return ( | |
| style.display !== "none" && | |
| style.visibility !== "hidden" && | |
| Number(style.opacity) > 0 && | |
| rect.width > 1 && | |
| rect.height > 1 | |
| ); | |
| } | |
| function extractText(element) { | |
| if (element instanceof HTMLInputElement) { | |
| if (["password", "email", "tel", "number"].includes(element.type)) { | |
| return ""; | |
| } | |
| return normalizeText( | |
| element.placeholder || element.getAttribute("aria-label"), | |
| ); | |
| } | |
| if (element instanceof HTMLTextAreaElement) { | |
| return normalizeText( | |
| element.placeholder || element.getAttribute("aria-label"), | |
| ); | |
| } | |
| if (element instanceof HTMLSelectElement) { | |
| return normalizeText(element.selectedOptions[0]?.textContent); | |
| } | |
| return normalizeText( | |
| element.getAttribute("aria-label") || element.textContent, | |
| ); | |
| } | |
| function collectVisibleText(maxItems) { | |
| const items = []; | |
| const seenText = new Set(); | |
| const elements = document.querySelectorAll(CANDIDATE_SELECTOR); | |
| let sequence = 0; | |
| for (const element of elements) { | |
| if (items.length >= maxItems) { | |
| break; | |
| } | |
| if (!(element instanceof HTMLElement) || !isVisible(element)) { | |
| continue; | |
| } | |
| const text = extractText(element); | |
| if (text.length < 3 || text.length > 500 || seenText.has(text)) { | |
| continue; | |
| } | |
| seenText.add(text); | |
| const elementId = | |
| element.getAttribute(ATTRIBUTE) || | |
| `dp-${Date.now().toString(36)}-${sequence++}`; | |
| element.setAttribute(ATTRIBUTE, elementId); | |
| items.push({ | |
| id: elementId, | |
| text, | |
| tag: element.tagName.toLowerCase(), | |
| }); | |
| } | |
| return items; | |
| } | |
| function clearResults() { | |
| document.querySelectorAll(`.${HIGHLIGHT_CLASS}`).forEach((element) => { | |
| element.classList.remove(HIGHLIGHT_CLASS, "dp-audit-focus"); | |
| element.removeAttribute("data-dp-audit-severity"); | |
| element.removeAttribute("data-dp-audit-original-title"); | |
| if (element.getAttribute("data-dp-audit-title-set") === "1") { | |
| element.removeAttribute("title"); | |
| element.removeAttribute("data-dp-audit-title-set"); | |
| } | |
| }); | |
| } | |
| function applyResults(results) { | |
| installStyles(); | |
| clearResults(); | |
| for (const result of results) { | |
| const element = document.querySelector(`[${ATTRIBUTE}="${result.id}"]`); | |
| if (!(element instanceof HTMLElement)) { | |
| continue; | |
| } | |
| element.classList.add(HIGHLIGHT_CLASS); | |
| element.setAttribute("data-dp-audit-severity", result.severity || "medium"); | |
| if (!element.hasAttribute("title")) { | |
| element.setAttribute( | |
| "title", | |
| `${result.prediction}: ${result.confidence}% calibrated confidence`, | |
| ); | |
| element.setAttribute("data-dp-audit-title-set", "1"); | |
| } | |
| } | |
| } | |
| function focusResult(elementId) { | |
| const element = document.querySelector(`[${ATTRIBUTE}="${elementId}"]`); | |
| if (!(element instanceof HTMLElement)) { | |
| return false; | |
| } | |
| element.scrollIntoView({ behavior: "smooth", block: "center" }); | |
| element.classList.remove("dp-audit-focus"); | |
| requestAnimationFrame(() => element.classList.add("dp-audit-focus")); | |
| return true; | |
| } | |
| chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { | |
| if (message?.type === "COLLECT_VISIBLE_TEXT") { | |
| installStyles(); | |
| sendResponse({ | |
| items: collectVisibleText(Math.min(Number(message.maxItems) || 250, 300)), | |
| }); | |
| return; | |
| } | |
| if (message?.type === "APPLY_AUDIT_RESULTS") { | |
| applyResults(message.results || []); | |
| sendResponse({ applied: true }); | |
| return; | |
| } | |
| if (message?.type === "CLEAR_AUDIT_RESULTS") { | |
| clearResults(); | |
| sendResponse({ cleared: true }); | |
| return; | |
| } | |
| if (message?.type === "FOCUS_AUDIT_RESULT") { | |
| sendResponse({ focused: focusResult(message.elementId) }); | |
| } | |
| }); | |
| })(); | |