// HoopScores Rotator 3000 - Main Application Script class BoxscoreRotator { constructor() { this.games = []; this.currentGameIndex = 0; this.rotationInterval = null; this.isRotating = true; this.lastFetchDate = null; this.initializeElements(); this.setupEventListeners(); this.loadYesterdayGames(); } initializeElements() { this.boxscoreContainer = document.getElementById('boxscore-container'); this.currentGameNumber = document.getElementById('current-game-number'); this.totalGames = document.getElementById('total-games'); this.gameInfo = document.getElementById('game-info'); this.lastUpdated = document.getElementById('last-updated'); // Control buttons this.prevButton = document.getElementById('prev-game'); this.nextButton = document.getElementById('next-game'); this.toggleButton = document.getElementById('toggle-rotation'); } setupEventListeners() { this.prevButton.addEventListener('click', () => this.previousGame()); this.nextButton.addEventListener('click', () => this.nextGame()); this.toggleButton.addEventListener('click', () => this.toggleRotation()); // Keyboard navigation document.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft') this.previousGame(); if (e.key === 'ArrowRight') this.nextGame(); if (e.key === ' ') this.toggleRotation(); }); } async loadYesterdayGames() { try { // Get yesterday's date in EST const yesterday = this.getYesterdayDateEST(); this.lastFetchDate = yesterday; const scoreboardUrl = `https://corsproxy.io/?https://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard?dates=${yesterday}`; const response = await fetch(scoreboardUrl); const data = await response.json(); // Filter for completed games from yesterday this.games = (data.events || []).filter(event => { const status = event.status?.type?.state; return status === 'post'; // Only finished games }); this.updateGameCount(); if (this.games.length > 0) { this.displayCurrentGame(); this.startRotation(); } else { this.showNoGamesMessage(); } this.updateLastUpdatedTime(); } catch (error) { console.error('Error loading games:', error); this.showErrorMessage('Failed to load boxscores. Please try again later.'); } } getYesterdayDateEST() { const now = new Date(); const estOffset = -5 * 60; // EST is UTC-5 const estTime = new Date(now.getTime() + (estOffset + now.getTimezoneOffset()) * 60000); // Subtract one day estTime.setDate(estTime.getDate() - 1); // Format as YYYYMMDD const year = estTime.getFullYear(); const month = String(estTime.getMonth() + 1).padStart(2, '0'); const day = String(estTime.getDate()).padStart(2, '0'); return `${year}${month}${day}`; } async displayCurrentGame() { if (this.games.length === 0) return; const game = this.games[this.currentGameIndex]; this.boxscoreContainer.innerHTML = this.createLoadingHTML(); try { const boxscoreData = await this.fetchBoxscore(game.id); if (boxscoreData) { this.renderBoxscore(boxscoreData, game); } } catch (error) { console.error('Error displaying game:', error); this.showErrorMessage('Failed to load boxscore for this game.'); } } async fetchBoxscore(gameId) { const boxscoreUrl = `https://corsproxy.io/?https://site.api.espn.com/apis/site/v2/sports/basketball/nba/summary?event=${gameId}`; const response = await fetch(boxscoreUrl); return await response.json(); } renderBoxscore(boxscoreData, game) { const competition = game.competitions[0]; const homeTeam = competition.competitors.find(c => c.homeAway === 'home'); const awayTeam = competition.competitors.find(c => c.homeAway === 'away'); const playersByTeam = boxscoreData.boxscore?.players || []; const homePlayers = playersByTeam.find(p => p.team.id === homeTeam.team.id); const awayPlayers = playersByTeam.find(p => p.team.id === awayTeam.team.id); const html = `
${awayTeam.team.displayName}
${awayTeam.team.abbreviation}
${awayTeam.score || 0}
FINAL
${game.status?.type?.detail || ''}
${homeTeam.team.abbreviation}
${homeTeam.score || 0}
${homeTeam.team.displayName}
${this.renderTeamStats(awayPlayers, awayTeam.team.displayName)} ${this.renderTeamStats(homePlayers, homeTeam.team.displayName)}
`; this.boxscoreContainer.innerHTML = html; this.updateGameInfo(game, homeTeam, awayTeam); } renderTeamStats(teamData, teamName) { if (!teamData || !teamData.statistics || teamData.statistics.length === 0) { return `

${teamName}

No player data available

`; } const stats = teamData.statistics[0]; const headers = stats.labels || []; const athletes = stats.athletes || []; // Filter out OREB and DREB headers and stats const rebIndex = headers.findIndex(header => header === 'REB'); const orebIndex = headers.findIndex(header => header === 'OREB'); const drebIndex = headers.findIndex(header => header === 'DREB'); // Create new headers and stats arrays without OREB and DREB const filteredHeaders = headers.filter((header, index) => { return header !== 'OREB' && header !== 'DREB'; }); const filteredAthletes = athletes.map(athlete => { const filteredStats = athlete.stats.filter((stat, index) => { return headers[index] !== 'OREB' && headers[index] !== 'DREB'; }); return { ...athlete, stats: filteredStats }; }); return `

${teamName}

${filteredHeaders.map(header => ``).join('')} ${filteredAthletes.map(athlete => ` ${athlete.stats.map(stat => ``).join('')} `).join('')}
Player${header}
${athlete.athlete.displayName}${stat}
`; } updateGameInfo(game, homeTeam, awayTeam) { const venue = game.competitions[0]?.venue?.fullName || 'NBA Arena'; const date = new Date(game.date).toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); this.gameInfo.innerHTML = `

${awayTeam.team.displayName} @ ${homeTeam.team.displayName}

${venue} • ${date}

`; } nextGame() { this.currentGameIndex = (this.currentGameIndex + 1) % this.games.length; this.displayCurrentGame(); this.updateGameCount(); } previousGame() { this.currentGameIndex = this.currentGameIndex === 0 ? this.games.length - 1 : this.currentGameIndex - 1; this.displayCurrentGame(); this.updateGameCount(); } startRotation() { if (this.rotationInterval) { clearInterval(this.rotationInterval); } this.rotationInterval = setInterval(() => { if (this.isRotating) { this.nextGame(); } }, 15000); // 15 seconds per game } toggleRotation() { this.isRotating = !this.isRotating; const icon = this.isRotating ? 'pause' : 'play'; const text = this.isRotating ? 'Pause' : 'Resume'; this.toggleButton.innerHTML = ` ${text} `; feather.replace(); } updateGameCount() { this.currentGameNumber.textContent = this.currentGameIndex + 1; this.totalGames.textContent = this.games.length; } updateLastUpdatedTime() { const now = new Date(); this.lastUpdated.textContent = now.toLocaleString('en-US', { timeZone: 'America/New_York', dateStyle: 'medium', timeStyle: 'medium' }); } createLoadingHTML() { return `

Loading boxscore data...

`; } showNoGamesMessage() { this.boxscoreContainer.innerHTML = `

No Games Yesterday

There were no NBA games played yesterday.

`; feather.replace(); } showErrorMessage(message) { this.boxscoreContainer.innerHTML = `

Unable to Load Data

${message}

`; feather.replace(); } } // Auto-refresh at 2 AM EST function scheduleDailyRefresh() { const now = new Date(); const estTime = new Date(now.toLocaleString('en-US', { timeZone: 'America/New_York' })); // Set target time to 2 AM EST const targetTime = new Date(estTime); targetTime.setHours(2, 0, 0, 0); // If it's already past 2 AM today, schedule for tomorrow if (estTime >= targetTime) { targetTime.setDate(targetTime.getDate() + 1); } const timeUntilRefresh = targetTime - estTime; setTimeout(() => { window.location.reload(); }, timeUntilRefresh); } // Initialize application when DOM is loaded document.addEventListener('DOMContentLoaded', () => { window.boxscoreRotator = new BoxscoreRotator(); scheduleDailyRefresh(); }); // Handle page visibility changes document.addEventListener('visibilitychange', () => { if (!document.hidden && window.boxscoreRotator) { const currentDate = window.boxscoreRotator.getYesterdayDateEST(); if (currentDate !== window.boxscoreRotator.lastFetchDate) { window.location.reload(); } } });