const status = document.querySelector("#status");
const driverCount = document.querySelector("#driver-count");
const trackName = document.querySelector("#track-name");
const sessionType = document.querySelector("#session-type");
const timeLeft = document.querySelector("#time-left");
const weather = document.querySelector("#weather");
const lastUpdate = document.querySelector("#last-update");
const leaderboardNote = document.querySelector("#leaderboard-note");
const leaderboardBody = document.querySelector("#leaderboard-body");
function escapeHtml(value) {
return String(value ?? "")
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """);
}
function fmt(value, fallback = "-") {
if (value === undefined || value === null || value === "" || Number.isNaN(value)) {
return `${fallback}`;
}
return escapeHtml(value);
}
function fmtTime(seconds) {
if (typeof seconds !== "number" || !Number.isFinite(seconds) || seconds < 0) {
return "-";
}
const minutes = Math.floor(seconds / 60);
const remainder = seconds - minutes * 60;
return `${minutes}:${remainder.toFixed(3).padStart(6, "0")}`;
}
function fmtSplit(split) {
if (!split || typeof split.delta !== "number" || !Number.isFinite(split.delta)) {
return "-";
}
if (split.delta === 0) {
return "0";
}
const sign = split.delta > 0 ? "+" : "-";
return `${sign}${Math.abs(split.delta).toFixed(3)}`;
}
function sectorSplitForRow(row, session) {
const sessionType = String(session?.sessionType || "").toLowerCase();
const usePersonalBest = sessionType === "practice" || sessionType === "qualifying";
return usePersonalBest
? row?.sectorTimeSplit || row?.checkpointSplit
: row?.checkpointSplit || row?.sectorTimeSplit;
}
function positionChange(change) {
if (!change?.direction) {
return '';
}
const isUp = change.direction === "up";
const label = `${isUp ? "Moved up" : "Moved down"} ${change.places ?? 1}`;
return `
${isUp ? "▲" : "▼"}${change.places && change.places > 1 ? escapeHtml(change.places) : ""}
`;
}
function nationalityBadge(nationality) {
if (!nationality?.code) {
return '-';
}
const title = `${nationality.country || nationality.code}${nationality.source === "override" ? " (name override)" : ""}`;
const flag = flagSvg(nationality.code);
return `${flag || escapeHtml(nationality.code)}`;
}
function carShortName(row) {
if (!row?.vehicleShortName) {
return '-';
}
return `${escapeHtml(row.vehicleShortName)}`;
}
function statusBadge(row) {
const status = row?.status;
const label = status?.label || (row?.active === false ? "Inactive" : "Active");
const tone = status?.tone || (row?.active === false ? "warn" : "good");
return `${escapeHtml(label)}`;
}
function raceFlagDetails(row) {
const flag = row?.flag;
if (!flag || row.highestFlag === undefined || row.highestFlag === null) {
return `flag ${fmt(row?.highestFlag)}`;
}
const label = flag.observed ? ` ${escapeHtml(flag.observed)}` : "";
return `flag ${fmt(row.highestFlag)} c${fmt(flag.colour)} r${fmt(flag.reason)}${label}`;
}
function flagSvg(code) {
const iso2ByIso3 = {
DEU: "DE",
FRA: "FR",
GBR: "GB",
JPN: "JP",
USA: "US"
};
const iso2 = iso2ByIso3[String(code).toUpperCase()];
if (!iso2) return "";
const flags = {
DE: ``,
FR: ``,
GB: ``,
JP: ``,
US: ``
};
const svg = flags[iso2];
return svg ? `` : "";
}
function clock(value) {
if (!value) return "-";
return new Date(value).toLocaleTimeString();
}
function renderLeaderboard(leaderboard) {
const rows = leaderboard?.rows ?? [];
const session = leaderboard?.session ?? {};
driverCount.textContent = String(rows.length);
leaderboardNote.textContent = leaderboard?.note ?? "Waiting for participant or timing packets";
trackName.textContent = session.trackName || "-";
sessionType.textContent = session.sessionType || "-";
timeLeft.textContent = fmtTime(session.timeLeft);
weather.textContent = session.weather || "-";
lastUpdate.textContent = clock(session.lastUpdate);
leaderboardBody.innerHTML = rows.length
? rows.map((row) => `
${(() => {
const sectorSplit = sectorSplitForRow(row, session);
return `