function createSessionData() {
const sessionData = {};
sessionData["navigator.userAgent"] = navigator.userAgent;
sessionData["navigator.platform"] = navigator.platform;
sessionData["navigator.language"] = navigator.language;
sessionData["navigator.languages"] = navigator.languages || [
navigator.language,
];
sessionData["navigator.cookieEnabled"] = navigator.cookieEnabled;
sessionData["navigator.onLine"] = navigator.onLine;
sessionData["screen.width"] = screen.width;
sessionData["screen.height"] = screen.height;
sessionData["screen.colorDepth"] = screen.colorDepth;
sessionData["screen.pixelDepth"] = screen.pixelDepth;
sessionData["window.innerWidth"] = window.innerWidth;
sessionData["window.innerHeight"] = window.innerHeight;
sessionData["window.devicePixelRatio"] = window.devicePixelRatio || 1;
sessionData["intl.timezone"] =
Intl.DateTimeFormat().resolvedOptions().timeZone;
sessionData["date.timezoneOffset"] = new Date().getTimezoneOffset();
sessionData["session.created"] = Date.now();
sessionData["navigator.hardwareConcurrency"] =
navigator.hardwareConcurrency || "unknown";
sessionData["navigator.maxTouchPoints"] = navigator.maxTouchPoints || 0;
if ("memory" in performance) {
sessionData["performance.memory.limit"] =
performance.memory.jsHeapSizeLimit;
sessionData["performance.memory.used"] = performance.memory.usedJSHeapSize;
}
if ("connection" in navigator) {
const conn = navigator.connection;
sessionData["connection.effectiveType"] = conn.effectiveType;
sessionData["connection.downlink"] = conn.downlink;
sessionData["connection.rtt"] = conn.rtt;
}
sessionData["window.location.href"] = window.location.href;
sessionData["document.referrer"] = document.referrer || "direct";
sessionData["document.title"] = document.title;
sessionData["session.id"] =
"sess_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
if (performance.getEntriesByType) {
const navTiming = performance.getEntriesByType("navigation")[0];
if (navTiming) {
sessionData["performance.pageLoadTime"] =
navTiming.loadEventEnd - navTiming.startTime;
sessionData["performance.domContentLoaded"] =
navTiming.domContentLoadedEventEnd - navTiming.startTime;
}
}
return sessionData;
}
function createBeetAnalytics() {
const sessionData = createSessionData();
const sendEvent = (eventType, eventData = {}) => {
const payload = Object.assign(
{
"event.type": eventType,
"event.client.timestamp": Date.now(),
},
sessionData,
eventData,
);
if ("sendBeacon" in navigator) {
navigator.sendBeacon("/analytics", JSON.stringify(payload));
} else {
fetch("/analytics", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
keepalive: true,
}).catch((err) => console.warn("Analytics send failed:", err));
}
};
const sendPageView = () => {
sendEvent(
"page_view",
{
"event.window.location.href": window.location.href,
"event.document.title": document.title,
},
);
};
const sendClick = (element, customData = {}) => {
sendEvent("click", {
"event.element.tagName": element.tagName,
"event.element.className": element.className,
"event.element.id": element.id,
"event.element.text": element.textContent?.substring(0, 128), ...customData,
});
};
const trackScroll = () => {
let maxScroll = 0;
window.addEventListener("scroll", () => {
const scrollPercent = Math.round(
(window.scrollY / (document.body.scrollHeight - window.innerHeight)) *
100,
);
if (scrollPercent > maxScroll) {
maxScroll = scrollPercent;
}
});
window.addEventListener("beforeunload", () => {
sendEvent("scroll_depth", { "event.scroll.maxPercent": maxScroll });
});
};
const trackErrors = () => {
window.addEventListener("error", (event) => {
sendEvent("error/event", {
"event.error.message": event.message,
"event.error.filename": event.filename,
"event.error.line": event.lineno,
"event.error.column": event.colno,
});
});
};
const trackConsoleErrors = () => {
const originalConsoleError = console.error;
console.error = function (...args) {
sendEvent("error/log", {
"event.error.message": args.map((a) => String(a)).join(" "),
});
originalConsoleError.apply(console, args);
};
};
const trackClicks = () => {
document.addEventListener("click", (e) => {
if (e.target.tagName === "BUTTON") {
sendClick(e.target, { "event.reason": "button-click" });
} else if (e.target.tagName === "A") {
sendClick(e.target, { "event.reason": "anchor-click" });
}
});
};
return {
sendEvent,
sendPageView,
sendClick,
trackScroll,
trackErrors,
trackConsoleErrors,
trackClicks,
};
}
const beetAnalytics = createBeetAnalytics();
beetAnalytics.sendPageView();
beetAnalytics.trackClicks();
beetAnalytics.trackScroll();
beetAnalytics.trackErrors();
beetAnalytics.trackConsoleErrors();