490 lines
20 KiB
HTML
490 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Asteroid Position Editor</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { background: #1e1e2e; color: #cdd6f4; font-family: 'Segoe UI', system-ui, sans-serif; }
|
|
|
|
#toolbar {
|
|
position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
|
|
background: #181825; border-bottom: 1px solid #313244;
|
|
display: flex; align-items: center; gap: 12px; padding: 8px 16px; height: 48px;
|
|
}
|
|
#toolbar button {
|
|
background: #313244; color: #cdd6f4; border: 1px solid #45475a;
|
|
padding: 6px 14px; border-radius: 6px; cursor: pointer; font-size: 13px;
|
|
transition: background 0.15s;
|
|
}
|
|
#toolbar button:hover { background: #45475a; }
|
|
#toolbar button.primary { background: #6366f1; border-color: #818cf8; color: white; }
|
|
#toolbar button.primary:hover { background: #818cf8; }
|
|
#toolbar .page-nav { display: flex; align-items: center; gap: 6px; }
|
|
#toolbar .page-nav span { font-size: 14px; min-width: 90px; text-align: center; }
|
|
#toolbar .spacer { flex: 1; }
|
|
#toolbar .title { font-weight: 600; font-size: 14px; color: #a6adc8; }
|
|
|
|
#statusbar {
|
|
position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000;
|
|
background: #181825; border-top: 1px solid #313244;
|
|
padding: 6px 16px; font-size: 13px; height: 32px;
|
|
display: flex; align-items: center; gap: 20px;
|
|
}
|
|
#statusbar .pos { color: #89b4fa; }
|
|
#statusbar .info { color: #a6adc8; }
|
|
|
|
#worksheet-container {
|
|
padding: 48px 0 32px;
|
|
display: flex; flex-direction: column; align-items: center; gap: 20px;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.editor-draggable { cursor: grab; user-select: none; }
|
|
.editor-draggable:hover { outline: 2px solid rgba(99, 102, 241, 0.5); outline-offset: 2px; border-radius: 50%; }
|
|
.editor-selected { outline: 3px solid #6366f1 !important; outline-offset: 2px; border-radius: 50%; }
|
|
.editor-changed::after {
|
|
content: ''; position: absolute; top: 2px; right: 2px;
|
|
width: 8px; height: 8px; background: #f59e0b; border-radius: 50%;
|
|
border: 1px solid #1e1e2e;
|
|
}
|
|
body.dragging { cursor: grabbing !important; }
|
|
body.dragging * { cursor: grabbing !important; }
|
|
|
|
.ship-draggable { z-index: 50; }
|
|
.ship-draggable:hover { outline-color: rgba(251, 146, 60, 0.6); }
|
|
.ship-selected { outline-color: #fb923c !important; }
|
|
|
|
#coord-tooltip {
|
|
position: fixed; z-index: 2000; pointer-events: none;
|
|
background: #181825; color: #89b4fa; border: 1px solid #6366f1;
|
|
padding: 3px 8px; border-radius: 4px; font-size: 12px; font-family: monospace;
|
|
display: none;
|
|
}
|
|
#toast {
|
|
position: fixed; top: 60px; right: 16px; z-index: 2000;
|
|
background: #6366f1; color: white; padding: 8px 16px; border-radius: 6px;
|
|
font-size: 13px; opacity: 0; transition: opacity 0.3s; pointer-events: none;
|
|
}
|
|
#toast.show { opacity: 1; }
|
|
.page-label {
|
|
position: absolute; top: 4px; right: 4px; z-index: 100;
|
|
background: rgba(0,0,0,0.6); color: white; padding: 2px 8px;
|
|
border-radius: 4px; font-size: 12px; pointer-events: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div id="toolbar">
|
|
<span class="title">Asteroid Editor</span>
|
|
<div class="page-nav">
|
|
<button id="btn-prev" title="Previous page">←</button>
|
|
<span id="page-indicator">Page 1 / ?</span>
|
|
<button id="btn-next" title="Next page">→</button>
|
|
</div>
|
|
<div class="spacer"></div>
|
|
<button id="btn-reset">Reset Page</button>
|
|
<button id="btn-copy-changes">Copy Changes</button>
|
|
<button id="btn-copy" class="primary">Copy All JSON</button>
|
|
<button id="btn-save" class="primary" style="background:#22c55e;border-color:#4ade80;">Save</button>
|
|
</div>
|
|
|
|
<div id="worksheet-container"></div>
|
|
|
|
<div id="statusbar">
|
|
<span id="status-info" class="info">Click an asteroid or ship circle to select it</span>
|
|
<span id="status-pos" class="pos"></span>
|
|
</div>
|
|
|
|
<div id="coord-tooltip"></div>
|
|
<div id="toast"></div>
|
|
|
|
<script src="/src/editor/editor-core.js"></script>
|
|
<script>
|
|
(function() {
|
|
|
|
// === Asteroid-specific state ===
|
|
const baseScales = {};
|
|
let asteroids = [];
|
|
let ships = [];
|
|
let originalPositions = new Map();
|
|
let selectedElement = null;
|
|
let dragging = null;
|
|
let dragStart = null;
|
|
|
|
// Scale formula
|
|
function getBaseKey(p, t) { return 'p' + p + '-' + t; }
|
|
function getBaseScale(p, t) { return baseScales[getBaseKey(p, t)] || 0.30; }
|
|
function setBaseScale(p, t, v) { baseScales[getBaseKey(p, t)] = Math.round(v * 100) / 100; }
|
|
function valueToScale(val, p, t) { return ((p && t) ? getBaseScale(p, t) : 0.30) + 0.15 * val; }
|
|
|
|
// === Init via EditorCore ===
|
|
const core = EditorCore.init({
|
|
taskType: 'collecting-asteroids',
|
|
serialize: buildConfig,
|
|
onReset: resetCurrentPage,
|
|
onReady: function(pages, mmToPx) {
|
|
setupAsteroids(pages);
|
|
setupShips(pages);
|
|
setupGlobalEvents(mmToPx);
|
|
window.getConfig = () => buildConfig(false);
|
|
window.getChanges = () => buildConfig(true);
|
|
}
|
|
});
|
|
|
|
if (!core) return;
|
|
|
|
// === Setup asteroids ===
|
|
function setupAsteroids(pages) {
|
|
document.querySelectorAll('.absolute:has(img[src*="pack3-asteroids"])').forEach((el, globalIndex) => {
|
|
const page = el.closest('[data-page]');
|
|
const pageNum = page ? parseInt(page.dataset.page) : 0;
|
|
const left = parseFloat(el.style.left) || 0;
|
|
const top = parseFloat(el.style.top) || 0;
|
|
const img = el.querySelector('img[src*="pack3-asteroids"]');
|
|
const span = el.querySelector('span');
|
|
const astMatch = img ? img.src.match(/asteroid\d+/) : null;
|
|
|
|
let rotate = 0, scale = 1;
|
|
if (img && img.style.transform) {
|
|
const rotMatch = img.style.transform.match(/rotate\(([-\d.]+)deg\)/);
|
|
const scMatch = img.style.transform.match(/scale\(([\d.]+)\)/);
|
|
if (rotMatch) rotate = parseFloat(rotMatch[1]);
|
|
if (scMatch) scale = parseFloat(scMatch[1]);
|
|
}
|
|
|
|
const value = span ? parseInt(span.textContent.trim()) || 0 : 0;
|
|
const id = `p${pageNum}-a${globalIndex}`;
|
|
el.dataset.editorId = id;
|
|
el.dataset.editorType = 'asteroid';
|
|
el.dataset.pageNum = pageNum;
|
|
|
|
originalPositions.set(id, { left, top, rotate, scale, value });
|
|
el.classList.add('editor-draggable');
|
|
el.querySelectorAll('img').forEach(i => { i.style.pointerEvents = 'none'; i.draggable = false; });
|
|
|
|
asteroids.push({ el, id, pageNum, globalIndex, span, value, asteroid: astMatch ? astMatch[0] : 'unknown' });
|
|
|
|
el.addEventListener('mousedown', (e) => {
|
|
e.preventDefault(); e.stopPropagation();
|
|
selectElement(el, id, 'asteroid');
|
|
startDragAsteroid(el, e);
|
|
});
|
|
});
|
|
}
|
|
|
|
// === Setup ships ===
|
|
function setupShips(pages) {
|
|
document.querySelectorAll('.rounded-full.bg-indigo-600').forEach((circleEl, globalIndex) => {
|
|
const page = circleEl.closest('[data-page]');
|
|
const pageNum = page ? parseInt(page.dataset.page) : 0;
|
|
const span = circleEl.querySelector('span');
|
|
const value = span ? parseInt(span.textContent.trim()) || 0 : 0;
|
|
const origLeft = circleEl.style.left || '50%';
|
|
const origTop = circleEl.style.top || '50%';
|
|
|
|
const id = `p${pageNum}-s${globalIndex}`;
|
|
circleEl.dataset.editorId = id;
|
|
circleEl.dataset.editorType = 'ship';
|
|
circleEl.dataset.pageNum = pageNum;
|
|
|
|
originalPositions.set(id, { left: origLeft, top: origTop, value });
|
|
circleEl.classList.add('editor-draggable', 'ship-draggable');
|
|
circleEl.style.cursor = 'grab';
|
|
|
|
ships.push({ el: circleEl, id, pageNum, globalIndex, span, value });
|
|
|
|
circleEl.addEventListener('mousedown', (e) => {
|
|
e.preventDefault(); e.stopPropagation();
|
|
selectElement(circleEl, id, 'ship');
|
|
startDragShip(circleEl, e);
|
|
});
|
|
});
|
|
}
|
|
|
|
// === Global events ===
|
|
function setupGlobalEvents(mmToPx) {
|
|
document.addEventListener('mousemove', onMouseMove);
|
|
document.addEventListener('mouseup', onMouseUp);
|
|
document.addEventListener('keydown', onKeyDown);
|
|
|
|
document.getElementById('worksheet-container').addEventListener('mousedown', (e) => {
|
|
if (!e.target.closest('.editor-draggable')) deselectAll();
|
|
});
|
|
}
|
|
|
|
// === Selection ===
|
|
function selectElement(el, id, type) {
|
|
deselectAll();
|
|
el.classList.add(type === 'ship' ? 'ship-selected' : 'editor-selected');
|
|
selectedElement = { el, id, type };
|
|
updateStatus();
|
|
}
|
|
|
|
function deselectAll() {
|
|
document.querySelectorAll('.editor-selected, .ship-selected').forEach(el => {
|
|
el.classList.remove('editor-selected', 'ship-selected');
|
|
});
|
|
selectedElement = null;
|
|
document.getElementById('status-info').textContent = 'Click an asteroid or ship circle to select it';
|
|
document.getElementById('status-pos').textContent = '';
|
|
}
|
|
|
|
// === Asteroid helpers ===
|
|
function getAsteroidTransform(el) {
|
|
const img = el.querySelector('img[src*="pack3-asteroids"]');
|
|
let rotate = 0, scale = 1;
|
|
if (img && img.style.transform) {
|
|
const rotMatch = img.style.transform.match(/rotate\(([-\d.]+)deg\)/);
|
|
const scMatch = img.style.transform.match(/scale\(([\d.]+)\)/);
|
|
if (rotMatch) rotate = parseFloat(rotMatch[1]);
|
|
if (scMatch) scale = parseFloat(scMatch[1]);
|
|
}
|
|
return { rotate, scale, img };
|
|
}
|
|
|
|
function setAsteroidTransform(el, rotate, scale) {
|
|
const img = el.querySelector('img[src*="pack3-asteroids"]');
|
|
if (img) img.style.transform = `rotate(${rotate}deg) scale(${scale.toFixed(2)})`;
|
|
}
|
|
|
|
// === Status ===
|
|
function updateStatus() {
|
|
if (!selectedElement) return;
|
|
const { el, id, type } = selectedElement;
|
|
|
|
if (type === 'asteroid') {
|
|
const left = Math.round(parseFloat(el.style.left));
|
|
const top = Math.round(parseFloat(el.style.top));
|
|
const { rotate, scale } = getAsteroidTransform(el);
|
|
const ast = asteroids.find(a => a.id === id);
|
|
const base = ast ? getBaseScale(parseInt(el.dataset.pageNum), ast.asteroid) : 0.30;
|
|
document.getElementById('status-info').textContent =
|
|
`Page ${el.dataset.pageNum} | val=${ast ? ast.value : '?'} | ${ast ? ast.asteroid : ''} | base=${base.toFixed(2)} | [/] rot, +/- val, </.> base`;
|
|
document.getElementById('status-pos').textContent =
|
|
`left: ${left}mm, top: ${top}mm | rot: ${Math.round(rotate)}° | scale: ${scale.toFixed(2)}`;
|
|
} else {
|
|
const ship = ships.find(s => s.id === id);
|
|
document.getElementById('status-info').textContent =
|
|
`Page ${el.dataset.pageNum} | Ship capacity=${ship ? ship.value : '?'} | +/- change value`;
|
|
document.getElementById('status-pos').textContent =
|
|
`left: ${el.style.left}, top: ${el.style.top}`;
|
|
}
|
|
}
|
|
|
|
// === Dragging ===
|
|
function startDragAsteroid(el, e) {
|
|
dragging = { el, type: 'asteroid' };
|
|
dragStart = { mouseX: e.clientX, mouseY: e.clientY, left: parseFloat(el.style.left) || 0, top: parseFloat(el.style.top) || 0 };
|
|
document.body.classList.add('dragging');
|
|
}
|
|
|
|
function startDragShip(el, e) {
|
|
const parent = el.offsetParent || el.parentElement;
|
|
dragging = { el, type: 'ship', parentRect: parent.getBoundingClientRect() };
|
|
dragStart = { mouseX: e.clientX, mouseY: e.clientY, leftPct: parseFloat(el.style.left) || 50, topPct: parseFloat(el.style.top) || 50 };
|
|
document.body.classList.add('dragging');
|
|
}
|
|
|
|
function onMouseMove(e) {
|
|
if (!dragging) return;
|
|
e.preventDefault();
|
|
|
|
if (dragging.type === 'asteroid') {
|
|
const dx = (e.clientX - dragStart.mouseX) / core.mmToPx;
|
|
const dy = (e.clientY - dragStart.mouseY) / core.mmToPx;
|
|
let newLeft = Math.max(-10, Math.min(175, Math.round(dragStart.left + dx)));
|
|
let newTop = Math.max(-5, Math.min(160, Math.round(dragStart.top + dy)));
|
|
dragging.el.style.left = newLeft + 'mm';
|
|
dragging.el.style.top = newTop + 'mm';
|
|
core.showTooltip(e, `${newLeft}mm, ${newTop}mm`);
|
|
} else if (dragging.type === 'ship') {
|
|
const pW = dragging.parentRect.width;
|
|
const pH = dragging.parentRect.height;
|
|
if (pW === 0 || pH === 0) return;
|
|
let newLeftPct = Math.round(dragStart.leftPct + ((e.clientX - dragStart.mouseX) / pW) * 100);
|
|
let newTopPct = Math.round(dragStart.topPct + ((e.clientY - dragStart.mouseY) / pH) * 100);
|
|
dragging.el.style.left = newLeftPct + '%';
|
|
dragging.el.style.top = newTopPct + '%';
|
|
core.showTooltip(e, `${newLeftPct}%, ${newTopPct}%`);
|
|
}
|
|
|
|
updateStatus();
|
|
markChanged(dragging.el);
|
|
}
|
|
|
|
function onMouseUp() {
|
|
if (!dragging) return;
|
|
document.body.classList.remove('dragging');
|
|
core.hideTooltip();
|
|
dragging = null; dragStart = null;
|
|
}
|
|
|
|
// === Keyboard ===
|
|
function onKeyDown(e) {
|
|
if (!selectedElement) return;
|
|
const { el, id, type } = selectedElement;
|
|
if (type === 'asteroid') onKeyDownAsteroid(e, el, id);
|
|
else onKeyDownShip(e, el, id);
|
|
}
|
|
|
|
function onKeyDownAsteroid(e, el, id) {
|
|
const step = e.shiftKey ? 5 : 1;
|
|
let left = parseFloat(el.style.left) || 0;
|
|
let top = parseFloat(el.style.top) || 0;
|
|
const { rotate, scale } = getAsteroidTransform(el);
|
|
const rotStep = e.shiftKey ? 1 : 5;
|
|
const ast = asteroids.find(a => a.id === id);
|
|
const pageNum = parseInt(el.dataset.pageNum);
|
|
|
|
switch (e.key) {
|
|
case 'ArrowLeft': left -= step; break;
|
|
case 'ArrowRight': left += step; break;
|
|
case 'ArrowUp': top -= step; break;
|
|
case 'ArrowDown': top += step; break;
|
|
case '[': setAsteroidTransform(el, rotate - rotStep, scale); break;
|
|
case ']': setAsteroidTransform(el, rotate + rotStep, scale); break;
|
|
case '-': case '_':
|
|
if (ast && ast.value > 1) { ast.value--; ast.span.textContent = ast.value; setAsteroidTransform(el, rotate, valueToScale(ast.value, pageNum, ast.asteroid)); }
|
|
break;
|
|
case '=': case '+':
|
|
if (ast && ast.value < 20) { ast.value++; ast.span.textContent = ast.value; setAsteroidTransform(el, rotate, valueToScale(ast.value, pageNum, ast.asteroid)); }
|
|
break;
|
|
case ',': case '<':
|
|
if (ast) { const bs = e.shiftKey ? 0.01 : 0.05; setBaseScale(pageNum, ast.asteroid, Math.max(0.05, getBaseScale(pageNum, ast.asteroid) - bs)); applyBaseToType(pageNum, ast.asteroid); core.showToast(`Base ${ast.asteroid}: ${getBaseScale(pageNum, ast.asteroid).toFixed(2)}`); }
|
|
break;
|
|
case '.': case '>':
|
|
if (ast) { const bs = e.shiftKey ? 0.01 : 0.05; setBaseScale(pageNum, ast.asteroid, getBaseScale(pageNum, ast.asteroid) + bs); applyBaseToType(pageNum, ast.asteroid); core.showToast(`Base ${ast.asteroid}: ${getBaseScale(pageNum, ast.asteroid).toFixed(2)}`); }
|
|
break;
|
|
case 'Escape': deselectAll(); return;
|
|
default: return;
|
|
}
|
|
e.preventDefault();
|
|
el.style.left = Math.max(-10, Math.min(175, Math.round(left))) + 'mm';
|
|
el.style.top = Math.max(-5, Math.min(160, Math.round(top))) + 'mm';
|
|
updateStatus(); markChanged(el);
|
|
}
|
|
|
|
function onKeyDownShip(e, el, id) {
|
|
const ship = ships.find(s => s.id === id);
|
|
if (!ship) return;
|
|
const step = e.shiftKey ? 5 : 1;
|
|
|
|
switch (e.key) {
|
|
case '-': case '_': ship.value = Math.max(1, ship.value - step); ship.span.textContent = ship.value; break;
|
|
case '=': case '+': ship.value += step; ship.span.textContent = ship.value; break;
|
|
case 'ArrowLeft': el.style.left = ((parseFloat(el.style.left) || 50) - step) + '%'; break;
|
|
case 'ArrowRight': el.style.left = ((parseFloat(el.style.left) || 50) + step) + '%'; break;
|
|
case 'ArrowUp': el.style.top = ((parseFloat(el.style.top) || 50) - step) + '%'; break;
|
|
case 'ArrowDown': el.style.top = ((parseFloat(el.style.top) || 50) + step) + '%'; break;
|
|
case 'Escape': deselectAll(); return;
|
|
default: return;
|
|
}
|
|
e.preventDefault(); updateStatus(); markChanged(el);
|
|
}
|
|
|
|
// === Change tracking ===
|
|
function markChanged(el) {
|
|
const id = el.dataset.editorId;
|
|
const type = el.dataset.editorType;
|
|
const orig = originalPositions.get(id);
|
|
if (!orig) return;
|
|
|
|
let changed = false;
|
|
if (type === 'asteroid') {
|
|
const { rotate, scale } = getAsteroidTransform(el);
|
|
const ast = asteroids.find(a => a.id === id);
|
|
changed = Math.round(orig.left) !== Math.round(parseFloat(el.style.left)) ||
|
|
Math.round(orig.top) !== Math.round(parseFloat(el.style.top)) ||
|
|
Math.round(orig.rotate) !== Math.round(rotate) ||
|
|
Math.abs(orig.scale - scale) > 0.005 ||
|
|
(ast && orig.value !== ast.value);
|
|
} else {
|
|
const ship = ships.find(s => s.id === id);
|
|
changed = el.style.left !== orig.left || el.style.top !== orig.top || (ship && orig.value !== ship.value);
|
|
}
|
|
el.classList.toggle('editor-changed', changed);
|
|
}
|
|
|
|
function applyBaseToType(pageNum, asteroidType) {
|
|
asteroids.filter(a => a.pageNum === pageNum && a.asteroid === asteroidType).forEach(ast => {
|
|
const { rotate } = getAsteroidTransform(ast.el);
|
|
setAsteroidTransform(ast.el, rotate, valueToScale(ast.value, pageNum, asteroidType));
|
|
markChanged(ast.el);
|
|
});
|
|
}
|
|
|
|
// === Serialization ===
|
|
function buildConfig(changesOnly) {
|
|
const result = { file: core.docId, pages: [] };
|
|
|
|
core.pages.forEach((pageEl, pageIndex) => {
|
|
const pageNum = pageIndex + 1;
|
|
const pageData = { page: pageNum };
|
|
|
|
const pageAsteroids = asteroids.filter(a => a.pageNum === pageNum);
|
|
const asteroidItems = [];
|
|
pageAsteroids.forEach((ast, localIndex) => {
|
|
const curLeft = Math.round(parseFloat(ast.el.style.left));
|
|
const curTop = Math.round(parseFloat(ast.el.style.top));
|
|
const { rotate, scale } = getAsteroidTransform(ast.el);
|
|
const orig = originalPositions.get(ast.id);
|
|
if (changesOnly && orig &&
|
|
Math.round(orig.left) === curLeft && Math.round(orig.top) === curTop &&
|
|
Math.round(orig.rotate) === Math.round(rotate) && Math.abs(orig.scale - scale) < 0.005 &&
|
|
orig.value === ast.value) return;
|
|
asteroidItems.push({ index: localIndex, left: curLeft + 'mm', top: curTop + 'mm', rotate: Math.round(rotate) + 'deg', scale: parseFloat(scale.toFixed(2)), value: ast.value, asteroid: ast.asteroid });
|
|
});
|
|
|
|
const pageShips = ships.filter(s => s.pageNum === pageNum);
|
|
const shipItems = [];
|
|
pageShips.forEach((ship, localIndex) => {
|
|
const orig = originalPositions.get(ship.id);
|
|
if (changesOnly && orig && ship.el.style.left === orig.left && ship.el.style.top === orig.top && orig.value === ship.value) return;
|
|
shipItems.push({ index: localIndex, left: ship.el.style.left, top: ship.el.style.top, value: ship.value });
|
|
});
|
|
|
|
const pageBaseScales = {};
|
|
for (const [key, val] of Object.entries(baseScales)) {
|
|
if (key.startsWith('p' + pageNum + '-') && val !== 0.30) {
|
|
pageBaseScales[key.replace('p' + pageNum + '-', '')] = val;
|
|
}
|
|
}
|
|
|
|
if (asteroidItems.length > 0 || shipItems.length > 0 || Object.keys(pageBaseScales).length > 0) {
|
|
if (asteroidItems.length > 0) pageData.asteroids = asteroidItems;
|
|
if (shipItems.length > 0) pageData.ships = shipItems;
|
|
if (Object.keys(pageBaseScales).length > 0) pageData.baseScales = pageBaseScales;
|
|
result.pages.push(pageData);
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
// === Reset ===
|
|
function resetCurrentPage(pageNum) {
|
|
asteroids.filter(a => a.pageNum === pageNum).forEach(ast => {
|
|
const orig = originalPositions.get(ast.id);
|
|
if (!orig) return;
|
|
ast.el.style.left = orig.left + 'mm'; ast.el.style.top = orig.top + 'mm';
|
|
setAsteroidTransform(ast.el, orig.rotate, orig.scale);
|
|
ast.value = orig.value; ast.span.textContent = orig.value;
|
|
ast.el.classList.remove('editor-changed');
|
|
});
|
|
ships.filter(s => s.pageNum === pageNum).forEach(ship => {
|
|
const orig = originalPositions.get(ship.id);
|
|
if (!orig) return;
|
|
ship.el.style.left = orig.left; ship.el.style.top = orig.top;
|
|
ship.value = orig.value; ship.span.textContent = orig.value;
|
|
ship.el.classList.remove('editor-changed');
|
|
});
|
|
core.showToast(`Page ${pageNum} reset`);
|
|
}
|
|
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|