feat: Implement cookie consent management and improve navigation functionality

This commit is contained in:
2026-05-27 14:37:02 +02:00
parent f41dcefb09
commit 8df8eb625f
+134 -163
View File
@@ -754,6 +754,110 @@
(function () {
var menuToggle = document.getElementById('mobileMenuToggle');
var navLinks = document.getElementById('mainNav');
var consentKey = 'cookie_consent_v1';
var banner = document.getElementById('cookieBanner');
var acceptBtn = document.getElementById('cookieAcceptBtn');
var prefsBtn = document.getElementById('cookiePrefsBtn');
var prefsModal = document.getElementById('cookiePrefsModal');
var analyticsCheckbox = document.getElementById('cookie_analytics');
var marketingCheckbox = document.getElementById('cookie_marketing');
var prefsSave = document.getElementById('cookiePrefsSave');
var prefsAcceptAll = document.getElementById('cookiePrefsAcceptAll');
var prefsOnlyNecessary = document.getElementById('cookiePrefsOnlyNecessary');
function getConsent() {
try {
var raw = window.localStorage.getItem(consentKey);
return raw ? JSON.parse(raw) : null;
} catch (e) {
return null;
}
}
function applyConsent() {
try {
var c = getConsent() || {};
var analyticsAllowed = !!c.analytics;
var marketingAllowed = !!c.marketing;
document.querySelectorAll('script[type="text/plain"]').forEach(function (node) {
var required = node.getAttribute('data-consent') || 'analytics';
var allowed = (required === 'analytics' && analyticsAllowed) || (required === 'marketing' && marketingAllowed) || required === 'necessary';
if (!allowed) return;
if (node.getAttribute('data-consent-applied') === '1') return;
var script = document.createElement('script');
if (node.src) script.src = node.src;
else script.text = node.textContent || node.innerText || '';
var type = node.getAttribute('data-script-type');
if (type) script.type = type;
node.parentNode.insertBefore(script, node);
node.setAttribute('data-consent-applied', '1');
});
} catch (e) {
console.error('applyConsent error', e);
}
}
function saveConsent(obj) {
try {
window.localStorage.setItem(consentKey, JSON.stringify(obj));
document.cookie = 'cookie_consent=' + encodeURIComponent(JSON.stringify(obj)) + '; Max-Age=' + (60 * 60 * 24 * 365) + '; Path=/; SameSite=Strict';
applyConsent();
} catch (e) {
console.error('saveConsent error', e);
}
}
function showBannerIfNeeded() {
if (banner && !getConsent()) banner.classList.remove('hidden');
}
function openPrefs() {
var consent = getConsent() || {};
if (analyticsCheckbox) analyticsCheckbox.checked = !!consent.analytics;
if (marketingCheckbox) marketingCheckbox.checked = !!consent.marketing;
if (prefsModal) prefsModal.classList.remove('hidden');
}
function closePrefs() {
if (prefsModal) prefsModal.classList.add('hidden');
if (banner) banner.classList.add('hidden');
}
window.printDocument = function () {
try { window.print(); } catch (e) { console.error('print error', e); }
};
window.printPdf = function (url) {
try {
var popup = window.open(url, '_blank');
if (!popup) return;
var attempts = 0;
var timer = setInterval(function () {
attempts += 1;
try {
if (popup.document && (popup.document.readyState === 'complete' || popup.document.readyState === 'interactive')) {
popup.focus();
popup.print();
clearInterval(timer);
}
} catch (err) {
if (attempts > 8) {
clearInterval(timer);
try { popup.focus(); } catch (focusError) {}
}
}
if (attempts > 30) clearInterval(timer);
}, 300);
} catch (e) {
console.error('printPdf error', e);
}
};
if (menuToggle && navLinks) {
menuToggle.addEventListener('click', function () {
var isExpanded = this.getAttribute('aria-expanded') === 'true';
@@ -764,11 +868,10 @@
navLinks.querySelectorAll('a').forEach(function (link) {
link.addEventListener('click', function () {
if (navLinks.classList.contains('open')) {
menuToggle.setAttribute('aria-expanded', 'false');
navLinks.classList.remove('open');
navLinks.setAttribute('aria-hidden', 'true');
}
if (!navLinks.classList.contains('open')) return;
menuToggle.setAttribute('aria-expanded', 'false');
navLinks.classList.remove('open');
navLinks.setAttribute('aria-hidden', 'true');
});
});
@@ -780,16 +883,6 @@
}
});
navLinks.querySelectorAll('a').forEach(function (link) {
link.addEventListener('click', function () {
if (navLinks.classList.contains('open')) {
menuToggle.setAttribute('aria-expanded', 'false');
navLinks.classList.remove('open');
navLinks.setAttribute('aria-hidden', 'true');
}
});
});
window.addEventListener('resize', function () {
if (window.innerWidth > 820 && navLinks.classList.contains('open')) {
menuToggle.setAttribute('aria-expanded', 'false');
@@ -799,161 +892,39 @@
});
}
var banner = document.getElementById("cookieBanner");
var acceptBtn = document.getElementById("cookieAcceptBtn");
var declineBtn = document.getElementById("cookieDeclineBtn");
if (!banner || !acceptBtn || !declineBtn) {
return;
}
if (banner && acceptBtn) {
showBannerIfNeeded();
var consentKey = "cookie_consent_v1";
acceptBtn.addEventListener('click', function () {
saveConsent({ status: 'accepted', necessary: true, analytics: true, marketing: true, saved_at: Date.now() });
banner.classList.add('hidden');
});
function getConsent() {
try {
var raw = window.localStorage.getItem(consentKey);
if (!raw) return null;
return JSON.parse(raw);
} catch (e) {
return null;
}
}
if (prefsBtn) prefsBtn.addEventListener('click', openPrefs);
function saveConsent(obj) {
try {
window.localStorage.setItem(consentKey, JSON.stringify(obj));
var cookieVal = obj.status || (obj.accepted ? 'accepted' : 'custom');
document.cookie = "cookie_consent=" + encodeURIComponent(JSON.stringify(obj)) + "; Max-Age=" + (60 * 60 * 24 * 365) + "; Path=/; SameSite=Strict";
// Apply consent immediately: enable any scripts gated by consent
applyConsent();
} catch (e) {
// ignore
}
}
function showBannerIfNeeded() {
var c = getConsent();
if (!c) {
banner.classList.remove("hidden");
}
}
showBannerIfNeeded();
acceptBtn.addEventListener("click", function () {
var obj = { status: 'accepted', necessary: true, analytics: true, marketing: true, saved_at: Date.now() };
saveConsent(obj);
banner.classList.add("hidden");
});
// Preferences modal handling
var prefsBtn = document.getElementById('cookiePrefsBtn');
var prefsModal = document.getElementById('cookiePrefsModal');
var analyticsCheckbox = document.getElementById('cookie_analytics');
var marketingCheckbox = document.getElementById('cookie_marketing');
var prefsSave = document.getElementById('cookiePrefsSave');
var prefsAcceptAll = document.getElementById('cookiePrefsAcceptAll');
var prefsOnlyNecessary = document.getElementById('cookiePrefsOnlyNecessary');
function openPrefs() {
// initialize from existing consent if present
var c = getConsent() || {};
analyticsCheckbox.checked = !!c.analytics;
marketingCheckbox.checked = !!c.marketing;
prefsModal.classList.remove('hidden');
}
function closePrefs() {
prefsModal.classList.add('hidden');
banner.classList.add('hidden');
}
if (prefsBtn) prefsBtn.addEventListener('click', openPrefs);
prefsSave.addEventListener('click', function () {
var obj = { status: 'custom', necessary: true, analytics: !!analyticsCheckbox.checked, marketing: !!marketingCheckbox.checked, saved_at: Date.now() };
saveConsent(obj);
closePrefs();
});
prefsAcceptAll.addEventListener('click', function () {
var obj = { status: 'accepted', necessary: true, analytics: true, marketing: true, saved_at: Date.now() };
saveConsent(obj);
closePrefs();
});
prefsOnlyNecessary.addEventListener('click', function () {
var obj = { status: 'necessary', necessary: true, analytics: false, marketing: false, saved_at: Date.now() };
saveConsent(obj);
closePrefs();
});
// --- Consent-based script loader -------------------------------------------------
// Usage: include scripts that require consent like this in templates:
// <script type="text/plain" data-consent="analytics" src="https://www.googletagmanager.com/gtag/js?id=XXX"></script>
// or for inline scripts:
// <script type="text/plain" data-consent="marketing"> /* fbq or other init code */ </script>
function applyConsent() {
try {
var c = getConsent() || {};
var analyticsAllowed = !!c.analytics;
var marketingAllowed = !!c.marketing;
// Find all scripts that were intentionally disabled by using type="text/plain"
document.querySelectorAll('script[type="text/plain"]').forEach(function (node) {
var required = node.getAttribute('data-consent') || 'analytics';
if ((required === 'analytics' && analyticsAllowed) || (required === 'marketing' && marketingAllowed) || required === 'necessary') {
var s = document.createElement('script');
// copy src or inline content
if (node.src) s.src = node.src;
else s.text = node.textContent || node.innerText || '';
// copy attributes that are safe/needed
var type = node.getAttribute('data-script-type');
if (type) s.type = type;
// append to same parent
node.parentNode.insertBefore(s, node);
// mark original as applied to avoid double-inserting
node.setAttribute('data-consent-applied', '1');
}
if (prefsSave) prefsSave.addEventListener('click', function () {
saveConsent({
status: 'custom',
necessary: true,
analytics: !!(analyticsCheckbox && analyticsCheckbox.checked),
marketing: !!(marketingCheckbox && marketingCheckbox.checked),
saved_at: Date.now()
});
} catch (e) {
console.error('applyConsent error', e);
}
closePrefs();
});
if (prefsAcceptAll) prefsAcceptAll.addEventListener('click', function () {
saveConsent({ status: 'accepted', necessary: true, analytics: true, marketing: true, saved_at: Date.now() });
closePrefs();
});
if (prefsOnlyNecessary) prefsOnlyNecessary.addEventListener('click', function () {
saveConsent({ status: 'necessary', necessary: true, analytics: false, marketing: false, saved_at: Date.now() });
closePrefs();
});
}
// Run once on load in case user already saved consent
applyConsent();
// Global print helpers
window.printDocument = function () {
try { window.print(); } catch (e) { console.error('print error', e); }
};
window.printPdf = function (url) {
try {
var w = window.open(url, '_blank');
if (!w) return;
// Attempt to print after load; poll for readyState
var attempts = 0;
var t = setInterval(function () {
attempts++;
try {
if (w.document && (w.document.readyState === 'complete' || w.document.readyState === 'interactive')) {
w.focus();
w.print();
clearInterval(t);
}
} catch (err) {
// cross-domain or plugin viewer may block access; fallback: focus window and leave to user
if (attempts > 8) {
clearInterval(t);
try { w.focus(); } catch (e) {}
}
}
if (attempts > 30) clearInterval(t);
}, 300);
} catch (e) {
console.error('printPdf error', e);
}
};
})();
</script>
</body>