summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--webcards/LICENSE21
-rw-r--r--webcards/README.md5
-rw-r--r--webcards/assets/standard/diamond.svg37
-rw-r--r--webcards/assets/standard/heart.svg33
-rw-r--r--webcards/client.html82
-rw-r--r--webcards/favicon.icobin0 -> 1150 bytes
-rw-r--r--webcards/images/wc-icon-144.pngbin0 -> 5171 bytes
-rw-r--r--webcards/images/wc-icon-288.pngbin0 -> 10018 bytes
-rw-r--r--webcards/images/wc-icon-32.pngbin0 -> 1443 bytes
-rw-r--r--webcards/images/wc-icon-48.pngbin0 -> 1898 bytes
-rw-r--r--webcards/images/wc-icon-96.pngbin0 -> 3980 bytes
-rw-r--r--webcards/index.html21
-rw-r--r--webcards/scripts/cards/card.js97
-rw-r--r--webcards/scripts/cards/deck.js106
-rw-r--r--webcards/scripts/cards/drag.js83
-rw-r--r--webcards/scripts/client.js (renamed from webcards/scripts/client/client.js)8
-rw-r--r--webcards/scripts/client/lobby.js102
-rw-r--r--webcards/scripts/client/table.js16
-rw-r--r--webcards/scripts/gui/input.js159
-rw-r--r--webcards/scripts/gui/lobby.js196
-rw-r--r--webcards/scripts/gui/table.js34
-rw-r--r--webcards/scripts/socket/message.js (renamed from webcards/scripts/client/message.js)0
-rw-r--r--webcards/scripts/socket/sock.js (renamed from webcards/scripts/client/sock.js)13
-rw-r--r--webcards/styles/client/base.css104
-rw-r--r--webcards/styles/client/card.css116
-rw-r--r--webcards/styles/client/desktop.css10
-rw-r--r--webcards/styles/client/mobile.css2
-rw-r--r--webcards/styles/client/tablet.css4
-rw-r--r--webcards/styles/home/base.css8
-rw-r--r--webcards/styles/input.css296
30 files changed, 1373 insertions, 180 deletions
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 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ style="enable-background:new"
+ id="svg8"
+ version="1.1"
+ viewBox="0 0 33.866666 33.866668"
+ height="128"
+ width="128">
+ <defs
+ id="defs2" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ transform="translate(0,-263.13332)"
+ style="opacity:1"
+ id="layer1">
+ <path
+ id="path859"
+ d="M 16.933333,295.73051 5.3453296,280.06665 16.933332,264.40279 28.521336,280.06665 Z"
+ style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.49378231;stroke-opacity:1" />
+ </g>
+</svg>
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 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ width="128"
+ height="128"
+ viewBox="0 0 33.866666 33.866668"
+ version="1.1"
+ id="svg8"
+ style="enable-background:new">
+ <defs
+ id="defs2" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <path
+ transform="matrix(0.26458334,0,0,0.26458334,1.2695285,1.26953)"
+ style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.3796902;stroke-opacity:1;enable-background:new"
+ d="M 31.880591,0.16895874 C 14.609272,3.6552705 -0.10970932,24.675453 6.162248e-4,42.266772 0.20206882,74.388447 59.172962,118.40354 59.172962,118.40354 c 0,0 59.020808,-44.003376 59.229928,-76.136768 C 118.51733,24.675491 103.79578,3.6475603 86.522912,0.16895874 76.21487,-1.9070223 59.229844,15.963831 59.229844,15.963831 c 0,0 -17.02625,-17.8786392 -27.349253,-15.79487226 z"
+ id="path815" />
+</svg>
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 @@
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="styles/icofont.css">
+ <link rel="stylesheet" type="text/css" href="styles/input.css">
+
<link rel="stylesheet" type="text/css" href="styles/client/base.css">
<link rel="stylesheet" type="text/css" href="styles/client/desktop.css">
<link rel="stylesheet" type="text/css" href="styles/client/tablet.css">
<link rel="stylesheet" type="text/css" href="styles/client/mobile.css">
-
- <!--<script src="scripts/libs/discord-rpc.js"></script>-->
- <script src="scripts/client/message.js"></script>
- <script src="scripts/client/sock.js"></script>
- <script src="scripts/client/lobby.js"></script>
- <script src="scripts/client/table.js"></script>
- <script src="scripts/client/client.js"></script>
+ <link rel="stylesheet" type="text/css" href="styles/client/card.css">
+
+ <link rel="icon" sizes="32x32" href="images/wc-icon-32.png">
+ <link rel="icon" sizes="48x48" href="images/wc-icon-48.png">
+ <link rel="icon" sizes="96x96" href="images/wc-icon-96.png">
+ <link rel="icon" sizes="144x144" href="images/wc-icon-144.png">
+ <link rel="icon" sizes="288x288" href="images/wc-icon-288.png">
+
+ <script src="scripts/cards/card.js"></script>
+ <script src="scripts/cards/deck.js"></script>
+ <script src="scripts/cards/drag.js"></script>
+
+ <script src="scripts/gui/input.js"></script>
+ <script src="scripts/gui/lobby.js"></script>
+ <script src="scripts/gui/table.js"></script>
+
+ <script src="scripts/socket/message.js"></script>
+ <script src="scripts/socket/sock.js"></script>
+
+ <script src="scripts/client.js"></script>
+
+ <title>WebCards - Client</title>
</head>
<body>
- <div class="table" style="visibility: hidden;">
+ <div class="table" state="closed">
</div>
<div class="topbar" style="height: auto;">
<div class="top-buttons">
- <button id="newgame" class="top-button" onclick="game.lob.newGameScreen()"></button>
- <button id="settings" class="top-button" onclick="game.lob.mobileSettingsScreen()"></button>
+ <button id="newgame" class="top-button" onclick="game.lob.newGame()"></button>
+ <button id="settings" class="top-button" onclick="game.lob.mobileSettings()"></button>
<button id="reset" class="top-button" onclick="game.reset()"></button>
</div>
<div class="new-game" style="display: none;"></div>
- <div class="mobile-settings" style="display: none;">
- <span><input type="text" id="name" placeholder="Username"/></span>
- <span><input type="color" id="usercolor" value="#f00"/></span>
+ <div class="settings mobile-settings" style="display: none;">
+ <span><input type="text" placeholder="Username"/></span>
+ <span><input type="color" value="#f00"/></span>
<button id="set">Accept Settings</button>
</div>
@@ -82,11 +99,10 @@
</div>
</div>
- <div class="settings">
- <span><input type="text" id="name" placeholder="Username"/></span>
- <span><input type="color" id="usercolor" value="#f00"/></span>
-
- <button onclick="game.acceptSettings()">Accept Settings</button>
+ <div class="settings" >
+ <div class="input-container" type="color" onclick="this.getElementsByTagName('input')[0].click()">
+ <input type="color">
+ </div>
</div>
</div>
</div>
@@ -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();
+ }
</script>
</body>
diff --git a/webcards/favicon.ico b/webcards/favicon.ico
new file mode 100644
index 0000000..91e0742
--- /dev/null
+++ b/webcards/favicon.ico
Binary files differ
diff --git a/webcards/images/wc-icon-144.png b/webcards/images/wc-icon-144.png
new file mode 100644
index 0000000..ecc4084
--- /dev/null
+++ b/webcards/images/wc-icon-144.png
Binary files differ
diff --git a/webcards/images/wc-icon-288.png b/webcards/images/wc-icon-288.png
new file mode 100644
index 0000000..04f6a07
--- /dev/null
+++ b/webcards/images/wc-icon-288.png
Binary files differ
diff --git a/webcards/images/wc-icon-32.png b/webcards/images/wc-icon-32.png
new file mode 100644
index 0000000..ec390c5
--- /dev/null
+++ b/webcards/images/wc-icon-32.png
Binary files differ
diff --git a/webcards/images/wc-icon-48.png b/webcards/images/wc-icon-48.png
new file mode 100644
index 0000000..e787102
--- /dev/null
+++ b/webcards/images/wc-icon-48.png
Binary files differ
diff --git a/webcards/images/wc-icon-96.png b/webcards/images/wc-icon-96.png
new file mode 100644
index 0000000..a10d0eb
--- /dev/null
+++ b/webcards/images/wc-icon-96.png
Binary files 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 @@
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="styles/icofont.css">
+ <link rel="icon" sizes="32x32" href="images/wc-icon-32.png">
+ <link rel="icon" sizes="48x48" href="images/wc-icon-48.png">
+ <link rel="icon" sizes="96x96" href="images/wc-icon-96.png">
+ <link rel="icon" sizes="144x144" href="images/wc-icon-144.png">
+ <link rel="icon" sizes="288x288" href="images/wc-icon-288.png">
+
<link rel="stylesheet" type="text/css" href="styles/home/base.css">
<link rel="stylesheet" type="text/css" href="styles/home/desktop.css">
<link rel="stylesheet" type="text/css" href="styles/home/mobile.css">
+
+ <link rel="stylesheet" type="text/css" href="styles/input.css">
+
+ <title>WebCards</title>
</head>
<body>
<div class="content">
- <p style="font-size: 30px; font-weight: bold;"><span style="color: dodgerblue; font-weight: normal;">Web</span>Cards</p>
+ <p style="font-size: 30px; font-weight: normal;">Web<span style="color: #0084ff; font-weight: bold;">Cards</span></p>
<div>
<select id="type">
<option value="ws://" selected>ws</option>
@@ -27,7 +37,7 @@
<input id="port" type="number" value="4040">
</div>
- <a id="conn" href="client.html?s=ws://127.0.0.1:4040&g=-1">Connect</a>
+ <button id="conn" onclick="connect()">Connect</a>
</div>
<script>
@@ -40,9 +50,10 @@
a.onchange = updateLink;
p.onchange = updateLink;
- function updateLink() {
- var url = "client.html?s=" + t.value + a.value + ":" + p.value + "&g=-1";
- c.setAttribute("href", url);
+ function connect() {
+ var url = "./client.html?s=" + t.value + a.value + ":" + p.value + "&g=-1";
+ //c.setAttribute("href", url);
+ window.location = url;
}
</script>
</body>
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/client.js b/webcards/scripts/client.js
index b5dd4bf..ea62e26 100644
--- a/webcards/scripts/client/client.js
+++ b/webcards/scripts/client.js
@@ -13,12 +13,13 @@ function Client(serveraddr, game) {
}
Client.prototype = {
+ // Initialize the connection
init: function() {
this.soc.init();
},
- // Entry point for a message.
- // If it's a close message, the
+ // 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);
@@ -42,6 +43,7 @@ Client.prototype = {
}
},
+ // Called when negotiating with the server for the first time and we are determining versions
handshake: function(m) {
switch (m.type) {
case "verr":
@@ -57,6 +59,7 @@ Client.prototype = {
}
},
+ // Lobby switch, called when in the lobby and a message arrives from the server
lobby: function (m) {
switch (m.type) {
case "plist":
@@ -87,6 +90,7 @@ Client.prototype = {
}
},
+ // Game switch, called when in game and a message arrives from the server
game: function (m) {
switch (m.type) {
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/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/client/message.js b/webcards/scripts/socket/message.js
index 5e821c4..5e821c4 100644
--- a/webcards/scripts/client/message.js
+++ b/webcards/scripts/socket/message.js
diff --git a/webcards/scripts/client/sock.js b/webcards/scripts/socket/sock.js
index 78c4195..cf06a5e 100644
--- a/webcards/scripts/client/sock.js
+++ b/webcards/scripts/socket/sock.js
@@ -1,3 +1,4 @@
+// A wrapper around the wrapper
function SockWorker(serveraddr, version, callback) {
this.server = serveraddr;
this.version = version;
@@ -5,6 +6,7 @@ function SockWorker(serveraddr, version, callback) {
}
SockWorker.prototype = {
+ // Initialize the connection.
init: function() {
if(this.server == "" || this.server == null) {
return;
@@ -22,10 +24,13 @@ SockWorker.prototype = {
}
},
+ // 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)
@@ -33,18 +38,24 @@ SockWorker.prototype = {
}
},
+ // 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())
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