monorepo/native/wordpress/maple-gdpr-cookies/public/js/frontend-compliant.js

500 lines
16 KiB
JavaScript

/* Maple GDPR Cookies - Script Blocker (GDPR Compliant) */
(function () {
"use strict";
// Check consent status immediately
const consentCookie = getCookie("mgc_consent");
const consentCategories = getCookie("mgc_consent_categories");
// If user has given consent, enable scripts
if (consentCookie === "accept" && consentCategories) {
const categories = JSON.parse(consentCategories || "[]");
enableScripts(categories);
}
// Block scripts on initial load (GDPR compliance - prior consent)
document.addEventListener("DOMContentLoaded", function () {
blockScripts();
// Show notice if no consent given
if (!consentCookie) {
createNotice();
} else {
// Add persistent cookie settings button or footer text
addPersistentPreferenceLink();
}
// Handle clicks on shortcode preference links
document.addEventListener("click", function (e) {
if (
e.target.matches("[data-mgc-preferences-trigger]") ||
e.target.closest("[data-mgc-preferences-trigger]")
) {
e.preventDefault();
showSettingsModal();
}
});
});
/**
* Block all non-essential scripts until consent is given
* CRITICAL for GDPR compliance
*/
function blockScripts() {
// Find all scripts with data-category attribute
const scripts = document.querySelectorAll("script[data-cookie-category]");
scripts.forEach(function (script) {
const category = script.getAttribute("data-cookie-category");
// Check if this category is allowed
if (!isCategoryAllowed(category)) {
// Block the script by changing type
if (script.type !== "text/plain") {
script.type = "text/plain";
script.setAttribute("data-original-type", "text/javascript");
}
}
});
}
/**
* Enable scripts for allowed categories
*/
function enableScripts(allowedCategories) {
// Always allow functional
if (!allowedCategories.includes("functional")) {
allowedCategories.push("functional");
}
// Find all blocked scripts
const scripts = document.querySelectorAll(
'script[data-cookie-category][type="text/plain"]',
);
scripts.forEach(function (script) {
const category = script.getAttribute("data-cookie-category");
// If category is allowed, re-enable script
if (allowedCategories.includes(category)) {
const originalType =
script.getAttribute("data-original-type") || "text/javascript";
// Create new script element (changing type on existing doesn't execute)
const newScript = document.createElement("script");
newScript.type = originalType;
// Copy attributes
Array.from(script.attributes).forEach(function (attr) {
if (attr.name !== "type" && attr.name !== "data-original-type") {
newScript.setAttribute(attr.name, attr.value);
}
});
// Copy content
if (script.src) {
newScript.src = script.src;
} else {
newScript.textContent = script.textContent;
}
// Replace old script with new one
script.parentNode.replaceChild(newScript, script);
}
});
// Trigger event for other integrations
document.dispatchEvent(
new CustomEvent("mgc_scripts_loaded", {
detail: { categories: allowedCategories },
}),
);
}
/**
* Check if a category is currently allowed
*/
function isCategoryAllowed(category) {
const consentCookie = getCookie("mgc_consent");
if (consentCookie !== "accept") {
return false;
}
const categories = JSON.parse(getCookie("mgc_consent_categories") || "[]");
return categories.includes(category) || category === "functional";
}
/**
* Add persistent cookie preference link (GDPR requirement - easy withdrawal)
* Can be either a floating icon button or footer text link based on settings
*/
function addPersistentPreferenceLink() {
// Check what type of display is preferred
const displayType = window.mgcSettings?.preferenceDisplayType || "icon";
// If 'neither', don't add anything - user will use shortcode
if (displayType === "neither") {
return;
}
// Check if element already exists
if (
document.querySelector(".mgc-floating-button") ||
document.querySelector(".mgc-footer-preference-link")
) {
return;
}
if (displayType === "footer") {
// Create footer text link
const footerLink = document.createElement("a");
footerLink.className = "mgc-footer-preference-link";
footerLink.href = "#";
footerLink.textContent = "Cookie Preferences";
footerLink.setAttribute("aria-label", "Cookie Preferences");
footerLink.onclick = function (e) {
e.preventDefault();
showSettingsModal();
};
document.body.appendChild(footerLink);
} else {
// Create floating icon button (default)
const button = document.createElement("button");
button.className = "mgc-floating-button";
button.setAttribute("aria-label", "Cookie Settings");
button.innerHTML =
'<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z"/></svg>';
button.title = "Cookie Settings";
button.onclick = function () {
showSettingsModal();
};
document.body.appendChild(button);
}
}
function createNotice() {
const settings = window.mgcSettings || {};
// Create notice container
const notice = document.createElement("div");
notice.className =
"mgc-notice mgc-position-" +
(settings.position || "bottom") +
" mgc-theme-" +
(settings.theme || "light") +
" mgc-animation-" +
(settings.animation || "slide");
// Create content
const content = document.createElement("div");
content.className = "mgc-notice-content";
const noticeText = (
settings.noticeText ||
"We use cookies to enhance your browsing experience."
)
.replace(/\\"/g, '"')
.replace(/\\\\/g, "\\")
.replace(/&quot;/g, '"')
.replace(/"Accept All"/g, "<strong>Accept All</strong>")
.replace(/"Reject All"/g, "<strong>Reject All</strong>");
content.innerHTML = noticeText;
// Add privacy policy link if provided
if (settings.privacyPolicyUrl) {
content.innerHTML +=
' <a href="' +
settings.privacyPolicyUrl +
'" class="mgc-privacy-link" target="_blank">' +
(settings.privacyPolicyText || "Privacy Policy") +
"</a>";
}
// Create buttons container
const buttons = document.createElement("div");
buttons.className = "mgc-notice-buttons";
// Accept button
const acceptBtn = document.createElement("button");
acceptBtn.className =
"mgc-button mgc-button-accept mgc-button-color-" +
(settings.buttonColor || "blue");
acceptBtn.textContent = settings.acceptButtonText || "Accept All";
acceptBtn.onclick = function () {
handleConsent("accept", ["analytics", "marketing", "functional"]);
};
buttons.appendChild(acceptBtn);
// Reject button (optional)
if (settings.showRejectButton) {
const rejectBtn = document.createElement("button");
rejectBtn.className =
"mgc-button mgc-button-reject mgc-button-color-" +
(settings.buttonColor || "blue");
rejectBtn.textContent = settings.rejectButtonText || "Reject All";
rejectBtn.onclick = function () {
handleConsent("reject", ["functional"]); // Only functional cookies
};
buttons.appendChild(rejectBtn);
}
// Settings button (optional)
if (settings.showSettingsButton) {
const settingsBtn = document.createElement("button");
settingsBtn.className =
"mgc-button mgc-button-settings mgc-button-color-" +
(settings.buttonColor || "blue");
settingsBtn.textContent =
settings.settingsButtonText || "Cookie Settings";
settingsBtn.onclick = function () {
showSettingsModal();
};
buttons.appendChild(settingsBtn);
}
// Append elements
notice.appendChild(content);
notice.appendChild(buttons);
document.body.appendChild(notice);
}
function showSettingsModal() {
// Remove initial notice if present
const initialNotice = document.querySelector(".mgc-notice");
if (initialNotice) {
initialNotice.classList.add("mgc-hidden");
}
// Create settings modal
const modal = document.createElement("div");
modal.className = "mgc-settings-modal";
const modalContent = document.createElement("div");
modalContent.className = "mgc-settings-content";
// Modal title
const title = document.createElement("h3");
title.textContent = "Cookie Settings";
title.style.marginTop = "0";
modalContent.appendChild(title);
// Description
const desc = document.createElement("p");
desc.textContent = "Choose which types of cookies you want to allow:";
modalContent.appendChild(desc);
// Get current consent
const currentCategories = JSON.parse(
getCookie("mgc_consent_categories") || '["functional"]',
);
// Cookie categories
const categories = [
{
id: "functional",
label: "Functional Cookies",
description:
"These cookies are essential for the website to function properly. They enable basic features like page navigation, security, and access to secure areas. The website cannot function properly without these cookies.",
required: true,
},
{
id: "analytics",
label: "Analytics Cookies",
description:
"These cookies help us understand how visitors interact with our website by collecting and reporting information anonymously. They help us improve our website performance.",
required: false,
},
{
id: "marketing",
label: "Marketing Cookies",
description:
"These cookies are used to track visitors across websites and display personalized advertisements. They may be set by third-party advertising networks.",
required: false,
},
];
const checkboxes = [];
categories.forEach(function (cat) {
const categoryDiv = document.createElement("div");
categoryDiv.className = "mgc-category";
categoryDiv.style.marginBottom = "15px";
categoryDiv.style.padding = "10px";
categoryDiv.style.border = "1px solid #ddd";
categoryDiv.style.borderRadius = "4px";
const label = document.createElement("label");
label.style.display = "flex";
label.style.alignItems = "flex-start";
label.style.cursor = cat.required ? "not-allowed" : "pointer";
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.id = "mgc-cat-" + cat.id;
checkbox.value = cat.id;
checkbox.checked = currentCategories.includes(cat.id);
checkbox.disabled = cat.required;
checkbox.style.marginRight = "10px";
checkbox.style.marginTop = "3px";
if (!cat.required) {
checkboxes.push(checkbox);
}
const textDiv = document.createElement("div");
const labelText = document.createElement("strong");
labelText.textContent = cat.label;
if (cat.required) {
labelText.textContent += " (Always Active)";
}
const descText = document.createElement("div");
descText.textContent = cat.description;
descText.style.fontSize = "13px";
descText.style.color = "#666";
descText.style.marginTop = "3px";
textDiv.appendChild(labelText);
textDiv.appendChild(descText);
label.appendChild(checkbox);
label.appendChild(textDiv);
categoryDiv.appendChild(label);
modalContent.appendChild(categoryDiv);
});
// Buttons
const buttonRow = document.createElement("div");
buttonRow.className = "mgc-notice-buttons";
buttonRow.style.marginTop = "20px";
// Save Preferences button
const saveBtn = document.createElement("button");
saveBtn.className =
"mgc-button mgc-button-accept mgc-button-color-" +
(window.mgcSettings?.buttonColor || "blue");
saveBtn.textContent = "Save Preferences";
saveBtn.onclick = function () {
const selectedCategories = ["functional"];
checkboxes.forEach(function (cb) {
if (cb.checked) {
selectedCategories.push(cb.value);
}
});
handleConsent("accept", selectedCategories);
modal.remove();
};
buttonRow.appendChild(saveBtn);
// Accept All button
const acceptAll = document.createElement("button");
acceptAll.className =
"mgc-button mgc-button-accept mgc-button-color-" +
(window.mgcSettings?.buttonColor || "blue");
acceptAll.textContent = "Accept All";
acceptAll.onclick = function () {
handleConsent("accept", ["functional", "analytics", "marketing"]);
modal.remove();
};
buttonRow.appendChild(acceptAll);
// Reject Optional button
const rejectOptional = document.createElement("button");
rejectOptional.className =
"mgc-button mgc-button-settings mgc-button-color-" +
(window.mgcSettings?.buttonColor || "blue");
rejectOptional.textContent = "Reject Optional";
rejectOptional.onclick = function () {
handleConsent("accept", ["functional"]);
modal.remove();
};
buttonRow.appendChild(rejectOptional);
modalContent.appendChild(buttonRow);
modal.appendChild(modalContent);
document.body.appendChild(modal);
}
function handleConsent(type, categories) {
// Set cookies
const expiry = window.mgcSettings?.cookieExpiry || 365;
setCookie("mgc_consent", type, expiry);
setCookie("mgc_consent_categories", JSON.stringify(categories), expiry);
// Enable scripts for allowed categories
enableScripts(categories);
// Send to server
if (window.mgcSettings?.ajaxUrl) {
const formData = new FormData();
formData.append("action", "mgc_save_consent");
formData.append("nonce", window.mgcSettings.nonce);
formData.append("consent_type", type);
categories.forEach(function (cat) {
formData.append("categories[]", cat);
});
fetch(window.mgcSettings.ajaxUrl, {
method: "POST",
body: formData,
}).catch(function () {
// Silently fail
});
}
// Remove notice
const notice = document.querySelector(".mgc-notice");
if (notice) {
notice.classList.add("mgc-hidden");
setTimeout(function () {
notice.remove();
}, 300);
}
// Add persistent preference link
addPersistentPreferenceLink();
// Trigger event
document.dispatchEvent(
new CustomEvent("mgc_consent_saved", {
detail: { type: type, categories: categories },
}),
);
// Reload page to apply changes
if (type === "accept") {
setTimeout(function () {
window.location.reload();
}, 500);
}
}
function setCookie(name, value, days) {
const d = new Date();
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
const expires = "expires=" + d.toUTCString();
const secure = window.location.protocol === "https:" ? ";Secure" : "";
document.cookie =
name +
"=" +
encodeURIComponent(value) +
";" +
expires +
";path=/;SameSite=Lax" +
secure;
}
function getCookie(name) {
const nameEQ = name + "=";
const ca = document.cookie.split(";");
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === " ") c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0)
return decodeURIComponent(c.substring(nameEQ.length, c.length));
}
return null;
}
})();