const CC_MODAL_ID = 'cc-modal-cookies-banner'; const CC_PENDING_COOKIES_KEY = 'captainConsentPending'; const CC_COOKIE_MODAL = 'cc_consent'; const CC_SERVER_URL = 'https://cc-platform-api-prod.fly.dev'; const CC_STANDARD_MODE_ONLY_SETTINGS = 'ONLY_SETTINGS'; const CC_STANDARD_MODE_BANNER_LINEAL = 'BANNER_LINEAL'; const CC_MODES_ALLOWED = ['STRICTLY_NECESSARY_COOKIES', 'UNCLASSIFIED_COOKIES']; const CC_MODAL_ID_SETTINGS = 'cc-modal-cookies-banner-settings'; function toggleActions(shadowRoot) { const privacyLabels = shadowRoot.querySelectorAll('.privacy-label'); const toggleAccordion = (e) => { e.currentTarget.classList.toggle('active'); e.currentTarget.parentElement.nextElementSibling .querySelector('p') .classList.toggle('active'); if (e.currentTarget.parentElement.nextElementSibling.style.maxHeight) { e.currentTarget.parentElement.nextElementSibling.style.maxHeight = null; } else { e.currentTarget.parentElement.nextElementSibling.style.maxHeight = e.currentTarget.parentElement.nextElementSibling.scrollHeight + 'px'; } }; privacyLabels.forEach((label) => { label.addEventListener('click', toggleAccordion); }); } function setGTMDataLayer(event, data) { window.dataLayer = window.dataLayer || []; window.gtag = window.gtag || function () { dataLayer.push(arguments); }; window.dataLayer.push({ event, captainComplianceConsent: { ...data, }, }); gtag('consent', 'update', { analytics_storage: data.selectedCookies.PERFORMANCE_COOKIES ? 'granted' : 'denied', ad_storage: data.selectedCookies.TARGETING_COOKIES ? 'granted' : 'denied', ad_user_data: data.selectedCookies.TARGETING_COOKIES ? 'granted' : 'denied', ad_personalization: data.selectedCookies.TARGETING_COOKIES ? 'granted' : 'denied', functionality_storage: data.selectedCookies.FUNCTIONALITY_COOKIES ? 'granted' : 'denied', security_storage: data.selectedCookies.STRICTLY_NECESSARY_COOKIES ? 'granted' : 'denied', }); } function checkIfAllSwitchesAreOff(bannerDisplays) { if (!bannerDisplays?.length) return false; return (bannerDisplays || []).every(({ display }) => !display); } //transform: translate(50%, -50%); function getModalPosition(positionX, positionY, mode) { let top, bottom, left, right = 'unset'; const positionValue = mode === CC_STANDARD_MODE_BANNER_LINEAL ? '0px' : '10px'; if (positionY === 'top') { top = positionValue; } else if (positionY === 'bottom') { bottom = positionValue; } else if (positionY === 'middle') { top = '50%'; } if (positionX === 'left') { left = positionValue; } else if (positionX === 'right') { right = positionValue; } else if (positionX === 'middle') { left = '50%'; } return { top, bottom, left, right, }; } function getModalTranslate(positionX, positionY) { let vertical, horizontal = '0px'; if (positionY === 'middle') { vertical = '-50%'; } if (positionX === 'middle') { horizontal = '50%'; } return { vertical, horizontal, }; } function addStyleToShadowRoot(cssString, shadowRoot) { const template = document.createElement('template'); template.innerHTML = cssString.trim(); // Use a template to safely parse the input string const styleElement = template.content.querySelector('style'); if (styleElement) { shadowRoot.appendChild(styleElement); } else { console.warn( "Provided CSS string doesn't contain a valid `; addStyleToShadowRoot(modalNodeStyles, shadowRoot); addStyleToShadowRoot(cssSettings, shadowRoot); addStyleToShadowRoot(cssModal, shadowRoot); addStyleToShadowRoot(cssIcon, shadowRoot); } function getItemConfiguration(bannerDisplays, id) { const foundItem = (bannerDisplays || []).find( ({ cookieTypeId }) => cookieTypeId === id, ); return ( foundItem || { display: true, checked: false, } ); } function createSwitchList(switchData, bannerDisplays) { return switchData.reduce((accumulator, { key, name, id }) => { const foundItem = getItemConfiguration(bannerDisplays, id); if (key === 'UNCLASSIFIED_COOKIES' || !foundItem?.display) { return accumulator; } const strictlyNecessary = 'STRICTLY_NECESSARY_COOKIES' === key; const defaultValues = foundItem ? `${foundItem.checked ? 'checked' : ''} ${ strictlyNecessary ? 'disabled checked' : '' }` : ''; return ` ${accumulator}

${name.replace( 'cookies', '', )}

`; }, ''); } function generateHtmlModalNode() { const modalNode = document.createElement('div'); modalNode.classList.add('captain-compliance-modal-container'); const shadowRoot = modalNode.attachShadow({ mode: 'open' }); document.body.appendChild(modalNode); return shadowRoot; } function addModal( bannerConfiguration, switchData, bannerDisplays, report, html, htmlSettingsModal, htmlIcon, shadowRoot, ) { const hasImage = bannerConfiguration.displayLogo && !!bannerConfiguration.image; const shouldNotDisplayPartialButton = checkIfAllSwitchesAreOff(bannerDisplays); const switchList = createSwitchList(switchData, bannerDisplays); shadowRoot.innerHTML += htmlSettingsModal; shadowRoot.innerHTML += htmlIcon; if (html) { shadowRoot.innerHTML += html; } fillSwitchesFromPreferences(shadowRoot, switchData); const image = shadowRoot.querySelector( '#captain-compliance-modal-bn-body_content_icon_id', ); const switchWrapper = shadowRoot.querySelector( '#captain-compliance-modal-settings-body_content_switch_list_id', ); const cookieSettingsButton = shadowRoot.querySelector( '#cc-modal-cookie-settings', ); const modalTitle = shadowRoot.querySelector( '#captain-compliance-modal-bn-body_content_text_title_id', ); const modalDescription = shadowRoot.querySelector( '#captain-compliance-modal-bn-body_content_text_description_id', ); const modalTransparencyPage = shadowRoot.querySelector( '#captain-compliance-modal-bn-body_footer_transparency_id', ); if (modalTransparencyPage && report.active) { modalTransparencyPage.href = `https://${report.cname}.cookietransparency.com`; } else if (modalTransparencyPage) { modalTransparencyPage.remove(); } if (modalTitle && !!bannerConfiguration.title.length) { modalTitle.textContent = bannerConfiguration.title; } else if (modalTitle) { modalTitle.remove(); } if (modalDescription && !!bannerConfiguration.description.length) { modalDescription.textContent = bannerConfiguration.description; } else if (modalDescription) { modalDescription.remove(); } if (switchWrapper && !shouldNotDisplayPartialButton) { switchWrapper.innerHTML += switchList; } else if (switchWrapper && cookieSettingsButton) { switchWrapper.remove(); cookieSettingsButton.remove(); } if (image && hasImage) { image.src = bannerConfiguration.image; } else if (image) { image.remove(); } } async function loadBannerData() { const paramToken = "4c8cf23b-7642-456c-9ecb-e80f7451d52d"; const accessToken = paramToken || document.currentScript.getAttribute('access-token'); const response = await fetch( `${CC_SERVER_URL}/banner/banner-token?access-token=${accessToken}`, ); return await response.json(); } async function getModalStructure(mode) { const response = await fetch( `${CC_SERVER_URL}/bannerModeStyle/by-mode/${mode}`, ); return response ? await response.json() : null; } async function loadData(scannerId) { const response = await fetch( `${CC_SERVER_URL}/report/find-last-report?scannerId=${scannerId}`, ); return response ? await response.json() : null; } async function loadSwitchData() { const response = await fetch(`${CC_SERVER_URL}/cookies/cookie-types`); return await response.json(); } async function trackBannerLoad(bannerId) { const response = await fetch( `${CC_SERVER_URL}/banner/tracking?id=${bannerId}`, ); return await response.json(); } async function getCookiesFromServer() { const response = await fetch(`${CC_SERVER_URL}/banner/get-cookies`, { method: 'GET', credentials: 'include', // Ensure cookies are included in the request }); return await response.json(); } async function removeFromTheServer(cookieNames) { await fetch(`${CC_SERVER_URL}/banner/remove-cookies`, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ cookieNames, }), }); } async function updateStatus(bannerId, status) { await fetch(`${CC_SERVER_URL}/bannerTracking/banner/${bannerId}`, { method: 'PUT', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ status: status, }), }); } function mergeCookieList(cookieList) { const firstPartyValues = Object.keys(cookieList.firstParty); const dataToReturn = firstPartyValues .reduce((accumulator, key) => { if (CC_MODES_ALLOWED.includes(key)) return accumulator; return [ ...accumulator, ...cookieList.firstParty[key].data, ...cookieList.thirdParty[key].data, ]; }, []) .map(({ name, domain, path, regex }) => ({ name, domain, path, regex })); return dataToReturn; } function getCookieTypesToNotRemove(shadowRoot, switchData) { return switchData.reduce((accumulator, { key }) => { const checked = shadowRoot.getElementById(key)?.checked; if (checked || CC_MODES_ALLOWED.includes(key)) { return [...accumulator, key]; } return accumulator; }, []); } function removeKeyFromObject(cookieList, listToNotRemove) { const localCookieList = { ...cookieList }; listToNotRemove.forEach((key) => { delete localCookieList.firstParty[key]; delete localCookieList.thirdParty[key]; }); return localCookieList; } function addCookie(suffix, value) { var now = new Date(); var time = now.getTime(); var expireTime = time + 1000 * 60 * 60 * 24 * 30; now.setTime(expireTime); document.cookie = `${CC_COOKIE_MODAL}_${suffix}=${value}; Expires=` + now.toUTCString() + '; Path=/;'; } function closeModal(shadowRoot, scannerId) { shadowRoot.getElementById(CC_MODAL_ID).style.display = 'none'; addCookie(scannerId, 'ok'); } function addClickHandlerByClass(shadowRoot, className, cb) { const elements = shadowRoot.querySelectorAll(`.${className}`); elements.forEach((element) => { element.onclick = function () { cb(); }; }); } function handlePartialAllowedRemove(shadowRoot, switchData, cookieList) { const listToNotRemove = getCookieTypesToNotRemove(shadowRoot, switchData); const listUpdated = removeKeyFromObject(cookieList, listToNotRemove); reject(mergeCookieList(listUpdated)); } function handleDisplayModalSettings(modal, modalSettings) { return () => { modal.style.display = 'none'; modalSettings.style.visibility = 'visible'; }; } function fillSwitchesFromPreferences(shadowRoot, switchData, gtmData) { const userPreferenceCookie = gtmData || getCookie(`${CC_COOKIE_MODAL}_preference`); if (!userPreferenceCookie) return; try { const preferences = typeof userPreferenceCookie === 'object' ? userPreferenceCookie : JSON.parse(userPreferenceCookie); const selectedCookies = preferences.selectedCookies; switchData.forEach(({ key }) => { const switchElement = shadowRoot.getElementById(key); if (switchElement && selectedCookies.hasOwnProperty(key)) { switchElement.checked = selectedCookies[key]; } }); } catch (error) { console.error('Error parsing cookie preferences:', error); return; } } function modalActions( cookieList, switchData, scannerId, bannerId, configuration, shadowRoot, ) { const modal = shadowRoot.getElementById(CC_MODAL_ID); const modalSettings = shadowRoot.getElementById(CC_MODAL_ID_SETTINGS); const selectedCookieButton = shadowRoot.getElementById( 'cc-cookie-simple-button_id', ); const handleDisplayModalSettingsTrigger = handleDisplayModalSettings( modal, modalSettings, ); if (selectedCookieButton) { selectedCookieButton.onclick = function () { if ( configuration.mode === CC_STANDARD_MODE_ONLY_SETTINGS && modalSettings ) { modalSettings.style.visibility = 'visible'; } else if (modal) { modal.style.display = 'block'; sessionStorage.removeItem(CC_COOKIE_MODAL); } selectedCookieButton.style.visibility = 'hidden'; }; addClickHandlerByClass(shadowRoot, 'cc-modal-logo-footer-compliance', () => window.open( 'https://captaincompliance.com/solutions/cookie-consent-manager/', '_blank', ), ); } const selectedModalCloseButton = shadowRoot.getElementById('cc-modal-close-all'); if (selectedModalCloseButton) { selectedModalCloseButton.onclick = function () { const cookieRemoved = isCookieRemoved(`${CC_COOKIE_MODAL}_${scannerId}`); const hasThirdPartyClass = modal?.classList.contains( 'cc-trigger-from-third-party', ); if (cookieRemoved && modal && !hasThirdPartyClass) { modal.style.display = 'block'; } if (configuration.mode === CC_STANDARD_MODE_ONLY_SETTINGS) { selectedCookieButton.style.visibility = 'visible'; } if (modalSettings) { modalSettings.style.visibility = 'hidden'; } }; } const partialAllowedButton = shadowRoot.getElementById( 'cc-modal-allow-selection', ); if (partialAllowedButton) { partialAllowedButton.onclick = function () { closeModal(shadowRoot, scannerId); updateStatus(bannerId, 'PARTIALLY_ALLOWED'); modalSettings.style.visibility = 'hidden'; handlePartialAllowedRemove(shadowRoot, switchData, cookieList); const selectedCookies = switchData.reduce((accumulator, { key }) => { const checked = shadowRoot.getElementById(key)?.checked ?? false; return { ...accumulator, [key]: checked }; }, {}); const gtmData = { status: 'PARTIALLY_ALLOWED', scannerId, bannerId, selectedCookies, }; fillSwitchesFromPreferences(shadowRoot, switchData, gtmData); addCookie('preference', JSON.stringify(gtmData)); setGTMDataLayer('captainComplianceConsent', gtmData); }; } const allowAllButton = shadowRoot.getElementById('cc-modal-allow-all'); if (allowAllButton) { allowAllButton.onclick = function () { closeModal(shadowRoot, scannerId); updateStatus(bannerId, 'ALLOWED'); const gtmData = { status: 'ALLOWED', scannerId, bannerId, selectedCookies: { STRICTLY_NECESSARY_COOKIES: true, TARGETING_COOKIES: true, FUNCTIONALITY_COOKIES: true, PERFORMANCE_COOKIES: true, UNCLASSIFIED_COOKIES: true, }, }; fillSwitchesFromPreferences(shadowRoot, switchData, gtmData); addCookie('preference', JSON.stringify(gtmData)); setGTMDataLayer('captainComplianceConsent', gtmData); }; } const rejectAllButton = shadowRoot.getElementById('cc-modal-reject-all'); if (rejectAllButton) { rejectAllButton.onclick = function () { closeModal(shadowRoot, scannerId); updateStatus(bannerId, 'REJECTED'); reject(mergeCookieList(cookieList)); const gtmData = { status: 'REJECTED', scannerId, bannerId, selectedCookies: { STRICTLY_NECESSARY_COOKIES: false, TARGETING_COOKIES: false, FUNCTIONALITY_COOKIES: false, PERFORMANCE_COOKIES: false, UNCLASSIFIED_COOKIES: false, }, }; fillSwitchesFromPreferences(shadowRoot, switchData, gtmData); addCookie('preference', JSON.stringify(gtmData)); setGTMDataLayer('captainComplianceConsent', gtmData); }; } const modalCloseIconButton = shadowRoot.getElementById( 'cc-cookie-simple-button-close_id', ); if (modalCloseIconButton) { modalCloseIconButton.onclick = function () { modal.style.display = 'none'; selectedCookieButton.style.visibility = 'visible'; sessionStorage.setItem(CC_COOKIE_MODAL, 'true'); }; } const externalButton = document.getElementById('id-open-settings-cc'); if (externalButton) { externalButton.addEventListener('click', () => { modal.classList.add('cc-trigger-from-third-party'); handleDisplayModalSettingsTrigger(); }); } else { document.body.addEventListener('click', function (event) { if (event.target && event.target.id === 'id-open-settings-cc') { modal.classList.add('cc-trigger-from-third-party'); handleDisplayModalSettingsTrigger(); } }); } addClickHandlerByClass( shadowRoot, 'captain-compliance-open-settings', handleDisplayModalSettingsTrigger, ); } async function safeRemoveCookiesOnList(cookieList) { await new Promise((resolve) => setTimeout(resolve, 5000)); await removeCookiesOnList(cookieList); } async function removeCookiesOnList(cookieList) { const regex = new RegExp(`${CC_COOKIE_MODAL}_\\d+`); const cookies = document.cookie.split(';'); const cookiesServer = await getCookiesFromServer(); const cookiesServerArray = cookiesServer.cookies ? Object.keys(cookiesServer.cookies) : []; const fromServer = []; cookieList.forEach((cookieItem) => { const prefix = cookieItem.name; fromServer.push(prefix); [...cookies, ...cookiesServerArray].forEach((cookie) => { const [name] = cookie.trim().split('='); const isMatch = name.startsWith(prefix) && name.length >= prefix.length; const isRegexMatch = cookieItem.regex ? new RegExp(cookieItem.regex).test(name) : isMatch; if (!regex.test(name) && isRegexMatch) { const expiration = `${name}=; expires=Thu, 01 Jan 1970 00:00:01 GMT;`; document.cookie = `${expiration}`; document.cookie = `${expiration} path=/;`; document.cookie = `${expiration} path=/; domain=${cookieItem.domain}`; document.cookie = `${expiration} path=/; domain=${cookieItem.domain}; secure`; document.cookie = `${expiration} path=/; domain=${cookieItem.domain}; SameSite=None; Secure`; const cookieRemoved = isCookieRemoved(name); if (!cookieRemoved) { let date = new Date(); date.setTime(date.getTime() + -1 * 24 * 60 * 60 * 1000); const expires = '; expires=' + date.toGMTString(); document.cookie = `${name}=${expires}; path=/;`; } } }); }); removeFromTheServer(fromServer); } function checkAndRemoveMissingCookies() { const storedPendingCookies = localStorage.getItem(CC_PENDING_COOKIES_KEY); const pendingCookies = storedPendingCookies ? JSON.parse(storedPendingCookies) : []; safeRemoveCookiesOnList(pendingCookies); } function isCookieRemoved(cookieName) { return document.cookie.split(';').every((cookie) => { return cookie.trim().startsWith(`${cookieName}=`) === false; }); } function reject(cookieList) { removeCookiesOnList(cookieList); localStorage.setItem(CC_PENDING_COOKIES_KEY, JSON.stringify(cookieList)); } function rejectPassive(cookieList) { safeRemoveCookiesOnList(cookieList); } function shouldDisplayBanner(bannerConfiguration, banner) { if ( !!bannerConfiguration && !bannerConfiguration.active && !bannerConfiguration.region.isGlobal ) { return false; } const cookieRemoved = isCookieRemoved( `${CC_COOKIE_MODAL}_${banner.scannerId}`, ); if (!cookieRemoved) return false; return true; } function getDomain() { return window.location.hostname.replace('www.', ''); } function getDomainFromString(urlString) { try { const url = new URL(urlString); return url.hostname.replace('www.', ''); } catch (error) { return 'Invalid URL'; } } function getCookie(name) { const cookies = document.cookie.split('; '); for (const cookie of cookies) { const [cookieName, cookieValue] = cookie.split('='); if (cookieName === name) { return decodeURIComponent(cookieValue); } } return null; } function handleUserConsentPreferences() { const userPreferenceCookie = getCookie(`${CC_COOKIE_MODAL}_preference`); if (!userPreferenceCookie) return; try { const preferences = JSON.parse(userPreferenceCookie); setGTMDataLayer('captainComplianceConsent', preferences); } catch (error) { return; } } function getBannerDefaultStyles( bannerModeStyle, bannerConfiguration, icon, settings, ) { return { html: bannerConfiguration.html || bannerModeStyle.html, css: bannerConfiguration.css || bannerModeStyle.styles, miniHtml: bannerConfiguration.miniHtml || icon.html, miniCss: bannerConfiguration.miniCss || icon.styles, settingsHtml: bannerConfiguration.settingsHtml || settings.html, settingsCss: bannerConfiguration.settingsCss || settings.styles, }; } async function renderModal() { const isGPCEnabled = navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNotTrack === '1' || navigator.globalPrivacyControl === true; const data = await loadBannerData(); const currentDomain = getDomain(); const expectedDomain = getDomainFromString(data.banner.scanner.domain); if (currentDomain !== expectedDomain) { console.log( '%cSORRY THIS IS NOT THE ALLOWED DOMAIN', 'color: red; font-size: 20px', ); return; } const { bannerConfiguration, bannerDisplays, banner, report, bannerModeStyle, } = data; const bannerReport = await loadData(banner.scannerId); if (bannerReport) { const switchData = await loadSwitchData(); const shadowRoot = generateHtmlModalNode(); if (isGPCEnabled) { console.log('%cGPC Signal', 'color: blue; font-size: 15px'); handlePartialAllowedRemove( shadowRoot, switchData, bannerReport.reportInformation.cookies, ); return; } const settingsModalStructure = await getModalStructure('SETTINGS'); const iconCookieStructure = await getModalStructure('ICON'); const bannerStructured = getBannerDefaultStyles( bannerModeStyle, bannerConfiguration, iconCookieStructure, settingsModalStructure, ); rejectPassive(mergeCookieList(bannerReport.reportInformation.cookies)); trackBannerLoad(banner.id); addModal( bannerConfiguration, switchData, bannerDisplays, report, bannerStructured.html, bannerStructured.settingsHtml, bannerStructured.miniHtml, shadowRoot, ); createConsentBannerStyles( bannerConfiguration, banner, bannerStructured.css, bannerStructured.settingsCss, bannerStructured.miniCss, shadowRoot, ); modalActions( bannerReport.reportInformation.cookies, switchData, banner.scannerId, banner.id, bannerConfiguration, shadowRoot, ); toggleActions(shadowRoot); } } (function () { const originalPushState = history.pushState; const originalReplaceState = history.replaceState; function onRouteChange(url) { console.log(`Navigated to: ${url}`); checkAndRemoveMissingCookies(); handleUserConsentPreferences(); } history.pushState = function (...args) { originalPushState.apply(this, args); // Call the original method onRouteChange(window.location.href); // Trigger the route change handler }; history.replaceState = function (...args) { originalReplaceState.apply(this, args); // Call the original method onRouteChange(window.location.href); // Trigger the route change handler }; window.addEventListener('popstate', function () { onRouteChange(window.location.href); }); })(); (function () { function onContentLoaded() { console.log('All content loaded successfully.'); checkAndRemoveMissingCookies(); handleUserConsentPreferences(); } if (document.readyState === 'complete') { onContentLoaded(); } else { window.addEventListener('load', onContentLoaded); } console.log('Content load listener initialized.'); })(); window.addEventListener('beforeunload', function () { checkAndRemoveMissingCookies(); handleUserConsentPreferences(); }); renderModal();