gdprcookieconsent/views/js/gdpr_cookie_helper.js

212 lines
6.5 KiB
JavaScript

/**
* GDPR Cookie Consent Helper for PrestaShop
*
* @author Walzen665
* @copyright Copyright (c) 2025
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
*/
// This script runs early in the page load to tag inline scripts with appropriate consent categories
(function() {
// Known script patterns to identify analytics, marketing and functional scripts
const scriptPatterns = {
analytics: [
/google[\s\-_]?analytics/i,
/ga\s*\(\s*['"]create/i,
/googletagmanager/i,
/gtag/i,
/\_gaq/i,
/matomo/i,
/piwik/i,
/mixpanel/i,
/hotjar/i,
/clarity/i
],
marketing: [
/facebook[\s\-_]?pixel/i,
/fbq\s*\(\s*['"]init/i,
/doubleclick/i,
/adwords/i,
/google[\s\-_]?ad[\s\-_]?services/i,
/google[\s\-_]?tag[\s\-_]?manager/i,
/gtm/i,
/twitter[\s\-_]?pixel/i,
/pinterest[\s\-_]?tag/i
],
functional: [
/recaptcha/i,
/chat/i,
/livechat/i,
/support/i,
/feedback/i,
/preference/i,
/usercentrics/i
]
};
// Function to process and tag a script element
function processScript(script) {
// Skip if it already has a consent attribute
if (script.hasAttribute('data-cookieconsent')) {
return;
}
// Skip our own scripts
if (script.src && script.src.indexOf('gdpr_cookie') !== -1) {
script.setAttribute('data-cookieconsent', 'necessary');
return;
}
// Check content for patterns
const content = script.innerHTML || '';
const src = script.src || '';
// Determine script category
let category = 'necessary'; // Default category
for (const [cat, patterns] of Object.entries(scriptPatterns)) {
for (const pattern of patterns) {
if (pattern.test(content) || pattern.test(src)) {
category = cat;
break;
}
}
if (category !== 'necessary') {
break;
}
}
// Tag the script
script.setAttribute('data-cookieconsent', category);
// For non-necessary scripts, we'll create a duplicate with the correct attribute
// but disabled until consent is given
if (category !== 'necessary') {
const originalScript = script;
const newScript = document.createElement('script');
// Copy attributes
Array.from(originalScript.attributes).forEach(attr => {
if (attr.name !== 'data-cookieconsent') {
newScript.setAttribute(attr.name, attr.value);
}
});
// Set consent attribute
newScript.setAttribute('data-cookieconsent', category);
// Copy content if it's an inline script
if (!originalScript.src && originalScript.innerHTML) {
newScript.innerHTML = originalScript.innerHTML;
}
// Replace the original script
originalScript.parentNode.replaceChild(newScript, originalScript);
// Prevent execution by removing src and content
newScript.removeAttribute('src');
newScript.innerHTML = '';
}
}
// Process existing scripts
document.querySelectorAll('script').forEach(processScript);
// Use a MutationObserver to catch dynamically added scripts
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.tagName === 'SCRIPT') {
processScript(node);
} else if (node.querySelectorAll) {
node.querySelectorAll('script').forEach(processScript);
}
});
});
});
// Start observing the document
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
// Also process iframes
function processIframe(iframe) {
// Skip if it already has a consent attribute
if (iframe.hasAttribute('data-cookieconsent')) {
return;
}
const src = iframe.src || '';
// Common third-party iframe sources
const iframePatterns = {
marketing: [
/youtube/i,
/vimeo/i,
/facebook\.com\/plugins/i,
/twitter\.com\/widgets/i,
/instagram\.com/i,
/pinterest\.com/i,
/doubleclick/i,
/ads/i
],
analytics: [
/googletagmanager/i,
/analytics/i
],
functional: [
/recaptcha/i,
/maps\.google/i,
/google\.com\/maps/i,
/disqus/i,
/livechat/i,
/chat/i
]
};
// Determine iframe category
let category = 'necessary'; // Default category
for (const [cat, patterns] of Object.entries(iframePatterns)) {
for (const pattern of patterns) {
if (pattern.test(src)) {
category = cat;
break;
}
}
if (category !== 'necessary') {
break;
}
}
// Tag the iframe
iframe.setAttribute('data-cookieconsent', category);
}
// Process existing iframes
document.querySelectorAll('iframe').forEach(processIframe);
// Use the observer to catch dynamically added iframes
const iframeObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.tagName === 'IFRAME') {
processIframe(node);
} else if (node.querySelectorAll) {
node.querySelectorAll('iframe').forEach(processIframe);
}
});
});
});
// Start observing for iframes
iframeObserver.observe(document.documentElement, {
childList: true,
subtree: true
});
})();