1(function () { 2 "use strict"; 3 4 if (typeof window.gjciConfig === "undefined") { 5 return; 6 } 7 8 var config = window.gjciConfig; 9 var cookieName = config.cookieName || "gjci_consent_preferences"; 10 var cookieDays = parseInt(config.cookieDays || 180, 10); 11 var tagId = (config.googleTagId || "").trim(); 12 var clarityId = (config.clarityProjectId || "").trim(); 13 var dismissKey = "gjci_banner_dismissed"; 14 var root = document.getElementById("gjci-root"); 15 var banner = document.getElementById("gjci-banner"); 16 var panel = document.getElementById("gjci-panel"); 17 var badge = document.getElementById("gjci-badge"); 18 var bannerClose = root.querySelector('[data-gjci-action="dismiss-banner"]'); 19 var analyticsToggle = document.getElementById("gjci-analytics-toggle"); 20 var preferencesOpen = false; 21 var bannerDismissed = readDismissedState(); 22 23 if (!root || !banner || !panel || !badge) { 24 return; 25 } 26 27 function parseConsent() { 28 var match = document.cookie.match(new RegExp("(?:^|; )" + cookieName.replace(/([.$?*|{}()[\]\\/+^])/g, "\\$1") + "=([^;]*)")); 29 if (!match) { 30 return null; 31 } 32 33 try { 34 return JSON.parse(decodeURIComponent(match[1])); 35 } catch (error) { 36 return null; 37 } 38 } 39 40 function normalizeConsent(consent) { 41 if (!consent || typeof consent !== "object") { 42 return null; 43 } 44 45 return { 46 necessary: true, 47 analytics: !!consent.analytics, 48 updatedAt: consent.updatedAt || new Date().toISOString(), 49 }; 50 } 51 52 function saveConsent(consent) { 53 var expires = new Date(); 54 expires.setTime(expires.getTime() + cookieDays * 24 * 60 * 60 * 1000); 55 document.cookie = 56 cookieName + 57 "=" + 58 encodeURIComponent(JSON.stringify(consent)) + 59 "; expires=" + 60 expires.toUTCString() + 61 "; path=/; SameSite=Lax"; 62 } 63 64 function readDismissedState() { 65 try { 66 return window.localStorage.getItem(dismissKey) === "1"; 67 } catch (error) { 68 return false; 69 } 70 } 71 72 function writeDismissedState(value) { 73 try { 74 if (value) { 75 window.localStorage.setItem(dismissKey, "1"); 76 } else { 77 window.localStorage.removeItem(dismissKey); 78 } 79 } catch (error) { 80 // Ignore storage failures and keep banner behavior functional. 81 } 82 } 83 84 function ensureGtagStub() { 85 window.dataLayer = window.dataLayer || []; 86 if (typeof window.gtag !== "function") { 87 window.gtag = function () { 88 window.dataLayer.push(arguments); 89 }; 90 } 91 } 92 93 function syncToggles(consent) { 94 var state = normalizeConsent(consent) || { 95 analytics: false, 96 }; 97 98 if (analyticsToggle) { 99 analyticsToggle.checked = !!state.analytics; 100 } 101 } 102 103 function setUIState(consent) { 104 var normalized = normalizeConsent(consent); 105 var hasConsent = !!normalized; 106 var showBadge = !preferencesOpen && (bannerDismissed || (hasConsent && !normalized.analytics)); 107 108 banner.hidden = hasConsent || preferencesOpen || bannerDismissed; 109 panel.hidden = !preferencesOpen; 110 badge.hidden = !showBadge; 111 112 if (document.activeElement && root.contains(document.activeElement)) { 113 var active = document.activeElement; 114 if (active.hidden || active.offsetParent === null) { 115 active.blur(); 116 } 117 } 118 } 119 120 function applyGoogleConsent(consent) { 121 if (!tagId) { 122 return; 123 } 124 125 ensureGtagStub(); 126 127 var normalized = normalizeConsent(consent) || { 128 analytics: false, 129 }; 130 131 window.gtag("consent", "update", { 132 analytics_storage: normalized.analytics ? "granted" : "denied", 133 ad_storage: "denied", 134 ad_user_data: "denied", 135 ad_personalization: "denied", 136 functionality_storage: "granted", 137 security_storage: "granted", 138 }); 139 140 if (normalized.analytics && !window.gjciGoogleConfigured) { 141 window.gtag("config", tagId, { 142 anonymize_ip: true, 143 }); 144 window.gjciGoogleConfigured = true; 145 } 146 } 147 148 function applyClarity(consent) { 149 var normalized = normalizeConsent(consent); 150 if (!clarityId || !normalized || !normalized.analytics || window.gjciClarityLoaded) { 151 return; 152 } 153 154 (function (c, l, a, r, i, t, y) { 155 c[a] = 156 c[a] || 157 function () { 158 (c[a].q = c[a].q || []).push(arguments); 159 }; 160 t = l.createElement(r); 161 t.async = 1; 162 t.src = "https://www.clarity.ms/tag/" + i; 163 y = l.getElementsByTagName(r)[0]; 164 y.parentNode.insertBefore(t, y); 165 }
165)(window, document, "clarity", "script", clarityId); 166 167 window.gjciClarityLoaded = true; 168 } 169 170 function applyConsent(consent) { 171 var normalized = normalizeConsent(consent); 172 syncToggles(normalized); 173 setUIState(normalized); 174 applyGoogleConsent(normalized); 175 applyClarity(normalized); 176 } 177 178 function persistAndApply(consent) { 179 var normalized = normalizeConsent(consent); 180 if (!normalized) { 181 return; 182 } 183 184 normalized.updatedAt = new Date().toISOString(); 185 bannerDismissed = false; 186 writeDismissedState(false); 187 saveConsent(normalized); 188 preferencesOpen = false; 189 applyConsent(normalized); 190 } 191 192 function openPreferences() { 193 preferencesOpen = true; 194 syncToggles(parseConsent()); 195 setUIState(parseConsent()); 196 } 197 198 function closePreferences() { 199 preferencesOpen = false; 200 setUIState(parseConsent()); 201 } 202 203 function dismissBanner() { 204 if (normalizeConsent(parseConsent())) { 205 return; 206 } 207 208 bannerDismissed = true; 209 writeDismissedState(true); 210 preferencesOpen = false; 211 setUIState(null); 212 } 213 214 function collectPreferenceConsent() { 215 return { 216 necessary: true, 217 analytics: !!(analyticsToggle && analyticsToggle.checked), 218 }; 219 } 220 221 root.addEventListener("click", function (event) { 222 var trigger = event.target.closest("[data-gjci-action]"); 223 if (!trigger) { 224 return; 225 } 226 227 event.preventDefault(); 228 trigger.blur(); 229 230 var action = trigger.getAttribute("data-gjci-action"); 231 232 if (action === "accept") { 233 persistAndApply({ 234 necessary: true, 235 analytics: true, 236 }); 237 return; 238 } 239 240 if (action === "reject") { 241 persistAndApply({ 242 necessary: true, 243 analytics: false, 244 }); 245 return; 246 } 247 248 if (action === "open-preferences") { 249 openPreferences(); 250 return; 251 } 252 253 if (action === "close-preferences") { 254 closePreferences(); 255 return; 256 } 257 258 if (action === "dismiss-banner") { 259 dismissBanner(); 260 return; 261 } 262 263 if (action === "save-preferences") { 264 persistAndApply(collectPreferenceConsent()); 265 } 266 }); 267 268 if (bannerClose) { 269 bannerClose.addEventListener("click", function (event) { 270 event.preventDefault(); 271 event.stopPropagation();
272 dismissBanner(); 273 }); 274 } 275 276 var consent = normalizeConsent(parseConsent()); 277 if (consent) { 278 applyConsent(consent); 279 } else { 280 syncToggles(null); 281 setUIState(null); 282 } 283})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.