nordabiz/static/js/analytics-tracker.min.js
Maciej Pienczyn 847ecd00fa perf: minify JS + inline CSS (-24KB total)
- analytics-tracker.min.js: 12.5KB → 7.5KB (-5KB)
- Inline CSS in base.html minified: -19.3KB (whitespace, comments, semicolons)
- Total page weight reduced by ~24KB

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 18:32:11 +02:00

243 lines
7.4 KiB
JavaScript

(function() {
'use strict';
const HEARTBEAT_INTERVAL = 60000;
const SCROLL_DEBOUNCE_MS = 500;
const TRACK_ENDPOINT = '/api/analytics/track';
const HEARTBEAT_ENDPOINT = '/api/analytics/heartbeat';
const SCROLL_ENDPOINT = '/api/analytics/scroll';
const ERROR_ENDPOINT = '/api/analytics/error';
const PERFORMANCE_ENDPOINT = '/api/analytics/performance';
let pageStartTime = Date.now();
let currentPageViewId = null;
let maxScrollDepth = 0;
let scrollTimeout = null;
function init() {
const pageViewMeta = document.querySelector('meta[name="page-view-id"]');
if (pageViewMeta && pageViewMeta.content) {
currentPageViewId = parseInt(pageViewMeta.content, 10);
}
setInterval(sendHeartbeat, HEARTBEAT_INTERVAL);
document.addEventListener('click', handleClick, true);
window.addEventListener('beforeunload', handleUnload);
document.addEventListener('visibilitychange', handleVisibilityChange);
window.addEventListener('scroll', handleScroll, { passive: true });
window.onerror = handleError;
window.addEventListener('unhandledrejection', handlePromiseRejection);
if (document.readyState === 'complete') {
trackPerformance();
} else {
window.addEventListener('load', function() {
setTimeout(trackPerformance, 100);
});
}
trackContactClicks();
}
function handleClick(e) {
const target = e.target.closest('a, button, [data-track], input[type="submit"], .clickable, .company-card, nav a');
if (!target) return;
const data = {
type: 'click',
page_view_id: currentPageViewId,
element_type: getElementType(target, e),
element_id: target.id || null,
element_text: (target.textContent || '').trim().substring(0, 100) || null,
element_class: target.className || null,
target_url: target.href || target.closest('a')?.href || null,
x: e.clientX,
y: e.clientY
};
sendTracking(data);
}
function getElementType(target, e) {
if (target.closest('nav')) return 'nav';
if (target.closest('.company-card')) return 'company_card';
if (target.closest('form')) return 'form';
if (target.closest('.sidebar')) return 'sidebar';
if (target.closest('.dropdown')) return 'dropdown';
if (target.closest('.modal')) return 'modal';
if (target.tagName === 'A') return 'link';
if (target.tagName === 'BUTTON') return 'button';
if (target.tagName === 'INPUT') return 'input';
if (target.hasAttribute('data-track')) return target.getAttribute('data-track');
return target.tagName.toLowerCase();
}
function handleUnload() {
if (!currentPageViewId) return;
const timeOnPage = Math.round((Date.now() - pageStartTime) / 1000);
const data = JSON.stringify({
type: 'page_time',
page_view_id: currentPageViewId,
time_seconds: timeOnPage
});
navigator.sendBeacon(TRACK_ENDPOINT, data);
if (maxScrollDepth > 0) {
navigator.sendBeacon(SCROLL_ENDPOINT, JSON.stringify({
page_view_id: currentPageViewId,
scroll_depth: maxScrollDepth
}));
}
}
function handleVisibilityChange() {
if (document.visibilityState === 'hidden' && currentPageViewId) {
const timeOnPage = Math.round((Date.now() - pageStartTime) / 1000);
sendTracking({
type: 'page_time',
page_view_id: currentPageViewId,
time_seconds: timeOnPage
});
}
}
function handleScroll() {
if (scrollTimeout) {
clearTimeout(scrollTimeout);
}
scrollTimeout = setTimeout(function() {
calculateScrollDepth();
}, SCROLL_DEBOUNCE_MS);
}
function calculateScrollDepth() {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = document.documentElement.clientHeight;
const scrollPercent = Math.round((scrollTop + clientHeight) / scrollHeight * 100);
if (scrollPercent > maxScrollDepth) {
maxScrollDepth = Math.min(scrollPercent, 100);
if (currentPageViewId && (maxScrollDepth === 25 || maxScrollDepth === 50 ||
maxScrollDepth === 75 || maxScrollDepth >= 95)) {
fetch(SCROLL_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
page_view_id: currentPageViewId,
scroll_depth: maxScrollDepth
}),
credentials: 'same-origin'
}).catch(function() {
});
}
}
}
function handleError(message, source, lineno, colno, error) {
const errorData = {
message: message ? message.toString() : 'Unknown error',
source: source,
lineno: lineno,
colno: colno,
stack: error && error.stack ? error.stack : null,
url: window.location.href
};
fetch(ERROR_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(errorData),
credentials: 'same-origin'
}).catch(function() {
});
return false;
}
function handlePromiseRejection(event) {
const reason = event.reason;
handleError(
'Unhandled Promise Rejection: ' + (reason && reason.message ? reason.message : String(reason)),
null,
null,
null,
reason instanceof Error ? reason : null
);
}
function trackPerformance() {
if (!currentPageViewId) return;
if (!window.performance || !performance.timing) return;
const timing = performance.timing;
const navStart = timing.navigationStart;
const metrics = {
page_view_id: currentPageViewId,
dom_content_loaded_ms: timing.domContentLoadedEventEnd - navStart,
load_time_ms: timing.loadEventEnd - navStart
};
if (performance.getEntriesByType) {
const paintEntries = performance.getEntriesByType('paint');
paintEntries.forEach(function(entry) {
if (entry.name === 'first-paint') {
metrics.first_paint_ms = Math.round(entry.startTime);
}
if (entry.name === 'first-contentful-paint') {
metrics.first_contentful_paint_ms = Math.round(entry.startTime);
}
});
}
if (metrics.load_time_ms > 0 && metrics.load_time_ms < 300000) {
fetch(PERFORMANCE_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(metrics),
credentials: 'same-origin'
}).catch(function() {
});
}
}
function trackContactClicks() {
document.querySelectorAll('a[href^="mailto:"]').forEach(function(link) {
link.addEventListener('click', function(e) {
trackConversion('contact_click', 'email', link.href.replace('mailto:', ''));
});
});
document.querySelectorAll('a[href^="tel:"]').forEach(function(link) {
link.addEventListener('click', function(e) {
trackConversion('contact_click', 'phone', link.href.replace('tel:', ''));
});
});
if (window.location.pathname.startsWith('/company/')) {
document.querySelectorAll('a[target="_blank"][href^="http"]').forEach(function(link) {
const href = link.href.toLowerCase();
if (!href.includes('facebook.com') && !href.includes('linkedin.com') &&
!href.includes('instagram.com') && !href.includes('twitter.com')) {
link.addEventListener('click', function(e) {
trackConversion('contact_click', 'website', link.href);
});
}
});
}
}
function trackConversion(eventType, targetType, targetValue) {
let companyId = null;
const companyMeta = document.querySelector('meta[name="company-id"]');
if (companyMeta && companyMeta.content) {
companyId = parseInt(companyMeta.content, 10);
}
fetch('/api/analytics/conversion', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event_type: eventType,
target_type: targetType,
target_value: targetValue,
company_id: companyId
}),
credentials: 'same-origin'
}).catch(function() {
});
}
function sendHeartbeat() {
fetch(HEARTBEAT_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'same-origin'
}).catch(function() {
});
}
function sendTracking(data) {
fetch(TRACK_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
credentials: 'same-origin'
}).catch(function() {
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();