將製作ejcees(中國象棋打譜程式)
Re: 將製作ejcees(中國象棋打譜程式)
當url切換時,如果加有?url=xxx,則以xxx為域名前綴,否則去掉#前面的字符。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
改個小bug,在導入時如果把rotate寫到fen中,則rotate。
- 附件
-
ejcees20260405importFenRotate99977747.7z- (44.79 KiB) 已下载 6 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
然後我把導出svg動畫的方法也做成了一個獨立的js。
https://ejsoon.vip/wp-content/uploads/2 ... 4051957.js
- 附件
-
ejceeseExpSvg202604051957.7z- (20.78 KiB) 已下载 4 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
<script>
function handleConvert() {
const text = document.getElementById('inputText').value;
const moveInterval = document.getElementById('inputInterval').value;
const moveSpeed = document.getElementById('inputSpeed').value;
const container = document.getElementById('svg-container');
const resultSvg = ejceesExpSvg(text, moveInterval, moveSpeed);
container.innerHTML = resultSvg;
}
</script>https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
function handleConvert() {
const text = document.getElementById('inputText').value;
const moveInterval = document.getElementById('inputInterval').value;
const moveSpeed = document.getElementById('inputSpeed').value;
const container = document.getElementById('svg-container');
// 1. 生成 SVG 內容
const resultSvg = ejceesExpSvg(text, moveInterval, moveSpeed);
container.innerHTML = resultSvg;
// 2. 獲取或創建下載連結元素
let downloadLink = document.getElementById('svg-download-link');
if (!downloadLink) {
downloadLink = document.createElement('a');
downloadLink.id = 'svg-download-link';
downloadLink.style.display = 'block';
downloadLink.style.marginTop = '10px';
container.parentNode.insertBefore(downloadLink, container.nextSibling);
}
// 3. 處理檔案內容與大小
const blob = new Blob([resultSvg], { type: 'image/svg+xml' });
const fileSize = blob.size;
const url = URL.createObjectURL(blob);
// 4. 生成檔名 (yyyymmdd_hhiiss)
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
const timestamp = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_` +
`${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
const fileName = `ejcees_svg_${timestamp}.svg`;
// 5. 更新連結屬性
downloadLink.href = url;
downloadLink.download = fileName;
downloadLink.innerText = `download svg (size: ${fileSize} bytes)`;
}https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
const svgElement = container.querySelector('svg');
if (svgElement && typeof svgElement.setCurrentTime === 'function') {
svgElement.setCurrentTime(0);
}https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
一,當點擊tool-exp-txt時,進入「導出文本」狀態,右邊的四個按鈕,當其中一個處於active狀態時,再點擊一次都將能夠切換內容。
二,當切換內容時,它的背景色將變為橙色。再點擊則變回原來的顏色。程式將記住當前的狀態。
三,當第二個按鈕en在active狀態時被點擊時,它的表現跟cn一樣,即切換加評論的版本。
四,當第四個按鈕urlParameter在active狀態時被點擊時,它會切換為「url#...」。
url有三種來源:一是當頁面打開時如果有?url=value(value要經encodeURIComponent()編碼)則設為此值;二是當confirm時,如果文本中含有#且#後面沒有字符,則#前面的字符將成為url的值;三是當confirm時,如果文本中含有#且後面有字符,若#前面的字符跟當前的域名不同,則它將成為url的值。
五,當處於「導出文本」狀態時,在下方ejceestextbtn內的confirm和cancle中間新增兩個按鈕:複製和粘貼,分別加上合適的svg圖標。
複製和粘貼要用到clipboard API。這兩個按鈕在點擊後,退出「導出文本」狀態。複製就會把文本內容複製到剪貼板並cancle,粘貼就會把當前剪貼板的內容貼上並confirm。
六,在粘貼的右方,加上一個stick圖標。它在「導出文本」狀態時,是「固定」與「不固定」的切換。初始是不固定。
當點擊confirm、複製或粘貼時,如果它處於固定狀態,則當退出「導出文本」狀態時,ejceestextbtn內的五個button的最後兩個(moveInterval和moveSpeed)將消失,用「粘貼」和「取消固定」替代。
當點擊「取消固定」時,「粘貼」和「取消固定」消失,moveInterval和moveSpeed回復原來位置。
七,只需給出所要修改的地方,所有的代碼都要使用英文。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
Here are the modifications required to fulfill your requests. Please apply these changes to the appropriate sections in your code.
### 1. Update CSS
Add the following CSS rule inside your `<style>` block to handle the orange background for the toggled state:
```css
.exp-fmt-btn.active[data-odd="1"] {
background: orange;
}
```
### 2. Global Variables and URL Initialization
Add the new state variables at the beginning of your `<script>` tag, right after your existing variable declarations. Also, capture the initial URL parameter inside the `DOMContentLoaded` event:
```javascript
let isEditMode = false;
let editUndoStack = [];
let editRedoStack = [];
let selectedPalettePiece = null;
let isTile0Selected = true;
let editMiniBoardState = 0;
let preEditFEN = '';
let editPickedPieceId = null;
// --- NEW GLOBAL VARIABLES ---
let baseUrl = "";
let formatOddStates = { cn: 0, en: 0, engine: 0, url: 0 };
let isExportPinned = false;
// ----------------------------
// AutoPlay & Range states
// ... (Keep existing states)
document.addEventListener('DOMContentLoaded', () => {
// --- NEW INITIALIZATION ---
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('url')) {
baseUrl = decodeURIComponent(urlParams.get('url'));
}
// --------------------------
const svgElement = document.querySelector('.ejceespb');
```
### 3. Replace the Notation Generator Function
Replace your old `generateCnTextWidthComment` function completely with this combined function that handles both Chinese and English notation with comments:
```javascript
function generateTextWithComment(isEnglish = false) {
const pathRoot = getGamePath();
const fen = pathRoot.fen;
let text = fen + '\n';
if (pathRoot.c && pathRoot.c.trim() !== '') {
text += pathRoot.c.trim() + '\n';
}
let isRedTurn = fen.split(' ')[1] === 'w';
let moveNum = 1;
let currentNode = pathRoot;
let needsEllipsis = !isRedTurn;
while (currentNode.v && currentNode.v.length > 0) {
currentNode = currentNode.v[0];
const moveStr = isEnglish ?
currentNode.move :
NotationConverter.toChinese(currentNode.move);
if (isRedTurn) {
text += `${moveNum}. ${moveStr}`;
if (currentNode.c && currentNode.c.trim() !== '') {
text += `\n${currentNode.c.trim()}\n`;
needsEllipsis = true;
} else {
text += " ";
needsEllipsis = false;
}
isRedTurn = false;
} else {
if (needsEllipsis) {
text += `${moveNum}. ... ${moveStr}\n`;
} else {
text += `${moveStr}\n`;
}
if (currentNode.c && currentNode.c.trim() !== '') {
text += `${currentNode.c.trim()}\n`;
}
moveNum++;
isRedTurn = true;
needsEllipsis = false;
}
}
return text.trim();
}
```
### 4. Update the `generateUrlHash` Function
Modify the existing `generateUrlHash` to accept an `isToggled` argument and use the `baseUrl`:
```javascript
function generateUrlHash(isToggled = false) {
let hashStr = encodeUrlParam(historyFEN, true);
hashStr = hashStr.replace(/\$+$/, '');
let currentDomain = window.location.origin + window.location.pathname;
let prefix = currentDomain;
if (isToggled) {
prefix = baseUrl ? baseUrl : "url";
}
return prefix + "#" + hashStr;
}
```
### 5. Update the `importExportedText` Function
Add the URL detection logic at the very beginning of the `importExportedText` function:
```javascript
function importExportedText(text, importedFen = null) {
text = text.trim();
if (!text) {
renderRecordUI();
renderNoteUI();
updateToolHighlights();
return;
}
// --- NEW URL PARSING LOGIC ---
if (text.includes('#')) {
let parts = text.split('#');
let beforeHash = parts[0].trim();
let afterHash = parts.length > 1 ? parts[1].trim() : "";
if (afterHash === "") {
baseUrl = beforeHash;
} else {
let currentDomain = window.location.origin + window.location.pathname;
if (beforeHash !== "" && beforeHash !== currentDomain) {
baseUrl = beforeHash;
}
}
}
// -----------------------------
// Normalize Chinese notations
text = text.replace(/车/g, '車').replace(/马/g, '馬').replace(/帅/g, '帥').replace(/将/g, '將').replace(/后/g, '後').replace(/进/g, '進');
// ... (Keep the rest of the function the same)
```
### 6. Replace `renderExportTextUI`
Overwrite your entire `renderExportTextUI` function to include the new buttons, clipboards, pinning states, and toggle states:
```javascript
function renderExportTextUI() {
const recordContainer = document.querySelector('.ejceesrecord');
const commentDiv = document.querySelector('.ejceescomment');
const btnDiv = document.querySelector('.ejceestextbtn');
const cnText = generateExportText(false);
const cnTextWidthComment = generateTextWithComment(false);
const enText = generateExportText(true);
const enTextWidthComment = generateTextWithComment(true);
const urlText = generateUrlHash(false);
const urlTextToggled = generateUrlHash(true);
// Generate UCCI Engine Format String
let path = getGamePath();
let engineText = "position fen " + path.fen;
let currNode = path;
if (currNode && currNode.v && currNode.v.length > 0) {
engineText += " moves";
}
while (currNode && currNode.v && currNode.v.length > 0) {
currNode = currNode.v[0];
let dc = currNode.lastMove;
if (dc) {
engineText += " " + String.fromCharCode(97 + dc.startX) + (9 - dc.startY) + String.fromCharCode(97 + dc.endX) + (9 - dc.endY);
}
}
recordContainer.innerHTML = `<textarea class="ejceescomment-edit" id="export-textarea" style="width:100%; height:100%; resize:none; border:none; outline:none; background:#2a2a2a; color:#fff; padding:8px; font-family:monospace; font-size:14px; white-space:pre-wrap; overflow:auto;"></textarea>`;
const textarea = document.getElementById('export-textarea');
commentDiv.innerHTML = `<div class="exp-fmt-outer">
<div class="exp-fmt-btn ${lastExportFormat === 'cn' ? 'active' : ''}" data-fmt="cn" data-odd="${formatOddStates['cn']}">Chinese Notation</div>
<div class="exp-fmt-btn ${lastExportFormat === 'en' ? 'active' : ''}" data-fmt="en" data-odd="${formatOddStates['en']}">English Notation</div>
<div class="exp-fmt-btn ${lastExportFormat === 'engine' ? 'active' : ''}" data-fmt="engine" data-odd="${formatOddStates['engine']}">Engine Format</div>
<div class="exp-fmt-btn ${lastExportFormat === 'url' ? 'active' : ''}" data-fmt="url" data-odd="${formatOddStates['url']}">URL Parameter</div>
</div>`;
const formatBtns = commentDiv.querySelectorAll('.exp-fmt-btn');
const updateTextarea = (fmt) => {
if (fmt === 'cn') {
textarea.value = formatOddStates[fmt] === 1 ? cnTextWidthComment : cnText;
} else if (fmt === 'en') {
textarea.value = formatOddStates[fmt] === 1 ? enTextWidthComment : enText;
} else if (fmt === 'engine') {
if (formatOddStates[fmt] === 1) {
const isRed = currentTurn === 'w';
const moves = getAllLegalMoves(isRed, tileMap);
const searchMovesStr = moves.map(m => String.fromCharCode(97 + m.startX) + (9 - m.startY) + String.fromCharCode(97 + m.endX) + (9 - m.endY)).join(' ');
textarea.value = engineText + (searchMovesStr ? "\ngo searchmoves " + searchMovesStr : "");
} else {
textarea.value = engineText;
}
} else if (fmt === 'url') {
textarea.value = formatOddStates[fmt] === 1 ? urlTextToggled : urlText;
}
};
formatBtns.forEach(btn => {
btn.addEventListener('click', () => {
const wasActive = btn.classList.contains('active');
const fmt = btn.getAttribute('data-fmt');
lastExportFormat = fmt;
formatBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
if (wasActive) {
formatOddStates[fmt] = formatOddStates[fmt] === 1 ? 0 : 1;
}
btn.setAttribute('data-odd', formatOddStates[fmt]);
updateTextarea(fmt);
textarea.blur();
setTimeout(() => {
textarea.focus();
textarea.select();
}, 12);
});
});
updateTextarea(lastExportFormat);
setTimeout(() => textarea.select(), 10);
btnDiv.innerHTML = `
<div class="ejceestextbtninner">
<div class="note-btn btn-confirm" id="exp-confirm" title="Confirm">
<svg viewBox="0 0 24 24"><path fill="#4CAF50" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
</div>
<div class="note-btn btn-copy" id="exp-copy" title="Copy">
<svg viewBox="0 0 24 24"><path fill="#fff" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
</div>
<div class="note-btn btn-paste" id="exp-paste" title="Paste">
<svg viewBox="0 0 24 24"><path fill="#fff" d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"/></svg>
</div>
<div class="note-btn btn-pin" id="exp-pin" title="Pin" style="background-color: ${isExportPinned ? '#4CAF50' : '#555'};">
<svg viewBox="0 0 24 24"><path fill="#fff" d="M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z"/></svg>
</div>
<div class="note-btn btn-cancel" id="exp-cancel" title="Cancel">
<svg viewBox="0 0 24 24"><path fill="#F44336" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</div>
</div>
`;
document.getElementById('exp-cancel').addEventListener('click', () => {
isExportTextMode = false;
renderRecordUI();
renderNoteUI();
updateToolHighlights();
});
document.getElementById('exp-confirm').addEventListener('click', () => {
const text = textarea.value;
isExportTextMode = false;
importExportedText(text);
});
document.getElementById('exp-copy').addEventListener('click', () => {
navigator.clipboard.writeText(textarea.value).then(() => {
document.getElementById('exp-cancel').click();
}).catch(err => {
console.error('Copy failed', err);
document.getElementById('exp-cancel').click();
});
});
document.getElementById('exp-paste').addEventListener('click', () => {
navigator.clipboard.readText().then(text => {
textarea.value = text;
document.getElementById('exp-confirm').click();
}).catch(err => {
console.error('Paste failed', err);
});
});
document.getElementById('exp-pin').addEventListener('click', (e) => {
isExportPinned = !isExportPinned;
e.currentTarget.style.backgroundColor = isExportPinned ? '#4CAF50' : '#555';
});
}
```
### 7. Modify `renderNoteUI`
Find the `} else {` block in your `renderNoteUI` function (where it generates the buttons like `btn-del-move`, `btn-range`, `btn-interval`, etc.). Replace that specific block with the following logic:
```javascript
} else {
commentDiv.innerHTML = '';
commentDiv.textContent = node.c || '';
let extraBtns = '';
if (isExportPinned) {
extraBtns = `
<div class="note-btn" id="btn-pinned-paste" title="Paste">
<svg viewBox="0 0 24 24"><path fill="#fff" d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"/></svg>
</div>
<div class="note-btn" id="btn-unpin" title="Unpin">
<svg viewBox="0 0 24 24"><path fill="#F44336" d="M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z"/></svg>
</div>
`;
} else {
extraBtns = `
<div class="note-btn" id="btn-interval" title="Move Interval" style="width: auto; padding: 0 4px; font-weight: bold;">
${moveInterval}s
</div>
<div class="note-btn" id="btn-speed" title="Move Speed" style="width: auto; padding: 0 4px; font-weight: bold;">
${moveSpeed}s
</div>
`;
}
btnDiv.innerHTML = `
<div class="ejceestextbtninner">
<div class="note-btn" id="btn-del-move" title="Delete Move">
<svg viewBox="0 0 24 24"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
</div>
<div class="note-btn" id="btn-range" title="Range Selection" style="${isRangeMode ? 'background-color:#4CAF50;' : ''}">
<svg viewBox="0 0 24 24"><path fill="${isRangeMode ? '#fff' : '#ccc'}" d="M7 6h4v2H9v8h2v2H7V6zm10 0h-4v2h2v8h-2v2h4V6z"/></svg>
</div>
<div class="note-btn" id="btn-autoplay" title="Auto Play">
${isAutoPlaying
? '<svg viewBox="0 0 24 24"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>'
: '<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>'
}
</div>
${extraBtns}
</div>
`;
document.getElementById('btn-autoplay').addEventListener('click', () => {
if (isAutoPlaying) {
stopAutoPlay();
} else {
startAutoPlay();
}
});
document.getElementById('btn-del-move').addEventListener('click', deleteCurrentMoveAndAfter);
document.getElementById('btn-range').addEventListener('click', () => {
isRangeMode = !isRangeMode;
if (!isRangeMode) {
rangeStart = null;
rangeEnd = null;
rangeClicks = 0;
clearRangeBorders();
} else {
rangeStart = null;
rangeEnd = null;
rangeClicks = 0;
clearRangeBorders();
}
renderNoteUI();
});
if (isExportPinned) {
document.getElementById('btn-pinned-paste').addEventListener('click', () => {
navigator.clipboard.readText().then(text => {
importExportedText(text);
}).catch(err => {
console.error('Paste failed', err);
});
});
document.getElementById('btn-unpin').addEventListener('click', () => {
isExportPinned = false;
renderNoteUI();
});
} else {
document.getElementById('btn-interval').addEventListener('click', () => {
if (isAutoPlaying) return;
btnDiv.innerHTML = `
<div class="ejceestextbtninner" style="justify-content: flex-start; gap: 4px;">
<input type="text" id="interval-input" style="width: 36px; font-size: 12px; background: #222; color: #fff; border: 1px solid #555; text-align: center; outline: none;" value="${moveInterval}" maxlength="3">
<div class="note-btn interval-opt" data-val="1">1</div>
<div class="note-btn interval-opt" data-val="2">2</div>
<div class="note-btn interval-opt" data-val="3">3</div>
<div class="note-btn btn-confirm" id="interval-confirm" title="Confirm">
<svg viewBox="0 0 24 24"><path fill="#4CAF50" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
</div>
<div class="note-btn btn-cancel" id="interval-cancel" title="Cancel">
<svg viewBox="0 0 24 24"><path fill="#F44336" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</div>
</div>
`;
document.querySelectorAll('.interval-opt').forEach(btn => {
btn.addEventListener('click', (e) => {
moveInterval = parseFloat(e.target.getAttribute('data-val'));
renderNoteUI();
saveStateToUndo();
updateToolHighlights();
});
});
document.getElementById('interval-confirm').addEventListener('click', () => {
let val = parseFloat(document.getElementById('interval-input').value);
if (isNaN(val) || val < 0.2 || val > 9) {
val = moveInterval;
}
moveInterval = parseFloat(val.toFixed(1));
renderNoteUI();
saveStateToUndo();
updateToolHighlights();
});
document.getElementById('interval-cancel').addEventListener('click', () => {
renderNoteUI();
});
});
document.getElementById('btn-speed').addEventListener('click', () => {
if (isAutoPlaying) return;
btnDiv.innerHTML = `
<div class="ejceestextbtninner" style="justify-content: flex-start; gap: 4px;">
<input type="text" id="speed-input" style="width: 36px; font-size: 12px; background: #222; color: #fff; border: 1px solid #555; text-align: center; outline: none;" value="${moveSpeed}" maxlength="3">
<div class="note-btn speed-opt" data-val="0.2">0.2</div>
<div class="note-btn speed-opt" data-val="0.4">0.4</div>
<div class="note-btn speed-opt" data-val="0.6">0.6</div>
<div class="note-btn btn-confirm" id="speed-confirm" title="Confirm">
<svg viewBox="0 0 24 24"><path fill="#4CAF50" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
</div>
<div class="note-btn btn-cancel" id="speed-cancel" title="Cancel">
<svg viewBox="0 0 24 24"><path fill="#F44336" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</div>
</div>
`;
document.querySelectorAll('.speed-opt').forEach(btn => {
btn.addEventListener('click', (e) => {
moveSpeed = parseFloat(e.target.getAttribute('data-val'));
renderNoteUI();
saveStateToUndo();
updateToolHighlights();
});
});
document.getElementById('speed-confirm').addEventListener('click', () => {
let val = parseFloat(document.getElementById('speed-input').value);
if (isNaN(val) || val < 0.1 || val > moveInterval) {
val = moveSpeed;
}
moveSpeed = parseFloat(val.toFixed(1));
renderNoteUI();
saveStateToUndo();
updateToolHighlights();
});
document.getElementById('speed-cancel').addEventListener('click', () => {
renderNoteUI();
});
});
}
}
```https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
當按複製時,它將複製所要導出的文本。
只需給出所要修改的地方,所有的代碼和注釋都要使用英文。
代码: 全选
Here is the updated modification for the `renderNoteUI` function. Replace the `} else {` block (the one generating the default UI buttons for deleting, range, etc.) with the following code.
It conditionally replaces the Auto Play button with a Copy button when pinned, and dynamically generates the exact text string based on your currently saved export settings when clicked.
### Modify `renderNoteUI`
Replace the `} else {` block inside `renderNoteUI` with this:
```javascript
} else {
commentDiv.innerHTML = '';
commentDiv.textContent = node.c || '';
let extraBtns = '';
let autoPlayOrCopyBtn = '';
if (isExportPinned) {
autoPlayOrCopyBtn = `
<div class="note-btn" id="btn-pinned-copy" title="Copy">
<svg viewBox="0 0 24 24"><path fill="#fff" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
</div>
`;
extraBtns = `
<div class="note-btn" id="btn-pinned-paste" title="Paste">
<svg viewBox="0 0 24 24"><path fill="#fff" d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"/></svg>
</div>
<div class="note-btn" id="btn-unpin" title="Unpin">
<svg viewBox="0 0 24 24"><path fill="#F44336" d="M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z"/></svg>
</div>
`;
} else {
autoPlayOrCopyBtn = `
<div class="note-btn" id="btn-autoplay" title="Auto Play">
${isAutoPlaying
? '<svg viewBox="0 0 24 24"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>'
: '<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>'
}
</div>
`;
extraBtns = `
<div class="note-btn" id="btn-interval" title="Move Interval" style="width: auto; padding: 0 4px; font-weight: bold;">
${moveInterval}s
</div>
<div class="note-btn" id="btn-speed" title="Move Speed" style="width: auto; padding: 0 4px; font-weight: bold;">
${moveSpeed}s
</div>
`;
}
btnDiv.innerHTML = `
<div class="ejceestextbtninner">
<div class="note-btn" id="btn-del-move" title="Delete Move">
<svg viewBox="0 0 24 24"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
</div>
<div class="note-btn" id="btn-range" title="Range Selection" style="${isRangeMode ? 'background-color:#4CAF50;' : ''}">
<svg viewBox="0 0 24 24"><path fill="${isRangeMode ? '#fff' : '#ccc'}" d="M7 6h4v2H9v8h2v2H7V6zm10 0h-4v2h2v8h-2v2h4V6z"/></svg>
</div>
${autoPlayOrCopyBtn}
${extraBtns}
</div>
`;
document.getElementById('btn-del-move').addEventListener('click', deleteCurrentMoveAndAfter);
document.getElementById('btn-range').addEventListener('click', () => {
isRangeMode = !isRangeMode;
if (!isRangeMode) {
rangeStart = null;
rangeEnd = null;
rangeClicks = 0;
clearRangeBorders();
} else {
rangeStart = null;
rangeEnd = null;
rangeClicks = 0;
clearRangeBorders();
}
renderNoteUI();
});
if (isExportPinned) {
document.getElementById('btn-pinned-copy').addEventListener('click', () => {
let textToCopy = "";
const fmt = lastExportFormat || 'cn';
const odd = formatOddStates[fmt] || 0;
if (fmt === 'cn') {
textToCopy = odd === 1 ? generateTextWithComment(false) : generateExportText(false);
} else if (fmt === 'en') {
textToCopy = odd === 1 ? generateTextWithComment(true) : generateExportText(true);
} else if (fmt === 'engine') {
let path = getGamePath();
let engineText = "position fen " + path.fen;
let currNode = path;
if (currNode && currNode.v && currNode.v.length > 0) {
engineText += " moves";
}
while (currNode && currNode.v && currNode.v.length > 0) {
currNode = currNode.v[0];
let dc = currNode.lastMove;
if (dc) {
engineText += " " + String.fromCharCode(97 + dc.startX) + (9 - dc.startY) + String.fromCharCode(97 + dc.endX) + (9 - dc.endY);
}
}
if (odd === 1) {
const isRed = currentTurn === 'w';
const moves = getAllLegalMoves(isRed, tileMap);
const searchMovesStr = moves.map(m => String.fromCharCode(97 + m.startX) + (9 - m.startY) + String.fromCharCode(97 + m.endX) + (9 - m.endY)).join(' ');
textToCopy = engineText + (searchMovesStr ? "\ngo searchmoves " + searchMovesStr : "");
} else {
textToCopy = engineText;
}
} else if (fmt === 'url') {
textToCopy = generateUrlHash(odd === 1);
}
navigator.clipboard.writeText(textToCopy).catch(err => {
console.error('Copy failed', err);
});
});
document.getElementById('btn-pinned-paste').addEventListener('click', () => {
navigator.clipboard.readText().then(text => {
importExportedText(text);
}).catch(err => {
console.error('Paste failed', err);
});
});
document.getElementById('btn-unpin').addEventListener('click', () => {
isExportPinned = false;
renderNoteUI();
});
} else {
document.getElementById('btn-autoplay').addEventListener('click', () => {
if (isAutoPlaying) {
stopAutoPlay();
} else {
startAutoPlay();
}
});
document.getElementById('btn-interval').addEventListener('click', () => {
if (isAutoPlaying) return;
btnDiv.innerHTML = `
<div class="ejceestextbtninner" style="justify-content: flex-start; gap: 4px;">
<input type="text" id="interval-input" style="width: 36px; font-size: 12px; background: #222; color: #fff; border: 1px solid #555; text-align: center; outline: none;" value="${moveInterval}" maxlength="3">
<div class="note-btn interval-opt" data-val="1">1</div>
<div class="note-btn interval-opt" data-val="2">2</div>
<div class="note-btn interval-opt" data-val="3">3</div>
<div class="note-btn btn-confirm" id="interval-confirm" title="Confirm">
<svg viewBox="0 0 24 24"><path fill="#4CAF50" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
</div>
<div class="note-btn btn-cancel" id="interval-cancel" title="Cancel">
<svg viewBox="0 0 24 24"><path fill="#F44336" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</div>
</div>
`;
document.querySelectorAll('.interval-opt').forEach(btn => {
btn.addEventListener('click', (e) => {
moveInterval = parseFloat(e.target.getAttribute('data-val'));
renderNoteUI();
saveStateToUndo();
updateToolHighlights();
});
});
document.getElementById('interval-confirm').addEventListener('click', () => {
let val = parseFloat(document.getElementById('interval-input').value);
if (isNaN(val) || val < 0.2 || val > 9) {
val = moveInterval;
}
moveInterval = parseFloat(val.toFixed(1));
renderNoteUI();
saveStateToUndo();
updateToolHighlights();
});
document.getElementById('interval-cancel').addEventListener('click', () => {
renderNoteUI();
});
});
document.getElementById('btn-speed').addEventListener('click', () => {
if (isAutoPlaying) return;
btnDiv.innerHTML = `
<div class="ejceestextbtninner" style="justify-content: flex-start; gap: 4px;">
<input type="text" id="speed-input" style="width: 36px; font-size: 12px; background: #222; color: #fff; border: 1px solid #555; text-align: center; outline: none;" value="${moveSpeed}" maxlength="3">
<div class="note-btn speed-opt" data-val="0.2">0.2</div>
<div class="note-btn speed-opt" data-val="0.4">0.4</div>
<div class="note-btn speed-opt" data-val="0.6">0.6</div>
<div class="note-btn btn-confirm" id="speed-confirm" title="Confirm">
<svg viewBox="0 0 24 24"><path fill="#4CAF50" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
</div>
<div class="note-btn btn-cancel" id="speed-cancel" title="Cancel">
<svg viewBox="0 0 24 24"><path fill="#F44336" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</div>
</div>
`;
document.querySelectorAll('.speed-opt').forEach(btn => {
btn.addEventListener('click', (e) => {
moveSpeed = parseFloat(e.target.getAttribute('data-val'));
renderNoteUI();
saveStateToUndo();
updateToolHighlights();
});
});
document.getElementById('speed-confirm').addEventListener('click', () => {
let val = parseFloat(document.getElementById('speed-input').value);
if (isNaN(val) || val < 0.1 || val > moveInterval) {
val = moveSpeed;
}
moveSpeed = parseFloat(val.toFixed(1));
renderNoteUI();
saveStateToUndo();
updateToolHighlights();
});
document.getElementById('speed-cancel').addEventListener('click', () => {
renderNoteUI();
});
});
}
}
```https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
當點擊confirm時,如果字符的最前面是「go 」,則記下後方的字符。則下回第一次點擊engine,它將在原來的字符加上「\ngo 記下的字符\n」。
比如當我輸入「go movetime 12000」,點擊confirm,則下回我進入導出文本狀態時,第一次點擊engine,出現的字符是「...\ngo movetime 12000\n」。
第二次點擊engine,之前它是加上了「\ngo searchmoves ...」,現在首先要改成加上「\ngo searchmoves ...\n」,其次當前面的「go 」後字符有值,則加到go的後面。比如「\ngo movetime 12000 searchmoves ...\n」
只需給出所要修改的地方,所有的代碼和注釋都要使用英文。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
Here are the necessary modifications to implement the 3-state engine formatting and the `go ` command memory feature. Please update the corresponding sections in your code.
### 1. CSS Update
Update your CSS to apply the orange background for both toggled states (`data-odd="1"` and `data-odd="2"`).
```css
.exp-fmt-btn.active[data-odd="1"],
.exp-fmt-btn.active[data-odd="2"] {
background: orange;
}
```
### 2. Global Variable
Add `savedGoCommand` near the top of your `<script>` where your other new global variables are.
```javascript
// --- NEW GLOBAL VARIABLES ---
let baseUrl = "";
let formatOddStates = { cn: 0, en: 0, engine: 0, url: 0 };
let isExportPinned = false;
let savedGoCommand = ""; // NEW: Stores the saved 'go' arguments
// ----------------------------
```
### 3. Update `renderExportTextUI`
Replace the `updateTextarea`, `formatBtns` loop, and the `exp-confirm` event listener inside `renderExportTextUI` with the following:
```javascript
const updateTextarea = (fmt) => {
if (fmt === 'cn') {
textarea.value = formatOddStates[fmt] === 1 ? cnTextWidthComment : cnText;
} else if (fmt === 'en') {
textarea.value = formatOddStates[fmt] === 1 ? enTextWidthComment : enText;
} else if (fmt === 'engine') {
let goCmdStr = savedGoCommand ? ` ${savedGoCommand}` : "";
if (formatOddStates[fmt] === 1) {
// State 1: Append '\ngo [saved_command]\n'
textarea.value = engineText + `\ngo${goCmdStr}\n`;
} else if (formatOddStates[fmt] === 2) {
// State 2: Append '\ngo [saved_command] searchmoves ...\n'
const isRed = currentTurn === 'w';
const moves = getAllLegalMoves(isRed, tileMap);
const searchMovesStr = moves.map(m => String.fromCharCode(97 + m.startX) + (9 - m.startY) + String.fromCharCode(97 + m.endX) + (9 - m.endY)).join(' ');
textarea.value = engineText + `\ngo${goCmdStr}` + (searchMovesStr ? ` searchmoves ${searchMovesStr}` : "") + "\n";
} else {
// State 0: Default
textarea.value = engineText;
}
} else if (fmt === 'url') {
textarea.value = formatOddStates[fmt] === 1 ? urlTextToggled : urlText;
}
};
formatBtns.forEach(btn => {
btn.addEventListener('click', () => {
const wasActive = btn.classList.contains('active');
const fmt = btn.getAttribute('data-fmt');
lastExportFormat = fmt;
formatBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
if (wasActive) {
// Cycle states: engine goes 0 -> 1 -> 2 -> 0, others go 0 -> 1 -> 0
if (fmt === 'engine') {
formatOddStates[fmt] = (formatOddStates[fmt] + 1) % 3;
} else {
formatOddStates[fmt] = formatOddStates[fmt] === 1 ? 0 : 1;
}
}
btn.setAttribute('data-odd', formatOddStates[fmt]);
updateTextarea(fmt);
textarea.blur();
setTimeout(() => {
textarea.focus();
textarea.select();
}, 12);
});
});
updateTextarea(lastExportFormat);
setTimeout(() => textarea.select(), 10);
// ... (Keep the btnDiv.innerHTML declaration exactly as it was) ...
// ... (Keep exp-cancel as it was) ...
document.getElementById('exp-confirm').addEventListener('click', () => {
const text = textarea.value;
// Capture the 'go' command arguments if it starts with 'go '
if (text.startsWith('go ')) {
savedGoCommand = text.substring(3).trim();
}
isExportTextMode = false;
importExportedText(text);
});
```
### 4. Update `renderNoteUI` (Pinned Copy & Paste logic)
Find the `btn-pinned-copy` and `btn-pinned-paste` event listeners inside the `if (isExportPinned)` block of your `renderNoteUI` function and replace them to support the 3 states and capture the pasted `go ` string:
```javascript
if (isExportPinned) {
document.getElementById('btn-pinned-copy').addEventListener('click', () => {
let textToCopy = "";
const fmt = lastExportFormat || 'cn';
const odd = formatOddStates[fmt] || 0;
if (fmt === 'cn') {
textToCopy = odd === 1 ? generateTextWithComment(false) : generateExportText(false);
} else if (fmt === 'en') {
textToCopy = odd === 1 ? generateTextWithComment(true) : generateExportText(true);
} else if (fmt === 'engine') {
let path = getGamePath();
let engineText = "position fen " + path.fen;
let currNode = path;
if (currNode && currNode.v && currNode.v.length > 0) {
engineText += " moves";
}
while (currNode && currNode.v && currNode.v.length > 0) {
currNode = currNode.v[0];
let dc = currNode.lastMove;
if (dc) {
engineText += " " + String.fromCharCode(97 + dc.startX) + (9 - dc.startY) + String.fromCharCode(97 + dc.endX) + (9 - dc.endY);
}
}
let goCmdStr = savedGoCommand ? ` ${savedGoCommand}` : "";
if (odd === 1) {
textToCopy = engineText + `\ngo${goCmdStr}\n`;
} else if (odd === 2) {
const isRed = currentTurn === 'w';
const moves = getAllLegalMoves(isRed, tileMap);
const searchMovesStr = moves.map(m => String.fromCharCode(97 + m.startX) + (9 - m.startY) + String.fromCharCode(97 + m.endX) + (9 - m.endY)).join(' ');
textToCopy = engineText + `\ngo${goCmdStr}` + (searchMovesStr ? ` searchmoves ${searchMovesStr}` : "") + "\n";
} else {
textToCopy = engineText;
}
} else if (fmt === 'url') {
textToCopy = generateUrlHash(odd === 1);
}
navigator.clipboard.writeText(textToCopy).catch(err => {
console.error('Copy failed', err);
});
});
document.getElementById('btn-pinned-paste').addEventListener('click', () => {
navigator.clipboard.readText().then(text => {
// Capture the 'go' command arguments if it starts with 'go '
if (text.startsWith('go ')) {
savedGoCommand = text.substring(3).trim();
}
importExportedText(text);
}).catch(err => {
console.error('Paste failed', err);
});
});
```
### 5. Update Keyboard Shortcuts (Ctrl + Enter)
In the `document.addEventListener('keydown', (e) => {` section, locate the `if (isExportTextMode)` block under `e.ctrlKey && e.key === 'Enter'` and modify it to also capture the string:
```javascript
// 2. Check if in Export Text Mode
if (isExportTextMode) {
const confirmBtn = document.getElementById('exp-confirm');
if (confirmBtn) {
const textarea = document.getElementById('export-textarea');
const text = textarea.value;
// Capture the 'go' command arguments if it starts with 'go '
if (text.startsWith('go ')) {
savedGoCommand = text.substring(3).trim();
}
isExportTextMode = false;
importExportedText(text);
return;
}
}
```https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
- 附件
-
ejcees_json_20260406113612評論變為兩倍.txt- (5.23 KiB) 尚未被下载
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
加上了剪貼板功能。把四個導出文本的按鍵都加上了二次點擊功能。
https://ejsoon.vip/wp-content/uploads/2 ... 77748.html
- 附件
-
ejcees20260405importFenRotate99977748.7z- (45.58 KiB) 尚未被下载
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
在线用户
正浏览此版面之用户: 没有注册用户 和 0 访客
