Files
Webklar-Kundenbereich/analytics/client/h.js
T

99 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// WEBklar Analyse: Heatmap-Overlay (wird von w.js nur mit gueltigem ?__wk_heat=Token geladen).
function main() {
const me = document.currentScript;
const token = me && me.getAttribute('data-token');
const src = String((me && (me.src || me.getAttribute('src'))) || '').replace(/[?#].*$/, '');
const base = src.replace(/[^/]*$/, '');
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);
const canvas = document.createElement('canvas');
const cs = canvas.style;
cs.position = 'absolute'; cs.left = '0'; cs.top = '0'; cs.pointerEvents = 'none'; cs.zIndex = '2147483646';
document.body.appendChild(canvas);
let points = [];
let lastW = 0, lastH = 0, timer = 0;
// Farbverlauf: transparent -> blau -> gruen -> gelb -> rot
const ramp = (function () {
const c = document.createElement('canvas'); c.width = 256; c.height = 1;
const g = c.getContext('2d');
const lg = g.createLinearGradient(0, 0, 256, 0);
lg.addColorStop(0.25, 'rgb(0,0,255)'); lg.addColorStop(0.55, 'rgb(0,255,0)'); lg.addColorStop(0.85, 'rgb(255,255,0)'); lg.addColorStop(1, 'rgb(255,0,0)');
g.fillStyle = lg; g.fillRect(0, 0, 256, 1);
return g.getImageData(0, 0, 256, 1).data;
})();
function draw() {
const w = Math.max(de.scrollWidth, de.clientWidth, 1);
const h = Math.max(de.scrollHeight, de.clientHeight, 1);
lastW = w; lastH = h;
// Zeichenflaeche begrenzen (Speicher): hoechstens ca. 4 Mio. Pixel
const scale = Math.min(1, Math.sqrt(4e6 / (w * h)));
const cw = Math.max(1, Math.floor(w * scale)), ch = Math.max(1, Math.floor(h * scale));
canvas.width = cw; canvas.height = ch;
cs.width = w + 'px'; cs.height = h + 'px';
const g = canvas.getContext('2d');
g.clearRect(0, 0, cw, ch);
const r = Math.max(6, 30 * scale);
for (const p of points) {
const x = p.x * cw, y = p.y * ch;
const rg = g.createRadialGradient(x, y, 0, x, y, r);
rg.addColorStop(0, 'rgba(0,0,0,0.25)'); rg.addColorStop(1, 'rgba(0,0,0,0)');
g.fillStyle = rg; g.fillRect(x - r, y - r, r * 2, r * 2);
}
// Deckkraft-Stufen in Farben umsetzen
const img = g.getImageData(0, 0, cw, ch);
const d = img.data;
for (let i = 3; i < d.length; i += 4) {
const a = d[i];
if (!a) continue;
const k = Math.min(255, a * 3) * 4;
d[i - 3] = ramp[k]; d[i - 2] = ramp[k + 1]; d[i - 1] = ramp[k + 2];
d[i] = Math.min(200, 60 + a);
}
g.putImageData(img, 0, 0);
}
function redrawIfChanged() {
if (Math.max(de.scrollWidth, de.clientWidth) !== lastW || Math.max(de.scrollHeight, de.clientHeight) !== lastH) draw();
}
function stop() {
clearInterval(timer);
window.removeEventListener('resize', draw);
host.remove(); canvas.remove();
}
close.addEventListener('click', stop);
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; });
msg.textContent = 'Heatmap – ' + points.length + ' Klicks (letzte 30 Tage)';
draw();
timer = setInterval(redrawIfChanged, 1000);
})
.catch(function () { msg.textContent = 'Heatmap-Link abgelaufen oder ungültig'; });
}
try { main(); } catch (e) { /* nie die Seite stoeren */ }