Split app.js into modular components (state, evaluation, ui, board, data)
This commit is contained in:
255
js/ui.js
Normal file
255
js/ui.js
Normal file
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* Lara Kiesewetter – Live Schachturnier
|
||||
* UI rendering: player info, clocks, highlights, moves list, games list, PGN display
|
||||
*/
|
||||
|
||||
/* global $, Chess */
|
||||
|
||||
function showLoading(show) {
|
||||
document.getElementById('loading-overlay').style.display = show ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
function hideError() {
|
||||
document.getElementById('error-overlay').style.display = 'none';
|
||||
}
|
||||
|
||||
function updatePlayerInfo() {
|
||||
if (!currentGame) return;
|
||||
|
||||
const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter');
|
||||
|
||||
const laraPlayer = laraIsWhite ? currentGame.white : currentGame.black;
|
||||
const laraElo = laraIsWhite ? currentGame.whiteElo : currentGame.blackElo;
|
||||
const laraEmoji = laraIsWhite ? '⬜' : '⬛';
|
||||
|
||||
const oppPlayer = laraIsWhite ? currentGame.black : currentGame.white;
|
||||
const oppElo = laraIsWhite ? currentGame.blackElo : currentGame.whiteElo;
|
||||
const oppEmoji = laraIsWhite ? '⬛' : '⬜';
|
||||
|
||||
document.getElementById('black-name').textContent = oppPlayer;
|
||||
document.getElementById('black-elo').textContent = oppElo ? `(ELO: ${oppElo})` : '';
|
||||
document.querySelector('#player-black .player-avatar').textContent = oppEmoji;
|
||||
|
||||
document.getElementById('white-name').textContent = laraPlayer;
|
||||
document.getElementById('white-elo').textContent = laraElo ? `(ELO: ${laraElo})` : '';
|
||||
document.querySelector('#player-white .player-avatar').textContent = laraEmoji;
|
||||
|
||||
updateClocks(currentMoveIndex);
|
||||
|
||||
const roundInfo = `Runde ${currentGame.round} – ${currentGame.event || 'Turnier'}`;
|
||||
document.getElementById('round-info').textContent = roundInfo;
|
||||
const mobileRound = document.getElementById('round-info-mobile');
|
||||
if (mobileRound) mobileRound.textContent = roundInfo;
|
||||
|
||||
const resultEl = document.getElementById('result-info');
|
||||
if (currentGame.isLive) {
|
||||
resultEl.innerHTML = '<span style="color: #4ade80;">● Laufen</span>';
|
||||
} else {
|
||||
resultEl.textContent = `Ergebnis: ${currentGame.result}`;
|
||||
}
|
||||
}
|
||||
|
||||
function updateClocks(moveIndex) {
|
||||
if (!currentGame) return;
|
||||
|
||||
const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter');
|
||||
|
||||
let whiteClock, blackClock;
|
||||
|
||||
if (moveIndex < 0) {
|
||||
whiteClock = currentGame.whiteClock;
|
||||
blackClock = currentGame.blackClock;
|
||||
} else {
|
||||
const nonResultMoves = currentGame.moves.filter(m => !m.isResult);
|
||||
if (moveIndex < nonResultMoves.length) {
|
||||
whiteClock = nonResultMoves[moveIndex].whiteClock;
|
||||
blackClock = nonResultMoves[moveIndex].blackClock;
|
||||
}
|
||||
}
|
||||
|
||||
const laraClock = laraIsWhite ? whiteClock : blackClock;
|
||||
const oppClock = laraIsWhite ? blackClock : whiteClock;
|
||||
|
||||
document.getElementById('black-clock').textContent = oppClock || '--:--:--';
|
||||
document.getElementById('white-clock').textContent = laraClock || '--:--:--';
|
||||
}
|
||||
|
||||
function highlightActivePlayer() {
|
||||
if (!chess || !currentGame) return;
|
||||
|
||||
const laraPanel = document.getElementById('player-white');
|
||||
const oppPanel = document.getElementById('player-black');
|
||||
|
||||
laraPanel.classList.remove('active');
|
||||
oppPanel.classList.remove('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 {
|
||||
oppPanel.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
function highlightLastMove() {
|
||||
if (!board || !chess || !currentGame) return;
|
||||
|
||||
$('#board [data-square]').removeClass('last-move-highlight');
|
||||
|
||||
const nonResultMoves = currentGame.moves.filter(m => !m.isResult);
|
||||
|
||||
if (currentMoveIndex >= 0 && currentMoveIndex < nonResultMoves.length) {
|
||||
const moves = chess.history({ verbose: true });
|
||||
if (moves.length > 0) {
|
||||
const lastMoveData = moves[moves.length - 1];
|
||||
$(`#board [data-square="${lastMoveData.from}"]`).addClass('last-move-highlight');
|
||||
$(`#board [data-square="${lastMoveData.to}"]`).addClass('last-move-highlight');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generatePGN(game) {
|
||||
if (!game) return '';
|
||||
|
||||
let pgn = '';
|
||||
if (game.event) pgn += `[Event "${game.event}"]\n`;
|
||||
if (game.site) pgn += `[Site "${game.site}"]\n`;
|
||||
if (game.date) pgn += `[Date "${game.date}"]\n`;
|
||||
if (game.round) pgn += `[Round "${game.round}"]\n`;
|
||||
if (game.white) pgn += `[White "${game.white}"]\n`;
|
||||
if (game.black) pgn += `[Black "${game.black}"]\n`;
|
||||
if (game.result) pgn += `[Result "${game.result}"]\n`;
|
||||
if (game.whiteElo) pgn += `[WhiteElo "${game.whiteElo}"]\n`;
|
||||
if (game.blackElo) pgn += `[BlackElo "${game.blackElo}"]\n`;
|
||||
if (game.termination) pgn += `[Termination "${game.termination}"]\n`;
|
||||
|
||||
pgn += '\n';
|
||||
|
||||
const nonResultMoves = game.moves.filter(m => !m.isResult);
|
||||
for (let i = 0; i < nonResultMoves.length; i += 2) {
|
||||
const moveNumber = Math.floor(i / 2) + 1;
|
||||
pgn += `${moveNumber}. ${nonResultMoves[i].san}`;
|
||||
if (i + 1 < nonResultMoves.length) {
|
||||
pgn += ` ${nonResultMoves[i + 1].san} `;
|
||||
}
|
||||
}
|
||||
|
||||
const resultMove = game.moves.find(m => m.isResult);
|
||||
if (resultMove) {
|
||||
pgn += ` ${resultMove.san}`;
|
||||
}
|
||||
|
||||
return pgn.trim();
|
||||
}
|
||||
|
||||
function updatePGNDisplay() {
|
||||
if (!currentGame) return;
|
||||
const text = generatePGN(currentGame);
|
||||
document.querySelectorAll('.pgn-text').forEach(el => el.textContent = text);
|
||||
}
|
||||
|
||||
function updateMovesList() {
|
||||
if (!currentGame) return;
|
||||
|
||||
const movesList = document.getElementById('moves-list');
|
||||
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;
|
||||
|
||||
const numSpan = document.createElement('span');
|
||||
numSpan.className = 'move-number';
|
||||
numSpan.textContent = `${moveNumber}.`;
|
||||
movesList.appendChild(numSpan);
|
||||
|
||||
if (laraIsWhite) {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentMoveIndex >= 0) {
|
||||
const curMove = movesList.querySelector(`[data-index="${currentMoveIndex}"]`);
|
||||
if (curMove) curMove.classList.add('current');
|
||||
}
|
||||
}
|
||||
|
||||
function updateAllGamesList() {
|
||||
const list = document.getElementById('all-games-list');
|
||||
list.innerHTML = '';
|
||||
|
||||
const sorted = [...allLaraGames].sort((a, b) => {
|
||||
return (parseInt(a.round) || 0) - (parseInt(b.round) || 0);
|
||||
});
|
||||
|
||||
for (const game of sorted) {
|
||||
const entry = document.createElement('div');
|
||||
entry.className = 'game-entry';
|
||||
if (currentGame && game === currentGame) entry.classList.add('active');
|
||||
|
||||
const laraIsWhite = game.white.toLowerCase().includes('kiesewetter');
|
||||
const opponent = laraIsWhite ? game.black : game.white;
|
||||
const color = laraIsWhite ? '⬜ Weiß' : '⬛ Schwarz';
|
||||
|
||||
let resultIcon = '';
|
||||
if (game.isLive) {
|
||||
resultIcon = '⏳';
|
||||
} else if (game.result === '1/2-1/2') {
|
||||
resultIcon = '🤝';
|
||||
} else if ((laraIsWhite && game.result === '1-0') || (!laraIsWhite && game.result === '0-1')) {
|
||||
resultIcon = '✅';
|
||||
} else {
|
||||
resultIcon = '❌';
|
||||
}
|
||||
|
||||
entry.innerHTML = `
|
||||
<div class="game-round">${resultIcon} Runde ${game.round}</div>
|
||||
<div class="game-players">Lara ${color} vs ${opponent}</div>
|
||||
<div class="game-result">${game.isLive ? '● Laufen' : game.result}</div>
|
||||
`;
|
||||
|
||||
entry.addEventListener('click', () => {
|
||||
userSelectedGame = true;
|
||||
currentGame = game;
|
||||
previousMoveCount = -1;
|
||||
currentMoveIndex = Number.MAX_SAFE_INTEGER;
|
||||
updateBoard();
|
||||
updatePlayerInfo();
|
||||
updateMovesList();
|
||||
updateAllGamesList();
|
||||
updatePGNDisplay();
|
||||
});
|
||||
|
||||
list.appendChild(entry);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user