This application provides a modern, offline-first experience for viewing and analyzing chess games stored in the Portable Game Notation (PGN) format. It’s built as a Progressive Web Application (PWA), leveraging a robust template that includes features like internationalization, common UI components based on Bootstrap 5, and seamless cloud synchronization via Dropbox.
A live version of the app is available here, and the full code on GitHub here.
chess.js
.
// Example: Loading PGN data (from game-logic.js)
import { Chess } from './lib/chess-1.2.0.min.js';
// ...
export function loadPgn(pgnString) {
// ...
try {
chess = new Chess();
chess.loadPgn(pgnString);
initialFen = chess.getHeaders().FEN || new Chess().fen();
moveHistory = chess.history({ verbose: true }) || [];
currentMoveIndex = -1;
return true;
} catch (error) {
console.error(`Error loading PGN: ${error}`);
initializeGame(); // Reset to default state on error
return false;
}
}
// Example: Rendering the board (from board-ui.js)
export function renderBoard(boardContainer, fen) {
// ... clear previous board ...
// ... parse FEN piece placement ...
rankIteration.forEach((fenRank, rankIndex) => {
// ... iterate through rank characters ...
if (isNaN(parseInt(char, 10))) { // It's a piece
const algebraic = indicesToAlgebraic(rankIndex, currentFileIndex);
const square = createSquareElement(rankIndex, currentFileIndex, algebraic);
const pieceData = pieceMap[char];
if (pieceData) {
const pieceImage = document.createElement('img');
pieceImage.src = `${getCurrentPieceImagePath()}${pieceData}`;
// ... add piece to square ...
}
boardGrid.appendChild(square);
currentFileIndex++;
} else { // It's empty squares
// ... create empty squares ...
}
});
boardContainer.appendChild(boardGrid);
}
* Supports standard piece sets and allows cycling through different visual styles.
* Flip the board orientation (White/Black at the bottom) with a single click.
// Example: Navigating moves (from game-logic.js)
export function goToNextMove() {
if (currentMoveIndex >= moveHistory.length - 1) return null;
return goToMoveIndex(currentMoveIndex + 1);
}
export function goToMoveIndex(index) {
// ... reset board to initial FEN ...
for (let i = 0; i <= index; i++) {
if (!chess.move(moveHistory[i].san)) return null; // Apply moves up to index
}
currentMoveIndex = index;
return chess.fen(); // Return FEN of the target position
}
Built upon a flexible PWA template, this viewer offers:
// Example: Service Worker registration (from index.html)
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js', { scope: '/' })
.catch(error => console.error('Service Worker registration failed:', error));
}
// Example: Initializing Dropbox Sync (from dropbox-sync.js)
async function initializeDropboxSync() {
logVerbose('Initializing Dropbox Sync System...');
initializeSyncCoordinator(); // Handles sync logic
initializeOfflineHandling(); // Manages online/offline state changes
await initializeAuthentication(); // Handles login and token management
// ...
}
This PGN viewer combines standard chess game viewing functionalities with the convenience of a modern web application, accessible anywhere, anytime, even offline, with optional cloud backup and sync. The source code is publicly available on GitHub for those interested in the technical details.