200 lines
7.9 KiB
JavaScript
200 lines
7.9 KiB
JavaScript
(function () {
|
|
"use strict";
|
|
|
|
const webview = window.chrome && window.chrome.webview;
|
|
const connection = document.getElementById("playout-connection-state");
|
|
const prepare = document.getElementById("playout-prepare");
|
|
const takeIn = document.getElementById("playout-take-in");
|
|
const next = document.getElementById("playout-next");
|
|
const takeOut = document.getElementById("playout-take-out");
|
|
const backgroundNone = document.getElementById("playout-background-none");
|
|
const backgroundFile = document.getElementById("playout-background-file");
|
|
const backgroundName = document.getElementById("playout-background-name");
|
|
const fadeDuration = document.getElementById("playout-fade-duration");
|
|
const status = document.getElementById("playout-status");
|
|
const controls = [prepare, takeIn, next, takeOut, backgroundNone,
|
|
backgroundFile, fadeDuration];
|
|
let currentState = null;
|
|
let localBusy = false;
|
|
|
|
function hasOpenDialog() {
|
|
return Array.from(document.querySelectorAll("[role='dialog'], [role='alertdialog']"))
|
|
.some(function (dialog) {
|
|
const container = dialog.closest("[hidden]");
|
|
return !container && !dialog.hidden;
|
|
});
|
|
}
|
|
|
|
function post(type, payload) {
|
|
if (!webview || localBusy || hasOpenDialog()) return;
|
|
localBusy = true;
|
|
render(currentState);
|
|
webview.postMessage({ type: type, payload: payload || {} });
|
|
}
|
|
|
|
function setAllDisabled(disabled) {
|
|
controls.forEach(function (control) {
|
|
if (control) control.disabled = disabled;
|
|
});
|
|
}
|
|
|
|
function phaseLabel(phase) {
|
|
if (phase === "prepared") return "PREPARED";
|
|
if (phase === "program") return "PROGRAM";
|
|
return "IDLE";
|
|
}
|
|
|
|
function connectionLabel(playout) {
|
|
if (!playout) return "미연결";
|
|
if (playout.outcomeUnknown) return "결과 불명확";
|
|
if (playout.mode === "disabled") return "송출 비활성";
|
|
if (playout.mode === "dryRun") return "DRY RUN";
|
|
if (playout.isConnected) return "Tornado2 연결됨";
|
|
if (playout.isProcessRunning) return "Tornado2 감지됨";
|
|
return "미연결";
|
|
}
|
|
|
|
function pageLabel(playout) {
|
|
if (!playout || playout.pageCount <= 0) return "";
|
|
return " · Page " + String(playout.pageIndexZeroBased + 1) +
|
|
"/" + String(playout.pageCount);
|
|
}
|
|
|
|
function nextLabel(playout) {
|
|
if (!playout || playout.nextKind === "none") return "";
|
|
if (playout.nextKind === "endOfPlaylist") return " · 마지막 항목";
|
|
return playout.nextKind === "pageNext" ? " · 다음 페이지" : " · 다음 항목";
|
|
}
|
|
|
|
function canTakeInFromState(playout, hasPlaylist) {
|
|
return playout.phase === "prepared" ||
|
|
hasPlaylist && (playout.phase === "idle" || playout.phase === "program");
|
|
}
|
|
|
|
function canAdvanceFromState(playout) {
|
|
return playout.phase === "program" &&
|
|
playout.nextKind !== "none" && playout.nextKind !== "endOfPlaylist";
|
|
}
|
|
|
|
function render(state) {
|
|
currentState = state || currentState;
|
|
const playout = currentState && currentState.playout;
|
|
const nativeBusy = !!(currentState && currentState.isBusy) ||
|
|
!!(playout && playout.isBusy);
|
|
const busy = localBusy || nativeBusy;
|
|
if (!playout) {
|
|
connection.textContent = "미연결";
|
|
status.textContent = "송출 엔진 상태를 사용할 수 없습니다.";
|
|
status.title = status.textContent;
|
|
setAllDisabled(true);
|
|
return;
|
|
}
|
|
|
|
const commandAvailable = playout.isCommandAvailable === true &&
|
|
playout.outcomeUnknown !== true &&
|
|
playout.isTakeOutCompletionPending !== true;
|
|
const hasPlaylist = Array.isArray(currentState.playlist) &&
|
|
currentState.playlist.some(function (row) { return row.isEnabled === true; });
|
|
const liveTakeInAllowed = playout.mode !== "live" ||
|
|
playout.liveTakeInAllowed === true;
|
|
const canPrepareToggle = playout.phase === "idle" ? hasPlaylist :
|
|
playout.phase === "prepared" || playout.phase === "program";
|
|
|
|
const nextConnectionText = connectionLabel(playout);
|
|
if (connection.textContent !== nextConnectionText) {
|
|
connection.textContent = nextConnectionText;
|
|
}
|
|
connection.classList.toggle("connected", playout.isConnected === true);
|
|
connection.classList.toggle("outcome-unknown", playout.outcomeUnknown === true);
|
|
connection.classList.toggle("dry-run", playout.mode === "dryRun");
|
|
prepare.classList.toggle("active", playout.phase !== "idle");
|
|
prepare.disabled = busy || !commandAvailable || !canPrepareToggle ||
|
|
playout.isPlayCompletionPending === true;
|
|
const canTakeIn = canTakeInFromState(playout, hasPlaylist);
|
|
takeIn.disabled = busy || !commandAvailable || !liveTakeInAllowed ||
|
|
!canTakeIn || playout.isPlayCompletionPending === true;
|
|
next.disabled = busy || !commandAvailable ||
|
|
!canAdvanceFromState(playout) ||
|
|
playout.isPlayCompletionPending === true;
|
|
// MainForm's Escape path called StopAll whenever the Tornado player existed,
|
|
// even if its local m_TakeIn flag had already drifted back to IDLE.
|
|
takeOut.disabled = busy || !commandAvailable;
|
|
backgroundNone.disabled = busy || playout.canChangeBackground !== true;
|
|
backgroundFile.disabled = busy || playout.canChangeBackground !== true;
|
|
// MainForm.Designer keeps panel3 (which owns ComboDi) invisible. Preserve
|
|
// the native fade value as read-only state, but never expose an operator
|
|
// interaction path from the WebView shell.
|
|
fadeDuration.disabled = true;
|
|
backgroundNone.checked = playout.backgroundEnabled !== true;
|
|
backgroundName.value = playout.backgroundEnabled === true
|
|
? (playout.backgroundFileName || "선택한 배경") : "배경없음";
|
|
fadeDuration.value = String(playout.fadeDuration);
|
|
|
|
const scene = playout.onAirCode || playout.preparedCode;
|
|
const sceneLabel = scene ? " · " + scene : "";
|
|
const refresh = playout.refreshActive
|
|
? " · 자동 갱신 " + String(playout.refreshCompletedCount) + "회" : "";
|
|
status.textContent = phaseLabel(playout.phase) + sceneLabel +
|
|
pageLabel(playout) + nextLabel(playout) + refresh +
|
|
(playout.message ? " · " + playout.message : "");
|
|
status.title = status.textContent;
|
|
}
|
|
|
|
prepare.addEventListener("click", function () {
|
|
post("prepare-playout", {});
|
|
});
|
|
takeIn.addEventListener("click", function () {
|
|
post("take-in", {});
|
|
});
|
|
next.addEventListener("click", function () {
|
|
post("next-playout", {});
|
|
});
|
|
takeOut.addEventListener("click", function () {
|
|
post("take-out", {});
|
|
});
|
|
backgroundFile.addEventListener("click", function () {
|
|
post("choose-background", {});
|
|
});
|
|
backgroundNone.addEventListener("change", function () {
|
|
post("toggle-background", {});
|
|
});
|
|
document.addEventListener("keydown", function (event) {
|
|
if (event.defaultPrevented) return;
|
|
const legacyShortcut = event.key === "F2" || event.key === "F3" ||
|
|
event.key === "F8" || event.key === "Escape";
|
|
if (hasOpenDialog()) {
|
|
// ShowDialog/MessageBox own these keys. Consume browser defaults (notably
|
|
// Chromium's F3 Find Next) without invoking a MainForm playout shortcut.
|
|
if (legacyShortcut) event.preventDefault();
|
|
return;
|
|
}
|
|
if (event.repeat) return;
|
|
if (event.key === "F2") {
|
|
event.preventDefault();
|
|
if (!backgroundFile.disabled) backgroundFile.click();
|
|
} else if (event.key === "F3") {
|
|
event.preventDefault();
|
|
if (!backgroundNone.disabled) backgroundNone.click();
|
|
} else if (event.key === "F8") {
|
|
event.preventDefault();
|
|
if (!takeIn.disabled) takeIn.click();
|
|
} else if (event.key === "Escape") {
|
|
event.preventDefault();
|
|
if (!takeOut.disabled) takeOut.click();
|
|
}
|
|
});
|
|
|
|
if (webview) {
|
|
webview.addEventListener("message", function (event) {
|
|
if (event.data && event.data.type === "state" && event.data.payload) {
|
|
localBusy = false;
|
|
render(event.data.payload);
|
|
}
|
|
});
|
|
} else {
|
|
setAllDisabled(true);
|
|
status.textContent = "WebView2 송출 브리지를 사용할 수 없습니다.";
|
|
status.title = status.textContent;
|
|
}
|
|
}());
|