chore: baseline of the live help4bis.com docroot as of 2026-10-23 (cockpit #168)
Excludes analytics/data, vendor, support, .env, ACME tokens. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
/* Version: 1.0.0 | Created: 2026-04-08 | Updated: 2026-04-08 */
|
||||
/* HELP4BIS Analytics Tracker — lightweight, self-hosted, privacy-first */
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var endpoint = '/analytics/track.php';
|
||||
|
||||
function getPagePath() {
|
||||
return window.location.pathname || '/';
|
||||
}
|
||||
|
||||
function getReferrer() {
|
||||
var ref = document.referrer || '';
|
||||
// Strip own domain
|
||||
if (ref.indexOf(window.location.hostname) !== -1) return '';
|
||||
return ref;
|
||||
}
|
||||
|
||||
function getScreenSize() {
|
||||
return window.screen.width + 'x' + window.screen.height;
|
||||
}
|
||||
|
||||
function getUtmParams() {
|
||||
var params = {};
|
||||
var search = window.location.search;
|
||||
if (!search) return params;
|
||||
var pairs = search.substring(1).split('&');
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
var kv = pairs[i].split('=');
|
||||
var key = decodeURIComponent(kv[0]);
|
||||
if (key.indexOf('utm_') === 0) {
|
||||
params[key] = decodeURIComponent(kv[1] || '');
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
function track(eventType, data) {
|
||||
var payload = {
|
||||
event: eventType,
|
||||
page: getPagePath(),
|
||||
title: document.title,
|
||||
referrer: getReferrer(),
|
||||
screen: getScreenSize(),
|
||||
language: navigator.language || '',
|
||||
utm: getUtmParams(),
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
if (data) {
|
||||
for (var key in data) {
|
||||
if (data.hasOwnProperty(key)) payload[key] = data[key];
|
||||
}
|
||||
}
|
||||
|
||||
// Use sendBeacon for reliability, fallback to fetch
|
||||
var body = JSON.stringify(payload);
|
||||
if (navigator.sendBeacon) {
|
||||
navigator.sendBeacon(endpoint, body);
|
||||
} else {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', endpoint, true);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.send(body);
|
||||
}
|
||||
}
|
||||
|
||||
// Track page view on load
|
||||
track('pageview');
|
||||
|
||||
// Track time on page (fires on unload)
|
||||
var startTime = Date.now();
|
||||
window.addEventListener('beforeunload', function() {
|
||||
var duration = Math.round((Date.now() - startTime) / 1000);
|
||||
if (duration > 1) {
|
||||
track('duration', { seconds: duration });
|
||||
}
|
||||
});
|
||||
|
||||
// Track outbound link clicks
|
||||
document.addEventListener('click', function(e) {
|
||||
var link = e.target.closest('a');
|
||||
if (!link) return;
|
||||
var href = link.getAttribute('href') || '';
|
||||
// CTA buttons (contact, get started, etc.)
|
||||
if (link.classList.contains('btn') || link.classList.contains('cta')) {
|
||||
track('cta_click', { href: href, text: (link.textContent || '').trim().substring(0, 80) });
|
||||
}
|
||||
// Outbound links
|
||||
if (href.indexOf('http') === 0 && href.indexOf(window.location.hostname) === -1) {
|
||||
track('outbound', { href: href });
|
||||
}
|
||||
// Phone/email clicks
|
||||
if (href.indexOf('tel:') === 0 || href.indexOf('mailto:') === 0) {
|
||||
track('contact_click', { href: href });
|
||||
}
|
||||
});
|
||||
|
||||
// Track form submissions
|
||||
document.addEventListener('submit', function(e) {
|
||||
var form = e.target;
|
||||
track('form_submit', { form_id: form.id || '', form_action: form.action || '' });
|
||||
});
|
||||
|
||||
// Track scroll depth (25%, 50%, 75%, 100%)
|
||||
var scrollMarks = { 25: false, 50: false, 75: false, 100: false };
|
||||
window.addEventListener('scroll', function() {
|
||||
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||
var docHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
|
||||
if (docHeight <= 0) return;
|
||||
var pct = Math.round((scrollTop / docHeight) * 100);
|
||||
[25, 50, 75, 100].forEach(function(mark) {
|
||||
if (pct >= mark && !scrollMarks[mark]) {
|
||||
scrollMarks[mark] = true;
|
||||
track('scroll', { depth: mark });
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user