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}
`;
}, '');
}
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();