(the site generator already does this on every page). * * Wires real Google Consent Mode v2: "Reject Non-Essential" actually denies * analytics_storage via gtag('consent','update', ...), it isn't cosmetic. * The gtag() default-deny call must run BEFORE gtag.js loads -- see the * inline snippet in each page's
, not in this file. */ (function () { 'use strict'; var STORAGE_KEY = 'pc_cookie_consent'; var EXPIRY_MS = 365 * 24 * 60 * 60 * 1000; // 12 months, per spec // ---- storage: localStorage primary, cookie fallback for e.g. strict // private-browsing modes that block localStorage but allow cookies. ---- function readStored() { try { var raw = localStorage.getItem(STORAGE_KEY); if (raw) return JSON.parse(raw); } catch (e) {} var m = document.cookie.match(/(?:^|; )pc_cookie_consent=([^;]*)/); if (m) { try { return JSON.parse(decodeURIComponent(m[1])); } catch (e) {} } return null; } function writeStored(obj) { var json = JSON.stringify(obj); try { localStorage.setItem(STORAGE_KEY, json); } catch (e) {} try { var expires = new Date(Date.now() + EXPIRY_MS).toUTCString(); document.cookie = 'pc_cookie_consent=' + encodeURIComponent(json) + '; expires=' + expires + '; path=/; SameSite=Lax'; } catch (e) {} } function isExpired(rec) { return !rec || !rec.ts || (Date.now() - rec.ts) > EXPIRY_MS; } // ---- apply a choice to Google Consent Mode ---- function applyConsent(analyticsGranted) { if (typeof window.gtag === 'function') { window.gtag('consent', 'update', { analytics_storage: analyticsGranted ? 'granted' : 'denied', ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied' }); } } function save(analyticsGranted) { writeStored({ analytics: !!analyticsGranted, ts: Date.now() }); applyConsent(analyticsGranted); } // ---- DOM construction ---- var root, banner, settingsPanel, analyticsToggle, lastFocused; function build() { root = document.getElementById('cc-root'); if (!root) { // page didn't include the mount point -- create one root = document.createElement('div'); root.id = 'cc-root'; document.body.appendChild(root); } banner = document.createElement('div'); banner.className = 'cc-banner'; banner.setAttribute('role', 'region'); banner.setAttribute('aria-label', 'Cookie consent'); banner.innerHTML = '
We use cookies to improve your experience, analyze traffic, and personalize content. You can accept or reject non-essential cookies. See our Privacy Policy.
' + ''; root.appendChild(banner); settingsPanel = banner.querySelector('#cc-settings'); analyticsToggle = banner.querySelector('#cc-analytics-toggle'); banner.querySelector('#cc-accept').addEventListener('click', function () { save(true); hide(); }); banner.querySelector('#cc-reject').addEventListener('click', function () { save(false); hide(); }); banner.querySelector('#cc-settings-toggle').addEventListener('click', function () { openSettings(); }); banner.querySelector('#cc-cancel').addEventListener('click', function () { closeSettings(); }); banner.querySelector('#cc-save').addEventListener('click', function () { save(analyticsToggle.checked); hide(); }); // Simple focus trap while the banner is open -- Tab cycles within it // rather than escaping to page content behind it. banner.addEventListener('keydown', function (e) { if (e.key !== 'Tab') return; var focusables = banner.querySelectorAll('button, a[href], input:not([disabled])'); if (!focusables.length) return; var first = focusables[0], last = focusables[focusables.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } }); } function privacyHref() { // Each page sets window.PC_ROOT (e.g. '.' or '..') before this script // loads. Do NOT infer depth from location.pathname -- that breaks under // file:// URLs, which have arbitrary path depth on the user's disk. var root = (typeof window.PC_ROOT === 'string') ? window.PC_ROOT : '.'; return root + '/privacy-policy.html'; } function openSettings() { var rec = readStored(); analyticsToggle.checked = rec ? !!rec.analytics : false; settingsPanel.classList.add('cc-open'); banner.querySelector('#cc-settings-toggle').setAttribute('aria-expanded', 'true'); analyticsToggle.focus(); } function closeSettings() { settingsPanel.classList.remove('cc-open'); banner.querySelector('#cc-settings-toggle').setAttribute('aria-expanded', 'false'); } function show(openToSettings) { if (!banner) build(); lastFocused = document.activeElement; banner.classList.add('cc-visible'); if (openToSettings) { openSettings(); } else { banner.querySelector('#cc-accept').focus(); } } function hide() { if (!banner) return; banner.classList.remove('cc-visible'); closeSettings(); if (lastFocused && typeof lastFocused.focus === 'function') lastFocused.focus(); } // ---- public: footer "Cookie Preferences" link calls this to reopen ---- window.RankNationsCookiePrefs = function () { show(true); }; // ---- init ---- function init() { var rec = readStored(); if (!rec || isExpired(rec)) { show(false); // first visit, or the 12-month window lapsed } else { applyConsent(rec.analytics); // re-apply their prior choice silently } var prefsLink = document.getElementById('cookiePrefsLink'); if (prefsLink) { prefsLink.addEventListener('click', function (e) { e.preventDefault(); window.RankNationsCookiePrefs(); }); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();