130 lines
3.5 KiB
JavaScript
130 lines
3.5 KiB
JavaScript
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) {
|
|
const [groupName, name] = path.split('.');
|
|
const group = this.options[groupName];
|
|
const item = group[name];
|
|
const {type} = item;
|
|
const value = this.values[path];
|
|
switch (type) {
|
|
case 'number': return Number(value);
|
|
case 'boolean': return value === true || value === 'true';
|
|
default: {
|
|
console.log({
|
|
path, groupName, name,
|
|
group, item,
|
|
type, value
|
|
});
|
|
throw new Error('unknown option type');
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|