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; } }