/* Silktide Consent Manager - https://silktide.com/consent-manager/ Styles are at risked of being overridden by styles coming from the site the consent manager is used on. To help prevent this, global wrapper elements are prefixed with "#silktide-" */ /* -------------------------------- Global Styles - These elements exist in the main DOM and styling is limited to positioning and animation -------------------------------- */ /* Wrapper (Global) */ #silktide-wrapper { --focus: 0 0 0 2px #ffffff, 0 0 0 4px #000000, 0 0 0 6px #ffffff; --boxShadow: -5px 5px 10px 0px #00000012, 0px 0px 50px 0px #0000001a; --fontFamily: Helvetica Neue, Segoe UI, Arial, sans-serif; --primaryColor: #3B62E2; --backgroundColor: #FFFFFF; --textColor: #253B48; --backdropBackgroundColor: #00000033; --backdropBackgroundBlur: 0px; --cookieIconColor: #253B48; --cookieIconBackgroundColor: #FFFFFF; position: fixed; bottom: 0; right: 0; width: 100%; height: 100%; z-index: 99999; pointer-events: none; border: 0px; display: flex; justify-content: center; align-items: center } /* Backdrop (Global) */ #silktide-backdrop-global { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: auto; border: 0px; display: none; } /* -------------------------------- Links -------------------------------- */ #silktide-wrapper a { all: unset; display: inline-block; color: var(--primaryColor); text-decoration: underline; } #silktide-wrapper a:hover { cursor: pointer; color: var(--textColor); } /* -------------------------------- Focus Styles -------------------------------- */ #silktide-wrapper a:focus, #silktide-wrapper #silktide-banner button:focus, #silktide-wrapper #silktide-modal button:focus, #silktide-wrapper #silktide-cookie-icon:focus { outline: none; box-shadow: var(--focus); border-radius: 5px; } #silktide-wrapper #silktide-cookie-icon:focus { border-radius: 50%; } /* -------------------------------- General Styles -------------------------------- */ #silktide-wrapper .st-button { color: var(--backgroundColor); background-color: var(--primaryColor); border: 2px solid var(--primaryColor); padding: 10px 20px; text-decoration: none; text-align: center; display: inline-block; font-size: 16px; line-height: 24px; cursor: pointer; border-radius: 5px; } #silktide-wrapper .st-button--primary { } #silktide-wrapper .st-button--primary:hover { background-color: var(--backgroundColor); color: var(--primaryColor); } #silktide-wrapper .st-button--secondary { background-color: var(--backgroundColor); color: var(--primaryColor); } #silktide-wrapper .st-button--secondary:hover { background-color: var(--primaryColor); color: var(--backgroundColor); } /* -------------------------------- Banner -------------------------------- */ #silktide-banner { font-family: var(--fontFamily); color: var(--textColor); background-color: var(--backgroundColor); box-sizing: border-box; padding: 32px; border-radius: 5px; pointer-events: auto; border: 0px; position: fixed; bottom: 16px; right: 16px; width: 600px; overflow: auto; max-width: calc(100% - 32px); max-height: calc(100vh - 32px); transform: translate(0, -20px); opacity: 0; animation: silktide-slideInDown 350ms ease-out forwards; animation-delay: 0.3s; box-shadow: -5px 5px 10px 0px #00000012, 0px 0px 50px 0px #0000001a; } #silktide-banner:focus { border-radius: 50%; } #silktide-banner.center { top: 50%; left: 50%; bottom: auto; right: auto; position: fixed; transform: translate(-50%, calc(-50% - 20px)); animation: silktide-slideInDown-center 350ms ease-out forwards; } #silktide-banner.bottomLeft { bottom: 16px; left: 16px; position: fixed; } #silktide-banner.bottomCenter { bottom: 16px; left: 50%; position: fixed; transform: translate(-50%, -20px); animation: silktide-slideInDown-bottomCenter 350ms ease-out forwards; } #silktide-banner .preferences { display: flex; gap: 5px; border: none; padding: 15px 0px; background-color: transparent; color: var(--primaryColor); cursor: pointer; font-size: 16px; } #silktide-banner .preferences span { display: block; white-space: nowrap; text-decoration: underline; } #silktide-banner .preferences span:hover { color: var(--textColor); } #silktide-banner .preferences:after { display: block; content: '>'; text-decoration: none; } #silktide-banner p { font-size: 16px; line-height: 24px; margin: 0px 0px 15px; } #silktide-banner a { display: inline-block; color: var(--primaryColor); text-decoration: underline; background-color: var(--backgroundColor); } #silktide-banner a:hover { color: var(--textColor); } #silktide-banner a.silktide-logo { display: flex; align-items: center; justify-content: center; fill: var(--primaryColor); /* passed down to svg > path */ margin-left: auto; width: 48px; height: 48px; } #silktide-banner .actions { display: flex; gap: 16px; flex-direction: column; margin-top: 24px; } @media (min-width: 600px) { #silktide-banner .actions { flex-direction: row; align-items: center; } } #silktide-banner .actions-row { display: flex; gap: 16px; flex-direction: row; align-items: center; justify-content: space-between; flex-grow: 1; } /* -------------------------------- Modal -------------------------------- */ #silktide-modal { display: none; pointer-events: auto; overflow: auto; width: 800px; max-width: 100%; max-height: 100%; border: 0px; transform: translate(0px, -20px); opacity: 0; animation: silktide-slideInUp-center 350ms ease-out forwards; box-shadow: -5px 5px 10px 0px #00000012, 0px 0px 50px 0px #0000001a; font-family: var(--fontFamily); color: var(--textColor); flex-direction: column; padding: 30px; background-color: var(--backgroundColor); border-radius: 5px; box-sizing: border-box; } /* -------------------------------- Modal - Header -------------------------------- */ #silktide-modal header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; gap: 16px; } #silktide-modal h1 { font-family: var(--fontFamily); color: var(--textColor); font-size: 24px; font-weight: 500; margin: 0px; } #silktide-modal .modal-close { display: inline-flex; border: none; padding: 13px; border: 0px; cursor: pointer; background: var(--backgroundColor); color: var(--primaryColor); } #silktide-modal .modal-close svg { fill: var(--primaryColor); } /* -------------------------------- Modal - Content -------------------------------- */ #silktide-modal section { flex: 1; margin-top: 32px; } #silktide-modal section::-webkit-scrollbar { display: block; /* Force scrollbars to show */ width: 5px; /* Width of the scrollbar */ } #silktide-modal section::-webkit-scrollbar-thumb { background-color: var(--textColor); /* Color of the scrollbar thumb */ border-radius: 10px; /* Rounded corners for the thumb */ } #silktide-modal p { font-size: 16px; line-height: 24px; color: var(--textColor); margin: 0px 0px 15px; } #silktide-modal p:last-of-type { margin: 0px; } #silktide-modal fieldset { padding: 0px; border: none; margin: 0px 0px 32px; } #silktide-modal fieldset:last-of-type { margin: 0px; } #silktide-modal legend { padding: 0px; margin: 0px 0px 10px; font-weight: 700; color: var(--textColor); font-size: 16px; } #silktide-modal .cookie-type-content { display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; } /* -------------------------------- Modal - Switches -------------------------------- */ #silktide-modal .switch { flex-shrink: 0; position: relative; display: inline-block; height: 34px; width: 74px; cursor: pointer; } #silktide-modal .switch:focus-within { outline: none; box-shadow: var(--focus); border-radius: 25px; } #silktide-modal .switch input { opacity: 0; position: absolute; } /* Unchecked Switch Styles */ #silktide-modal .switch__pill { position: relative; display: block; height: 34px; width: 74px; background: var(--textColor); border-radius: 25px; } #silktide-modal .switch__dot { position: absolute; top: 2px; left: 2px; display: block; height: 30px; width: 30px; background: var(--backgroundColor); border-radius: 50%; transition: left 150ms ease-out; } #silktide-modal .switch__off, #silktide-modal .switch__on { text-transform: uppercase; font-size: 15px; font-weight: 500; color: var(--backgroundColor); position: absolute; top: 7px; right: 8px; transition: right 150ms ease-out, opacity 150ms ease-out; } #silktide-modal .switch__off { opacity: 1; } #silktide-modal .switch__on { opacity: 0; } /* Checked Switch Styles */ #silktide-modal .switch input:checked + .switch__pill { background: var(--primaryColor); } #silktide-modal .switch input:checked ~ .switch__dot { left: calc(100% - 32px); } #silktide-modal .switch input:checked ~ .switch__off { right: calc(100% - 32px); opacity: 0; } #silktide-modal .switch input:checked ~ .switch__on { right: calc(100% - 34px); opacity: 1; } /* Disabled Switch Styles */ #silktide-modal .switch input:disabled + .switch__pill { opacity: 0.65; cursor: not-allowed; } /* -------------------------------- Modal - Footer -------------------------------- */ #silktide-modal footer { display: flex; flex-direction: column; gap: 16px; margin-top: 24px; } @media (min-width: 600px) { #silktide-modal footer { flex-direction: row; align-items: center; } } #silktide-modal footer a { margin-left: auto; padding: 14px 0px; } /* Cookie Icon */ #silktide-cookie-icon { display: none; position: fixed; bottom: 10px; left: 10px; justify-content: center; align-items: center; width: 60px; height: 60px; border-radius: 50%; padding: 0px; border: none; background-color: var(--cookieIconColor); cursor: pointer; box-shadow: 0px 0px 6px 0px #0000001a; pointer-events: auto; animation: silktide-fadeIn 0.3s ease-in-out forwards; } #silktide-cookie-icon.bottomRight { left: auto; right: 10px; } #silktide-cookie-icon svg { fill: var(--cookieIconBackgroundColor); } /* -------------------------------- Backdrop -------------------------------- */ #silktide-backdrop { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: var(--backdropBackgroundColor); backdrop-filter: blur(var(--backdropBackgroundBlur)); pointer-events: all; } /* -------------------------------- Animations -------------------------------- */ @keyframes silktide-fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes silktide-slideInDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } @keyframes silktide-slideInDown-center { from { opacity: 0; transform: translate(-50%, calc(-50% - 20px)); } to { opacity: 1; transform: translate(-50%, -50%); } } @keyframes silktide-slideInDown-bottomCenter { from { opacity: 0; transform: translate(-50%, -20px); } to { opacity: 1; transform: translate(-50%, 0); } } @keyframes silktide-slideInUp-center { from { opacity: 0; transform: translate(0px, 20px); } to { opacity: 1; transform: translate(0px, 0px); } } // Silktide Consent Manager - https://silktide.com/consent-manager/ class SilktideCookieBanner { constructor(config) { this.config = config; // Save config to the instance this.wrapper = null; this.banner = null; this.modal = null; this.cookieIcon = null; this.backdrop = null; this.createWrapper(); if (this.shouldShowBackdrop()) { this.createBackdrop(); } this.createCookieIcon(); this.createModal(); if (this.shouldShowBanner()) { this.createBanner(); this.showBackdrop(); } else { this.showCookieIcon(); } this.setupEventListeners(); if (this.hasSetInitialCookieChoices()) { this.loadRequiredCookies(); this.runAcceptedCookieCallbacks(); } } destroyCookieBanner() { // Remove all cookie banner elements from the DOM if (this.wrapper && this.wrapper.parentNode) { this.wrapper.parentNode.removeChild(this.wrapper); } // Restore scrolling this.allowBodyScroll(); // Clear all references this.wrapper = null; this.banner = null; this.modal = null; this.cookieIcon = null; this.backdrop = null; } // ---------------------------------------------------------------- // Wrapper // ---------------------------------------------------------------- createWrapper() { this.wrapper = document.createElement('div'); this.wrapper.id = 'silktide-wrapper'; document.body.insertBefore(this.wrapper, document.body.firstChild); } // ---------------------------------------------------------------- // Wrapper Child Generator // ---------------------------------------------------------------- createWrapperChild(htmlContent, id) { // Create child element const child = document.createElement('div'); child.id = id; child.innerHTML = htmlContent; // Ensure wrapper exists if (!this.wrapper || !document.body.contains(this.wrapper)) { this.createWrapper(); } // Append child to wrapper this.wrapper.appendChild(child); return child; } // ---------------------------------------------------------------- // Backdrop // ---------------------------------------------------------------- createBackdrop() { this.backdrop = this.createWrapperChild(null, 'silktide-backdrop'); } showBackdrop() { if (this.backdrop) { this.backdrop.style.display = 'block'; } // Trigger optional onBackdropOpen callback if (typeof this.config.onBackdropOpen === 'function') { this.config.onBackdropOpen(); } } hideBackdrop() { if (this.backdrop) { this.backdrop.style.display = 'none'; } // Trigger optional onBackdropClose callback if (typeof this.config.onBackdropClose === 'function') { this.config.onBackdropClose(); } } shouldShowBackdrop() { return this.config?.background?.showBackground || false; } // update the checkboxes in the modal with the values from localStorage updateCheckboxState(saveToStorage = false) { const preferencesSection = this.modal.querySelector('#cookie-preferences'); const checkboxes = preferencesSection.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach((checkbox) => { const [, cookieId] = checkbox.id.split('cookies-'); const cookieType = this.config.cookieTypes.find(type => type.id === cookieId); if (!cookieType) return; if (saveToStorage) { // Save the current state to localStorage and run callbacks const currentState = checkbox.checked; if (cookieType.required) { localStorage.setItem( `silktideCookieChoice_${cookieId}${this.getBannerSuffix()}`, 'true' ); } else { localStorage.setItem( `silktideCookieChoice_${cookieId}${this.getBannerSuffix()}`, currentState.toString() ); // Run appropriate callback if (currentState && typeof cookieType.onAccept === 'function') { cookieType.onAccept(); } else if (!currentState && typeof cookieType.onReject === 'function') { cookieType.onReject(); } } } else { // When reading values (opening modal) if (cookieType.required) { checkbox.checked = true; checkbox.disabled = true; } else { const storedValue = localStorage.getItem( `silktideCookieChoice_${cookieId}${this.getBannerSuffix()}` ); if (storedValue !== null) { checkbox.checked = storedValue === 'true'; } else { checkbox.checked = !!cookieType.defaultValue; } } } }); } setInitialCookieChoiceMade() { window.localStorage.setItem(`silktideCookieBanner_InitialChoice${this.getBannerSuffix()}`, 1); } // ---------------------------------------------------------------- // Consent Handling // ---------------------------------------------------------------- handleCookieChoice(accepted) { // We set that an initial choice was made regardless of what it was so we don't show the banner again this.setInitialCookieChoiceMade(); this.removeBanner(); this.hideBackdrop(); this.toggleModal(false); this.showCookieIcon(); this.config.cookieTypes.forEach((type) => { // Set localStorage and run accept/reject callbacks if (type.required == true) { localStorage.setItem(`silktideCookieChoice_${type.id}${this.getBannerSuffix()}`, 'true'); if (typeof type.onAccept === 'function') { type.onAccept() } } else { localStorage.setItem( `silktideCookieChoice_${type.id}${this.getBannerSuffix()}`, accepted.toString(), ); if (accepted) { if (typeof type.onAccept === 'function') { type.onAccept(); } } else { if (typeof type.onReject === 'function') { type.onReject(); } } } }); // Trigger optional onAcceptAll/onRejectAll callbacks if (accepted && typeof this.config.onAcceptAll === 'function') { if (typeof this.config.onAcceptAll === 'function') { this.config.onAcceptAll(); } } else if (typeof this.config.onRejectAll === 'function') { if (typeof this.config.onRejectAll === 'function') { this.config.onRejectAll(); } } // finally update the checkboxes in the modal with the values from localStorage this.updateCheckboxState(); } getAcceptedCookies() { return (this.config.cookieTypes || []).reduce((acc, cookieType) => { acc[cookieType.id] = localStorage.getItem(`silktideCookieChoice_${cookieType.id}${this.getBannerSuffix()}`) === 'true'; return acc; }, {}); } runAcceptedCookieCallbacks() { if (!this.config.cookieTypes) return; const acceptedCookies = this.getAcceptedCookies(); this.config.cookieTypes.forEach((type) => { if (type.required) return; // we run required cookies separately in loadRequiredCookies if (acceptedCookies[type.id] && typeof type.onAccept === 'function') { if (typeof type.onAccept === 'function') { type.onAccept(); } } }); } runRejectedCookieCallbacks() { if (!this.config.cookieTypes) return; const rejectedCookies = this.getRejectedCookies(); this.config.cookieTypes.forEach((type) => { if (rejectedCookies[type.id] && typeof type.onReject === 'function') { if (typeof type.onReject === 'function') { type.onReject(); } } }); } /** * Run through all of the cookie callbacks based on the current localStorage values */ runStoredCookiePreferenceCallbacks() { this.config.cookieTypes.forEach((type) => { const accepted = localStorage.getItem(`silktideCookieChoice_${type.id}${this.getBannerSuffix()}`) === 'true'; // Set localStorage and run accept/reject callbacks if (accepted) { if (typeof type.onAccept === 'function') { type.onAccept(); } } else { if (typeof type.onReject === 'function') { type.onReject(); } } }); } loadRequiredCookies() { if (!this.config.cookieTypes) return; this.config.cookieTypes.forEach((cookie) => { if (cookie.required && typeof cookie.onAccept === 'function') { if (typeof cookie.onAccept === 'function') { cookie.onAccept(); } } }); } // ---------------------------------------------------------------- // Banner // ---------------------------------------------------------------- getBannerContent() { const bannerDescription = this.config.text?.banner?.description || "

We use cookies on our site to enhance your user experience, provide personalized content, and analyze our traffic.

"; // Accept button const acceptAllButtonText = this.config.text?.banner?.acceptAllButtonText || 'Accept all'; const acceptAllButtonLabel = this.config.text?.banner?.acceptAllButtonAccessibleLabel; const acceptAllButton = ``; // Reject button const rejectNonEssentialButtonText = this.config.text?.banner?.rejectNonEssentialButtonText || 'Reject non-essential'; const rejectNonEssentialButtonLabel = this.config.text?.banner?.rejectNonEssentialButtonAccessibleLabel; const rejectNonEssentialButton = ``; // Preferences button const preferencesButtonText = this.config.text?.banner?.preferencesButtonText || 'Preferences'; const preferencesButtonLabel = this.config.text?.banner?.preferencesButtonAccessibleLabel; const preferencesButton = ``; // Silktide logo link const silktideLogo = ` `; const bannerContent = ` ${bannerDescription}
${acceptAllButton} ${rejectNonEssentialButton}
${preferencesButton} ${silktideLogo}
`; return bannerContent; } hasSetInitialCookieChoices() { return !!localStorage.getItem(`silktideCookieBanner_InitialChoice${this.getBannerSuffix()}`); } createBanner() { // Create banner element this.banner = this.createWrapperChild(this.getBannerContent(), 'silktide-banner'); // Add positioning class from config if (this.banner && this.config.position?.banner) { this.banner.classList.add(this.config.position.banner); } // Trigger optional onBannerOpen callback if (this.banner && typeof this.config.onBannerOpen === 'function') { this.config.onBannerOpen(); } } removeBanner() { if (this.banner && this.banner.parentNode) { this.banner.parentNode.removeChild(this.banner); this.banner = null; // Trigger optional onBannerClose callback if (typeof this.config.onBannerClose === 'function') { this.config.onBannerClose(); } } } shouldShowBanner() { if (this.config.showBanner === false) { return false; } return ( localStorage.getItem(`silktideCookieBanner_InitialChoice${this.getBannerSuffix()}`) === null ); } // ---------------------------------------------------------------- // Modal // ---------------------------------------------------------------- getModalContent() { const preferencesTitle = this.config.text?.preferences?.title || 'Customize your cookie preferences'; const preferencesDescription = this.config.text?.preferences?.description || "

We respect your right to privacy. You can choose not to allow some types of cookies. Your cookie preferences will apply across our website.

"; // Preferences button const preferencesButtonLabel = this.config.text?.banner?.preferencesButtonAccessibleLabel; const closeModalButton = ``; const cookieTypes = this.config.cookieTypes || []; const acceptedCookieMap = this.getAcceptedCookies(); // Accept button const acceptAllButtonText = this.config.text?.banner?.acceptAllButtonText || 'Accept all'; const acceptAllButtonLabel = this.config.text?.banner?.acceptAllButtonAccessibleLabel; const acceptAllButton = ``; // Reject button const rejectNonEssentialButtonText = this.config.text?.banner?.rejectNonEssentialButtonText || 'Reject non-essential'; const rejectNonEssentialButtonLabel = this.config.text?.banner?.rejectNonEssentialButtonAccessibleLabel; const rejectNonEssentialButton = ``; // Credit link const creditLinkText = this.config.text?.preferences?.creditLinkText || 'Get this banner for free'; const creditLinkAccessibleLabel = this.config.text?.preferences?.creditLinkAccessibleLabel; const creditLink = `${creditLinkText}`; const modalContent = `

${preferencesTitle}

${closeModalButton}
${preferencesDescription} `; return modalContent; } createModal() { // Create banner element this.modal = this.createWrapperChild(this.getModalContent(), 'silktide-modal'); } toggleModal(show) { if (!this.modal) return; this.modal.style.display = show ? 'flex' : 'none'; if (show) { this.showBackdrop(); this.hideCookieIcon(); this.removeBanner(); this.preventBodyScroll(); // Focus the close button const modalCloseButton = this.modal.querySelector('.modal-close'); modalCloseButton.focus(); // Trigger optional onPreferencesOpen callback if (typeof this.config.onPreferencesOpen === 'function') { this.config.onPreferencesOpen(); } this.updateCheckboxState(false); // read from storage when opening } else { // Set that an initial choice was made when closing the modal this.setInitialCookieChoiceMade(); // Save current checkbox states to storage this.updateCheckboxState(true); this.hideBackdrop(); this.showCookieIcon(); this.allowBodyScroll(); // Trigger optional onPreferencesClose callback if (typeof this.config.onPreferencesClose === 'function') { this.config.onPreferencesClose(); } } } // ---------------------------------------------------------------- // Cookie Icon // ---------------------------------------------------------------- getCookieIconContent() { return ` `; } createCookieIcon() { this.cookieIcon = document.createElement('button'); this.cookieIcon.id = 'silktide-cookie-icon'; this.cookieIcon.title = 'Manage your cookie preferences for this site'; this.cookieIcon.innerHTML = this.getCookieIconContent(); if (this.config.text?.banner?.preferencesButtonAccessibleLabel) { this.cookieIcon.ariaLabel = this.config.text?.banner?.preferencesButtonAccessibleLabel; } // Ensure wrapper exists if (!this.wrapper || !document.body.contains(this.wrapper)) { this.createWrapper(); } // Append child to wrapper this.wrapper.appendChild(this.cookieIcon); // Add positioning class from config if (this.cookieIcon && this.config.cookieIcon?.position) { this.cookieIcon.classList.add(this.config.cookieIcon.position); } // Add color scheme class from config if (this.cookieIcon && this.config.cookieIcon?.colorScheme) { this.cookieIcon.classList.add(this.config.cookieIcon.colorScheme); } } showCookieIcon() { if (this.cookieIcon) { this.cookieIcon.style.display = 'flex'; } } hideCookieIcon() { if (this.cookieIcon) { this.cookieIcon.style.display = 'none'; } } /** * This runs if the user closes the modal without making a choice for the first time * We apply the default values and the necessary values as default */ handleClosedWithNoChoice() { this.config.cookieTypes.forEach((type) => { let accepted = true; // Set localStorage and run accept/reject callbacks if (type.required == true || type.defaultValue) { localStorage.setItem( `silktideCookieChoice_${type.id}${this.getBannerSuffix()}`, accepted.toString(), ); } else { accepted = false; localStorage.setItem( `silktideCookieChoice_${type.id}${this.getBannerSuffix()}`, accepted.toString(), ); } if (accepted) { if (typeof type.onAccept === 'function') { type.onAccept(); } } else { if (typeof type.onReject === 'function') { type.onReject(); } } // set the flag to say that the cookie choice has been made this.setInitialCookieChoiceMade(); this.updateCheckboxState(); }); } // ---------------------------------------------------------------- // Focusable Elements // ---------------------------------------------------------------- getFocusableElements(element) { return element.querySelectorAll( 'button, a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])', ); } // ---------------------------------------------------------------- // Event Listeners // ---------------------------------------------------------------- setupEventListeners() { // Check Banner exists before trying to add event listeners if (this.banner) { // Get the buttons const acceptButton = this.banner.querySelector('.accept-all'); const rejectButton = this.banner.querySelector('.reject-all'); const preferencesButton = this.banner.querySelector('.preferences'); // Add event listeners to the buttons acceptButton?.addEventListener('click', () => this.handleCookieChoice(true)); rejectButton?.addEventListener('click', () => this.handleCookieChoice(false)); preferencesButton?.addEventListener('click', () => { this.showBackdrop(); this.toggleModal(true); }); // Focus Trap const focusableElements = this.getFocusableElements(this.banner); const firstFocusableEl = focusableElements[0]; const lastFocusableEl = focusableElements[focusableElements.length - 1]; // Add keydown event listener to handle tab navigation this.banner.addEventListener('keydown', (e) => { if (e.key === 'Tab') { if (e.shiftKey) { if (document.activeElement === firstFocusableEl) { lastFocusableEl.focus(); e.preventDefault(); } } else { if (document.activeElement === lastFocusableEl) { firstFocusableEl.focus(); e.preventDefault(); } } } }); // Set initial focus if (this.config.mode !== 'wizard') { acceptButton?.focus(); } } // Check Modal exists before trying to add event listeners if (this.modal) { const closeButton = this.modal.querySelector('.modal-close'); const acceptAllButton = this.modal.querySelector('.preferences-accept-all'); const rejectAllButton = this.modal.querySelector('.preferences-reject-all'); closeButton?.addEventListener('click', () => { this.toggleModal(false); const hasMadeFirstChoice = this.hasSetInitialCookieChoices(); if (hasMadeFirstChoice) { // run through the callbacks based on the current localStorage state this.runStoredCookiePreferenceCallbacks(); } else { // handle the case where the user closes without making a choice for the first time this.handleClosedWithNoChoice(); } }); acceptAllButton?.addEventListener('click', () => this.handleCookieChoice(true)); rejectAllButton?.addEventListener('click', () => this.handleCookieChoice(false)); // Banner Focus Trap const focusableElements = this.getFocusableElements(this.modal); const firstFocusableEl = focusableElements[0]; const lastFocusableEl = focusableElements[focusableElements.length - 1]; this.modal.addEventListener('keydown', (e) => { if (e.key === 'Tab') { if (e.shiftKey) { if (document.activeElement === firstFocusableEl) { lastFocusableEl.focus(); e.preventDefault(); } } else { if (document.activeElement === lastFocusableEl) { firstFocusableEl.focus(); e.preventDefault(); } } } if (e.key === 'Escape') { this.toggleModal(false); } }); closeButton?.focus(); // Update the checkbox event listeners const preferencesSection = this.modal.querySelector('#cookie-preferences'); const checkboxes = preferencesSection.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach(checkbox => { checkbox.addEventListener('change', (event) => { const [, cookieId] = event.target.id.split('cookies-'); const isAccepted = event.target.checked; const previousValue = localStorage.getItem( `silktideCookieChoice_${cookieId}${this.getBannerSuffix()}` ) === 'true'; // Only proceed if the value has actually changed if (isAccepted !== previousValue) { // Find the corresponding cookie type const cookieType = this.config.cookieTypes.find(type => type.id === cookieId); if (cookieType) { // Update localStorage localStorage.setItem( `silktideCookieChoice_${cookieId}${this.getBannerSuffix()}`, isAccepted.toString() ); // Run the appropriate callback only if the value changed if (isAccepted && typeof cookieType.onAccept === 'function') { cookieType.onAccept(); } else if (!isAccepted && typeof cookieType.onReject === 'function') { cookieType.onReject(); } } } }); }); } // Check Cookie Icon exists before trying to add event listeners if (this.cookieIcon) { this.cookieIcon.addEventListener('click', () => { // If modal is not found, create it if (!this.modal) { this.createModal(); this.toggleModal(true); this.hideCookieIcon(); } // If modal is hidden, show it else if (this.modal.style.display === 'none' || this.modal.style.display === '') { this.toggleModal(true); this.hideCookieIcon(); } // If modal is visible, hide it else { this.toggleModal(false); } }); } } getBannerSuffix() { if (this.config.bannerSuffix) { return '_' + this.config.bannerSuffix; } return ''; } preventBodyScroll() { document.body.style.overflow = 'hidden'; // Prevent iOS Safari scrolling document.body.style.position = 'fixed'; document.body.style.width = '100%'; } allowBodyScroll() { document.body.style.overflow = ''; document.body.style.position = ''; document.body.style.width = ''; } } (function () { window.silktideCookieBannerManager = {}; let config = {}; let cookieBanner; function updateCookieBannerConfig(userConfig = {}) { config = {...config, ...userConfig}; // If cookie banner exists, destroy and recreate it with new config if (cookieBanner) { cookieBanner.destroyCookieBanner(); // We'll need to add this method cookieBanner = null; } // Only initialize if document.body exists if (document.body) { initCookieBanner(); } else { // Wait for DOM to be ready document.addEventListener('DOMContentLoaded', initCookieBanner, {once: true}); } } function initCookieBanner() { if (!cookieBanner) { cookieBanner = new SilktideCookieBanner(config); // Pass config to the CookieBanner instance } } function injectScript(url, loadOption) { // Check if script with this URL already exists const existingScript = document.querySelector(`script[src="${url}"]`); if (existingScript) { return; // Script already exists, don't add it again } const script = document.createElement('script'); script.src = url; // Apply the async or defer attribute based on the loadOption parameter if (loadOption === 'async') { script.async = true; } else if (loadOption === 'defer') { script.defer = true; } document.head.appendChild(script); } window.silktideCookieBannerManager.initCookieBanner = initCookieBanner; window.silktideCookieBannerManager.updateCookieBannerConfig = updateCookieBannerConfig; window.silktideCookieBannerManager.injectScript = injectScript; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initCookieBanner, {once: true}); } else { initCookieBanner(); } })();