/** * Cookie Consent with Google Consent Mode v2 * * This script must be loaded BEFORE Google Analytics. * It implements GDPR-compliant consent management. * * The banner HTML is injected dynamically to avoid duplication across pages. */ (function() { 'use strict'; const CONSENT_KEY = 'kelly_green_cookie_consent'; const CONSENT_VERSION = '1'; // Increment if consent requirements change // Banner HTML template (injected dynamically like nav.js and footer.js) const BANNER_HTML = `
`; // Initialize Google Consent Mode v2 with default denied state // This MUST run before gtag.js loads window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } // Set default consent state (denied until user accepts) gtag('consent', 'default', { 'ad_storage': 'denied', 'ad_user_data': 'denied', 'ad_personalization': 'denied', 'analytics_storage': 'denied', 'wait_for_update': 500 // Wait up to 500ms for consent update }); // Check for existing consent function getStoredConsent() { try { const stored = localStorage.getItem(CONSENT_KEY); if (stored) { const consent = JSON.parse(stored); if (consent.version === CONSENT_VERSION) { return consent; } } } catch (e) { console.error('Error reading consent:', e); } return null; } // Store consent choice function storeConsent(accepted) { try { localStorage.setItem(CONSENT_KEY, JSON.stringify({ version: CONSENT_VERSION, accepted: accepted, timestamp: new Date().toISOString() })); } catch (e) { console.error('Error storing consent:', e); } } // Update Google Consent Mode function updateGoogleConsent(accepted) { const consentState = accepted ? 'granted' : 'denied'; gtag('consent', 'update', { 'ad_storage': consentState, 'ad_user_data': consentState, 'ad_personalization': consentState, 'analytics_storage': consentState }); } // Show the cookie banner function showBanner() { const banner = document.getElementById('cookie-consent-banner'); if (banner) { banner.classList.remove('hidden'); setTimeout(function() { banner.classList.add('opacity-100', 'translate-y-0'); banner.classList.remove('opacity-0', 'translate-y-4'); }, 10); } } // Hide the cookie banner function hideBanner() { const banner = document.getElementById('cookie-consent-banner'); if (banner) { banner.classList.add('opacity-0', 'translate-y-4'); banner.classList.remove('opacity-100', 'translate-y-0'); setTimeout(function() { banner.classList.add('hidden'); }, 300); } } // Handle user accepting cookies function acceptCookies() { storeConsent(true); updateGoogleConsent(true); hideBanner(); } // Handle user declining cookies function declineCookies() { storeConsent(false); updateGoogleConsent(false); hideBanner(); } // Inject banner and initialize consent function initConsent() { // Inject banner HTML into the page document.body.insertAdjacentHTML('beforeend', BANNER_HTML); const existingConsent = getStoredConsent(); if (existingConsent !== null) { // User has already made a choice updateGoogleConsent(existingConsent.accepted); } else { // No consent yet - show banner showBanner(); } // Set up button handlers document.getElementById('cookie-accept').addEventListener('click', acceptCookies); document.getElementById('cookie-decline').addEventListener('click', declineCookies); } // Initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initConsent); } else { initConsent(); } // Expose functions globally for settings page or debugging window.CookieConsent = { accept: acceptCookies, decline: declineCookies, reset: function() { localStorage.removeItem(CONSENT_KEY); location.reload(); } }; })();