var win = window; var poppedOut = false; const setupChatboxEvents = (socket) => { // clear events by just reconstructing the form const oldChatForm = win.document.querySelector("#chatbox-send"); const chatForm = oldChatForm.cloneNode(true); oldChatForm.replaceWith(chatForm); if (!poppedOut) { setupPopoutEvent(socket); } chatForm.addEventListener("submit", (e) => { e.preventDefault(); const input = chatForm.querySelector("input"); const content = input.value; if (content.trim().length) { input.value = ""; socket.send( JSON.stringify({ op: "ChatMessage", data: { message: content, }, }) ); } }); }; const setupPopoutEvent = (socket) => { const button = document.querySelector("#pop-chat"); button.addEventListener("click", () => { openPopout(socket); }); }; const fixChatSize = () => { const video = document.querySelector("video"); const chatbox = document.querySelector("#chatbox"); const chatboxContainer = document.querySelector("#chatbox-container"); if (video && chatbox && chatboxContainer) { const delta = chatboxContainer.clientHeight - chatbox.clientHeight; chatbox.style["height"] = `calc(${ window.innerHeight - video.clientHeight }px - ${delta}px - 1em)`; } }; // TODO: transfer chat history to popout and back? const openPopout = async (socket) => { win.document.querySelector("#chatbox-container").style["display"] = "none"; const popout = window.open("/chat.html", "popout", "width=800,height=1000"); win = popout; poppedOut = true; // I am aware of the fact that this is incredibly cursed, // but apparently this is the only way to wait for a child window to load.... // I love browsers :) function defer(callback) { var channel = new MessageChannel(); channel.port1.onmessage = function (e) { callback(); }; channel.port2.postMessage(null); } win.addEventListener("unload", () => { defer(() => { if (popout.document.readyState === "complete") { setupChat(socket); // pop the chat back into the parent window on close win.addEventListener("unload", () => { popBackChat(socket); }); } else { win.document.addEventListener("DOMContentLoaded", () => { setupChat(socket); // pop the chat back into the parent window on close win.addEventListener("unload", () => { popBackChat(socket); }); }); } }); }); }; const popBackChat = async (socket) => { win = window; poppedOut = false; setupChat(socket); }; const resizeCallback = () => { fixChatSize(); }; /** * @param {WebSocket} socket */ export const setupChat = async (socket) => { win.document.querySelector("#chatbox-container").style["display"] = "block"; setupChatboxEvents(socket); if (!poppedOut) { fixChatSize(); window.addEventListener("resize", resizeCallback); } else { window.removeEventListener("resize", resizeCallback); } }; const printToChat = (elem) => { const chatbox = win.document.querySelector("#chatbox"); chatbox.appendChild(elem); chatbox.scrollTop = chatbox.scrollHeight; }; export const handleChatEvent = (event) => { switch (event.op) { case "UserJoin": { // print something to the chat const chatMessage = win.document.createElement("div"); chatMessage.classList.add("chat-message"); chatMessage.classList.add("user-join"); const userName = win.document.createElement("strong"); userName.textContent = event.data; chatMessage.appendChild(userName); chatMessage.appendChild(win.document.createTextNode(" joined")); printToChat(chatMessage); break; } case "UserLeave": { const chatMessage = win.document.createElement("div"); chatMessage.classList.add("chat-message"); chatMessage.classList.add("user-leave"); const userName = win.document.createElement("strong"); userName.textContent = event.data; chatMessage.appendChild(userName); chatMessage.appendChild(win.document.createTextNode(" left")); printToChat(chatMessage); break; } case "ChatMessage": { const chatMessage = win.document.createElement("div"); chatMessage.classList.add("chat-message"); const userName = win.document.createElement("strong"); userName.innerText = event.data.user; chatMessage.appendChild(userName); chatMessage.appendChild(win.document.createTextNode(" ")); const messageContent = win.document.createElement("span"); messageContent.classList.add("message-content"); messageContent.textContent = event.data.message; chatMessage.appendChild(messageContent); printToChat(chatMessage); break; } } };