const CC_MODAL_ID = 'cc-modal-cookies-banner'; const CC_PENDING_COOKIES_KEY = 'captainConsentPending'; const CC_COOKIE_MODAL = 'captain_cookie_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 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 (positionY === 'middle') { left = '50%'; } return { top, bottom, left, right, }; } function getModalTranslate(positionX, positionY) { let vertical, horizontal = '0px'; if (positionY === 'middle') { vertical = '-50%'; } if (positionX === 'middle') { left = '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; } 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 = null; 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 }) => ({ name, domain, path })); return dataToReturn; } function getCookieTypesToNotRemove(shadowRoot, switchData, bannerDisplays) { return switchData.reduce((accumulator, { key, id }) => { const foundItem = getItemConfiguration(bannerDisplays, id); const checked = shadowRoot.getElementById(key)?.checked || foundItem.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) { 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}=ok; Expires=` + now.toUTCString() + '; Path=/;'; } function closeModal(shadowRoot, scannerId) { shadowRoot.getElementById(CC_MODAL_ID).style.display = 'none'; addCookie(scannerId); } function addClickHandlerByClass(shadowRoot, className, cb) { const elements = shadowRoot.querySelectorAll(`.${className}`); elements.forEach((element) => { element.onclick = function () { cb(); }; }); } function handlePartialAllowedRemove( shadowRoot, switchData, bannerDisplays, cookieList, ) { const listToNotRemove = getCookieTypesToNotRemove( shadowRoot, switchData, bannerDisplays, ); const listUpdated = removeKeyFromObject(cookieList, listToNotRemove); reject(mergeCookieList(listUpdated)); } function handleDisplayModalSettings(modal, modalSettings) { return () => { modal.style.display = 'none'; modalSettings.style.visibility = 'visible'; }; } function modalActions( cookieList, switchData, scannerId, bannerId, bannerDisplays, 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://www.captaincompliance.com/', '_blank'), ); } const selectedModalCloseButton = shadowRoot.getElementById('cc-modal-close-all'); if (selectedModalCloseButton) { selectedModalCloseButton.onclick = function () { const cookieRemoved = isCookieRemoved(`${CC_COOKIE_MODAL}_${scannerId}`); if (cookieRemoved && modal) { 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, bannerDisplays, cookieList, ); }; } const allowAllButton = shadowRoot.getElementById('cc-modal-allow-all'); if (allowAllButton) { allowAllButton.onclick = function () { closeModal(shadowRoot, scannerId); updateStatus(bannerId, 'ALLOWED'); }; } const rejectAllButton = shadowRoot.getElementById('cc-modal-reject-all'); if (rejectAllButton) { rejectAllButton.onclick = function () { closeModal(shadowRoot, scannerId); updateStatus(bannerId, 'REJECTED'); reject(mergeCookieList(cookieList)); }; } 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', handleDisplayModalSettingsTrigger); } else { document.body.addEventListener('click', function (event) { if (event.target && event.target.id === 'id-open-settings-cc') { handleDisplayModalSettingsTrigger(); } }); } addClickHandlerByClass( shadowRoot, 'captain-compliance-open-settings', handleDisplayModalSettingsTrigger, ); } async function removeCookiesOnList(cookieList) { const regex = /captain_cookie_consent_\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; if (isMatch && !regex.test(name)) { 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) : []; removeCookiesOnList(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) { removeCookiesOnList(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'; } } 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, bannerDisplays, bannerReport.reportInformation.cookies, ); return; } const settingsModalStructure = await getModalStructure('SETTINGS'); const iconCookieStructure = await getModalStructure('ICON'); rejectPassive(mergeCookieList(bannerReport.reportInformation.cookies)); trackBannerLoad(banner.id); addModal( bannerConfiguration, switchData, bannerDisplays, report, bannerModeStyle.html, settingsModalStructure.html, iconCookieStructure.html, shadowRoot, ); createConsentBannerStyles( bannerConfiguration, banner, bannerModeStyle.styles, settingsModalStructure.styles, iconCookieStructure.styles, shadowRoot, ); modalActions( bannerReport.reportInformation.cookies, switchData, banner.scannerId, banner.id, bannerDisplays, bannerConfiguration, shadowRoot, ); toggleActions(shadowRoot); } } (function () { const originalPushState = history.pushState; const originalReplaceState = history.replaceState; function onRouteChange(url) { console.log(`Navigated to: ${url}`); checkAndRemoveMissingCookies(); } 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(); } if (document.readyState === 'complete') { onContentLoaded(); } else { window.addEventListener('load', onContentLoaded); } console.log('Content load listener initialized.'); })(); window.addEventListener('beforeunload', function () { checkAndRemoveMissingCookies(); }); renderModal();