/** * ATT Consent Manager * * Handles Google Consent Mode v2 updates, session attribution replay, * custom script injection, and consent state persistence. * * Phase 1 (consent defaults + attribution capture) runs inline in . * This file handles Phase 2 (consent updates) and Phase 3 (basic mode tag loading). */ var AttConsent = (function() { 'use strict'; var gtag = window.attCCGtag; var config = window.attCCConfig || {}; /** * Update consent state. * * @param {Object} categories - { functional: bool, analytics: bool, marketing: bool } */ function update(categories) { // Step 1: Replay stored attribution BEFORE consent update. // This ensures GA4 session_start gets correct source/medium. if (!sessionStorage.getItem('att_cc_consent_given')) { replayAttribution(); sessionStorage.setItem('att_cc_consent_given', '1'); } // Step 2: Send consent update to Google. gtag('consent', 'update', { ad_storage: categories.marketing ? 'granted' : 'denied', analytics_storage: categories.analytics ? 'granted' : 'denied', ad_user_data: categories.marketing ? 'granted' : 'denied', ad_personalization: categories.marketing ? 'granted' : 'denied', functionality_storage: categories.functional ? 'granted' : 'denied', personalization_storage: categories.functional ? 'granted' : 'denied' }); // Step 3: Store consent in first-party cookie. setConsentCookie(categories); // Step 4: In basic mode, inject tracking script if analytics or marketing consented. if (config.consent_mode === 'basic' && (categories.analytics || categories.marketing)) { injectTrackingScript(); } // Step 5: Execute custom scripts for consented categories. executeConsentedScripts(categories); // Step 6: Fire custom event for theme/plugin integration. try { document.dispatchEvent(new CustomEvent('att_consent_update', { detail: categories })); } catch (e) { // IE11 fallback (if ever needed). var evt = document.createEvent('CustomEvent'); evt.initCustomEvent('att_consent_update', true, true, categories); document.dispatchEvent(evt); } // Step 7: WP Consent API integration. if (typeof wp_set_consent === 'function') { wp_set_consent('functional', categories.functional ? 'allow' : 'deny'); wp_set_consent('statistics', categories.analytics ? 'allow' : 'deny'); wp_set_consent('marketing', categories.marketing ? 'allow' : 'deny'); } } /** * Replay stored attribution data via gtag('set'). * Called BEFORE the consent update so GA4 picks up the original * campaign parameters on the session_start event. */ function replayAttribution() { var stored = sessionStorage.getItem('att_cc_attr'); if (!stored) { return; } try { var a = JSON.parse(stored); var params = {}; if (a.us) { params.campaign_source = a.us; } if (a.um) { params.campaign_medium = a.um; } if (a.uc) { params.campaign_name = a.uc; } if (a.ut) { params.campaign_term = a.ut; } if (a.uo) { params.campaign_content = a.uo; } if (Object.keys(params).length > 0) { gtag('set', params); } } catch (e) { // Silent failure - don't block consent. } } /** * Store consent choices in a first-party cookie. * * @param {Object} categories Consent categories. */ function setConsentCookie(categories) { var days = config.consent_expiry || 365; var expires = new Date(Date.now() + days * 864e5).toUTCString(); var value = encodeURIComponent(JSON.stringify(categories)); var cookie = 'att_cc_consent=' + value + '; expires=' + expires + '; path=/; SameSite=Lax'; if (location.protocol === 'https:') { cookie += '; Secure'; } document.cookie = cookie; } /** * Inject the tracking script in basic mode (deferred until consent). */ function injectTrackingScript() { if (!config.tracking_snippet || window.attCCTrackingLoaded) { return; } window.attCCTrackingLoaded = true; injectHTML(config.tracking_snippet, document.head); } /** * Execute custom scripts for the consented categories. * Scans the DOM for inert type="text/plain" script tags and template tags * with matching data-att-cc-category attributes, then activates them. * * @param {Object} categories Consent categories. */ function executeConsentedScripts(categories) { var cats = Object.keys(categories).filter(function(cat) { return categories[cat]; }); if (!cats.length) return; var selectors = []; cats.forEach(function(cat) { selectors.push('script[type="text/plain"][data-att-cc-category="' + cat + '"]'); selectors.push('template[data-att-cc-category="' + cat + '"][data-att-cc-type="html"]'); }); var elements = document.querySelectorAll(selectors.join(',')); Array.prototype.slice.call(elements).forEach(function(el) { if (el.nodeName === 'SCRIPT') { activateScript(el); } else if (el.nodeName === 'TEMPLATE') { activateTemplate(el); } }); } /** * Activate an inert script element by creating a fresh