/** * ATT Consent — TCF v2.2 Bridge * * Used when cmp_mode = 'tcf_bridge'. A separate, IAB TCF v2.2 certified CMP * (e.g. Google's "Privacy & messaging" / Funding Choices, Tarteaucitron.js) * is expected to render the banner and emit gtag('consent', ...) calls. * * This script: * 1. Waits for window.__tcfapi to appear. * 2. Subscribes to the TCF event stream. * 3. Maps TCF v2.2 purposes back to this plugin's three categories * (functional / analytics / marketing). * 4. Calls AttConsent.update() so custom scripts, the att_cc_consent * cookie cache and the WP Consent API bridge all stay in sync. * 5. Wires the floating widget click to the CMP's "re-open" entry point. * * If __tcfapi never appears (CMP not installed, or visitor is outside the * TCF jurisdiction and the CMP loaded no stub) the plugin's default-denied * state simply persists — no fail-open here. * * TCF purpose → category mapping: * 1, 5, 6 → functional (device storage, personalised content) * 7, 8, 9, 10 → analytics (ad/content measurement, market research, product dev) * 2, 3, 4 → marketing (basic ads, ad profiling, personalised ads) */ (function () { 'use strict'; if (typeof window.AttConsent === 'undefined') { return; } var MAX_WAIT_MS = 10000; var POLL_INTERVAL = 100; var elapsed = 0; var lastSignature = ''; function mapTcDataToCategories(tcData) { // Outside the TCF scope (typically non-EEA/UK/CH): the CMP isn't // asserting any restriction, so treat as full consent. The site // operator remains responsible for any other applicable law (GPP/CCPA). if (tcData && tcData.gdprApplies === false) { return { functional: true, analytics: true, marketing: true }; } var purposes = (tcData && tcData.purpose && tcData.purpose.consents) || {}; return { functional: !!(purposes[1] || purposes[5] || purposes[6]), analytics: !!(purposes[7] || purposes[8] || purposes[9] || purposes[10]), marketing: !!(purposes[2] || purposes[3] || purposes[4]) }; } function handleTcData(tcData, success) { if (!success || !tcData) { return; } // Only act on terminal states. if (tcData.eventStatus !== 'tcloaded' && tcData.eventStatus !== 'useractioncomplete') { return; } var cats = mapTcDataToCategories(tcData); // Suppress repeat updates with identical state — TCF can fire // multiple times per page (e.g. tcloaded then useractioncomplete). var sig = cats.functional + '|' + cats.analytics + '|' + cats.marketing; if (sig === lastSignature) { return; } lastSignature = sig; try { window.AttConsent.update(cats); } catch (e) { // Swallow — don't break the page if consent-manager errored. } } function attach() { try { window.__tcfapi('addEventListener', 2, handleTcData); } catch (e) { // Bad CMP stub. Nothing else to do. } } function poll() { if (typeof window.__tcfapi === 'function') { attach(); return; } elapsed += POLL_INTERVAL; if (elapsed >= MAX_WAIT_MS) { // __tcfapi never appeared: visitor is outside TCF jurisdiction (e.g. Americas) // or the CMP script was blocked. GDPR opt-in doesn't apply; grant full consent // so these visitors aren't silently tracked as denied. try { window.AttConsent.update({ functional: true, analytics: true, marketing: true }); } catch (e) {} return; } setTimeout(poll, POLL_INTERVAL); } // --- Floating widget: re-open the external CMP UI --- var widget = document.getElementById('att-cc-widget'); if (widget) { widget.addEventListener('click', function (e) { e.preventDefault(); // 1. Google Funding Choices: use its own re-prompt API. // FC accepts but silently ignores the IAB 'displayConsentUi' // command, so we must try the FC path FIRST when FC is detected. if (window.googlefc && typeof window.googlefc.showRevocationMessage === 'function') { try { window.googlefc.showRevocationMessage(); return; } catch (err) {} } if (window.googlefc && window.googlefc.callbackQueue) { window.googlefc.callbackQueue.push({ CONSENT_DATA_READY: function () { if (typeof window.googlefc.showRevocationMessage === 'function') { window.googlefc.showRevocationMessage(); } } }); return; } // 2. Generic IAB TCF v2.2 re-prompt — for non-FC CMPs that // actually implement this (Sourcepoint, Didomi, etc.). if (typeof window.__tcfapi === 'function') { try { window.__tcfapi('displayConsentUi', 2, function () {}); return; } catch (err) {} } // 3. Last-ditch: scroll to a #privacy / #cookies anchor if the // site has one in its footer. Fail silently otherwise. var fallback = document.querySelector('a[href*="#cookie"], a[href*="#privacy"]'); if (fallback) { fallback.click(); } }); } poll(); })();