gravity/tool/state.js
2026-01-05 01:30:32 -06:00

107 lines
3.2 KiB
JavaScript

import {TOOL_INFO_CLASSNAME, WIDE_CLASSNAME} from '../config.js';
import {hash} from '../helper.js';
import {Tool} from '../tool.js';
export class StateTool extends Tool {
stored = [];
async setContainer(container) {
super.setContainer(container);
const buttons = document.createElement('div');
const save = document.createElement('button');
const list = document.createElement('div');
save.innerHTML = 'Save';
save.classList.add(WIDE_CLASSNAME);
buttons.style.display = 'flex';
buttons.style.flexDirection = 'row';
buttons.appendChild(save);
list.style.display = 'flex';
list.style.flexDirection = 'column';
this.div.appendChild(buttons);
this.div.appendChild(list);
save.addEventListener('click', async () => {
const state = this.sim.toJSON();
this.stored.push(state);
const item = await this.createItem(state);
list.appendChild(item);
});
// Check url query parameter, and load specified state if found
await this.fromUrl();
}
async toUrl(state) {
const stateText = JSON.stringify(state);
const digest = await hash(stateText);
const rawUrl = `./?state=${stateText}&digest=${digest}`;
const url = encodeURI(rawUrl);
return {url, digest};
}
async fromUrl() {
const paramsString = window.location.search;
const searchParams = new URLSearchParams(paramsString);
const stateEnc = searchParams.get("state");
const rxDigest = searchParams.get("digest");
if (stateEnc) {
const stateText = decodeURI(stateEnc);
console.log('decoded state text', stateText);
const state = JSON.parse(stateText);
const digest = await hash(stateText);
if (digest !== rxDigest) {
throw new Error('state query parameter does not match digest query parameter');
}
// Tools in this system can be very powerful
this.sim.fromJSON(state);
}
}
getStateDescription(state) {
const date = new Date(state.dateSaved);
const Y = date.getFullYear().toString();
const M = (date.getMonth() + 1).toString().padStart(2, '0');
const D = date.getDate().toString().padStart(2, '0');
const h = date.getHours().toString().padStart(2, '0');
const m = date.getMinutes().toString().padStart(2, '0');
const s = date.getSeconds().toString().padStart(2, '0');
return `${Y}-${M}-${D} ${h}:${m}:${s}`;
}
async createItem(state) {
const item = document.createElement('div');
item.style.display = 'flex';
item.style.flexDirection = 'row';
item.style.flexWrap = 'wrap';
const description = document.createElement('button');
description.style.flex = '2';
description.classList.add(TOOL_INFO_CLASSNAME);
description.innerHTML = this.getStateDescription(state);
const load = document.createElement('button');
load.style.flex = '1';
const link = document.createElement('a');
const {url, digest} = await this.toUrl(state);
link.href = url;
link.innerHTML = digest.slice(0, 5);
load.appendChild(link);
item.appendChild(description);
item.appendChild(load);
load.addEventListener('click', (e) => {
e.preventDefault();
// Tools in this system can wield great power
this.sim.fromJSON(state);
});
return item;
}
}