class GameUI { constructor() { // Initialize UI state and configurations this.state = { currentPage: 'landing', isAnimating: false, darkMode: true, animationDuration: 300 }; // Store references to frequently accessed DOM elements this.elements = { pages: {}, buttons: {}, containers: {}, overlays: {} }; // Initialize the UI this.initialize(); } async initialize() { // Wait for DOM to be fully loaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.setupUI()); } else { this.setupUI(); } } setupUI() { // Cache all important DOM elements this.cacheElements(); // Set up event listeners this.setupEventListeners(); // Initialize the landing page this.showPage('landing'); } cacheElements() { // Cache all page elements ['landing', 'setup', 'game', 'recording', 'listening', 'voting', 'results'].forEach(pageId => { this.elements.pages[pageId] = document.getElementById(`${pageId}-page`); }); // Cache all button elements this.elements.buttons = { play: document.getElementById('play-button'), addPlayer: document.getElementById('add-player-button'), start: document.getElementById('start-button'), record: document.getElementById('record-button'), settings: document.getElementById('settings-button') }; // Cache container elements this.elements.containers = { playerList: document.getElementById('player-list'), questionDisplay: document.getElementById('question-display'), timerDisplay: document.getElementById('timer-display'), recordingVisualizer: document.getElementById('recording-visualizer'), votingOptions: document.getElementById('voting-options') }; // Cache overlay elements this.elements.overlays = { loading: document.getElementById('loading-overlay'), error: document.getElementById('error-overlay'), settings: document.getElementById('settings-overlay') }; } setupEventListeners() { // Set up button click handlers Object.entries(this.elements.buttons).forEach(([key, button]) => { if (button) { button.addEventListener('click', () => this.handleButtonClick(key)); } }); // Set up keyboard shortcuts document.addEventListener('keydown', (e) => this.handleKeyPress(e)); // Set up settings toggle if (this.elements.buttons.settings) { this.elements.buttons.settings.addEventListener('click', () => this.toggleSettings()); } } handleButtonClick(buttonType) { switch (buttonType) { case 'play': this.transitionToPage('setup'); break; case 'addPlayer': window.game.addPlayer(); break; case 'start': window.game.startGame(); break; case 'record': this.toggleRecording(); break; default: console.warn(`Unhandled button type: ${buttonType}`); } } async transitionToPage(pageName) { if (this.state.isAnimating || this.state.currentPage === pageName) return; this.state.isAnimating = true; // Fade out current page const currentPage = this.elements.pages[this.state.currentPage]; if (currentPage) { await this.animateElement(currentPage, 'fadeOut'); currentPage.classList.remove('active'); } // Update state and fade in new page this.state.currentPage = pageName; const newPage = this.elements.pages[pageName]; if (newPage) { newPage.classList.add('active'); await this.animateElement(newPage, 'fadeIn'); } this.state.isAnimating = false; } async animateElement(element, animation) { return new Promise(resolve => { element.classList.add(animation); setTimeout(() => { element.classList.remove(animation); resolve(); }, this.state.animationDuration); }); } updatePlayerList(players) { if (!this.elements.containers.playerList) return; const playerList = this.elements.containers.playerList; playerList.innerHTML = ''; players.forEach(player => { const playerElement = document.createElement('div'); playerElement.className = 'player-avatar'; // Create avatar circle const avatarCircle = document.createElement('div'); avatarCircle.className = 'avatar-circle'; avatarCircle.textContent = player.id; // Create player name const playerName = document.createElement('div'); playerName.className = 'player-name'; playerName.textContent = player.name; playerElement.appendChild(avatarCircle); playerElement.appendChild(playerName); playerList.appendChild(playerElement); }); } updateTimer(timeLeft) { if (!this.elements.containers.timerDisplay) return; const minutes = Math.floor(timeLeft / 60); const seconds = timeLeft % 60; this.elements.containers.timerDisplay.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`; // Add warning class when time is running low if (timeLeft <= 10) { this.elements.containers.timerDisplay.classList.add('warning'); } } showQuestion(question) { if (!this.elements.containers.questionDisplay) return; const questionElement = this.elements.containers.questionDisplay; questionElement.textContent = question; // Animate question appearance this.animateElement(questionElement, 'slideIn'); } toggleRecording(isRecording) { if (!this.elements.buttons.record) return; const recordButton = this.elements.buttons.record; recordButton.classList.toggle('recording', isRecording); recordButton.textContent = isRecording ? 'Stop Recording' : 'Start Recording'; } showLoadingOverlay(show, message = 'Loading...') { if (!this.elements.overlays.loading) return; const overlay = this.elements.overlays.loading; if (show) { overlay.querySelector('.loading-message').textContent = message; overlay.classList.add('active'); } else { overlay.classList.remove('active'); } } showError(message, duration = 3000) { if (!this.elements.overlays.error) return; const errorOverlay = this.elements.overlays.error; const errorMessage = errorOverlay.querySelector('.error-message'); errorMessage.textContent = message; errorOverlay.classList.add('active'); setTimeout(() => { errorOverlay.classList.remove('active'); }, duration); } updateVotingOptions(players, currentPlayer) { if (!this.elements.containers.votingOptions) return; const votingContainer = this.elements.containers.votingOptions; votingContainer.innerHTML = ''; players.forEach(player => { if (player.id !== currentPlayer) { const voteButton = document.createElement('button'); voteButton.className = 'vote-button'; voteButton.dataset.playerId = player.id; const playerCircle = document.createElement('div'); playerCircle.className = 'player-circle'; playerCircle.textContent = player.id; voteButton.appendChild(playerCircle); votingContainer.appendChild(voteButton); voteButton.addEventListener('click', () => { window.game.submitVote(player.id); }); } }); } showResults(results) { const resultsPage = this.elements.pages.results; if (!resultsPage) return; // Clear previous results resultsPage.innerHTML = ''; // Create results content const content = document.createElement('div'); content.className = 'results-content'; // Add impostor reveal const impostorReveal = document.createElement('div'); impostorReveal.className = 'impostor-reveal'; impostorReveal.textContent = `The impostor was Player ${results.impostor}!`; // Add voting results const votingResults = document.createElement('div'); votingResults.className = 'voting-results'; Object.entries(results.votes).forEach(([player, vote]) => { const voteEntry = document.createElement('div'); voteEntry.className = 'vote-entry'; voteEntry.textContent = `Player ${player} voted for Player ${vote}`; votingResults.appendChild(voteEntry); }); content.appendChild(impostorReveal); content.appendChild(votingResults); resultsPage.appendChild(content); this.transitionToPage('results'); } handleKeyPress(event) { // Add keyboard shortcuts switch (event.key) { case 'Escape': this.closeAllOverlays(); break; case 'r': if (this.state.currentPage === 'recording') { this.toggleRecording(); } break; } } closeAllOverlays() { Object.values(this.elements.overlays).forEach(overlay => { if (overlay) { overlay.classList.remove('active'); } }); } // Method to clean up UI resources cleanup() { // Remove event listeners Object.values(this.elements.buttons).forEach(button => { if (button) { button.replaceWith(button.cloneNode(true)); } }); // Clear all intervals and timeouts this.closeAllOverlays(); } } // Export the GameUI class export default GameUI;