Declare setting types

This commit is contained in:
Shin'ya Ueoka 2019-05-05 08:03:29 +09:00
parent d01db82c0d
commit a0882bbceb
48 changed files with 1618 additions and 903 deletions

View file

@ -1,85 +0,0 @@
export default {
source: 'json',
json: `{
"keymaps": {
"0": { "type": "scroll.home" },
":": { "type": "command.show" },
"o": { "type": "command.show.open", "alter": false },
"O": { "type": "command.show.open", "alter": true },
"t": { "type": "command.show.tabopen", "alter": false },
"T": { "type": "command.show.tabopen", "alter": true },
"w": { "type": "command.show.winopen", "alter": false },
"W": { "type": "command.show.winopen", "alter": true },
"b": { "type": "command.show.buffer" },
"a": { "type": "command.show.addbookmark", "alter": true },
"k": { "type": "scroll.vertically", "count": -1 },
"j": { "type": "scroll.vertically", "count": 1 },
"h": { "type": "scroll.horizonally", "count": -1 },
"l": { "type": "scroll.horizonally", "count": 1 },
"<C-U>": { "type": "scroll.pages", "count": -0.5 },
"<C-D>": { "type": "scroll.pages", "count": 0.5 },
"<C-B>": { "type": "scroll.pages", "count": -1 },
"<C-F>": { "type": "scroll.pages", "count": 1 },
"gg": { "type": "scroll.top" },
"G": { "type": "scroll.bottom" },
"$": { "type": "scroll.end" },
"d": { "type": "tabs.close" },
"D": { "type": "tabs.close.right" },
"!d": { "type": "tabs.close.force" },
"u": { "type": "tabs.reopen" },
"K": { "type": "tabs.prev", "count": 1 },
"J": { "type": "tabs.next", "count": 1 },
"gT": { "type": "tabs.prev", "count": 1 },
"gt": { "type": "tabs.next", "count": 1 },
"g0": { "type": "tabs.first" },
"g$": { "type": "tabs.last" },
"<C-6>": { "type": "tabs.prevsel" },
"r": { "type": "tabs.reload", "cache": false },
"R": { "type": "tabs.reload", "cache": true },
"zp": { "type": "tabs.pin.toggle" },
"zd": { "type": "tabs.duplicate" },
"zi": { "type": "zoom.in" },
"zo": { "type": "zoom.out" },
"zz": { "type": "zoom.neutral" },
"f": { "type": "follow.start", "newTab": false },
"F": { "type": "follow.start", "newTab": true, "background": false },
"m": { "type": "mark.set.prefix" },
"'": { "type": "mark.jump.prefix" },
"H": { "type": "navigate.history.prev" },
"L": { "type": "navigate.history.next" },
"[[": { "type": "navigate.link.prev" },
"]]": { "type": "navigate.link.next" },
"gu": { "type": "navigate.parent" },
"gU": { "type": "navigate.root" },
"gi": { "type": "focus.input" },
"gf": { "type": "page.source" },
"gh": { "type": "page.home" },
"gH": { "type": "page.home", "newTab": true },
"y": { "type": "urls.yank" },
"p": { "type": "urls.paste", "newTab": false },
"P": { "type": "urls.paste", "newTab": true },
"/": { "type": "find.start" },
"n": { "type": "find.next" },
"N": { "type": "find.prev" },
"<S-Esc>": { "type": "addon.toggle.enabled" }
},
"search": {
"default": "google",
"engines": {
"google": "https://google.com/search?q={}",
"yahoo": "https://search.yahoo.com/search?p={}",
"bing": "https://www.bing.com/search?q={}",
"duckduckgo": "https://duckduckgo.com/?q={}",
"twitter": "https://twitter.com/search?q={}",
"wikipedia": "https://en.wikipedia.org/w/index.php?search={}"
}
},
"properties": {
"hintchars": "abcdefghijklmnopqrstuvwxyz",
"smoothscroll": false,
"complete": "sbh"
},
"blacklist": [
]
}`,
};

View file

@ -1,24 +0,0 @@
// describe types of a propety as:
// mystr: 'string',
// mynum: 'number',
// mybool: 'boolean',
const types: { [key: string]: string } = {
hintchars: 'string',
smoothscroll: 'boolean',
complete: 'string',
};
// describe default values of a property
const defaults: { [key: string]: string | number | boolean } = {
hintchars: 'abcdefghijklmnopqrstuvwxyz',
smoothscroll: false,
complete: 'sbh',
};
const docs: { [key: string]: string } = {
hintchars: 'hint characters on follow mode',
smoothscroll: 'smooth scroll',
complete: 'which are completed at the open page',
};
export { types, defaults, docs };

View file

@ -1,32 +0,0 @@
import DefaultSettings from './default';
import * as settingsValues from './values';
const loadRaw = async(): Promise<any> => {
let { settings } = await browser.storage.local.get('settings');
if (!settings) {
return DefaultSettings;
}
return { ...DefaultSettings, ...settings as object };
};
const loadValue = async() => {
let settings = await loadRaw();
let value = JSON.parse(DefaultSettings.json);
if (settings.source === 'json') {
value = settingsValues.valueFromJson(settings.json);
} else if (settings.source === 'form') {
value = settingsValues.valueFromForm(settings.form);
}
if (!value.properties) {
value.properties = {};
}
return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value };
};
const save = (settings: any): Promise<any> => {
return browser.storage.local.set({
settings,
});
};
export { loadRaw, loadValue, save };

View file

@ -1,76 +0,0 @@
import * as operations from '../operations';
import * as properties from './properties';
const VALID_TOP_KEYS = ['keymaps', 'search', 'blacklist', 'properties'];
const VALID_OPERATION_VALUES = Object.keys(operations).map((key) => {
return operations[key];
});
const validateInvalidTopKeys = (settings: any): void => {
let invalidKey = Object.keys(settings).find((key) => {
return !VALID_TOP_KEYS.includes(key);
});
if (invalidKey) {
throw Error(`Unknown key: "${invalidKey}"`);
}
};
const validateKeymaps = (keymaps: any): void => {
for (let key of Object.keys(keymaps)) {
let value = keymaps[key];
if (!VALID_OPERATION_VALUES.includes(value.type)) {
throw Error(`Unknown operation: "${value.type}"`);
}
}
};
const validateSearch = (search: any): void => {
let engines = search.engines;
for (let key of Object.keys(engines)) {
if ((/\s/).test(key)) {
throw new Error(
`While space in search engine name is not allowed: "${key}"`
);
}
let url = engines[key];
if (!url.match(/{}/)) {
throw new Error(`No {}-placeholders in URL of "${key}"`);
}
if (url.match(/{}/g).length > 1) {
throw new Error(`Multiple {}-placeholders in URL of "${key}"`);
}
}
if (!search.default) {
throw new Error(`Default engine is not set`);
}
if (!Object.keys(engines).includes(search.default)) {
throw new Error(`Default engine "${search.default}" not found`);
}
};
const validateProperties = (props: any): void => {
for (let name of Object.keys(props)) {
if (!properties.types[name]) {
throw new Error(`Unknown property name: "${name}"`);
}
if (typeof props[name] !== properties.types[name]) {
throw new Error(`Invalid type for property: "${name}"`);
}
}
};
const validate = (settings: any): void => {
validateInvalidTopKeys(settings);
if (settings.keymaps) {
validateKeymaps(settings.keymaps);
}
if (settings.search) {
validateSearch(settings.search);
}
if (settings.properties) {
validateProperties(settings.properties);
}
};
export { validate };

View file

@ -1,108 +0,0 @@
import * as properties from './properties';
const operationFromFormName = (name: string): any => {
let [type, argStr] = name.split('?');
let args = {};
if (argStr) {
args = JSON.parse(argStr);
}
return { type, ...args };
};
const operationToFormName = (op: any): string => {
let type = op.type;
let args = { ...op };
delete args.type;
if (Object.keys(args).length === 0) {
return type;
}
return op.type + '?' + JSON.stringify(args);
};
const valueFromJson = (json: string): object => {
return JSON.parse(json);
};
const valueFromForm = (form: any): object => {
let keymaps: any = undefined;
if (form.keymaps) {
keymaps = {};
for (let name of Object.keys(form.keymaps)) {
let keys = form.keymaps[name];
keymaps[keys] = operationFromFormName(name);
}
}
let search: any = undefined;
if (form.search) {
search = { default: form.search.default };
if (form.search.engines) {
search.engines = {};
for (let [name, url] of form.search.engines) {
search.engines[name] = url;
}
}
}
return {
keymaps,
search,
blacklist: form.blacklist,
properties: form.properties
};
};
const jsonFromValue = (value: any): string => {
return JSON.stringify(value, undefined, 2);
};
const formFromValue = (value: any, allowedOps: any[]): any => {
let keymaps: any = undefined;
if (value.keymaps) {
let allowedSet = new Set(allowedOps);
keymaps = {};
for (let keys of Object.keys(value.keymaps)) {
let op = operationToFormName(value.keymaps[keys]);
if (allowedSet.has(op)) {
keymaps[op] = keys;
}
}
}
let search: any = undefined;
if (value.search) {
search = { default: value.search.default };
if (value.search.engines) {
search.engines = Object.keys(value.search.engines).map((name) => {
return [name, value.search.engines[name]];
});
}
}
let formProperties = { ...properties.defaults, ...value.properties };
return {
keymaps,
search,
blacklist: value.blacklist,
properties: formProperties,
};
};
const jsonFromForm = (form: any): string => {
return jsonFromValue(valueFromForm(form));
};
const formFromJson = (json: string, allowedOps: any[]): any => {
let value = valueFromJson(json);
return formFromValue(value, allowedOps);
};
export {
valueFromJson, valueFromForm, jsonFromValue, formFromValue,
jsonFromForm, formFromJson
};