Files
MBN_STOCK_WEBVIEW/src/MBN_STOCK_WEBVIEW.LegacyParityApp/Web/playout-ui.js

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;
}
}());