1import { Controller } from "@hotwired/stimulus" 2 3// Cookie consent controller for GDPR compliance 4// Shows banner on first visit, blocks analytics until consent given 5export default class extends Controller { 6 static targets = ["banner"] 7 8 connect() { 9 // Check if user has already made a choice 10 const consent = this.getConsent() 11 12 if (consent === null) { 13 // First visit - show banner 14 this.showBanner() 15 } else if (consent === true) { 16 // Previously accepted - initialize analytics immediately 17 this.initializeAnalytics() 18 } 19 // If consent === false, do nothing (respect their decline) 20 } 21 22 accept() { 23 this.setConsent(true) 24 this.hideBanner() 25 this.initializeAnalytics() 26 } 27 28 decline() { 29 this.setConsent(false) 30 this.hideBanner() 31 } 32 33 showBanner() { 34 if (this.hasBannerTarget) { 35 this.bannerTarget.classList.remove("hidden") 36 } 37 } 38 39 hideBanner() { 40 if (this.hasBannerTarget) { 41 this.bannerTarget.classList.add("hidden") 42 } 43 } 44 45 getConsent() { 46 // Check both localStorage and cookie 47 const localStorageConsent = localStorage.getItem("cookie_consent") 48 const cookieConsent = this.getCookie("cookie_consent") 49 50 if (localStorageConsent !== null) { 51 return localStorageConsent === "true" 52 } 53 if (cookieConsent !== null) { 54 return cookieConsent === "true" 55 } 56 return null 57 } 58 59 setConsent(accepted) { 60 const value = accepted.toString() 61 62 // Store in localStorage (faster, more reliable) 63 localStorage.setItem("cookie_consent", value) 64 65 // Also store in cookie (ironically) for cross-device/session persistence 66 // Expires in 1 year 67 const expiryDate = new Date() 68 expiryDate.setFullYear(expiryDate.getFullYear() + 1) 69 document.cookie = `cookie_consent=${value}; expires=${expiryDate.toUTCString()}; path=/; SameSite=Lax` 70 } 71 72 getCookie(name) { 73 const value = `; ${document.cookie}` 74 const parts = value.split(`; ${name}=`) 75 if (parts.length === 2) { 76 const cookieValue = parts.pop().split(';').shift() 77 return cookieValue 78 } 79 return null 80 } 81 82 initializeAnalytics() { 83 // Dispatch custom event that analytics initialization will listen for 84 const event = new CustomEvent('cookie-consent-accepted', { 85 detail: { timestamp: new Date() } 86 }) 87 window.dispatchEvent(event) 88 89 // Also set a global flag for synchronous checks 90 window.cookieConsentGiven = true 91 } 92}
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.