function renderSpeakerToggle(settingKey, labelKey, { state, t }) {
return `
`;
}
function getQuickAddState(transponder, { state }) {
const normalized = String(transponder || "").trim();
const driver = state.drivers.find((item) => String(item.transponder || "").trim() === normalized) || null;
const car = state.cars.find((item) => String(item.transponder || "").trim() === normalized) || null;
return {
transponder: normalized,
hasDriver: Boolean(driver),
hasCar: Boolean(car),
};
}
function getPreferredClassId(session, { state }) {
const event = state.events.find((item) => item.id === session?.eventId);
if (event?.classId) {
return event.classId;
}
return state.classes[0]?.id || "";
}
function beginQuickAddDraft(session, type, transponder, deps) {
const { state, setQuickAddDraft, renderView, getPreferredClassId } = deps;
const normalized = String(transponder || "").trim();
if (!normalized) {
return;
}
if (type === "driver" && state.drivers.some((item) => String(item.transponder || "").trim() === normalized)) {
return;
}
if (type === "car" && state.cars.some((item) => String(item.transponder || "").trim() === normalized)) {
return;
}
setQuickAddDraft({
type,
transponder: normalized,
classId: getPreferredClassId(session),
name: type === "driver" ? normalized : `Car ${normalized}`,
});
renderView();
}
function renderQuickAddActions(session, transponder, idPrefix, deps) {
const { t, getQuickAddState } = deps;
const quickState = getQuickAddState(transponder);
if (!quickState.transponder || (quickState.hasDriver && quickState.hasCar)) {
return "";
}
return `
${!quickState.hasDriver ? `` : ""}
${!quickState.hasCar ? `` : ""}
`;
}
function bindQuickAddActions(session, transponder, idPrefix, deps) {
const { beginQuickAddDraft } = deps;
document.getElementById(`${idPrefix}-add-driver`)?.addEventListener("click", () => {
beginQuickAddDraft(session, "driver", transponder);
});
document.getElementById(`${idPrefix}-add-car`)?.addEventListener("click", () => {
beginQuickAddDraft(session, "car", transponder);
});
}
function renderQuickAddPanel(session, deps) {
const { state, t, escapeHtml, getQuickAddDraft, getPreferredClassId } = deps;
const quickAddDraft = getQuickAddDraft();
if (!quickAddDraft || !quickAddDraft.transponder) {
return "";
}
const classOptions = state.classes
.map(
(item) => ``
)
.join("");
const isDriver = quickAddDraft.type === "driver";
return `
`;
}
function renderLeaderboardModal(session, row, deps) {
const { t, escapeHtml, formatLap, formatRaceClock, renderTable, getCompetitorPassings, renderQuickAddActions } = deps;
const passings = getCompetitorPassings(session, row);
return `
${escapeHtml(row.displayName || row.driverName)} • ${escapeHtml(row.subLabel || row.carName)}
${t("table.transponder")}: ${escapeHtml(row.transponder)}
${renderQuickAddActions(session, row.transponder, "leaderboardModal")}
${t("table.laps")}: ${row.laps}
${t("timing.total_time")}: ${escapeHtml(row.resultDisplay)}
${t("table.best_lap")}: ${formatLap(row.bestLapMs)}
${t("table.last_lap")}: ${formatLap(row.lastLapMs)}
${t("table.own_delta")}: ${escapeHtml(row.lapDelta || "-")}
${t("timing.manual_corrections")}
${t("timing.lap_adjustment")}: ${Number(row.manualLapAdjustment || 0) || 0}
${t("timing.time_penalty")}: ${(Number(row.manualTimeAdjustmentMs || 0) || 0) > 0 ? "+" : (Number(row.manualTimeAdjustmentMs || 0) || 0) < 0 ? "-" : ""}${formatLap(Math.abs(Number(row.manualTimeAdjustmentMs || 0) || 0))}
${t("timing.lap_history")}
${
passings.length
? renderTable(
[t("table.lap"), t("table.time"), t("table.last_lap")],
passings.map(
(passing, index) => `
| ${index + 1} |
${formatRaceClock(Math.max(0, passing.timestamp - (session.startedAt || passing.timestamp)))} |
${formatLap(passing.lapMs)} |
`
)
)
: `
${t("timing.no_lap_history")}
`
}
`;
}
function renderLeaderboard(rows, deps) {
const { t, escapeHtml, renderTable, getManualCorrectionSummary, formatLap, formatTeamActiveMemberLabel } = deps;
if (!rows.length) {
return `${t("timing.no_laps")}
`;
}
return renderTable(
[
t("table.pos"),
t("table.driver"),
t("table.car"),
t("table.transponder"),
t("table.laps"),
t("table.result"),
t("table.last_lap"),
t("table.best_lap"),
t("table.leader_gap"),
t("table.ahead_gap"),
t("table.own_delta"),
"",
],
rows.map((row, idx) => {
const posClass = idx === 0 ? "pos-1" : idx === 1 ? "pos-2" : idx === 2 ? "pos-3" : "";
return `
| ${idx + 1} |
${escapeHtml(row.displayName || row.driverName)}
${row.teamId ? `${t("overlay.active_member")}: ${escapeHtml(formatTeamActiveMemberLabel(row))} ` : ""}
${getManualCorrectionSummary(row) ? `${escapeHtml(getManualCorrectionSummary(row))} ` : ""}
${row.invalidPending ? `${escapeHtml(row.invalidLabel)}${row.invalidLapMs ? ` • ${formatLap(row.invalidLapMs)}` : ""} ` : ""}
|
${escapeHtml(row.subLabel || row.carName)} |
${escapeHtml(row.transponder)} |
${row.laps} |
${escapeHtml(row.resultDisplay)} |
${formatLap(row.lastLapMs)} |
${formatLap(row.bestLapMs)} |
${escapeHtml(row.leaderGap || row.gap || "-")} |
${escapeHtml(row.gapAhead || "-")} |
${escapeHtml(row.lapDelta || "-")} |
|
`;
})
);
}
function renderRecentPassings(session, deps) {
const { t, escapeHtml, renderTable, ensureSessionResult, formatLap, getPassingValidationLabel, isCountedPassing, renderQuickAddActions } = deps;
if (!session) {
return `${t("timing.no_session_selected")}
`;
}
const result = ensureSessionResult(session.id);
const items = result.passings.slice(-20).reverse();
if (!items.length) {
return `${t("timing.no_passings")}
`;
}
return renderTable(
[t("table.time"), t("table.transponder"), t("table.driver"), t("table.car"), t("table.last_lap"), t("table.status"), ""],
items.map((p, index) => {
return `
| ${new Date(p.timestamp).toLocaleTimeString()} |
${escapeHtml(p.transponder)} |
${escapeHtml(p.teamName ? `${p.teamName} • ${p.driverName || t("common.unknown_driver")}` : p.driverName || t("common.unknown_driver"))} |
${escapeHtml(p.carName || p.subLabel || "-")} |
${formatLap(p.lapMs)} |
${escapeHtml(getPassingValidationLabel(p))} |
${renderQuickAddActions(session, p.transponder, `recentPassing-${index}`)} |
`;
})
);
}
export function renderTimingView(deps) {
const {
state, dom, t, escapeHtml, formatLap, formatCountdown, formatElapsedClock, renderTable, uid,
getActiveSession, ensureSessionResult, buildLeaderboard, getSessionTiming, getVisiblePassings,
getEventName, getSessionTypeLabel, getStatusLabel, normalizeStartMode, getStartModeLabel, renderPositionGrid,
connectDecoder, disconnectDecoder, applyCompetitorCorrection, invalidateCompetitorLastLap, restoreCompetitorLastInvalidLap,
ensureAudioContext, validateTrackSessionForStart, announceRaceStarted, announceRaceFinished, saveState, updateHeaderState, renderView,
normalizeDriver, normalizeCar, processDecoderMessage, getSelectedLeaderboardKey, setSelectedLeaderboardKey,
getQuickAddDraft, setQuickAddDraft, getPassingValidationLabel, isCountedPassing, getCompetitorPassings,
getManualCorrectionSummary, formatTeamActiveMemberLabel, formatRaceClock,
setLastFinishAnnouncementSessionId, lastOverlayLeaderKeyBySession, lastOverlayTop3BySession, overlayEvents, setOverlayEvents,
} = deps;
const helperDeps = {
state, t, escapeHtml, renderTable, formatLap, formatRaceClock, getCompetitorPassings,
getQuickAddDraft, getPassingValidationLabel, isCountedPassing, getManualCorrectionSummary,
formatTeamActiveMemberLabel,
};
helperDeps.getPreferredClassId = (session) => getPreferredClassId(session, { state });
helperDeps.getQuickAddState = (transponder) => getQuickAddState(transponder, { state });
helperDeps.setQuickAddDraft = setQuickAddDraft;
helperDeps.renderView = renderView;
helperDeps.beginQuickAddDraft = (session, type, transponder) => beginQuickAddDraft(session, type, transponder, helperDeps);
helperDeps.renderQuickAddActions = (session, transponder, idPrefix) => renderQuickAddActions(session, transponder, idPrefix, helperDeps);
const active = getActiveSession();
const result = active ? ensureSessionResult(active.id) : null;
const leaderboard = active ? buildLeaderboard(active) : [];
const sessionTiming = active ? getSessionTiming(active) : null;
const clockLabel = active && sessionTiming?.followUpActive ? t("timing.follow_up") : active && sessionTiming?.untimed ? t("timing.elapsed") : t("timing.remaining");
const clockValue = sessionTiming?.followUpActive
? formatCountdown(sessionTiming?.followUpRemainingMs ?? 0)
: sessionTiming?.untimed
? formatElapsedClock(sessionTiming?.elapsedMs ?? 0)
: formatCountdown(sessionTiming?.remainingMs ?? 0);
const showFinishedBanner = Boolean(active && active.status === "finished" && active.finishedByTimer);
const showFollowUpBanner = Boolean(active && sessionTiming?.followUpActive);
const selectedRow = leaderboard.find((row) => row.key === getSelectedLeaderboardKey()) || null;
if (getSelectedLeaderboardKey() && !selectedRow) {
setSelectedLeaderboardKey(null);
}
dom.view.innerHTML = `
${t("timing.status")}
${state.decoder.connected ? t("timing.connected") : t("timing.disconnected")}
${t("timing.last_message")}: ${state.decoder.lastMessageAt ? new Date(state.decoder.lastMessageAt).toLocaleString() : "-"}
${escapeHtml(state.decoder.lastError || "")}
${
active
? `
${escapeHtml(active.name)}
${escapeHtml(getSessionTypeLabel(active.type))}
${escapeHtml(getEventName(active.eventId))}
${escapeHtml(getStatusLabel(active.status))}
${clockLabel}${clockValue}
${t("timing.started")}${active.startedAt ? new Date(active.startedAt).toLocaleTimeString() : "-"}
${t("table.start_mode")}${escapeHtml(getStartModeLabel(active.startMode))}
${t("timing.seeding_mode")}${active.seedBestLapCount > 0 ? `${active.seedBestLapCount}` : "-"}
${t("timing.total_passings")}${getVisiblePassings(result).length}
${
active.type === "free_practice"
? `
${t("events.free_practice_note")}
`
: active.type === "open_practice"
? `
${t("events.open_practice_note")}
`
: ""
}`
: `
${t("timing.no_active")}
`
}
${showFollowUpBanner ? `
${t("timing.follow_up_active")}
` : ""}
${showFinishedBanner ? `
${t("timing.race_finished")}
` : ""}
${active && normalizeStartMode(active.startMode) === "position" ? renderPositionGrid(active) : ""}
${active ? renderQuickAddPanel(active, helperDeps) : ""}
${t("timing.speaker_panel_hint")}
${renderSpeakerToggle("speakerPassingCueEnabled", "settings.speaker_passing_cue", { state, t })}
${renderSpeakerToggle("speakerLeaderCueEnabled", "settings.speaker_leader_cue", { state, t })}
${renderSpeakerToggle("speakerBestLapCueEnabled", "settings.speaker_bestlap_cue", { state, t })}
${renderSpeakerToggle("speakerTop3CueEnabled", "settings.speaker_top3_cue", { state, t })}
${renderSpeakerToggle("speakerSessionStartCueEnabled", "settings.speaker_start_cue", { state, t })}
${renderSpeakerToggle("speakerFinishCueEnabled", "settings.speaker_finish_cue", { state, t })}
${renderLeaderboard(leaderboard, helperDeps)}
${renderRecentPassings(active, { ...helperDeps, ensureSessionResult, renderQuickAddActions: (session, transponder, idPrefix) => renderQuickAddActions(session, transponder, idPrefix, helperDeps) })}
${selectedRow && active ? renderLeaderboardModal(active, selectedRow, { ...helperDeps, renderQuickAddActions: (session, transponder, idPrefix) => renderQuickAddActions(session, transponder, idPrefix, helperDeps) }) : ""}
`;
document.getElementById("timingConnect")?.addEventListener("click", () => {
const input = document.getElementById("timingWsUrl");
if (input && input.value.trim()) {
state.settings.wsUrl = input.value.trim();
saveState();
}
connectDecoder();
});
document.getElementById("timingDisconnect")?.addEventListener("click", disconnectDecoder);
leaderboard.forEach((row) => {
document.getElementById(`leaderboard-detail-${row.key}`)?.addEventListener("click", () => {
setSelectedLeaderboardKey(row.key);
renderView();
});
});
if (active && selectedRow) {
bindQuickAddActions(active, selectedRow.transponder, "leaderboardModal", helperDeps);
document.getElementById("corrLapPlus")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { lapDelta: 1 });
renderView();
});
document.getElementById("corrLapMinus")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { lapDelta: -1 });
renderView();
});
document.getElementById("corrSecPlus")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { timeMsDelta: 1000 });
renderView();
});
document.getElementById("corr5SecPlus")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { timeMsDelta: 5000 });
renderView();
});
document.getElementById("corrSecMinus")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { timeMsDelta: -1000 });
renderView();
});
document.getElementById("corrInvalidateLast")?.addEventListener("click", () => {
invalidateCompetitorLastLap(active, selectedRow);
renderView();
});
document.getElementById("corrRestoreInvalid")?.addEventListener("click", () => {
restoreCompetitorLastInvalidLap(active, selectedRow);
renderView();
});
document.getElementById("corrReset")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { reset: true });
renderView();
});
}
if (active) {
ensureSessionResult(active.id)
.passings.slice(-20)
.reverse()
.forEach((passing, index) => {
bindQuickAddActions(active, passing.transponder, `recentPassing-${index}`, helperDeps);
});
}
document.getElementById("quickAddCancel")?.addEventListener("click", () => {
setQuickAddDraft(null);
renderView();
});
document.getElementById("quickAddForm")?.addEventListener("submit", (event) => {
event.preventDefault();
const quickAddDraft = getQuickAddDraft();
if (!active || !quickAddDraft) {
return;
}
const form = new FormData(event.currentTarget);
const name = String(form.get("name") || "").trim();
if (!name) {
return;
}
const transponder = String(form.get("transponder") || "").trim();
const brand = String(form.get("brand") || "").trim();
if (!transponder) {
return;
}
if (quickAddDraft.type === "driver") {
if (!state.drivers.some((item) => String(item.transponder || "").trim() === transponder)) {
state.drivers.push(
normalizeDriver({
id: uid("driver"),
name,
classId: String(form.get("classId") || getPreferredClassId(active, { state })),
brand,
transponder,
})
);
}
} else if (!state.cars.some((item) => String(item.transponder || "").trim() === transponder)) {
state.cars.push(
normalizeCar({
id: uid("car"),
name,
brand,
transponder,
})
);
}
setQuickAddDraft(null);
saveState();
renderView();
});
document.getElementById("leaderboardModalClose")?.addEventListener("click", () => {
setSelectedLeaderboardKey(null);
renderView();
});
document.getElementById("leaderboardModalOverlay")?.addEventListener("click", (event) => {
if (event.target?.id === "leaderboardModalOverlay") {
setSelectedLeaderboardKey(null);
renderView();
}
});
document.getElementById("timingSimPass")?.addEventListener("click", () => {
const tp = prompt(t("timing.prompt_transponder"), "232323");
if (!tp) {
return;
}
processDecoderMessage({
msg: "PASSING",
transponder: tp,
rtc_time: new Date().toISOString(),
strength: 0,
resend: false,
loop_id: "sim",
});
});
document.getElementById("setActiveSession")?.addEventListener("click", () => {
const select = document.getElementById("activeSessionSelect");
if (!select || !select.value) {
return;
}
state.activeSessionId = select.value;
saveState();
updateHeaderState();
renderView();
});
document.getElementById("startSession")?.addEventListener("click", () => {
const session = getActiveSession();
if (!session) {
return;
}
ensureAudioContext();
if (session.mode === "track") {
const trackValidation = validateTrackSessionForStart(session);
if (!trackValidation.ok) {
alert(trackValidation.message);
return;
}
}
session.status = "running";
session.startedAt = Date.now();
session.endedAt = null;
session.finishedByTimer = false;
session.followUpStartedAt = null;
setLastFinishAnnouncementSessionId(null);
lastOverlayLeaderKeyBySession[session.id] = null;
lastOverlayTop3BySession[session.id] = [];
setOverlayEvents([]);
ensureSessionResult(session.id);
announceRaceStarted(session);
saveState();
updateHeaderState();
renderView();
});
document.getElementById("stopSession")?.addEventListener("click", () => {
const session = getActiveSession();
if (!session) {
return;
}
session.status = "finished";
session.endedAt = Date.now();
session.finishedByTimer = false;
session.followUpStartedAt = null;
setLastFinishAnnouncementSessionId(session.id);
announceRaceFinished();
saveState();
updateHeaderState();
renderView();
});
document.getElementById("resetSession")?.addEventListener("click", () => {
const session = getActiveSession();
if (!session) {
return;
}
if (!confirm(t("timing.clear_confirm"))) {
return;
}
delete state.resultsBySession[session.id];
session.followUpStartedAt = null;
setLastFinishAnnouncementSessionId(null);
delete lastOverlayLeaderKeyBySession[session.id];
delete lastOverlayTop3BySession[session.id];
setOverlayEvents([]);
saveState();
renderView();
});
document.querySelectorAll("[data-speaker-setting]").forEach((node) => {
node.addEventListener("change", (event) => {
const input = event.currentTarget;
if (!(input instanceof HTMLInputElement)) {
return;
}
state.settings[input.dataset.speakerSetting] = input.checked;
saveState();
});
});
}
export function renderJudgingView(deps) {
const {
dom, t, escapeHtml, formatLap, renderTable, getActiveSession, ensureSessionResult, buildLeaderboard,
getSessionTypeLabel, getJudgeFilteredRows, getJudgeFilteredLog, getCompetitorPassings, isCountedPassing,
getPassingValidationLabel, undoJudgingAdjustment, applyCompetitorCorrection, invalidateCompetitorLastLap,
restoreCompetitorLastInvalidLap, renderView, getSelectedJudgeKey, setSelectedJudgeKey,
getJudgingCompetitorFilter, setJudgingCompetitorFilter, getJudgingLogFilter, setJudgingLogFilter,
} = deps;
const active = getActiveSession();
if (!active) {
dom.view.innerHTML = `
${t("judging.no_active_session")}
`;
return;
}
const result = ensureSessionResult(active.id);
const leaderboard = buildLeaderboard(active);
const filteredRows = getJudgeFilteredRows(leaderboard, getJudgingCompetitorFilter());
if (getSelectedJudgeKey() && !filteredRows.some((row) => row.key === getSelectedJudgeKey())) {
setSelectedJudgeKey(null);
}
if (!getSelectedJudgeKey() && filteredRows.length) {
setSelectedJudgeKey(filteredRows[0].key);
}
const selectedRow = leaderboard.find((row) => row.key === getSelectedJudgeKey()) || null;
const selectedPassings = selectedRow ? getCompetitorPassings(active, selectedRow, { includeInvalid: true }) : [];
const actionLog = getJudgeFilteredLog(Array.isArray(result.adjustments) ? result.adjustments.slice(-50).reverse() : [], getJudgingLogFilter());
const latestUndoable = (result.adjustments || []).slice().reverse().find((entry) => !entry.undoneAt && entry.undo);
dom.view.innerHTML = `
${t("judging.active_session")}: ${escapeHtml(active.name)} • ${escapeHtml(getSessionTypeLabel(active.type))}
${renderTable(
[t("table.pos"), t("table.driver"), t("table.result"), t("table.best_lap"), ""],
filteredRows.map(
(row, index) => `
| ${index + 1} |
${escapeHtml(row.displayName || row.driverName)}
${escapeHtml(row.subLabel || row.transponder || "-")}
|
${escapeHtml(row.resultDisplay || "-")} |
${formatLap(row.bestLapMs)} |
|
`
)
)}
${
selectedRow
? `
${escapeHtml(selectedRow.displayName || selectedRow.driverName)} • ${escapeHtml(selectedRow.subLabel || selectedRow.carName || "-")}
${t("table.laps")}: ${selectedRow.laps}
${t("table.best_lap")}: ${formatLap(selectedRow.bestLapMs)}
${t("table.last_lap")}: ${formatLap(selectedRow.lastLapMs)}
${t("timing.lap_history")}
${
selectedPassings.length
? renderTable(
[t("table.lap"), t("table.last_lap"), t("table.status")],
selectedPassings.map(
(passing, index) => `
| ${index + 1} |
${formatLap(passing.lapMs)} |
${escapeHtml(getPassingValidationLabel(passing))} |
`
)
)
: `
${t("timing.no_lap_history")}
`
}
`
: `
${t("judging.selected_none")}
`
}
${
actionLog.length
? renderTable(
[t("table.time"), t("table.driver"), t("events.actions"), t("table.status"), ""],
actionLog.map(
(entry) => `
| ${new Date(entry.ts).toLocaleTimeString()} |
${escapeHtml(entry.displayName || "-")} |
${escapeHtml(entry.action || "-")} |
${escapeHtml(entry.detail || "-")}${entry.undoneAt ? ` • ${escapeHtml(t("judging.filter_log_undo"))}` : ""} |
${entry.undo && !entry.undoneAt ? `` : ""} |
`
)
)
: `
${t("judging.no_action_log")}
`
}
`;
leaderboard.forEach((row) => {
document.getElementById(`judge-select-${row.key}`)?.addEventListener("click", () => {
setSelectedJudgeKey(row.key);
renderJudgingView(deps);
});
});
document.getElementById("judgingCompetitorFilter")?.addEventListener("change", (event) => {
const input = event.currentTarget;
if (!(input instanceof HTMLSelectElement)) {
return;
}
setJudgingCompetitorFilter(input.value);
renderJudgingView(deps);
});
document.getElementById("judgingLogFilter")?.addEventListener("change", (event) => {
const input = event.currentTarget;
if (!(input instanceof HTMLSelectElement)) {
return;
}
setJudgingLogFilter(input.value);
renderJudgingView(deps);
});
document.getElementById("judgingExportLog")?.addEventListener("click", () => {
const rows = (result.adjustments || []).map((entry) => ({
time: new Date(entry.ts).toISOString(),
competitor: entry.displayName || "-",
action: entry.action || "-",
detail: entry.detail || "-",
category: entry.category || "-",
undoneAt: entry.undoneAt ? new Date(entry.undoneAt).toISOString() : "",
}));
const blob = new Blob([JSON.stringify({ session: active.name, items: rows }, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `${active.name.replaceAll(/\s+/g, "_")}_judging_log.json`;
link.click();
URL.revokeObjectURL(url);
});
document.getElementById("judgingUndoLast")?.addEventListener("click", () => {
if (!latestUndoable) {
alert(t("judging.no_undo"));
return;
}
undoJudgingAdjustment(active, latestUndoable.id);
renderJudgingView(deps);
});
actionLog.forEach((entry) => {
if (!entry.undo || entry.undoneAt) {
return;
}
document.getElementById(`judge-undo-${entry.id}`)?.addEventListener("click", () => {
undoJudgingAdjustment(active, entry.id);
renderJudgingView(deps);
});
});
if (!selectedRow) {
return;
}
document.getElementById("judgeLapPlus")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { lapDelta: 1 });
renderJudgingView(deps);
});
document.getElementById("judgeLapMinus")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { lapDelta: -1 });
renderJudgingView(deps);
});
document.getElementById("judgeSecPlus")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { timeMsDelta: 1000 });
renderJudgingView(deps);
});
document.getElementById("judgeFivePlus")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { timeMsDelta: 5000 });
renderJudgingView(deps);
});
document.getElementById("judgeSecMinus")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { timeMsDelta: -1000 });
renderJudgingView(deps);
});
document.getElementById("judgeInvalidate")?.addEventListener("click", () => {
invalidateCompetitorLastLap(active, selectedRow);
renderJudgingView(deps);
});
document.getElementById("judgeRestore")?.addEventListener("click", () => {
restoreCompetitorLastInvalidLap(active, selectedRow);
renderJudgingView(deps);
});
document.getElementById("judgeReset")?.addEventListener("click", () => {
applyCompetitorCorrection(active, selectedRow, { reset: true });
renderJudgingView(deps);
});
}