diff --git a/client/src/config/globals.js b/client/src/config/globals.js
index fba64bc..ebca36d 100644
--- a/client/src/config/globals.js
+++ b/client/src/config/globals.js
@@ -31,17 +31,6 @@ export const globals = {
MESSAGES: {
ENTER_NAME: 'Client must enter name.'
},
- EVENTS: {
- PLAYER_JOINED: 'playerJoined',
- SYNC_GAME_STATE: 'syncGameState',
- START_TIMER: 'startTimer',
- KILL_PLAYER: 'killPlayer',
- REVEAL_PLAYER: 'revealPlayer',
- CHANGE_NAME: 'changeName',
- START_GAME: 'startGame',
- PLAYER_LEFT: 'playerLeft',
- NEW_SPECTATOR: 'newSpectator'
- },
SOCKET_EVENTS: {
IN_GAME_MESSAGE: 'inGameMessage'
},
@@ -55,7 +44,12 @@ export const globals = {
REVEAL_PLAYER: 'revealPlayer',
TRANSFER_MODERATOR: 'transferModerator',
CHANGE_NAME: 'changeName',
- END_GAME: 'endGame'
+ END_GAME: 'endGame',
+ PLAYER_JOINED: 'playerJoined',
+ SYNC_GAME_STATE: 'syncGameState',
+ START_TIMER: 'startTimer',
+ PLAYER_LEFT: 'playerLeft',
+ NEW_SPECTATOR: 'newSpectator'
},
USER_TYPES: {
MODERATOR: 'moderator',
diff --git a/client/src/modules/HTMLFragments.js b/client/src/modules/front_end_components/HTMLFragments.js
similarity index 99%
rename from client/src/modules/HTMLFragments.js
rename to client/src/modules/front_end_components/HTMLFragments.js
index bc105d2..e8bba82 100644
--- a/client/src/modules/HTMLFragments.js
+++ b/client/src/modules/front_end_components/HTMLFragments.js
@@ -156,7 +156,7 @@ export const HTMLFragments = {
diff --git a/client/src/modules/ModalManager.js b/client/src/modules/front_end_components/ModalManager.js
similarity index 100%
rename from client/src/modules/ModalManager.js
rename to client/src/modules/front_end_components/ModalManager.js
diff --git a/client/src/modules/Navbar.js b/client/src/modules/front_end_components/Navbar.js
similarity index 97%
rename from client/src/modules/Navbar.js
rename to client/src/modules/front_end_components/Navbar.js
index 1cd04f4..731cc18 100644
--- a/client/src/modules/Navbar.js
+++ b/client/src/modules/front_end_components/Navbar.js
@@ -38,7 +38,7 @@ function flipHamburger () {
function getNavbarLinks (page = null, device) {
const linkClass = device === 'mobile' ? 'mobile-link' : 'desktop-link';
return '
' +
- '
' +
+ '
' +
'' +
'
Home' +
'
Create' +
diff --git a/client/src/modules/Toast.js b/client/src/modules/front_end_components/Toast.js
similarity index 100%
rename from client/src/modules/Toast.js
rename to client/src/modules/front_end_components/Toast.js
diff --git a/client/src/modules/DeckStateManager.js b/client/src/modules/game_creation/DeckStateManager.js
similarity index 97%
rename from client/src/modules/DeckStateManager.js
rename to client/src/modules/game_creation/DeckStateManager.js
index 5eed73b..da2a9d9 100644
--- a/client/src/modules/DeckStateManager.js
+++ b/client/src/modules/game_creation/DeckStateManager.js
@@ -1,7 +1,7 @@
-import { globals } from '../config/globals.js';
-import { HTMLFragments } from './HTMLFragments.js';
-import { toast } from './Toast.js';
-import { ModalManager } from './ModalManager.js';
+import { globals } from '../../config/globals.js';
+import { HTMLFragments } from '../front_end_components/HTMLFragments.js';
+import { toast } from '../front_end_components/Toast.js';
+import { ModalManager } from '../front_end_components/ModalManager.js';
export class DeckStateManager {
constructor () {
diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/game_creation/GameCreationStepManager.js
similarity index 97%
rename from client/src/modules/GameCreationStepManager.js
rename to client/src/modules/game_creation/GameCreationStepManager.js
index f40e39d..8aa88dc 100644
--- a/client/src/modules/GameCreationStepManager.js
+++ b/client/src/modules/game_creation/GameCreationStepManager.js
@@ -1,10 +1,10 @@
-import { Game } from '../model/Game.js';
-import { cancelCurrentToast, toast } from './Toast.js';
-import { ModalManager } from './ModalManager.js';
-import { XHRUtility } from './XHRUtility.js';
-import { globals } from '../config/globals.js';
-import { HTMLFragments } from './HTMLFragments.js';
-import { UserUtility } from './UserUtility.js';
+import { Game } from '../../model/Game.js';
+import { cancelCurrentToast, toast } from '../front_end_components/Toast.js';
+import { ModalManager } from '../front_end_components/ModalManager.js';
+import { XHRUtility } from '../utility/XHRUtility.js';
+import { globals } from '../../config/globals.js';
+import { HTMLFragments } from '../front_end_components/HTMLFragments.js';
+import { UserUtility } from '../utility/UserUtility.js';
import { RoleBox } from './RoleBox.js';
export class GameCreationStepManager {
@@ -457,7 +457,7 @@ function showButtons (back, forward, forwardHandler, backHandler, builtGame = nu
document.querySelector('#create-game')?.remove();
if (back) {
const backButton = document.createElement('button');
- backButton.innerHTML = '

';
+ backButton.innerHTML = '

';
backButton.addEventListener('click', backHandler);
backButton.setAttribute('id', 'step-back-button');
backButton.classList.add('cancel');
@@ -467,7 +467,7 @@ function showButtons (back, forward, forwardHandler, backHandler, builtGame = nu
if (forward && builtGame === null) {
const fwdButton = document.createElement('button');
- fwdButton.innerHTML = '

';
+ fwdButton.innerHTML = '

';
fwdButton.addEventListener('click', forwardHandler);
fwdButton.setAttribute('id', 'step-forward-button');
fwdButton.classList.add('app-button');
diff --git a/client/src/modules/RoleBox.js b/client/src/modules/game_creation/RoleBox.js
similarity index 97%
rename from client/src/modules/RoleBox.js
rename to client/src/modules/game_creation/RoleBox.js
index d5bbe5a..b88e8af 100644
--- a/client/src/modules/RoleBox.js
+++ b/client/src/modules/game_creation/RoleBox.js
@@ -1,8 +1,8 @@
-import { HTMLFragments } from './HTMLFragments.js';
-import { globals } from '../config/globals.js';
-import { defaultRoles } from '../config/defaultRoles.js';
-import { toast } from './Toast.js';
-import { ModalManager } from './ModalManager.js';
+import { HTMLFragments } from '../front_end_components/HTMLFragments.js';
+import { globals } from '../../config/globals.js';
+import { defaultRoles } from '../../config/defaultRoles.js';
+import { toast } from '../front_end_components/Toast.js';
+import { ModalManager } from '../front_end_components/ModalManager.js';
export class RoleBox {
constructor (container, deckManager) {
diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/game_state/GameStateRenderer.js
similarity index 98%
rename from client/src/modules/GameStateRenderer.js
rename to client/src/modules/game_state/GameStateRenderer.js
index 50e776a..49d28d6 100644
--- a/client/src/modules/GameStateRenderer.js
+++ b/client/src/modules/game_state/GameStateRenderer.js
@@ -1,11 +1,11 @@
-import { globals } from '../config/globals.js';
-import { toast } from './Toast.js';
-import { HTMLFragments } from './HTMLFragments.js';
-import { ModalManager } from './ModalManager.js';
-import { XHRUtility } from './XHRUtility.js';
-import { UserUtility } from './UserUtility.js';
+import { globals } from '../../config/globals.js';
+import { toast } from '../front_end_components/Toast.js';
+import { HTMLFragments } from '../front_end_components/HTMLFragments.js';
+import { ModalManager } from '../front_end_components/ModalManager.js';
+import { XHRUtility } from '../utility/XHRUtility.js';
+import { UserUtility } from '../utility/UserUtility.js';
// QRCode module via: https://github.com/soldair/node-qrcode
-import { QRCode } from './third_party/qrcode.js';
+import { QRCode } from '../third_party/qrcode.js';
export class GameStateRenderer {
constructor (stateBucket, socket) {
diff --git a/client/src/modules/StateBucket.js b/client/src/modules/game_state/StateBucket.js
similarity index 100%
rename from client/src/modules/StateBucket.js
rename to client/src/modules/game_state/StateBucket.js
diff --git a/client/src/modules/page_handlers/createHandler.js b/client/src/modules/page_handlers/createHandler.js
new file mode 100644
index 0000000..2447c9e
--- /dev/null
+++ b/client/src/modules/page_handlers/createHandler.js
@@ -0,0 +1,8 @@
+import { injectNavbar } from '../front_end_components/Navbar.js';
+import createTemplate from '../../view_templates/CreateTemplate.js';
+
+export const createHandler = (gameCreationStepManager) => {
+ injectNavbar();
+ document.getElementById('game-creation-container').innerHTML = createTemplate;
+ gameCreationStepManager.renderStep('creation-step-container', 1);
+};
diff --git a/client/src/modules/page_handlers/gameHandler.js b/client/src/modules/page_handlers/gameHandler.js
new file mode 100644
index 0000000..69c71b9
--- /dev/null
+++ b/client/src/modules/page_handlers/gameHandler.js
@@ -0,0 +1,440 @@
+import { injectNavbar } from '../front_end_components/Navbar.js';
+import { stateBucket } from '../game_state/StateBucket.js';
+import { GameTimerManager } from '../timer/GameTimerManager.js';
+import { GameStateRenderer } from '../game_state/GameStateRenderer.js';
+import { UserUtility } from '../utility/UserUtility.js';
+import { toast } from '../front_end_components/Toast.js';
+import { globals } from '../../config/globals.js';
+import { HTMLFragments } from '../front_end_components/HTMLFragments.js';
+import { ModalManager } from '../front_end_components/ModalManager.js';
+
+export const gameHandler = async (socket, XHRUtility, window, gameDOM) => {
+ document.body.innerHTML = gameDOM + document.body.innerHTML;
+ injectNavbar();
+
+ const response = await XHRUtility.xhr(
+ '/api/games/environment',
+ 'GET',
+ null,
+ null
+ ).catch((res) => {
+ toast(res.content, 'error', true);
+ });
+
+ stateBucket.environment = response.content;
+ const timerWorker = new Worker(new URL('../timer/Timer.js', import.meta.url));
+ const gameTimerManager = new GameTimerManager(stateBucket, socket);
+ const gameStateRenderer = new GameStateRenderer(stateBucket, socket);
+
+ socket.on('connect', function () {
+ syncWithGame(
+ stateBucket,
+ gameTimerManager,
+ gameStateRenderer,
+ timerWorker,
+ socket,
+ UserUtility.validateAnonUserSignature(response.content),
+ window
+ );
+ });
+
+ socket.on('connect_error', (err) => {
+ toast(err, 'error', true, false);
+ });
+
+ socket.on('disconnect', () => {
+ toast('Disconnected. Attempting reconnect...', 'error', true, false);
+ });
+
+ setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager);
+};
+
+function syncWithGame (stateBucket, gameTimerManager, gameStateRenderer, timerWorker, socket, cookie, window) {
+ const splitUrl = window.location.href.split('/game/');
+ const accessCode = splitUrl[1];
+ if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) {
+ socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.FETCH_GAME_STATE, accessCode, { personId: cookie }, function (gameState) {
+ if (gameState === null) {
+ window.location = '/not-found?reason=' + encodeURIComponent('game-not-found');
+ } else {
+ stateBucket.currentGameState = gameState;
+ document.querySelector('.spinner-container')?.remove();
+ document.querySelector('.spinner-background')?.remove();
+ document.getElementById('game-content').innerHTML = HTMLFragments.INITIAL_GAME_DOM;
+ toast('You are connected.', 'success', true, true, 'short');
+ processGameState(stateBucket.currentGameState, cookie, socket, gameStateRenderer, gameTimerManager, timerWorker, true, true);
+ }
+ });
+ } else {
+ window.location = '/not-found?reason=' + encodeURIComponent('invalid-access-code');
+ }
+}
+
+function processGameState (
+ currentGameState,
+ userId,
+ socket,
+ gameStateRenderer,
+ gameTimerManager,
+ timerWorker,
+ refreshPrompt = true,
+ animateContainer = false
+) {
+ const containerAnimation = document.getElementById('game-state-container').animate(
+ [
+ { opacity: '0', transform: 'translateY(10px)' },
+ { opacity: '1', transform: 'translateY(0px)' }
+ ], {
+ duration: 500,
+ easing: 'ease-in-out',
+ fill: 'both'
+ });
+ if (animateContainer) {
+ containerAnimation.play();
+ }
+ displayClientInfo(currentGameState.client.name, currentGameState.client.userType);
+ if (refreshPrompt) {
+ removeStartGameFunctionalityIfPresent(gameStateRenderer);
+ document.querySelector('#end-game-prompt')?.remove();
+ }
+ switch (currentGameState.status) {
+ case globals.STATUS.LOBBY:
+ document.getElementById('game-state-container').innerHTML = HTMLFragments.LOBBY;
+ gameStateRenderer.renderLobbyHeader();
+ gameStateRenderer.renderLobbyPlayers();
+ if ((
+ currentGameState.client.userType === globals.USER_TYPES.MODERATOR
+ || currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
+ )
+ && refreshPrompt
+ ) {
+ displayStartGamePromptForModerators(currentGameState, gameStateRenderer);
+ }
+ break;
+ case globals.STATUS.IN_PROGRESS:
+ gameStateRenderer.renderGameHeader();
+ switch (currentGameState.client.userType) {
+ case globals.USER_TYPES.PLAYER:
+ document.getElementById('game-state-container').innerHTML = HTMLFragments.PLAYER_GAME_VIEW;
+ gameStateRenderer.renderPlayerView();
+ break;
+ case globals.USER_TYPES.KILLED_PLAYER:
+
+ document.getElementById('game-state-container').innerHTML = HTMLFragments.PLAYER_GAME_VIEW;
+ gameStateRenderer.renderPlayerView(true);
+ break;
+ case globals.USER_TYPES.MODERATOR:
+ document.getElementById('transfer-mod-prompt').innerHTML = HTMLFragments.TRANSFER_MOD_MODAL;
+ document.getElementById('game-state-container').innerHTML = HTMLFragments.MODERATOR_GAME_VIEW;
+ gameStateRenderer.renderModeratorView();
+ break;
+ case globals.USER_TYPES.TEMPORARY_MODERATOR:
+ document.getElementById('transfer-mod-prompt').innerHTML = HTMLFragments.TRANSFER_MOD_MODAL;
+ document.getElementById('game-state-container').innerHTML = HTMLFragments.TEMP_MOD_GAME_VIEW;
+ gameStateRenderer.renderTempModView();
+ break;
+ case globals.USER_TYPES.SPECTATOR:
+ document.getElementById('game-state-container').innerHTML = HTMLFragments.SPECTATOR_GAME_VIEW;
+ gameStateRenderer.renderSpectatorView();
+ break;
+ default:
+ break;
+ }
+ if (currentGameState.timerParams) {
+ socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.GET_TIME_REMAINING, currentGameState.accessCode);
+ } else {
+ document.querySelector('#game-timer')?.remove();
+ document.querySelector('#timer-container-moderator')?.remove();
+ document.querySelector('label[for="game-timer"]')?.remove();
+ }
+ break;
+ case globals.STATUS.ENDED: {
+ const container = document.getElementById('game-state-container');
+ container.innerHTML = HTMLFragments.END_OF_GAME_VIEW;
+ gameStateRenderer.renderEndOfGame(currentGameState);
+ break;
+ }
+ default:
+ break;
+ }
+
+ activateRoleInfoButton(stateBucket.currentGameState.deck);
+}
+
+function activateRoleInfoButton (deck) {
+ deck.sort((a, b) => {
+ return a.team === globals.ALIGNMENT.GOOD ? -1 : 1;
+ });
+ document.getElementById('role-info-button').addEventListener('click', (e) => {
+ e.preventDefault();
+ document.getElementById('role-info-prompt').innerHTML = HTMLFragments.ROLE_INFO_MODAL;
+ const modalContent = document.getElementById('game-role-info-container');
+ for (const card of deck) {
+ const roleDiv = document.createElement('div');
+ const roleNameDiv = document.createElement('div');
+
+ roleNameDiv.classList.add('role-info-name');
+
+ const roleName = document.createElement('h5');
+ const roleQuantity = document.createElement('h5');
+ const roleDescription = document.createElement('p');
+
+ roleDescription.innerText = card.description;
+ roleName.innerText = card.role;
+ roleQuantity.innerText = card.quantity + 'x';
+
+ if (card.team === globals.ALIGNMENT.GOOD) {
+ roleName.classList.add(globals.ALIGNMENT.GOOD);
+ } else {
+ roleName.classList.add(globals.ALIGNMENT.EVIL);
+ }
+
+ roleNameDiv.appendChild(roleQuantity);
+ roleNameDiv.appendChild(roleName);
+
+ roleDiv.appendChild(roleNameDiv);
+ roleDiv.appendChild(roleDescription);
+
+ modalContent.appendChild(roleDiv);
+ }
+ ModalManager.displayModal('role-info-modal', 'role-info-modal-background', 'close-role-info-modal-button');
+ });
+}
+
+function displayClientInfo (name, userType) {
+ document.getElementById('client-name').innerText = name;
+ document.getElementById('client-user-type').innerText = userType;
+ document.getElementById('client-user-type').innerText += globals.USER_TYPE_ICONS[userType];
+}
+
+function setClientSocketHandlers (stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager) {
+ socket.on(globals.EVENT_IDS.PLAYER_JOINED, (player, gameIsFull) => {
+ toast(player.name + ' joined!', 'success', false, true, 'short');
+ stateBucket.currentGameState.people.push(player);
+ stateBucket.currentGameState.isFull = gameIsFull;
+ gameStateRenderer.renderLobbyPlayers();
+ if ((
+ stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR
+ || stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
+ )
+ ) {
+ displayStartGamePromptForModerators(stateBucket.currentGameState, gameStateRenderer);
+ }
+ });
+
+ socket.on(globals.EVENT_IDS.NEW_SPECTATOR, (spectator) => {
+ stateBucket.currentGameState.spectators.push(spectator);
+ });
+
+ socket.on(globals.EVENT_IDS.PLAYER_LEFT, (player) => {
+ removeStartGameFunctionalityIfPresent(gameStateRenderer);
+ toast(player.name + ' has left!', 'error', false, true, 'short');
+ const index = stateBucket.currentGameState.people.findIndex(person => person.id === player.id);
+ if (index >= 0) {
+ stateBucket.currentGameState.people.splice(
+ index,
+ 1
+ );
+ gameStateRenderer.renderLobbyPlayers();
+ }
+ });
+
+ socket.on(globals.EVENT_IDS.START_GAME, () => {
+ socket.emit(
+ globals.SOCKET_EVENTS.IN_GAME_MESSAGE,
+ globals.EVENT_IDS.FETCH_GAME_STATE,
+ stateBucket.currentGameState.accessCode,
+ { personId: stateBucket.currentGameState.client.cookie },
+ function (gameState) {
+ stateBucket.currentGameState = gameState;
+ processGameState(
+ stateBucket.currentGameState,
+ gameState.client.cookie,
+ socket,
+ gameStateRenderer,
+ gameTimerManager,
+ timerWorker,
+ true,
+ true
+ );
+ }
+ );
+ });
+
+ socket.on(globals.EVENT_IDS.SYNC_GAME_STATE, () => {
+ socket.emit(
+ globals.SOCKET_EVENTS.IN_GAME_MESSAGE,
+ globals.EVENT_IDS.FETCH_GAME_STATE,
+ stateBucket.currentGameState.accessCode,
+ { personId: stateBucket.currentGameState.client.cookie },
+ function (gameState) {
+ stateBucket.currentGameState = gameState;
+ processGameState(
+ stateBucket.currentGameState,
+ gameState.client.cookie,
+ socket,
+ gameStateRenderer,
+ gameTimerManager,
+ timerWorker,
+ true,
+ true
+ );
+ }
+ );
+ });
+
+ if (timerWorker && gameTimerManager) {
+ gameTimerManager.attachTimerSocketListeners(socket, timerWorker, gameStateRenderer);
+ }
+
+ socket.on(globals.EVENT_IDS.KILL_PLAYER, (id) => {
+ const killedPerson = stateBucket.currentGameState.people.find((person) => person.id === id);
+ if (killedPerson) {
+ killedPerson.out = true;
+ if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR) {
+ toast(killedPerson.name + ' killed.', 'success', true, true, 'medium');
+ gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(stateBucket.currentGameState.status === globals.STATUS.ENDED);
+ } else {
+ if (killedPerson.id === stateBucket.currentGameState.client.id) {
+ const clientUserType = document.getElementById('client-user-type');
+ if (clientUserType) {
+ clientUserType.innerText = globals.USER_TYPES.KILLED_PLAYER + ' \uD83D\uDC80';
+ }
+ gameStateRenderer.updatePlayerCardToKilledState();
+ toast('You have been killed!', 'warning', true, true, 'medium');
+ } else {
+ toast(killedPerson.name + ' was killed!', 'warning', true, true, 'medium');
+ }
+ if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
+ gameStateRenderer.removePlayerListEventListeners(false);
+ } else {
+ gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false);
+ }
+ }
+ }
+ });
+
+ socket.on(globals.EVENT_IDS.REVEAL_PLAYER, (revealData) => {
+ const revealedPerson = stateBucket.currentGameState.people.find((person) => person.id === revealData.id);
+ if (revealedPerson) {
+ revealedPerson.revealed = true;
+ revealedPerson.gameRole = revealData.gameRole;
+ revealedPerson.alignment = revealData.alignment;
+ if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR) {
+ toast(revealedPerson.name + ' revealed.', 'success', true, true, 'medium');
+ gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(stateBucket.currentGameState.status === globals.STATUS.ENDED);
+ } else {
+ if (revealedPerson.id === stateBucket.currentGameState.client.id) {
+ toast('Your role has been revealed!', 'warning', true, true, 'medium');
+ } else {
+ toast(revealedPerson.name + ' was revealed as a ' + revealedPerson.gameRole + '!', 'warning', true, true, 'medium');
+ }
+ if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
+ gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true);
+ } else {
+ gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false);
+ }
+ }
+ }
+ });
+
+ socket.on(globals.EVENT_IDS.CHANGE_NAME, (personId, name) => {
+ propagateNameChange(stateBucket.currentGameState, name, personId);
+ updateDOMWithNameChange(stateBucket.currentGameState, gameStateRenderer);
+ processGameState(
+ stateBucket.currentGameState,
+ stateBucket.currentGameState.client.cookie,
+ socket,
+ gameStateRenderer,
+ gameTimerManager,
+ timerWorker,
+ false,
+ false
+ );
+ });
+
+ socket.on(globals.COMMANDS.END_GAME, (people) => {
+ stateBucket.currentGameState.people = people;
+ stateBucket.currentGameState.status = globals.STATUS.ENDED;
+ processGameState(
+ stateBucket.currentGameState,
+ stateBucket.currentGameState.client.cookie,
+ socket,
+ gameStateRenderer,
+ gameTimerManager,
+ timerWorker,
+ true,
+ true
+ );
+ });
+}
+
+function displayStartGamePromptForModerators (gameState, gameStateRenderer) {
+ const existingPrompt = document.getElementById('start-game-prompt');
+ if (existingPrompt) {
+ enableOrDisableStartButton(gameState, existingPrompt, gameStateRenderer.startGameHandler);
+ } else {
+ const newPrompt = document.createElement('div');
+ newPrompt.setAttribute('id', 'start-game-prompt');
+ newPrompt.innerHTML = HTMLFragments.START_GAME_PROMPT;
+
+ document.body.appendChild(newPrompt);
+ enableOrDisableStartButton(gameState, newPrompt, gameStateRenderer.startGameHandler);
+ }
+}
+
+function enableOrDisableStartButton (gameState, buttonContainer, handler) {
+ if (gameState.isFull) {
+ buttonContainer.querySelector('#start-game-button').addEventListener('click', handler);
+ buttonContainer.querySelector('#start-game-button').classList.remove('disabled');
+ } else {
+ buttonContainer.querySelector('#start-game-button').removeEventListener('click', handler);
+ buttonContainer.querySelector('#start-game-button').classList.add('disabled');
+ }
+}
+
+function removeStartGameFunctionalityIfPresent (gameStateRenderer) {
+ document.querySelector('#start-game-prompt')?.removeEventListener('click', gameStateRenderer.startGameHandler);
+ document.querySelector('#start-game-prompt')?.remove();
+}
+
+function propagateNameChange (gameState, name, personId) {
+ if (gameState.client.id === personId) {
+ gameState.client.name = name;
+ }
+ const matchingPerson = gameState.people.find((person) => person.id === personId);
+ if (matchingPerson) {
+ matchingPerson.name = name;
+ }
+
+ if (gameState.moderator.id === personId) {
+ gameState.moderator.name = name;
+ }
+
+ const matchingSpectator = gameState.spectators?.find((spectator) => spectator.id === personId);
+ if (matchingSpectator) {
+ matchingSpectator.name = name;
+ }
+}
+
+function updateDOMWithNameChange (gameState, gameStateRenderer) {
+ if (gameState.status === globals.STATUS.IN_PROGRESS) {
+ switch (gameState.client.userType) {
+ case globals.USER_TYPES.PLAYER:
+ case globals.USER_TYPES.KILLED_PLAYER:
+ case globals.USER_TYPES.SPECTATOR:
+ gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false);
+ break;
+ case globals.USER_TYPES.MODERATOR:
+ gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(gameState.status === globals.STATUS.ENDED);
+ break;
+ case globals.USER_TYPES.TEMPORARY_MODERATOR:
+ gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true);
+ break;
+ default:
+ break;
+ }
+ } else {
+ gameStateRenderer.renderLobbyPlayers();
+ }
+}
diff --git a/client/src/modules/GameTimerManager.js b/client/src/modules/timer/GameTimerManager.js
similarity index 99%
rename from client/src/modules/GameTimerManager.js
rename to client/src/modules/timer/GameTimerManager.js
index 4bb2089..71defae 100644
--- a/client/src/modules/GameTimerManager.js
+++ b/client/src/modules/timer/GameTimerManager.js
@@ -1,4 +1,4 @@
-import { globals } from '../config/globals.js';
+import { globals } from '../../config/globals.js';
export class GameTimerManager {
constructor (stateBucket, socket) {
diff --git a/client/src/modules/Timer.js b/client/src/modules/timer/Timer.js
similarity index 100%
rename from client/src/modules/Timer.js
rename to client/src/modules/timer/Timer.js
diff --git a/client/src/modules/UserUtility.js b/client/src/modules/utility/UserUtility.js
similarity index 95%
rename from client/src/modules/UserUtility.js
rename to client/src/modules/utility/UserUtility.js
index b445cd7..d9ae4b0 100644
--- a/client/src/modules/UserUtility.js
+++ b/client/src/modules/utility/UserUtility.js
@@ -1,4 +1,4 @@
-import { globals } from '../config/globals.js';
+import { globals } from '../../config/globals.js';
/*
we will use sessionStorage during local development to aid in testing, vs. localStorage for production.
diff --git a/client/src/modules/XHRUtility.js b/client/src/modules/utility/XHRUtility.js
similarity index 100%
rename from client/src/modules/XHRUtility.js
rename to client/src/modules/utility/XHRUtility.js
diff --git a/client/src/scripts/create.js b/client/src/scripts/create.js
index c60d852..0fae57e 100644
--- a/client/src/scripts/create.js
+++ b/client/src/scripts/create.js
@@ -1,18 +1,5 @@
-import { DeckStateManager } from '../modules/DeckStateManager.js';
-import { GameCreationStepManager } from '../modules/GameCreationStepManager.js';
-import { injectNavbar } from '../modules/Navbar.js';
-import createTemplate from '../view_templates/CreateTemplate.js';
+import { createHandler } from '../modules/page_handlers/createHandler.js';
+import { GameCreationStepManager } from '../modules/game_creation/GameCreationStepManager.js';
+import { DeckStateManager } from '../modules/game_creation/DeckStateManager.js';
-const create = () => {
- injectNavbar();
- document.getElementById('game-creation-container').innerHTML = createTemplate;
- const deckManager = new DeckStateManager();
- const gameCreationStepManager = new GameCreationStepManager(deckManager);
- gameCreationStepManager.renderStep('creation-step-container', 1);
-};
-
-if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
- module.exports = create;
-} else {
- create();
-}
+createHandler(new GameCreationStepManager(new DeckStateManager()));
diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js
index 024fbc1..09c57df 100644
--- a/client/src/scripts/game.js
+++ b/client/src/scripts/game.js
@@ -1,443 +1,8 @@
-import { UserUtility } from '../modules/UserUtility.js';
-import { globals } from '../config/globals.js';
-import { HTMLFragments } from '../modules/HTMLFragments.js';
-import { GameStateRenderer } from '../modules/GameStateRenderer.js';
-import { toast } from '../modules/Toast.js';
-import { GameTimerManager } from '../modules/GameTimerManager.js';
-import { ModalManager } from '../modules/ModalManager.js';
-import { stateBucket } from '../modules/StateBucket.js';
+import 'core-js/stable';
+import 'regenerator-runtime/runtime';
+import { gameHandler } from '../modules/page_handlers/gameHandler';
import { io } from 'socket.io-client';
-import { injectNavbar } from '../modules/Navbar.js';
-import { XHRUtility } from '../modules/XHRUtility.js';
+import { XHRUtility } from '../modules/utility/XHRUtility.js';
+import gameTemplate from '../view_templates/GameTemplate.js';
-const game = () => {
- injectNavbar();
- XHRUtility.xhr(
- '/api/games/environment',
- 'GET',
- null,
- null
- )
- .then((res) => {
- stateBucket.environment = res.content;
- const socket = io('/in-game');
- const timerWorker = new Worker(new URL('../modules/Timer.js', import.meta.url));
- const gameTimerManager = new GameTimerManager(stateBucket, socket);
- const gameStateRenderer = new GameStateRenderer(stateBucket, socket);
- socket.on('connect', () => {
- syncWithGame(
- stateBucket,
- gameTimerManager,
- gameStateRenderer,
- timerWorker,
- socket,
- UserUtility.validateAnonUserSignature(res.content)
- );
- });
- socket.on('connect_error', (err) => {
- toast(err, 'error', true, false);
- });
- socket.on('disconnect', () => {
- toast('Disconnected. Attempting reconnect...', 'error', true, false);
- });
- setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager);
- }).catch((res) => {
- toast(res.content, 'error', true);
- });
-};
-
-function syncWithGame (stateBucket, gameTimerManager, gameStateRenderer, timerWorker, socket, cookie) {
- const splitUrl = window.location.href.split('/game/');
- const accessCode = splitUrl[1];
- if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) {
- socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.FETCH_GAME_STATE, accessCode, { personId: cookie }, function (gameState) {
- if (gameState === null) {
- window.location = '/not-found?reason=' + encodeURIComponent('game-not-found');
- } else {
- stateBucket.currentGameState = gameState;
- document.querySelector('.spinner-container')?.remove();
- document.querySelector('.spinner-background')?.remove();
- document.getElementById('game-content').innerHTML = HTMLFragments.INITIAL_GAME_DOM;
- toast('You are connected.', 'success', true, true, 'short');
- processGameState(stateBucket.currentGameState, cookie, socket, gameStateRenderer, gameTimerManager, timerWorker, true, true);
- }
- });
- } else {
- window.location = '/not-found?reason=' + encodeURIComponent('invalid-access-code');
- }
-}
-
-function processGameState (
- currentGameState,
- userId,
- socket,
- gameStateRenderer,
- gameTimerManager,
- timerWorker,
- refreshPrompt = true,
- animateContainer = false
-) {
- const containerAnimation = document.getElementById('game-state-container').animate(
- [
- { opacity: '0', transform: 'translateY(10px)' },
- { opacity: '1', transform: 'translateY(0px)' }
- ], {
- duration: 500,
- easing: 'ease-in-out',
- fill: 'both'
- });
- if (animateContainer) {
- containerAnimation.play();
- }
- displayClientInfo(currentGameState.client.name, currentGameState.client.userType);
- if (refreshPrompt) {
- removeStartGameFunctionalityIfPresent(gameStateRenderer);
- document.querySelector('#end-game-prompt')?.remove();
- }
- switch (currentGameState.status) {
- case globals.STATUS.LOBBY:
- document.getElementById('game-state-container').innerHTML = HTMLFragments.LOBBY;
- gameStateRenderer.renderLobbyHeader();
- gameStateRenderer.renderLobbyPlayers();
- if ((
- currentGameState.client.userType === globals.USER_TYPES.MODERATOR
- || currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
- )
- && refreshPrompt
- ) {
- displayStartGamePromptForModerators(currentGameState, gameStateRenderer);
- }
- break;
- case globals.STATUS.IN_PROGRESS:
- gameStateRenderer.renderGameHeader();
- switch (currentGameState.client.userType) {
- case globals.USER_TYPES.PLAYER:
- document.getElementById('game-state-container').innerHTML = HTMLFragments.PLAYER_GAME_VIEW;
- gameStateRenderer.renderPlayerView();
- break;
- case globals.USER_TYPES.KILLED_PLAYER:
-
- document.getElementById('game-state-container').innerHTML = HTMLFragments.PLAYER_GAME_VIEW;
- gameStateRenderer.renderPlayerView(true);
- break;
- case globals.USER_TYPES.MODERATOR:
- document.getElementById('transfer-mod-prompt').innerHTML = HTMLFragments.TRANSFER_MOD_MODAL;
- document.getElementById('game-state-container').innerHTML = HTMLFragments.MODERATOR_GAME_VIEW;
- gameStateRenderer.renderModeratorView();
- break;
- case globals.USER_TYPES.TEMPORARY_MODERATOR:
- document.getElementById('transfer-mod-prompt').innerHTML = HTMLFragments.TRANSFER_MOD_MODAL;
- document.getElementById('game-state-container').innerHTML = HTMLFragments.TEMP_MOD_GAME_VIEW;
- gameStateRenderer.renderTempModView();
- break;
- case globals.USER_TYPES.SPECTATOR:
- document.getElementById('game-state-container').innerHTML = HTMLFragments.SPECTATOR_GAME_VIEW;
- gameStateRenderer.renderSpectatorView();
- break;
- default:
- break;
- }
- if (currentGameState.timerParams) {
- socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.GET_TIME_REMAINING, currentGameState.accessCode);
- } else {
- document.querySelector('#game-timer')?.remove();
- document.querySelector('#timer-container-moderator')?.remove();
- document.querySelector('label[for="game-timer"]')?.remove();
- }
- break;
- case globals.STATUS.ENDED: {
- const container = document.getElementById('game-state-container');
- container.innerHTML = HTMLFragments.END_OF_GAME_VIEW;
- gameStateRenderer.renderEndOfGame(currentGameState);
- break;
- }
- default:
- break;
- }
-
- activateRoleInfoButton(stateBucket.currentGameState.deck);
-}
-
-function displayClientInfo (name, userType) {
- document.getElementById('client-name').innerText = name;
- document.getElementById('client-user-type').innerText = userType;
- document.getElementById('client-user-type').innerText += globals.USER_TYPE_ICONS[userType];
-}
-
-function setClientSocketHandlers (stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager) {
- socket.on(globals.EVENTS.PLAYER_JOINED, (player, gameIsFull) => {
- toast(player.name + ' joined!', 'success', false, true, 'short');
- stateBucket.currentGameState.people.push(player);
- stateBucket.currentGameState.isFull = gameIsFull;
- gameStateRenderer.renderLobbyPlayers();
- if ((
- stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR
- || stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
- )
- ) {
- displayStartGamePromptForModerators(stateBucket.currentGameState, gameStateRenderer);
- }
- });
-
- socket.on(globals.EVENTS.NEW_SPECTATOR, (spectator) => {
- stateBucket.currentGameState.spectators.push(spectator);
- });
-
- socket.on(globals.EVENTS.PLAYER_LEFT, (player) => {
- removeStartGameFunctionalityIfPresent(gameStateRenderer);
- toast(player.name + ' has left!', 'error', false, true, 'short');
- const index = stateBucket.currentGameState.people.findIndex(person => person.id === player.id);
- if (index >= 0) {
- stateBucket.currentGameState.people.splice(
- index,
- 1
- );
- gameStateRenderer.renderLobbyPlayers();
- }
- });
-
- socket.on(globals.EVENTS.START_GAME, () => {
- socket.emit(
- globals.SOCKET_EVENTS.IN_GAME_MESSAGE,
- globals.EVENT_IDS.FETCH_GAME_STATE,
- stateBucket.currentGameState.accessCode,
- { personId: stateBucket.currentGameState.client.cookie },
- function (gameState) {
- stateBucket.currentGameState = gameState;
- processGameState(
- stateBucket.currentGameState,
- gameState.client.cookie,
- socket,
- gameStateRenderer,
- gameTimerManager,
- timerWorker,
- true,
- true
- );
- }
- );
- });
-
- socket.on(globals.EVENTS.SYNC_GAME_STATE, () => {
- socket.emit(
- globals.SOCKET_EVENTS.IN_GAME_MESSAGE,
- globals.EVENT_IDS.FETCH_GAME_STATE,
- stateBucket.currentGameState.accessCode,
- { personId: stateBucket.currentGameState.client.cookie },
- function (gameState) {
- stateBucket.currentGameState = gameState;
- processGameState(
- stateBucket.currentGameState,
- gameState.client.cookie,
- socket,
- gameStateRenderer,
- gameTimerManager,
- timerWorker,
- true,
- true
- );
- }
- );
- });
-
- if (timerWorker && gameTimerManager) {
- gameTimerManager.attachTimerSocketListeners(socket, timerWorker, gameStateRenderer);
- }
-
- socket.on(globals.EVENTS.KILL_PLAYER, (id) => {
- const killedPerson = stateBucket.currentGameState.people.find((person) => person.id === id);
- if (killedPerson) {
- killedPerson.out = true;
- if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR) {
- toast(killedPerson.name + ' killed.', 'success', true, true, 'medium');
- gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(stateBucket.currentGameState.status === globals.STATUS.ENDED);
- } else {
- if (killedPerson.id === stateBucket.currentGameState.client.id) {
- const clientUserType = document.getElementById('client-user-type');
- if (clientUserType) {
- clientUserType.innerText = globals.USER_TYPES.KILLED_PLAYER + ' \uD83D\uDC80';
- }
- gameStateRenderer.updatePlayerCardToKilledState();
- toast('You have been killed!', 'warning', true, true, 'medium');
- } else {
- toast(killedPerson.name + ' was killed!', 'warning', true, true, 'medium');
- }
- if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
- gameStateRenderer.removePlayerListEventListeners(false);
- } else {
- gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false);
- }
- }
- }
- });
-
- socket.on(globals.EVENTS.REVEAL_PLAYER, (revealData) => {
- const revealedPerson = stateBucket.currentGameState.people.find((person) => person.id === revealData.id);
- if (revealedPerson) {
- revealedPerson.revealed = true;
- revealedPerson.gameRole = revealData.gameRole;
- revealedPerson.alignment = revealData.alignment;
- if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR) {
- toast(revealedPerson.name + ' revealed.', 'success', true, true, 'medium');
- gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(stateBucket.currentGameState.status === globals.STATUS.ENDED);
- } else {
- if (revealedPerson.id === stateBucket.currentGameState.client.id) {
- toast('Your role has been revealed!', 'warning', true, true, 'medium');
- } else {
- toast(revealedPerson.name + ' was revealed as a ' + revealedPerson.gameRole + '!', 'warning', true, true, 'medium');
- }
- if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
- gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true);
- } else {
- gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false);
- }
- }
- }
- });
-
- socket.on(globals.EVENTS.CHANGE_NAME, (personId, name) => {
- propagateNameChange(stateBucket.currentGameState, name, personId);
- updateDOMWithNameChange(stateBucket.currentGameState, gameStateRenderer);
- processGameState(
- stateBucket.currentGameState,
- stateBucket.currentGameState.client.cookie,
- socket,
- gameStateRenderer,
- gameTimerManager,
- timerWorker,
- false,
- false
- );
- });
-
- socket.on(globals.COMMANDS.END_GAME, (people) => {
- stateBucket.currentGameState.people = people;
- stateBucket.currentGameState.status = globals.STATUS.ENDED;
- processGameState(
- stateBucket.currentGameState,
- stateBucket.currentGameState.client.cookie,
- socket,
- gameStateRenderer,
- gameTimerManager,
- timerWorker,
- true,
- true
- );
- });
-}
-
-function displayStartGamePromptForModerators (gameState, gameStateRenderer) {
- const existingPrompt = document.getElementById('start-game-prompt');
- if (existingPrompt) {
- enableOrDisableStartButton(gameState, existingPrompt, gameStateRenderer.startGameHandler);
- } else {
- const newPrompt = document.createElement('div');
- newPrompt.setAttribute('id', 'start-game-prompt');
- newPrompt.innerHTML = HTMLFragments.START_GAME_PROMPT;
-
- document.body.appendChild(newPrompt);
- enableOrDisableStartButton(gameState, newPrompt, gameStateRenderer.startGameHandler);
- }
-}
-
-function enableOrDisableStartButton (gameState, buttonContainer, handler) {
- if (gameState.isFull) {
- buttonContainer.querySelector('#start-game-button').addEventListener('click', handler);
- buttonContainer.querySelector('#start-game-button').classList.remove('disabled');
- } else {
- buttonContainer.querySelector('#start-game-button').removeEventListener('click', handler);
- buttonContainer.querySelector('#start-game-button').classList.add('disabled');
- }
-}
-
-function removeStartGameFunctionalityIfPresent (gameStateRenderer) {
- document.querySelector('#start-game-prompt')?.removeEventListener('click', gameStateRenderer.startGameHandler);
- document.querySelector('#start-game-prompt')?.remove();
-}
-
-function propagateNameChange (gameState, name, personId) {
- if (gameState.client.id === personId) {
- gameState.client.name = name;
- }
- const matchingPerson = gameState.people.find((person) => person.id === personId);
- if (matchingPerson) {
- matchingPerson.name = name;
- }
-
- if (gameState.moderator.id === personId) {
- gameState.moderator.name = name;
- }
-
- const matchingSpectator = gameState.spectators?.find((spectator) => spectator.id === personId);
- if (matchingSpectator) {
- matchingSpectator.name = name;
- }
-}
-
-function updateDOMWithNameChange (gameState, gameStateRenderer) {
- if (gameState.status === globals.STATUS.IN_PROGRESS) {
- switch (gameState.client.userType) {
- case globals.USER_TYPES.PLAYER:
- case globals.USER_TYPES.KILLED_PLAYER:
- case globals.USER_TYPES.SPECTATOR:
- gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false);
- break;
- case globals.USER_TYPES.MODERATOR:
- gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(gameState.status === globals.STATUS.ENDED);
- break;
- case globals.USER_TYPES.TEMPORARY_MODERATOR:
- gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true);
- break;
- default:
- break;
- }
- } else {
- gameStateRenderer.renderLobbyPlayers();
- }
-}
-
-function activateRoleInfoButton (deck) {
- deck.sort((a, b) => {
- return a.team === globals.ALIGNMENT.GOOD ? -1 : 1;
- });
- document.getElementById('role-info-button').addEventListener('click', (e) => {
- e.preventDefault();
- document.getElementById('role-info-prompt').innerHTML = HTMLFragments.ROLE_INFO_MODAL;
- const modalContent = document.getElementById('game-role-info-container');
- for (const card of deck) {
- const roleDiv = document.createElement('div');
- const roleNameDiv = document.createElement('div');
-
- roleNameDiv.classList.add('role-info-name');
-
- const roleName = document.createElement('h5');
- const roleQuantity = document.createElement('h5');
- const roleDescription = document.createElement('p');
-
- roleDescription.innerText = card.description;
- roleName.innerText = card.role;
- roleQuantity.innerText = card.quantity + 'x';
-
- if (card.team === globals.ALIGNMENT.GOOD) {
- roleName.classList.add(globals.ALIGNMENT.GOOD);
- } else {
- roleName.classList.add(globals.ALIGNMENT.EVIL);
- }
-
- roleNameDiv.appendChild(roleQuantity);
- roleNameDiv.appendChild(roleName);
-
- roleDiv.appendChild(roleNameDiv);
- roleDiv.appendChild(roleDescription);
-
- modalContent.appendChild(roleDiv);
- }
- ModalManager.displayModal('role-info-modal', 'role-info-modal-background', 'close-role-info-modal-button');
- });
-}
-
-if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
- module.exports = game;
-} else {
- game();
-}
+await gameHandler(io('/in-game'), XHRUtility, window, gameTemplate);
diff --git a/client/src/scripts/home.js b/client/src/scripts/home.js
index 17a9a94..f6f85d0 100644
--- a/client/src/scripts/home.js
+++ b/client/src/scripts/home.js
@@ -1,6 +1,6 @@
-import { XHRUtility } from '../modules/XHRUtility.js';
-import { toast } from '../modules/Toast.js';
-import { injectNavbar } from '../modules/Navbar.js';
+import { XHRUtility } from '../modules/utility/XHRUtility.js';
+import { toast } from '../modules/front_end_components/Toast.js';
+import { injectNavbar } from '../modules/front_end_components/Navbar.js';
const home = () => {
injectNavbar();
diff --git a/client/src/scripts/howToUse.js b/client/src/scripts/howToUse.js
index 6cf8d9d..59b5f46 100644
--- a/client/src/scripts/howToUse.js
+++ b/client/src/scripts/howToUse.js
@@ -1,4 +1,4 @@
-import { injectNavbar } from '../modules/Navbar.js';
+import { injectNavbar } from '../modules/front_end_components/Navbar.js';
const howToUse = () => {
injectNavbar();
diff --git a/client/src/scripts/join.js b/client/src/scripts/join.js
index cd8a3c0..72e72f8 100644
--- a/client/src/scripts/join.js
+++ b/client/src/scripts/join.js
@@ -1,7 +1,7 @@
-import { injectNavbar } from '../modules/Navbar.js';
-import { toast } from '../modules/Toast.js';
-import { XHRUtility } from '../modules/XHRUtility.js';
-import { UserUtility } from '../modules/UserUtility.js';
+import { injectNavbar } from '../modules/front_end_components/Navbar.js';
+import { toast } from '../modules/front_end_components/Toast.js';
+import { XHRUtility } from '../modules/utility/XHRUtility.js';
+import { UserUtility } from '../modules/utility/UserUtility.js';
import { globals } from '../config/globals.js';
const join = () => {
diff --git a/client/src/scripts/notFound.js b/client/src/scripts/notFound.js
index e83aec9..58e2f21 100644
--- a/client/src/scripts/notFound.js
+++ b/client/src/scripts/notFound.js
@@ -1,4 +1,4 @@
-import { injectNavbar } from '../modules/Navbar.js';
+import { injectNavbar } from '../modules/front_end_components/Navbar.js';
const notFound = () => {
injectNavbar();
diff --git a/client/src/view_templates/GameTemplate.js b/client/src/view_templates/GameTemplate.js
new file mode 100644
index 0000000..9c16664
--- /dev/null
+++ b/client/src/view_templates/GameTemplate.js
@@ -0,0 +1,42 @@
+const template =
+ `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Connecting to game...
+
+
+
+
`;
+
+export default template;
diff --git a/client/src/views/game.html b/client/src/views/game.html
index ca4c905..935d264 100644
--- a/client/src/views/game.html
+++ b/client/src/views/game.html
@@ -19,46 +19,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Connecting to game...
-
-
-
-
-
+
+