代码: 全选
h0h6 b9c7 b0c2 c6c5 e3e4 b7b6 h6h4 c9e7 g2e3 b6c6 g3g4 a9b9 g4g5 h7i7 h4h9 g7h9 a0b0 d9e8 b2b7 e7g5 b7i7 h9i7
position fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w 1 - 0 1 moves h2e2 h9g7 h0g2 i9h9 i0h0 g6g5代码: 全选
h0h6 b9c7 b0c2 c6c5 e3e4 b7b6 h6h4 c9e7 g2e3 b6c6 g3g4 a9b9 g4g5 h7i7 h4h9 g7h9 a0b0 d9e8 b2b7 e7g5 b7i7 h9i7
position fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w 1 - 0 1 moves h2e2 h9g7 h0g2 i9h9 i0h0 g6g5代码: 全选
const text = "position fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w 1 - 0 1";
// 1. 如果只是想提取核心的局面和走子方
const match = text.match(/(?:[rnbakcpRNBAKCP1-9]+\/){9}[rnbakcpRNBAKCP1-9]+ [wb]/);
if (match) {
console.log("匹配到的局勢:", match[0]);
}
// 2. 如果要檢查一個字串是否為合法的象棋 FEN (嚴格模式)
function isValidXiangqiFen(str) {
// 這裡加上了結尾的後續參數匹配 (步數等,可選)
const strictRegex = /^(?:[rnbakcpRNBAKCP1-9]+\/){9}[rnbakcpRNBAKCP1-9]+ [wb](?: -| \d+)*$/;
return strictRegex.test(str.trim());
}代码: 全选
const fenRegex = /\b(?:[rnbakcpRNBAKCP1-9]{1,9}\/){9}[rnbakcpRNBAKCP1-9]{1,9} [wb](?:(?: -| \d+){4})?\b/;代码: 全选
movesStr = movesStr.replace(/position fen /, '').replace(fenRegex, '').replace(/ moves /, '');
代码: 全选
// 假設 fenRegex 已經定義好了
const cleanRegex = new RegExp(`position fen | moves |${fenRegex.source}`, 'g');
movesStr = movesStr.replace(cleanRegex, '').trim();代码: 全选
// Add keyboard support at the end of DOMContentLoaded
document.addEventListener('keydown', (e) => {
// Check if user is currently changing move speed or interval
const isEditingSettings = document.getElementById('interval-input') !== null || document.getElementById('speed-input') !== null;
// Determine if the key pressed is allowed during Range Selection mode
const allowedInRangeMode = e.key === 'Insert' || e.key === 'Delete';
// Global block condition for shortcuts based on the application's current state
if (isExportTextMode || isAutoPlaying || isEditingComment || isEditingSettings || (isRangeMode && !allowedInRangeMode)) {
return;
}
// Undo / Redo logic (works in both Normal and Edit modes)
if (e.ctrlKey && e.key === 'z') {
e.preventDefault();
isEditMode ? undoEdit() : undo();
return;
}
if (e.ctrlKey && e.key === 'y') {
e.preventDefault();
isEditMode ? redoEdit() : redo();
return;
}
// All subsequent navigation and action shortcuts are disabled in Edit Mode
if (isEditMode) return;
// Get max step bounds from the slider for navigation limits
const maxStep = parseInt(stepSlider.max, 10) || 0;
switch (e.key) {
case 'Home':
e.preventDefault();
jumpToStep(0);
saveStateToUndo();
updateToolHighlights();
break;
case 'End':
e.preventDefault();
jumpToStep(maxStep);
saveStateToUndo();
updateToolHighlights();
break;
case 'PageUp':
e.preventDefault();
// Move backward 5 turns (10 steps/ply)
jumpToStep(Math.max(0, currentStepIndex - 10));
saveStateToUndo();
updateToolHighlights();
break;
case 'PageDown':
e.preventDefault();
// Move forward 5 turns (10 steps/ply)
jumpToStep(Math.min(maxStep, currentStepIndex + 10));
saveStateToUndo();
updateToolHighlights();
break;
case 'ArrowUp':
case 'Up': // Handles legacy Edge/IE names
e.preventDefault();
// Move backward 1 turn (2 steps/ply)
jumpToStep(Math.max(0, currentStepIndex - 2));
saveStateToUndo();
updateToolHighlights();
break;
case 'ArrowDown':
case 'Down':
e.preventDefault();
// Move forward 1 turn (2 steps/ply)
jumpToStep(Math.min(maxStep, currentStepIndex + 2));
saveStateToUndo();
updateToolHighlights();
break;
case 'ArrowLeft':
case 'Left':
e.preventDefault();
// Move backward 1 step
jumpToStep(Math.max(0, currentStepIndex - 1));
saveStateToUndo();
updateToolHighlights();
break;
case 'ArrowRight':
case 'Right':
e.preventDefault();
// Move forward 1 step
jumpToStep(Math.min(maxStep, currentStepIndex + 1));
saveStateToUndo();
updateToolHighlights();
break;
case 'Delete':
case 'Del':
e.preventDefault();
// Triggers the deletion of the current move and the branch after it
deleteCurrentMoveAndAfter();
break;
case 'Insert':
case 'Ins':
e.preventDefault();
// Triggers the text export/import tool
document.getElementById('tool-exp-txt').click();
break;
}
});代码: 全选
// Add keyboard support at the end of DOMContentLoaded
document.addEventListener('keydown', (e) => {
const isEditingSettings = document.getElementById('interval-input') !== null || document.getElementById('speed-input') !== null;
// --- Feature: ESC key to exit various active states ---
if (e.key === 'Escape' || e.key === 'Esc') {
e.preventDefault();
// 1. Export SVG Modal
const exportModal = document.querySelector('.ejcees-export-modal');
if (exportModal) {
const closeBtn = exportModal.querySelector('.close-btn');
if (closeBtn) closeBtn.click();
return;
}
// 2. Edit Mode
if (isEditMode) {
const editCancelBtn = document.getElementById('edit-cancel');
if (editCancelBtn) editCancelBtn.click();
return;
}
// 3. Export Text Mode
if (isExportTextMode) {
const expCancelBtn = document.getElementById('exp-cancel');
if (expCancelBtn) expCancelBtn.click();
return;
}
// 4. Range Selection Mode
if (isRangeMode) {
const rangeBtn = document.getElementById('btn-range');
if (rangeBtn) rangeBtn.click();
return;
}
// 5. Interval / Speed Settings
if (isEditingSettings) {
const intervalCancel = document.getElementById('interval-cancel');
const speedCancel = document.getElementById('speed-cancel');
if (intervalCancel) intervalCancel.click();
if (speedCancel) speedCancel.click();
return;
}
// 6. Editing Comment
if (isEditingComment) {
const commentCancel = document.querySelector('.btn-cancel');
if (commentCancel) commentCancel.click();
return;
}
return;
}
// --- Feature: Ctrl + Arrow keys for branch navigation ---
if (e.ctrlKey) {
if (e.key === 'ArrowLeft' || e.key === 'Left') {
e.preventDefault();
// Jump to the previous fork point
for (let i = currentStepIndex - 1; i >= 0; i--) {
let node = getNodeAtStep(i);
if (node && node.v && node.v.length > 1) {
jumpToStep(i);
saveStateToUndo();
updateToolHighlights();
break;
}
}
return;
}
if (e.key === 'ArrowRight' || e.key === 'Right') {
e.preventDefault();
// Jump to the next fork point
const totalDepth = getPathDepth(historyFEN, currentBranch);
for (let i = currentStepIndex + 1; i < totalDepth; i++) {
let node = getNodeAtStep(i);
if (node && node.v && node.v.length > 1) {
jumpToStep(i);
saveStateToUndo();
updateToolHighlights();
break;
}
}
return;
}
if (e.key === 'ArrowUp' || e.key === 'Up' || e.key === 'ArrowDown' || e.key === 'Down') {
e.preventDefault();
const isUp = (e.key === 'ArrowUp' || e.key === 'Up');
// Find the nearest previous fork and its respective index
let forkIndex = -1;
let tempNode = historyFEN;
let currentForks = [];
for (let i = 0; i <= currentStepIndex; i++) {
if (tempNode && tempNode.v && tempNode.v.length > 1) {
forkIndex++;
currentForks.push({ step: i, forkIdx: forkIndex, node: tempNode });
}
const choice = (tempNode && tempNode.v && tempNode.v.length > 1) ? (currentBranch[forkIndex] || 0) : 0;
if (tempNode && tempNode.v && tempNode.v.length > 0) {
tempNode = tempNode.v[choice];
} else {
break;
}
}
if (currentForks.length > 0) {
let lastFork = currentForks[currentForks.length - 1];
let choicesCount = lastFork.node.v.length;
let currentChoice = currentBranch[lastFork.forkIdx] !== undefined ? currentBranch[lastFork.forkIdx] : 0;
let newChoice;
if (isUp) {
// Switch to previous branch
newChoice = (currentChoice - 1 + choicesCount) % choicesCount;
} else {
// Switch to next branch
newChoice = (currentChoice + 1) % choicesCount;
}
// Apply branch change and rerender
updateBranchPath(lastFork.forkIdx, newChoice);
renderRecordUI();
// Stay at the current depth if possible, or cap at the new branch's maximum depth
const newDepth = getPathDepth(historyFEN, currentBranch);
jumpToStep(Math.min(currentStepIndex, newDepth - 1));
saveStateToUndo();
updateToolHighlights();
}
return;
}
}
// Determine if the key pressed is allowed during Range Selection mode
const allowedInRangeMode = e.key === 'Insert' || e.key === 'Delete';
// Global block condition for shortcuts based on the application's current state
if (isExportTextMode || isAutoPlaying || isEditingComment || isEditingSettings || (isRangeMode && !allowedInRangeMode)) {
return;
}
// Undo / Redo logic
if (e.ctrlKey && e.key === 'z') {
e.preventDefault();
isEditMode ? undoEdit() : undo();
return;
}
if (e.ctrlKey && e.key === 'y') {
e.preventDefault();
isEditMode ? redoEdit() : redo();
return;
}
// Navigation and action shortcuts are disabled in Edit Mode
if (isEditMode) return;
// Get max step bounds from the slider for navigation limits
const maxStep = parseInt(stepSlider.max, 10) || 0;
switch (e.key) {
case 'Home':
e.preventDefault();
jumpToStep(0);
saveStateToUndo();
updateToolHighlights();
break;
case 'End':
e.preventDefault();
jumpToStep(maxStep);
saveStateToUndo();
updateToolHighlights();
break;
case 'PageUp':
e.preventDefault();
// Move backward 5 turns (10 steps/ply)
jumpToStep(Math.max(0, currentStepIndex - 10));
saveStateToUndo();
updateToolHighlights();
break;
case 'PageDown':
e.preventDefault();
// Move forward 5 turns (10 steps/ply)
jumpToStep(Math.min(maxStep, currentStepIndex + 10));
saveStateToUndo();
updateToolHighlights();
break;
case 'ArrowUp':
case 'Up': // Handles legacy Edge/IE names
e.preventDefault();
// Move backward 1 turn (2 steps/ply)
jumpToStep(Math.max(0, currentStepIndex - 2));
saveStateToUndo();
updateToolHighlights();
break;
case 'ArrowDown':
case 'Down':
e.preventDefault();
// Move forward 1 turn (2 steps/ply)
jumpToStep(Math.min(maxStep, currentStepIndex + 2));
saveStateToUndo();
updateToolHighlights();
break;
case 'ArrowLeft':
case 'Left':
e.preventDefault();
// Move backward 1 step
jumpToStep(Math.max(0, currentStepIndex - 1));
saveStateToUndo();
updateToolHighlights();
break;
case 'ArrowRight':
case 'Right':
e.preventDefault();
// Move forward 1 step
jumpToStep(Math.min(maxStep, currentStepIndex + 1));
saveStateToUndo();
updateToolHighlights();
break;
case 'Delete':
case 'Del':
e.preventDefault();
// Triggers the deletion of the current move and the branch after it
deleteCurrentMoveAndAfter();
break;
case 'Insert':
case 'Ins':
e.preventDefault();
// Triggers the text export/import tool
document.getElementById('tool-exp-txt').click();
break;
}
});正浏览此版面之用户: 没有注册用户 和 5 访客