Usa screenshot di pagina come sfondo heatmap al posto dell’iframe.

Cattura periodica client-side (con consenso), storage ActiveStorage e overlay allineato all’immagine.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-20 23:14:47 +02:00
co-authored by Cursor
parent 406a90ae5b
commit 70fccc493e
25 changed files with 442 additions and 73 deletions
+84
View File
@@ -3,12 +3,14 @@
"use strict";
var ENDPOINT = "/analytics/events";
var SNAPSHOT_ENDPOINT = "/analytics/snapshot";
var STORAGE_KEY = "mltv_cookie_consent";
var COOKIE_NAME = "mltv_cookie_consent";
var FLUSH_MS = 6000;
var MAX_QUEUE = 60;
var MOVE_THROTTLE_MS = 180;
var MOVE_GRID = 40;
var SNAPSHOT_DELAY_MS = 4000;
var queue = [];
var moveBuckets = {};
@@ -195,6 +197,87 @@
}, FLUSH_MS);
}
function snapshotStorageKey() {
return "mltv_snap:" + location.pathname + ":" + deviceBucket();
}
function todayKey() {
var d = new Date();
return d.getUTCFullYear() + "-" + (d.getUTCMonth() + 1) + "-" + d.getUTCDate();
}
function alreadyCapturedToday() {
try {
return localStorage.getItem(snapshotStorageKey()) === todayKey();
} catch (e) {
return false;
}
}
function markCapturedToday() {
try {
localStorage.setItem(snapshotStorageKey(), todayKey());
} catch (e) { /* ignore */ }
}
function loadHtml2Canvas(cb) {
if (window.html2canvas) {
cb(window.html2canvas);
return;
}
var s = document.createElement("script");
s.src = "/vendor/html2canvas.min.js";
s.async = true;
s.onload = function () {
if (window.html2canvas) cb(window.html2canvas);
};
s.onerror = function () { /* ignore */ };
document.head.appendChild(s);
}
function captureSnapshot() {
if (!hasAnalyticsConsent() || alreadyCapturedToday()) return;
loadHtml2Canvas(function (html2canvas) {
var size = pageSize();
html2canvas(document.documentElement, {
scale: Math.min(1, 1400 / Math.max(size.width, 1)),
useCORS: true,
allowTaint: false,
logging: false,
windowWidth: size.width,
windowHeight: size.height,
scrollX: 0,
scrollY: 0
})
.then(function (canvas) {
if (!canvas || !canvas.toBlob) return;
canvas.toBlob(
function (blob) {
if (!blob || blob.size < 8000 || blob.size > 1800000) return;
var fd = new FormData();
fd.append("image", blob, "snapshot.jpg");
fd.append("path", location.pathname);
fd.append("device", deviceBucket());
fd.append("width", String(Math.round(size.width)));
fd.append("height", String(Math.round(size.height)));
fetch(SNAPSHOT_ENDPOINT, {
method: "POST",
body: fd,
credentials: "same-origin"
})
.then(function (res) {
if (res.ok) markCapturedToday();
})
.catch(function () { /* ignore */ });
},
"image/jpeg",
0.72
);
})
.catch(function () { /* ignore */ });
});
}
function start() {
if (started) return;
if (excludedPath(location.pathname)) return;
@@ -216,6 +299,7 @@
flush(true);
});
scheduleFlush();
setTimeout(captureSnapshot, SNAPSHOT_DELAY_MS);
}
window.mltvSiteAnalyticsStart = start;