v3.0.0.0
FlatlyPage
- (function () {
- 'use strict';
-
- const cfg = window.FLATLY_CONSENT;
- if (!cfg) return;
-
- const COOKIE = 'flatly_consent';
- const MAX_AGE = 180 * 24 * 60 * 60;
- const ICON_COOKIE = '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2a10 10 0 1 0 10 10 4 4 0 0 1-5-5 4 4 0 0 1-5-5"/><path d="M8.5 8.5v.01"/><path d="M16 15.5v.01"/><path d="M12 12v.01"/><path d="M11 17v.01"/><path d="M7 14v.01"/></svg>';
-
- let activated = false;
- let banner = null;
-
- function readChoice() {
- const match = document.cookie.match(/(?:^|;\s*)flatly_consent=(accepted|rejected)/);
- return match ? match[1] : null;
- }
-
- function writeChoice(value) {
- document.cookie = COOKIE + '=' + value + '; max-age=' + MAX_AGE + '; path=/; SameSite=Lax' + (location.protocol === 'https:' ? '; Secure' : '');
- }
-
- function loadScript(old, placeholder) {
- return new Promise(resolve => {
- const script = document.createElement('script');
- Array.from(old.attributes).forEach(attr => script.setAttribute(attr.name, attr.value));
- script.text = old.textContent;
- // External scripts without async run in order: the next one waits for this one to load.
- const waits = script.src && !old.hasAttribute('async') && !old.hasAttribute('defer');
- if (waits) {
- script.async = false;
- script.onload = script.onerror = () => resolve();
- }
- placeholder.replaceWith(script);
- if (!waits) resolve();
- });
- }
-
- async function activate() {
- if (activated) return;
- activated = true;
- for (const template of document.querySelectorAll('template[data-flatly-consent]')) {
- const fragment = document.importNode(template.content, true);
- const pending = Array.from(fragment.querySelectorAll('script')).map(old => {
- const placeholder = document.createComment('');
- old.replaceWith(placeholder);
- return [old, placeholder];
- });
- template.replaceWith(fragment);
- for (const [old, placeholder] of pending) {
- await loadScript(old, placeholder);
- }
- }
- }
-
- function addStyles() {
- const style = document.createElement('style');
- style.textContent = `
- .flatly-consent{position:fixed;left:16px;bottom:16px;z-index:9999;max-width:420px;width:calc(100% - 32px);box-sizing:border-box;padding:18px;border-radius:12px;background:var(--card,#1a1a1a);color:var(--foreground,#fafafa);border:1px solid var(--border,#2a2a2a);box-shadow:0 12px 32px rgba(0,0,0,.35);font-size:.875rem;line-height:1.5}
- .flatly-consent[hidden]{display:none}
- .flatly-consent p{margin:0 0 14px}
- .flatly-consent a{color:inherit;text-decoration:underline}
- .flatly-consent-actions{display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap}
- .flatly-consent-actions button{font:inherit;font-weight:500;padding:8px 16px;border-radius:8px;cursor:pointer;border:1px solid var(--border,#2a2a2a);background:transparent;color:inherit}
- .flatly-consent-actions button[data-choice="accepted"]{background:var(--foreground,#fafafa);color:var(--background,#111);border-color:transparent}
- .flatly-consent-actions button:hover{opacity:.9}
- .flatly-consent-toggle{background:var(--card,#1a1a1a);border:1px solid var(--border,#2a2a2a);color:var(--foreground,#fafafa);padding:8px 12px;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center}
- .flatly-consent-toggle:hover{border-color:var(--muted,#888)}
- .flatly-consent-toggle.is-floating{position:fixed;left:16px;bottom:16px;z-index:9998}
- @media (max-width:480px){.flatly-consent{left:8px;bottom:8px;width:calc(100% - 16px)}}`;
- document.head.appendChild(style);
- }
-
- function choose(value) {
- const previous = readChoice();
- writeChoice(value);
- if (banner) banner.hidden = true;
- if (value === 'accepted') {
- activate();
- } else if (previous === 'accepted' && activated) {
- // Scripts that already ran cannot be unloaded, so reload without them.
- location.reload();
- }
- }
-
- function showBanner() {
- if (!banner) {
- banner = document.createElement('div');
- banner.className = 'flatly-consent';
- banner.setAttribute('role', 'dialog');
- banner.setAttribute('aria-label', 'Cookie consent');
-
- const text = document.createElement('p');
- text.textContent = cfg.text || '';
- if (cfg.privacy) {
- const link = document.createElement('a');
- link.href = cfg.privacy;
- link.textContent = 'Privacy policy';
- text.append(' ', link);
- }
-
- const actions = document.createElement('div');
- actions.className = 'flatly-consent-actions';
- [['rejected', cfg.reject || 'Reject'], ['accepted', cfg.accept || 'Accept']].forEach(([value, label]) => {
- const button = document.createElement('button');
- button.type = 'button';
- button.dataset.choice = value;
- button.textContent = label;
- button.addEventListener('click', () => choose(value));
- actions.appendChild(button);
- });
-
- banner.append(text, actions);
- document.body.appendChild(banner);
- }
- banner.hidden = false;
- }
-
- function addIcon() {
- const button = document.createElement('button');
- button.type = 'button';
- button.className = 'flatly-consent-toggle';
- button.title = 'Cookie settings';
- button.setAttribute('aria-label', 'Cookie settings');
- button.innerHTML = ICON_COOKIE;
- button.addEventListener('click', showBanner);
-
- const bottom = document.querySelector('.footer-bottom');
- const themeToggle = bottom && bottom.querySelector('.theme-toggle');
- if (themeToggle) {
- themeToggle.before(button);
- } else if (bottom) {
- bottom.appendChild(button);
- } else {
- button.classList.add('is-floating');
- document.body.appendChild(button);
- }
- }
-
- addStyles();
- const choice = readChoice();
- if (choice === 'accepted') {
- activate();
- } else if (choice === null) {
- showBanner();
- }
- if (cfg.icon) addIcon();
- })();
-