(function () { "use strict"; const MEASUREMENT_ID = "G-GLKY62YQC8"; const PRODUCTION_HOSTS = new Set([ "academicarcade.org", "www.academicarcade.org" ]); const IS_PRODUCTION = PRODUCTION_HOSTS.has( String(window.location.hostname || "").toLowerCase() ); const SAFE_QUERY_PARAMETERS = new Set(["grade", "subject", "type"]); const SENSITIVE_PARAMETER_NAME = /(?:^|_)(?:answer|auth|child|email|password|session|student|stripe|token)(?:_|$)/i; function sanitizeUrl(value) { try { const url = new URL(String(value), window.location.href); const safeParameters = new URLSearchParams(); url.searchParams.forEach(function (parameterValue, parameterName) { if (SAFE_QUERY_PARAMETERS.has(parameterName.toLowerCase())) { safeParameters.append(parameterName, parameterValue); } }); url.search = safeParameters.toString(); url.hash = ""; return url.toString(); } catch (error) { return ""; } } function sanitizeSearchTerm(value) { const term = String(value || "").trim().slice(0, 100); if (!term) return ""; if (/(?:@|https?:\/\/|\b(?:my\s+(?:child|son|daughter)|student|answer|password|token|session)\b)/i.test(term)) { return "[redacted]"; } return term .replace(/\b\+?\d[\d\s().-]{7,}\d\b/g, "[redacted]") .replace(/\b[A-Fa-f0-9]{24,}\b/g, "[redacted]"); } function sanitizeParameters(parameters) { const safeParameters = {}; if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) { return safeParameters; } Object.keys(parameters).forEach(function (key) { if (SENSITIVE_PARAMETER_NAME.test(key)) return; let value = parameters[key]; if (value === undefined || value === null || value === "") return; if (key === "search_term") { value = sanitizeSearchTerm(value); } else if (["file_url", "link_url", "page_location"].includes(key)) { value = sanitizeUrl(value); } if (["string", "number", "boolean"].includes(typeof value) && value !== "") { safeParameters[key] = value; } }); return safeParameters; } window.aaTrackEvent = function (eventName, parameters) { try { if (typeof eventName !== "string" || !/^[a-z][a-z0-9_]{0,39}$/.test(eventName)) { return false; } if (!IS_PRODUCTION || typeof window.gtag !== "function") { return false; } window.gtag("event", eventName, sanitizeParameters(parameters || {})); return true; } catch (error) { return false; } }; function elementUrl(element) { return element && element.href ? sanitizeUrl(element.href) : ""; } function gradeFromLink(element) { if (!element || !element.href) return ""; try { const pathname = new URL(element.href, window.location.href).pathname; const match = pathname.match(/\/grades\/(kg|grade([1-5]))\.html$/i); if (!match) return ""; return match[1].toLowerCase() === "kg" ? "kindergarten" : match[2]; } catch (error) { return ""; } } function parametersFromData(element) { const parameters = {}; Object.keys(element.dataset).forEach(function (key) { if (["gaEvent", "gaPageEvent", "gaSearch", "gaSearchInput"].includes(key)) return; if (!key.startsWith("ga")) return; const parameterName = key .slice(2) .replace(/^[A-Z]/, function (letter) { return letter.toLowerCase(); }) .replace(/[A-Z]/g, function (letter) { return "_" + letter.toLowerCase(); }); if (parameterName) parameters[parameterName] = element.dataset[key]; }); return parameters; } function trackSearch() { const activeGrade = document.querySelector(".filter-pill.active[data-grade]"); const activeSubject = document.querySelector(".filter-pill.active[data-subject]"); const activeType = document.querySelector(".filter-pill.active[data-type]"); const searchInput = document.querySelector("[data-ga-search-input]"); window.aaTrackEvent("worksheet_search", { search_term: searchInput ? searchInput.value : "", grade_filter: activeGrade && activeGrade.dataset.grade ? activeGrade.dataset.grade : "all", subject_filter: activeSubject && activeSubject.dataset.subject ? activeSubject.dataset.subject : "all", type_filter: activeType && activeType.dataset.type ? activeType.dataset.type : "all" }); } document.addEventListener("click", function (event) { try { const searchControl = event.target.closest("[data-ga-search], .filter-pill"); if (searchControl) trackSearch(); const trackedElement = event.target.closest("[data-ga-event]"); if (trackedElement) { const eventName = trackedElement.dataset.gaEvent; const parameters = parametersFromData(trackedElement); if (!parameters.link_url && trackedElement.matches("a[href]")) { parameters.link_url = elementUrl(trackedElement); } if (eventName === "grade_selected") { parameters.page_location = sanitizeUrl(window.location.href); } if (eventName === "worksheet_download" && !parameters.file_url) { parameters.file_url = elementUrl(trackedElement); } window.aaTrackEvent(eventName, parameters); return; } const gradeLink = event.target.closest("a[href]"); const grade = gradeFromLink(gradeLink); if (grade) { window.aaTrackEvent("grade_selected", { grade: grade, link_url: elementUrl(gradeLink), page_location: sanitizeUrl(window.location.href) }); } } catch (error) { // Analytics must never interfere with the underlying click action. } }); document.addEventListener("keydown", function (event) { if (event.key === "Enter" && event.target.matches("[data-ga-search-input]")) { trackSearch(); } }); document.addEventListener("DOMContentLoaded", function () { const pageEventElement = document.querySelector("[data-ga-page-event]"); if (pageEventElement) { window.aaTrackEvent( pageEventElement.dataset.gaPageEvent, parametersFromData(pageEventElement) ); } }); if (!IS_PRODUCTION) return; window.dataLayer = window.dataLayer || []; window.gtag = window.gtag || function () { window.dataLayer.push(arguments); }; window.gtag("js", new Date()); const config = { anonymize_ip: true, page_location: sanitizeUrl(window.location.href) }; if (document.referrer) { config.page_referrer = sanitizeUrl(document.referrer); } window.gtag("config", MEASUREMENT_ID, config); try { const script = document.createElement("script"); script.async = true; script.src = "https://www.googletagmanager.com/gtag/js?id=" + encodeURIComponent(MEASUREMENT_ID); script.onerror = function () { // Ad blockers and network failures should fail silently. }; document.head.appendChild(script); } catch (error) { // Loading analytics is optional and must never break the page. } })();