1(function () { 2 const CONSENT_KEY = "cubbes_consent_v1"; 3 const ANALYTICS_KEYS = [ 4 "cubbes_session_id", 5 "cubbes_session_started_at", 6 "cubbes_visitor_id", 7 "cubbes_last_visit_at", 8 ]; 9 10 let consent = readConsent(); 11 let modal; 12 let settingsButton; 13 let analyticsToggle; 14 let settingsPanel; 15 let previouslyFocused; 16 17 function readConsent() { 18 try { 19 const value = JSON.parse(localStorage.getItem(CONSENT_KEY)); 20 return value && value.version === 1 && typeof value.analytics === "boolean" 21 ? value 22 : null; 23 } catch (_) { 24 return null; 25 } 26 } 27 28 function clearAnalyticsStorage() { 29 ANALYTICS_KEYS.forEach((key) => { 30 try { 31 localStorage.removeItem(key); 32 sessionStorage.removeItem(key); 33 } catch (_) { 34 // Storage can be unavailable in strict browser privacy modes. 35 } 36 }); 37 } 38 39 function saveConsent(analytics) { 40 consent = { 41 version: 1, 42 essential: true, 43 analytics: Boolean(analytics), 44 updatedAt: new Date().toISOString(), 45 }; 46 47 try { 48 localStorage.setItem(CONSENT_KEY, JSON.stringify(consent)); 49 } catch (_) { 50 // The choice still applies for the current page if storage is unavailable. 51 } 52 53 if (!consent.analytics) { 54 clearAnalyticsStorage(); 55 } 56 57 window.dispatchEvent( 58 new CustomEvent("cubbes:consent-changed", { 59 detail: { analytics: consent.analytics }, 60 }), 61 ); 62 closeModal(); 63 } 64 65 function showMainPanel() { 66 settingsPanel.hidden = true; 67 modal.querySelector("[data-consent-main]").hidden = false; 68 } 69 70 function showSettingsPanel() { 71 modal.querySelector("[data-consent-main]").hidden = true; 72 settingsPanel.hidden = false; 73 analyticsToggle.checked = Boolean(consent?.analytics); 74 analyticsToggle.focus(); 75 } 76 77 function openModal() { 78 previouslyFocused = document.activeElement; 79 showMainPanel(); 80 modal.hidden = false; 81 settingsButton.hidden = true; 82 document.body.classList.add("cookie-consent-open"); 83 modal.querySelector(".cookie-consent__close").focus(); 84 } 85 86 function closeModal() { 87 if (!modal) return; 88 modal.hidden = true; 89 settingsButton.hidden = false; 90 document.body.classList.remove("cookie-consent-open"); 91 if (previouslyFocused && typeof previouslyFocused.focus === "function") { 92 previouslyFocused.focus(); 93 } 94 } 95 96 function closeWithNecessaryOnly() { 97 if (consent) { 98 closeModal(); 99 } else { 100 saveConsent(false); 101 } 102 } 103 104 function keepFocusInside(event) { 105 if (event.key === "Escape") { 106 closeWithNecessaryOnly(); 107 return; 108 } 109 110 if (event.key !== "Tab") return; 111 const focusable = Array.from( 112 modal.querySelectorAll( 113 'button:not([hidden]), input:not([disabled]), a[href]:not([hidden])', 114 ), 115 ).filter((element) => element.offsetParent !== null); 116 if (!focusable.length) return; 117 118 const first = focusable[0]; 119 const last = focusable[focusable.length - 1]; 120 if (event.shiftKey && document.activeElement === first) { 121 event.preventDefault(); 122 last.focus(); 123 } else if (!event.shiftKey && document.activeElement === last) { 124 event.preventDefault(); 125 first.focus(); 126 } 127 } 128 129 function render() { 130 document.body.insertAdjacentHTML( 131 "beforeend", 132 ` 133 <div class="cookie-consent" role="dialog" aria-modal="true" aria-label="Privacy choices" hidden> 134 <div class="cookie-consent__card"> 135 <div class="cookie-consent__header"> 136 <img class="cookie-consent__logo" src="/assets/logo.svg" alt="Cubbes" /> 137 <button class="cookie-consent__close" type="button" aria-label="Close privacy choices">×</button> 138 </div> 139 140 <div data-consent-main> 141 <h2 id="cookieConsentTitle">We value your privacy</h2> 142 <p> 143 We use necessary storage to remember your privacy choice. With your permission, 144 we also use analytics to understand how people use Cubbes and improve the website. 145 Read our <a href="/privacy-policy">privacy policy</a>. 146 </p> 147 <div class="cookie-consent__actions"> 148 <button class="btn btn-outline-primary" type="button" data-consent-customise>Customise</button> 149 <button class="btn btn-outline-primary" type="button" data-consent-reject>Reject optional</button> 150 <button class="btn btn-primary" type="button" data-consent-accept>Accept all</button> 151 </div> 152 </div> 153 154 <div data-consent-settings hidden> 155 <h2>Privacy choices</h2> 156 <div class="cookie-consent__option"> 157 <div> 158 <strong>Necessary</strong>
159 <span>Remembers your privacy choice.</span> 160 </div> 161 <span class="cookie-consent__always-on">Always on</span> 162 </div> 163 <label class="cookie-consent__option" for="cookieAnalyticsConsent"> 164 <div> 165 <strong>Analytics</strong> 166 <span>Helps us understand visits and improve Cubbes.</span> 167 </div> 168 <input id="cookieAnalyticsConsent" type="checkbox" /> 169 </label> 170 <div class="cookie-consent__actions cookie-consent__actions--settings"> 171 <button class="btn btn-outline-primary" type="button" data-consent-back>Back</button> 172 <button class="btn btn-primary" type="button" data-consent-save>Save choices</button> 173 </div> 174 </div> 175 </div> 176 </div> 177 <button class="cookie-settings-button" type="button" hidden>Privacy choices</button> 178 `, 179 ); 180 181 modal = document.querySelector(".cookie-consent");
182 settingsButton = document.querySelector(".cookie-settings-button"); 183 analyticsToggle = document.getElementById("cookieAnalyticsConsent"); 184 settingsPanel = modal.querySelector("[data-consent-settings]"); 185 186 modal 187 .querySelector(".cookie-consent__close") 188 .addEventListener("click", closeWithNecessaryOnly); 189 modal 190 .querySelector("[data-consent-customise]") 191 .addEventListener("click", showSettingsPanel); 192 modal 193 .querySelector("[data-consent-reject]") 194 .addEventListener("click", () => saveConsent(false)); 195 modal 196 .querySelector("[data-consent-accept]") 197 .addEventListener("click", () => saveConsent(true)); 198 modal 199 .querySelector("[data-consent-back]") 200 .addEventListener("click", showMainPanel); 201 modal 202 .querySelector("[data-consent-save]") 203 .addEventListener("click", () => saveConsent(analyticsToggle.checked)); 204 modal.addEventListener("keydown", keepFocusInside); 205 settingsButton.addEventListener("click", openModal); 206 207 document.querySelectorAll("[data-cookie-settings]").forEach((button) => { 208 button.addEventListener("click", (event) => { 209 event.preventDefault(); 210 openModal(); 211 }); 212 }); 213 214 if (consent) { 215 settingsButton.hidden = false; 216 } else { 217 clearAnalyticsStorage(); 218 openModal(); 219 } 220 } 221 222 window.CubbesConsent = { 223 hasAnalyticsConsent: () => Boolean(consent?.analytics), 224 open: () => openModal(), 225 }; 226 227 if (document.readyState === "loading") { 228 document.addEventListener("DOMContentLoaded", render); 229 } else { 230 render(); 231 } 232})();
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.