From 1697da112a9b9f529fad2f54c62aecd7bbb614e6 Mon Sep 17 00:00:00 2001 From: Kyle Gunger Date: Fri, 3 Apr 2020 16:44:23 -0400 Subject: [WEBCARDS] Update some webcards stuff --- webcards/LICENSE | 21 +++ webcards/README.md | 5 + webcards/assets/standard/diamond.svg | 37 +++++ webcards/assets/standard/heart.svg | 33 ++++ webcards/client.html | 82 +++++++--- webcards/favicon.ico | Bin 0 -> 1150 bytes webcards/images/wc-icon-144.png | Bin 0 -> 5171 bytes webcards/images/wc-icon-288.png | Bin 0 -> 10018 bytes webcards/images/wc-icon-32.png | Bin 0 -> 1443 bytes webcards/images/wc-icon-48.png | Bin 0 -> 1898 bytes webcards/images/wc-icon-96.png | Bin 0 -> 3980 bytes webcards/index.html | 21 ++- webcards/scripts/cards/card.js | 97 ++++++++++++ webcards/scripts/cards/deck.js | 106 +++++++++++++ webcards/scripts/cards/drag.js | 83 ++++++++++ webcards/scripts/client.js | 109 +++++++++++++ webcards/scripts/client/client.js | 105 ------------- webcards/scripts/client/lobby.js | 102 ------------ webcards/scripts/client/message.js | 14 -- webcards/scripts/client/sock.js | 52 ------ webcards/scripts/client/table.js | 16 -- webcards/scripts/gui/input.js | 159 +++++++++++++++++++ webcards/scripts/gui/lobby.js | 196 +++++++++++++++++++++++ webcards/scripts/gui/table.js | 34 ++++ webcards/scripts/libs/discord-rpc.js | 8 - webcards/scripts/socket/message.js | 14 ++ webcards/scripts/socket/sock.js | 63 ++++++++ webcards/styles/client/base.css | 104 +++++++++--- webcards/styles/client/card.css | 116 ++++++++++++++ webcards/styles/client/desktop.css | 10 +- webcards/styles/client/mobile.css | 2 +- webcards/styles/client/tablet.css | 4 + webcards/styles/home/base.css | 8 +- webcards/styles/input.css | 296 +++++++++++++++++++++++++++++++++++ 34 files changed, 1541 insertions(+), 356 deletions(-) create mode 100644 webcards/LICENSE create mode 100644 webcards/README.md create mode 100644 webcards/assets/standard/diamond.svg create mode 100644 webcards/assets/standard/heart.svg create mode 100644 webcards/favicon.ico create mode 100644 webcards/images/wc-icon-144.png create mode 100644 webcards/images/wc-icon-288.png create mode 100644 webcards/images/wc-icon-32.png create mode 100644 webcards/images/wc-icon-48.png create mode 100644 webcards/images/wc-icon-96.png create mode 100644 webcards/scripts/cards/card.js create mode 100644 webcards/scripts/cards/deck.js create mode 100644 webcards/scripts/cards/drag.js create mode 100644 webcards/scripts/client.js delete mode 100644 webcards/scripts/client/client.js delete mode 100644 webcards/scripts/client/lobby.js delete mode 100644 webcards/scripts/client/message.js delete mode 100644 webcards/scripts/client/sock.js delete mode 100644 webcards/scripts/client/table.js create mode 100644 webcards/scripts/gui/input.js create mode 100644 webcards/scripts/gui/lobby.js create mode 100644 webcards/scripts/gui/table.js delete mode 100644 webcards/scripts/libs/discord-rpc.js create mode 100644 webcards/scripts/socket/message.js create mode 100644 webcards/scripts/socket/sock.js create mode 100644 webcards/styles/client/card.css create mode 100644 webcards/styles/input.css diff --git a/webcards/LICENSE b/webcards/LICENSE new file mode 100644 index 0000000..15d71cd --- /dev/null +++ b/webcards/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Kyle Gunger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/webcards/README.md b/webcards/README.md new file mode 100644 index 0000000..0c6b070 --- /dev/null +++ b/webcards/README.md @@ -0,0 +1,5 @@ +# WebCardsClient + +![WebCards](http://35.11.215.147:3000/CCGKyle/WebCardsClient/raw/branch/master/images/wc-icon-144.png) + +An in-browser client to play WebCards \ No newline at end of file diff --git a/webcards/assets/standard/diamond.svg b/webcards/assets/standard/diamond.svg new file mode 100644 index 0000000..41f7cc2 --- /dev/null +++ b/webcards/assets/standard/diamond.svg @@ -0,0 +1,37 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/webcards/assets/standard/heart.svg b/webcards/assets/standard/heart.svg new file mode 100644 index 0000000..8cc32e4 --- /dev/null +++ b/webcards/assets/standard/heart.svg @@ -0,0 +1,33 @@ + + + + + + + image/svg+xml + + + + + + + diff --git a/webcards/client.html b/webcards/client.html index 70d7f1d..0b06712 100644 --- a/webcards/client.html +++ b/webcards/client.html @@ -8,37 +8,54 @@ + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + WebCards - Client - @@ -96,6 +112,36 @@ var params = new URLSearchParams((new URL(window.location)).search); var game = new Client(params.get("s"), params.get("g")); setTimeout(game.init.bind(game), 100); + + + // Live testing purposes only + var d = new MultiDrag(); + var c1 = new Card({ + all: [ + { + type: "image", + image: "assets/standard/diamond.svg" + } + ] + }); + var c2 = new Card({ + all: [ + { + type: "image", + image: "assets/standard/heart.svg" + } + ] + }); + function test() { + c1.e.addEventListener("mousedown", d.startDragging.bind(d)); + c1.e.addEventListener("mouseup", d.stopDraggingAll.bind(d)); + c2.e.addEventListener("mousedown", d.startDragging.bind(d)); + c2.e.addEventListener("mouseup", d.stopDraggingAll.bind(d)); + + game.tab.root.append(c1.e); + game.tab.root.append(c2.e); + game.tab.openTable(); + } diff --git a/webcards/favicon.ico b/webcards/favicon.ico new file mode 100644 index 0000000..91e0742 Binary files /dev/null and b/webcards/favicon.ico differ diff --git a/webcards/images/wc-icon-144.png b/webcards/images/wc-icon-144.png new file mode 100644 index 0000000..ecc4084 Binary files /dev/null and b/webcards/images/wc-icon-144.png differ diff --git a/webcards/images/wc-icon-288.png b/webcards/images/wc-icon-288.png new file mode 100644 index 0000000..04f6a07 Binary files /dev/null and b/webcards/images/wc-icon-288.png differ diff --git a/webcards/images/wc-icon-32.png b/webcards/images/wc-icon-32.png new file mode 100644 index 0000000..ec390c5 Binary files /dev/null and b/webcards/images/wc-icon-32.png differ diff --git a/webcards/images/wc-icon-48.png b/webcards/images/wc-icon-48.png new file mode 100644 index 0000000..e787102 Binary files /dev/null and b/webcards/images/wc-icon-48.png differ diff --git a/webcards/images/wc-icon-96.png b/webcards/images/wc-icon-96.png new file mode 100644 index 0000000..a10d0eb Binary files /dev/null and b/webcards/images/wc-icon-96.png differ diff --git a/webcards/index.html b/webcards/index.html index c7d2f71..c322ec1 100644 --- a/webcards/index.html +++ b/webcards/index.html @@ -8,14 +8,24 @@ + + + + + + + + + + WebCards
-

WebCards

+

WebCards

- Connect +
diff --git a/webcards/scripts/cards/card.js b/webcards/scripts/cards/card.js new file mode 100644 index 0000000..015995d --- /dev/null +++ b/webcards/scripts/cards/card.js @@ -0,0 +1,97 @@ +var CardPos = ["top", "topl", "topr", "mid", "midt", "midb", "bot", "botl", "botr", "all"]; + +// Card class represents one card. +// Every card should have a deck. +// Use deck.appendCard or deck.prependCard to make a card visible +function Card (data) { + this.e = this.generateElements(data); + this.e.style.left = "0px"; + this.e.style.top = "0px"; +} + +// Internal +Card.prototype = { + // Main generation func + generateElements: function (data) { + switch (typeof data) { + case "object": + return this.generateObjectCard(data); + case "string": + return this.generateBasicCard(data); + } + let e = document.createElement("card"); + let t = document.createElement("carea"); + t.className = "mid"; + t.innerText = "Card Error: data"; + e.append(t); + return e; + }, + + // Generate a card with basic text only + generateBasicCard: function (data) { + let e = document.createElement("card"); + let t = document.createElement("carea"); + t.className = "mid"; + t.innerText = data; + e.appendChild(t); + return e; + }, + + // Generate a card with rich visuals + generateObjectCard: function (data) { + let e = document.createElement("card"); + + // Check for an asset URL + if (typeof data.assetURL != "string") { + data.assetURL = ""; + } + + // Set card styles + for (let i in data.style) { + e.style[i] = data.style[i]; + } + + // Generate card areas. + for (let i in CardPos) { + if (typeof data[CardPos[i]] == "object") + e.appendChild(this.generateCArea(data[CardPos[i]], CardPos[i], data.assetURL)); + } + + return e; + }, + + generateCArea: function (data, carea, assetURL) { + // Create and set area + let area = document.createElement("carea"); + area.className = carea; + + // Create inner area text and images + for (let i in data) { + if (i == "style") + for (j in data.style) + area.style[j] = data.style[j]; + + if (data[i].type == "text") { + let e = document.createElement("ctext"); + + e.innerText = data[i].text; + + for (let j in data[i].style) { + e.style[j] = data[i].style[j]; + } + + area.appendChild(e); + + } else if (data[i].type == "image") { + let e = document.createElement("cimage"); + + e.style.backgroundImage = "url(\"" + assetURL + data[i].image + "\")"; + + area.appendChild(e); + } + } + + return area; + } + +}; \ No newline at end of file diff --git a/webcards/scripts/cards/deck.js b/webcards/scripts/cards/deck.js new file mode 100644 index 0000000..544a9ef --- /dev/null +++ b/webcards/scripts/cards/deck.js @@ -0,0 +1,106 @@ +// Deck class represents multiple cards. +// Can be arranged in multiple ways. +function Deck (options = {}){ + this.cards = []; + + // View mode + // infdraw - infinite draw. always appears as if there are multiple cards + // stack - stack mode + // strip + // horizontal + // left (strip-hl) + // right (strip-hr) + // vertical + // top (strip-vt) + // bottom (strip-vb) + this.mode = options.mode; + + // Select mode + // above + // below + // around + // one + // all + this.smode = options.smode; + + // Select count (-1 = all available) + // above - controls number of cards above clicked are selected + // below - controls number of cards below clicked are selected + // around + // number - number above and below selected + // array - [first number: number above selected] [second number: number below selected] + // one - no effect + // all - no effect + this.sct = options.sct; + + // Position + // array of where the deck is centered + this.x = options.pos[0]; + this.y = options.pos[1]; + + this.e = document.createElement("deck"); +} + +Deck.prototype = { + // Add a card to the front of the deck + appendCard: function(card) { + this.cards.push(card); + this.e.appendChild(card.e); + }, + + // Add a card to the back of the deck + prependCard: function(card) { + this.cards.unshift(card); + this.e.prepend(card.e); + }, + + // Add a card at the index specified + addCardAt: function(card, index) { + if(index < 0 || index > this.cards.length) + return + + if(index == 0) { + this.prependCard(card); + } else if (index == this.cards.length) { + this.appendCard(card); + } else { + let temp = this.cards.slice(0, index); + temp[temp.length - 1].e.after(card.e); + temp.push(card); + this.cards.unshift(...temp); + } + }, + + // Swap the cards at the specified indexes + swapCard: function(index1, index2) { + if(index1 < 0 || index1 >= this.cards.length || index2 < 0 || index2 >= this.cards.length) + return + + var temp = this.cards[index1] + this.cards[index1] = this.cards[index2]; + this.cards[index2] = temp; + + this.cards[index1 - 1].e.after(this.cards[index1]); + this.cards[index2 - 1].e.after(this.cards[index2]); + }, + + // Remove the card at the front of the deck (index length - 1), returns the card removed (if any) + removeFront: function() { + return this.removeCard(this.cards.length - 1); + }, + + // Remove the card at the back of the deck (index 0), returns the card removed (if any) + removeBack: function() { + return this.removeCard(0); + }, + + // Remove a card from the deck, returning the card element + removeCard: function(index) { + + if(index < 0 || index >= this.cards.length) + return + + this.e.removeChild(this.cards[index].e); + return this.cards.splice(index, 1)[0]; + } +}; \ No newline at end of file diff --git a/webcards/scripts/cards/drag.js b/webcards/scripts/cards/drag.js new file mode 100644 index 0000000..cce1e72 --- /dev/null +++ b/webcards/scripts/cards/drag.js @@ -0,0 +1,83 @@ +function MultiDrag() { + this.del = false; + this.drag = []; + window.addEventListener("mousemove", this.update.bind(this)); + document.body.addEventListener("mouseleave", this.stopDraggingAll.bind(this)); +} + +MultiDrag.prototype = { + addDragEl: function(el, ox, oy, px, py, pt) { + if(this.del) + return; + + el.style.transitionDuration = "0.04s"; + + this.drag.push({ + e: el, + osx: ox, + osy: oy, + prx: px, + pry: py, + ptd: pt + }); + + return this.drag.length - 1; + }, + + startDragging: function(mevent) { + if(this.del) + return; + + console.log(mevent); + + if(mevent.button != 0) + return; + + let pos = mevent.target.getBoundingClientRect(); + + return this.addDragEl( + mevent.currentTarget, + mevent.clientX - pos.left, + mevent.clientY - pos.top, + mevent.currentTarget.style.left, + mevent.currentTarget.style.top, + mevent.currentTarget.style.transitionDuration + ); + }, + + stopDragging: function(i) { + this.del = true; + + if (i < 0 || i >= this.drag.length) + return; + + this.drag[i].e.style.transitionDuration = this.drag[i].ptd; + this.drag[i].e.style.left = this.drag[i].prx; + this.drag[i].e.style.top = this.drag[i].pry; + + this.drag.splice(i, 1); + + this.del = false; + }, + + stopDraggingAll: function() { + this.del = true; + + while (this.drag.length > 0) { + this.drag[0].e.style.transitionDuration = this.drag[0].ptd; + this.drag[0].e.style.left = this.drag[0].prx; + this.drag[0].e.style.top = this.drag[0].pry; + + this.drag.shift(); + } + + this.del = false; + }, + + update: function(e) { + for (let i = 0; i < this.drag.length && !this.del; i++) { + this.drag[i].e.style.left = e.clientX - this.drag[i].osx + "px"; + this.drag[i].e.style.top = e.clientY - this.drag[i].osy + "px"; + } + } +}; \ No newline at end of file diff --git a/webcards/scripts/client.js b/webcards/scripts/client.js new file mode 100644 index 0000000..ea62e26 --- /dev/null +++ b/webcards/scripts/client.js @@ -0,0 +1,109 @@ +// Client acts as the message hub for the whole game. +// WebSocket messages come into Client and Client redirects them to the lobby or table based on the state of the game. +// Client also performs the handshake for first starting the connection and messages everyone if the connection errors or closes. +function Client(serveraddr, game) { + this.state = "handshake"; + + this.soc = new SockWorker(serveraddr, "1", this.cb.bind(this)); + + this.lob = new Lobby(document.getElementsByClassName("lobby")[0], this.soc); + this.tab = new Table(document.getElementsByClassName("table")[0], this.soc); + + this.game = game; +} + +Client.prototype = { + // Initialize the connection + init: function() { + this.soc.init(); + }, + + // Entry point for a message from the server. + // If it's a close message, we close the game if it is open and change the lobby to reflect the error/close. + cb: function(m) { + console.log(m); + + if(m.type == "error" || m.type == "closed") { + var t = m.type; + t = t[0].toUpperCase() + t.slice(1) + this.lob.setState(t, "#D00", this.soc.server); + this.tab.handleClose(); + return; + } + + switch(this.state) { + case "handshake": + this.handshake(m); + break; + case "lobby": + this.lobby(m); + break; + case "game": + break; + } + }, + + // Called when negotiating with the server for the first time and we are determining versions + handshake: function(m) { + switch (m.type) { + case "verr": + this.soc.close(); + alert(`Error connecting to server: version of client (${this.version}) not accepted.`); + console.error("Error connecting to server: version of client (${this.version}) not accepted."); + console.error(m.data); + return; + case "lobby": + this.state = "lobby"; + this.soc.send("ready", ""); + return; + } + }, + + // Lobby switch, called when in the lobby and a message arrives from the server + lobby: function (m) { + switch (m.type) { + case "plist": + this.lob.packList(m.data); + break; + case "glist": + this.lob.gameList(m.data, this.game); + this.game = null; + break; + case "players": + this.lob.players(m.data); + break; + case "gdel": + this.lob.removeGame(m.data); + break; + case "gadd": + this.lob.addGame(m.data); + break; + case "pdel": + this.lob.removePlayer(m.data); + break; + case "padd": + this.lob.addPlayer(m.data); + break; + case "pmove": + this.lob.movePlayer(m.data); + break; + } + }, + + // Game switch, called when in game and a message arrives from the server + game: function (m) { + switch (m.type) { + + } + }, + + // Reset the lobby and table, then attempt to reopen the connection to the server. + reset: function() { + this.state = "handshake"; + + this.lob.reset(); + this.tab.reset(); + + this.soc.init(); + } +}; diff --git a/webcards/scripts/client/client.js b/webcards/scripts/client/client.js deleted file mode 100644 index b5dd4bf..0000000 --- a/webcards/scripts/client/client.js +++ /dev/null @@ -1,105 +0,0 @@ -// Client acts as the message hub for the whole game. -// WebSocket messages come into Client and Client redirects them to the lobby or table based on the state of the game. -// Client also performs the handshake for first starting the connection and messages everyone if the connection errors or closes. -function Client(serveraddr, game) { - this.state = "handshake"; - - this.soc = new SockWorker(serveraddr, "1", this.cb.bind(this)); - - this.lob = new Lobby(document.getElementsByClassName("lobby")[0], this.soc); - this.tab = new Table(document.getElementsByClassName("table")[0], this.soc); - - this.game = game; -} - -Client.prototype = { - init: function() { - this.soc.init(); - }, - - // Entry point for a message. - // If it's a close message, the - cb: function(m) { - console.log(m); - - if(m.type == "error" || m.type == "closed") { - var t = m.type; - t = t[0].toUpperCase() + t.slice(1) - this.lob.setState(t, "#D00", this.soc.server); - this.tab.handleClose(); - return; - } - - switch(this.state) { - case "handshake": - this.handshake(m); - break; - case "lobby": - this.lobby(m); - break; - case "game": - break; - } - }, - - handshake: function(m) { - switch (m.type) { - case "verr": - this.soc.close(); - alert(`Error connecting to server: version of client (${this.version}) not accepted.`); - console.error("Error connecting to server: version of client (${this.version}) not accepted."); - console.error(m.data); - return; - case "lobby": - this.state = "lobby"; - this.soc.send("ready", ""); - return; - } - }, - - lobby: function (m) { - switch (m.type) { - case "plist": - this.lob.packList(m.data); - break; - case "glist": - this.lob.gameList(m.data, this.game); - this.game = null; - break; - case "players": - this.lob.players(m.data); - break; - case "gdel": - this.lob.removeGame(m.data); - break; - case "gadd": - this.lob.addGame(m.data); - break; - case "pdel": - this.lob.removePlayer(m.data); - break; - case "padd": - this.lob.addPlayer(m.data); - break; - case "pmove": - this.lob.movePlayer(m.data); - break; - } - }, - - game: function (m) { - switch (m.type) { - - } - }, - - // Reset the lobby and table, then attempt to reopen the connection to the server. - reset: function() { - this.state = "handshake"; - - this.lob.reset(); - this.tab.reset(); - - this.soc.init(); - } -}; diff --git a/webcards/scripts/client/lobby.js b/webcards/scripts/client/lobby.js deleted file mode 100644 index 8d46352..0000000 --- a/webcards/scripts/client/lobby.js +++ /dev/null @@ -1,102 +0,0 @@ -// Lobby manages the players and games provided by the server and allows users to join or create their own games. -function Lobby(el){ - this.root = el; - - this.elements = { - status: this.root.getElementsByClassName("status")[0], - addr: this.root.getElementsByClassName("addr")[0], - - stats: { - game: document.getElementById("game"), - packs: document.getElementById("packs"), - online: document.getElementById("online"), - ingame: document.getElementById("ingame"), - pubgame: document.getElementById("pubgame") - }, - - settings: { - name: document.getElementById("name"), - color: document.getElementById("usercolor") - } - }; - - this.top = new TopBar(document.getElementsByClassName("topbar")[0]); - - this.init = false; - this.online = []; - this.games = []; - this.packs = []; - this.players = []; -} - -Lobby.prototype = { - packList: function(data){ - - this.elements.stats.packs.innerText = this.packs.length(); - }, - - gameList: function(data, game){ - - this.elements.stats.pubgame.innerText = this.games.length(); - }, - - players: function(data) { - - this.elements.stats.online.innerText = this.players.length(); - this.init = true; - }, - - addGame: function(data){ - - }, - - removeGame: function(data){ - - }, - - addPlayer: function(data){ - - }, - - movePlayer: function(data){ - - }, - - removePlayer: function(data){ - - }, - - newGameScreen: function(){ - if(this.init) return; - }, - - setState: function(text, color, server){ - this.elements.status.style.backgroundColor = color; - this.elements.status.innerText = text; - this.elements.addr.innerText = server; - this.top.setColor(color); - }, - - reset: function(){ - this.setState("Connecting", "#DA0", this.elements.addr.innerText); - this.init = false; - } -}; - -// ############### -// # TopBar Code # -// ############### - -function TopBar(el) { - this.root = el; - - this.newGame = el.getElementsByClassName("new-game")[0]; - this.mobileSettings = el.getElementsByClassName("mobile-settings")[0]; - this.status = el.getElementsByClassName("status")[0]; -} - -TopBar.prototype = { - setColor: function(color) { - this.status.style.backgroundColor = color; - } -}; diff --git a/webcards/scripts/client/message.js b/webcards/scripts/client/message.js deleted file mode 100644 index 5e821c4..0000000 --- a/webcards/scripts/client/message.js +++ /dev/null @@ -1,14 +0,0 @@ -function Message(type, data){ - this.t = type; - this.d = data; -} - -Message.prototype = { - stringify: function(){ - var dat = this.d - if(typeof dat !== "string"){ - dat = JSON.stringify(dat); - } - return JSON.stringify({type: this.t, data: dat}); - } -}; diff --git a/webcards/scripts/client/sock.js b/webcards/scripts/client/sock.js deleted file mode 100644 index 78c4195..0000000 --- a/webcards/scripts/client/sock.js +++ /dev/null @@ -1,52 +0,0 @@ -function SockWorker(serveraddr, version, callback) { - this.server = serveraddr; - this.version = version; - this.cb = callback; -} - -SockWorker.prototype = { - init: function() { - if(this.server == "" || this.server == null) { - return; - } - try { - this.socket = new WebSocket(this.server); - - this.socket.addEventListener("open", this.o.bind(this)); - this.socket.addEventListener("message", this.msg.bind(this)); - - this.socket.addEventListener("closed", this.c.bind(this)); - this.socket.addEventListener("error", this.err.bind(this)); - } catch (e) { - this.err(); - } - }, - - o: function() { - this.send("version", this.version); - }, - - msg: function(e) { - if(typeof e.data == "string") { - var dat = JSON.parse(e.data) - this.cb(dat); - } - }, - - c: function() { - this.cb({type: "close", data: ""}); - }, - - err: function() { - this.cb({type: "error", data: ""}); - }, - - close: function() { - this.socket.close(); - }, - - send: function(type, data) { - var m = new Message(type, data); - this.socket.send(m.stringify()) - } -}; \ No newline at end of file diff --git a/webcards/scripts/client/table.js b/webcards/scripts/client/table.js deleted file mode 100644 index 911763a..0000000 --- a/webcards/scripts/client/table.js +++ /dev/null @@ -1,16 +0,0 @@ -// Table represents and manages the actual game. It accepts inputs from the server and tries to queries the server when the player makes a move. -function Table(el, soc) { - this.root = el; - this.soc = soc; -} - -Table.prototype = { - - handleClose: function() { - - }, - - reset: function() { - - } -} \ No newline at end of file diff --git a/webcards/scripts/gui/input.js b/webcards/scripts/gui/input.js new file mode 100644 index 0000000..c349a07 --- /dev/null +++ b/webcards/scripts/gui/input.js @@ -0,0 +1,159 @@ +var inputFuncs = { + createInput: function(type = "text", wrapped = false, id) { + var el = document.createElement("input"); + el.setAttribute("type", type); + + if(typeof id == "string") + el.setAttribute("id", id); + + if(wrapped) { + var wrapper = document.createElement("div"); + wrapper.className = "input-container"; + wrapper.setAttribute("type", type); + wrapper.setAttribute("onclick", "this.firstElementChild.click()"); + wrapper.appendChild(el); + wrapper.input = el; + return wrapper; + } + + el.getValue = function () { + return this.value; + } + return el; + }, + + inputLabel(text, id) { + var el = document.createElement("label"); + el.innerText = text; + if(typeof id == "string") + el.setAttribute("for", id); + return el; + }, + + colorInput: function(value, id) { + var el = this.createInput("color", true, id); + el.value = value; + return el; + }, + + textInput: function(value, placeholder, id) { + var el = this.createInput("text", false, id); + el.setAttribute("placeholder", placeholder); + el.value = value; + return el; + }, + + numberInput: function(value, id) { + var el = this.createInput("number", false, id); + el.value = value; + return el; + }, + + fileInput: function(value, id) { + var el = this.createInput("file", true, id); + + el.value = value; + + el.setAttribute("data-files", "Choose a file"); + + el.firstElementChild.onchange = function () { + let text = ""; + switch (this.files.length) { + case 0: + text = "Choose a file"; + break; + case 1: + text = "File: " + this.files[0].name; + break; + default: + text = "Files: " + this.files[0].name + "..."; + break; + } + el.setAttribute("data-files", text); + } + + return el; + }, + + checkboxInput: function(checked = false, id) { + var el = this.createInput("checkbox", false, id); + if(checked) + el.setAttribute("checked"); + return el; + }, + + radioInput: function(group, value, checked = false, id) { + var el = this.createInput("radio", false, id); + el.setAttribute("name", group); + el.setAttribute("value", value); + if(checked) + el.checked = true; + return el; + }, + + radioInputs: function(group, names, values, checked = 0) { + var wrapper = document.createElement("div"); + wrapper.className = "input-container"; + wrapper.setAttribute("type", "radio"); + + wrapper.getValue = function() { + for(let i = 0; i < this.children.length; i++){ + if(this.children[i].checked) + return this.children[i].value; + } + }; + + for(let i = 0; i < values.length; i++) { + wrapper.appendChild(this.inputLabel(names[i], group+"-"+i)); + if(i == checked) + wrapper.appendChild(this.radioInput(group, values[i], true, group+"-"+i)); + else + wrapper.appendChild(this.radioInput(group, values[i], false, group+"-"+i)); + wrapper.appendChild(document.createElement("br")); + } + + return wrapper; + }, + + wrapInput: function(el) { + + } +}; + +function Settings () { + this.settings = { + username: { + type: "text", + args: [Math.floor(Math.random() * 100000), "Username", "userName"] + } + }; + + this.genSettings(); +} + +Settings.prototype = { + getSettings: function() { + var out = {}; + for(let key in this.settings) { + + } + }, + + putSettings: function (el) { + for(let key in this.settings) { + el.appendChild(this.settings[key]); + } + }, + + genSettings: function() { + for(let key in this.settings) { + switch(this.settings[key].type) { + case "radio": + this.settings[key] = inputFuncs.radioInputs(...this.settings[key].args); + default: + if(typeof inputFuncs[this.settings[key].type+"Input"] != null) + this.settings[key] = inputFuncs[this.settings[key].type+"Input"](...this.settings[key].args); + } + } + } +}; \ No newline at end of file diff --git a/webcards/scripts/gui/lobby.js b/webcards/scripts/gui/lobby.js new file mode 100644 index 0000000..731d9ec --- /dev/null +++ b/webcards/scripts/gui/lobby.js @@ -0,0 +1,196 @@ +// Lobby manages the players and games provided by the server and allows users to join or create their own games. +function Lobby(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.init = false; + this.online = []; + this.games = []; + this.packs = []; + this.players = []; +} + +Lobby.prototype = { + // Set initial pack list + // {data array} array of strings representing pack names + packList: function(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: function(data) { + while (this.e.games.firstChild != null) { + this.e.games.remove(this.elements.games.firstChild) + } + + for (let i in data.games) { + let gel = new GameEl(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: function(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: function(data) { + + }, + + // Called when a new public game is removed on the server + // { data string } the uuid of the game to delete + removeGame: function(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: function(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: function(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: function(data) { + + }, + + // Called when a player exits the game (from lobby or game) + // {data string } uuid of player + removePlayer: function(data) { + + }, + + // Called when the client wants to toggle the new game screen + newGame: function() { + //if(this.init) return; + this.top.toggleNewGame(); + }, + + // Called when the client wants to toggle the mobile settings screen + mobileSettings: function() { + //if(this.init) return; + this.top.toggleMobileSettings(); + }, + + // Called when the WebSocket state has changed. + setState: function(text, color, server) { + this.e.status.style.backgroundColor = color; + this.e.status.innerText = text; + this.e.addr.innerText = server; + this.top.setColor(color); + }, + + // Called when we are resetting the game. + reset: function() { + while (this.e.games.firstElementChild != null) { + this.e.games.removeChild(this.e.games.firstElementChild) + } + + this.setState("Connecting", "#DA0", this.e.addr.innerText); + this.init = false; + } +}; + +// ############### +// # TopBar Code # +// ############### + +// TopBar represents the bar at the top of the screen when client is in the lobby. + +function TopBar(el) { + this.root = el; + + this.newGame = el.getElementsByClassName("new-game")[0]; + this.mobileSettings = el.getElementsByClassName("mobile-settings")[0]; + this.status = el.getElementsByClassName("status")[0]; +} + +TopBar.prototype = { + // Set color of status bar + setColor: function(color) { + this.status.style.backgroundColor = color; + }, + + // Toggle showing the new game screen + toggleNewGame: function() { + if (this.newGame.style.display !== "none") + this.newGame.style.display = "none"; + else + this.newGame.style.display = "block"; + }, + + // Toggle showing the mobile settings + toggleMobileSettings: function() { + if (this.mobileSettings.style.display !== "none") + this.mobileSettings.style.display = "none"; + else + this.mobileSettings.style.display = "block"; + } +}; + +// ############# +// # Game code # +// ############# + +// GameEl represents a single game in the lobby view. It has methods for setting up the elements and such. +function GameEl(name, packs, maxp, id) { + +} \ No newline at end of file diff --git a/webcards/scripts/gui/table.js b/webcards/scripts/gui/table.js new file mode 100644 index 0000000..db67529 --- /dev/null +++ b/webcards/scripts/gui/table.js @@ -0,0 +1,34 @@ +// Table represents and manages the actual game. It accepts inputs from the server and tries to queries the server when the player makes a move. +function Table(el, soc) { + this.root = el; + this.soc = soc; +} + +Table.prototype = { + + openTable: function(){ + let state = this.root.getAttribute("state") + if((state == "close" || state == "closed") && state != "") { + this.root.setAttribute("state", "closed"); + setTimeout(this.root.setAttribute.bind(this.root), 50, "state", "open"); + } + }, + + closeTable: function(){ + let state = this.root.getAttribute("state") + if(state != "close" && state != "closed") { + this.root.setAttribute("state", ""); + setTimeout(this.root.setAttribute.bind(this.root), 50, "state", "close"); + } + }, + + + + handleClose: function() { + this.reset(); + }, + + reset: function() { + this.closeTable(); + } +} \ No newline at end of file diff --git a/webcards/scripts/libs/discord-rpc.js b/webcards/scripts/libs/discord-rpc.js deleted file mode 100644 index b51d10f..0000000 --- a/webcards/scripts/libs/discord-rpc.js +++ /dev/null @@ -1,8 +0,0 @@ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.RPC=e():t.RPC=e()}(window,(function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(r,i,function(e){return t[e]}.bind(null,i));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=6)}([function(t,e,n){"use strict";(function(e){let r;try{const{app:t}=n(7);r=t.setAsDefaultProtocolClient.bind(t)}catch(t){try{r=n(8)}catch(t){}}"function"!=typeof r&&(r=()=>!1);t.exports={pid:function(){return void 0!==e?e.pid:null},register:r,uuid:()=>{let t="";for(let e=0;e<32;e+=1){let n;if(8!==e&&12!==e&&16!==e&&20!==e||(t+="-"),12===e)n=4;else{const t=16*Math.random()|0;n=16===e?3&t|0:t}t+=n.toString(16)}return t}}}).call(this,n(1))},function(t,e){var n,r,i=t.exports={};function o(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function u(t){if(n===setTimeout)return setTimeout(t,0);if((n===o||!n)&&setTimeout)return n=setTimeout,setTimeout(t,0);try{return n(t,0)}catch(e){try{return n.call(null,t,0)}catch(e){return n.call(this,t,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:o}catch(t){n=o}try{r="function"==typeof clearTimeout?clearTimeout:s}catch(t){r=s}}();var a,c=[],f=!1,h=-1;function l(){f&&a&&(f=!1,a.length?c=a.concat(c):h=-1,c.length&&p())}function p(){if(!f){var t=u(l);f=!0;for(var e=c.length;e;){for(a=c,c=[];++h1)for(var n=1;n0&&s.length>i&&!s.warned){s.warned=!0;var a=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(e)+" listeners added. Use emitter.setMaxListeners() to increase limit");a.name="MaxListenersExceededWarning",a.emitter=t,a.type=e,a.count=s.length,u=a,console&&console.warn&&console.warn(u)}return t}function l(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function p(t,e,n){var r={fired:!1,wrapFn:void 0,target:t,type:e,listener:n},i=l.bind(r);return i.listener=n,r.wrapFn=i,i}function d(t,e,n){var r=t._events;if(void 0===r)return[];var i=r[e];return void 0===i?[]:"function"==typeof i?n?[i.listener||i]:[i]:n?function(t){for(var e=new Array(t.length),n=0;n0&&(s=e[0]),s instanceof Error)throw s;var u=new Error("Unhandled error."+(s?" ("+s.message+")":""));throw u.context=s,u}var a=i[t];if(void 0===a)return!1;if("function"==typeof a)o(a,this,e);else{var c=a.length,f=_(a,c);for(n=0;n=0;o--)if(n[o]===e||n[o].listener===e){s=n[o].listener,i=o;break}if(i<0)return this;0===i?n.shift():function(t,e){for(;e+1=0;r--)this.removeListener(t,e[r]);return this},u.prototype.listeners=function(t){return d(this,t,!0)},u.prototype.rawListeners=function(t){return d(this,t,!1)},u.listenerCount=function(t,e){return"function"==typeof t.listenerCount?t.listenerCount(e):E.call(t,e)},u.prototype.listenerCount=E,u.prototype.eventNames=function(){return this._eventsCount>0?r(this._events):[]}},function(t,e){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(t){"object"==typeof window&&(n=window)}t.exports=n},function(t,e,n){"use strict";var r=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==r)return r;throw new Error("unable to locate global object")}();t.exports=e=r.fetch,e.default=r.fetch.bind(r),e.Headers=r.Headers,e.Request=r.Request,e.Response=r.Response},function(t,e,n){"use strict";function r(t){const e={};for(const n of t)e[n]=n;return e}e.browser="undefined"!=typeof window,e.RPCCommands=r(["DISPATCH","AUTHORIZE","AUTHENTICATE","GET_GUILD","GET_GUILDS","GET_CHANNEL","GET_CHANNELS","GET_RELATIONSHIPS","GET_USER","SUBSCRIBE","UNSUBSCRIBE","SET_USER_VOICE_SETTINGS","SET_USER_VOICE_SETTINGS_2","SELECT_VOICE_CHANNEL","GET_SELECTED_VOICE_CHANNEL","SELECT_TEXT_CHANNEL","GET_VOICE_SETTINGS","SET_VOICE_SETTINGS_2","SET_VOICE_SETTINGS","CAPTURE_SHORTCUT","SET_ACTIVITY","SEND_ACTIVITY_JOIN_INVITE","CLOSE_ACTIVITY_JOIN_REQUEST","ACTIVITY_INVITE_USER","ACCEPT_ACTIVITY_INVITE","INVITE_BROWSER","DEEP_LINK","CONNECTIONS_CALLBACK","BRAINTREE_POPUP_BRIDGE_CALLBACK","GIFT_CODE_BROWSER","OVERLAY","BROWSER_HANDOFF","SET_CERTIFIED_DEVICES","GET_IMAGE","CREATE_LOBBY","UPDATE_LOBBY","DELETE_LOBBY","UPDATE_LOBBY_MEMBER","CONNECT_TO_LOBBY","DISCONNECT_FROM_LOBBY","SEND_TO_LOBBY","SEARCH_LOBBIES","CONNECT_TO_LOBBY_VOICE","DISCONNECT_FROM_LOBBY_VOICE","SET_OVERLAY_LOCKED","OPEN_OVERLAY_ACTIVITY_INVITE","OPEN_OVERLAY_GUILD_INVITE","OPEN_OVERLAY_VOICE_SETTINGS","VALIDATE_APPLICATION","GET_ENTITLEMENT_TICKET","GET_APPLICATION_TICKET","START_PURCHASE","GET_SKUS","GET_ENTITLEMENTS","GET_NETWORKING_CONFIG","NETWORKING_SYSTEM_METRICS","NETWORKING_PEER_METRICS","NETWORKING_CREATE_TOKEN","SET_USER_ACHIEVEMENT","GET_USER_ACHIEVEMENTS"]),e.RPCEvents=r(["CURRENT_USER_UPDATE","GUILD_STATUS","GUILD_CREATE","CHANNEL_CREATE","RELATIONSHIP_UPDATE","VOICE_CHANNEL_SELECT","VOICE_STATE_CREATE","VOICE_STATE_DELETE","VOICE_STATE_UPDATE","VOICE_SETTINGS_UPDATE","VOICE_SETTINGS_UPDATE_2","VOICE_CONNECTION_STATUS","SPEAKING_START","SPEAKING_STOP","GAME_JOIN","GAME_SPECTATE","ACTIVITY_JOIN","ACTIVITY_JOIN_REQUEST","ACTIVITY_SPECTATE","ACTIVITY_INVITE","NOTIFICATION_CREATE","MESSAGE_CREATE","MESSAGE_UPDATE","MESSAGE_DELETE","LOBBY_DELETE","LOBBY_UPDATE","LOBBY_MEMBER_CONNECT","LOBBY_MEMBER_DISCONNECT","LOBBY_MEMBER_UPDATE","LOBBY_MESSAGE","CAPTURE_SHORTCUT_CHANGE","OVERLAY","OVERLAY_UPDATE","ENTITLEMENT_CREATE","ENTITLEMENT_DELETE","USER_ACHIEVEMENT_UPDATE","READY","ERROR"]),e.RPCErrors={CAPTURE_SHORTCUT_ALREADY_LISTENING:5004,GET_GUILD_TIMED_OUT:5002,INVALID_ACTIVITY_JOIN_REQUEST:4012,INVALID_ACTIVITY_SECRET:5005,INVALID_CHANNEL:4005,INVALID_CLIENTID:4007,INVALID_COMMAND:4002,INVALID_ENTITLEMENT:4015,INVALID_EVENT:4004,INVALID_GIFT_CODE:4016,INVALID_GUILD:4003,INVALID_INVITE:4011,INVALID_LOBBY:4013,INVALID_LOBBY_SECRET:4014,INVALID_ORIGIN:4008,INVALID_PAYLOAD:4e3,INVALID_PERMISSIONS:4006,INVALID_TOKEN:4009,INVALID_USER:4010,LOBBY_FULL:5007,NO_ELIGIBLE_ACTIVITY:5006,OAUTH2_ERROR:5e3,PURCHASE_CANCELED:5008,PURCHASE_ERROR:5009,RATE_LIMITED:5011,SELECT_CHANNEL_TIMED_OUT:5001,SELECT_VOICE_FORCE_REQUIRED:5003,SERVICE_UNAVAILABLE:1001,TRANSACTION_ABORTED:1002,UNAUTHORIZED_FOR_ACHIEVEMENT:5010,UNKNOWN_ERROR:1e3},e.RPCCloseCodes={CLOSE_NORMAL:1e3,CLOSE_UNSUPPORTED:1003,CLOSE_ABNORMAL:1006,INVALID_CLIENTID:4e3,INVALID_ORIGIN:4001,RATELIMITED:4002,TOKEN_REVOKED:4003,INVALID_VERSION:4004,INVALID_ENCODING:4005},e.LobbyTypes={PRIVATE:1,PUBLIC:2},e.RelationshipTypes={NONE:0,FRIEND:1,BLOCKED:2,PENDING_INCOMING:3,PENDING_OUTGOING:4,IMPLICIT:5}},function(t,e,n){"use strict";const r=n(0);t.exports={Client:n(9),register:t=>r.register(`discord-${t}`)}},function(t,e){},function(t,e){},function(t,e,n){"use strict";const r=n(2),{setTimeout:i,clearTimeout:o}=n(10),s=n(4),u=n(12),{RPCCommands:a,RPCEvents:c,RelationshipTypes:f}=n(5),{pid:h,uuid:l}=n(0);function p(t,e){return`${t}${JSON.stringify(e)}`}t.exports=class extends r{constructor(t={}){super(),this.options=t,this.accessToken=null,this.clientId=null,this.application=null,this.user=null;const e=u[t.transport];if(!e)throw new TypeError("RPC_INVALID_TRANSPORT",t.transport);this.fetch=(t,e,{data:n,query:r}={})=>s(`${this.fetch.endpoint}${e}${r?new URLSearchParams(r):""}`,{method:t,body:n,headers:{Authorization:`Bearer ${this.accessToken}`}}).then(t=>t.json()),this.fetch.endpoint="https://discordapp.com/api",this.transport=new e(this),this.transport.on("message",this._onRpcMessage.bind(this)),this._expecting=new Map,this._subscriptions=new Map,this._connectPromise=void 0}connect(t){return this._connectPromise||(this.clientId=t,this._connectPromise=new Promise((e,n)=>{this.clientId=t;const r=i(()=>n(new Error("RPC_CONNECTION_TIMEOUT")),1e4);r.unref(),this.once("connected",()=>{o(r),e(this)}),this.transport.once("close",()=>{this._expecting.forEach(t=>{t.reject(new Error("connection closed"))}),this.emit("disconnected"),n()}),this.transport.connect().catch(n)})),this._connectPromise}async login(t={}){let{clientId:e,accessToken:n}=t;return await this.connect(e),t.scopes?(n||(n=await this.authorize(t)),this.authenticate(n)):(this.emit("ready"),this)}request(t,e,n){return new Promise((r,i)=>{const o=l();this.transport.send({cmd:t,args:e,evt:n,nonce:o}),this._expecting.set(o,{resolve:r,reject:i})})}_onRpcMessage(t){if(t.cmd===a.DISPATCH&&t.evt===c.READY)t.data.user&&(this.user=t.data.user),this.emit("connected");else if(this._expecting.has(t.nonce)){const{resolve:e,reject:n}=this._expecting.get(t.nonce);if("ERROR"===t.evt){const e=new Error(t.data.message);e.code=t.data.code,e.data=t.data,n(e)}else e(t.data);this._expecting.delete(t.nonce)}else{const e=p(t.evt,t.args);if(!this._subscriptions.has(e))return;this._subscriptions.get(e)(t.data)}}async authorize({scopes:t,clientSecret:e,rpcToken:n,redirectUri:r}={}){if(e&&!0===n){n=(await this.fetch("POST","/oauth2/token/rpc",{data:new URLSearchParams({client_id:this.clientId,client_secret:e})})).rpc_token}const{code:i}=await this.request("AUTHORIZE",{scopes:t,client_id:this.clientId,rpc_token:n,redirect_uri:r});return(await this.fetch("POST","/oauth2/token",{data:new URLSearchParams({client_id:this.clientId,client_secret:e,code:i,grant_type:"authorization_code",redirect_uri:r})})).access_token}authenticate(t){return this.request("AUTHENTICATE",{access_token:t}).then(({application:e,user:n})=>(this.accessToken=t,this.application=e,this.user=n,this.emit("ready"),this))}getGuild(t,e){return this.request(a.GET_GUILD,{guild_id:t,timeout:e})}getGuilds(t){return this.request(a.GET_GUILDS,{timeout:t})}getChannel(t,e){return this.request(a.GET_CHANNEL,{channel_id:t,timeout:e})}async getChannels(t,e){const{channels:n}=await this.request(a.GET_CHANNELS,{timeout:e,guild_id:t});return n}setCertifiedDevices(t){return this.request(a.SET_CERTIFIED_DEVICES,{devices:t.map(t=>({type:t.type,id:t.uuid,vendor:t.vendor,model:t.model,related:t.related,echo_cancellation:t.echoCancellation,noise_suppression:t.noiseSuppression,automatic_gain_control:t.automaticGainControl,hardware_mute:t.hardwareMute}))})}setUserVoiceSettings(t,e){return this.request(a.SET_USER_VOICE_SETTINGS,{user_id:t,pan:e.pan,mute:e.mute,volume:e.volume})}selectVoiceChannel(t,{timeout:e,force:n=!1}={}){return this.request(a.SELECT_VOICE_CHANNEL,{channel_id:t,timeout:e,force:n})}selectTextChannel(t,{timeout:e,force:n=!1}={}){return this.request(a.SELECT_TEXT_CHANNEL,{channel_id:t,timeout:e,force:n})}getVoiceSettings(){return this.request(a.GET_VOICE_SETTINGS).then(t=>({automaticGainControl:t.automatic_gain_control,echoCancellation:t.echo_cancellation,noiseSuppression:t.noise_suppression,qos:t.qos,silenceWarning:t.silence_warning,deaf:t.deaf,mute:t.mute,input:{availableDevices:t.input.available_devices,device:t.input.device_id,volume:t.input.volume},output:{availableDevices:t.output.available_devices,device:t.output.device_id,volume:t.output.volume},mode:{type:t.mode.type,autoThreshold:t.mode.auto_threshold,threshold:t.mode.threshold,shortcut:t.mode.shortcut,delay:t.mode.delay}}))}setVoiceSettings(t){return this.request(a.SET_VOICE_SETTINGS,{automatic_gain_control:t.automaticGainControl,echo_cancellation:t.echoCancellation,noise_suppression:t.noiseSuppression,qos:t.qos,silence_warning:t.silenceWarning,deaf:t.deaf,mute:t.mute,input:t.input?{device_id:t.input.device,volume:t.input.volume}:void 0,output:t.output?{device_id:t.output.device,volume:t.output.volume}:void 0,mode:t.mode?{mode:t.mode.type,auto_threshold:t.mode.autoThreshold,threshold:t.mode.threshold,shortcut:t.mode.shortcut,delay:t.mode.delay}:void 0})}captureShortcut(t){const e=p(c.CAPTURE_SHORTCUT_CHANGE),n=()=>(this._subscriptions.delete(e),this.request(a.CAPTURE_SHORTCUT,{action:"STOP"}));return this._subscriptions.set(e,({shortcut:e})=>{t(e,n)}),this.request(a.CAPTURE_SHORTCUT,{action:"START"}).then(()=>n)}setActivity(t={},e=h()){let n,r,i,o;if(t.startTimestamp||t.endTimestamp){if(n={start:t.startTimestamp,end:t.endTimestamp},n.start instanceof Date&&(n.start=Math.round(n.start.getTime())),n.end instanceof Date&&(n.end=Math.round(n.end.getTime())),n.start>2147483647e3)throw new RangeError("timestamps.start must fit into a unix timestamp");if(n.end>2147483647e3)throw new RangeError("timestamps.end must fit into a unix timestamp")}return(t.largeImageKey||t.largeImageText||t.smallImageKey||t.smallImageText)&&(r={large_image:t.largeImageKey,large_text:t.largeImageText,small_image:t.smallImageKey,small_text:t.smallImageText}),(t.partySize||t.partyId||t.partyMax)&&(i={id:t.partyId},(t.partySize||t.partyMax)&&(i.size=[t.partySize,t.partyMax])),(t.matchSecret||t.joinSecret||t.spectateSecret)&&(o={match:t.matchSecret,join:t.joinSecret,spectate:t.spectateSecret}),this.request(a.SET_ACTIVITY,{pid:e,activity:{state:t.state,details:t.details,timestamps:n,assets:r,party:i,secrets:o,instance:!!t.instance}})}clearActivity(t=h()){return this.request(a.SET_ACTIVITY,{pid:t})}sendJoinInvite(t){return this.request(a.SEND_ACTIVITY_JOIN_INVITE,{user_id:t.id||t})}sendJoinRequest(t){return this.request(a.SEND_ACTIVITY_JOIN_REQUEST,{user_id:t.id||t})}closeJoinRequest(t){return this.request(a.CLOSE_ACTIVITY_JOIN_REQUEST,{user_id:t.id||t})}createLobby(t,e,n){return this.request(a.CREATE_LOBBY,{type:t,capacity:e,metadata:n})}updateLobby(t,{type:e,owner:n,capacity:r,metadata:i}={}){return this.request(a.UPDATE_LOBBY,{id:t.id||t,type:e,owner_id:n&&n.id||n,capacity:r,metadata:i})}deleteLobby(t){return this.request(a.DELETE_LOBBY,{id:t.id||t})}connectToLobby(t,e){return this.request(a.CONNECT_TO_LOBBY,{id:t,secret:e})}sendToLobby(t,e){return this.request(a.SEND_TO_LOBBY,{id:t.id||t,data:e})}disconnectFromLobby(t){return this.request(a.DISCONNECT_FROM_LOBBY,{id:t.id||t})}updateLobbyMember(t,e,n){return this.request(a.UPDATE_LOBBY_MEMBER,{lobby_id:t.id||t,user_id:e.id||e,metadata:n})}getRelationships(){const t=Object.keys(f);return this.request(a.GET_RELATIONSHIPS).then(e=>e.relationships.map(e=>({...e,type:t[e.type]})))}subscribe(t,e,n){return n||"function"!=typeof e||(n=e,e=void 0),this.request(a.SUBSCRIBE,e,t).then(()=>{const r=p(t,e);return this._subscriptions.set(r,n),{unsubscribe:()=>this.request(a.UNSUBSCRIBE,e,t).then(()=>this._subscriptions.delete(r))}})}async destroy(){this.transport.close()}}},function(t,e,n){(function(t){var r=void 0!==t&&t||"undefined"!=typeof self&&self||window,i=Function.prototype.apply;function o(t,e){this._id=t,this._clearFn=e}e.setTimeout=function(){return new o(i.call(setTimeout,r,arguments),clearTimeout)},e.setInterval=function(){return new o(i.call(setInterval,r,arguments),clearInterval)},e.clearTimeout=e.clearInterval=function(t){t&&t.close()},o.prototype.unref=o.prototype.ref=function(){},o.prototype.close=function(){this._clearFn.call(r,this._id)},e.enroll=function(t,e){clearTimeout(t._idleTimeoutId),t._idleTimeout=e},e.unenroll=function(t){clearTimeout(t._idleTimeoutId),t._idleTimeout=-1},e._unrefActive=e.active=function(t){clearTimeout(t._idleTimeoutId);var e=t._idleTimeout;e>=0&&(t._idleTimeoutId=setTimeout((function(){t._onTimeout&&t._onTimeout()}),e))},n(11),e.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==t&&t.setImmediate||this&&this.setImmediate,e.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==t&&t.clearImmediate||this&&this.clearImmediate}).call(this,n(3))},function(t,e,n){(function(t,e){!function(t,n){"use strict";if(!t.setImmediate){var r,i,o,s,u,a=1,c={},f=!1,h=t.document,l=Object.getPrototypeOf&&Object.getPrototypeOf(t);l=l&&l.setTimeout?l:t,"[object process]"==={}.toString.call(t.process)?r=function(t){e.nextTick((function(){d(t)}))}:!function(){if(t.postMessage&&!t.importScripts){var e=!0,n=t.onmessage;return t.onmessage=function(){e=!1},t.postMessage("","*"),t.onmessage=n,e}}()?t.MessageChannel?((o=new MessageChannel).port1.onmessage=function(t){d(t.data)},r=function(t){o.port2.postMessage(t)}):h&&"onreadystatechange"in h.createElement("script")?(i=h.documentElement,r=function(t){var e=h.createElement("script");e.onreadystatechange=function(){d(t),e.onreadystatechange=null,i.removeChild(e),e=null},i.appendChild(e)}):r=function(t){setTimeout(d,0,t)}:(s="setImmediate$"+Math.random()+"$",u=function(e){e.source===t&&"string"==typeof e.data&&0===e.data.indexOf(s)&&d(+e.data.slice(s.length))},t.addEventListener?t.addEventListener("message",u,!1):t.attachEvent("onmessage",u),r=function(e){t.postMessage(s+e,"*")}),l.setImmediate=function(t){"function"!=typeof t&&(t=new Function(""+t));for(var e=new Array(arguments.length-1),n=0;n{const o=function(t){if("win32"===e.platform)return`\\\\?\\pipe\\discord-ipc-${t}`;const{env:{XDG_RUNTIME_DIR:n,TMPDIR:r,TMP:i,TEMP:o}}=e;return`${(n||r||i||o||"/tmp").replace(/\/$/,"")}/discord-ipc-${t}`}(t),s=()=>{t<10?n(p(t+1)):r(new Error("Could not connect"))},u=i.createConnection(o,()=>{u.removeListener("error",s),n(u)});u.once("error",s)})}function d(t,e){e=JSON.stringify(e);const n=r.byteLength(e),i=r.alloc(8+n);return i.writeInt32LE(t,0),i.writeInt32LE(n,4),i.write(e,8,n),i}const E={full:"",op:void 0};function _(t,e){const n=t.read();if(!n)return;let r,{op:i}=E;if(""===E.full){i=E.op=n.readInt32LE(0);const t=n.readInt32LE(4);r=n.slice(8,t+8)}else r=n.toString();try{e({op:i,data:JSON.parse(E.full+r)}),E.full="",E.op=void 0}catch(t){E.full+=r}_(t,e)}t.exports=class extends o{constructor(t){super(),this.client=t,this.socket=null}async connect(){const t=this.socket=await p();t.on("close",this.onClose.bind(this)),t.on("error",this.onClose.bind(this)),this.emit("open"),t.write(d(a,{v:1,client_id:this.client.clientId})),t.pause(),t.on("readable",()=>{_(t,({op:t,data:e})=>{switch(t){case h:this.send(e,l);break;case c:if(!e)return;"AUTHORIZE"===e.cmd&&"ERROR"!==e.evt&&async function t(e=0){if(e>30)throw new Error("Could not find endpoint");const n=`http://127.0.0.1:${6463+e%10}`;try{return 401!==(await s(n)).status?t(e+1):n}catch(n){return t(e+1)}}().then(t=>{this.client.request.endpoint=t}),this.emit("message",e);break;case f:this.emit("close",e)}})})}onClose(t){this.emit("close",t)}send(t,e=c){this.socket.write(d(e,t))}close(){this.send({},f),this.socket.end()}ping(){this.send(u(),h)}},t.exports.encode=d,t.exports.decode=_}).call(this,n(1),n(14).Buffer)},function(t,e,n){"use strict";(function(t){ -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -var r=n(15),i=n(16),o=n(17);function s(){return a.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function u(t,e){if(s()=s())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+s().toString(16)+" bytes");return 0|t}function d(t,e){if(a.isBuffer(t))return t.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(t)||t instanceof ArrayBuffer))return t.byteLength;"string"!=typeof t&&(t=""+t);var n=t.length;if(0===n)return 0;for(var r=!1;;)switch(e){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return G(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return k(t).length;default:if(r)return G(t).length;e=(""+e).toLowerCase(),r=!0}}function E(t,e,n){var r=!1;if((void 0===e||e<0)&&(e=0),e>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(e>>>=0))return"";for(t||(t="utf8");;)switch(t){case"hex":return L(this,e,n);case"utf8":case"utf-8":return C(this,e,n);case"ascii":return S(this,e,n);case"latin1":case"binary":return O(this,e,n);case"base64":return R(this,e,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return N(this,e,n);default:if(r)throw new TypeError("Unknown encoding: "+t);t=(t+"").toLowerCase(),r=!0}}function _(t,e,n){var r=t[e];t[e]=t[n],t[n]=r}function T(t,e,n,r,i){if(0===t.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=i?0:t.length-1),n<0&&(n=t.length+n),n>=t.length){if(i)return-1;n=t.length-1}else if(n<0){if(!i)return-1;n=0}if("string"==typeof e&&(e=a.from(e,r)),a.isBuffer(e))return 0===e.length?-1:g(t,e,n,r,i);if("number"==typeof e)return e&=255,a.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(t,e,n):Uint8Array.prototype.lastIndexOf.call(t,e,n):g(t,[e],n,r,i);throw new TypeError("val must be string, number or Buffer")}function g(t,e,n,r,i){var o,s=1,u=t.length,a=e.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(t.length<2||e.length<2)return-1;s=2,u/=2,a/=2,n/=2}function c(t,e){return 1===s?t[e]:t.readUInt16BE(e*s)}if(i){var f=-1;for(o=n;ou&&(n=u-a),o=n;o>=0;o--){for(var h=!0,l=0;li&&(r=i):r=i;var o=e.length;if(o%2!=0)throw new TypeError("Invalid hex string");r>o/2&&(r=o/2);for(var s=0;s>8,i=n%256,o.push(i),o.push(r);return o}(e,t.length-n),t,n,r)}function R(t,e,n){return 0===e&&n===t.length?r.fromByteArray(t):r.fromByteArray(t.slice(e,n))}function C(t,e,n){n=Math.min(t.length,n);for(var r=[],i=e;i239?4:c>223?3:c>191?2:1;if(i+h<=n)switch(h){case 1:c<128&&(f=c);break;case 2:128==(192&(o=t[i+1]))&&(a=(31&c)<<6|63&o)>127&&(f=a);break;case 3:o=t[i+1],s=t[i+2],128==(192&o)&&128==(192&s)&&(a=(15&c)<<12|(63&o)<<6|63&s)>2047&&(a<55296||a>57343)&&(f=a);break;case 4:o=t[i+1],s=t[i+2],u=t[i+3],128==(192&o)&&128==(192&s)&&128==(192&u)&&(a=(15&c)<<18|(63&o)<<12|(63&s)<<6|63&u)>65535&&a<1114112&&(f=a)}null===f?(f=65533,h=1):f>65535&&(f-=65536,r.push(f>>>10&1023|55296),f=56320|1023&f),r.push(f),i+=h}return function(t){var e=t.length;if(e<=4096)return String.fromCharCode.apply(String,t);var n="",r=0;for(;r0&&(t=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(t+=" ... ")),""},a.prototype.compare=function(t,e,n,r,i){if(!a.isBuffer(t))throw new TypeError("Argument must be a Buffer");if(void 0===e&&(e=0),void 0===n&&(n=t?t.length:0),void 0===r&&(r=0),void 0===i&&(i=this.length),e<0||n>t.length||r<0||i>this.length)throw new RangeError("out of range index");if(r>=i&&e>=n)return 0;if(r>=i)return-1;if(e>=n)return 1;if(this===t)return 0;for(var o=(i>>>=0)-(r>>>=0),s=(n>>>=0)-(e>>>=0),u=Math.min(o,s),c=this.slice(r,i),f=t.slice(e,n),h=0;hi)&&(n=i),t.length>0&&(n<0||e<0)||e>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var o=!1;;)switch(r){case"hex":return y(this,t,e,n);case"utf8":case"utf-8":return m(this,t,e,n);case"ascii":return I(this,t,e,n);case"latin1":case"binary":return v(this,t,e,n);case"base64":return A(this,t,e,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return w(this,t,e,n);default:if(o)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),o=!0}},a.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};function S(t,e,n){var r="";n=Math.min(t.length,n);for(var i=e;ir)&&(n=r);for(var i="",o=e;on)throw new RangeError("Trying to access beyond buffer length")}function P(t,e,n,r,i,o){if(!a.isBuffer(t))throw new TypeError('"buffer" argument must be a Buffer instance');if(e>i||et.length)throw new RangeError("Index out of range")}function U(t,e,n,r){e<0&&(e=65535+e+1);for(var i=0,o=Math.min(t.length-n,2);i>>8*(r?i:1-i)}function B(t,e,n,r){e<0&&(e=4294967295+e+1);for(var i=0,o=Math.min(t.length-n,4);i>>8*(r?i:3-i)&255}function D(t,e,n,r,i,o){if(n+r>t.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function M(t,e,n,r,o){return o||D(t,0,n,4),i.write(t,e,n,r,23,4),n+4}function Y(t,e,n,r,o){return o||D(t,0,n,8),i.write(t,e,n,r,52,8),n+8}a.prototype.slice=function(t,e){var n,r=this.length;if((t=~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),(e=void 0===e?r:~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),e0&&(i*=256);)r+=this[t+--e]*i;return r},a.prototype.readUInt8=function(t,e){return e||b(t,1,this.length),this[t]},a.prototype.readUInt16LE=function(t,e){return e||b(t,2,this.length),this[t]|this[t+1]<<8},a.prototype.readUInt16BE=function(t,e){return e||b(t,2,this.length),this[t]<<8|this[t+1]},a.prototype.readUInt32LE=function(t,e){return e||b(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+16777216*this[t+3]},a.prototype.readUInt32BE=function(t,e){return e||b(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<<8|this[t+3])},a.prototype.readIntLE=function(t,e,n){t|=0,e|=0,n||b(t,e,this.length);for(var r=this[t],i=1,o=0;++o=(i*=128)&&(r-=Math.pow(2,8*e)),r},a.prototype.readIntBE=function(t,e,n){t|=0,e|=0,n||b(t,e,this.length);for(var r=e,i=1,o=this[t+--r];r>0&&(i*=256);)o+=this[t+--r]*i;return o>=(i*=128)&&(o-=Math.pow(2,8*e)),o},a.prototype.readInt8=function(t,e){return e||b(t,1,this.length),128&this[t]?-1*(255-this[t]+1):this[t]},a.prototype.readInt16LE=function(t,e){e||b(t,2,this.length);var n=this[t]|this[t+1]<<8;return 32768&n?4294901760|n:n},a.prototype.readInt16BE=function(t,e){e||b(t,2,this.length);var n=this[t+1]|this[t]<<8;return 32768&n?4294901760|n:n},a.prototype.readInt32LE=function(t,e){return e||b(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24},a.prototype.readInt32BE=function(t,e){return e||b(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},a.prototype.readFloatLE=function(t,e){return e||b(t,4,this.length),i.read(this,t,!0,23,4)},a.prototype.readFloatBE=function(t,e){return e||b(t,4,this.length),i.read(this,t,!1,23,4)},a.prototype.readDoubleLE=function(t,e){return e||b(t,8,this.length),i.read(this,t,!0,52,8)},a.prototype.readDoubleBE=function(t,e){return e||b(t,8,this.length),i.read(this,t,!1,52,8)},a.prototype.writeUIntLE=function(t,e,n,r){(t=+t,e|=0,n|=0,r)||P(this,t,e,n,Math.pow(2,8*n)-1,0);var i=1,o=0;for(this[e]=255&t;++o=0&&(o*=256);)this[e+i]=t/o&255;return e+n},a.prototype.writeUInt8=function(t,e,n){return t=+t,e|=0,n||P(this,t,e,1,255,0),a.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),this[e]=255&t,e+1},a.prototype.writeUInt16LE=function(t,e,n){return t=+t,e|=0,n||P(this,t,e,2,65535,0),a.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):U(this,t,e,!0),e+2},a.prototype.writeUInt16BE=function(t,e,n){return t=+t,e|=0,n||P(this,t,e,2,65535,0),a.TYPED_ARRAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):U(this,t,e,!1),e+2},a.prototype.writeUInt32LE=function(t,e,n){return t=+t,e|=0,n||P(this,t,e,4,4294967295,0),a.TYPED_ARRAY_SUPPORT?(this[e+3]=t>>>24,this[e+2]=t>>>16,this[e+1]=t>>>8,this[e]=255&t):B(this,t,e,!0),e+4},a.prototype.writeUInt32BE=function(t,e,n){return t=+t,e|=0,n||P(this,t,e,4,4294967295,0),a.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):B(this,t,e,!1),e+4},a.prototype.writeIntLE=function(t,e,n,r){if(t=+t,e|=0,!r){var i=Math.pow(2,8*n-1);P(this,t,e,n,i-1,-i)}var o=0,s=1,u=0;for(this[e]=255&t;++o>0)-u&255;return e+n},a.prototype.writeIntBE=function(t,e,n,r){if(t=+t,e|=0,!r){var i=Math.pow(2,8*n-1);P(this,t,e,n,i-1,-i)}var o=n-1,s=1,u=0;for(this[e+o]=255&t;--o>=0&&(s*=256);)t<0&&0===u&&0!==this[e+o+1]&&(u=1),this[e+o]=(t/s>>0)-u&255;return e+n},a.prototype.writeInt8=function(t,e,n){return t=+t,e|=0,n||P(this,t,e,1,127,-128),a.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),t<0&&(t=255+t+1),this[e]=255&t,e+1},a.prototype.writeInt16LE=function(t,e,n){return t=+t,e|=0,n||P(this,t,e,2,32767,-32768),a.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):U(this,t,e,!0),e+2},a.prototype.writeInt16BE=function(t,e,n){return t=+t,e|=0,n||P(this,t,e,2,32767,-32768),a.TYPED_ARRAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):U(this,t,e,!1),e+2},a.prototype.writeInt32LE=function(t,e,n){return t=+t,e|=0,n||P(this,t,e,4,2147483647,-2147483648),a.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8,this[e+2]=t>>>16,this[e+3]=t>>>24):B(this,t,e,!0),e+4},a.prototype.writeInt32BE=function(t,e,n){return t=+t,e|=0,n||P(this,t,e,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),a.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):B(this,t,e,!1),e+4},a.prototype.writeFloatLE=function(t,e,n){return M(this,t,e,!0,n)},a.prototype.writeFloatBE=function(t,e,n){return M(this,t,e,!1,n)},a.prototype.writeDoubleLE=function(t,e,n){return Y(this,t,e,!0,n)},a.prototype.writeDoubleBE=function(t,e,n){return Y(this,t,e,!1,n)},a.prototype.copy=function(t,e,n,r){if(n||(n=0),r||0===r||(r=this.length),e>=t.length&&(e=t.length),e||(e=0),r>0&&r=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),t.length-e=0;--i)t[i+e]=this[i+n];else if(o<1e3||!a.TYPED_ARRAY_SUPPORT)for(i=0;i>>=0,n=void 0===n?this.length:n>>>0,t||(t=0),"number"==typeof t)for(o=e;o55295&&n<57344){if(!i){if(n>56319){(e-=3)>-1&&o.push(239,191,189);continue}if(s+1===r){(e-=3)>-1&&o.push(239,191,189);continue}i=n;continue}if(n<56320){(e-=3)>-1&&o.push(239,191,189),i=n;continue}n=65536+(i-55296<<10|n-56320)}else i&&(e-=3)>-1&&o.push(239,191,189);if(i=null,n<128){if((e-=1)<0)break;o.push(n)}else if(n<2048){if((e-=2)<0)break;o.push(n>>6|192,63&n|128)}else if(n<65536){if((e-=3)<0)break;o.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((e-=4)<0)break;o.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return o}function k(t){return r.toByteArray(function(t){if((t=function(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}(t).replace(V,"")).length<2)return"";for(;t.length%4!=0;)t+="=";return t}(t))}function j(t,e,n,r){for(var i=0;i=e.length||i>=t.length);++i)e[i+n]=t[i];return i}}).call(this,n(3))},function(t,e,n){"use strict";e.byteLength=function(t){var e=c(t),n=e[0],r=e[1];return 3*(n+r)/4-r},e.toByteArray=function(t){var e,n,r=c(t),s=r[0],u=r[1],a=new o(function(t,e,n){return 3*(e+n)/4-n}(0,s,u)),f=0,h=u>0?s-4:s;for(n=0;n>16&255,a[f++]=e>>8&255,a[f++]=255&e;2===u&&(e=i[t.charCodeAt(n)]<<2|i[t.charCodeAt(n+1)]>>4,a[f++]=255&e);1===u&&(e=i[t.charCodeAt(n)]<<10|i[t.charCodeAt(n+1)]<<4|i[t.charCodeAt(n+2)]>>2,a[f++]=e>>8&255,a[f++]=255&e);return a},e.fromByteArray=function(t){for(var e,n=t.length,i=n%3,o=[],s=0,u=n-i;su?u:s+16383));1===i?(e=t[n-1],o.push(r[e>>2]+r[e<<4&63]+"==")):2===i&&(e=(t[n-2]<<8)+t[n-1],o.push(r[e>>10]+r[e>>4&63]+r[e<<2&63]+"="));return o.join("")};for(var r=[],i=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",u=0,a=s.length;u0)throw new Error("Invalid string. Length must be a multiple of 4");var n=t.indexOf("=");return-1===n&&(n=e),[n,n===e?0:4-n%4]}function f(t,e,n){for(var i,o,s=[],u=e;u>18&63]+r[o>>12&63]+r[o>>6&63]+r[63&o]);return s.join("")}i["-".charCodeAt(0)]=62,i["_".charCodeAt(0)]=63},function(t,e){e.read=function(t,e,n,r,i){var o,s,u=8*i-r-1,a=(1<>1,f=-7,h=n?i-1:0,l=n?-1:1,p=t[e+h];for(h+=l,o=p&(1<<-f)-1,p>>=-f,f+=u;f>0;o=256*o+t[e+h],h+=l,f-=8);for(s=o&(1<<-f)-1,o>>=-f,f+=r;f>0;s=256*s+t[e+h],h+=l,f-=8);if(0===o)o=1-c;else{if(o===a)return s?NaN:1/0*(p?-1:1);s+=Math.pow(2,r),o-=c}return(p?-1:1)*s*Math.pow(2,o-r)},e.write=function(t,e,n,r,i,o){var s,u,a,c=8*o-i-1,f=(1<>1,l=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,p=r?0:o-1,d=r?1:-1,E=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(u=isNaN(e)?1:0,s=f):(s=Math.floor(Math.log(e)/Math.LN2),e*(a=Math.pow(2,-s))<1&&(s--,a*=2),(e+=s+h>=1?l/a:l*Math.pow(2,1-h))*a>=2&&(s++,a/=2),s+h>=f?(u=0,s=f):s+h>=1?(u=(e*a-1)*Math.pow(2,i),s+=h):(u=e*Math.pow(2,h-1)*Math.pow(2,i),s=0));i>=8;t[n+p]=255&u,p+=d,u/=256,i-=8);for(s=s<0;t[n+p]=255&s,p+=d,s/=256,c-=8);t[n+p-d]|=128*E}},function(t,e){var n={}.toString;t.exports=Array.isArray||function(t){return"[object Array]"==n.call(t)}},function(t,e){},function(t,e,n){"use strict";const r=n(2),{browser:i}=n(5),o=i?window.WebSocket:n(20);t.exports=class extends r{constructor(t){super(),this.client=t,this.ws=null,this.tries=0}async connect(t,e=this.tries){if(this.connected)return;const n=6463+e%10;this.hostAndPort=`localhost:${n}`;const r=this.ws=new o(`ws://${this.hostAndPort}/?v=1&client_id=${this.client.clientId}&encoding=json`);r.onopen=this.onOpen.bind(this),r.onclose=r.onerror=this.onClose.bind(this),r.onmessage=this.onMessage.bind(this)}send(t){var e;this.ws&&this.ws.send((e=t,JSON.stringify(e)))}close(){this.ws&&this.ws.close()}ping(){}onMessage(t){var e;this.emit("message",(e=t.data,JSON.parse(e)))}onOpen(){this.emit("open")}onClose(t){try{this.ws.close()}catch(t){}const e=t.code>=4e3&&t.code<5e3;t.code&&!e||(console.log(t),this.emit("close",t)),e||setTimeout(()=>this.connect(void 0,1006===t.code?++this.tries:0),250)}}},function(t,e){}])})); \ No newline at end of file diff --git a/webcards/scripts/socket/message.js b/webcards/scripts/socket/message.js new file mode 100644 index 0000000..5e821c4 --- /dev/null +++ b/webcards/scripts/socket/message.js @@ -0,0 +1,14 @@ +function Message(type, data){ + this.t = type; + this.d = data; +} + +Message.prototype = { + stringify: function(){ + var dat = this.d + if(typeof dat !== "string"){ + dat = JSON.stringify(dat); + } + return JSON.stringify({type: this.t, data: dat}); + } +}; diff --git a/webcards/scripts/socket/sock.js b/webcards/scripts/socket/sock.js new file mode 100644 index 0000000..cf06a5e --- /dev/null +++ b/webcards/scripts/socket/sock.js @@ -0,0 +1,63 @@ +// A wrapper around the wrapper +function SockWorker(serveraddr, version, callback) { + this.server = serveraddr; + this.version = version; + this.cb = callback; +} + +SockWorker.prototype = { + // Initialize the connection. + init: function() { + if(this.server == "" || this.server == null) { + return; + } + try { + this.socket = new WebSocket(this.server); + + this.socket.addEventListener("open", this.o.bind(this)); + this.socket.addEventListener("message", this.msg.bind(this)); + + this.socket.addEventListener("closed", this.c.bind(this)); + this.socket.addEventListener("error", this.err.bind(this)); + } catch (e) { + this.err(); + } + }, + + // Called when the connection connects to the server + o: function() { + this.send("version", this.version); + }, + + // Called when the connection gets a message from the server + // Attempts to turn the message into a usable object and pass it to the callback + msg: function(e) { + if(typeof e.data == "string") { + var dat = JSON.parse(e.data) + this.cb(dat); + } + }, + + // Called when the connection closes. + // Passes a close object to the callback. + c: function() { + this.cb({type: "close", data: ""}); + }, + + // Called when the connection encounters an error. + // Passes an error to the callback + err: function() { + this.cb({type: "error", data: ""}); + }, + + // Call to close the connection to the server + close: function() { + this.socket.close(); + }, + + // Send a message to the server + send: function(type, data) { + var m = new Message(type, data); + this.socket.send(m.stringify()) + } +}; \ No newline at end of file diff --git a/webcards/styles/client/base.css b/webcards/styles/client/base.css index cae61f6..fc8e310 100644 --- a/webcards/styles/client/base.css +++ b/webcards/styles/client/base.css @@ -13,7 +13,10 @@ html, body { } -.topbar { +/* Topbar rules */ + +.topbar +{ position: fixed; background-color: white; @@ -29,66 +32,75 @@ html, body { z-index: 1; } -.top-buttons { +.top-buttons +{ display: flex; } -button, input[type="button"] { - display: inline-block; - border: none; - background-color: dodgerblue; - padding: 10px; - font-size: medium; - border-radius: 5px; - box-sizing: border-box; -} - -button.top-button { +button.top-button +{ border-radius: 0; background-color: white; + color: black; flex: 1; border-left: 1px solid black; border-right: 1px solid black; + + transition-duration: 0.2s; } -.top-buttons > button:first-child { +button.top-button:hover { + background-color: #ddd; +} + +.top-buttons > button:first-child +{ border-left: none; } -.top-buttons > button:last-child { +.top-buttons > button:last-child +{ border-right: none; } -div.new-game { +div.new-game +{ flex: 1; flex-grow: 1; border-top: 2px solid black; } -div.mobile-settings { +div.mobile-settings +{ flex: 1; flex-grow: 1; border-top: 2px solid black; } -div.topbar > div.status { +div.topbar > div.status +{ height: 5px; flex-basis: auto; } -div.stats { +/* Content rules */ + +div.stats +{ padding: 5px; display: flex; flex-direction: column; } -div.stats > div { +div.stats > div +{ flex: 1; display: flex; } -div.stats > div > span { +div.stats > div > span +{ flex-basis: content; display: inline-block; } @@ -109,4 +121,52 @@ div.game { div.game:last-child { margin-bottom: none; -} \ No newline at end of file +} + +div.settings { + display:flex; + flex-direction: column; + padding: 5px; +} + +/* Table rules */ + +.table { + position: absolute; + z-index: 2; + width: 100vw; + height: 100vh; + + animation-duration: 0.8s; + + background-color: rgba(0, 0, 0, 0.5); +} + +.table[state="open"] ,.table[state="close"]{ + animation-name: slide-in; + animation-timing-function: cubic-bezier(0.5, 0, 0.5, 1); + animation-iteration-count: 1; + animation-direction: normal; + animation-fill-mode: forwards; +} + +.table[state="close"] { + animation-direction: reverse; +} + +.table[state="closed"] { + transform: translate(0, -100vh); + opacity: 0; +} + +@keyframes slide-in { + from { + transform: translate(0, -100vh); + opacity: 0; + } + + to { + transform: translate(0, 0); + opacity: 1; + } +} diff --git a/webcards/styles/client/card.css b/webcards/styles/client/card.css new file mode 100644 index 0000000..62b4054 --- /dev/null +++ b/webcards/styles/client/card.css @@ -0,0 +1,116 @@ +card +{ + position: absolute; + display: block; + width: 150px; + height: 225px; + background-color: white; + border: 6px double #bbb; + border-radius: 10px; + transition-duration: 0.2s; + cursor: pointer; + flex-direction: column; + overflow: hidden; + user-select: none; +} + +card:hover +{ + box-shadow: 0 0 10px #0f0; +} + +carea +{ + padding-left: 2px; + padding-right: 2px; + position: absolute; + display: flex; + vertical-align: middle; + width: 100%; +} + +carea.top, carea.topl, carea.topr +{ + top: 0; + height: 8%; + text-align: center; +} + +carea.topl +{ + width: 50%; + text-align: left; +} + +carea.topr +{ + right: 0; + width: 50%; + text-align: right; +} + +carea.mid +{ + top: 8%; + height: 84%; +} + +carea.midt +{ + top: 8%; + height: 42%; +} + +carea.midb +{ + + top: 50%; + height: 42%; +} + +carea.bot, carea.botl, carea.botr +{ + bottom: 0; + height: 8%; + text-align: center; +} + +carea.botl +{ + width: 50%; + text-align: left; +} + +carea.botr +{ + right: 0; + width: 50%; + text-align: right; +} + +carea.all +{ + top: 0; + height: 100%; +} + +ctext +{ + display: inline-block; + flex: 1; + font-size: small; + height: auto; +} + +cimage +{ + background-position: center center; + background-repeat: no-repeat; + background-size: contain; + flex: 1; +} + +card[drag = "true"] +{ + z-index: 3; +} diff --git a/webcards/styles/client/desktop.css b/webcards/styles/client/desktop.css index 46dc83d..08e24fe 100644 --- a/webcards/styles/client/desktop.css +++ b/webcards/styles/client/desktop.css @@ -10,14 +10,6 @@ TopBar rules */ - button.top-button { - transition-duration: 0.2s; - } - - button.top-button:hover { - background-color: #ddd; - } - button#newgame:before{ content: "Create Game"; } @@ -101,7 +93,7 @@ padding: 10px; border-radius: 10px; background-color: #ddd; - flex: 4; + flex: 2; border: 5px solid #ddd; } diff --git a/webcards/styles/client/mobile.css b/webcards/styles/client/mobile.css index cea233d..4659ce6 100644 --- a/webcards/styles/client/mobile.css +++ b/webcards/styles/client/mobile.css @@ -1,4 +1,4 @@ -@media (max-width: 599px), (max-height: 500px) { +@media (max-width: 599px) or (max-height: 500px) { /* Hide stuff */ diff --git a/webcards/styles/client/tablet.css b/webcards/styles/client/tablet.css index 37750f8..b9b5bd1 100644 --- a/webcards/styles/client/tablet.css +++ b/webcards/styles/client/tablet.css @@ -38,4 +38,8 @@ margin-top: 0px; margin-left: 10px; } + + div.games { + flex: 4; + } } diff --git a/webcards/styles/home/base.css b/webcards/styles/home/base.css index 2ef3693..99bb148 100644 --- a/webcards/styles/home/base.css +++ b/webcards/styles/home/base.css @@ -33,21 +33,21 @@ div.content { align-items: center; } -a { +a, button { font-size: 24px; display: block; color: white; + box-sizing: border-box; border-radius: 5px; + border: none; padding: 5px; - background-color: dodgerblue; + background-color: #0084ff; transition-duration: 0.2s; text-decoration: none; margin-top: 10px; - - max-width: 100px; } \ No newline at end of file diff --git a/webcards/styles/input.css b/webcards/styles/input.css new file mode 100644 index 0000000..16a5385 --- /dev/null +++ b/webcards/styles/input.css @@ -0,0 +1,296 @@ +/* Begin Input CSS */ + +/* All input */ + +input +{ + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + + margin: 5px; + + display: block; + z-index: 1; +} + +/* Button input */ + +input[type=button], input[type=submit], button +{ + padding: 10px; + box-sizing: border-box; + + border: none; + border-radius: 5px; + + color: white; + background-color: #0084ff; + + font-size: medium; + + transition-duration: 0.2s; + + cursor: pointer; +} + +input[type=button]:hover, input[type=submit]:hover, button:hover +{ + background-color: #3ea2ff; +} + +input[type=button]:active, input[type=submit]:active, button:active +{ + background-color: #0056a7; +} + +/* Text, date, number, and time input */ + +input[type=text], input[type=date], input[type=time], input[type="number"] +{ + border: 2px solid #555; + border-radius: 3px; + padding: 5px; + background-color: white; + font-size: 1em; +} + +input[type=text]:hover, input[type=date]:hover, input[type=time]:hover, input[type="number"]:hover +{ + border-color: black; +} + +input[type=text]:focus, input[type=date]:focus, input[type=time]:focus, input[type="number"]:focus +{ + border-color: #0084ff; +} + +/* Radial input */ + +input[type=radio] +{ + width: 20px; + height: 20px; + + border: 3px solid black; + border-radius: 50%; + + transition-duration: 0.2s; + + background-color: white; + + cursor: pointer; + + display: inline-block; + vertical-align: middle; +} + +input[type=radio]:checked, input[type=radio]:hover{ + border-width: 6px; +} + +input[type=radio]:checked +{ + background-color: black; + border-color: #0084ff; +} + +input[type=radio]:hover +{ + border-color: #3ea2ff; +} + +input[type=radio]:active +{ + border-color: black; +} + +/* Checkbox input */ + +input[type=checkbox] +{ + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + + display: inline-block; + vertical-align: middle; + + width: 20px; + height: 20px; + + border: 3px solid black; + + border-radius: 2px; + + transition-duration: 0.2s; + + background-color: white; + + cursor: pointer; +} + +input[type=checkbox]:checked +{ + border-width: 10px; + border-color: #0084ff; +} + +input[type=checkbox]:hover +{ + border-color: #3ea2ff; +} + +input[type=checkbox]:active +{ + border-color: black; +} + +input[type=checkbox]::after +{ + height: 16px; + width: 10px; + + box-sizing: border-box; + + position: relative; + + display: block; + + border-color: black; + border-style: solid; + border-width: 0px 0px 0px 0px; + + top: calc(50% - 10px); + left: calc(50% - 5px); + + transform-origin: center; + transform: rotate(45deg); + + content: ''; +} + +input[type=checkbox]:checked::after +{ + border-width: 0px 3px 3px 0px; +} + +/* Color input */ + +input[type=color] +{ + display: inline-block; + + vertical-align: middle; + + height: 20px; + width: 20px; + + margin: 0; + padding: 0; + + border: 0; + + cursor: pointer; +} + +/* File input */ + +input[type=file] +{ + display: none; +} + +/* Input container */ + +div.input-container { + margin: 5px; + padding: 5px; + + border-radius: 3px; +} + +/* Color container */ + +div.input-container[type=color] +{ + text-align: center; + + background-color: #0084ff; + color: white; + + transition-duration: 0.2s; + + cursor: pointer; + + width: max-content; +} + +div.input-container[type=color]::after +{ + display: inline; + + vertical-align: middle; + + content: ' Pick a color'; +} + +div.input-container[type=color]:hover +{ + background-color: #3ea2ff; +} + +div.input-container[type=color]:active +{ + background-color: #0056a7; +} + +/* File input container */ + +div.input-container[type=file] +{ + background-color: #0084ff; + color: white; + + transition-duration: 0.2s; + + cursor: pointer; + + text-align: center; + + width: max-content; + height: max-content; +} + +div.input-container[type=file]:hover +{ + background-color: #3ea2ff; +} + +div.input-container[type=file]:active +{ + background-color: #0056a7; +} + +div.input-container[type=file]::after +{ + display: inline; + + vertical-align: middle; + + content: attr(data-files); +} + +/* Radio input container */ + +div.input-container[type=radio]::before { + display: block; + content: 'Radio'; +} + +div.input-container[type=radio] +{ + background-color: rgba(255, 255, 255, 0.3); +} + +/* End Input CSS */ \ No newline at end of file -- cgit v1.2.3