diff --git a/app.js b/app.js index 05a6d67..1a01eab 100644 --- a/app.js +++ b/app.js @@ -14,6 +14,8 @@ let allLaraGames = []; let refreshTimer = null; let countdown = 0; let serverLastFetch = null; +let laraColor = null; +let currentMoveIndex = -1; /** * Lädt die PGN-Datei und aktualisiert die Anzeige @@ -75,19 +77,23 @@ function updateBoard() { // Spiegele das Brett, wenn Lara Schwarz hat const laraIsBlack = currentGame.black.toLowerCase().includes('kiesewetter'); const orientation = laraIsBlack ? 'black' : 'white'; + laraColor = laraIsBlack ? 'b' : 'w'; // Führe alle Züge aus - for (const move of currentGame.moves) { - if (move.isResult) break; + const nonResultMoves = currentGame.moves.filter(m => !m.isResult); + for (const move of nonResultMoves) { try { chess.move(move.san); } catch (e) { // Ignoriere ungültige Züge } } + currentMoveIndex = nonResultMoves.length - 1; - if (board) board.position(chess.fen(), true); - else { + if (board) { + board.position(chess.fen(), true); + board.orientation(orientation); + } else { board = Chessboard('board', { position: chess.fen(), orientation: orientation, @@ -104,6 +110,38 @@ function updateBoard() { highlightActivePlayer(); } +/** + * Gehe zu einem bestimmten Zug (Index) + */ +function goToMove(index) { + if (!currentGame) return; + + const nonResultMoves = currentGame.moves.filter(m => !m.isResult); + + if (index < -1) index = -1; + if (index >= nonResultMoves.length) index = nonResultMoves.length - 1; + + currentMoveIndex = index; + + chess = new Chess(); + for (let i = 0; i <= index; i++) { + try { + chess.move(nonResultMoves[i].san); + } catch (err) { + break; + } + } + + board.position(chess.fen(), true); + highlightActivePlayer(); + + document.querySelectorAll('#moves-list .move').forEach(el => el.classList.remove('current')); + if (index >= 0) { + const moveEl = document.querySelector(`#moves-list [data-index="${index}"]`); + if (moveEl) moveEl.classList.add('current'); + } +} + /** * Aktualisiert die Spielerinformationen */ @@ -112,23 +150,27 @@ function updatePlayerInfo() { const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter'); - if (laraIsWhite) { - document.getElementById('white-name').textContent = currentGame.white; - document.getElementById('white-elo').textContent = currentGame.whiteElo ? `(ELO: ${currentGame.whiteElo})` : ''; - document.getElementById('white-clock').textContent = formatClock(currentGame.whiteClock); - - document.getElementById('black-name').textContent = currentGame.black; - document.getElementById('black-elo').textContent = currentGame.blackElo ? `(ELO: ${currentGame.blackElo})` : ''; - document.getElementById('black-clock').textContent = formatClock(currentGame.blackClock); - } else { - document.getElementById('white-name').textContent = currentGame.white; - document.getElementById('white-elo').textContent = currentGame.whiteElo ? `(ELO: ${currentGame.whiteElo})` : ''; - document.getElementById('white-clock').textContent = formatClock(currentGame.whiteClock); - - document.getElementById('black-name').textContent = currentGame.black; - document.getElementById('black-elo').textContent = currentGame.blackElo ? `(ELO: ${currentGame.blackElo})` : ''; - document.getElementById('black-clock').textContent = formatClock(currentGame.blackClock); - } + const laraPlayer = laraIsWhite ? currentGame.white : currentGame.black; + const laraElo = laraIsWhite ? currentGame.whiteElo : currentGame.blackElo; + const laraClock = laraIsWhite ? currentGame.whiteClock : currentGame.blackClock; + const laraEmoji = laraIsWhite ? '⬜' : '⬛'; + + const oppPlayer = laraIsWhite ? currentGame.black : currentGame.white; + const oppElo = laraIsWhite ? currentGame.blackElo : currentGame.whiteElo; + const oppClock = laraIsWhite ? currentGame.blackClock : currentGame.whiteClock; + const oppEmoji = laraIsWhite ? '⬛' : '⬜'; + + // Top-Panel (player-black) = Gegner + document.getElementById('black-name').textContent = oppPlayer; + document.getElementById('black-elo').textContent = oppElo ? `(ELO: ${oppElo})` : ''; + document.getElementById('black-clock').textContent = formatClock(oppClock); + document.querySelector('#player-black .player-avatar').textContent = oppEmoji; + + // Bottom-Panel (player-white) = Lara + document.getElementById('white-name').textContent = laraPlayer; + document.getElementById('white-elo').textContent = laraElo ? `(ELO: ${laraElo})` : ''; + document.getElementById('white-clock').textContent = formatClock(laraClock); + document.querySelector('#player-white .player-avatar').textContent = laraEmoji; // Round info const roundInfo = `Runde ${currentGame.round} – ${currentGame.event || 'Turnier'}`; @@ -147,18 +189,21 @@ function updatePlayerInfo() { * Highlight den Spieler, der gerade am Zug ist */ function highlightActivePlayer() { - if (!chess) return; + if (!chess || !currentGame) return; - const whiteEl = document.getElementById('player-white'); - const blackEl = document.getElementById('player-black'); + const laraPanel = document.getElementById('player-white'); + const oppPanel = document.getElementById('player-black'); - whiteEl.classList.remove('active'); - blackEl.classList.remove('active'); + laraPanel.classList.remove('active'); + oppPanel.classList.remove('active'); - if (chess.turn() === 'w') { - whiteEl.classList.add('active'); + const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter'); + const isLaraTurn = (chess.turn() === 'w' && laraIsWhite) || (chess.turn() === 'b' && !laraIsWhite); + + if (isLaraTurn) { + laraPanel.classList.add('active'); } else { - blackEl.classList.add('active'); + oppPanel.classList.add('active'); } } @@ -172,37 +217,53 @@ function updateMovesList() { movesList.innerHTML = ''; const nonResultMoves = currentGame.moves.filter(m => !m.isResult); + const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter'); for (let i = 0; i < nonResultMoves.length; i += 2) { const moveNumber = Math.floor(i / 2) + 1; - // Move number const numSpan = document.createElement('span'); numSpan.className = 'move-number'; numSpan.textContent = `${moveNumber}.`; movesList.appendChild(numSpan); - // White move - const whiteMove = document.createElement('span'); - whiteMove.className = 'move'; - whiteMove.textContent = nonResultMoves[i].san; - whiteMove.dataset.index = i; - movesList.appendChild(whiteMove); - - // Black move - if (i + 1 < nonResultMoves.length) { - const blackMove = document.createElement('span'); - blackMove.className = 'move'; - blackMove.textContent = nonResultMoves[i + 1].san; - blackMove.dataset.index = i + 1; - movesList.appendChild(blackMove); + if (laraIsWhite) { + // Lara (Weiß) zuerst + const laraMove = document.createElement('span'); + laraMove.className = 'move lara-move'; + laraMove.textContent = nonResultMoves[i].san; + laraMove.dataset.index = i; + movesList.appendChild(laraMove); + + if (i + 1 < nonResultMoves.length) { + const oppMove = document.createElement('span'); + oppMove.className = 'move opp-move'; + oppMove.textContent = nonResultMoves[i + 1].san; + oppMove.dataset.index = i + 1; + movesList.appendChild(oppMove); + } + } else { + // Lara (Schwarz) – Gegner zuerst, dann Lara + const oppMove = document.createElement('span'); + oppMove.className = 'move opp-move'; + oppMove.textContent = nonResultMoves[i].san; + oppMove.dataset.index = i; + movesList.appendChild(oppMove); + + if (i + 1 < nonResultMoves.length) { + const laraMove = document.createElement('span'); + laraMove.className = 'move lara-move'; + laraMove.textContent = nonResultMoves[i + 1].san; + laraMove.dataset.index = i + 1; + movesList.appendChild(laraMove); + } } } - // Mark last move - if (nonResultMoves.length > 0) { - const lastMove = movesList.querySelector(`[data-index="${nonResultMoves.length - 1}"]`); - if (lastMove) lastMove.classList.add('current'); + // Mark current move + if (currentMoveIndex >= 0) { + const curMove = movesList.querySelector(`[data-index="${currentMoveIndex}"]`); + if (curMove) curMove.classList.add('current'); } // Scroll to bottom @@ -214,24 +275,7 @@ function updateMovesList() { */ function handleMoveClick(e) { if (!e.target.classList.contains('move') || !currentGame) return; - - const index = parseInt(e.target.dataset.index); - const nonResultMoves = currentGame.moves.filter(m => !m.isResult); - - chess = new Chess(); - for (let i = 0; i <= index; i++) { - try { - chess.move(nonResultMoves[i].san); - } catch (err) { - break; - } - } - - board.position(chess.fen(), true); - - // Update current highlight - document.querySelectorAll('#moves-list .move').forEach(el => el.classList.remove('current')); - e.target.classList.add('current'); + goToMove(parseInt(e.target.dataset.index)); } /** @@ -337,6 +381,23 @@ document.getElementById('refresh-btn').addEventListener('click', () => { loadPGN(); }); +/** + * Pfeiltasten-Navigation + */ +document.addEventListener('keydown', (e) => { + if (!currentGame) return; + + const nonResultMoves = currentGame.moves.filter(m => !m.isResult); + + if (e.key === 'ArrowLeft') { + e.preventDefault(); + goToMove(currentMoveIndex - 1); + } else if (e.key === 'ArrowRight') { + e.preventDefault(); + goToMove(currentMoveIndex + 1); + } +}); + /** * Init */ diff --git a/style.css b/style.css index fcbfda1..a229922 100644 --- a/style.css +++ b/style.css @@ -213,6 +213,15 @@ header h1 { color: #fff; } +#moves-list .lara-move { + color: #ffd700; + font-weight: bold; +} + +#moves-list .opp-move { + color: #aaa; +} + #all-games-list { display: flex; flex-direction: column;