/** * 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 }); })();