From 34d77cd777bd44d7bfe0e3ab3379f9be256ca88b Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sun, 9 Nov 2025 09:56:59 +0100 Subject: Moved to javascript codebase --- index.html | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 index.html (limited to 'index.html') diff --git a/index.html b/index.html new file mode 100644 index 0000000..5ac86c8 --- /dev/null +++ b/index.html @@ -0,0 +1,31 @@ + + + + + + + Match Up! + + + + +
+ +
+

Regulars

+
+
+
+ + + + \ No newline at end of file -- cgit v1.3.1 From 6456fae865aae8b2ba398b5f78f2589c93d0643d Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sun, 9 Nov 2025 11:53:05 +0100 Subject: Working regulars and courts --- court.mjs | 28 ++++++++++++++++++++++++++++ index.html | 13 ++++++++----- index.js | 29 +++++++++++++++++++++-------- player.mjs | 4 ++-- 4 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 court.mjs (limited to 'index.html') diff --git a/court.mjs b/court.mjs new file mode 100644 index 0000000..31f277f --- /dev/null +++ b/court.mjs @@ -0,0 +1,28 @@ +export class Court { + constructor(id) { + this.id = id; + this.name = "Court " + (id + 1); + this.status = true; + } + toggle() { + this.status = !this.status; + this.print(); + } + print() { + let status = this.status ? "active" : "inactive"; + console.log(this.name + " is currently " + status); + } + render() { + let type = this.status ? "" : 'class="outline secondary"'; + return ``; + } +} + +export function generateCourts() { + let courts = []; + const totalCourts = 12; + for (let i = 0; i < totalCourts; i++) { + courts.push(new Court(i)); + } + return courts; +} \ No newline at end of file diff --git a/index.html b/index.html index 5ac86c8..8bc2191 100644 --- a/index.html +++ b/index.html @@ -3,9 +3,8 @@ - Match Up! - + @@ -15,14 +14,18 @@
  • Match Up
  • +

    Courts

    +
    +
    +

    Regulars

    -
    +
    diff --git a/index.js b/index.js index 97c65d6..5d34c96 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,24 @@ +import { generateCourts } from "./court.mjs"; import { generatePlayers } from "./player.mjs"; -// Player handling -let players = generatePlayers(); -let renderedHTML = players.map((p) => {return p.render();}).join(''); -document.getElementById('player_list').innerHTML = renderedHTML; -function togglePlayer(id) { - console.log(players); - let player = players.find((p) => p.id === id); - document.getElementById('player_' + id).outerHTML = player.toggle(); +// Court handling +let courts = generateCourts(); +addToggleBehaviour(courts, "court_"); + +// Regulars handling +let regulars = generatePlayers(); +addToggleBehaviour(regulars, "regular_"); + +function addToggleBehaviour(list, description) { + let renderedHTML = list.map((i) => {return i.render();}).join(''); + let listId = description + 'list'; + document.getElementById(listId).innerHTML = renderedHTML; + for (let index = 0; index < list.length; ++index) { + const p = document.getElementById(description + index); + p.addEventListener("click", () => { + list[index].toggle(); + p.classList.toggle('secondary'); + p.classList.toggle('outline'); + }); + } } \ No newline at end of file diff --git a/player.mjs b/player.mjs index a0e2535..d75e659 100644 --- a/player.mjs +++ b/player.mjs @@ -7,7 +7,7 @@ export class Player { } toggle() { this.status = !this.status; - return this.render(); + this.print(); } print() { let status = this.status ? "active" : "inactive"; @@ -15,7 +15,7 @@ export class Player { } render() { let type = this.status ? "" : 'class="outline secondary"'; - return ``; + return ``; } } -- cgit v1.3.1 From 3dc01fbb930a4eacf416a9cc7911d90da75e6c33 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sun, 9 Nov 2025 15:57:07 +0100 Subject: Moved a lot of the functionality --- assets/icon.ipe | 461 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ court.mjs | 2 +- game.mjs | 47 ++++++ icon.svg | 15 ++ index.css | 4 + index.html | 79 +++++++--- index.js | 32 ++-- player.mjs | 2 +- timer.js | 91 +++++++++++ utils.mjs | 13 ++ 10 files changed, 709 insertions(+), 37 deletions(-) create mode 100644 assets/icon.ipe create mode 100644 game.mjs create mode 100644 icon.svg create mode 100644 timer.js create mode 100644 utils.mjs (limited to 'index.html') diff --git a/assets/icon.ipe b/assets/icon.ipe new file mode 100644 index 0000000..d23d37f --- /dev/null +++ b/assets/icon.ipe @@ -0,0 +1,461 @@ + + + + + + + +0 0 m +-1 0.333 l +-1 -0.333 l +h + + + + +0 0 m +-1 0.333 l +-1 -0.333 l +h + + + + +0 0 m +-1 0.333 l +-0.8 0 l +-1 -0.333 l +h + + + + +0 0 m +-1 0.333 l +-0.8 0 l +-1 -0.333 l +h + + + + +0.6 0 0 0.6 0 0 e +0.4 0 0 0.4 0 0 e + + + + +0.6 0 0 0.6 0 0 e + + + + + +0.5 0 0 0.5 0 0 e + + +0.6 0 0 0.6 0 0 e +0.4 0 0 0.4 0 0 e + + + + + +-0.6 -0.6 m +0.6 -0.6 l +0.6 0.6 l +-0.6 0.6 l +h +-0.4 -0.4 m +0.4 -0.4 l +0.4 0.4 l +-0.4 0.4 l +h + + + + +-0.6 -0.6 m +0.6 -0.6 l +0.6 0.6 l +-0.6 0.6 l +h + + + + + +-0.5 -0.5 m +0.5 -0.5 l +0.5 0.5 l +-0.5 0.5 l +h + + +-0.6 -0.6 m +0.6 -0.6 l +0.6 0.6 l +-0.6 0.6 l +h +-0.4 -0.4 m +0.4 -0.4 l +0.4 0.4 l +-0.4 0.4 l +h + + + + + + +-0.43 -0.57 m +0.57 0.43 l +0.43 0.57 l +-0.57 -0.43 l +h + + +-0.43 0.57 m +0.57 -0.43 l +0.43 -0.57 l +-0.57 0.43 l +h + + + + + +0 0 m +-1 0.333 l +-1 -0.333 l +h + + + + +0 0 m +-1 0.333 l +-0.8 0 l +-1 -0.333 l +h + + + + +0 0 m +-1 0.333 l +-0.8 0 l +-1 -0.333 l +h + + + + +-1 0.333 m +0 0 l +-1 -0.333 l + + + + +0 0 m +-1 0.333 l +-1 -0.333 l +h +-1 0 m +-2 0.333 l +-2 -0.333 l +h + + + + +0 0 m +-1 0.333 l +-1 -0.333 l +h +-1 0 m +-2 0.333 l +-2 -0.333 l +h + + + + +0.5 0 m +-0.5 0.333 l +-0.5 -0.333 l +h + + + + +0.5 0 m +-0.5 0.333 l +-0.5 -0.333 l +h + + + + +0.5 0 m +-0.5 0.333 l +-0.3 0 l +-0.5 -0.333 l +h + + + + +0.5 0 m +-0.5 0.333 l +-0.3 0 l +-0.5 -0.333 l +h + + + + +1 0 m +0 0.333 l +0 -0.333 l +h +0 0 m +-1 0.333 l +-1 -0.333 l +h + + + + +1 0 m +0 0.333 l +0 -0.333 l +h +0 0 m +-1 0.333 l +-1 -0.333 l +h + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +96 624 m +92 612 +88 600 +92 588 +96 576 c +96 576 +100 588 +104 600 +100 612 +96 624 c +h + + +96 624 m +92 612 +88 600 +92 588 +96 576 c +96 576 +100 588 +104 600 +100 612 +96 624 c +h + + +96 624 m +92 612 +88 600 +92 588 +96 576 c +96 576 +100 588 +104 600 +100 612 +96 624 c +h + + +96 624 m +92 612 +88 600 +92 588 +96 576 c +96 576 +100 588 +104 600 +100 612 +96 624 c +h + + +96 624 m +92 612 +88 600 +92 588 +96 576 c +96 576 +100 588 +104 600 +100 612 +96 624 c +h + + +92 620 m +4 0 0 -4 96 620 100 620 a +100 616 l +92 616 l +h + + +96 624 m +92 612 +88 600 +92 588 +96 576 c +96 576 +100 588 +104 600 +100 612 +96 624 c +h + + +96 624 m +92 612 +88 600 +92 588 +96 576 c +96 576 +100 588 +104 600 +100 612 +96 624 c +h + + +96 624 m +92 612 +88 600 +92 588 +96 576 c +96 576 +100 588 +104 600 +100 612 +96 624 c +h + + +96 624 m +92 612 +88 600 +92 588 +96 576 c +96 576 +100 588 +104 600 +100 612 +96 624 c +h + + +96 624 m +92 612 +88 600 +92 588 +96 576 c +96 576 +100 588 +104 600 +100 612 +96 624 c +h + + +92 620 m +4 0 0 -4 96 620 100 620 a +100 616 l +92 616 l +h + + + diff --git a/court.mjs b/court.mjs index 31f277f..b2cd9de 100644 --- a/court.mjs +++ b/court.mjs @@ -13,7 +13,7 @@ export class Court { console.log(this.name + " is currently " + status); } render() { - let type = this.status ? "" : 'class="outline secondary"'; + let type = this.status ? `class="button is-success"` : 'class="button is-dark"'; return ``; } } diff --git a/game.mjs b/game.mjs new file mode 100644 index 0000000..7b5d4c9 --- /dev/null +++ b/game.mjs @@ -0,0 +1,47 @@ +class Team { + constructor(player_1, player_2) { + this.player_1 = player_1; + this.player_2 = player_2; + } + print() { + console.log(this.player_1 + " and " + this.player_2); + } + render() { + return `${this.player_1}${this.player_2}`; + } +} + +class Game { + constructor(court, team_1, team_2) { + this.court_id = court.id; + this.court_status = court.status; + this.team_1 = team_1; + this.team_2 = team_2; + } + print() { + let team_1 = this.team_1.player_1 + " and " + this.team_1.player_2; + let team_2 = this.team_2.player_1 + " and " + this.team_2.player_2; + console.log(team_1 + " is playing against " + team_2); + } + render() { + let type = this.team_1.player_1 === "RESERVED" ? `class="is-danger is-light"` : ``; + return `${this.court_id + 1}${this.team_1.render()}${this.team_2.render()}`; + } +} + +export class Round { + constructor(number, courts) { + this.number = number; + this.courts = courts; + } + render() { + let output = `

    Round ${this.number}

    `; + let team = new Team("---", "---"); + for (const court of this.courts ) { + let game = new Game(court, team, team); + output += game.render() + } + output += "
    CourtTeam 1Team 2
    "; + return output; + } +} \ No newline at end of file diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..d2700a4 --- /dev/null +++ b/icon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/index.css b/index.css index 50a97c3..4e559cf 100644 --- a/index.css +++ b/index.css @@ -7,4 +7,8 @@ .button-grid button { padding: 10px; text-align: center; +} +#timer-display{ + text-align: center; + width: fit-content; } \ No newline at end of file diff --git a/index.html b/index.html index 8bc2191..e85e6f9 100644 --- a/index.html +++ b/index.html @@ -1,34 +1,69 @@ - + Match Up! - + + + -
    - -
    -

    Courts

    -
    +
    +
    +
    +

    Match Up!

    +
    +
    +
    + + + + + +
    +
    +
    -
    -

    Regulars

    -
    -
    -
    - +
    +

    Games

    +
    +
    +
    +

    Controls

    +
    + + +
    +
    +
    +

    Courts

    +
    +
    +
    +

    Regulars

    +
    +
    +
    +

    Player Update

    +
    + +
    +
    + + + \ No newline at end of file diff --git a/index.js b/index.js index 5d34c96..98236a4 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ +import { Round } from "./game.mjs"; import { generateCourts } from "./court.mjs"; import { generatePlayers } from "./player.mjs"; +import { addToggleBehaviour } from "./utils.mjs"; // Court handling let courts = generateCourts(); @@ -8,17 +10,21 @@ addToggleBehaviour(courts, "court_"); // Regulars handling let regulars = generatePlayers(); addToggleBehaviour(regulars, "regular_"); - -function addToggleBehaviour(list, description) { - let renderedHTML = list.map((i) => {return i.render();}).join(''); - let listId = description + 'list'; - document.getElementById(listId).innerHTML = renderedHTML; - for (let index = 0; index < list.length; ++index) { - const p = document.getElementById(description + index); - p.addEventListener("click", () => { - list[index].toggle(); - p.classList.toggle('secondary'); - p.classList.toggle('outline'); - }); +const fileInput = document.querySelector("#file-js input[type=file]"); + if (fileInput !== null) { + fileInput.onchange = () => { + if (fileInput.files.length > 0) { + const fileName = document.querySelector("#file-js .file-name"); + fileName.textContent = fileInput.files[0].name; + console.log(fileInput.files[0]); + } } -} \ No newline at end of file +}; + +// Game handling +let games = new Round(0, courts); +let renderedHTML = games.render(); +document.getElementById("game_list").innerHTML = renderedHTML; +document.getElementById("propose").addEventListener("click", () => { + console.log("Propose new games!"); +}) \ No newline at end of file diff --git a/player.mjs b/player.mjs index d75e659..2982df0 100644 --- a/player.mjs +++ b/player.mjs @@ -14,7 +14,7 @@ export class Player { console.log(this.name + " has a level of " + this.level + " and is currently " + status); } render() { - let type = this.status ? "" : 'class="outline secondary"'; + let type = this.status ? `class="button is-success"` : 'class="button is-danger"'; return ``; } } diff --git a/timer.js b/timer.js new file mode 100644 index 0000000..25d4610 --- /dev/null +++ b/timer.js @@ -0,0 +1,91 @@ +let minsPerRound = Number(sessionStorage.getItem("minsPerRound")) || 12; +let timerRunningFlag = Number(sessionStorage.getItem("timerRunningFlag")) || 0; +let deadline = Number(sessionStorage.getItem("deadline")) || new Date().getTime(); +let clock = 0; + +function setTimer(m, s) { + let timer = document.getElementById("timer-display"); + m = m < 10 ? ("0" + m) : m; + s = s < 10 ? ("0" + s) : s; + timer.value = `${m}:${s}`; +} + +function setSessionData() { + sessionStorage.setItem("deadline", deadline); + sessionStorage.setItem("minsPerRound", minsPerRound); + sessionStorage.setItem("timerRunningFlag", timerRunningFlag); +} + +function resetTimer() { + clearInterval(clock); + setTimer(minsPerRound, 0); + timerRunningFlag = 0; + setSessionData(); +} + +function updateTimer(milliseconds) { + const m = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60)); + const s = Math.floor((milliseconds % (1000 * 60)) / 1000); + setTimer(m, s); +} + +function refreshTimer() { + const now = new Date().getTime(); + const millisecondsToDeadline = deadline - now; + if (millisecondsToDeadline > 0) { + updateTimer(millisecondsToDeadline); + return; + } + resetTimer(); + const notification = new Notyf({ + duration: 30 * 1000, + position: {x: 'center', y: 'top'}, + types: [{ + type: 'success', + background: 'indianred', + duration: 1000 * 1000, + dismissible: true + }] + }); + notification.success("Round completed"); +} + +function startTimer(fresh) { + if (fresh) { + let now = new Date().getTime(); + const millisecondsToDeadline = minsPerRound * 60 * 1000; + deadline = now + millisecondsToDeadline; + } + timerRunningFlag = 1; + setSessionData(); + clock = setInterval(refreshTimer, 1000); +} + +window.onload = function () { + if (timerRunningFlag) + startTimer(0); + else resetTimer(); + + document.getElementById("timer-start").addEventListener('click', ()=> { + if (timerRunningFlag != 0) return; + startTimer(1); + }); + + document.getElementById("timer-stop").addEventListener('click', ()=>{ + resetTimer(); + }); + + document.getElementById("timer-inc").addEventListener('click', ()=> { + if (timerRunningFlag == 0) { + minsPerRound += 1; + resetTimer(); + } + }); + + document.getElementById("timer-dec").addEventListener('click', ()=> { + if (timerRunningFlag == 0 && minsPerRound != 0) { + minsPerRound -= 1; + resetTimer(); + } + }); +} \ No newline at end of file diff --git a/utils.mjs b/utils.mjs new file mode 100644 index 0000000..e639916 --- /dev/null +++ b/utils.mjs @@ -0,0 +1,13 @@ +export function addToggleBehaviour(list, description) { + let renderedHTML = list.map((i) => {return i.render();}).join(''); + let listId = description + 'list'; + document.getElementById(listId).innerHTML = renderedHTML; + for (let index = 0; index < list.length; ++index) { + const p = document.getElementById(description + index); + p.addEventListener("click", () => { + list[index].toggle(); + p.classList.toggle('is-success'); + p.classList.toggle('is-danger'); + }); + } +} \ No newline at end of file -- cgit v1.3.1 From 02d11ff6d5392ae6207380ea7656f8060a5a8c0b Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sun, 9 Nov 2025 16:38:22 +0100 Subject: Updated the deploy step --- .gitignore | 5 ++++- .gitlab-ci.yml | 6 ------ index.html | 1 + 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'index.html') diff --git a/.gitignore b/.gitignore index fc0846e..e8f5eb9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ # Data files -*.csv \ No newline at end of file +*.csv + +# Output files +public \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 31bc86e..96fb3e5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,13 +2,7 @@ image: node:lts create-pages: pages: - # The folder that contains the files to be exposed at the Page URL publish: public - rules: - # This ensures that only pushes to the default branch will trigger - # a pages deploy - - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH - # Functions that should be executed before the build script is run before_script: - mkdir public script: diff --git a/index.html b/index.html index e85e6f9..bd3c8cb 100644 --- a/index.html +++ b/index.html @@ -13,6 +13,7 @@
    +

    Match Up!

    -- cgit v1.3.1 From dd0c6a458ef5229a5fddf0e7cae25fb3a5d495f7 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sun, 9 Nov 2025 19:22:25 +0100 Subject: Cleaned up the index --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'index.html') diff --git a/index.html b/index.html index bd3c8cb..e58a3da 100644 --- a/index.html +++ b/index.html @@ -13,11 +13,11 @@
    -
    +

    Match Up!

    -
    +
    -- cgit v1.3.1 From 5dcf65616f82cb9b652f6913c93e26ff50983fdf Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Tue, 11 Nov 2025 16:23:12 +0100 Subject: Data load added --- index.html | 4 ++-- index.js | 18 +++++++++++++----- player.mjs | 20 ++++++++++++++++++++ utils.mjs | 19 +++++++++++++++++-- 4 files changed, 52 insertions(+), 9 deletions(-) (limited to 'index.html') diff --git a/index.html b/index.html index e58a3da..9cefa80 100644 --- a/index.html +++ b/index.html @@ -49,9 +49,9 @@

    Player Update

    -
    +
    -
    - -
    -
    -
    - -
    -
    -
    - + +
    +
    +
    - - - - - + + + + +
    diff --git a/index.js b/index.js index 6d74bd2..35f564e 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,6 @@ import { CourtManager } from "./court.mjs"; import { PlayerManager, addLoadBehaviour } from "./player.mjs"; import { Round, generateDummyGames, proposeGames } from "./game.mjs"; - // Court and player handling let c = new CourtManager(); let p = new PlayerManager(); @@ -11,34 +10,39 @@ addLoadBehaviour(p); // Game handling let proposal; let session = []; // Persist this in local storage with 1 day limit -if (session.length === 0) {session.push(new Round(generateDummyGames(c.listData, "---"), 0))}; +if (session.length === 0) { + session.push(new Round(generateDummyGames(c.listData, "---"), 0)); +} document.getElementById("game_list").innerHTML = session.at(-1).render(); document.getElementById("propose").addEventListener("click", () => { - let normalize = Number(document.getElementById("skill").value); - console.log(normalize); - let players = p.listData.map(function(p) { - p.skill = Math.ceil(p.skill / normalize); - return p; - }) - proposal = proposeGames(players, c.listData); - document.getElementById("game_list").innerHTML = new Round(proposal).render(); -}) + let normalize = Number(document.getElementById("skill").value); + console.log(normalize); + let players = p.listData.map(function (p) { + p.skill = Math.ceil(p.skill / normalize); + return p; + }); + proposal = proposeGames(players, c.listData); + document.getElementById("game_list").innerHTML = new Round(proposal).render(); +}); document.getElementById("confirm").addEventListener("click", () => { - console.log("Confirm the proposal on screen for round " + session.length + "."); - session.push(new Round(proposal, session.length)); - // Need to update the number of games played here - document.getElementById("game_list").innerHTML = session.at(-1).render(); - document.getElementById("timer-start").click(); - const notification = new Notyf({ - duration: 30 * 1000, - position: {x: 'center', y: 'top'}, - types: [{ - type: 'success', - background: 'green', - duration: 1000 * 1000, - dismissible: true - }] - }); - notification.success("Round confirmed!"); - -}) \ No newline at end of file + console.log( + "Confirm the proposal on screen for round " + session.length + "." + ); + session.push(new Round(proposal, session.length)); + // Need to update the number of games played here + document.getElementById("game_list").innerHTML = session.at(-1).render(); + document.getElementById("timer-start").click(); + const notification = new Notyf({ + duration: 30 * 1000, + position: { x: "center", y: "top" }, + types: [ + { + type: "success", + background: "green", + duration: 1000 * 1000, + dismissible: true, + }, + ], + }); + notification.success("Round confirmed!"); +}); diff --git a/player.mjs b/player.mjs index 5d8f16a..1614c89 100644 --- a/player.mjs +++ b/player.mjs @@ -1,104 +1,115 @@ import { Manager } from "./utils.mjs"; class Player { - constructor(id, name, level, status, totalPlayed) { - this.id = id; - this.name = name; - this.level = typeof level !== 'undefined' ? level : 1; - this.status = typeof status !== 'undefined' ? status : false; - this.totalPlayed = typeof totalPlayed !== 'undefined' ? status : 0; - } - toggle() { - this.status = !this.status; - this.print(); - } - print() { - let status = this.status ? "active" : "inactive"; - console.log(this.name + " has a level of " + this.level + " and is currently " + status); - } - render() { - let type = this.status ? `class="button is-success"` : 'class="button is-danger"'; - return ``; - } + constructor(id, name, level, status, totalPlayed) { + this.id = id; + this.name = name; + this.level = typeof level !== "undefined" ? level : 1; + this.status = typeof status !== "undefined" ? status : false; + this.totalPlayed = typeof totalPlayed !== "undefined" ? status : 0; + } + toggle() { + this.status = !this.status; + this.print(); + } + print() { + let status = this.status ? "active" : "inactive"; + console.log( + this.name + + " has a level of " + + this.level + + " and is currently " + + status + ); + } + render() { + let type = this.status + ? `class="button is-medium is-success"` + : 'class="button is-medium is-danger"'; + return ``; + } } function randomLevel() { - const max_level = 10; - return Math.floor(Math.random() * max_level + 1); + const max_level = 10; + return Math.floor(Math.random() * max_level + 1); } function randomStatus() { - return Math.random() < 0.5; + return Math.random() < 0.5; } function parseLineToPlayer(data) { - var info = data.split(","); - var id = Number(info[0].trim()); - var name = info[1].trim() + " " + info[2].trim(); - var level = Number(info[3].trim()); - return new Player(id, name, level, true, 0); + var info = data.split(","); + var id = Number(info[0].trim()); + var name = info[1].trim() + " " + info[2].trim(); + var level = Number(info[3].trim()); + return new Player(id, name, level, true, 0); } export class PlayerManager extends Manager { - constructor() { - super("regular", []); - console.log("Loading player data."); - let data = JSON.parse(localStorage.getItem(this.description)); - if (data === null || data.length === 0) { - console.log("Player data unavailable: " + data); - this.generate(); - return; - } - for (let i = 0; i < data.length; i++) { - var id = Number(data[i].id); - var name = data[i].name; - var level = data[i].level; - var status = data[i].status; - var totalPlayed = data[i].totalPlayed; - this.listData.push(new Player(id, name, level, status, totalPlayed)); - } - console.log("Loaded " + this.listData.length + " players."); - this.render(); + constructor() { + super("regular", []); + console.log("Loading player data."); + let data = JSON.parse(localStorage.getItem(this.description)); + if (data === null || data.length === 0) { + console.log("Player data unavailable: " + data); + this.generate(); + return; } - generate() { - console.log("Generating player data."); - let maxPlayerCount = 71; - for (let i = 0; i < maxPlayerCount; i++) { - let p = new Player(i, "Player " + i, randomLevel(), randomStatus(), 0); - this.listData.push(p); - } - console.log("Generated data for " + this.listData.length + " players"); - this.store(); - this.render(); + for (let i = 0; i < data.length; i++) { + var id = Number(data[i].id); + var name = data[i].name; + var level = data[i].level; + var status = data[i].status; + var totalPlayed = data[i].totalPlayed; + this.listData.push(new Player(id, name, level, status, totalPlayed)); } - import(data) { - var lines = data.split("\n"); - var players = []; - for (var i = 1; i < lines.length; i++) { - if(lines[i] == undefined || lines[i].trim() == "") {continue;} - players.push(parseLineToPlayer(lines[i])); - } - console.log("Loaded " + players.length + " Players"); - for (var i=0; i { - const file = f.files[0]; - let importData; - console.log("Loading data from "+ file.name); - const reader = new FileReader(); - reader.onload = function(e) { - importData = reader.result; - p.import(importData); - }; - reader.readAsText(file); - }) - -} \ No newline at end of file + const f = document.querySelector("#player-load input[type=file]"); + f.addEventListener("change", () => { + const file = f.files[0]; + let importData; + console.log("Loading data from " + file.name); + const reader = new FileReader(); + reader.onload = function (e) { + importData = reader.result; + p.import(importData); + }; + reader.readAsText(file); + }); +} diff --git a/timer.js b/timer.js index 2049d0f..207fb66 100644 --- a/timer.js +++ b/timer.js @@ -1,86 +1,86 @@ let minsPerRound = Number(sessionStorage.getItem("minsPerRound")) || 12; let timerRunningFlag = Number(sessionStorage.getItem("timerRunningFlag")) || 0; -let deadline = Number(sessionStorage.getItem("deadline")) || new Date().getTime(); +let deadline = + Number(sessionStorage.getItem("deadline")) || new Date().getTime(); let clock = 0; function setTimer(m, s) { - let timer = document.getElementById("timer-display"); - m = m < 10 ? ("0" + m) : m; - s = s < 10 ? ("0" + s) : s; - timer.value = `${m}:${s}`; + let timer = document.getElementById("timer-display"); + m = m < 10 ? "0" + m : m; + s = s < 10 ? "0" + s : s; + timer.value = `${m}:${s}`; } function setSessionData() { - sessionStorage.setItem("deadline", deadline); - sessionStorage.setItem("minsPerRound", minsPerRound); - sessionStorage.setItem("timerRunningFlag", timerRunningFlag); + sessionStorage.setItem("deadline", deadline); + sessionStorage.setItem("minsPerRound", minsPerRound); + sessionStorage.setItem("timerRunningFlag", timerRunningFlag); } function resetTimer() { - clearInterval(clock); - setTimer(minsPerRound, 0); - timerRunningFlag = 0; - setSessionData(); + clearInterval(clock); + setTimer(minsPerRound, 0); + timerRunningFlag = 0; + setSessionData(); } function updateTimer(milliseconds) { - const m = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60)); - const s = Math.floor((milliseconds % (1000 * 60)) / 1000); - setTimer(m, s); + const m = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60)); + const s = Math.floor((milliseconds % (1000 * 60)) / 1000); + setTimer(m, s); } function refreshTimer() { - const now = new Date().getTime(); - const millisecondsToDeadline = deadline - now; - if (millisecondsToDeadline > 0) { - updateTimer(millisecondsToDeadline); - return; - } - resetTimer(); - const notification = new Notyf({ - duration: 0, - position: {x: 'center', y: 'top'}, - dismissible: true, - }); - notification.error("Round completed"); + const now = new Date().getTime(); + const millisecondsToDeadline = deadline - now; + if (millisecondsToDeadline > 0) { + updateTimer(millisecondsToDeadline); + return; + } + resetTimer(); + const notification = new Notyf({ + duration: 0, + position: { x: "center", y: "top" }, + dismissible: true, + }); + notification.error("Round completed"); } function startTimer(fresh) { - if (fresh) { - let now = new Date().getTime(); - const millisecondsToDeadline = minsPerRound * 60 * 1000; - deadline = now + millisecondsToDeadline; - } - timerRunningFlag = 1; - setSessionData(); - clock = setInterval(refreshTimer, 1000); + if (fresh) { + let now = new Date().getTime(); + const millisecondsToDeadline = minsPerRound * 60 * 1000; + deadline = now + millisecondsToDeadline; + } + timerRunningFlag = 1; + setSessionData(); + clock = setInterval(refreshTimer, 1000); } window.onload = function () { - if (timerRunningFlag) - startTimer(0); - else resetTimer(); + if (timerRunningFlag) startTimer(0); + else resetTimer(); - document.getElementById("timer-start").addEventListener('click', ()=> { - if (timerRunningFlag != 0) return; - startTimer(1); - }); + document.getElementById("timer-start").addEventListener("click", () => { + if (timerRunningFlag != 0) return; + startTimer(1); + }); - document.getElementById("timer-stop").addEventListener('click', ()=>{ - resetTimer(); - }); + document.getElementById("timer-stop").addEventListener("click", () => { + resetTimer(); + }); - document.getElementById("timer-inc").addEventListener('click', ()=> { - if (timerRunningFlag == 0) { - minsPerRound += 1; - resetTimer(); - } - }); + document.getElementById("timer-inc").addEventListener("click", () => { + if (timerRunningFlag == 0) { + minsPerRound += 1; + resetTimer(); + } + }); - document.getElementById("timer-dec").addEventListener('click', ()=> { - if (timerRunningFlag == 0 && minsPerRound != 0) { - minsPerRound -= 1; - resetTimer(); - } - }); -} \ No newline at end of file + document.getElementById("timer-dec").addEventListener("click", () => { + if (timerRunningFlag == 0 && minsPerRound != 0) { + minsPerRound -= 1; + resetTimer(); + } + }); +}; diff --git a/utils.mjs b/utils.mjs index afe200c..b21539e 100644 --- a/utils.mjs +++ b/utils.mjs @@ -1,47 +1,62 @@ export class Manager { - constructor(description, listData) { - this.description = description; - this.listData = listData; - this.count = listData.length; - } - store() { - let stringData = JSON.stringify(this.listData); - localStorage.setItem(this.description, stringData); - } - render() { - let renderedHTML = this.listData.map((i) => {return i.render();}).join(''); - let elementId = this.description + '_list'; - document.getElementById(elementId).innerHTML = renderedHTML; - this.addToggleBehaviour(); - } - addToggleBehaviour() { - for (let idx = 0; idx < this.listData.length; ++idx) { - const elementId = this.description + "_" + this.listData[idx].id; - const p = document.getElementById(elementId); - p.addEventListener("click", () => { - this.listData[idx].toggle(); - this.store(); - p.classList.toggle('is-success'); - p.classList.toggle('is-danger'); - }); - } + constructor(description, listData) { + this.description = description; + this.listData = listData; + this.count = listData.length; + } + store() { + let stringData = JSON.stringify(this.listData); + localStorage.setItem(this.description, stringData); + } + render() { + let renderedHTML = this.listData + .map((i) => { + return i.render(); + }) + .join(""); + let elementId = this.description + "_list"; + document.getElementById(elementId).innerHTML = renderedHTML; + this.addToggleBehaviour(); + } + addToggleBehaviour() { + for (let idx = 0; idx < this.listData.length; ++idx) { + const elementId = this.description + "_" + this.listData[idx].id; + const p = document.getElementById(elementId); + p.addEventListener("click", () => { + this.listData[idx].toggle(); + this.store(); + p.classList.toggle("is-success"); + p.classList.toggle("is-danger"); + }); } + } } export function getMatchCount(regulars, courts) { - const activeCourts = courts.filter(function (c) { return c.status }); - console.log("There are a total of " + activeCourts.length + " courts available."); - const activePlayers = regulars.filter(function (p) { return p.status;}); - console.log("There are a total of " + activePlayers.length + " players available."); - var possibleMatches = Math.floor(activePlayers.length / 4); - possibleMatches = possibleMatches > activeCourts.length ? activeCourts.length : possibleMatches; - return possibleMatches; + const activeCourts = courts.filter(function (c) { + return c.status; + }); + console.log( + "There are a total of " + activeCourts.length + " courts available." + ); + const activePlayers = regulars.filter(function (p) { + return p.status; + }); + console.log( + "There are a total of " + activePlayers.length + " players available." + ); + var possibleMatches = Math.floor(activePlayers.length / 4); + possibleMatches = + possibleMatches > activeCourts.length + ? activeCourts.length + : possibleMatches; + return possibleMatches; } export function shuffle(array) { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; -} \ No newline at end of file + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; +} -- cgit v1.3.1 From 0878eeb2200d609930ba597699933ed852e3ee98 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Wed, 12 Nov 2025 14:55:33 +0100 Subject: Added my name! --- index.html | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'index.html') diff --git a/index.html b/index.html index 2c4ea75..57602df 100644 --- a/index.html +++ b/index.html @@ -71,6 +71,13 @@
    +
    +
    +

    + Match Up made with ❤️ by Karan. The source code is licensed CC BY-SA 4.0. +

    +
    +
    -- cgit v1.3.1 From 6dff11c285d4990bb9a5b11d6ac36d491bea8de3 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Wed, 12 Nov 2025 21:46:00 +0100 Subject: Added the history and working version for games --- court.mjs | 2 + game.mjs | 202 +++++++++++++++++++++++++++++++++++++++---------------------- index.html | 21 +++++-- index.js | 49 ++++----------- player.mjs | 21 +++++-- utils.mjs | 2 +- 6 files changed, 176 insertions(+), 121 deletions(-) (limited to 'index.html') diff --git a/court.mjs b/court.mjs index 3a8e551..aa5bf29 100644 --- a/court.mjs +++ b/court.mjs @@ -39,6 +39,7 @@ export class CourtManager extends Manager { } console.log("Loaded " + this.listData.length + " courts."); this.render(); + this.addToggleBehaviour(); } generate() { console.log("Generating court data."); @@ -48,5 +49,6 @@ export class CourtManager extends Manager { console.log("Generated data for " + this.listData.length + " courts"); this.store(); this.render(); + this.addToggleBehaviour(); } } diff --git a/game.mjs b/game.mjs index bc5de34..d5000c3 100644 --- a/game.mjs +++ b/game.mjs @@ -1,4 +1,5 @@ -import { getMatchCount, shuffle } from "./utils.mjs"; +import { CourtManager } from "./court.mjs"; +import { getMatchCount, shuffle, Manager } from "./utils.mjs"; class Team { constructor(playerOne, playerTwo) { @@ -13,9 +14,9 @@ class Team { } } -export class Game { - constructor(court, team_1, team_2) { - this.courtId = court.id; +class Game { + constructor(courtId, team_1, team_2) { + this.courtId = courtId; this.team_1 = team_1; this.team_2 = team_2; } @@ -27,91 +28,146 @@ export class Game { render() { let type = this.team_1.playerOne === "RESERVED" ? `class="is-danger is-dark"` : ``; - // type = this.team_1.playerOne === "---" ? `class="is-warning is-dark"` : ``; return `${ this.courtId + 1 }${this.team_1.render()}${this.team_2.render()}`; } } -export function generateDummyGames(courts, description) { +function generateDummyGames(courts, description) { let team = new Team(description, description); let games = []; for (const court of courts) { - let game = new Game(court, team, team); + let game = new Game(court.id, team, team); games.push(game); } return games; } -export class Round { - constructor(games, number) { - this.games = games; - this.description = - typeof number === "undefined" ? "Proposal" : "Round " + number; +export class GameManager extends Manager { + constructor() { + super("game", []); + this.round = Number(localStorage.getItem("round")) || 0; + this.courts = new CourtManager(); + console.log("Loading game data."); + let data = JSON.parse(localStorage.getItem(this.description)); + if (data === null || data.length === 0) { + console.log("Game data unavailable: " + data); + this.generate(); + return; + } + for (let i = 0; i < data.length; i++) { + let courtId = Number(data[i].courtId); + let teamOne = new Team( + data[i].team_1.playerOne, + data[i].team_1.playerTwo + ); + let teamTwo = new Team( + data[i].team_2.playerOne, + data[i].team_2.playerTwo + ); + this.listData.push(new Game(courtId, teamOne, teamTwo)); + } + console.log("Loaded " + this.listData.length + " games."); + this.render(); + this.addDescription(); } - render() { - let output = ``; - for (const game of this.games) { - output += game.render(); + generate() { + console.log("Generating the dummy round."); + this.listData = generateDummyGames(this.courts.listData, "---"); + console.log("Loaded " + this.listData.length + " dummy round."); + this.store(); + this.render(); + this.addDescription(); + } + addDescription() { + let description = this.proposed ? "Proposal" : "Round " + this.round; + document.getElementById("game_description").innerHTML = description; + } + propose(players) { + console.log("Proposing new games!"); + let possibleMatches = getMatchCount(players, this.courts.listData); + let requiredPlayers = possibleMatches * 4; + console.log("Required players are " + requiredPlayers); + const activeCourts = this.courts.listData.filter(function (c) { + return c.status; + }); + const inactiveCourts = this.courts.listData.filter(function (c) { + return !c.status; + }); + const activePlayers = players.filter(function (p) { + return p.status; + }); + console.log("Total reserved courts are " + inactiveCourts.length + "."); + console.log("Total possible matches are " + possibleMatches + "."); + console.log("Active players are:"); + console.log( + activePlayers + .map((i) => { + return i.name; + }) + .join(",") + ); + const shuffledPlayers = shuffle(activePlayers); + shuffledPlayers.sort((a, b) => a.totalPlayed - b.totalPlayed); + const selectedPlayers = shuffledPlayers.slice(0, requiredPlayers); + selectedPlayers.sort((a, b) => a.level - b.level); + console.log("Selected players are:"); + console.log( + selectedPlayers + .map((i) => { + return i.name; + }) + .join(",") + ); + console.log("The matches are:"); + this.listData = generateDummyGames( + activeCourts.slice(possibleMatches + 1), + "---" + ); + this.listData = this.listData.concat( + generateDummyGames(inactiveCourts, "RESERVED") + ); + for (let index = 0; index < possibleMatches; index++) { + const startIndex = 4 * index; + const endIndex = startIndex + 4; + const courtPlayers = selectedPlayers.slice(startIndex, endIndex); + const team_1 = new Team(courtPlayers[0].name, courtPlayers[3].name); + const team_2 = new Team(courtPlayers[1].name, courtPlayers[2].name); + const game = new Game(activeCourts[index].id, team_1, team_2); + game.print(); + this.listData.push(game); } - output += "
    ${this.description}
    CourtTeam 1Team 2
    "; - return output; + this.listData.sort((a, b) => a.courtId - b.courtId); + this.proposed = true; + this.render(); + this.addDescription(); } -} - -export function proposeGames(players, courts) { - console.log("Proposing new games!"); - let possibleMatches = getMatchCount(players, courts); - let requiredPlayers = possibleMatches * 4; - console.log("Required players are " + requiredPlayers); - const activeCourts = courts.filter(function (c) { - return c.status; - }); - const inactiveCourts = courts.filter(function (c) { - return !c.status; - }); - const activePlayers = players.filter(function (p) { - return p.status; - }); - console.log("Total reserved courts are " + inactiveCourts.length + "."); - console.log("Total possible matches are " + possibleMatches + "."); - console.log("Active players are:"); - console.log( - activePlayers - .map((i) => { - return i.name; - }) - .join(",") - ); - const shuffledPlayers = shuffle(activePlayers); - shuffledPlayers.sort((a, b) => a.totalPlayed - b.totalPlayed); - const selectedPlayers = shuffledPlayers.slice(0, requiredPlayers); - selectedPlayers.sort((a, b) => a.level - b.level); - console.log("Selected players are:"); - console.log( - selectedPlayers - .map((i) => { - return i.name; - }) - .join(",") - ); - console.log("The matches are:"); - let games = generateDummyGames( - activeCourts.slice(possibleMatches + 1), - "---" - ); - games = games.concat(generateDummyGames(inactiveCourts, "RESERVED")); - for (let index = 0; index < possibleMatches; index++) { - const startIndex = 4 * index; - const endIndex = startIndex + 4; - const courtPlayers = selectedPlayers.slice(startIndex, endIndex); - const team_1 = new Team(courtPlayers[0].name, courtPlayers[3].name); - const team_2 = new Team(courtPlayers[1].name, courtPlayers[2].name); - const game = new Game(activeCourts[index], team_1, team_2); - game.print(); - games.push(game); + confirm() { + this.proposed = false; + this.round += 1; + localStorage.setItem("round", this.round); + // Need to update the number of games played here + this.store(); + this.addDescription(); + document.getElementById("timer-start").click(); + const notification = new Notyf({ + duration: 30 * 1000, + position: { x: "center", y: "top" }, + types: [ + { + type: "success", + background: "green", + duration: 1000 * 1000, + dismissible: true, + }, + ], + }); + notification.success("Round confirmed!"); + } + reset() { + localStorage.clear("round"); + localStorage.clear("round"); + window.location.reload(); } - games.sort((a, b) => a.courtId - b.courtId); - return games; } diff --git a/index.html b/index.html index 57602df..f243f54 100644 --- a/index.html +++ b/index.html @@ -20,8 +20,18 @@
    -

    Games

    -
    +

    Games

    + + + + + + + + + + +
    CourtTeam 1Team 2
    @@ -35,6 +45,7 @@
    +
    @@ -47,15 +58,15 @@
    -

    Courts

    +

    Courts

    -

    Regulars

    +

    Regulars

    -

    Player Update

    +

    Player Update

    - - - - - - \ No newline at end of file diff --git a/main.html b/main.html new file mode 100644 index 0000000..b3f2f0a --- /dev/null +++ b/main.html @@ -0,0 +1,102 @@ + + + + + + Match Up! + + + + + + +
    + +
    +
    + + + + + + + + + + +
    CourtTeam 1Team 2
    +
    +
    +
    + +
    + +
    + + +
    +
    +
    +

    Courts

    +
    +
    +
    +

    Regulars

    +
    +
    +
    +

    Guests

    +
    +
    +
    +

    Player Update

    +
    + +
    +
    +
    +
    + + + + + + \ No newline at end of file -- cgit v1.3.1 From 944fc2715e967a51e4ccca49d87e987ee2727bc2 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Thu, 13 Nov 2025 21:48:40 +0100 Subject: Transitioned to npm --- index.html | 97 ++++++ package-lock.json | 882 +++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 22 ++ public/favicon.ico | Bin 0 -> 13054 bytes public/icon.png | Bin 0 -> 1453 bytes src/court.mjs | 53 ++++ src/game.mjs | 186 +++++++++++ src/guest.mjs | 69 +++++ src/main.js | 20 ++ src/player.mjs | 150 +++++++++ src/style.css | 12 + src/timer.js | 88 ++++++ src/utils.mjs | 51 ++++ 13 files changed, 1630 insertions(+) create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 public/favicon.ico create mode 100644 public/icon.png create mode 100644 src/court.mjs create mode 100644 src/game.mjs create mode 100644 src/guest.mjs create mode 100644 src/main.js create mode 100644 src/player.mjs create mode 100644 src/style.css create mode 100644 src/timer.js create mode 100644 src/utils.mjs (limited to 'index.html') diff --git a/index.html b/index.html new file mode 100644 index 0000000..287841a --- /dev/null +++ b/index.html @@ -0,0 +1,97 @@ + + + + + + Match Up! + + +
    + +
    +
    + + + + + + + + + + +
    CourtTeam 1Team 2
    +
    +
    +
    + +
    + +
    + + +
    +
    +
    +

    Courts

    +
    +
    +
    +

    Regulars

    +
    +
    +
    +

    Guests

    +
    +
    +
    +

    Player Update

    +
    + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2e440f5 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,882 @@ +{ + "name": "matchup", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "matchup", + "version": "0.0.0", + "dependencies": { + "@fortawesome/fontawesome-free": "^7.1.0", + "bulma": "^1.0.4", + "notyf": "^3.10.0" + }, + "devDependencies": { + "vite": "npm:rolldown-vite@7.2.2" + } + }, + "node_modules/@emnapi/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.0.tgz", + "integrity": "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz", + "integrity": "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.1.0.tgz", + "integrity": "sha512-+WxNld5ZCJHvPQCr/GnzCTVREyStrAJjisUPtUxG5ngDA8TMlPnKp6dddlTpai4+1GNmltAeuk1hJEkBohwZYA==", + "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", + "engines": { + "node": ">=6" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", + "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@tybys/wasm-util": "^0.10.1" + } + }, + "node_modules/@oxc-project/runtime": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.96.0.tgz", + "integrity": "sha512-34lh4o9CcSw09Hx6fKihPu85+m+4pmDlkXwJrLvN5nMq5JrcGhhihVM415zDqT8j8IixO1PYYdQZRN4SwQCncg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.96.0.tgz", + "integrity": "sha512-r/xkmoXA0xEpU6UGtn18CNVjXH6erU3KCpCDbpLmbVxBFor1U9MqN5Z2uMmCHJuXjJzlnDR+hWY+yPoLo8oHDw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.47.tgz", + "integrity": "sha512-vPP9/MZzESh9QtmvQYojXP/midjgkkc1E4AdnPPAzQXo668ncHJcVLKjJKzoBdsQmaIvNjrMdsCwES8vTQHRQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.47.tgz", + "integrity": "sha512-Lc3nrkxeaDVCVl8qR3qoxh6ltDZfkQ98j5vwIr5ALPkgjZtDK4BGCrrBoLpGVMg+csWcaqUbwbKwH5yvVa0oOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.47.tgz", + "integrity": "sha512-eBYxQDwP0O33plqNVqOtUHqRiSYVneAknviM5XMawke3mwMuVlAsohtOqEjbCEl/Loi/FWdVeks5WkqAkzkYWQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.47.tgz", + "integrity": "sha512-Ns+kgp2+1Iq/44bY/Z30DETUSiHY7ZuqaOgD5bHVW++8vme9rdiWsN4yG4rRPXkdgzjvQ9TDHmZZKfY4/G11AA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.47.tgz", + "integrity": "sha512-4PecgWCJhTA2EFOlptYJiNyVP2MrVP4cWdndpOu3WmXqWqZUmSubhb4YUAIxAxnXATlGjC1WjxNPhV7ZllNgdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.47.tgz", + "integrity": "sha512-CyIunZ6D9U9Xg94roQI1INt/bLkOpPsZjZZkiaAZ0r6uccQdICmC99M9RUPlMLw/qg4yEWLlQhG73W/mG437NA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.47.tgz", + "integrity": "sha512-doozc/Goe7qRCSnzfJbFINTHsMktqmZQmweull6hsZZ9sjNWQ6BWQnbvOlfZJe4xE5NxM1NhPnY5Giqnl3ZrYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.47.tgz", + "integrity": "sha512-fodvSMf6Aqwa0wEUSTPewmmZOD44rc5Tpr5p9NkwQ6W1SSpUKzD3SwpJIgANDOhwiYhDuiIaYPGB7Ujkx1q0UQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.47.tgz", + "integrity": "sha512-Rxm5hYc0mGjwLh5sjlGmMygxAaV2gnsx7CNm2lsb47oyt5UQyPDZf3GP/ct8BEcwuikdqzsrrlIp8+kCSvMFNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.47.tgz", + "integrity": "sha512-YakuVe+Gc87jjxazBL34hbr8RJpRuFBhun7NEqoChVDlH5FLhLXjAPHqZd990TVGVNkemourf817Z8u2fONS8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.47.tgz", + "integrity": "sha512-ak2GvTFQz3UAOw8cuQq8pWE+TNygQB6O47rMhvevvTzETh7VkHRFtRUwJynX5hwzFvQMP6G0az5JrBGuwaMwYQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.7" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.47.tgz", + "integrity": "sha512-o5BpmBnXU+Cj+9+ndMcdKjhZlPb79dVPBZnWwMnI4RlNSSq5yOvFZqvfPYbyacvnW03Na4n5XXQAPhu3RydZ0w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-ia32-msvc": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.47.tgz", + "integrity": "sha512-FVOmfyYehNE92IfC9Kgs913UerDog2M1m+FADJypKz0gmRg3UyTt4o1cZMCAl7MiR89JpM9jegNO1nXuP1w1vw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.47.tgz", + "integrity": "sha512-by/70F13IUE101Bat0oeH8miwWX5mhMFPk1yjCdxoTNHTyTdLgb0THNaebRM6AP7Kz+O3O2qx87sruYuF5UxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz", + "integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/bulma": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-1.0.4.tgz", + "integrity": "sha512-Ffb6YGXDiZYX3cqvSbHWqQ8+LkX6tVoTcZuVB3lm93sbAVXlO0D6QlOTMnV6g18gILpAXqkG2z9hf9z4hCjz2g==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/notyf": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/notyf/-/notyf-3.10.0.tgz", + "integrity": "sha512-Mtnp+0qiZxgrH+TzVlzhWyZceHdAZ/UWK0/ju9U0HQeDpap1mZ8cC7H5wSI5mwgni6yeAjaxsTw0sbMK+aSuHw==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.47.tgz", + "integrity": "sha512-Mid74GckX1OeFAOYz9KuXeWYhq3xkXbMziYIC+ULVdUzPTG9y70OBSBQDQn9hQP8u/AfhuYw1R0BSg15nBI4Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.96.0", + "@rolldown/pluginutils": "1.0.0-beta.47" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-beta.47", + "@rolldown/binding-darwin-arm64": "1.0.0-beta.47", + "@rolldown/binding-darwin-x64": "1.0.0-beta.47", + "@rolldown/binding-freebsd-x64": "1.0.0-beta.47", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.47", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.47", + "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.47", + "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.47", + "@rolldown/binding-linux-x64-musl": "1.0.0-beta.47", + "@rolldown/binding-openharmony-arm64": "1.0.0-beta.47", + "@rolldown/binding-wasm32-wasi": "1.0.0-beta.47", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.47", + "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.47", + "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.47" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/vite": { + "name": "rolldown-vite", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/rolldown-vite/-/rolldown-vite-7.2.2.tgz", + "integrity": "sha512-Fl3ZdmJhDMJGcqrr342pPVrhugXdOcuNBRBauz4S7QGSRXbQy7y8q5QYJtgkcrG8XjY0EENSZeTk58c3m20FxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/runtime": "0.96.0", + "fdir": "^6.5.0", + "lightningcss": "^1.30.2", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rolldown": "1.0.0-beta.47", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "esbuild": "^0.25.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6e96392 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "matchup", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "npm:rolldown-vite@7.2.2" + }, + "overrides": { + "vite": "npm:rolldown-vite@7.2.2" + }, + "dependencies": { + "@fortawesome/fontawesome-free": "^7.1.0", + "bulma": "^1.0.4", + "notyf": "^3.10.0" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..6cb5040 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/icon.png b/public/icon.png new file mode 100644 index 0000000..655915f Binary files /dev/null and b/public/icon.png differ diff --git a/src/court.mjs b/src/court.mjs new file mode 100644 index 0000000..713c488 --- /dev/null +++ b/src/court.mjs @@ -0,0 +1,53 @@ +import { Manager } from "./utils.mjs"; + +class Court { + constructor(id, status) { + this.id = id; + this.status = typeof status !== "undefined" ? status : true; + this.name = "Court " + (id + 1); + } + toggle() { + this.status = !this.status; + this.print(); + } + print() { + let status = this.status ? "active" : "inactive"; + console.log(this.name + " is currently " + status); + } + render() { + let color = this.status ? "is-success" : "is-danger"; + let type = `class="button is-medium ${color}"`; + return ``; + } +} + +export class CourtManager extends Manager { + maxCourtCount = 12; + + constructor() { + super("court", []); + console.log("Loading court data."); + let data = JSON.parse(localStorage.getItem(this.description)); + if (data === null || data.length != this.maxCourtCount) { + console.log("Court data unavailable: " + data); + this.generate(); + return; + } + for (let i = 0; i < this.maxCourtCount; i++) { + this.listData.push(new Court(data[i].id, data[i].status)); + } + console.log("Loaded " + this.listData.length + " courts."); + this.render(); + this.addBehaviour(); + } + generate() { + console.log("Generating court data."); + for (let i = 0; i < this.maxCourtCount; i++) { + this.listData.push(new Court(i)); + } + console.log("Generated data for " + this.listData.length + " courts"); + this.store(); + this.render(); + this.addBehaviour(); + } +} diff --git a/src/game.mjs b/src/game.mjs new file mode 100644 index 0000000..c8931ac --- /dev/null +++ b/src/game.mjs @@ -0,0 +1,186 @@ +import { Notyf } from 'notyf'; +import { Manager } from "./utils.mjs"; +import { CourtManager } from "./court.mjs"; +import { PlayerManager } from "./player.mjs"; + +class Team { + constructor(playerOne, playerTwo) { + this.playerOne = playerOne; + this.playerTwo = playerTwo; + } + print() { + console.log(this.playerOne + " and " + this.playerTwo); + } + render() { + let p1 = `${this.playerOne}`; + let p2 = `${this.playerTwo}`; + return p1 + p2; + } +} + +class Game { + constructor(id, teamOne, teamTwo, status) { + this.id = id; + this.teamOne = teamOne; + this.teamTwo = teamTwo; + this.status = typeof status !== "undefined" ? status : false; + } + print() { + let teamOne = this.teamOne.playerOne + " and " + this.teamOne.playerTwo; + let teamTwo = this.teamTwo.playerOne + " and " + this.teamTwo.playerTwo; + console.log(teamOne + " is playing against " + teamTwo); + } + render() { + let blockedState = this.teamOne.playerOne === "RESERVED"; + let type = blockedState ? `class="is-danger is-dark"` : ``; + return `${ + this.id + 1 + }${this.teamOne.render()}${this.teamTwo.render()}`; + } +} + +function generateDummyGames(courts, description) { + let team = new Team(description, description); + let games = []; + for (const court of courts) { + let game = new Game(court.id, team, team); + games.push(game); + } + return games; +} + +export class GameManager extends Manager { + courts = new CourtManager(); + players = new PlayerManager(); + + constructor() { + super("game", []); + this.round = Number(localStorage.getItem("round")) || 0; + console.log("Loading game data."); + let data = JSON.parse(localStorage.getItem(this.description)); + if (data === null || data.length === 0) { + console.log("Game data unavailable: " + data); + this.generate(); + return; + } + for (let i = 0; i < data.length; i++) { + let id = Number(data[i].id); + let teamOne = new Team( + data[i].teamOne.playerOne, + data[i].teamOne.playerTwo + ); + let teamTwo = new Team( + data[i].teamTwo.playerOne, + data[i].teamTwo.playerTwo + ); + let status = Boolean(data[i].status); + this.listData.push(new Game(id, teamOne, teamTwo, status)); + } + console.log("Loaded " + this.listData.length + " games."); + this.render(); + } + isConfirmed() { + return this.listData[0].status; + } + generate() { + console.log("Generating the dummy round."); + this.listData = generateDummyGames(this.courts.listData, "---"); + console.log("Loaded " + this.listData.length + " dummy round."); + for (let i = 0; i < this.listData.length; i++) { + this.listData[i].status = true; + } + console.log(this.listData); + this.store(); + this.render(); + } + addBehaviour() { + let description = this.isConfirmed() ? "Round " + this.round : "Proposal"; + document.getElementById("game_description").innerHTML = description; + } + getBlockedGames() { + const inactiveCourts = this.courts.inactive(); + console.log("Total reserved courts are " + inactiveCourts.length + "."); + return generateDummyGames(inactiveCourts, "RESERVED"); + } + inactive(courts) { + return generateDummyGames(courts, "---"); + } + active(courts, players) { + let games = []; + for (let index = 0; index < courts.length; index++) { + const startIndex = 4 * index; + const endIndex = startIndex + 4; + const courtPlayers = players.slice(startIndex, endIndex); + const team_1 = new Team(courtPlayers[0].name, courtPlayers[3].name); + const team_2 = new Team(courtPlayers[1].name, courtPlayers[2].name); + const game = new Game(courts[index].id, team_1, team_2); + game.print(); + games.push(game); + } + return games; + } + propose() { + console.log("Proposing new games!"); + let possibleMatches = getMatchCount( + this.players.listData, + this.courts.listData + ); + console.log("Total possible matches are " + possibleMatches + "."); + this.listData = this.getBlockedGames(); + const activeCourts = this.courts.active(); + const unusedCourts = activeCourts.slice(possibleMatches + 1); + this.listData = this.listData.concat(this.inactive(unusedCourts)); + const usedCourts = activeCourts.slice(0, possibleMatches); + let requiredPlayerCount = possibleMatches * 4; + const selectedPlayers = this.players.propose(requiredPlayerCount); + console.log("The matches are:"); + this.listData = this.listData.concat( + this.active(usedCourts, selectedPlayers) + ); + this.listData.sort((a, b) => a.id - b.id); + this.render(); + } + confirm() { + if (this.isConfirmed()) { + const notification = new Notyf(); + notification.error("Create a proposal first!"); + return; + } + console.log("Confirm the proposal for round " + this.round + "."); + this.round += 1; + localStorage.setItem("round", this.round); + for (let i = 0; i < this.listData.length; i++) { + this.listData[i].status = true; + } + // Need to update the number of games played here + this.store(); + this.render(); + document.getElementById("timer-start").click(); + const notification = new Notyf(); + notification.success("Round confirmed!"); + } + reset() { + localStorage.removeItem("round"); + localStorage.removeItem(this.description); + localStorage.removeItem(this.courts.description); + document.getElementById("timer-stop").click(); + window.location.reload(); + const notification = new Notyf(); + notification.error("Session has been reset!"); + } +} + +function getMatchCount(players, courts) { + courts = courts.filter(function (c) { + return c.status; + }); + console.log("There are a total of " + courts.length + " courts available."); + console.log(players); + players = players.filter(function (p) { + return p.status; + }); + console.log("There are a total of " + players.length + " players available."); + var possibleMatches = Math.floor(players.length / 4); + possibleMatches = Math.min(possibleMatches, courts.length); + return possibleMatches; +} diff --git a/src/guest.mjs b/src/guest.mjs new file mode 100644 index 0000000..ba9e8cc --- /dev/null +++ b/src/guest.mjs @@ -0,0 +1,69 @@ +import { Manager } from "./utils.mjs"; + +export class Guest { + constructor(id, name, level, status, totalPlayed) { + this.id = id; + this.status = typeof status !== "undefined" ? status : false; + this.name = name; + this.level = typeof level !== "undefined" ? level : 1; + this.totalPlayed = typeof totalPlayed !== "undefined" ? status : 0; + } + toggle() { + this.status = !this.status; + this.print(); + } + print() { + let status = this.status ? "active" : "inactive"; + console.log(this.name + " is currently " + status); + } + render() { + let color = this.status ? "is-success" : "is-danger"; + let type = `class="button is-medium ${color}"`; + return ``; + } +} + +export class GuestManager extends Manager { + constructor() { + super("guest", []); + console.log("Loading guest data."); + let data = JSON.parse(localStorage.getItem(this.description)); + if (data === null || data.length === 0) { + console.log("Guest data unavailable: " + data); + this.generate(); + return; + } + for (let i = 0; i < data.length; i++) { + let id = Number(data[i].id); + let name = data[i].name; + let level = data[i].level; + let status = data[i].status; + let totalPlayed = data[i].totalPlayed; + this.listData.push(new Player(id, name, level, status, totalPlayed)); + } + console.log("Loaded " + this.listData.length + " players."); + this.render(); + } + generate() { + console.log("Generating guest data."); + let maxPlayerCount = 12; + for (let i = 0; i < maxPlayerCount; i++) { + let level = i < 3 ? 1 : ( i < 6 ? 3 : 7); + let p = new Player(i, "Player " + i, level, false, 0); + this.listData.push(p); + } + console.log("Generated data for " + this.listData.length + " guest"); + this.store(); + this.render(); + } + active(factor) { + factor = typeof factor !== "undefined" ? factor : 1; + let data = super.active(); + console.log("Normalize skill by a factor of " + factor + "."); + let players = data.map(function (p) { + p.skill = Math.ceil(p.skill / factor); + return p; + }); + return players; + } +} \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..91ac386 --- /dev/null +++ b/src/main.js @@ -0,0 +1,20 @@ +import 'notyf/notyf.min.css' +import 'bulma/css/bulma.css' +import '@fortawesome/fontawesome-free/css/all.css' +import './style.css' + +import { GameManager } from "./game.mjs"; + +// Court and player handling +let g = new GameManager(); + +// Game handling +document.getElementById("propose").addEventListener("click", () => { + g.propose(); +}); +document.getElementById("confirm").addEventListener("click", () => { + g.confirm(); +}); +document.getElementById("reset").addEventListener("click", () => { + g.reset(); +}); diff --git a/src/player.mjs b/src/player.mjs new file mode 100644 index 0000000..adf874d --- /dev/null +++ b/src/player.mjs @@ -0,0 +1,150 @@ +import { GuestManager } from "./guest.mjs"; +import { shuffle, Manager } from "./utils.mjs"; + +class Player { + constructor(id, name, level, status, totalPlayed) { + this.id = id; + this.status = typeof status !== "undefined" ? status : false; + this.name = name; + this.level = typeof level !== "undefined" ? level : 1; + this.totalPlayed = typeof totalPlayed !== "undefined" ? status : 0; + } + toggle() { + this.status = !this.status; + this.print(); + } + print() { + let status = this.status ? "active" : "inactive"; + console.log(this.name + " is currently " + status); + } + render() { + let color = this.status ? "is-success" : "is-danger"; + let type = `class="button is-medium ${color}"`; + return ``; + } +} + +function randomLevel() { + const max_level = 10; + return Math.floor(Math.random() * max_level + 1); +} + +function randomStatus() { + return Math.random() < 0.5; +} + +function parseLineToPlayer(data) { + var info = data.split(","); + var id = Number(info[0].trim()); + var name = info[1].trim() + " " + info[2].trim(); + var level = Number(info[3].trim()); + return new Player(id, name, level, true, 0); +} + +export class PlayerManager extends Manager { + // g = new GuestManager(); + + constructor() { + super("regular", []); + console.log("Loading player data."); + let data = JSON.parse(localStorage.getItem(this.description)); + if (data === null || data.length === 0) { + console.log("Player data unavailable: " + data); + this.generate(); + return; + } + for (let i = 0; i < data.length; i++) { + let id = Number(data[i].id); + let name = data[i].name; + let level = data[i].level; + let status = data[i].status; + let totalPlayed = data[i].totalPlayed; + this.listData.push(new Player(id, name, level, status, totalPlayed)); + } + console.log("Loaded " + this.listData.length + " players."); + this.render(); + this.addBehaviour(); + addLoadBehaviour(this); + } + generate() { + console.log("Generating player data."); + let maxPlayerCount = 71; + for (let i = 0; i < maxPlayerCount; i++) { + let p = new Player(i, "Player " + i, randomLevel(), randomStatus(), 0); + this.listData.push(p); + } + console.log("Generated data for " + this.listData.length + " players"); + this.store(); + this.render(); + } + import(data) { + var lines = data.split("\n"); + var players = []; + for (var i = 1; i < lines.length; i++) { + if (lines[i] == undefined || lines[i].trim() == "") { + continue; + } + players.push(parseLineToPlayer(lines[i])); + } + console.log("Loaded " + players.length + " Players"); + for (var i = 0; i < players.length; i++) { + players[i].print(); + } + this.listData = players; + this.store(); + this.render(); + } + active(factor) { + factor = typeof factor !== "undefined" ? factor : 1; + let data = super.active(); + console.log("Normalize skill by a factor of " + factor + "."); + let players = data.map(function (p) { + p.skill = Math.ceil(p.skill / factor); + return p; + }); + return players; + } + propose(count) { + console.log("Required players are " + count); + let factor = Number(document.getElementById("skill").value); + console.log("Skill normalized by a factor of " + factor + "."); + const activePlayers = this.active(factor); + console.log("Active players are:"); + console.log( + activePlayers + .map((i) => { + return i.name; + }) + .join(",") + ); + const shuffledPlayers = shuffle(activePlayers); + shuffledPlayers.sort((a, b) => a.totalPlayed - b.totalPlayed); + const selectedPlayers = shuffledPlayers.slice(0, count); + selectedPlayers.sort((a, b) => a.level - b.level); + console.log("Selected players are:"); + console.log( + selectedPlayers + .map((i) => { + return i.name; + }) + .join(",") + ); + return selectedPlayers; + } +} + +// TODO Add load behaviour to constructor +export function addLoadBehaviour(p) { + const f = document.querySelector("#player-load input[type=file]"); + f.addEventListener("change", () => { + const file = f.files[0]; + let importData; + console.log("Loading data from " + file.name); + const reader = new FileReader(); + reader.onload = function (e) { + importData = reader.result; + p.import(importData); + }; + reader.readAsText(file); + }); +} diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..8e3b713 --- /dev/null +++ b/src/style.css @@ -0,0 +1,12 @@ +.button-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-gap: 15px; +} +#timer-display { + text-align: center; + width: fit-content; +} +table { + font-size: 1.5em; +} \ No newline at end of file diff --git a/src/timer.js b/src/timer.js new file mode 100644 index 0000000..09189c0 --- /dev/null +++ b/src/timer.js @@ -0,0 +1,88 @@ +let minsPerRound = Number(sessionStorage.getItem("minsPerRound")) || 12; +let timerRunningFlag = Number(sessionStorage.getItem("timerRunningFlag")) || 0; +let deadline = + Number(sessionStorage.getItem("deadline")) || new Date().getTime(); +let clock = 0; + +function setTimer(m, s) { + let timer = document.getElementById("timer-display"); + m = m < 10 ? "0" + m : m; + s = s < 10 ? "0" + s : s; + timer.value = `${m}:${s}`; +} + +function setSessionData() { + sessionStorage.setItem("deadline", deadline); + sessionStorage.setItem("minsPerRound", minsPerRound); + sessionStorage.setItem("timerRunningFlag", timerRunningFlag); +} + +function resetTimer() { + clearInterval(clock); + let mins = Math.floor(minsPerRound); + let secs = (minsPerRound - mins) * 60; + setTimer(mins, secs); + timerRunningFlag = 0; + setSessionData(); +} + +function updateTimer(milliseconds) { + const m = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60)); + const s = Math.floor((milliseconds % (1000 * 60)) / 1000); + setTimer(m, s); +} + +function refreshTimer() { + const now = new Date().getTime(); + const millisecondsToDeadline = deadline - now; + if (millisecondsToDeadline > 0) { + updateTimer(millisecondsToDeadline); + return; + } + resetTimer(); + const notification = new Notyf({ + duration: 0, + position: { x: "center", y: "top" }, + dismissible: true, + }); + notification.error("Round completed"); +} + +function startTimer(fresh) { + if (fresh) { + let now = new Date().getTime(); + const millisecondsToDeadline = minsPerRound * 60 * 1000; + deadline = now + millisecondsToDeadline; + } + timerRunningFlag = 1; + setSessionData(); + clock = setInterval(refreshTimer, 1000); +} + +window.onload = function () { + if (timerRunningFlag) startTimer(0); + else resetTimer(); + + document.getElementById("timer-start").addEventListener("click", () => { + if (timerRunningFlag != 0) return; + startTimer(1); + }); + + document.getElementById("timer-stop").addEventListener("click", () => { + resetTimer(); + }); + + document.getElementById("timer-inc").addEventListener("click", () => { + if (timerRunningFlag == 0) { + minsPerRound += 0.5; + resetTimer(); + } + }); + + document.getElementById("timer-dec").addEventListener("click", () => { + if (timerRunningFlag == 0 && minsPerRound != 0) { + minsPerRound -= 0.5; + resetTimer(); + } + }); +}; diff --git a/src/utils.mjs b/src/utils.mjs new file mode 100644 index 0000000..06a2519 --- /dev/null +++ b/src/utils.mjs @@ -0,0 +1,51 @@ +export class Manager { + constructor(description, listData) { + this.description = description; + this.listData = listData; + this.count = listData.length; + } + store() { + let stringData = JSON.stringify(this.listData); + localStorage.setItem(this.description, stringData); + } + render() { + let renderedHTML = this.listData + .map((i) => { + return i.render(); + }) + .join(""); + let elementId = this.description + "_list"; + document.getElementById(elementId).innerHTML = renderedHTML; + this.addBehaviour(); + } + addBehaviour() { + for (let idx = 0; idx < this.listData.length; ++idx) { + const elementId = this.description + "_" + this.listData[idx].id; + const p = document.getElementById(elementId); + p.addEventListener("click", () => { + this.listData[idx].toggle(); + this.store(); + p.classList.toggle("is-success"); + p.classList.toggle("is-danger"); + }); + } + } + active() { + return this.listData.filter(function (c) { + return c.status; + }); + } + inactive() { + return this.listData.filter(function (c) { + return !c.status; + }); + } +} + +export function shuffle(array) { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; +} -- cgit v1.3.1 From 98d5844b21b3e348d8b27f9ce7e7c6859e1a27c7 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Thu, 13 Nov 2025 21:58:12 +0100 Subject: Standardized the icon use --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'index.html') diff --git a/index.html b/index.html index 287841a..b2501a5 100644 --- a/index.html +++ b/index.html @@ -74,7 +74,7 @@ - + Update players... @@ -85,7 +85,7 @@
    - +