import { EVENT_OPTION_SET, OBJECT_MAGIC_PROP_PREFIX, } from './config.js'; export class Options { sim = undefined; options = undefined; values = {}; undefinedObj = { [OBJECT_MAGIC_PROP_PREFIX + 'undefined']: true}; nullObj = { [OBJECT_MAGIC_PROP_PREFIX + 'null']: true}; getStorageKey(path) { return `${path}:options`; } constructor(sim, options) { this.sim = sim; this.options = options; // Global methods to get/set current option values this.sim.getOption = (path) => this.getOption(path); this.sim.setOption = (path, value) => this.setOption(path, value); this.sim.onOptionSet = (path, cb) => this.onOptionSet(path, cb); // Initialize values from localStorage for (const groupName of Object.keys(options)) { for (const [name, [, , defaultValue]] of Object.entries(this.options[groupName])) { const path = [groupName, name].join('.'); let value = this.getFromLocalStorage(path); if (value === undefined) { value = defaultValue; } this.values[path] = value; } } } toStored(value) { if (value === undefined) { // Do we want to interpret this as removing from storage? // Let's just treat it as a value for now; // Semantically it works because when retrieved, it will return undefined, // which is the same result you get if the key is not set return JSON.stringify(this.undefinedObj); } else if (value === null) { return JSON.stringify(this.nullObj); } return JSON.stringify(value); } // value: string fromStored(value) { if (value === null) { return undefined; } else if (value === JSON.stringify(this.undefinedObj)) { return undefined; } else if (value === JSON.stringify(this.nullObj)) { return null; } return JSON.parse(value); } getFromLocalStorage(path) { const storageKey = this.getStorageKey(path); const value = this.fromStored(window.localStorage.getItem(storageKey)); this.values[path] = value; return value; } getOption(path) { return this.values[path]; } setOption(path, value) { this.values[path] = value; const storageKey = this.getStorageKey(path); window.localStorage.setItem(storageKey, this.toStored(value)); const e = new CustomEvent(EVENT_OPTION_SET, {detail: {path, value}}); this.sim.div.dispatchEvent(e); } // cb: (value) => undefined onOptionSet(path, cb) { this.sim.div.addEventListener(EVENT_OPTION_SET, (e) => { if (path === e.detail.path) { cb(e.detail.value); } }); } getSection(sectionName) { const section = this.options[sectionName]; const group = { type: 'group', name: sectionName, title: section._title, items: [], }; for (const name in section) { if (name.startsWith('_')) continue; const [title, type, defaultValue, opts] = section[name]; group.items.push({ name, type, title, default: defaultValue, ...opts }) } return group; } }