65 lines
1.9 KiB
JavaScript
65 lines
1.9 KiB
JavaScript
// Export / import of a save as a JSON or TXT file (or pasted text).
|
||
// Works on every device: download via a Blob link, import via file input or paste.
|
||
|
||
import { sanitizeState, serializeState } from '../engine/state.js';
|
||
|
||
const GAME_ID = 'idle-clicker';
|
||
const FORMAT = 1;
|
||
|
||
export class ImportError extends Error {
|
||
/** @param {'empty'|'invalid'|'unknown_theme'|'wrong_game'} code */
|
||
constructor(code) {
|
||
super(code);
|
||
this.code = code;
|
||
}
|
||
}
|
||
|
||
/** The text a player downloads, copies or pastes back. */
|
||
export function buildExport(themeId, state, now = Date.now()) {
|
||
return JSON.stringify(
|
||
{
|
||
game: GAME_ID,
|
||
format: FORMAT,
|
||
theme: themeId,
|
||
exportedAt: new Date(now).toISOString(),
|
||
state: JSON.parse(serializeState(state)),
|
||
},
|
||
null,
|
||
2,
|
||
);
|
||
}
|
||
|
||
/** Validate imported text. Throws ImportError; never trusts the content. */
|
||
export function parseImport(text, ctx, knownThemeIds) {
|
||
const trimmed = String(text ?? '').trim().replace(/^/, '');
|
||
if (!trimmed) throw new ImportError('empty');
|
||
|
||
let data;
|
||
try {
|
||
data = JSON.parse(trimmed);
|
||
} catch {
|
||
throw new ImportError('invalid');
|
||
}
|
||
if (data?.game !== GAME_ID || data.format !== FORMAT) throw new ImportError('wrong_game');
|
||
if (!knownThemeIds.includes(data.theme)) throw new ImportError('unknown_theme');
|
||
|
||
const state = sanitizeState(data.state, ctx);
|
||
if (!state) throw new ImportError('invalid');
|
||
return { themeId: data.theme, state };
|
||
}
|
||
|
||
export function downloadText(filename, text, mime = 'application/json') {
|
||
const url = URL.createObjectURL(new Blob([text], { type: `${mime};charset=utf-8` }));
|
||
const link = document.createElement('a');
|
||
link.href = url;
|
||
link.download = filename;
|
||
document.body.append(link);
|
||
link.click();
|
||
link.remove();
|
||
setTimeout(() => URL.revokeObjectURL(url), 10_000);
|
||
}
|
||
|
||
export function readFileText(file) {
|
||
return file.text();
|
||
}
|