const offlineText = "Offline" const missingPlayerText = "Leer" const elementIdActionBar = "action-bar" const elementIdTableName = "table-name-label" const elementIdAvailableGamesList = "available-games-list" const elementIdGameSummary = "game-summary" // The web socket to connect to the server var socket = null; function closeSocketIfNeeded() { if (socket) { socket.close() didCloseSocket() } } function didCloseSocket() { socket = null } function showAlertWithError(error) { window.alert(error.message) } function showLoginPage() { deleteSessionToken(); deleteTableId(); closeSocketIfNeeded(); window.location.href = "login.html"; } function showListPage() { deleteTableId(); closeSocketIfNeeded(); window.location.href = "list.html"; } function logoutUser() { const token = loadSessionToken() if (token == null) { showLoginPage(); return } performLogoutRequest(token) .then(function() { showLoginPage(); }).catch(showAlertWithError) } function loadExistingSession() { const token = loadSessionToken() if (token == null) { showLoginPage(); return } resumeSessionRequest(token) .then(function(name) { storePlayerName(name); loadCurrentTable(token) }).catch(function(error) { console.log("Failed to resume session"); console.log(error); showLoginPage(); }) } function loadCurrentTable(token) { performGetCurrentTableRequest(token) .then(function(table) { if (table == null) { showListPage(); } else { storeTableId(table.id); updateTableInfo(table); openSocket(token); } }) .catch(showAlertWithError) } function leaveTable() { const token = loadSessionToken() if (token == null) { showLoginPage(); return } performLeaveTableRequest(token) .then(function() { showListPage(); }) .catch(showAlertWithError) } function openSocket(token) { const socketPath = webSocketPath() socket = new WebSocket(socketPath) socket.onopen = function(e) { socket.send(token); showConnectedState() }; socket.onmessage = function(event) { const table = convertJsonResponse(event.data) updateTableInfo(table) }; socket.onclose = function(event) { if (event.wasClean) { } else { // e.g. server process killed or network down // event.code is usually 1006 in this case // TODO: Retry several times to open socket, // stop after too many failed attempts } didCloseSocket() showDisconnectedState() }; socket.onerror = function(error) { // error.message }; } function refreshTables() { const token = loadSessionToken() if (token == null) { showLoginPage() return } performGetPublicTablesRequest(token) .then(processTableList) .catch(showAlertWithError) } function processTableList(tables) { var html = createTableRow for (let i = 0, len = tables.length; i < len; i++) { tableInfo = tables[i] html += "
" if (tableInfo.players.length < 4) { html += "" } else { html += "" } html += "
" + tableInfo.name + "
" if (tableInfo.players.length == 0) { html += "
No players
" } else { const names = tableInfo.players.join(", ") html += "
" + names + "
" } html += "
" // table-row } setTableListContent(html) } function dealCards() { const token = loadSessionToken() if (token == null) { showLoginPage() return } performDealCardsRequest(token) .catch(showAlertWithError) } function playCard(card) { const token = loadSessionToken() if (token == null) { showLoginPage() return } performPlayCardRequest(token, card) .catch(function(error) { console.log(error) }) } function setDisplayStyle(id, style) { document.getElementById(id).style.display = style } function hide(elementId) { setDisplayStyle(elementId, "none") } function showTableName(name) { document.getElementById(elementIdTableName).innerHTML = name } function showConnectedState() { showPlayerState("bottom", "") } function showDisconnectedState() { showPlayerDisconnected("bottom") } function setEmptyPlayerInfo(position) { setTablePlayerName(position, null, false) showPlayerState(position, "") setTableCard(position, "", 1) } function setTablePlayerName(position, name, active) { const nameElement = document.getElementById("table-player-name-" + position) if (name == null) { nameElement.style.color = "var(--secondary-text-color)" nameElement.innerHTML = missingPlayerText } else { nameElement.style.color = active ? "var(--button-color)" : "var(--text-color)" nameElement.innerHTML = name } } function showPlayerDisconnected(position) { setPlayerState(position, "var(--alert-color)", offlineText) } function showPlayerState(position, state) { setPlayerState(position, "var(--secondary-text-color)", state) } function setPlayerState(position, color, text) { const connectionElement = "table-player-state-" + position const element = document.getElementById(connectionElement) element.style.color = color element.innerHTML = text } function setTableCard(position, card, layer) { const id = "table-player-card-" + position setCard(id, card, layer) } function setHandCard(index, card, playable) { const id = "player-card" + index.toString() setCard(id, card, 1) const button = document.getElementById(id) if (playable) { button.disabled = false button.onclick = function() { playCard(card) } } else { button.disabled = true button.onclick = function() { } } } function setCard(id, card, layer) { const element = document.getElementById(id) if (card == "") { element.style.backgroundColor = "var(--element-background)" element.style.backgroundImage = "" element.style.zIndex = 0 } else { element.style.backgroundColor = "" element.style.backgroundImage = "url('cards/" + card + ".jpeg')" element.style.zIndex = layer + 1 } } function updateTableInfo(table) { showTableName(table.name) // Set own cards const cardCount = table.cards.length for (let i = 0; i < cardCount; i += 1) { const card = table.cards[i] setHandCard(i+1, card.card, card.playable) } for (let i = cardCount; i < 8; i += 1) { setHandCard(i+1, "", false) } let playedGame = null if (table.hasOwnProperty("game")) { playedGame = textForAction(table.game) } // Show player info console.log(table) setInfoForPlayer(table.player, "bottom", playedGame) if (table.playerSelectsGame) { setActionsForOwnPlayer(table.playableGames) showAvailableGames([]) } else { if (table.player.active) { showAvailableGames(table.playableGames) } else { showAvailableGames([]) } setActionsForOwnPlayer(table.actions) } if (table.hasOwnProperty("playerLeft")) { setInfoForPlayer(table.playerLeft, "left", playedGame) } else { setEmptyPlayerInfo("left") } if (table.hasOwnProperty("playerAcross")) { setInfoForPlayer(table.playerAcross, "top", playedGame) } else { setEmptyPlayerInfo("top") } if (table.hasOwnProperty("playerRight")) { setInfoForPlayer(table.playerRight, "right", playedGame) } else { setEmptyPlayerInfo("right") } if (table.hasOwnProperty("summary")) { setGameSummary(table.summary) } else { hideGameSummary() } } function setGameSummary(summary) { document.getElementById(elementIdGameSummary).innerHTML = summary.text setDisplayStyle(elementIdGameSummary, "inherit") } function hideGameSummary() { hide(elementIdGameSummary) } function setInfoForPlayer(player, position, game) { var card = "" if (player.hasOwnProperty("card")) { card = player.card } const layer = player.position setTableCard(position, card, layer) setTablePlayerName(position, player.name, player.active) var state = player.state if (game != null && state.indexOf("selects") > -1) { const i = state.indexOf("selects") state[i] = game } const text = state.map(x => convertStateToString(x)).join("
") showPlayerState(position, text) } function doubleText(doubles) { if (doubles == 0) { return null } if (doubles == 1) { return "gedoppelt" } return doubles.toString() + "x gedoppelt" } function clearInfoForPlayer(position) { setEmptyPlayerInfo(position) } function setActionsForOwnPlayer(actions) { var len = actions.length var html = "" for (let i = 0; i < len; i += 1) { const action = actions[i] const content = textForAction(action) html += "" } document.getElementById(elementIdActionBar).innerHTML = html } function textForAction(action) { switch (action) { /// The player can request cards to be dealt case "deal": return "Austeilen" /// The player doubles on the initial four cards case "double": return "Legen" /// The player does not double on the initial four cards case "skip": return "Nicht legen" /// The player offers a wedding (one trump card) case "wedding": return "Hochzeit anbieten" /// The player can choose to accept the wedding case "accept": return "Hochzeit akzeptieren" /// The player matches or increases the game during auction case "bid": return "Spielen" /// The player does not want to play case "out": return "Weg" /// The player claims to win and doubles the game cost ("schießen") case "raise": return "Schießen" case "ruf": return "Ruf" case "ruf-eichel": return "Ruf Eichel" case "ruf-blatt": return "Ruf Blatt" case "ruf-schelln": return "Ruf Schelln" case "bettel": return "Bettel" case "wenz": return "Wenz" case "geier": return "Geier" case "solo": return "Solo" case "solo-eichel": return "Eichel Solo" case "solo-blatt": return "Blatt Solo" case "solo-herz": return "Herz Solo" case "solo-schelln": return "Schelln Solo" } return action } function performAction(action) { const token = loadSessionToken() if (token == null) { showBlankLogin() return } performPlayerActionRequest(token, action) // TODO: Handle errors and success } function showAvailableGames(games) { var len = games.length var html = "" for (let i = 0; i < len; i += 1) { const game = games[i] const content = textForAction(game) html += "
" + content + "
" } document.getElementById(elementIdAvailableGamesList).innerHTML = html }