2025-09-08 12:59:47 +02:00
<!DOCTYPE html>
2025-09-08 13:14:50 +02:00
< html lang = "en" id = "htmlRoot" >
2025-09-08 12:59:47 +02:00
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > 🍪 Privacy-First Demo | GDPR Compliant Tracking< / title >
2025-09-08 13:05:14 +02:00
<!-- Favicon -->
< link rel = "icon" type = "image/x-icon" href = "favicon.ico" >
< link rel = "icon" type = "image/svg+xml" href = "favicon.svg" >
<!-- Meta Tags -->
< meta name = "description" content = "Experience GDPR-compliant optional tracking. Privacy protected by default with enhanced features available only when you choose to enable them." >
< meta name = "author" content = "olemd" >
2025-09-08 12:59:47 +02:00
< style >
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #333;
}
.hero {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
text-align: center;
padding: 2rem;
position: relative;
}
.hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
}
.hero-content {
position: relative;
z-index: 1;
max-width: 800px;
}
.hero h1 {
font-size: 3.5rem;
font-weight: 700;
margin: 0 0 1rem 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero .subtitle {
font-size: 1.4rem;
color: #666;
margin-bottom: 2rem;
font-weight: 300;
}
.privacy-badge {
display: inline-flex;
align-items: center;
gap: 8px;
background: #e8f5e8;
color: #2e7d2e;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
font-weight: 600;
margin-bottom: 2rem;
box-shadow: 0 2px 8px rgba(46, 125, 46, 0.1);
}
.demo-info {
background: white;
padding: 2rem;
border-radius: 16px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
margin-top: 2rem;
text-align: left;
}
.demo-info h2 {
color: #333;
margin-top: 0;
display: flex;
align-items: center;
gap: 8px;
}
.feature-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
margin: 1.5rem 0;
}
.feature {
display: flex;
align-items: center;
gap: 12px;
padding: 1rem;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #667eea;
}
.feature-icon {
font-size: 1.5rem;
min-width: 24px;
}
.tracking-status {
position: fixed;
top: 20px;
left: 20px;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
padding: 12px 20px;
border-radius: 25px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
z-index: 999;
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: 14px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #4caf50;
}
.status-dot.minimal {
background: #ff9800;
}
2025-09-08 13:14:50 +02:00
.language-toggle {
position: fixed;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
padding: 8px;
border-radius: 20px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
z-index: 998;
display: flex;
gap: 4px;
}
.lang-btn {
padding: 6px 12px;
border: none;
border-radius: 15px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
background: transparent;
color: #666;
}
.lang-btn.active {
background: #667eea;
color: white;
}
.lang-btn:hover {
background: rgba(102, 126, 234, 0.1);
}
.lang-btn.active:hover {
background: #5a6fd8;
}
2025-09-08 12:59:47 +02:00
@media (max-width: 768px) {
.hero h1 {
font-size: 2.5rem;
}
.hero .subtitle {
font-size: 1.2rem;
}
.demo-info {
padding: 1.5rem;
}
.tracking-status {
position: static;
margin-bottom: 2rem;
}
2025-09-08 13:14:50 +02:00
.language-toggle {
position: static;
margin-bottom: 1rem;
}
2025-09-08 12:59:47 +02:00
}
.tracking-consent-container {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
}
.tracking-consent-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 24px;
border-radius: 25px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 14px;
font-weight: 600;
cursor: pointer;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
.tracking-consent-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}
.tracking-consent-btn:active {
transform: translateY(0);
}
.tracking-consent-btn.enabled {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.tracking-consent-btn.enabled::after {
content: "✓";
margin-left: 4px;
}
.tracking-icon {
font-size: 16px;
}
.consent-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1001;
justify-content: center;
align-items: center;
}
.consent-modal-content {
background: white;
padding: 30px;
border-radius: 12px;
max-width: 500px;
margin: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.consent-modal h3 {
margin: 0 0 16px 0;
color: #333;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.consent-modal p {
margin: 0 0 20px 0;
color: #666;
line-height: 1.5;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.consent-modal-buttons {
display: flex;
gap: 12px;
justify-content: flex-end;
}
.consent-btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.consent-btn.primary {
background: #667eea;
color: white;
}
.consent-btn.secondary {
background: #f5f5f5;
color: #333;
}
.consent-btn:hover {
opacity: 0.9;
}
2025-09-25 13:55:12 +02:00
.comparison-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin: 20px 0;
}
.comparison-box {
padding: 15px;
border-radius: 8px;
background: #f8f9fa;
}
.privacy-box {
border: 2px solid #4caf50;
background: #e8f5e8;
}
.tracking-box {
border: 1px solid #ddd;
}
.comparison-box h4 {
margin: 0 0 12px 0;
font-size: 1.1em;
color: #333;
}
.comparison-box ul {
list-style: none;
padding: 0;
margin: 0;
}
.comparison-box li {
padding: 4px 0;
font-size: 0.95em;
color: #555;
}
.privacy-badge-inline {
margin-top: 10px;
text-align: center;
background: white;
padding: 5px 10px;
border-radius: 15px;
display: inline-block;
font-size: 0.9em;
color: #4caf50;
font-weight: 600;
}
.equal-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.consent-btn.privacy-choice {
background: linear-gradient(135deg, #4caf50 0%, #45a049 100%);
color: white;
font-size: 1.05em;
padding: 12px 20px;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.consent-btn.tracking-choice {
background: #f5f5f5;
color: #333;
border: 2px solid #ddd;
font-size: 1.05em;
padding: 12px 20px;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.consent-btn.privacy-choice:hover {
background: linear-gradient(135deg, #45a049 0%, #3d8b40 100%);
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(76, 175, 80, 0.3);
}
.consent-btn.tracking-choice:hover {
background: #667eea;
color: white;
border-color: #667eea;
}
.cookie-counter {
position: fixed;
top: 70px;
left: 20px;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
padding: 10px 16px;
border-radius: 20px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
z-index: 997;
font-size: 13px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.privacy-score {
position: fixed;
top: 120px;
left: 20px;
background: linear-gradient(135deg, #4caf50, #45a049);
color: white;
padding: 10px 16px;
border-radius: 20px;
box-shadow: 0 4px 15px rgba(76, 175, 80, 0.2);
z-index: 996;
font-size: 13px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.achievement-toast {
position: fixed;
bottom: 100px;
right: 20px;
background: linear-gradient(135deg, #ffd700, #ffed4e);
color: #333;
padding: 15px 20px;
border-radius: 12px;
box-shadow: 0 8px 25px rgba(255, 215, 0, 0.3);
z-index: 1003;
font-weight: 600;
display: flex;
align-items: center;
gap: 12px;
animation: slideInRight 0.5s ease-out;
}
@keyframes slideInRight {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.privacy-stats {
background: #e8f5e8;
padding: 15px;
border-radius: 8px;
margin: 20px 0;
text-align: center;
border-left: 4px solid #4caf50;
}
.privacy-stats strong {
color: #4caf50;
font-size: 1.2em;
}
@media (max-width: 768px) {
.comparison-container {
grid-template-columns: 1fr;
}
.equal-buttons {
grid-template-columns: 1fr;
}
.cookie-counter, .privacy-score {
position: relative;
top: 0;
left: 0;
margin-bottom: 10px;
}
}
2025-09-08 12:59:47 +02:00
< / style >
< / head >
< body >
2025-09-08 13:14:50 +02:00
< div class = "language-toggle" >
< button class = "lang-btn active" onclick = "setLanguage('en')" id = "langEn" > EN< / button >
< button class = "lang-btn" onclick = "setLanguage('no')" id = "langNo" > NO< / button >
< / div >
2025-09-08 12:59:47 +02:00
< div class = "tracking-status" id = "trackingStatus" >
< div class = "status-dot minimal" id = "statusDot" > < / div >
< span id = "statusText" > Privacy Mode: Minimal Tracking< / span >
< / div >
2025-09-25 13:55:12 +02:00
< div class = "cookie-counter" id = "cookieCounter" style = "cursor: pointer;" onclick = "showCookieDetails()" title = "Click to see cookie details" >
< span id = "cookieIcon" > 🍪< / span >
< span id = "cookieText" > 0 cookies (essential only)< / span >
< span style = "font-size: 10px; margin-left: 4px;" > ℹ ️ < / span >
< / div >
< div class = "privacy-score" id = "privacyScore" >
< span > 🛡️< / span >
< span id = "scoreText" > Privacy Score: 100%< / span >
< / div >
2025-09-08 12:59:47 +02:00
< div class = "hero" >
< div class = "hero-content" >
< div class = "privacy-badge" >
🔒 < span > GDPR Compliant by Default< / span >
< / div >
< h1 > Privacy-First Demo< / h1 >
< p class = "subtitle" >
Experience truly optional tracking. Your privacy is protected by default,< br >
with enhanced features available only when < em > you< / em > choose to enable them.
< / p >
< div class = "demo-info" >
< h2 > 🎯 How It Works< / h2 >
< p > This demo shows how modern websites can respect your privacy while still providing great experiences:< / p >
< div class = "feature-list" >
< div class = "feature" >
< span class = "feature-icon" > 🔒< / span >
< div >
< strong > Default: Privacy Protected< / strong > < br >
< small > Only essential cookies, anonymized analytics< / small >
< / div >
< / div >
< div class = "feature" >
< span class = "feature-icon" > 📊< / span >
< div >
< strong > Optional: Enhanced Tracking< / strong > < br >
< small > Detailed analytics, personalization when you opt-in< / small >
< / div >
< / div >
< div class = "feature" >
< span class = "feature-icon" > 🎚️< / span >
< div >
< strong > Full Control< / strong > < br >
< small > Toggle anytime with the button below< / small >
< / div >
< / div >
< div class = "feature" >
< span class = "feature-icon" > 🍪< / span >
< div >
< strong > Smart Cookie Management< / strong > < br >
< small > Automatic cleanup when disabled< / small >
< / div >
< / div >
< / div >
< p > < strong > Try it out!< / strong > Click the colorful button in the bottom-right corner to see the consent flow in action. 👇< / p >
2025-09-08 13:05:14 +02:00
2025-09-25 13:55:12 +02:00
< div class = "privacy-stats" >
< p style = "margin: 0; font-size: 1.1em;" >
< span style = "font-size: 1.5em;" > 🎉< / span > < br >
< strong > 73%< / strong > of our users choose privacy mode< br >
< small style = "color: #666;" > Join thousands who enjoy a tracking-free experience!< / small >
< / p >
< / div >
2025-09-08 13:05:14 +02:00
< div style = "margin-top: 2rem; padding: 1rem; background: #f8f9fa; border-radius: 8px; text-align: center;" >
< p style = "margin: 0; color: #666; font-size: 14px;" >
💡 < strong > Want to use this in your project?< / strong > < br >
View source code and documentation at
< a href = "https://kode.naiv.no/olemd/gdpr" target = "_blank" rel = "noopener"
style="color: #667eea; text-decoration: none; font-weight: 600;">
kode.naiv.no/olemd/gdpr
< / a >
< / p >
< / div >
2025-09-25 13:55:12 +02:00
< div style = "margin-top: 2rem; padding: 1.5rem; background: linear-gradient(135deg, #667eea15, #764ba215); border-radius: 12px; border-left: 4px solid #667eea;" >
< h3 style = "margin: 0 0 1rem 0; color: #333; display: flex; align-items: center; gap: 8px;" >
< span > 🚀< / span > How Simple Is GDPR Compliance?
< / h3 >
< p style = "margin: 0 0 1rem 0; color: #555;" >
This entire consent system is just < strong style = "color: #667eea;" > ~100 lines of JavaScript!< / strong >
< / p >
< pre style = "background: white; padding: 1rem; border-radius: 8px; overflow-x: auto; margin: 0;" >
< code style = "color: #333; font-size: 13px;" > // Complete GDPR compliance in 3 simple steps:
1. Default to minimal tracking
if (!hasConsent()) {
disableTracking();
}
2. Ask for consent (optional)
showConsentModal();
3. Respect the choice
if (userSaidYes) {
enableTracking();
}
// That's it! 🎉< / code > < / pre >
< p style = "margin: 1rem 0 0 0; color: #666; font-size: 14px;" >
< strong > Key insight:< / strong > Privacy-first doesn't mean complex. It means defaulting to the right thing.
< / p >
< / div >
2025-09-08 12:59:47 +02:00
< / div >
< / div >
< / div >
< div class = "tracking-consent-container" >
< button id = "trackingConsentBtn" class = "tracking-consent-btn" >
< span class = "tracking-icon" > 📊< / span >
< span id = "btnText" > Plz trac me!< / span >
< / button >
< / div >
2025-09-25 13:55:12 +02:00
<!-- Cookie Details Modal -->
< div id = "cookieDetailsModal" class = "consent-modal" style = "display: none;" >
< div class = "consent-modal-content" >
< h3 > 🍪 Cookie Details< / h3 >
< div id = "cookieDetailsList" style = "max-height: 400px; overflow-y: auto;" >
<!-- Cookie details will be populated here -->
< / div >
< div style = "margin-top: 20px; text-align: right;" >
< button class = "consent-btn primary" onclick = "closeCookieDetails()" > Close< / button >
< / div >
< / div >
< / div >
2025-09-08 12:59:47 +02:00
< div id = "consentModal" class = "consent-modal" >
< div class = "consent-modal-content" >
2025-09-25 13:55:12 +02:00
< h3 > Your Privacy, Your Choice! 🛡️< / h3 >
< p style = "text-align: center; font-size: 1.1em; color: #4caf50; margin-bottom: 20px;" >
< strong > ✨ Everything works perfectly without tracking! ✨< / strong >
2025-09-08 12:59:47 +02:00
< / p >
2025-09-25 13:55:12 +02:00
< div class = "comparison-container" >
< div class = "comparison-box privacy-box" >
< h4 > 🛡️ Keep Privacy Mode (Recommended)< / h4 >
< ul >
< li > ✅ Full site functionality< / li >
< li > ✅ Lightning fast< / li >
< li > ✅ No cookies following you< / li >
< li > ✅ Zero personal data collected< / li >
< li > ✅ No retargeting ads< / li >
< / ul >
< div class = "privacy-badge-inline" >
< span > 🏆 Privacy Champion< / span >
< / div >
< / div >
< div class = "comparison-box tracking-box" >
< h4 > 📊 Enable Tracking< / h4 >
< ul >
< li > ➕ Helps us improve< / li >
< li > ➖ Adds 5+ cookies< / li >
< li > ➖ Shares data with Google< / li >
< li > ➖ Enables remarketing< / li >
< li > ➖ Slower page loads< / li >
< / ul >
< / div >
< / div >
< p style = "text-align: center; color: #666; margin: 20px 0;" >
< small > You can change your mind anytime. Join the < strong > 73%< / strong > who choose privacy!< / small >
2025-09-08 12:59:47 +02:00
< / p >
2025-09-25 13:55:12 +02:00
< div class = "consent-modal-buttons equal-buttons" >
< button class = "consent-btn privacy-choice" onclick = "choosePrivacy()" >
< span style = "font-size: 1.2em;" > 🛡️< / span >
No thanks, I'm good!
< / button >
< button class = "consent-btn tracking-choice" onclick = "enableTracking()" >
< span style = "font-size: 1.2em;" > 📊< / span >
Enable tracking
< / button >
2025-09-08 12:59:47 +02:00
< / div >
< / div >
< / div >
< script >
2025-09-08 13:14:50 +02:00
// Internationalization
const translations = {
en: {
title: '🍪 Privacy-First Demo | GDPR Compliant Tracking',
description: 'Experience GDPR-compliant optional tracking. Privacy protected by default with enhanced features available only when you choose to enable them.',
statusMinimal: 'Privacy Mode: Minimal Tracking',
statusEnhanced: 'Privacy Mode: Enhanced Tracking',
gdprBadge: 'GDPR Compliant by Default',
heroTitle: 'Privacy-First Demo',
heroSubtitle: 'Experience truly optional tracking. Your privacy is protected by default,< br > with enhanced features available only when < em > you< / em > choose to enable them.',
howItWorks: 'How It Works',
howItWorksDesc: 'This demo shows how modern websites can respect your privacy while still providing great experiences:',
featurePrivacy: 'Default: Privacy Protected',
featurePrivacyDesc: 'Only essential cookies, anonymized analytics',
featureTracking: 'Optional: Enhanced Tracking',
featureTrackingDesc: 'Detailed analytics, personalization when you opt-in',
featureControl: 'Full Control',
featureControlDesc: 'Toggle anytime with the button below',
featureCookies: 'Smart Cookie Management',
featureCookiesDesc: 'Automatic cleanup when disabled',
tryItOut: 'Try it out!',
tryItOutDesc: 'Click the colorful button in the bottom-right corner to see the consent flow in action. 👇',
wantToUse: 'Want to use this in your project?',
viewSource: 'View source code and documentation at',
buttonText: 'Plz trac me!',
buttonTextOn: 'Tracking ON',
modalTitle: 'Optional Enhanced Tracking',
modalDesc: 'We respect your privacy! By default, we only use essential cookies and basic analytics. If you would like to help us improve our service with enhanced tracking (detailed analytics, personalization, marketing cookies), you can opt in below.',
modalWhatWeTrack: 'What we will track:',
modalItem1: '• Detailed page interactions',
modalItem2: '• User preferences for personalization',
modalItem3: '• Marketing campaign effectiveness',
modalItem4: '• Enhanced usage analytics',
modalCanChange: 'You can change your mind anytime by clicking the tracking button again.',
modalNo: 'No, thanks',
modalYes: 'Yes, track me!',
notificationEnabled: 'Enhanced tracking enabled! Thanks for helping us improve.',
notificationDisabled: 'Switched to minimal tracking. Your privacy is protected.',
2025-09-25 13:55:12 +02:00
confirmDisable: 'Disable enhanced tracking? We will switch back to minimal analytics only.',
privacyChosen: '🎉 Excellent choice! Your privacy is protected and everything works perfectly!',
privacyScore100: 'Privacy Score: 100%',
privacyScore0: 'Privacy Score: 0%',
cookiesEssential: 'cookies (essential only)',
cookiesTracking: 'cookies (with tracking)',
achievementPrivacyChampion: '🏆 Achievement Unlocked: Privacy Champion!',
achievementDataMinimalist: '💚 Achievement: Data Minimalist!',
privacyModeActive: '🛡️ Privacy Mode Active - You\'re Protected!',
trackingModeActive: '📊 Tracking Active - Thank You for Helping!'
2025-09-08 13:14:50 +02:00
},
no: {
title: '🍪 Personvern-først demo | GDPR-kompatibel sporing',
description: 'Opplev GDPR-kompatibel valgfri sporing. Personvernet er beskyttet som standard med utvidede funksjoner tilgjengelig kun når du velger å aktivere dem.',
statusMinimal: 'Personvernmodus: minimal sporing',
statusEnhanced: 'Personvernmodus: utvidet sporing',
gdprBadge: 'GDPR-kompatibelt som standard',
heroTitle: 'Personvern-først demo',
heroSubtitle: 'Opplev virkelig valgfri sporing. Ditt personvern er beskyttet som standard,< br > med utvidede funksjoner tilgjengelig kun når < em > du< / em > velger å aktivere dem.',
howItWorks: 'Hvordan det fungerer',
howItWorksDesc: 'Denne demoen viser hvordan moderne nettsteder kan respektere ditt personvern mens de fortsatt gir gode opplevelser:',
featurePrivacy: 'Standard: personvernbeskyttelse',
featurePrivacyDesc: 'Kun essensielle cookies, anonymisert analyse',
featureTracking: 'Valgfritt: utvidet sporing',
featureTrackingDesc: 'Detaljert analyse, personalisering når du velger det',
featureControl: 'Full kontroll',
featureControlDesc: 'Slå på/av når som helst med knappen nedenfor',
featureCookies: 'Smart cookie-håndtering',
featureCookiesDesc: 'Automatisk opprydding når deaktivert',
tryItOut: 'Prøv det ut!',
tryItOutDesc: 'Klikk på den fargerike knappen i nederste høyre hjørne for å se samtykke-flyten i aksjon. 👇',
wantToUse: 'Vil du bruke dette i ditt prosjekt?',
viewSource: 'Se kildekode og dokumentasjon på',
buttonText: 'Spor meg takk!',
buttonTextOn: 'Sporing PÅ',
modalTitle: 'Valgfri utvidet sporing',
modalDesc: 'Vi respekterer ditt personvern! Som standard bruker vi kun essensielle cookies og grunnleggende analyse. Hvis du ønsker å hjelpe oss forbedre tjenesten vår med utvidet sporing (detaljert analyse, personalisering, markedsføringscookies), kan du velge det nedenfor.',
modalWhatWeTrack: 'Hva vi vil spore:',
modalItem1: '• Detaljerte sideinteraksjoner',
modalItem2: '• Brukerpreferanser for personalisering',
modalItem3: '• Effektivitet av markedsføringskampanjer',
modalItem4: '• Utvidet bruksanalyse',
modalCanChange: 'Du kan ombestemme deg når som helst ved å klikke på sporingsknappen igjen.',
modalNo: 'Nei takk',
modalYes: 'Ja, spor meg!',
notificationEnabled: 'Utvidet sporing aktivert! Takk for at du hjelper oss forbedre.',
notificationDisabled: 'Byttet til minimal sporing. Ditt personvern er beskyttet.',
2025-09-25 13:55:12 +02:00
confirmDisable: 'Deaktiver utvidet sporing? Vi bytter tilbake til kun minimal analyse.',
privacyChosen: '🎉 Utmerket valg! Ditt personvern er beskyttet og alt fungerer perfekt!',
privacyScore100: 'Personvernscore: 100%',
privacyScore0: 'Personvernscore: 0%',
cookiesEssential: 'cookies (kun essensielle)',
cookiesTracking: 'cookies (med sporing)',
achievementPrivacyChampion: '🏆 Prestasjon låst opp: Personvernmester!',
achievementDataMinimalist: '💚 Prestasjon: Dataminimalist!',
privacyModeActive: '🛡️ Personvernmodus aktiv - Du er beskyttet!',
trackingModeActive: '📊 Sporing aktiv - Takk for hjelpen!'
2025-09-08 13:14:50 +02:00
}
};
let currentLang = localStorage.getItem('gdpr-language') || 'en';
function setLanguage(lang) {
currentLang = lang;
localStorage.setItem('gdpr-language', lang);
document.getElementById('htmlRoot').lang = lang;
// Update button states
document.getElementById('langEn').classList.toggle('active', lang === 'en');
document.getElementById('langNo').classList.toggle('active', lang === 'no');
updateTranslations();
// Update tracking consent if it exists
if (window.trackingConsent) {
window.trackingConsent.updateButtonState();
}
}
function updateTranslations() {
const t = translations[currentLang];
// Update page title and meta
document.title = t.title;
document.querySelector('meta[name="description"]').content = t.description;
// Update status text
const statusText = document.getElementById('statusText');
const isTracking = localStorage.getItem('gdpr-tracking-consent') === 'true';
statusText.textContent = isTracking ? t.statusEnhanced : t.statusMinimal;
// Update all text elements
const updates = {
'.privacy-badge span': t.gdprBadge,
'.hero h1': t.heroTitle,
'.hero .subtitle': t.heroSubtitle,
'.demo-info h2': '🎯 ' + t.howItWorks,
'.demo-info > p': t.howItWorksDesc,
'.feature:nth-child(1) strong': t.featurePrivacy,
'.feature:nth-child(1) small': t.featurePrivacyDesc,
'.feature:nth-child(2) strong': t.featureTracking,
'.feature:nth-child(2) small': t.featureTrackingDesc,
'.feature:nth-child(3) strong': t.featureControl,
'.feature:nth-child(3) small': t.featureControlDesc,
'.feature:nth-child(4) strong': t.featureCookies,
'.feature:nth-child(4) small': t.featureCookiesDesc,
'#consentModal h3': t.modalTitle,
'.consent-btn.secondary': t.modalNo,
'.consent-btn.primary': t.modalYes
};
Object.entries(updates).forEach(([selector, text]) => {
const element = document.querySelector(selector);
if (element) element.innerHTML = text;
});
// Update complex elements
const tryItOutP = document.querySelector('.demo-info p:last-of-type');
if (tryItOutP & & tryItOutP.querySelector('strong')) {
tryItOutP.innerHTML = `< strong > ${t.tryItOut}< / strong > ${t.tryItOutDesc}`;
}
const projectLink = document.querySelector('.demo-info > div:last-child p');
if (projectLink) {
projectLink.innerHTML = `💡 < strong > ${t.wantToUse}< / strong > < br > ${t.viewSource} < a href = "https://kode.naiv.no/olemd/gdpr" target = "_blank" rel = "noopener" style = "color: #667eea; text-decoration: none; font-weight: 600;" > kode.naiv.no/olemd/gdpr< / a > `;
}
const modalP = document.querySelector('#consentModal p:first-of-type');
if (modalP) {
modalP.innerHTML = t.modalDesc + '< br > < br > < strong > ' + t.modalWhatWeTrack + '< / strong > < br > ' + t.modalItem1 + '< br > ' + t.modalItem2 + '< br > ' + t.modalItem3 + '< br > ' + t.modalItem4;
}
const modalLastP = document.querySelector('#consentModal p:last-of-type');
if (modalLastP & & modalLastP.textContent.includes('change your mind')) {
modalLastP.textContent = t.modalCanChange;
}
}
2025-09-08 12:59:47 +02:00
class GDPRTrackingConsent {
constructor() {
this.storageKey = 'gdpr-tracking-consent';
2025-09-25 13:55:12 +02:00
this.achievementsKey = 'gdpr-achievements';
this.statsKey = 'gdpr-stats';
2025-09-08 12:59:47 +02:00
this.isTrackingEnabled = this.getConsentStatus();
2025-09-25 13:55:12 +02:00
this.achievements = this.loadAchievements();
2025-09-08 12:59:47 +02:00
this.init();
}
init() {
this.updateButtonState();
this.setupEventListeners();
2025-09-25 13:55:12 +02:00
this.updateCookieCounter();
this.updatePrivacyScore();
2025-09-08 12:59:47 +02:00
if (this.isTrackingEnabled) {
this.enableEnhancedTracking();
} else {
this.enableMinimalTracking();
2025-09-25 13:55:12 +02:00
this.checkPrivacyAchievements();
2025-09-08 12:59:47 +02:00
}
2025-09-25 13:55:12 +02:00
// Update cookie counter every 2 seconds
setInterval(() => this.updateCookieCounter(), 2000);
2025-09-08 12:59:47 +02:00
}
setupEventListeners() {
const btn = document.getElementById('trackingConsentBtn');
btn.addEventListener('click', () => {
if (this.isTrackingEnabled) {
this.showDisableConfirm();
} else {
this.showConsentModal();
}
});
const modal = document.getElementById('consentModal');
modal.addEventListener('click', (e) => {
if (e.target === modal) {
this.closeConsentModal();
}
});
}
getConsentStatus() {
const stored = localStorage.getItem(this.storageKey);
return stored === 'true';
}
setConsentStatus(status) {
localStorage.setItem(this.storageKey, status.toString());
this.isTrackingEnabled = status;
}
updateButtonState() {
const btn = document.getElementById('trackingConsentBtn');
const btnText = document.getElementById('btnText');
const statusText = document.getElementById('statusText');
const statusDot = document.getElementById('statusDot');
2025-09-08 13:14:50 +02:00
const t = translations[currentLang];
2025-09-08 12:59:47 +02:00
if (this.isTrackingEnabled) {
btn.classList.add('enabled');
2025-09-25 13:55:12 +02:00
btnText.textContent = t.buttonTextOn || 'Tracking ON';
statusText.textContent = t.trackingModeActive || '📊 Tracking Active - Thank You for Helping!';
2025-09-08 12:59:47 +02:00
statusDot.className = 'status-dot';
} else {
btn.classList.remove('enabled');
2025-09-25 13:55:12 +02:00
btnText.textContent = t.buttonText || 'Plz trac me!';
statusText.textContent = t.privacyModeActive || '🛡️ Privacy Mode Active - You\'re Protected!';
2025-09-08 12:59:47 +02:00
statusDot.className = 'status-dot minimal';
}
2025-09-25 13:55:12 +02:00
// Update other UI elements
this.updateCookieCounter();
this.updatePrivacyScore();
2025-09-08 12:59:47 +02:00
}
showConsentModal() {
document.getElementById('consentModal').style.display = 'flex';
}
closeConsentModal() {
document.getElementById('consentModal').style.display = 'none';
}
showDisableConfirm() {
2025-09-08 13:14:50 +02:00
const t = translations[currentLang];
if (confirm(t.confirmDisable)) {
2025-09-08 12:59:47 +02:00
this.disableTracking();
}
}
enableTracking() {
this.setConsentStatus(true);
this.updateButtonState();
this.closeConsentModal();
this.enableEnhancedTracking();
2025-09-08 13:14:50 +02:00
this.showNotification(translations[currentLang].notificationEnabled);
2025-09-08 12:59:47 +02:00
}
disableTracking() {
this.setConsentStatus(false);
this.updateButtonState();
this.enableMinimalTracking();
2025-09-08 13:14:50 +02:00
this.showNotification(translations[currentLang].notificationDisabled);
2025-09-08 12:59:47 +02:00
}
enableMinimalTracking() {
console.log('🔒 GDPR Mode: Minimal tracking enabled');
if (typeof gtag !== 'undefined') {
gtag('consent', 'update', {
'analytics_storage': 'denied',
'ad_storage': 'denied',
'personalization_storage': 'denied',
'functionality_storage': 'granted'
});
}
this.cleanupEnhancedCookies();
this.fireCustomEvent('trackingModeChanged', { mode: 'minimal' });
}
enableEnhancedTracking() {
console.log('📊 Enhanced tracking enabled');
if (typeof gtag !== 'undefined') {
gtag('consent', 'update', {
'analytics_storage': 'granted',
'ad_storage': 'granted',
'personalization_storage': 'granted',
'functionality_storage': 'granted'
});
}
this.initEnhancedAnalytics();
this.fireCustomEvent('trackingModeChanged', { mode: 'enhanced' });
}
initEnhancedAnalytics() {
if (typeof gtag !== 'undefined') {
gtag('event', 'enhanced_tracking_enabled', {
'event_category': 'privacy',
'event_label': 'user_opted_in'
});
}
}
cleanupEnhancedCookies() {
const cookiesToRemove = ['_ga', '_gid', '_gat', '_fbp', '_fbc'];
cookiesToRemove.forEach(cookie => {
document.cookie = `${cookie}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
document.cookie = `${cookie}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=.${window.location.hostname}`;
});
}
fireCustomEvent(eventName, data) {
const event = new CustomEvent(eventName, { detail: data });
document.dispatchEvent(event);
}
2025-09-25 13:55:12 +02:00
showNotification(message, isSuccess = true) {
2025-09-08 12:59:47 +02:00
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
2025-09-25 13:55:12 +02:00
background: ${isSuccess ? '#4caf50' : '#667eea'};
2025-09-08 12:59:47 +02:00
color: white;
padding: 12px 20px;
border-radius: 6px;
z-index: 1002;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 3000);
}
2025-09-25 13:55:12 +02:00
updateCookieCounter() {
const cookieCount = document.cookie.split(';').filter(c => c.trim()).length || 0;
const cookieText = document.getElementById('cookieText');
const cookieIcon = document.getElementById('cookieIcon');
const t = translations[currentLang];
if (cookieText) {
if (this.isTrackingEnabled) {
cookieText.textContent = `${cookieCount} ${t.cookiesTracking || 'cookies (with tracking)'}`;
cookieIcon.textContent = '📊';
} else {
cookieText.textContent = `${cookieCount} ${t.cookiesEssential || 'cookies (essential only)'}`;
cookieIcon.textContent = '🛡️';
}
}
}
updatePrivacyScore() {
const scoreText = document.getElementById('scoreText');
const privacyScore = document.getElementById('privacyScore');
const t = translations[currentLang];
if (scoreText & & privacyScore) {
if (this.isTrackingEnabled) {
scoreText.textContent = t.privacyScore0 || 'Privacy Score: 0%';
privacyScore.style.background = 'linear-gradient(135deg, #ff6b6b, #ff8787)';
} else {
scoreText.textContent = t.privacyScore100 || 'Privacy Score: 100%';
privacyScore.style.background = 'linear-gradient(135deg, #4caf50, #45a049)';
}
}
}
loadAchievements() {
const stored = localStorage.getItem(this.achievementsKey);
return stored ? JSON.parse(stored) : {};
}
saveAchievements() {
localStorage.setItem(this.achievementsKey, JSON.stringify(this.achievements));
}
unlockAchievement(achievementId, message) {
if (!this.achievements[achievementId]) {
this.achievements[achievementId] = {
unlockedAt: new Date().toISOString(),
message: message
};
this.saveAchievements();
this.showAchievement(message);
}
}
showAchievement(message) {
const achievement = document.createElement('div');
achievement.className = 'achievement-toast';
achievement.innerHTML = `
< span style = "font-size: 1.5em;" > 🏆< / span >
< span > ${message}< / span >
`;
document.body.appendChild(achievement);
setTimeout(() => {
achievement.style.animation = 'slideInRight 0.5s ease-out reverse';
setTimeout(() => achievement.remove(), 500);
}, 4000);
}
checkPrivacyAchievements() {
const t = translations[currentLang];
// Check for Privacy Champion achievement
if (!this.isTrackingEnabled) {
setTimeout(() => {
this.unlockAchievement(
'privacy_champion',
t.achievementPrivacyChampion || '🏆 Achievement Unlocked: Privacy Champion!'
);
}, 1000);
}
// Check for Data Minimalist achievement (if never enabled tracking)
const hasEverEnabledTracking = localStorage.getItem('has-enabled-tracking');
if (!hasEverEnabledTracking & & !this.isTrackingEnabled) {
setTimeout(() => {
this.unlockAchievement(
'data_minimalist',
t.achievementDataMinimalist || '💚 Achievement: Data Minimalist!'
);
}, 3000);
}
}
updatePrivacyStats() {
// Simulate privacy stats (in real app, this would come from server)
const stats = {
privacyUsers: 73,
totalUsers: 100
};
localStorage.setItem(this.statsKey, JSON.stringify(stats));
return stats;
}
2025-09-08 12:59:47 +02:00
}
function closeConsentModal() {
window.trackingConsent.closeConsentModal();
}
function enableTracking() {
2025-09-25 13:55:12 +02:00
// Mark that user has enabled tracking at least once
localStorage.setItem('has-enabled-tracking', 'true');
2025-09-08 12:59:47 +02:00
window.trackingConsent.enableTracking();
}
2025-09-25 13:55:12 +02:00
function choosePrivacy() {
const t = translations[currentLang];
window.trackingConsent.closeConsentModal();
window.trackingConsent.showNotification(
t.privacyChosen || '🎉 Excellent choice! Your privacy is protected and everything works perfectly!',
true
);
window.trackingConsent.checkPrivacyAchievements();
}
function showCookieDetails() {
const modal = document.getElementById('cookieDetailsModal');
const detailsList = document.getElementById('cookieDetailsList');
const cookieExplanations = {
'gdpr-tracking-consent': {
name: 'GDPR Tracking Consent',
type: '✅ Essential',
purpose: 'Remembers your privacy choice',
duration: 'Permanent',
details: 'This cookie stores whether you\'ve chosen to enable or disable tracking. It\'s essential for the site to respect your privacy preferences.'
},
'gdpr-language': {
name: 'Language Preference',
type: '✅ Essential',
purpose: 'Remembers your language choice',
duration: 'Permanent',
details: 'Stores your preferred language (English or Norwegian) so the site displays in your chosen language.'
},
'_ga': {
name: 'Google Analytics',
type: '📊 Tracking',
purpose: 'User identification',
duration: '2 years',
details: 'Used by Google Analytics to distinguish unique users. Only set when tracking is enabled.'
},
'_gid': {
name: 'Google Analytics ID',
type: '📊 Tracking',
purpose: 'Session tracking',
duration: '24 hours',
details: 'Used by Google Analytics to distinguish users and sessions. Only set when tracking is enabled.'
},
'_gat': {
name: 'Google Analytics Throttle',
type: '📊 Tracking',
purpose: 'Request rate limiting',
duration: '1 minute',
details: 'Used to throttle request rate to Google Analytics. Only set when tracking is enabled.'
}
};
// Get current cookies
const cookies = document.cookie.split(';').filter(c => c.trim());
let html = '< div style = "padding: 10px;" > ';
if (cookies.length === 0) {
html += '< p style = "text-align: center; color: #4caf50;" > < strong > 🎉 No cookies found!< / strong > < br > Your browser is completely cookie-free on this site.< / p > ';
} else {
html += '< p style = "margin-bottom: 15px;" > Currently active cookies:< / p > ';
cookies.forEach(cookie => {
const [name, value] = cookie.trim().split('=');
const info = cookieExplanations[name] || {
name: name,
type: '❓ Unknown',
purpose: 'Unknown purpose',
duration: 'Unknown',
details: 'This cookie is not recognized by our system.'
};
html += `
< div style = "margin-bottom: 15px; padding: 12px; background: #f8f9fa; border-radius: 8px; border-left: 3px solid ${info.type.includes('Essential') ? '#4caf50' : '#ff9800'};" >
< div style = "display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;" >
< strong > ${info.name}< / strong >
< span style = "font-size: 12px; padding: 2px 8px; background: white; border-radius: 10px;" > ${info.type}< / span >
< / div >
< div style = "font-size: 13px; color: #666;" >
< div > < strong > Purpose:< / strong > ${info.purpose}< / div >
< div > < strong > Duration:< / strong > ${info.duration}< / div >
< div style = "margin-top: 5px; font-style: italic;" > ${info.details}< / div >
< / div >
< / div >
`;
});
}
html += '< / div > ';
// Add educational note
html += `
< div style = "margin-top: 20px; padding: 15px; background: #e8f5e8; border-radius: 8px;" >
< p style = "margin: 0; font-size: 14px;" >
< strong > 💡 Did you know?< / strong > < br >
Essential cookies are necessary for basic site functions like remembering your preferences.
They don't track you or collect personal data. Tracking cookies are only set if you explicitly enable them!
< / p >
< / div >
`;
detailsList.innerHTML = html;
modal.style.display = 'flex';
}
function closeCookieDetails() {
document.getElementById('cookieDetailsModal').style.display = 'none';
}
2025-09-08 12:59:47 +02:00
document.addEventListener('DOMContentLoaded', () => {
2025-09-08 13:14:50 +02:00
// Initialize language
setLanguage(currentLang);
2025-09-08 12:59:47 +02:00
window.trackingConsent = new GDPRTrackingConsent();
});
document.addEventListener('trackingModeChanged', (e) => {
console.log('Tracking mode changed to:', e.detail.mode);
});
< / script >
< / body >
< / html >