From 63881f05fa1cc5a6c0c9072d411b89e32e31a64f Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Thu, 16 Nov 2023 23:44:32 -0700 Subject: [PATCH] New scoreboard view --- theme/moth.mjs | 5 ++- theme/scoreboard-all.html | 19 +++++++++++ theme/scoreboard.css | 41 ++++++++++++++++------ theme/scoreboard.html | 2 +- theme/scoreboard.mjs | 71 +++++++++++++++++++++++++++++++++++---- 5 files changed, 117 insertions(+), 21 deletions(-) create mode 100644 theme/scoreboard-all.html diff --git a/theme/moth.mjs b/theme/moth.mjs index 4bce873..4e2854d 100644 --- a/theme/moth.mjs +++ b/theme/moth.mjs @@ -492,9 +492,7 @@ class State { * @returns {Scores} */ CurrentScores() { - let scores - for (scores of this.ScoreHistory()); - return scores + return [...this.ScoresHistory()].pop() } } @@ -676,4 +674,5 @@ class Server { export { Hash, Server, + State, } \ No newline at end of file diff --git a/theme/scoreboard-all.html b/theme/scoreboard-all.html new file mode 100644 index 0000000..42613ca --- /dev/null +++ b/theme/scoreboard-all.html @@ -0,0 +1,19 @@ + + + + Scoreboard + + + + + + + + +
+
+
+
+
+ + diff --git a/theme/scoreboard.css b/theme/scoreboard.css index 1ebdaa6..b15d1cb 100644 --- a/theme/scoreboard.css +++ b/theme/scoreboard.css @@ -33,32 +33,37 @@ max-height: 60vh; } +/* Only the first child of a rotate class is visible */ +.rotate > div:nth-child(n + 2) { + display: none; +} + /** Scoreboard */ -.rankings { +.rankings.classic { width: 100%; position: relative; background-color: #000c; } -.rankings div { +.rankings.classic div { height: 1.2rem; display: flex; align-items: center; } -.rankings div:nth-child(6n){ +.rankings.classic div:nth-child(6n){ background-color: #ccc3; } -.rankings div:nth-child(6n+3) { +.rankings.classic div:nth-child(6n+3) { background-color: #0f03; } -.rankings span { +.rankings.classic span { display: inline-block; overflow: hidden; } -.rankings span.category { +.rankings.classic span.category { font-size: 80%; } -.rankings span.teamname { +.rankings.classic span.teamname { height: auto; font-size: inherit; color: white; @@ -67,8 +72,8 @@ position: absolute; right: 0.2em; } -.rankings span.teamname:hover, -.rankings span.category:hover { +.rankings.classic span.teamname:hover, +.rankings.classic span.category:hover { width: inherit; max-width: 100%; } @@ -78,8 +83,24 @@ vertical-align: top; } +.rankings.category { + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; +} +.rankings.category div { + border: solid black 2px; + min-width: 15em; +} +.rankings.category table { + width: 100%; +} +.rankings.category td.number { + text-align: right; +} + @media only screen and (max-width: 450px) { - .rankings span.teamname { + .rankings.classic span.teamname { max-width: 6em; text-overflow: ellipsis; } diff --git a/theme/scoreboard.html b/theme/scoreboard.html index a88c549..a9be4fb 100644 --- a/theme/scoreboard.html +++ b/theme/scoreboard.html @@ -10,7 +10,7 @@ -
+
diff --git a/theme/scoreboard.mjs b/theme/scoreboard.mjs index 6f96a14..f14ab30 100644 --- a/theme/scoreboard.mjs +++ b/theme/scoreboard.mjs @@ -34,22 +34,39 @@ async function update() { } // Pull configuration settings - let ScoreboardConfig = config.Scoreboard ?? {} - let ReplayHistory = ScoreboardConfig.ReplayHistory ?? false - let ReplayDurationMS = ScoreboardConfig.ReplayDurationMS ?? 300 - let ReplayFPS = ScoreboardConfig.ReplayFPS ?? 24 if (!config.Scoreboard) { console.warn("config.json has empty Scoreboard section") } - + let ScoreboardConfig = config.Scoreboard ?? {} let state = await server.GetState() + // Show URL of server for (let e of document.querySelectorAll(".location")) { e.textContent = common.BaseURL e.classList.toggle("hidden", !(ScoreboardConfig.DisplayServerURLWhenEnabled && state.Enabled)) } - let rankingsElement = document.querySelector(".rankings") + // Rotate views + for (let e of document.querySelectorAll(".rotate")) { + e.appendChild(e.firstChild) + } + + // Render rankings + for (let e of document.querySelectorAll(".rankings")) { + if (e.classList.contains("classic")) { + classicRankings(e, state, ScoreboardConfig) + } else if (e.classList.contains("category")) { + categoryRankings(e, state, ScoreboardConfig) + } + } + +} + +async function classicRankings(rankingsElement, state, ScoreboardConfig) { + let ReplayHistory = ScoreboardConfig.ReplayHistory ?? false + let ReplayDurationMS = ScoreboardConfig.ReplayDurationMS ?? 300 + let ReplayFPS = ScoreboardConfig.ReplayFPS ?? 24 + let logSize = state.PointsLog.length // Figure out the timing so that we can replay the scoreboard in about @@ -78,7 +95,7 @@ async function update() { let topScore = scores.CyFiScore(sortedTeamIDs[0]) for (let teamID of sortedTeamIDs) { - let teamName = state.TeamNames[teamID] + let teamName = state.TeamNames[teamID] ?? "rodney" let row = rankingsElement.appendChild(document.createElement("div")) @@ -118,6 +135,46 @@ async function update() { } } +/** + * + * @param {*} rankingsElement + * @param {moth.State} state + * @param {*} ScoreboardConfig + */ +async function categoryRankings(rankingsElement, state, ScoreboardConfig) { + while (rankingsElement.firstChild) rankingsElement.firstChild.remove() + let scores = state.CurrentScores() + for (let category of scores.Categories) { + let categoryBox = rankingsElement.appendChild(document.createElement("div")) + categoryBox.classList.add("category") + + categoryBox.appendChild(document.createElement("h2")).textContent = category + + let categoryScores = [] + for (let teamID in state.TeamNames) { + categoryScores.push({ + teamName: state.TeamNames[teamID], + score: scores.GetPoints(category, teamID), + }) + } + categoryScores.sort((a, b) => b.score - a.score) + + let table = categoryBox.appendChild(document.createElement("table")) + let rows = 0 + for (let categoryScore of categoryScores) { + let row = table.appendChild(document.createElement("tr")) + row.appendChild(document.createElement("td")).textContent = categoryScore.teamName + let td = row.appendChild(document.createElement("td")) + td.textContent = categoryScore.score + td.classList.add("number") + rows += 1 + if (rows == 5) { + break + } + } + } +} + function init() { setInterval(update, common.Minute) update()