${closeModalButton}
${preferencesDescription}
${cookieTypes
.map((type) => {
const accepted = acceptedCookieMap[type.id];
let isChecked = false;
// if it's accepted then show as checked
if (accepted) {
isChecked = true;
}
// if nothing has been accepted / rejected yet, then show as checked if the default value is true
if (!accepted && !this.hasSetInitialCookieChoices()) {
isChecked = type.defaultValue;
}
return `
`;
})
.join("")}
`;
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.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) {
localStorage.setItem(
`silktideCookieChoice_${type.id}${this.getBannerSuffix()}`,
accepted.toString()
);
} else if (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();
}
})();
function deleteAllCookies(except = []) {
const cookies = document.cookie.split(";");
cookies.forEach((cookie) => {
const name = cookie.split("=")[0].trim();
if (except.includes(name)) return;
document.cookie = `${name}=; Max-Age=0; path=/`;
document.cookie = `${name}=; Max-Age=0; path=/; domain=${window.location.hostname}`;
});
}
silktideCookieBannerManager.updateCookieBannerConfig({
background: {
showBackground: true,
},
cookieIcon: {
position: "bottomLeft",
},
cookieTypes: [
{
id: "necessary",
name: "Necessary",
description:
"
These cookies are necessary for the website to function properly and cannot be switched off. They help with things like logging in and setting your privacy preferences.
",
required: true,
onAccept: function () {
console.log("Add logic for the required Necessary here");
},
},
{
id: "analytical",
name: "Analytical",
description:
"
These cookies help us improve the site by tracking which pages are most popular and how visitors move around the site.