diff --git a/static/script.js b/static/script.js index cf85cd6..6072fe8 100644 --- a/static/script.js +++ b/static/script.js @@ -98,6 +98,45 @@ function formatClock(totalSeconds) { return `${minutes}:${seconds}`; } +function formatVmixClock(totalSeconds) { + const safeSeconds = Math.max(0, Math.floor(Number(totalSeconds) || 0)); + const hours = String(Math.floor(safeSeconds / 3600)).padStart(2, "0"); + const minutes = String(Math.floor((safeSeconds % 3600) / 60)).padStart(2, "0"); + const seconds = String(safeSeconds % 60).padStart(2, "0"); + return `${hours}:${minutes}:${seconds}`; +} + +function parseClockInput(value) { + const raw = String(value || "").trim().replaceAll(".", ":").replaceAll(",", ":"); + if (!raw) return null; + + const parts = raw.split(":").map(part => part.trim()); + if (parts.length > 3 || parts.some(part => part === "" || !/^\d+$/.test(part))) { + return null; + } + + let hours = 0; + let minutes = 0; + let seconds = 0; + + if (parts.length === 1) { + minutes = Number(parts[0]); + } else if (parts.length === 2) { + minutes = Number(parts[0]); + seconds = Number(parts[1]); + } else { + hours = Number(parts[0]); + minutes = Number(parts[1]); + seconds = Number(parts[2]); + } + + if (![hours, minutes, seconds].every(Number.isFinite) || seconds > 59 || minutes > 999) { + return null; + } + + return Math.max(0, hours * 3600 + minutes * 60 + seconds); +} + function getFootballMinute(totalSeconds) { if (currentPeriod === "2H" || currentPeriod === "FT") { const secondHalfSeconds = Math.max(0, totalSeconds - 45 * 60); @@ -353,6 +392,90 @@ function startTimer() { saveMatchState(); } +async function syncEditedClockToVmix(totalSeconds, shouldRun) { + const commands = [ + vmixCmd("Function=SetCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=02:00:00"), + vmixCmd(`Function=ChangeCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=${formatVmixClock(totalSeconds)}`), + vmixCmd(shouldRun + ? "Function=StartCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text" + : "Function=PauseCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text" + ), + vmixCmd("Function=PreviewInput&Input=ВЕРХНИЙ СЧЕТ") + ]; + + await sendDirectVmixCommands(commands); +} + +function closeClockEditor() { + const modal = document.getElementById("clockEditorModal"); + if (modal) modal.classList.remove("show"); +} + +function openClockEditor() { + if (!(currentPeriod === "1H" || currentPeriod === "2H")) { + alert("Редактировать время можно после запуска 1-го или 2-го тайма"); + return; + } + + updateTimerUI(); + + const modal = document.getElementById("clockEditorModal"); + const input = document.getElementById("clockEditorInput"); + const hint = document.getElementById("clockEditorHint"); + + if (input) { + input.value = formatClock(matchClockSeconds); + } + + if (hint) { + hint.textContent = timerId + ? "Таймер сейчас идёт. После сохранения он продолжит идти с нового времени." + : "Таймер сейчас на паузе. После сохранения он останется на паузе."; + } + + if (modal) modal.classList.add("show"); + + setTimeout(() => { + if (input) { + input.focus(); + input.select(); + } + }, 0); +} + +async function applyClockEditor() { + const input = document.getElementById("clockEditorInput"); + const newSeconds = parseClockInput(input ? input.value : ""); + + if (newSeconds === null) { + alert("Введите время в формате MM:SS, например 12:30 или 57:20"); + return; + } + + const wasRunning = !!timerId; + + if (timerId) { + clearInterval(timerId); + timerId = null; + } + + pausedAccumulatedSeconds = newSeconds; + matchClockSeconds = newSeconds; + periodStartedAt = wasRunning ? Date.now() : null; + + if (wasRunning) { + timerId = setInterval(() => { + updateTimerUI(); + saveMatchState(); + }, 1000); + } + + updateTimerUI(); + saveMatchState(); + closeClockEditor(); + await syncEditedClockToVmix(newSeconds, wasRunning); +} + async function triggerVmixExtraTimeAction(minutes, half) { const title = "ДОБАВЛЕННОЕ ВРЕМЯ ДЛЯ ВЕРХНЕГО СЧЕТА" const value = `+${minutes}`; @@ -3761,49 +3884,4 @@ function buildChannelValue(matchId) { if (hasMatch) return "match"; return ""; -} - -function setClockSeconds(newSeconds) { - const wasRunning = !!timerId; - - if (timerId) { - clearInterval(timerId); - timerId = null; - } - - pausedAccumulatedSeconds = Math.max(0, Number(newSeconds) || 0); - matchClockSeconds = pausedAccumulatedSeconds; - - if (wasRunning) { - periodStartedAt = Date.now(); - timerId = setInterval(() => { - updateTimerUI(); - saveMatchState(); - }, 1000); - } else { - periodStartedAt = null; - } - - updateTimerUI(); - saveMatchState(); - syncClockToVmix(matchClockSeconds, wasRunning); -} - -async function syncClockToVmix(seconds, shouldRun) { - const h = String(Math.floor(seconds / 3600)).padStart(2, "0"); - const m = String(Math.floor((seconds % 3600) / 60)).padStart(2, "0"); - const s = String(seconds % 60).padStart(2, "0"); - const value = `${h}:${m}:${s}`; - - const commands = [ - vmixCmd("Function=SetCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=02:00:00"), - vmixCmd(`Function=ChangeCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=${value}`), - vmixCmd( - shouldRun - ? "Function=StartCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text" - : "Function=PauseCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text" - ) - ]; - - await sendDirectVmixCommands(commands); } \ No newline at end of file diff --git a/static/styles.css b/static/styles.css index 34a4c19..2e05aec 100644 --- a/static/styles.css +++ b/static/styles.css @@ -2351,4 +2351,35 @@ body:not(.edit-mode-on) .referee-search { .event-time-edit-btn:focus-visible { outline: 2px solid rgba(95,132,255,0.45); outline-offset: 2px; +} + +.timer-box-clickable { + cursor: pointer; +} + +.clock-editor-modal { + position: fixed; + inset: 0; + background: rgba(0,0,0,.45); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; +} + +.clock-editor-content { + background: #1f1f1f; + padding: 20px; + border-radius: 12px; + width: 320px; + color: white; +} + +.clock-editor-content input { + width: 100%; + padding: 10px; + font-size: 24px; + text-align: center; + margin-top: 10px; + margin-bottom: 15px; } \ No newline at end of file diff --git a/templates/match_workspace.html b/templates/match_workspace.html index f930408..19beda9 100644 --- a/templates/match_workspace.html +++ b/templates/match_workspace.html @@ -289,7 +289,7 @@ Матч не начат -