From 6c4896d3aa9d618f024b54c8d51f25ca3f625744 Mon Sep 17 00:00:00 2001 From: Kyle Gunger Date: Fri, 15 Oct 2021 21:01:22 -0400 Subject: [Themes] Theme selection and small fixes --- index.html | 330 ++++++++++++++++++++++++----------------- scripts/client.js | 9 +- scripts/cookie.js | 6 +- scripts/gui/chat.js | 25 +++- scripts/gui/input.js | 37 ++++- scripts/gui/lobby.js | 391 +++++++++++++++++++++++-------------------------- scripts/gui/table.js | 18 ++- scripts/theme.js | 43 ++++++ styles/client/base.css | 1 + styles/home/base.css | 2 + styles/input.css | 2 + 11 files changed, 502 insertions(+), 362 deletions(-) diff --git a/index.html b/index.html index 362addb..93da590 100644 --- a/index.html +++ b/index.html @@ -1,140 +1,198 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - WebCards - - - -
-

WebCards

-
-
- - - - - - - -
This site uses cookies to store themes and previous servers. That is all.
-
You may choose to submit user generated data over this site (Such as player name or text chat). This site stores none of that data. Please check with the server owner for privacy policy and/or any legal issue.
-
- - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + WebCards + + + +
+

WebCards

+
+
+ + + + + +
+
+
+ + +
+ +
This site uses cookies to store themes and previous servers. That is all.
+
You may choose to submit user generated data over this site (Such as player name or text chat). This site stores none of that data. Please check with the server owner for privacy policy and/or any legal issue.
+ + +
+ + + + + + + \ No newline at end of file diff --git a/scripts/client.js b/scripts/client.js index 7f98c7b..07e2fce 100644 --- a/scripts/client.js +++ b/scripts/client.js @@ -55,6 +55,7 @@ class Client{ this.socket.addEventListener("handshake", this.handshake.bind(this)); this.socket.addEventListener("menu", this.menu.bind(this)); this.socket.addEventListener("game", this.game.bind(this)); + this.socket.addEventListener("chat", this.chat.bind(this)); this.lobby = new Lobby(document.getElementsByClassName("lobby")[0], this.socket); @@ -63,8 +64,8 @@ class Client{ this.table = new Table(document.getElementsByClassName("table")[0], this.drag, this.socket); this.chat = new Chat(document.getElementsByClassName("chat")[0], this.socket); - this.chat.addChannel("global"); - this.chat.switchChannel("global"); + this.chat.addChannel("Global"); + this.chat.switchChannel("Global"); this.settings = new Settings(DefaultUserOps); this.settings.putSettings(this.lobby.e.settings); @@ -164,7 +165,9 @@ class Client{ game (m) { switch (m.type) { - + case "move": + this.table.moveByID(m.data.card, m.data.deck, m.data.pos); + break; } } diff --git a/scripts/cookie.js b/scripts/cookie.js index d614af7..44a0be9 100644 --- a/scripts/cookie.js +++ b/scripts/cookie.js @@ -12,7 +12,7 @@ class Cookies { return ""; } - static setCookie(name, value, data = {}) { + static setCookie(name, value, data = {SameSite: "Strict"}) { let extra = ""; for(let key in data) @@ -26,11 +26,11 @@ class Cookies { static setYearCookie(name, value) { var date = new Date(Date.now()); date.setFullYear(date.getFullYear() + 1); - Cookies.setCookie(name, value, {expires: date.toUTCString()}); + Cookies.setCookie(name, value, {SameSite: "Strict", expires: date.toUTCString()}); } static removeCookie(name) { var date = new Date(0); - Cookies.setCookie(name, "", {expires: date.toUTCString()}); + Cookies.setCookie(name, "", {SameSite: "Strict", expires: date.toUTCString()}); } } diff --git a/scripts/gui/chat.js b/scripts/gui/chat.js index fa4b88d..8a5db9f 100644 --- a/scripts/gui/chat.js +++ b/scripts/gui/chat.js @@ -1,11 +1,17 @@ 'use strict'; class Chat { - constructor(e) + constructor(e, soc) { this.chats = []; this.root = e; + this.socket = soc; e.getElementsByClassName("toggle-chat")[0].onclick = this.toggle.bind(this); + let cin = e.getElementsByClassName("chat-input")[0]; + this.chatInput = cin.children[0]; + + cin.children[0].addEventListener("keydown", this.checkEnter.bind(this)); + cin.children[0].addEventListener("click", this.sendMessage.bind(this)); } getChannel (name) @@ -50,7 +56,7 @@ class Chat { b.onclick = this.switchChannel.bind(this, name); - b.innerText = name[0].toUpperCase() + name.slice(1).toLowerCase(); + b.innerText = name; d.className = "chat-text"; @@ -92,6 +98,21 @@ class Chat { }); } + checkEnter(e) + { + if(e.key === "Enter") { + this.sendMessage(); + } + } + + sendMessage () + { + var str = this.chatInput.value; + this.chatInput.value = ""; + this.socket.send("chat", str); + + } + recieveMessage (channel, msg) { let c = this.getChannel(channel); diff --git a/scripts/gui/input.js b/scripts/gui/input.js index f72dd91..a44784b 100644 --- a/scripts/gui/input.js +++ b/scripts/gui/input.js @@ -199,6 +199,10 @@ class MakeInput { var wrapper = MakeInput.wrapInputs("select", se); wrapper.getValue = MakeInput.selValue.bind(null, se); + wrapper.getIndex = MakeInput.selIndex.bind(null, se); + wrapper.addOption = MakeInput.selAdd.bind(se); + wrapper.removeOption = MakeInput.selRem.bind(se); + wrapper.setIndex = MakeInput.selSet.bind(se); wrapper.setAttribute("tabindex", 0); return wrapper; @@ -213,18 +217,47 @@ class MakeInput { return ""; } + + static selIndex (el) { + return parseInt(el.getAttribute("selected")); + } - static selOption (el) { + static selOption (el, dispatch = true) { let sn = el.selectIndex; let psn = parseInt(el.parentElement.getAttribute("selected")); - if(Number.isInteger(psn)) + if(Number.isInteger(psn) && psn < el.parentElement.childElementCount) el.parentElement.children[psn].setAttribute("selected", false); el.parentElement.setAttribute("selected", sn); el.setAttribute("selected", true); + + if(dispatch) + el.parentElement.parentElement.dispatchEvent(new InputEvent("change")); } + static selSet (index) { + MakeInput.selOption(this.children[index], false); + } + + static selAdd (name, value) { + this.appendChild(MakeInput.selectOption(value, name, this.childElementCount, false)); + return this.childElementCount - 1; + } + + static selRem (index) { + if(index >= this.childElementCount) + return false; + + this.children[index].remove(); + + for(let i = index; i < this.childElementCount; i++) { + this.children[index].selectIndex = "" + i; + } + + return true; + } + static titleWrap(el, title) { var wrapper = document.createElement("div"); wrapper.className = "input-title-wrapper"; diff --git a/scripts/gui/lobby.js b/scripts/gui/lobby.js index 13f3eab..8242843 100644 --- a/scripts/gui/lobby.js +++ b/scripts/gui/lobby.js @@ -6,43 +6,43 @@ // TopBar represents the bar at the top of the screen when client is in the lobby. class TopBar{ - constructor (el, desktopSettings) - { - this.root = el; - this.desktopSettings = desktopSettings; - - this.newGame = el.getElementsByClassName("new-game")[0]; - this.mobileSettings = el.getElementsByClassName("mobile-settings")[0]; - this.status = el.getElementsByClassName("status")[0]; - } - - // Set color of status bar - setStatus (s) { - this.status.setAttribute("s", s); - } - - // Toggle showing the new game screen - toggleNewGame () { - if (this.newGame.style.display !== "none") - this.newGame.style.display = "none"; - else - this.newGame.style.display = "block"; - } - - // Toggle showing the mobile settings - toggleMobileSettings (settings) { - if (this.mobileSettings.style.display !== "none"){ - this.mobileSettings.style.display = "none"; - settings.putSettings(this.desktopSettings); - } else { - this.mobileSettings.style.display = "block"; - settings.putSettings(this.mobileSettings); - } - } - - mobileSettingsOpen() { - return this.mobileSettings.style.display !== "none" - } + constructor (el, desktopSettings) + { + this.root = el; + this.desktopSettings = desktopSettings; + + this.newGame = el.getElementsByClassName("new-game")[0]; + this.mobileSettings = el.getElementsByClassName("mobile-settings")[0]; + this.status = el.getElementsByClassName("status")[0]; + } + + // Set color of status bar + setStatus (s) { + this.status.setAttribute("s", s); + } + + // Toggle showing the new game screen + toggleNewGame () { + if (this.newGame.style.display !== "none") + this.newGame.style.display = "none"; + else + this.newGame.style.display = "block"; + } + + // Toggle showing the mobile settings + toggleMobileSettings (settings) { + if (this.mobileSettings.style.display !== "none"){ + this.mobileSettings.style.display = "none"; + settings.putSettings(this.desktopSettings); + } else { + this.mobileSettings.style.display = "block"; + settings.putSettings(this.mobileSettings); + } + } + + mobileSettingsOpen() { + return this.mobileSettings.style.display !== "none" + } } // ############# @@ -50,22 +50,32 @@ class TopBar{ // ############# // Game represents a single game in the lobby view. It has methods for setting up the elements and such. -function createGameEl (options = {id: 0, name: ""}, el) -{ - let e = document.createElement("div"); - e.className = "game"; - - let title = document.createElement("h2"); - title.textContent = options.name; - e.appendChild(title); - - let join = document.createElement("button"); - join.className = "join"; - join.textContent = "Join"; - join.addEventListener("click", game.joinGame.bind(game, options.id)); - e.appendChild(join); - - el.appendChild(e); +class Game { + constructor(options = {id: 0, name: ""}, el) + { + this.getID = function () { + return options.id; + } + + this.getName = function () { + return options.name; + } + + let e = document.createElement("div"); + e.className = "game"; + + let title = document.createElement("h2"); + title.textContent = options.name; + e.appendChild(title); + + let join = document.createElement("button"); + join.className = "join"; + join.textContent = "Join"; + join.addEventListener("click", game.joinGame.bind(game, options.id)); + e.appendChild(join); + + el.appendChild(e); + } } // ############## @@ -74,160 +84,123 @@ function createGameEl (options = {id: 0, name: ""}, el) // Lobby manages the players and games provided by the server and allows users to join or create their own games. class Lobby { - constructor (el) - { - this.root = el; - - this.e = { - status: el.getElementsByClassName("status")[0], - addr: el.getElementsByClassName("addr")[0], - games: el.getElementsByClassName("games")[0], - settings: el.getElementsByClassName("settings")[0], - - stats: { - game: document.getElementById("game"), - packs: document.getElementById("packs"), - online: document.getElementById("online"), - ingame: document.getElementById("ingame"), - pubgame: document.getElementById("pubgame") - } - }; - - this.top = new TopBar( - document.getElementsByClassName("topbar")[0], - this.e.settings - ); - - this.init = false; - this.online = []; - this.games = []; - this.packs = []; - this.players = []; - } - - // Set initial pack list - // {data array} array of strings representing pack names - packList (data) { - this.packs = data; - this.top.setPacks(this.packs) - this.e.stats.packs.innerText = this.packs.length(); - } - - // Set initial game list. - // { data object } object containing {games} and {name} - // { data.name string } name of the game the server runs - // { data.games array } array of public games the server is running - // { data.games[n].name } room name - // { data.games[n].packs } list of the pack names used by this game - // { data.games[n].id } room identifier (uuid) - // { data.games[n].max } max players in room - gameList (data) { - while (this.e.games.firstChild != null) { - this.e.games.remove(this.elements.games.firstChild) - } - - for (let i in data.games) { - let gel = new Game(i.name, i.packs, i.id); - this.games.push(gel); - this.e.games.appendChild(gel.getElement()); - } - - this.e.stats.game.innerText = data.name; - this.e.stats.pubgame.innerText = this.games.length(); - } - - // Set the initial player list. - // { data array } represents a list of player objects from the server - // { data[n].name string } name of the player - // { data[n].game string } id of the game room (empty if not in game). - // { data[n].color string } css color chosen by player. - // { data[n].uuid string } uuid of the player - players (data) { - - this.e.stats.online.innerText = this.players.length(); - this.init = true; - } - - // Called when a new public game is created on the server - // { data object } the game object - // { data.name } room name - // { data.packs } list of the pack names used by this game - // { data.id } room identifier (uuid) - addGame (data) { - createGameEl(data, this.e.games); - } - - // Called when a new public game is removed on the server - // { data string } the uuid of the game to delete - removeGame (data) { - - } - - // Called when a new player enters the lobby. - // { data object } an object representing the player - // { data.name string } name of the player - // { data.game string } id of the game room (empty if not in game). - // { data.color string } css color chosen by player. - // { data.uuid string } uuid of the player - addPlayer (data) { - - } - - // Called when a player modifies their settings in the lobby. - // { data object } new player settings - // { data.name string } non null if the player has changed their name - // { data.color string } non null if the player has changed their color - // { data.uuid string } uuid of player changing their settings - modPlayer (data) { - - } - - // Called when a player moves between the lobby and a game, or between two games - // { data object } new location - // { data.player } uuid of player changing location - // { data.loc } uuid of room player is moving to (empty if moving to lobby) - movePlayer (data) { - - } - - // Called when a player exits the game (from lobby or game) - // { data string } uuid of player - removePlayer (data) { - - } - - // Called when the client wants to toggle the new game screen - newGame () { - //if(this.init) return; - this.top.toggleNewGame(); - } - - // Called when the client wants to toggle the mobile settings screen - mobileSettings (settings) { - //if(this.init) return; - this.top.toggleMobileSettings(settings); - } - - // Called when the WebSocket state has changed. - setState (text, s, server) { - this.e.status.setAttribute("s", s); - if(this.e.status.innerText != "Error" || ( this.e.status.innerText == "Error" && text != "Closed")) - this.e.status.innerText = text; - this.e.addr.innerText = server; - this.top.setStatus(s); - } - - getState () { - return this.e.status.innerText.toLowerCase(); - } - - // Called when we are resetting the game. - reset () { - while (this.e.games.firstElementChild != null) { - this.e.games.removeChild(this.e.games.firstElementChild) - } - - this.setState("Connecting", "loading", this.e.addr.innerText); - this.init = false; - } + constructor (el) + { + this.root = el; + + this.e = { + status: el.getElementsByClassName("status")[0], + addr: el.getElementsByClassName("addr")[0], + games: el.getElementsByClassName("games")[0], + settings: el.getElementsByClassName("settings")[0], + + stats: { + game: document.getElementById("game"), + packs: document.getElementById("packs"), + online: document.getElementById("online"), + ingame: document.getElementById("ingame"), + pubgame: document.getElementById("pubgame") + } + }; + + this.top = new TopBar( + document.getElementsByClassName("topbar")[0], + this.e.settings + ); + + this.init = false; + this.online = []; + this.games = []; + this.packs = []; + } + + // Set initial pack list + // {data array} array of strings representing pack names + packList (data) { + this.packs = data; + this.top.setPacks(this.packs) + this.e.stats.packs.innerText = this.packs.length(); + } + + // Set initial game list. + // { data object } object containing {games} and {name} + // { data.name string } name of the game the server runs + // { data.games array } array of public games the server is running + // { data.games[n].name } room name + // { data.games[n].packs } list of the pack names used by this game + // { data.games[n].id } room identifier (uuid) + // { data.games[n].max } max players in room + gameList (data) { + while (this.e.games.firstChild != null) { + this.e.games.remove(this.elements.games.firstChild) + } + + for (let i of data.games) { + let g = new Game(i, this.e.games); + this.games.push(g); + } + + this.e.stats.game.innerText = data.name; + this.e.stats.pubgame.innerText = this.games.length(); + } + + // Set the initial player list. + // { data object } player statistics from the server + // { data.online number } players on server + // { data.ingame number } players on server and in game + players (data) { + this.e.stats.online.innerText = data.online; + this.e.stats.ingame.innerText = data.ingame; + } + + // Called when a new public game is created on the server + // { data object } the game object + // { data.name } room name + // { data.packs } list of the pack names used by this game + // { data.id } room identifier (uuid) + addGame (data) { + let g = new Game(data, this.e.games); + this.games.push(g); + } + + // Called when a new public game is removed on the server + // { data string } the uuid of the game to delete + removeGame (data) { + + } + + // Called when the client wants to toggle the new game screen + newGame () { + //if(this.init) return; + this.top.toggleNewGame(); + } + + // Called when the client wants to toggle the mobile settings screen + mobileSettings (settings) { + //if(this.init) return; + this.top.toggleMobileSettings(settings); + } + + // Called when the WebSocket state has changed. + setState (text, s, server) { + this.e.status.setAttribute("s", s); + if(this.e.status.innerText != "Error" || ( this.e.status.innerText == "Error" && text != "Closed")) + this.e.status.innerText = text; + this.e.addr.innerText = server; + this.top.setStatus(s); + } + + getState () { + return this.e.status.innerText.toLowerCase(); + } + + // Called when we are resetting the game. + reset () { + while (this.e.games.firstElementChild != null) { + this.e.games.removeChild(this.e.games.firstElementChild) + } + + this.setState("Connecting", "loading", this.e.addr.innerText); + this.init = false; + } } diff --git a/scripts/gui/table.js b/scripts/gui/table.js index 0d21262..9aac2ae 100644 --- a/scripts/gui/table.js +++ b/scripts/gui/table.js @@ -83,7 +83,7 @@ class Table{ return null; } - moveCard(card, newDeck) + moveCard(card, newDeck, index = -1) { for(let d of this.decks) { @@ -91,10 +91,14 @@ class Table{ break; } card.resetPos(); - newDeck.appendCard(card); + + if(index < 0) + newDeck.appendCard(card); + else + newDeck.addCardAt(card, index); } - moveByID(cardID, deckID) + moveByID(cardID, deckID, index = -1) { let card, deck; for(let d of this.decks) @@ -107,12 +111,12 @@ class Table{ deck = d; } - this.moveCard(card, deck); + this.moveCard(card, deck, index); } - checkMove(cardID, deckID) + checkMove(cardID, deckID, index = -1) { - this.socket.send("game", {}); + this.socket.send("game", {type: "move", card: cardID, deck: deckID, pos: index}); } dragCheck(cap) @@ -131,7 +135,7 @@ class Table{ if(c !== null) { if(d !== null) - this.moveCard(c, d); + this.checkMove(c.getID(), d.getID()); else c.resetPos(); } diff --git a/scripts/theme.js b/scripts/theme.js index 627b833..b9dcb37 100644 --- a/scripts/theme.js +++ b/scripts/theme.js @@ -1,10 +1,28 @@ 'use strict'; +const BASE_THEMES = [[ + "styles/themes/colors-base.css", + "styles/themes/colors-dark.css" +], +[ + "Light", + "Dark" +]]; + class Theme{ static theme = document.getElementById("theme"); + static UserThemes = [[],[]]; static init() { + let uth = Cookies.getCookie("userThemes").split(','); + + for (let i = 1; i < uth.length; i += 2) + { + this.UserThemes[0].push(uth[i - 1]); + this.UserThemes[1].push(uth[i]); + } + if(Cookies.getCookie("theme") == ""){ Cookies.setYearCookie("theme", "styles/themes/colors-base.css"); } @@ -21,6 +39,31 @@ class Theme{ Cookies.setYearCookie("theme", sheet); Theme.theme.setAttribute("href", sheet + "?v=" + Date.now()); } + + static setUserThemes() { + let out = ""; + for (let i = 0; i < this.UserThemes[0].length; i++) + { + if(i !== 0) + out = out + ","; + + out = out + this.UserThemes[0][i] + "," + this.UserThemes[1][i]; + } + + Cookies.setYearCookie("userThemes", out); + } + + static removeUserTheme (index) { + this.UserThemes[0].splice(index, 1); + this.UserThemes[1].splice(index, 1); + this.setUserThemes(); + } + + static addUserTheme (name, value) { + this.UserThemes[0].push(name); + this.UserThemes[1].push(value); + this.setUserThemes(); + } } Theme.restore(); \ No newline at end of file diff --git a/styles/client/base.css b/styles/client/base.css index 635fc78..a0178fd 100644 --- a/styles/client/base.css +++ b/styles/client/base.css @@ -1,5 +1,6 @@ * { font-family: 'Montserrat', sans-serif; + transition-duration: 0.2s; } html, body { diff --git a/styles/home/base.css b/styles/home/base.css index 9cd2a2f..3c8e321 100644 --- a/styles/home/base.css +++ b/styles/home/base.css @@ -1,5 +1,6 @@ * { font-family: 'Montserrat', sans-serif; + transition-duration: 0.2s; } html, body { @@ -25,6 +26,7 @@ div.content { padding: 20px; background-color: var(--gui-bg-main); + color: var(--main-color); border-radius: 10px; display: flex; diff --git a/styles/input.css b/styles/input.css index 6f99fb5..8101ae2 100644 --- a/styles/input.css +++ b/styles/input.css @@ -223,6 +223,8 @@ div.input-select overflow-x: hidden; max-height: 8em; + + border-radius: 3px; } div.input-select > div -- cgit v1.2.3