add embedded chat for texas holdem
authorGeoffrey Allott <geoffrey@allott.email>
Sun, 21 Mar 2021 14:11:30 +0000 (14:11 +0000)
committerGeoffrey Allott <geoffrey@allott.email>
Sun, 21 Mar 2021 14:11:30 +0000 (14:11 +0000)
site/modules/datetime.js [new file with mode: 0644]
site/modules/gamelist.js
site/modules/poker.js
site/style/chatroom.css
site/style/game.css
site/style/poker.css

diff --git a/site/modules/datetime.js b/site/modules/datetime.js
new file mode 100644 (file)
index 0000000..9499403
--- /dev/null
@@ -0,0 +1,46 @@
+export function parse_datetime(input) {
+    const re = /^(?:(?<year>[0-9]{4})-(?<month>[0-9]{1,2})-(?<day>[0-9]{1,2})\s)?\s*(?<hour>[0-9]{1,2}):(?<minute>[0-9]{2})(?::(?<second>[0-9]{2}))?$/;
+    const match = input.match(re);
+    if (match === null) return null;
+    const datetime = new Date();
+    if (match.groups.year !== undefined) datetime.setFullYear(+match.groups.year);
+    if (match.groups.month !== undefined) datetime.setMonth(+match.groups.month - 1);
+    if (match.groups.day !== undefined) datetime.setDate(+match.groups.day);
+    datetime.setHours(+match.groups.hour);
+    datetime.setMinutes(+match.groups.minute);
+    datetime.setSeconds(match.groups.second !== undefined ? +match.groups.second : 0);
+    return datetime.getTime();
+}
+
+export function stringify_datetime(millis) {
+    if (millis === null || millis === undefined) return null;
+    const datetime = new Date(millis);
+    const pad = num => String(num).padStart(2, '0');
+    const now = new Date();
+    let date = "";
+    if (datetime.getFullYear() !== now.getFullYear() || datetime.getMonth() !== now.getMonth() || datetime.getDate() !== now.getDate()) {
+        date = datetime.getFullYear() + "-" + pad(datetime.getMonth() + 1) + "-" + pad(datetime.getDate()) + " ";
+    }
+    const time = pad(datetime.getHours()) + ":" + pad(datetime.getMinutes());
+    let seconds = "";
+    if (datetime.getSeconds() !== 0) {
+        seconds = ":" + pad(datetime.getSeconds());
+    }
+    return date + time + seconds;
+}
+
+export function parse_duration(input) {
+    const re = /^(?:(?<hours>[0-9]{1,2}):)?(?<minutes>[0-9]{2}):(?<seconds>[0-9]{2})$/;
+    const match = input.match(re);
+    if (match === null) return null;
+    return ((((match.groups.hours !== undefined ? +match.groups.hours : 0) * 60 + +match.groups.minutes) * 60) + +match.groups.seconds) * 1000;
+}
+
+export function stringify_duration(millis) {
+    if (millis === null || millis === undefined) return null;
+    const hours = ((millis / 1000 / 60 / 60) >> 0);
+    const minutes = ((millis / 1000 / 60) >> 0) % 60;
+    const seconds = ((millis / 1000) >> 0) % 60;
+    const pad = num => String(num).padStart(2, '0');
+    return (hours !== 0 ? pad(hours) + ":" : "") + pad(minutes) + ":" + pad(seconds);
+}
index 9237549d45e055c4f372a66c0e5526094181a108..a75a1cb03a30b0b6f502bec6d0a8f277d1de02e6 100644 (file)
@@ -1,3 +1,4 @@
+import { parse_datetime, parse_duration, stringify_datetime, stringify_duration } from "./datetime.js";
 import { random_title } from "./words.js";
 
 export class GameList {
@@ -103,20 +104,37 @@ export class GameList {
 
         const id_row = document.createElement("tr");
         const id_header = document.createElement("th");
-        id_header.innerText = "id";
+        id_header.innerText = "ID";
         const id_value = document.createElement("td");
         id_value.innerText = game.id;
         id_row.append(id_header);
         id_row.append(id_value);
         table.append(id_row);
 
+        const field_types = {
+            start_time: "datetime",
+            round_length: "duration",
+            tournament_length: "duration",
+            action_timeout: "duration",
+        };
+
         for (const setting of Object.keys(game.settings)) {
             if (setting === "title") continue;
             const row = document.createElement("tr");
             const header = document.createElement("th");
-            header.innerText = setting;
+            header.innerText = setting.split("_").map(w => w[0].toUpperCase() + w.substr(1)).join(" ");
             const value = document.createElement("td");
-            value.innerText = game.settings[setting];
+            switch (field_types[setting]) {
+                case "datetime":
+                    value.innerText = stringify_datetime(game.settings[setting]) || "When Full";
+                    break;
+                case "duration":
+                    value.innerText = stringify_duration(game.settings[setting]) || "";
+                    break;
+                default:
+                    value.innerText = game.settings[setting];
+                    break;
+            }
             row.append(header);
             row.append(value);
             table.append(row);
@@ -185,15 +203,16 @@ export class GameList {
         table.append(format_row);
 
         const fields = [
-            { name: "max_players", display: "Max Players", value: 2, formats: ["TexasHoldEm", "KnockOutWhist"] },
-            { name: "small_blind", display: "Small Blind", value: 25, formats: ["TexasHoldEm"] },
-            { name: "starting_stack", display: "Starting Stack", value: 1000, formats: ["TexasHoldEm"] },
-            { name: "round_length", display: "Round Length", value: 60000, formats: ["TexasHoldEm"] },
-            { name: "tournament_length", display: "Tournament Length", value: 600000, formats: ["TexasHoldEm"] },
-            { name: "action_timeout", display: "Action Timeout", value: 30000, formats: ["TexasHoldEm"] },
+            { name: "start_time", display: "Start Time", value: "", type: "datetime", formats: ["TexasHoldEm", "KnockOutWhist"] },
+            { name: "max_players", display: "Max Players", value: 2, type: "number", formats: ["TexasHoldEm", "KnockOutWhist"] },
+            { name: "small_blind", display: "Small Blind", value: 25, type: "number", formats: ["TexasHoldEm"] },
+            { name: "starting_stack", display: "Starting Stack", value: 5000, step: 1000, type: "number", formats: ["TexasHoldEm"] },
+            { name: "round_length", display: "Round Length", value: "", type: "duration", formats: ["TexasHoldEm"] },
+            { name: "tournament_length", display: "Tournament Length", value: "", type: "duration", formats: ["TexasHoldEm"] },
+            { name: "action_timeout", display: "Action Timeout", value: "", type: "duration", formats: ["TexasHoldEm"] },
         ];
 
-        for (const {name, display, value, formats} of fields) {
+        for (const {name, display, value, type, step, formats} of fields) {
             const row = document.createElement("tr");
             row.classList.add("game-field");
             for (const format of formats) {
@@ -204,9 +223,42 @@ export class GameList {
             row.append(title);
             const cell = document.createElement("td");
             const input = document.createElement("input");
-            input.setAttribute("type", "number");
+            switch (type) {
+                case "datetime":
+                    input.setAttribute("placeholder", "[yyyy-mm-dd] hh:mm[:ss]");
+                    input.onchange = () => {
+                        settings[name] = parse_datetime(input.value);
+                        input.classList.toggle("invalid-input", settings[name] === null && input.value !== "");
+                        if (settings[name] === null) {
+                            delete settings[name];
+                        }
+                    };
+                    break;
+                case "duration":
+                    input.setAttribute("placeholder", "[hh:]mm:ss");
+                    input.onchange = () => {
+                        settings[name] = parse_duration(input.value);
+                        input.classList.toggle("invalid-input", settings[name] === null && input.value !== "");
+                        if (settings[name] === null) {
+                            delete settings[name];
+                        }
+                    };
+                    break;
+                case "number":
+                    input.setAttribute("type", "number");
+                    input.setAttribute("min", 0);
+                    if (step !== undefined) {
+                        input.setAttribute("step", step);
+                    }
+                    input.onchange = () => {
+                        settings[name] = Number(input.value);
+                        if (settings[name] === 0) {
+                            delete settings[name];
+                        }
+                    };
+                    break;
+            }
             input.value = value;
-            input.onchange = () => settings[name] = Number(input.value);
             input.onchange();
             cell.append(input);
             row.append(cell);
index 72c6b54782ae8b4207c7f4199092b9c9e09be32d..6ea0fd5331e4bb0a096136a947becdc4a4225cc1 100644 (file)
@@ -1,6 +1,7 @@
 const svgns = "http://www.w3.org/2000/svg";
 
 import { card_href, suit_href } from "./card.js";
+import { Chatroom } from "./chatroom.js";
 import { CongratulateWinner } from "./winner.js";
 
 export class TexasHoldEm {
@@ -215,11 +216,27 @@ export class TexasHoldEm {
         bet_control_label.classList.add("bet-control-label");
         this.svg.append(bet_control_label);
 
+        this.chat_control = document.createElementNS(svgns, "rect");
+        this.chat_control.setAttribute("x", "5");
+        this.chat_control.setAttribute("y", "470");
+        this.chat_control.setAttribute("width", "25");
+        this.chat_control.setAttribute("height", "25");
+        this.chat_control.setAttribute("rx", "2");
+        this.chat_control.onclick = () => this.chatroom_container.classList.toggle("hidden");
+        this.chat_control.classList.add("chat-control");
+        this.svg.append(this.chat_control);
+
         this.container.append(this.svg);
         for (const user_action of this.actions) {
             this.take_action(user_action);
         }
 
+        this.chatroom_container = document.createElement("div");
+        this.chatroom_container.classList.add("embedded-chatroom");
+        this.chatroom = new Chatroom(this.chatroom_container, summary, actions, username, send);
+
+        this.container.append(this.chatroom_container);
+
         if (!this.seats.has(this.username)) {
             this.send({type: "TakeAction", action: {action: "Join", seat: 0, chips: this.summary.settings.starting_stack}});
         }
index fddab8de9e1129091522ee69509c7377ca712ed9..2d2c41835a5008f1a27eb6eb9ec8f73c87a3d7f7 100644 (file)
@@ -2,7 +2,7 @@
     width: 100%;
     height: 100%;
     display: grid;
-    font-size: 4vw;
+    font-size: 4vmin;
     overflow: hidden;
     position: relative;
     background-color: #dfefff;
@@ -16,7 +16,7 @@
 
 .chatroom > input {
     width: 100%;
-    height: 10vw;
+    height: 10vmin;
     font-size: inherit;
     align-self: end;
 }
 
 .chatroom-username {
     justify-self: left;
-    width: 36vw;
+    width: 36vmin;
     height: 100%;
-    margin: 0.5vw;
+    margin: 0.5vmin;
     background-color: grey;
     color: white;
     text-align: center;
-    border-radius: 3vw;
+    border-radius: 3vmin;
 }
 
 .chatroom-error > .chatroom-username {
@@ -59,7 +59,7 @@
 }
 
 .chatroom-win-game > .chatroom-username {
-    background: repeating-radial-gradient(circle at center, #a17f1a 0vw, gold, #a17f1a 2vw);
+    background: repeating-radial-gradient(circle at center, #a17f1a 0vmin, gold, #a17f1a 2vmin);
     color: black;
 }
 
@@ -67,6 +67,6 @@
     justify-self: right;
     text-align: left;
     height: 100%;
-    margin: 0.5vw;
-    width: calc(100% - 36vw);
+    margin: 0.5vmin;
+    width: calc(100% - 36vmin);
 }
index 6c0cc4cc0805f6ae237e5337ed43d6b81adbbb18..deb73aff8bde68890504368464b35398aa0b9e1f 100644 (file)
     top: 0;
     font-size: 5vw;
 }
+
+@media (max-aspect-ratio: 1/1) {
+    .embedded-chatroom {
+        grid-row: 2;
+        grid-column: 1;
+        width: 100vw;
+        height: calc(100vh - 100vw);
+    }
+}
+
+@media (min-aspect-ratio: 1/1) {
+    .embedded-chatroom {
+        grid-row: 1;
+        grid-column: 2;
+        width: calc(100vw - 100vh);
+        height: 100vh;
+    }
+}
+
+.embedded-chatroom
+    grid-column: 2;
index beb9a58d3e271687a847c1460652334cb11e0088..f0446b9626f346e7ae9a210d240474eb22d9f702 100644 (file)
@@ -9,6 +9,17 @@
     stroke-width: 2px;
 }
 
+.chat-control {
+    fill: #e0e0e0;
+    stroke: black;
+    stroke-width: 1px;
+    transition: fill 0.5s;
+}
+
+.chat-control:hover {
+    fill: #ffffff;
+}
+
 .fold-control, .call-control, .bet-control {
     fill: #808080;
     stroke: black;