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:
2026-10-23 06:45:34 +10:00
co-authored by Claude Sonnet 5.5
parent 84c1a01338
commit 9e6a41fc5d
86 changed files with 20852 additions and 0 deletions
+119
View File
@@ -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 });
}
});
});
})();