(() => { 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) }); } }); })();