diff --git a/Website/templates/base.html b/Website/templates/base.html
index e226149..4405c99 100644
--- a/Website/templates/base.html
+++ b/Website/templates/base.html
@@ -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:
- //
- // or for inline scripts:
- //
- 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);
- }
- };
})();