diff --git a/Public/play.js b/Public/play.js
new file mode 100644
index 0000000..6c647d9
--- /dev/null
+++ b/Public/play.js
@@ -0,0 +1,458 @@
+
+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 += "
Join "
+ } else {
+ html += "
Full "
+ }
+ 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 += "
" + content + " "
+ }
+ 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
+}
diff --git a/Public/storage.js b/Public/storage.js
index ac1b00b..7656b88 100644
--- a/Public/storage.js
+++ b/Public/storage.js
@@ -1,6 +1,7 @@
// Local storage element identifiers
const localStorageTokenId = "token";
const localStoragePlayerName = "name";
+const localStorageTableId = "table";
// Can prevent loading of session token, to allow multiple players per browser
const debugMode = false
@@ -11,28 +12,45 @@ const debugMode = false
* Parameter token: The session token for the player session
*/
function storePlayerNameAndToken(name, token) {
+ storePlayerName(name);
+ storeSessionToken(token);
+}
+
+function storePlayerName(name) {
localStorage.setItem(localStoragePlayerName, name);
- localStorage.setItem(localStorageTokenId, token);
+}
+
+function loadPlayerName() {
+ return localStorage.getItem(localStoragePlayerName);
+}
+
+function deletePlayerName() {
+ localStorage.removeItem(localStoragePlayerName);
}
/*
* Get the last session token from local storage.
*/
function loadSessionToken() {
- if (debugMode) {
- return debugSessionToken
- }
return localStorage.getItem(localStorageTokenId)
}
function storeSessionToken(token) {
- if (debugMode) {
- debugSessionToken = token
- return
- }
localStorage.setItem(localStorageTokenId, token)
}
function deleteSessionToken() {
- localStorage.removeItem(localStorageTokenId)
+ localStorage.removeItem(localStorageTokenId);
+}
+
+function storeTableId(table) {
+ localStorage.setItem(localStorageTableId, table);
+}
+
+function getTableId() {
+ return localStorage.getItem(localStorageTableId);
+}
+
+function deleteTableId() {
+ localStorage.removeItem(localStorageTableId);
}