Heatmap in Geraeterahmen (Computer/Tablet + Handy) und Keks statt "Datenschutz-Einstellungen"
Heatmap im Portal (Funktionen -> Analyse): die echte Website laeuft jetzt in realistischen Geraeterahmen, gross MacBook oder iPad (Umschalter oben), daneben das iPhone, jeweils in echter Breite (1512 / 820 / 402 px), verkleinert und im Geraet scrollbar, mit den Klicks der passenden Geraeteklasse als Overlay. MacBook und iPhone stammen aus den Rahmen der neuen Website (device-mockups), das iPad ist im gleichen Stil nachgebaut (public/geraete.css/.js). - heatmap-link: embed: true haengt __wk_embed=1 an; w.js reicht das als data-embed an h.js. - h.js eingebettet (nur in einem Rahmen): keine Leiste, Links und Formulare gesperrt (kein Seitenwechsel, also keine Zaehlung und kein Banner), Scrollleisten aus, Klickzahl per postMessage nur an die Herkunft von h.js (project.webklar.com). - iframe mit Sandbox allow-scripts allow-same-origin (keine Formulare, Pop-ups, keine Navigation des Portals); Laden erst, wenn die Heatmap in Sicht kommt. Laesst sich eine Website nicht einbetten, kommt nach 20 s ein Hinweis mit "In neuem Tab oeffnen" (alter Weg). Banner-Skript: der Einstellungsknopf nach der Wahl ist jetzt ein angeknabberter Keks unten rechts (Inline-SVG, 44 px). Er dreht sich langsam, haelt beim Darueberfahren/Fokus an und zeigt dann "Datenschutz-Einstellungen" (auch sein Name fuer Screenreader, passend zum Bannertext); bei reduzierter Bewegung dreht er sich nicht. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ff7fe35c06
commit
0a113c2983
14 files changed
+1658
-91
No files matched your search
Vendored
+56
-25
@@ -119,6 +119,8 @@ function escapeHtml(s) {
|
||||
}
|
||||
|
||||
// WEBklar Analyse: Heatmap-Overlay (wird von w.js nur mit gueltigem ?__wk_heat=Token geladen).
|
||||
// data-embed="1" (nur in einem Rahmen): Ansicht im Geraeterahmen des Kundenportals. Dann ohne Leiste,
|
||||
// Links und Formulare gesperrt, und die Klickzahl geht per postMessage nur an das Portal (Herkunft von h.js).
|
||||
|
||||
function main() {
|
||||
const me = document.currentScript;
|
||||
@@ -128,21 +130,13 @@ function main() {
|
||||
if (!token || !base) return;
|
||||
|
||||
const de = document.documentElement;
|
||||
const host = document.createElement('div');
|
||||
host.style.position = 'fixed'; host.style.left = '0'; host.style.right = '0'; host.style.top = '0'; host.style.zIndex = '2147483647';
|
||||
const root = host.attachShadow({ mode: 'open' });
|
||||
const style = document.createElement('style');
|
||||
style.textContent = ':host{all:initial}.bar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;background:#111;color:#fff;font:14px/1.4 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}button{font:600 14px/1 system-ui,sans-serif;padding:8px 14px;border:2px solid #fff;border-radius:6px;background:#fff;color:#111;cursor:pointer}button:focus-visible{outline:3px solid #1a73e8;outline-offset:2px}';
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'bar';
|
||||
const msg = document.createElement('span');
|
||||
msg.textContent = 'Heatmap wird geladen ...';
|
||||
const close = document.createElement('button');
|
||||
close.setAttribute('type', 'button');
|
||||
close.textContent = 'Schließen';
|
||||
bar.appendChild(msg); bar.appendChild(close);
|
||||
root.appendChild(style); root.appendChild(bar);
|
||||
document.body.appendChild(host);
|
||||
let portal = '';
|
||||
try { portal = new URL(base).origin; } catch (e) { /* ohne Portal keine Einbettung */ }
|
||||
const embed = !!portal && me.getAttribute('data-embed') === '1' && window.parent && window.parent !== window;
|
||||
function tell(state, count) {
|
||||
if (!embed) return;
|
||||
try { window.parent.postMessage({ wk: 'heat', state: state, count: count }, portal); } catch (e) { /* egal */ }
|
||||
}
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const cs = canvas.style;
|
||||
@@ -162,6 +156,38 @@ function main() {
|
||||
return g.getImageData(0, 0, 256, 1).data;
|
||||
})();
|
||||
|
||||
if (embed) {
|
||||
// Nur ansehen: kein Seitenwechsel (er wuerde zaehlen und das Banner zeigen)
|
||||
const block = function (e) {
|
||||
const t = e.target;
|
||||
if (e.type === 'submit' || (t && t.closest && t.closest('a,area'))) { e.preventDefault(); e.stopPropagation(); }
|
||||
};
|
||||
window.addEventListener('click', block, true);
|
||||
window.addEventListener('submit', block, true);
|
||||
// Handy und Tablet haben ueberlagernde Scrollleisten; so stimmt die Breite wie auf dem Geraet
|
||||
const st = document.createElement('style');
|
||||
st.textContent = 'html{scrollbar-width:none}html::-webkit-scrollbar{display:none}';
|
||||
(document.head || de).appendChild(st);
|
||||
start(function (n) { tell('ready', n); }, function () { tell('error', 0); });
|
||||
return;
|
||||
}
|
||||
|
||||
const host = document.createElement('div');
|
||||
host.style.position = 'fixed'; host.style.left = '0'; host.style.right = '0'; host.style.top = '0'; host.style.zIndex = '2147483647';
|
||||
const root = host.attachShadow({ mode: 'open' });
|
||||
const style = document.createElement('style');
|
||||
style.textContent = ':host{all:initial}.bar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;background:#111;color:#fff;font:14px/1.4 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}button{font:600 14px/1 system-ui,sans-serif;padding:8px 14px;border:2px solid #fff;border-radius:6px;background:#fff;color:#111;cursor:pointer}button:focus-visible{outline:3px solid #1a73e8;outline-offset:2px}';
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'bar';
|
||||
const msg = document.createElement('span');
|
||||
msg.textContent = 'Heatmap wird geladen ...';
|
||||
const close = document.createElement('button');
|
||||
close.setAttribute('type', 'button');
|
||||
close.textContent = 'Schließen';
|
||||
bar.appendChild(msg); bar.appendChild(close);
|
||||
root.appendChild(style); root.appendChild(bar);
|
||||
document.body.appendChild(host);
|
||||
|
||||
function draw() {
|
||||
const w = Math.max(de.scrollWidth, de.clientWidth, 1);
|
||||
const h = Math.max(de.scrollHeight, de.clientHeight, 1);
|
||||
@@ -202,17 +228,22 @@ function main() {
|
||||
host.remove(); canvas.remove();
|
||||
}
|
||||
close.addEventListener('click', stop);
|
||||
window.addEventListener('resize', draw);
|
||||
start(function (n) { msg.textContent = 'Heatmap – ' + n + ' Klicks (letzte 30 Tage)'; },
|
||||
function () { msg.textContent = 'Heatmap-Link abgelaufen oder ungültig'; });
|
||||
|
||||
fetch(base + 'heat?token=' + encodeURIComponent(token), { credentials: 'omit' })
|
||||
.then(function (r) { if (!r.ok) throw new Error('heat'); return r.json(); })
|
||||
.then(function (j) {
|
||||
points = (j && Array.isArray(j.points) ? j.points : []).filter(function (p) { return p && p.x >= 0 && p.x <= 1 && p.y >= 0 && p.y <= 1; });
|
||||
msg.textContent = 'Heatmap – ' + points.length + ' Klicks (letzte 30 Tage)';
|
||||
draw();
|
||||
timer = setInterval(redrawIfChanged, 1000);
|
||||
})
|
||||
.catch(function () { msg.textContent = 'Heatmap-Link abgelaufen oder ungültig'; });
|
||||
// Punkte laden, zeichnen und bei Groessenaenderung neu zeichnen
|
||||
function start(onReady, onError) {
|
||||
window.addEventListener('resize', draw);
|
||||
fetch(base + 'heat?token=' + encodeURIComponent(token), { credentials: 'omit' })
|
||||
.then(function (r) { if (!r.ok) throw new Error('heat'); return r.json(); })
|
||||
.then(function (j) {
|
||||
points = (j && Array.isArray(j.points) ? j.points : []).filter(function (p) { return p && p.x >= 0 && p.x <= 1 && p.y >= 0 && p.y <= 1; });
|
||||
draw();
|
||||
timer = setInterval(redrawIfChanged, 1000);
|
||||
onReady(points.length);
|
||||
})
|
||||
.catch(function () { onError(); });
|
||||
}
|
||||
}
|
||||
|
||||
try { main(); } catch (e) { /* nie die Seite stoeren */ }
|
||||
|
||||
Vendored
+36
-2
@@ -142,12 +142,16 @@ function main() {
|
||||
const qs = new URLSearchParams(WIN.location.search);
|
||||
const heatToken = qs.get('__wk_heat');
|
||||
if (heatToken) {
|
||||
// __wk_embed=1: Ansicht im Geraeterahmen des Kundenportals (h.js ohne Leiste, Links gesperrt)
|
||||
const embed = qs.get('__wk_embed') === '1';
|
||||
qs.delete('__wk_heat');
|
||||
qs.delete('__wk_embed');
|
||||
const rest = qs.toString();
|
||||
try { WIN.history.replaceState(null, '', WIN.location.pathname + (rest ? '?' + rest : '') + (WIN.location.hash || '')); } catch (e) { /* egal */ }
|
||||
const s = DOC.createElement('script');
|
||||
s.setAttribute('src', base + 'h.js');
|
||||
s.setAttribute('data-token', heatToken);
|
||||
if (embed) s.setAttribute('data-embed', '1');
|
||||
(DOC.head || DOC.documentElement).appendChild(s);
|
||||
return;
|
||||
}
|
||||
@@ -377,14 +381,44 @@ function main() {
|
||||
if (linkHostEl) linkHostEl.style.display = '';
|
||||
}
|
||||
|
||||
// Einstellungsknopf: angeknabberter Keks unten rechts, dreht sich langsam (nicht bei reduzierter Bewegung).
|
||||
// Beim Darueberfahren haelt er an und zeigt "Datenschutz-Einstellungen"; das ist auch sein Name fuer Screenreader.
|
||||
// Inline-SVG statt data:-Grafik, damit eine strenge CSP der Website (img-src) ihn nicht blockiert.
|
||||
const SETTINGS_CSS = ':host{all:initial}' +
|
||||
'.b{position:relative;display:block;width:44px;height:44px;padding:2px;margin:0;border:0;border-radius:50%;background:transparent;cursor:pointer;-webkit-tap-highlight-color:transparent}' +
|
||||
'.b:focus-visible{outline:3px solid #1a73e8;outline-offset:2px}' +
|
||||
'.c{display:block;width:40px;height:40px;filter:drop-shadow(0 2px 3px rgba(0,0,0,.35));animation:wk-spin 9s linear infinite}' +
|
||||
'.b:hover .c,.b:focus-visible .c{animation-play-state:paused}' +
|
||||
'.l{position:absolute;right:52px;top:50%;transform:translate(6px,-50%);padding:7px 12px;border-radius:999px;background:#fff;color:#222;box-shadow:0 2px 10px rgba(0,0,0,.18);font:600 13px/1 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s,transform .15s}' +
|
||||
'.b:hover .l,.b:focus-visible .l{opacity:1;transform:translate(0,-50%)}' +
|
||||
'@keyframes wk-spin{to{transform:rotate(360deg)}}' +
|
||||
'@media (prefers-reduced-motion:reduce){.c{animation:none}.l{transition:none}}';
|
||||
const SVG_NS = 'http://www.w3.org/2000/svg';
|
||||
function svgEl(tag, attrs) {
|
||||
const n = DOC.createElementNS(SVG_NS, tag);
|
||||
for (const k in attrs) n.setAttribute(k, attrs[k]);
|
||||
return n;
|
||||
}
|
||||
function cookieSvg() {
|
||||
const svg = svgEl('svg', { class: 'c', viewBox: '0 0 64 64', 'aria-hidden': 'true', focusable: 'false' });
|
||||
// Umriss mit Bissspur oben rechts (zwei Bissboegen, dann der grosse Kreis zurueck)
|
||||
svg.appendChild(svgEl('path', { d: 'M44.84 6A10 10 0 0 0 57.5 19.37A7.5 7.5 0 0 0 60.96 33.5A29 29 0 1 1 44.84 6Z', fill: '#dfa25b', stroke: '#a5652c', 'stroke-width': '3', 'stroke-linejoin': 'round' }));
|
||||
const chips = [[19, 22, 4.2, 3.3, -20], [31, 14, 3.2, 2.6, 15], [33, 33, 4, 3.2, 30], [46, 41, 3.6, 2.8, -35], [21, 45, 4, 3, 10], [36, 52, 3, 2.4, -10], [11, 34, 2.6, 2.1, 40], [47, 31, 2.4, 2, 0]];
|
||||
for (const c of chips) svg.appendChild(svgEl('ellipse', { cx: c[0], cy: c[1], rx: c[2], ry: c[3], transform: 'rotate(' + c[4] + ' ' + c[0] + ' ' + c[1] + ')', fill: '#5a3216' }));
|
||||
const crumbs = [[26, 28, 1.1], [40, 22, 1], [28, 40, 1.2], [14, 26, 0.9], [40, 46, 1]];
|
||||
for (const c of crumbs) svg.appendChild(svgEl('circle', { cx: c[0], cy: c[1], r: c[2], fill: '#f3c98c' }));
|
||||
return svg;
|
||||
}
|
||||
function addSettingsLink() {
|
||||
linkHostEl = DOC.createElement('div');
|
||||
const st = linkHostEl.style;
|
||||
st.position = 'fixed'; st.right = '12px'; st.bottom = '12px'; st.zIndex = '2147483646';
|
||||
hosts.push(linkHostEl);
|
||||
const root = linkHostEl.attachShadow({ mode: 'open' });
|
||||
root.appendChild(el('style', {}, ':host{all:initial}button{font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:13px;line-height:1;min-width:24px;min-height:32px;padding:8px 12px;border:1px solid #bbb;border-radius:6px;background:#fff;color:#444;cursor:pointer}button:focus-visible{outline:3px solid #1a73e8}'));
|
||||
const b = el('button', { type: 'button' }, SETTINGS_LABEL);
|
||||
root.appendChild(el('style', {}, SETTINGS_CSS));
|
||||
const b = el('button', { type: 'button', class: 'b', 'aria-label': SETTINGS_LABEL });
|
||||
b.appendChild(el('span', { class: 'l', 'aria-hidden': 'true' }, SETTINGS_LABEL));
|
||||
b.appendChild(cookieSvg());
|
||||
b.addEventListener('click', safe(function () { openBanner(true); }));
|
||||
root.appendChild(b);
|
||||
DOC.body.appendChild(linkHostEl);
|
||||
|
||||
+56
-25
@@ -1,4 +1,6 @@
|
||||
// WEBklar Analyse: Heatmap-Overlay (wird von w.js nur mit gueltigem ?__wk_heat=Token geladen).
|
||||
// data-embed="1" (nur in einem Rahmen): Ansicht im Geraeterahmen des Kundenportals. Dann ohne Leiste,
|
||||
// Links und Formulare gesperrt, und die Klickzahl geht per postMessage nur an das Portal (Herkunft von h.js).
|
||||
|
||||
function main() {
|
||||
const me = document.currentScript;
|
||||
@@ -8,21 +10,13 @@ function main() {
|
||||
if (!token || !base) return;
|
||||
|
||||
const de = document.documentElement;
|
||||
const host = document.createElement('div');
|
||||
host.style.position = 'fixed'; host.style.left = '0'; host.style.right = '0'; host.style.top = '0'; host.style.zIndex = '2147483647';
|
||||
const root = host.attachShadow({ mode: 'open' });
|
||||
const style = document.createElement('style');
|
||||
style.textContent = ':host{all:initial}.bar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;background:#111;color:#fff;font:14px/1.4 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}button{font:600 14px/1 system-ui,sans-serif;padding:8px 14px;border:2px solid #fff;border-radius:6px;background:#fff;color:#111;cursor:pointer}button:focus-visible{outline:3px solid #1a73e8;outline-offset:2px}';
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'bar';
|
||||
const msg = document.createElement('span');
|
||||
msg.textContent = 'Heatmap wird geladen ...';
|
||||
const close = document.createElement('button');
|
||||
close.setAttribute('type', 'button');
|
||||
close.textContent = 'Schließen';
|
||||
bar.appendChild(msg); bar.appendChild(close);
|
||||
root.appendChild(style); root.appendChild(bar);
|
||||
document.body.appendChild(host);
|
||||
let portal = '';
|
||||
try { portal = new URL(base).origin; } catch (e) { /* ohne Portal keine Einbettung */ }
|
||||
const embed = !!portal && me.getAttribute('data-embed') === '1' && window.parent && window.parent !== window;
|
||||
function tell(state, count) {
|
||||
if (!embed) return;
|
||||
try { window.parent.postMessage({ wk: 'heat', state: state, count: count }, portal); } catch (e) { /* egal */ }
|
||||
}
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const cs = canvas.style;
|
||||
@@ -42,6 +36,38 @@ function main() {
|
||||
return g.getImageData(0, 0, 256, 1).data;
|
||||
})();
|
||||
|
||||
if (embed) {
|
||||
// Nur ansehen: kein Seitenwechsel (er wuerde zaehlen und das Banner zeigen)
|
||||
const block = function (e) {
|
||||
const t = e.target;
|
||||
if (e.type === 'submit' || (t && t.closest && t.closest('a,area'))) { e.preventDefault(); e.stopPropagation(); }
|
||||
};
|
||||
window.addEventListener('click', block, true);
|
||||
window.addEventListener('submit', block, true);
|
||||
// Handy und Tablet haben ueberlagernde Scrollleisten; so stimmt die Breite wie auf dem Geraet
|
||||
const st = document.createElement('style');
|
||||
st.textContent = 'html{scrollbar-width:none}html::-webkit-scrollbar{display:none}';
|
||||
(document.head || de).appendChild(st);
|
||||
start(function (n) { tell('ready', n); }, function () { tell('error', 0); });
|
||||
return;
|
||||
}
|
||||
|
||||
const host = document.createElement('div');
|
||||
host.style.position = 'fixed'; host.style.left = '0'; host.style.right = '0'; host.style.top = '0'; host.style.zIndex = '2147483647';
|
||||
const root = host.attachShadow({ mode: 'open' });
|
||||
const style = document.createElement('style');
|
||||
style.textContent = ':host{all:initial}.bar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;background:#111;color:#fff;font:14px/1.4 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}button{font:600 14px/1 system-ui,sans-serif;padding:8px 14px;border:2px solid #fff;border-radius:6px;background:#fff;color:#111;cursor:pointer}button:focus-visible{outline:3px solid #1a73e8;outline-offset:2px}';
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'bar';
|
||||
const msg = document.createElement('span');
|
||||
msg.textContent = 'Heatmap wird geladen ...';
|
||||
const close = document.createElement('button');
|
||||
close.setAttribute('type', 'button');
|
||||
close.textContent = 'Schließen';
|
||||
bar.appendChild(msg); bar.appendChild(close);
|
||||
root.appendChild(style); root.appendChild(bar);
|
||||
document.body.appendChild(host);
|
||||
|
||||
function draw() {
|
||||
const w = Math.max(de.scrollWidth, de.clientWidth, 1);
|
||||
const h = Math.max(de.scrollHeight, de.clientHeight, 1);
|
||||
@@ -82,17 +108,22 @@ function main() {
|
||||
host.remove(); canvas.remove();
|
||||
}
|
||||
close.addEventListener('click', stop);
|
||||
window.addEventListener('resize', draw);
|
||||
start(function (n) { msg.textContent = 'Heatmap – ' + n + ' Klicks (letzte 30 Tage)'; },
|
||||
function () { msg.textContent = 'Heatmap-Link abgelaufen oder ungültig'; });
|
||||
|
||||
fetch(base + 'heat?token=' + encodeURIComponent(token), { credentials: 'omit' })
|
||||
.then(function (r) { if (!r.ok) throw new Error('heat'); return r.json(); })
|
||||
.then(function (j) {
|
||||
points = (j && Array.isArray(j.points) ? j.points : []).filter(function (p) { return p && p.x >= 0 && p.x <= 1 && p.y >= 0 && p.y <= 1; });
|
||||
msg.textContent = 'Heatmap – ' + points.length + ' Klicks (letzte 30 Tage)';
|
||||
draw();
|
||||
timer = setInterval(redrawIfChanged, 1000);
|
||||
})
|
||||
.catch(function () { msg.textContent = 'Heatmap-Link abgelaufen oder ungültig'; });
|
||||
// Punkte laden, zeichnen und bei Groessenaenderung neu zeichnen
|
||||
function start(onReady, onError) {
|
||||
window.addEventListener('resize', draw);
|
||||
fetch(base + 'heat?token=' + encodeURIComponent(token), { credentials: 'omit' })
|
||||
.then(function (r) { if (!r.ok) throw new Error('heat'); return r.json(); })
|
||||
.then(function (j) {
|
||||
points = (j && Array.isArray(j.points) ? j.points : []).filter(function (p) { return p && p.x >= 0 && p.x <= 1 && p.y >= 0 && p.y <= 1; });
|
||||
draw();
|
||||
timer = setInterval(redrawIfChanged, 1000);
|
||||
onReady(points.length);
|
||||
})
|
||||
.catch(function () { onError(); });
|
||||
}
|
||||
}
|
||||
|
||||
try { main(); } catch (e) { /* nie die Seite stoeren */ }
|
||||
+36
-2
@@ -23,12 +23,16 @@ function main() {
|
||||
const qs = new URLSearchParams(WIN.location.search);
|
||||
const heatToken = qs.get('__wk_heat');
|
||||
if (heatToken) {
|
||||
// __wk_embed=1: Ansicht im Geraeterahmen des Kundenportals (h.js ohne Leiste, Links gesperrt)
|
||||
const embed = qs.get('__wk_embed') === '1';
|
||||
qs.delete('__wk_heat');
|
||||
qs.delete('__wk_embed');
|
||||
const rest = qs.toString();
|
||||
try { WIN.history.replaceState(null, '', WIN.location.pathname + (rest ? '?' + rest : '') + (WIN.location.hash || '')); } catch (e) { /* egal */ }
|
||||
const s = DOC.createElement('script');
|
||||
s.setAttribute('src', base + 'h.js');
|
||||
s.setAttribute('data-token', heatToken);
|
||||
if (embed) s.setAttribute('data-embed', '1');
|
||||
(DOC.head || DOC.documentElement).appendChild(s);
|
||||
return;
|
||||
}
|
||||
@@ -258,14 +262,44 @@ function main() {
|
||||
if (linkHostEl) linkHostEl.style.display = '';
|
||||
}
|
||||
|
||||
// Einstellungsknopf: angeknabberter Keks unten rechts, dreht sich langsam (nicht bei reduzierter Bewegung).
|
||||
// Beim Darueberfahren haelt er an und zeigt "Datenschutz-Einstellungen"; das ist auch sein Name fuer Screenreader.
|
||||
// Inline-SVG statt data:-Grafik, damit eine strenge CSP der Website (img-src) ihn nicht blockiert.
|
||||
const SETTINGS_CSS = ':host{all:initial}' +
|
||||
'.b{position:relative;display:block;width:44px;height:44px;padding:2px;margin:0;border:0;border-radius:50%;background:transparent;cursor:pointer;-webkit-tap-highlight-color:transparent}' +
|
||||
'.b:focus-visible{outline:3px solid #1a73e8;outline-offset:2px}' +
|
||||
'.c{display:block;width:40px;height:40px;filter:drop-shadow(0 2px 3px rgba(0,0,0,.35));animation:wk-spin 9s linear infinite}' +
|
||||
'.b:hover .c,.b:focus-visible .c{animation-play-state:paused}' +
|
||||
'.l{position:absolute;right:52px;top:50%;transform:translate(6px,-50%);padding:7px 12px;border-radius:999px;background:#fff;color:#222;box-shadow:0 2px 10px rgba(0,0,0,.18);font:600 13px/1 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s,transform .15s}' +
|
||||
'.b:hover .l,.b:focus-visible .l{opacity:1;transform:translate(0,-50%)}' +
|
||||
'@keyframes wk-spin{to{transform:rotate(360deg)}}' +
|
||||
'@media (prefers-reduced-motion:reduce){.c{animation:none}.l{transition:none}}';
|
||||
const SVG_NS = 'http://www.w3.org/2000/svg';
|
||||
function svgEl(tag, attrs) {
|
||||
const n = DOC.createElementNS(SVG_NS, tag);
|
||||
for (const k in attrs) n.setAttribute(k, attrs[k]);
|
||||
return n;
|
||||
}
|
||||
function cookieSvg() {
|
||||
const svg = svgEl('svg', { class: 'c', viewBox: '0 0 64 64', 'aria-hidden': 'true', focusable: 'false' });
|
||||
// Umriss mit Bissspur oben rechts (zwei Bissboegen, dann der grosse Kreis zurueck)
|
||||
svg.appendChild(svgEl('path', { d: 'M44.84 6A10 10 0 0 0 57.5 19.37A7.5 7.5 0 0 0 60.96 33.5A29 29 0 1 1 44.84 6Z', fill: '#dfa25b', stroke: '#a5652c', 'stroke-width': '3', 'stroke-linejoin': 'round' }));
|
||||
const chips = [[19, 22, 4.2, 3.3, -20], [31, 14, 3.2, 2.6, 15], [33, 33, 4, 3.2, 30], [46, 41, 3.6, 2.8, -35], [21, 45, 4, 3, 10], [36, 52, 3, 2.4, -10], [11, 34, 2.6, 2.1, 40], [47, 31, 2.4, 2, 0]];
|
||||
for (const c of chips) svg.appendChild(svgEl('ellipse', { cx: c[0], cy: c[1], rx: c[2], ry: c[3], transform: 'rotate(' + c[4] + ' ' + c[0] + ' ' + c[1] + ')', fill: '#5a3216' }));
|
||||
const crumbs = [[26, 28, 1.1], [40, 22, 1], [28, 40, 1.2], [14, 26, 0.9], [40, 46, 1]];
|
||||
for (const c of crumbs) svg.appendChild(svgEl('circle', { cx: c[0], cy: c[1], r: c[2], fill: '#f3c98c' }));
|
||||
return svg;
|
||||
}
|
||||
function addSettingsLink() {
|
||||
linkHostEl = DOC.createElement('div');
|
||||
const st = linkHostEl.style;
|
||||
st.position = 'fixed'; st.right = '12px'; st.bottom = '12px'; st.zIndex = '2147483646';
|
||||
hosts.push(linkHostEl);
|
||||
const root = linkHostEl.attachShadow({ mode: 'open' });
|
||||
root.appendChild(el('style', {}, ':host{all:initial}button{font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:13px;line-height:1;min-width:24px;min-height:32px;padding:8px 12px;border:1px solid #bbb;border-radius:6px;background:#fff;color:#444;cursor:pointer}button:focus-visible{outline:3px solid #1a73e8}'));
|
||||
const b = el('button', { type: 'button' }, SETTINGS_LABEL);
|
||||
root.appendChild(el('style', {}, SETTINGS_CSS));
|
||||
const b = el('button', { type: 'button', class: 'b', 'aria-label': SETTINGS_LABEL });
|
||||
b.appendChild(el('span', { class: 'l', 'aria-hidden': 'true' }, SETTINGS_LABEL));
|
||||
b.appendChild(cookieSvg());
|
||||
b.addEventListener('click', safe(function () { openBanner(true); }));
|
||||
root.appendChild(b);
|
||||
DOC.body.appendChild(linkHostEl);
|
||||
|
||||
Reference in new issue
Block a user