Compare commits
2 Commits
84973c002c
...
0d94cac60c
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d94cac60c | |||
| 15b6f31f62 |
199
app.js
199
app.js
@@ -14,6 +14,9 @@ let allLaraGames = [];
|
|||||||
let refreshTimer = null;
|
let refreshTimer = null;
|
||||||
let countdown = 0;
|
let countdown = 0;
|
||||||
let serverLastFetch = null;
|
let serverLastFetch = null;
|
||||||
|
let laraColor = null;
|
||||||
|
let currentMoveIndex = -1;
|
||||||
|
let userSelectedGame = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt die PGN-Datei und aktualisiert die Anzeige
|
* Lädt die PGN-Datei und aktualisiert die Anzeige
|
||||||
@@ -44,11 +47,11 @@ async function loadPGN() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finde die aktuelle/live Partie
|
// Nur automatisch wechseln, wenn der Benutzer keine andere Partie ausgewählt hat
|
||||||
const liveGame = getLiveGame(allLaraGames);
|
if (!userSelectedGame) {
|
||||||
const targetGame = liveGame || getLatestGame(allLaraGames);
|
const liveGame = getLiveGame(allLaraGames);
|
||||||
|
currentGame = liveGame || getLatestGame(allLaraGames);
|
||||||
currentGame = targetGame;
|
}
|
||||||
updateBoard();
|
updateBoard();
|
||||||
updatePlayerInfo();
|
updatePlayerInfo();
|
||||||
updateMovesList();
|
updateMovesList();
|
||||||
@@ -75,19 +78,23 @@ function updateBoard() {
|
|||||||
// Spiegele das Brett, wenn Lara Schwarz hat
|
// Spiegele das Brett, wenn Lara Schwarz hat
|
||||||
const laraIsBlack = currentGame.black.toLowerCase().includes('kiesewetter');
|
const laraIsBlack = currentGame.black.toLowerCase().includes('kiesewetter');
|
||||||
const orientation = laraIsBlack ? 'black' : 'white';
|
const orientation = laraIsBlack ? 'black' : 'white';
|
||||||
|
laraColor = laraIsBlack ? 'b' : 'w';
|
||||||
|
|
||||||
// Führe alle Züge aus
|
// Führe alle Züge aus
|
||||||
for (const move of currentGame.moves) {
|
const nonResultMoves = currentGame.moves.filter(m => !m.isResult);
|
||||||
if (move.isResult) break;
|
for (const move of nonResultMoves) {
|
||||||
try {
|
try {
|
||||||
chess.move(move.san);
|
chess.move(move.san);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignoriere ungültige Züge
|
// Ignoriere ungültige Züge
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
currentMoveIndex = nonResultMoves.length - 1;
|
||||||
|
|
||||||
if (board) board.position(chess.fen(), true);
|
if (board) {
|
||||||
else {
|
board.position(chess.fen(), true);
|
||||||
|
board.orientation(orientation);
|
||||||
|
} else {
|
||||||
board = Chessboard('board', {
|
board = Chessboard('board', {
|
||||||
position: chess.fen(),
|
position: chess.fen(),
|
||||||
orientation: orientation,
|
orientation: orientation,
|
||||||
@@ -104,6 +111,38 @@ function updateBoard() {
|
|||||||
highlightActivePlayer();
|
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
|
* Aktualisiert die Spielerinformationen
|
||||||
*/
|
*/
|
||||||
@@ -112,23 +151,27 @@ function updatePlayerInfo() {
|
|||||||
|
|
||||||
const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter');
|
const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter');
|
||||||
|
|
||||||
if (laraIsWhite) {
|
const laraPlayer = laraIsWhite ? currentGame.white : currentGame.black;
|
||||||
document.getElementById('white-name').textContent = currentGame.white;
|
const laraElo = laraIsWhite ? currentGame.whiteElo : currentGame.blackElo;
|
||||||
document.getElementById('white-elo').textContent = currentGame.whiteElo ? `(ELO: ${currentGame.whiteElo})` : '';
|
const laraClock = laraIsWhite ? currentGame.whiteClock : currentGame.blackClock;
|
||||||
document.getElementById('white-clock').textContent = formatClock(currentGame.whiteClock);
|
const laraEmoji = laraIsWhite ? '⬜' : '⬛';
|
||||||
|
|
||||||
document.getElementById('black-name').textContent = currentGame.black;
|
const oppPlayer = laraIsWhite ? currentGame.black : currentGame.white;
|
||||||
document.getElementById('black-elo').textContent = currentGame.blackElo ? `(ELO: ${currentGame.blackElo})` : '';
|
const oppElo = laraIsWhite ? currentGame.blackElo : currentGame.whiteElo;
|
||||||
document.getElementById('black-clock').textContent = formatClock(currentGame.blackClock);
|
const oppClock = laraIsWhite ? currentGame.blackClock : currentGame.whiteClock;
|
||||||
} else {
|
const oppEmoji = 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;
|
// Top-Panel (player-black) = Gegner
|
||||||
document.getElementById('black-elo').textContent = currentGame.blackElo ? `(ELO: ${currentGame.blackElo})` : '';
|
document.getElementById('black-name').textContent = oppPlayer;
|
||||||
document.getElementById('black-clock').textContent = formatClock(currentGame.blackClock);
|
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
|
// Round info
|
||||||
const roundInfo = `Runde ${currentGame.round} – ${currentGame.event || 'Turnier'}`;
|
const roundInfo = `Runde ${currentGame.round} – ${currentGame.event || 'Turnier'}`;
|
||||||
@@ -147,18 +190,21 @@ function updatePlayerInfo() {
|
|||||||
* Highlight den Spieler, der gerade am Zug ist
|
* Highlight den Spieler, der gerade am Zug ist
|
||||||
*/
|
*/
|
||||||
function highlightActivePlayer() {
|
function highlightActivePlayer() {
|
||||||
if (!chess) return;
|
if (!chess || !currentGame) return;
|
||||||
|
|
||||||
const whiteEl = document.getElementById('player-white');
|
const laraPanel = document.getElementById('player-white');
|
||||||
const blackEl = document.getElementById('player-black');
|
const oppPanel = document.getElementById('player-black');
|
||||||
|
|
||||||
whiteEl.classList.remove('active');
|
laraPanel.classList.remove('active');
|
||||||
blackEl.classList.remove('active');
|
oppPanel.classList.remove('active');
|
||||||
|
|
||||||
if (chess.turn() === 'w') {
|
const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter');
|
||||||
whiteEl.classList.add('active');
|
const isLaraTurn = (chess.turn() === 'w' && laraIsWhite) || (chess.turn() === 'b' && !laraIsWhite);
|
||||||
|
|
||||||
|
if (isLaraTurn) {
|
||||||
|
laraPanel.classList.add('active');
|
||||||
} else {
|
} else {
|
||||||
blackEl.classList.add('active');
|
oppPanel.classList.add('active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,37 +218,53 @@ function updateMovesList() {
|
|||||||
movesList.innerHTML = '';
|
movesList.innerHTML = '';
|
||||||
|
|
||||||
const nonResultMoves = currentGame.moves.filter(m => !m.isResult);
|
const nonResultMoves = currentGame.moves.filter(m => !m.isResult);
|
||||||
|
const laraIsWhite = currentGame.white.toLowerCase().includes('kiesewetter');
|
||||||
|
|
||||||
for (let i = 0; i < nonResultMoves.length; i += 2) {
|
for (let i = 0; i < nonResultMoves.length; i += 2) {
|
||||||
const moveNumber = Math.floor(i / 2) + 1;
|
const moveNumber = Math.floor(i / 2) + 1;
|
||||||
|
|
||||||
// Move number
|
|
||||||
const numSpan = document.createElement('span');
|
const numSpan = document.createElement('span');
|
||||||
numSpan.className = 'move-number';
|
numSpan.className = 'move-number';
|
||||||
numSpan.textContent = `${moveNumber}.`;
|
numSpan.textContent = `${moveNumber}.`;
|
||||||
movesList.appendChild(numSpan);
|
movesList.appendChild(numSpan);
|
||||||
|
|
||||||
// White move
|
if (laraIsWhite) {
|
||||||
const whiteMove = document.createElement('span');
|
// Lara (Weiß) zuerst
|
||||||
whiteMove.className = 'move';
|
const laraMove = document.createElement('span');
|
||||||
whiteMove.textContent = nonResultMoves[i].san;
|
laraMove.className = 'move lara-move';
|
||||||
whiteMove.dataset.index = i;
|
laraMove.textContent = nonResultMoves[i].san;
|
||||||
movesList.appendChild(whiteMove);
|
laraMove.dataset.index = i;
|
||||||
|
movesList.appendChild(laraMove);
|
||||||
|
|
||||||
// Black move
|
if (i + 1 < nonResultMoves.length) {
|
||||||
if (i + 1 < nonResultMoves.length) {
|
const oppMove = document.createElement('span');
|
||||||
const blackMove = document.createElement('span');
|
oppMove.className = 'move opp-move';
|
||||||
blackMove.className = 'move';
|
oppMove.textContent = nonResultMoves[i + 1].san;
|
||||||
blackMove.textContent = nonResultMoves[i + 1].san;
|
oppMove.dataset.index = i + 1;
|
||||||
blackMove.dataset.index = i + 1;
|
movesList.appendChild(oppMove);
|
||||||
movesList.appendChild(blackMove);
|
}
|
||||||
|
} 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
|
// Mark current move
|
||||||
if (nonResultMoves.length > 0) {
|
if (currentMoveIndex >= 0) {
|
||||||
const lastMove = movesList.querySelector(`[data-index="${nonResultMoves.length - 1}"]`);
|
const curMove = movesList.querySelector(`[data-index="${currentMoveIndex}"]`);
|
||||||
if (lastMove) lastMove.classList.add('current');
|
if (curMove) curMove.classList.add('current');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll to bottom
|
// Scroll to bottom
|
||||||
@@ -214,24 +276,7 @@ function updateMovesList() {
|
|||||||
*/
|
*/
|
||||||
function handleMoveClick(e) {
|
function handleMoveClick(e) {
|
||||||
if (!e.target.classList.contains('move') || !currentGame) return;
|
if (!e.target.classList.contains('move') || !currentGame) return;
|
||||||
|
goToMove(parseInt(e.target.dataset.index));
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -262,6 +307,7 @@ function updateAllGamesList() {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
entry.addEventListener('click', () => {
|
entry.addEventListener('click', () => {
|
||||||
|
userSelectedGame = true;
|
||||||
currentGame = game;
|
currentGame = game;
|
||||||
updateBoard();
|
updateBoard();
|
||||||
updatePlayerInfo();
|
updatePlayerInfo();
|
||||||
@@ -337,6 +383,23 @@ document.getElementById('refresh-btn').addEventListener('click', () => {
|
|||||||
loadPGN();
|
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
|
* Init
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -66,21 +66,43 @@ function parseGameBlock(block) {
|
|||||||
function parseMoves(movesText) {
|
function parseMoves(movesText) {
|
||||||
const moves = [];
|
const moves = [];
|
||||||
|
|
||||||
// Remove comments in curly braces
|
let clockWhite = null;
|
||||||
movesText = movesText.replace(/\{[^}]*\}/g, '');
|
let clockBlack = null;
|
||||||
|
let color = 'w';
|
||||||
|
|
||||||
// Remove move numbers
|
const tokenRegex = /\d+\.\s*|\{[^}]*\}|\S+/g;
|
||||||
movesText = movesText.replace(/\d+\.\s*/g, '');
|
const tokens = movesText.match(tokenRegex) || [];
|
||||||
|
|
||||||
// Split into individual moves
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
const tokens = movesText.split(/\s+/).filter(t => t.trim());
|
let token = tokens[i].trim();
|
||||||
|
|
||||||
for (const token of tokens) {
|
|
||||||
if (['1-0', '0-1', '1/2-1/2', '*'].includes(token)) {
|
if (['1-0', '0-1', '1/2-1/2', '*'].includes(token)) {
|
||||||
moves.push({ san: token, isResult: true });
|
moves.push({ san: token, isResult: true, whiteClock: clockWhite, blackClock: clockBlack });
|
||||||
} else if (token.length > 0) {
|
continue;
|
||||||
moves.push({ san: token, isResult: false });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (/^\d+\.$/.test(token)) continue;
|
||||||
|
|
||||||
|
if (token.startsWith('{')) {
|
||||||
|
const clkMatch = token.match(/\[%clk\s+([\d:]+)\]/);
|
||||||
|
if (clkMatch && moves.length > 0) {
|
||||||
|
const lastMove = moves[moves.length - 1];
|
||||||
|
if (!lastMove.isResult && lastMove.color) {
|
||||||
|
if (lastMove.color === 'w') {
|
||||||
|
clockWhite = clkMatch[1];
|
||||||
|
} else {
|
||||||
|
clockBlack = clkMatch[1];
|
||||||
|
}
|
||||||
|
lastMove.whiteClock = clockWhite;
|
||||||
|
lastMove.blackClock = clockBlack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const move = { san: token, isResult: false, whiteClock: clockWhite, blackClock: clockBlack, color };
|
||||||
|
moves.push(move);
|
||||||
|
color = color === 'w' ? 'b' : 'w';
|
||||||
}
|
}
|
||||||
|
|
||||||
return moves;
|
return moves;
|
||||||
|
|||||||
Reference in New Issue
Block a user