parent
d01db82c0d
commit
a0882bbceb
48 changed files with 1617 additions and 902 deletions
@ -1,6 +1,7 @@ |
|||||||
export interface GlobalMark { |
export default interface GlobalMark { |
||||||
readonly tabId: number; |
readonly tabId: number; |
||||||
readonly url: string; |
readonly url: string; |
||||||
readonly x: number; |
readonly x: number; |
||||||
readonly y: number; |
readonly y: number; |
||||||
|
// eslint-disable-next-line semi
|
||||||
} |
} |
||||||
|
@ -1,59 +0,0 @@ |
|||||||
import DefaultSettings from '../../shared/settings/default'; |
|
||||||
import * as settingsValues from '../../shared/settings/values'; |
|
||||||
|
|
||||||
type SettingValue = { |
|
||||||
source: string, |
|
||||||
json: string, |
|
||||||
form: any |
|
||||||
} |
|
||||||
|
|
||||||
export default class Setting { |
|
||||||
private obj: SettingValue; |
|
||||||
|
|
||||||
constructor({ source, json, form }: SettingValue) { |
|
||||||
this.obj = { |
|
||||||
source, json, form |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
get source(): string { |
|
||||||
return this.obj.source; |
|
||||||
} |
|
||||||
|
|
||||||
get json(): string { |
|
||||||
return this.obj.json; |
|
||||||
} |
|
||||||
|
|
||||||
get form(): any { |
|
||||||
return this.obj.form; |
|
||||||
} |
|
||||||
|
|
||||||
value() { |
|
||||||
let value = JSON.parse(DefaultSettings.json); |
|
||||||
if (this.obj.source === 'json') { |
|
||||||
value = settingsValues.valueFromJson(this.obj.json); |
|
||||||
} else if (this.obj.source === 'form') { |
|
||||||
value = settingsValues.valueFromForm(this.obj.form); |
|
||||||
} |
|
||||||
if (!value.properties) { |
|
||||||
value.properties = {}; |
|
||||||
} |
|
||||||
return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value }; |
|
||||||
} |
|
||||||
|
|
||||||
serialize(): SettingValue { |
|
||||||
return this.obj; |
|
||||||
} |
|
||||||
|
|
||||||
static deserialize(obj: SettingValue): Setting { |
|
||||||
return new Setting({ source: obj.source, json: obj.json, form: obj.form }); |
|
||||||
} |
|
||||||
|
|
||||||
static defaultSettings() { |
|
||||||
return new Setting({ |
|
||||||
source: DefaultSettings.source, |
|
||||||
json: DefaultSettings.json, |
|
||||||
form: {}, |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
@ -1,12 +1,12 @@ |
|||||||
import Setting from '../domains/Setting'; |
import SettingData from '../../shared/SettingData'; |
||||||
|
|
||||||
export default class SettingRepository { |
export default class SettingRepository { |
||||||
async load(): Promise<any> { |
async load(): Promise<SettingData | null> { |
||||||
let { settings } = await browser.storage.local.get('settings'); |
let { settings } = await browser.storage.local.get('settings'); |
||||||
if (!settings) { |
if (!settings) { |
||||||
return null; |
return null; |
||||||
} |
} |
||||||
return Setting.deserialize(settings); |
return SettingData.valueOf(settings); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
|
@ -0,0 +1,15 @@ |
|||||||
|
import SettingData, { DefaultSettingData } from '../shared/SettingData'; |
||||||
|
|
||||||
|
export const load = async(): Promise<SettingData> => { |
||||||
|
let { settings } = await browser.storage.local.get('settings'); |
||||||
|
if (!settings) { |
||||||
|
return DefaultSettingData; |
||||||
|
} |
||||||
|
return SettingData.valueOf(settings); |
||||||
|
}; |
||||||
|
|
||||||
|
export const save = (data: SettingData) => { |
||||||
|
return browser.storage.local.set({ |
||||||
|
settings: data.toJSON(), |
||||||
|
}); |
||||||
|
}; |
@ -0,0 +1,414 @@ |
|||||||
|
import * as operations from './operations'; |
||||||
|
import Settings, * as settings from './Settings'; |
||||||
|
|
||||||
|
export class FormKeymaps { |
||||||
|
private data: {[op: string]: string}; |
||||||
|
|
||||||
|
constructor(data: {[op: string]: string}) { |
||||||
|
this.data = data; |
||||||
|
} |
||||||
|
|
||||||
|
toKeymaps(): settings.Keymaps { |
||||||
|
let keymaps: settings.Keymaps = {}; |
||||||
|
for (let name of Object.keys(this.data)) { |
||||||
|
let [type, argStr] = name.split('?'); |
||||||
|
let args = {}; |
||||||
|
if (argStr) { |
||||||
|
args = JSON.parse(argStr); |
||||||
|
} |
||||||
|
let key = this.data[name]; |
||||||
|
keymaps[key] = operations.valueOf({ type, ...args }); |
||||||
|
} |
||||||
|
return keymaps; |
||||||
|
} |
||||||
|
|
||||||
|
toJSON(): {[op: string]: string} { |
||||||
|
return this.data; |
||||||
|
} |
||||||
|
|
||||||
|
buildWithOverride(op: string, keys: string): FormKeymaps { |
||||||
|
let newData = { |
||||||
|
...this.data, |
||||||
|
[op]: keys, |
||||||
|
}; |
||||||
|
return new FormKeymaps(newData); |
||||||
|
} |
||||||
|
|
||||||
|
static valueOf(o: ReturnType<FormKeymaps['toJSON']>): FormKeymaps { |
||||||
|
let data: {[op: string]: string} = {}; |
||||||
|
for (let op of Object.keys(o)) { |
||||||
|
data[op] = o[op] as string; |
||||||
|
} |
||||||
|
return new FormKeymaps(data); |
||||||
|
} |
||||||
|
|
||||||
|
static fromKeymaps(keymaps: settings.Keymaps): FormKeymaps { |
||||||
|
let data: {[op: string]: string} = {}; |
||||||
|
for (let key of Object.keys(keymaps)) { |
||||||
|
let op = keymaps[key]; |
||||||
|
let args = { ...op }; |
||||||
|
delete args.type; |
||||||
|
|
||||||
|
let name = op.type; |
||||||
|
if (Object.keys(args).length > 0) { |
||||||
|
name += '?' + JSON.stringify(args); |
||||||
|
} |
||||||
|
data[name] = key; |
||||||
|
} |
||||||
|
return new FormKeymaps(data); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export class FormSearch { |
||||||
|
private default: string; |
||||||
|
|
||||||
|
private engines: string[][]; |
||||||
|
|
||||||
|
constructor(defaultEngine: string, engines: string[][]) { |
||||||
|
this.default = defaultEngine; |
||||||
|
this.engines = engines; |
||||||
|
} |
||||||
|
|
||||||
|
toSearchSettings(): settings.Search { |
||||||
|
return { |
||||||
|
default: this.default, |
||||||
|
engines: this.engines.reduce( |
||||||
|
(o: {[key: string]: string}, [name, url]) => { |
||||||
|
o[name] = url; |
||||||
|
return o; |
||||||
|
}, {}), |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
toJSON(): { |
||||||
|
default: string; |
||||||
|
engines: string[][]; |
||||||
|
} { |
||||||
|
return { |
||||||
|
default: this.default, |
||||||
|
engines: this.engines, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
static valueOf(o: ReturnType<FormSearch['toJSON']>): FormSearch { |
||||||
|
if (!Object.prototype.hasOwnProperty.call(o, 'default')) { |
||||||
|
throw new TypeError(`"default" field not set`); |
||||||
|
} |
||||||
|
if (!Object.prototype.hasOwnProperty.call(o, 'engines')) { |
||||||
|
throw new TypeError(`"engines" field not set`); |
||||||
|
} |
||||||
|
return new FormSearch(o.default, o.engines); |
||||||
|
} |
||||||
|
|
||||||
|
static fromSearch(search: settings.Search): FormSearch { |
||||||
|
let engines = Object.entries(search.engines).reduce( |
||||||
|
(o: string[][], [name, url]) => { |
||||||
|
return o.concat([[name, url]]); |
||||||
|
}, []); |
||||||
|
return new FormSearch(search.default, engines); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export class JSONSettings { |
||||||
|
private json: string; |
||||||
|
|
||||||
|
constructor(json: any) { |
||||||
|
this.json = json; |
||||||
|
} |
||||||
|
|
||||||
|
toSettings(): Settings { |
||||||
|
return settings.valueOf(JSON.parse(this.json)); |
||||||
|
} |
||||||
|
|
||||||
|
toJSON(): string { |
||||||
|
return this.json; |
||||||
|
} |
||||||
|
|
||||||
|
static valueOf(o: ReturnType<JSONSettings['toJSON']>): JSONSettings { |
||||||
|
return new JSONSettings(o); |
||||||
|
} |
||||||
|
|
||||||
|
static fromSettings(data: Settings): JSONSettings { |
||||||
|
return new JSONSettings(JSON.stringify(data, undefined, 2)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export class FormSettings { |
||||||
|
private keymaps: FormKeymaps; |
||||||
|
|
||||||
|
private search: FormSearch; |
||||||
|
|
||||||
|
private properties: settings.Properties; |
||||||
|
|
||||||
|
private blacklist: string[]; |
||||||
|
|
||||||
|
constructor( |
||||||
|
keymaps: FormKeymaps, |
||||||
|
search: FormSearch, |
||||||
|
properties: settings.Properties, |
||||||
|
blacklist: string[], |
||||||
|
) { |
||||||
|
this.keymaps = keymaps; |
||||||
|
this.search = search; |
||||||
|
this.properties = properties; |
||||||
|
this.blacklist = blacklist; |
||||||
|
} |
||||||
|
|
||||||
|
buildWithKeymaps(keymaps: FormKeymaps): FormSettings { |
||||||
|
return new FormSettings( |
||||||
|
keymaps, |
||||||
|
this.search, |
||||||
|
this.properties, |
||||||
|
this.blacklist, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
buildWithSearch(search: FormSearch): FormSettings { |
||||||
|
return new FormSettings( |
||||||
|
this.keymaps, |
||||||
|
search, |
||||||
|
this.properties, |
||||||
|
this.blacklist, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
buildWithProperties(props: settings.Properties): FormSettings { |
||||||
|
return new FormSettings( |
||||||
|
this.keymaps, |
||||||
|
this.search, |
||||||
|
props, |
||||||
|
this.blacklist, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
buildWithBlacklist(blacklist: string[]): FormSettings { |
||||||
|
return new FormSettings( |
||||||
|
this.keymaps, |
||||||
|
this.search, |
||||||
|
this.properties, |
||||||
|
blacklist, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
toSettings(): Settings { |
||||||
|
return settings.valueOf({ |
||||||
|
keymaps: this.keymaps.toKeymaps(), |
||||||
|
search: this.search.toSearchSettings(), |
||||||
|
properties: this.properties, |
||||||
|
blacklist: this.blacklist, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
toJSON(): { |
||||||
|
keymaps: ReturnType<FormKeymaps['toJSON']>; |
||||||
|
search: ReturnType<FormSearch['toJSON']>; |
||||||
|
properties: settings.Properties; |
||||||
|
blacklist: string[]; |
||||||
|
} { |
||||||
|
return { |
||||||
|
keymaps: this.keymaps.toJSON(), |
||||||
|
search: this.search.toJSON(), |
||||||
|
properties: this.properties, |
||||||
|
blacklist: this.blacklist, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
static valueOf(o: ReturnType<FormSettings['toJSON']>): FormSettings { |
||||||
|
for (let name of ['keymaps', 'search', 'properties', 'blacklist']) { |
||||||
|
if (!Object.prototype.hasOwnProperty.call(o, name)) { |
||||||
|
throw new Error(`"${name}" field not set`); |
||||||
|
} |
||||||
|
} |
||||||
|
return new FormSettings( |
||||||
|
FormKeymaps.valueOf(o.keymaps), |
||||||
|
FormSearch.valueOf(o.search), |
||||||
|
settings.propertiesValueOf(o.properties), |
||||||
|
settings.blacklistValueOf(o.blacklist), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
static fromSettings(data: Settings): FormSettings { |
||||||
|
return new FormSettings( |
||||||
|
FormKeymaps.fromKeymaps(data.keymaps), |
||||||
|
FormSearch.fromSearch(data.search), |
||||||
|
data.properties, |
||||||
|
data.blacklist); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export enum SettingSource { |
||||||
|
JSON = 'json', |
||||||
|
Form = 'form', |
||||||
|
} |
||||||
|
|
||||||
|
export default class SettingData { |
||||||
|
private source: SettingSource; |
||||||
|
|
||||||
|
private json?: JSONSettings; |
||||||
|
|
||||||
|
private form?: FormSettings; |
||||||
|
|
||||||
|
constructor({ |
||||||
|
source, json, form |
||||||
|
}: { |
||||||
|
source: SettingSource, |
||||||
|
json?: JSONSettings, |
||||||
|
form?: FormSettings, |
||||||
|
}) { |
||||||
|
this.source = source; |
||||||
|
this.json = json; |
||||||
|
this.form = form; |
||||||
|
} |
||||||
|
|
||||||
|
getSource(): SettingSource { |
||||||
|
return this.source; |
||||||
|
} |
||||||
|
|
||||||
|
getJSON(): JSONSettings { |
||||||
|
if (!this.json) { |
||||||
|
throw new TypeError('json settings not set'); |
||||||
|
} |
||||||
|
return this.json; |
||||||
|
} |
||||||
|
|
||||||
|
getForm(): FormSettings { |
||||||
|
if (!this.form) { |
||||||
|
throw new TypeError('form settings not set'); |
||||||
|
} |
||||||
|
return this.form; |
||||||
|
} |
||||||
|
|
||||||
|
toJSON(): any { |
||||||
|
switch (this.source) { |
||||||
|
case SettingSource.JSON: |
||||||
|
return { |
||||||
|
source: this.source, |
||||||
|
json: (this.json as JSONSettings).toJSON(), |
||||||
|
}; |
||||||
|
case SettingSource.Form: |
||||||
|
return { |
||||||
|
source: this.source, |
||||||
|
form: (this.form as FormSettings).toJSON(), |
||||||
|
}; |
||||||
|
} |
||||||
|
throw new Error(`unknown settings source: ${this.source}`); |
||||||
|
} |
||||||
|
|
||||||
|
toSettings(): Settings { |
||||||
|
switch (this.source) { |
||||||
|
case SettingSource.JSON: |
||||||
|
return this.getJSON().toSettings(); |
||||||
|
case SettingSource.Form: |
||||||
|
return this.getForm().toSettings(); |
||||||
|
} |
||||||
|
throw new Error(`unknown settings source: ${this.source}`); |
||||||
|
} |
||||||
|
|
||||||
|
static valueOf(o: { |
||||||
|
source: string; |
||||||
|
json?: string; |
||||||
|
form?: ReturnType<FormSettings['toJSON']>; |
||||||
|
}): SettingData { |
||||||
|
switch (o.source) { |
||||||
|
case SettingSource.JSON: |
||||||
|
return new SettingData({ |
||||||
|
source: o.source, |
||||||
|
json: JSONSettings.valueOf( |
||||||
|
o.json as ReturnType<JSONSettings['toJSON']>), |
||||||
|
}); |
||||||
|
case SettingSource.Form: |
||||||
|
return new SettingData({ |
||||||
|
source: o.source, |
||||||
|
form: FormSettings.valueOf( |
||||||
|
o.form as ReturnType<FormSettings['toJSON']>), |
||||||
|
}); |
||||||
|
} |
||||||
|
throw new Error(`unknown settings source: ${o.source}`); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const DefaultSettingData: SettingData = SettingData.valueOf({ |
||||||
|
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" }, |
||||||
|
"J": { "type": "tabs.next" }, |
||||||
|
"gT": { "type": "tabs.prev" }, |
||||||
|
"gt": { "type": "tabs.next" }, |
||||||
|
"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": [ |
||||||
|
] |
||||||
|
}`,
|
||||||
|
}); |
@ -0,0 +1,200 @@ |
|||||||
|
import * as operations from './operations'; |
||||||
|
import * as PropertyDefs from './property-defs'; |
||||||
|
|
||||||
|
export type Keymaps = {[key: string]: operations.Operation}; |
||||||
|
|
||||||
|
export interface Search { |
||||||
|
default: string; |
||||||
|
engines: { [key: string]: string }; |
||||||
|
} |
||||||
|
|
||||||
|
export interface Properties { |
||||||
|
hintchars: string; |
||||||
|
smoothscroll: boolean; |
||||||
|
complete: string; |
||||||
|
} |
||||||
|
|
||||||
|
export default interface Settings { |
||||||
|
keymaps: Keymaps; |
||||||
|
search: Search; |
||||||
|
properties: Properties; |
||||||
|
blacklist: string[]; |
||||||
|
// eslint-disable-next-line semi
|
||||||
|
} |
||||||
|
|
||||||
|
const DefaultProperties: Properties = PropertyDefs.defs.reduce( |
||||||
|
(o: {[name: string]: PropertyDefs.Type}, def) => { |
||||||
|
o[def.name] = def.defaultValue; |
||||||
|
return o; |
||||||
|
}, {}) as Properties; |
||||||
|
|
||||||
|
|
||||||
|
export const keymapsValueOf = (o: any): Keymaps => { |
||||||
|
return Object.keys(o).reduce((keymaps: Keymaps, key: string): Keymaps => { |
||||||
|
let op = operations.valueOf(o[key]); |
||||||
|
keymaps[key] = op; |
||||||
|
return keymaps; |
||||||
|
}, {}); |
||||||
|
}; |
||||||
|
|
||||||
|
export const searchValueOf = (o: any): Search => { |
||||||
|
if (typeof o.default !== 'string') { |
||||||
|
throw new TypeError('string field "default" not set"'); |
||||||
|
} |
||||||
|
for (let name of Object.keys(o.engines)) { |
||||||
|
if ((/\s/).test(name)) { |
||||||
|
throw new TypeError( |
||||||
|
`While space in the search engine not allowed: "${name}"`); |
||||||
|
} |
||||||
|
let url = o.engines[name]; |
||||||
|
if (typeof url !== 'string') { |
||||||
|
throw new TypeError('"engines" not an object of string'); |
||||||
|
} |
||||||
|
let matches = url.match(/{}/g); |
||||||
|
if (matches === null) { |
||||||
|
throw new TypeError(`No {}-placeholders in URL of "${name}"`); |
||||||
|
} else if (matches.length > 1) { |
||||||
|
throw new TypeError(`Multiple {}-placeholders in URL of "${name}"`); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
if (!Object.prototype.hasOwnProperty.call(o.engines, o.default)) { |
||||||
|
throw new TypeError(`Default engine "${o.default}" not found`); |
||||||
|
} |
||||||
|
return { |
||||||
|
default: o.default as string, |
||||||
|
engines: { ...o.engines }, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
export const propertiesValueOf = (o: any): Properties => { |
||||||
|
let defNames = new Set(PropertyDefs.defs.map(def => def.name)); |
||||||
|
let unknownName = Object.keys(o).find(name => !defNames.has(name)); |
||||||
|
if (unknownName) { |
||||||
|
throw new TypeError(`Unknown property name: "${unknownName}"`); |
||||||
|
} |
||||||
|
|
||||||
|
for (let def of PropertyDefs.defs) { |
||||||
|
if (!Object.prototype.hasOwnProperty.call(o, def.name)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (typeof o[def.name] !== def.type) { |
||||||
|
throw new TypeError(`property "${def.name}" is not ${def.type}`); |
||||||
|
} |
||||||
|
} |
||||||
|
return { |
||||||
|
...DefaultProperties, |
||||||
|
...o, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
export const blacklistValueOf = (o: any): string[] => { |
||||||
|
if (!Array.isArray(o)) { |
||||||
|
throw new TypeError(`"blacklist" is not an array of string`); |
||||||
|
} |
||||||
|
for (let x of o) { |
||||||
|
if (typeof x !== 'string') { |
||||||
|
throw new TypeError(`"blacklist" is not an array of string`); |
||||||
|
} |
||||||
|
} |
||||||
|
return o as string[]; |
||||||
|
}; |
||||||
|
|
||||||
|
export const valueOf = (o: any): Settings => { |
||||||
|
let settings = { ...DefaultSetting }; |
||||||
|
if (Object.prototype.hasOwnProperty.call(o, 'keymaps')) { |
||||||
|
settings.keymaps = keymapsValueOf(o.keymaps); |
||||||
|
} |
||||||
|
if (Object.prototype.hasOwnProperty.call(o, 'search')) { |
||||||
|
settings.search = searchValueOf(o.search); |
||||||
|
} |
||||||
|
if (Object.prototype.hasOwnProperty.call(o, 'properties')) { |
||||||
|
settings.properties = propertiesValueOf(o.properties); |
||||||
|
} |
||||||
|
if (Object.prototype.hasOwnProperty.call(o, 'blacklist')) { |
||||||
|
settings.blacklist = blacklistValueOf(o.blacklist); |
||||||
|
} |
||||||
|
return settings; |
||||||
|
}; |
||||||
|
|
||||||
|
const DefaultSetting: Settings = { |
||||||
|
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' }, |
||||||
|
'J': { 'type': 'tabs.next' }, |
||||||
|
'gT': { 'type': 'tabs.prev' }, |
||||||
|
'gt': { 'type': 'tabs.next' }, |
||||||
|
'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, 'background': 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', 'newTab': false }, |
||||||
|
'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: [] |
||||||
|
}; |
@ -0,0 +1,50 @@ |
|||||||
|
export type Type = string | number | boolean; |
||||||
|
|
||||||
|
export class Def { |
||||||
|
private name0: string; |
||||||
|
|
||||||
|
private description0: string; |
||||||
|
|
||||||
|
private defaultValue0: Type; |
||||||
|
|
||||||
|
constructor( |
||||||
|
name: string, |
||||||
|
description: string, |
||||||
|
defaultValue: Type, |
||||||
|
) { |
||||||
|
this.name0 = name; |
||||||
|
this.description0 = description; |
||||||
|
this.defaultValue0 = defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
public get name(): string { |
||||||
|
return this.name0; |
||||||
|
} |
||||||
|
|
||||||
|
public get defaultValue(): Type { |
||||||
|
return this.defaultValue0; |
||||||
|
} |
||||||
|
|
||||||
|
public get description(): Type { |
||||||
|
return this.description0; |
||||||
|
} |
||||||
|
|
||||||
|
public get type(): string { |
||||||
|
return typeof this.defaultValue; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const defs: Def[] = [ |
||||||
|
new Def( |
||||||
|
'hintchars', |
||||||
|
'hint characters on follow mode', |
||||||
|
'abcdefghijklmnopqrstuvwxyz'), |
||||||
|
new Def( |
||||||
|
'smoothscroll', |
||||||
|
'smooth scroll', |
||||||
|
false), |
||||||
|
new Def( |
||||||
|
'complete', |
||||||
|
'which are completed at the open page', |
||||||
|
'sbh'), |
||||||
|
]; |
@ -0,0 +1,50 @@ |
|||||||
|
export type Type = string | number | boolean; |
||||||
|
|
||||||
|
export class Def { |
||||||
|
private name0: string; |
||||||
|
|
||||||
|
private description0: string; |
||||||
|
|
||||||
|
private defaultValue0: Type; |
||||||
|
|
||||||
|
constructor( |
||||||
|
name: string, |
||||||
|
description: string, |
||||||
|
defaultValue: Type, |
||||||
|
) { |
||||||
|
this.name0 = name; |
||||||
|
this.description0 = description; |
||||||
|
this.defaultValue0 = defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
public get name(): string { |
||||||
|
return this.name0; |
||||||
|
} |
||||||
|
|
||||||
|
public get defaultValue(): Type { |
||||||
|
return this.defaultValue0; |
||||||
|
} |
||||||
|
|
||||||
|
public get description(): Type { |
||||||
|
return this.description0; |
||||||
|
} |
||||||
|
|
||||||
|
public get type(): string { |
||||||
|
return typeof this.defaultValue; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const defs: Def[] = [ |
||||||
|
new Def( |
||||||
|
'hintchars', |
||||||
|
'hint characters on follow mode', |
||||||
|
'abcdefghijklmnopqrstuvwxyz'), |
||||||
|
new Def( |
||||||
|
'smoothscroll', |
||||||
|
'smooth scroll', |
||||||
|
false), |
||||||
|
new Def( |
||||||
|
'complete', |
||||||
|
'which are completed at the open page', |
||||||
|
'sbh'), |
||||||
|
]; |
@ -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": [ |
|
||||||
] |
|
||||||
}`,
|
|
||||||
}; |
|
@ -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 }; |
|
@ -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 }; |
|
@ -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 }; |
|
@ -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 |
|
||||||
}; |
|
@ -0,0 +1,293 @@ |
|||||||
|
import SettingData, { |
||||||
|
FormKeymaps, JSONSettings, FormSettings, |
||||||
|
} from '../../src/shared/SettingData'; |
||||||
|
import Settings, { Keymaps } from '../../src/shared/Settings'; |
||||||
|
import { expect } from 'chai'; |
||||||
|
|
||||||
|
describe('shared/SettingData', () => { |
||||||
|
describe('FormKeymaps', () => { |
||||||
|
describe('#valueOF to #toKeymaps', () => { |
||||||
|
it('parses form keymaps and convert to operations', () => { |
||||||
|
let data = { |
||||||
|
'scroll.vertically?{"count":1}': 'j', |
||||||
|
'scroll.home': '0', |
||||||
|
} |
||||||
|
|
||||||
|
let keymaps = FormKeymaps.valueOf(data).toKeymaps(); |
||||||
|
expect(keymaps).to.deep.equal({ |
||||||
|
'j': { type: 'scroll.vertically', count: 1 }, |
||||||
|
'0': { type: 'scroll.home' }, |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('#fromKeymaps to #toJSON', () => { |
||||||
|
it('create from a Keymaps and create a JSON object', () => { |
||||||
|
let data: Keymaps = { |
||||||
|
'j': { type: 'scroll.vertically', count: 1 }, |
||||||
|
'0': { type: 'scroll.home' }, |
||||||
|
} |
||||||
|
|
||||||
|
let keymaps = FormKeymaps.fromKeymaps(data).toJSON(); |
||||||
|
expect(keymaps).to.deep.equal({ |
||||||
|
'scroll.vertically?{"count":1}': 'j', |
||||||
|
'scroll.home': '0', |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('JSONSettings', () => { |
||||||
|
describe('#valueOf to #toSettings', () => { |
||||||
|
it('parse object and create a Settings', () => { |
||||||
|
let o = `{
|
||||||
|
"keymaps": {}, |
||||||
|
"search": { |
||||||
|
"default": "google", |
||||||
|
"engines": { |
||||||
|
"google": "https://google.com/search?q={}" |
||||||
|
} |
||||||
|
}, |
||||||
|
"properties": { |
||||||
|
"hintchars": "abcdefghijklmnopqrstuvwxyz", |
||||||
|
"smoothscroll": false, |
||||||
|
"complete": "sbh" |
||||||
|
}, |
||||||
|
"blacklist": [] |
||||||
|
}`;
|
||||||
|
|
||||||
|
let settings = JSONSettings.valueOf(o).toSettings(); |
||||||
|
expect(settings).to.deep.equal(JSON.parse(o)); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('#fromSettings to #toJSON', () => { |
||||||
|
it('create from a Settings and create a JSON string', () => { |
||||||
|
let o = { |
||||||
|
keymaps: {}, |
||||||
|
search: { |
||||||
|
default: "google", |
||||||
|
engines: { |
||||||
|
google: "https://google.com/search?q={}", |
||||||
|
}, |
||||||
|
}, |
||||||
|
properties: { |
||||||
|
hintchars: "abcdefghijklmnopqrstuvwxyz", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}, |
||||||
|
blacklist: [], |
||||||
|
}; |
||||||
|
|
||||||
|
let json = JSONSettings.fromSettings(o).toJSON(); |
||||||
|
expect(JSON.parse(json)).to.deep.equal(o); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('FormSettings', () => { |
||||||
|
describe('#valueOf to #toSettings', () => { |
||||||
|
it('parse object and create a Settings', () => { |
||||||
|
let data = { |
||||||
|
keymaps: { |
||||||
|
'scroll.vertically?{"count":1}': 'j', |
||||||
|
'scroll.home': '0', |
||||||
|
}, |
||||||
|
search: { |
||||||
|
default: "google", |
||||||
|
engines: [ |
||||||
|
["google", "https://google.com/search?q={}"], |
||||||
|
] |
||||||
|
}, |
||||||
|
properties: { |
||||||
|
hintchars: "abcdefghijklmnopqrstuvwxyz", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}, |
||||||
|
blacklist: [] |
||||||
|
}; |
||||||
|
|
||||||
|
let settings = FormSettings.valueOf(data).toSettings(); |
||||||
|
expect(settings).to.deep.equal({ |
||||||
|
keymaps: { |
||||||
|
'j': { type: 'scroll.vertically', count: 1 }, |
||||||
|
'0': { type: 'scroll.home' }, |
||||||
|
}, |
||||||
|
search: { |
||||||
|
default: "google", |
||||||
|
engines: { |
||||||
|
"google": "https://google.com/search?q={}" |
||||||
|
} |
||||||
|
}, |
||||||
|
properties: { |
||||||
|
hintchars: "abcdefghijklmnopqrstuvwxyz", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}, |
||||||
|
blacklist: [] |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('#fromSettings to #toJSON', () => { |
||||||
|
it('create from a Settings and create a JSON string', () => { |
||||||
|
let data: Settings = { |
||||||
|
keymaps: { |
||||||
|
'j': { type: 'scroll.vertically', count: 1 }, |
||||||
|
'0': { type: 'scroll.home' }, |
||||||
|
}, |
||||||
|
search: { |
||||||
|
default: "google", |
||||||
|
engines: { |
||||||
|
"google": "https://google.com/search?q={}" |
||||||
|
} |
||||||
|
}, |
||||||
|
properties: { |
||||||
|
hintchars: "abcdefghijklmnopqrstuvwxyz", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}, |
||||||
|
blacklist: [] |
||||||
|
}; |
||||||
|
|
||||||
|
let json = FormSettings.fromSettings(data).toJSON(); |
||||||
|
expect(json).to.deep.equal({ |
||||||
|
keymaps: { |
||||||
|
'scroll.vertically?{"count":1}': 'j', |
||||||
|
'scroll.home': '0', |
||||||
|
}, |
||||||
|
search: { |
||||||
|
default: "google", |
||||||
|
engines: [ |
||||||
|
["google", "https://google.com/search?q={}"], |
||||||
|
] |
||||||
|
}, |
||||||
|
properties: { |
||||||
|
hintchars: "abcdefghijklmnopqrstuvwxyz", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}, |
||||||
|
blacklist: [], |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('SettingData', () => { |
||||||
|
describe('#valueOf to #toJSON', () => { |
||||||
|
it('parse object from json source', () => { |
||||||
|
let data = { |
||||||
|
source: 'json', |
||||||
|
json: `{
|
||||||
|
"keymaps": {}, |
||||||
|
"search": { |
||||||
|
"default": "google", |
||||||
|
"engines": { |
||||||
|
"google": "https://google.com/search?q={}" |
||||||
|
} |
||||||
|
}, |
||||||
|
"properties": { |
||||||
|
"hintchars": "abcdefghijklmnopqrstuvwxyz", |
||||||
|
"smoothscroll": false, |
||||||
|
"complete": "sbh" |
||||||
|
}, |
||||||
|
"blacklist": [] |
||||||
|
}`,
|
||||||
|
}; |
||||||
|
|
||||||
|
let j = SettingData.valueOf(data).toJSON(); |
||||||
|
expect(j.source).to.equal('json'); |
||||||
|
expect(j.json).to.be.a('string'); |
||||||
|
}); |
||||||
|
|
||||||
|
it('parse object from form source', () => { |
||||||
|
let data = { |
||||||
|
source: 'form', |
||||||
|
form: { |
||||||
|
keymaps: {}, |
||||||
|
search: { |
||||||
|
default: "yahoo", |
||||||
|
engines: [ |
||||||
|
['yahoo', 'https://yahoo.com/search?q={}'], |
||||||
|
], |
||||||
|
}, |
||||||
|
properties: { |
||||||
|
hintchars: "abcdefghijklmnopqrstuvwxyz", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}, |
||||||
|
blacklist: [], |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
let j = SettingData.valueOf(data).toJSON(); |
||||||
|
expect(j.source).to.equal('form'); |
||||||
|
expect(j.form).to.deep.equal({ |
||||||
|
keymaps: {}, |
||||||
|
search: { |
||||||
|
default: "yahoo", |
||||||
|
engines: [ |
||||||
|
['yahoo', 'https://yahoo.com/search?q={}'], |
||||||
|
], |
||||||
|
}, |
||||||
|
properties: { |
||||||
|
hintchars: "abcdefghijklmnopqrstuvwxyz", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}, |
||||||
|
blacklist: [], |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('#toSettings', () => { |
||||||
|
it('parse object from json source', () => { |
||||||
|
let data = { |
||||||
|
source: 'json', |
||||||
|
json: `{
|
||||||
|
"keymaps": {}, |
||||||
|
"search": { |
||||||
|
"default": "google", |
||||||
|
"engines": { |
||||||
|
"google": "https://google.com/search?q={}" |
||||||
|
} |
||||||
|
}, |
||||||
|
"properties": { |
||||||
|
"hintchars": "abcdefghijklmnopqrstuvwxyz", |
||||||
|
"smoothscroll": false, |
||||||
|
"complete": "sbh" |
||||||
|
}, |
||||||
|
"blacklist": [] |
||||||
|
}`,
|
||||||
|
}; |
||||||
|
|
||||||
|
let settings = SettingData.valueOf(data).toSettings(); |
||||||
|
expect(settings.search.default).to.equal('google'); |
||||||
|
}); |
||||||
|
|
||||||
|
it('parse object from form source', () => { |
||||||
|
let data = { |
||||||
|
source: 'form', |
||||||
|
form: { |
||||||
|
keymaps: {}, |
||||||
|
search: { |
||||||
|
default: "yahoo", |
||||||
|
engines: [ |
||||||
|
['yahoo', 'https://yahoo.com/search?q={}'], |
||||||
|
], |
||||||
|
}, |
||||||
|
properties: { |
||||||
|
hintchars: "abcdefghijklmnopqrstuvwxyz", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}, |
||||||
|
blacklist: [], |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
let settings = SettingData.valueOf(data).toSettings(); |
||||||
|
expect(settings.search.default).to.equal('yahoo'); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,190 @@ |
|||||||
|
import * as settings from '../../src/shared/Settings'; |
||||||
|
import { expect } from 'chai'; |
||||||
|
|
||||||
|
describe('Settings', () => { |
||||||
|
describe('#keymapsValueOf', () => { |
||||||
|
it('returns empty object by empty settings', () => { |
||||||
|
let keymaps = settings.keymapsValueOf({}); |
||||||
|
expect(keymaps).to.be.empty; |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns keymaps by valid settings', () => { |
||||||
|
let keymaps = settings.keymapsValueOf({ |
||||||
|
k: { type: "scroll.vertically", count: -1 }, |
||||||
|
j: { type: "scroll.vertically", count: 1 }, |
||||||
|
}); |
||||||
|
|
||||||
|
expect(keymaps['k']).to.deep.equal({ type: "scroll.vertically", count: -1 }); |
||||||
|
expect(keymaps['j']).to.deep.equal({ type: "scroll.vertically", count: 1 }); |
||||||
|
}); |
||||||
|
|
||||||
|
it('throws a TypeError by invalid settings', () => { |
||||||
|
expect(() => settings.keymapsValueOf(null)).to.throw(TypeError); |
||||||
|
expect(() => settings.keymapsValueOf({ |
||||||
|
k: { type: "invalid.operation" }, |
||||||
|
})).to.throw(TypeError); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('#searchValueOf', () => { |
||||||
|
it('returns search settings by valid settings', () => { |
||||||
|
let search = settings.searchValueOf({ |
||||||
|
default: "google", |
||||||
|
engines: { |
||||||
|
"google": "https://google.com/search?q={}", |
||||||
|
"yahoo": "https://search.yahoo.com/search?p={}", |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
expect(search).to.deep.equal({ |
||||||
|
default: "google", |
||||||
|
engines: { |
||||||
|
"google": "https://google.com/search?q={}", |
||||||
|
"yahoo": "https://search.yahoo.com/search?p={}", |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('throws a TypeError by invalid settings', () => { |
||||||
|
expect(() => settings.searchValueOf(null)).to.throw(TypeError); |
||||||
|
expect(() => settings.searchValueOf({})).to.throw(TypeError); |
||||||
|
expect(() => settings.searchValueOf([])).to.throw(TypeError); |
||||||
|
expect(() => settings.searchValueOf({ |
||||||
|
default: 123, |
||||||
|
engines: {} |
||||||
|
})).to.throw(TypeError); |
||||||
|
expect(() => settings.searchValueOf({ |
||||||
|
default: "google", |
||||||
|
engines: { |
||||||
|
"google": 123456, |
||||||
|
} |
||||||
|
})).to.throw(TypeError); |
||||||
|
expect(() => settings.searchValueOf({ |
||||||
|
default: "wikipedia", |
||||||
|
engines: { |
||||||
|
"google": "https://google.com/search?q={}", |
||||||
|
"yahoo": "https://search.yahoo.com/search?p={}", |
||||||
|
} |
||||||
|
})).to.throw(TypeError); |
||||||
|
expect(() => settings.searchValueOf({ |
||||||
|
default: "g o o g l e", |
||||||
|
engines: { |
||||||
|
"g o o g l e": "https://google.com/search?q={}", |
||||||
|
} |
||||||
|
})).to.throw(TypeError); |
||||||
|
expect(() => settings.searchValueOf({ |
||||||
|
default: "google", |
||||||
|
engines: { |
||||||
|
"google": "https://google.com/search", |
||||||
|
} |
||||||
|
})).to.throw(TypeError); |
||||||
|
expect(() => settings.searchValueOf({ |
||||||
|
default: "google", |
||||||
|
engines: { |
||||||
|
"google": "https://google.com/search?q={}&r={}", |
||||||
|
} |
||||||
|
})).to.throw(TypeError); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('#propertiesValueOf', () => { |
||||||
|
it('returns with default properties by empty settings', () => { |
||||||
|
let props = settings.propertiesValueOf({}); |
||||||
|
expect(props).to.deep.equal({ |
||||||
|
hintchars: "abcdefghijklmnopqrstuvwxyz", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}) |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns properties by valid settings', () => { |
||||||
|
let props = settings.propertiesValueOf({ |
||||||
|
hintchars: "abcdefgh", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}); |
||||||
|
|
||||||
|
expect(props).to.deep.equal({ |
||||||
|
hintchars: "abcdefgh", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('throws a TypeError by invalid settings', () => { |
||||||
|
expect(() => settings.keymapsValueOf(null)).to.throw(TypeError); |
||||||
|
expect(() => settings.keymapsValueOf({ |
||||||
|
smoothscroll: 'false', |
||||||
|
})).to.throw(TypeError); |
||||||
|
expect(() => settings.keymapsValueOf({ |
||||||
|
unknown: 'xyz' |
||||||
|
})).to.throw(TypeError); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('#blacklistValueOf', () => { |
||||||
|
it('returns empty array by empty settings', () => { |
||||||
|
let blacklist = settings.blacklistValueOf([]); |
||||||
|
expect(blacklist).to.be.empty; |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns blacklist by valid settings', () => { |
||||||
|
let blacklist = settings.blacklistValueOf([ |
||||||
|
"github.com", |
||||||
|
"circleci.com", |
||||||
|
]); |
||||||
|
|
||||||
|
expect(blacklist).to.deep.equal([ |
||||||
|
"github.com", |
||||||
|
"circleci.com", |
||||||
|
]); |
||||||
|
}); |
||||||
|
|
||||||
|
it('throws a TypeError by invalid settings', () => { |
||||||
|
expect(() => settings.blacklistValueOf(null)).to.throw(TypeError); |
||||||
|
expect(() => settings.blacklistValueOf({})).to.throw(TypeError); |
||||||
|
expect(() => settings.blacklistValueOf([1,2,3])).to.throw(TypeError); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('#valueOf', () => { |
||||||
|
it('returns settings by valid settings', () => { |
||||||
|
let x = settings.valueOf({ |
||||||
|
keymaps: {}, |
||||||
|
"search": { |
||||||
|
"default": "google", |
||||||
|
"engines": { |
||||||
|
"google": "https://google.com/search?q={}", |
||||||
|
} |
||||||
|
}, |
||||||
|
"properties": {}, |
||||||
|
"blacklist": [] |
||||||
|
}); |
||||||
|
|
||||||
|
expect(x).to.deep.equal({ |
||||||
|
keymaps: {}, |
||||||
|
search: { |
||||||
|
default: "google", |
||||||
|
engines: { |
||||||
|
google: "https://google.com/search?q={}", |
||||||
|
} |
||||||
|
}, |
||||||
|
properties: { |
||||||
|
hintchars: "abcdefghijklmnopqrstuvwxyz", |
||||||
|
smoothscroll: false, |
||||||
|
complete: "sbh" |
||||||
|
}, |
||||||
|
blacklist: [] |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('sets default settings', () => { |
||||||
|
let value = settings.valueOf({}); |
||||||
|
expect(value.keymaps).to.not.be.empty; |
||||||
|
expect(value.properties).to.not.be.empty; |
||||||
|
expect(value.search.default).to.be.a('string'); |
||||||
|
expect(value.search.engines).to.be.an('object'); |
||||||
|
expect(value.blacklist).to.be.empty; |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,18 @@ |
|||||||
|
import * as settings from 'shared/settings'; |
||||||
|
|
||||||
|
describe('properties', () => { |
||||||
|
describe('Def class', () => { |
||||||
|
it('returns property definitions', () => { |
||||||
|
let def = new proerties.Def( |
||||||
|
'smoothscroll', |
||||||
|
'smooth scroll', |
||||||
|
false); |
||||||
|
|
||||||
|
expect(def.name).to.equal('smoothscroll'); |
||||||
|
expect(def.describe).to.equal('smooth scroll'); |
||||||
|
expect(def.defaultValue).to.equal(false); |
||||||
|
expect(def.type).to.equal('boolean'); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
@ -0,0 +1,18 @@ |
|||||||
|
import * as settings from 'shared/settings'; |
||||||
|
|
||||||
|
describe('properties', () => { |
||||||
|
describe('Def class', () => { |
||||||
|
it('returns property definitions', () => { |
||||||
|
let def = new proerties.Def( |
||||||
|
'smoothscroll', |
||||||
|
'smooth scroll', |
||||||
|
false); |
||||||
|
|
||||||
|
expect(def.name).to.equal('smoothscroll'); |
||||||
|
expect(def.describe).to.equal('smooth scroll'); |
||||||
|
expect(def.defaultValue).to.equal(false); |
||||||
|
expect(def.type).to.equal('boolean'); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
@ -1,81 +0,0 @@ |
|||||||
import { validate } from 'shared/settings/validator'; |
|
||||||
|
|
||||||
describe("setting validator", () => { |
|
||||||
describe("unknown top keys", () => { |
|
||||||
it('throws an error for unknown settings', () => { |
|
||||||
let settings = { keymaps: {}, poison: 123 }; |
|
||||||
let fn = validate.bind(undefined, settings) |
|
||||||
expect(fn).to.throw(Error, 'poison'); |
|
||||||
}) |
|
||||||
}); |
|
||||||
|
|
||||||
describe("keymaps settings", () => { |
|
||||||
it('throws an error for unknown operation', () => { |
|
||||||
let settings = { |
|
||||||
keymaps: { |
|
||||||
a: { 'type': 'scroll.home' }, |
|
||||||
b: { 'type': 'poison.dressing' }, |
|
||||||
} |
|
||||||
}; |
|
||||||
let fn = validate.bind(undefined, settings) |
|
||||||
expect(fn).to.throw(Error, 'poison.dressing'); |
|
||||||
}); |
|
||||||
}); |
|
||||||
|
|
||||||
describe("search settings", () => { |
|
||||||
it('throws an error for invalid search engine name', () => { |
|
||||||
let settings = { |
|
||||||
search: { |
|
||||||
default: 'google', |
|
||||||
engines: { |
|
||||||
'google': 'https://google.com/search?q={}', |
|
||||||
'cherry pie': 'https://cherypie.com/search?q={}', |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
let fn = validate.bind(undefined, settings) |
|
||||||
expect(fn).to.throw(Error, 'cherry pie'); |
|
||||||
}); |
|
||||||
|
|
||||||
it('throws an error for no {}-placeholder', () => { |
|
||||||
let settings = { |
|
||||||
search: { |
|
||||||
default: 'google', |
|
||||||
engines: { |
|
||||||
'google': 'https://google.com/search?q={}', |
|
||||||
'yahoo': 'https://search.yahoo.com/search', |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
let fn = validate.bind(undefined, settings) |
|
||||||
expect(fn).to.throw(Error, 'yahoo'); |
|
||||||
}); |
|
||||||
|
|
||||||
it('throws an error for no default engines', () => { |
|
||||||
let settings = { |
|
||||||
search: { |
|
||||||
engines: { |
|
||||||
'google': 'https://google.com/search?q={}', |
|
||||||
'yahoo': 'https://search.yahoo.com/search?q={}', |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
let fn = validate.bind(undefined, settings) |
|
||||||
expect(fn).to.throw(Error, 'Default engine'); |
|
||||||
}); |
|
||||||
|
|
||||||
it('throws an error for invalid default engine', () => { |
|
||||||
let settings = { |
|
||||||
search: { |
|
||||||
default: 'twitter', |
|
||||||
engines: { |
|
||||||
'google': 'https://google.com/search?q={}', |
|
||||||
'yahoo': 'https://search.yahoo.com/search?q={}', |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
let fn = validate.bind(undefined, settings) |
|
||||||
expect(fn).to.throw(Error, 'twitter'); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}); |
|
@ -1,138 +0,0 @@ |
|||||||
import * as values from 'shared/settings/values'; |
|
||||||
|
|
||||||
describe("settings values", () => { |
|
||||||
describe('valueFromJson', () => { |
|
||||||
it('return object from json string', () => { |
|
||||||
let json = `{
|
|
||||||
"keymaps": { "0": {"type": "scroll.home"}}, |
|
||||||
"search": { "default": "google", "engines": { "google": "https://google.com/search?q={}" }}, |
|
||||||
"blacklist": [ "*.slack.com"], |
|
||||||
"properties": { |
|
||||||
"mystr": "value", |
|
||||||
"mynum": 123, |
|
||||||
"mybool": true |
|
||||||
} |
|
||||||
}`;
|
|
||||||
let value = values.valueFromJson(json); |
|
||||||
|
|
||||||
expect(value.keymaps).to.deep.equal({ 0: {type: "scroll.home"}}); |
|
||||||
expect(value.search).to.deep.equal({ default: "google", engines: { google: "https://google.com/search?q={}"} }); |
|
||||||
expect(value.blacklist).to.deep.equal(["*.slack.com"]); |
|
||||||
expect(value.properties).to.have.property('mystr', 'value'); |
|
||||||
expect(value.properties).to.have.property('mynum', 123); |
|
||||||
expect(value.properties).to.have.property('mybool', true); |
|
||||||
}); |
|
||||||
}); |
|
||||||
|
|
||||||
describe('valueFromForm', () => { |
|
||||||
it('returns value from form', () => { |
|
||||||
let form = { |
|
||||||
keymaps: { |
|
||||||
'scroll.vertically?{"count":1}': 'j', |
|
||||||
'scroll.home': '0', |
|
||||||
}, |
|
||||||
search: { |
|
||||||
default: 'google', |
|
||||||
engines: [['google', 'https://google.com/search?q={}']], |
|
||||||
}, |
|
||||||
blacklist: ['*.slack.com'], |
|
||||||
"properties": { |
|
||||||
"mystr": "value", |
|
||||||
"mynum": 123, |
|
||||||
"mybool": true, |
|
||||||
} |
|
||||||
}; |
|
||||||
let value = values.valueFromForm(form); |
|
||||||
|
|
||||||
expect(value.keymaps).to.have.deep.property('j', { type: "scroll.vertically", count: 1 }); |
|
||||||
expect(value.keymaps).to.have.deep.property('0', { type: "scroll.home" }); |
|
||||||
expect(JSON.stringify(value.search)).to.deep.equal(JSON.stringify({ default: "google", engines: { google: "https://google.com/search?q={}"} })); |
|
||||||
expect(value.search).to.deep.equal({ default: "google", engines: { google: "https://google.com/search?q={}"} }); |
|
||||||
expect(value.blacklist).to.deep.equal(["*.slack.com"]); |
|
||||||
expect(value.properties).to.have.property('mystr', 'value'); |
|
||||||
expect(value.properties).to.have.property('mynum', 123); |
|
||||||
expect(value.properties).to.have.property('mybool', true); |
|
||||||
}); |
|
||||||
|
|
||||||
it('convert from empty form', () => { |
|
||||||
let form = {}; |
|
||||||
let value = values.valueFromForm(form); |
|
||||||
expect(value).to.not.have.key('keymaps'); |
|
||||||
expect(value).to.not.have.key('search'); |
|
||||||
expect(value).to.not.have.key('blacklist'); |
|
||||||
expect(value).to.not.have.key('properties'); |
|
||||||
}); |
|
||||||
|
|
||||||
it('override keymaps', () => { |
|
||||||
let form = { |
|
||||||
keymaps: { |
|
||||||
'scroll.vertically?{"count":1}': 'j', |
|
||||||
'scroll.vertically?{"count":-1}': 'j', |
|
||||||
} |
|
||||||
}; |
|
||||||
let value = values.valueFromForm(form); |
|
||||||
|
|
||||||
expect(value.keymaps).to.have.key('j'); |
|
||||||
}); |
|
||||||
|
|
||||||
it('override search engine', () => { |
|
||||||
let form = { |
|
||||||
search: { |
|
||||||
default: 'google', |
|
||||||
engines: [ |
|
||||||
['google', 'https://google.com/search?q={}'], |
|
||||||
['google', 'https://google.co.jp/search?q={}'], |
|
||||||
] |
|
||||||
} |
|
||||||
}; |
|
||||||
let value = values.valueFromForm(form); |
|
||||||
|
|
||||||
expect(value.search.engines).to.have.property('google', 'https://google.co.jp/search?q={}'); |
|
||||||
}); |
|
||||||
}); |
|
||||||
|
|
||||||
describe('jsonFromValue', () => { |
|
||||||
}); |
|
||||||
|
|
||||||
describe('formFromValue', () => { |
|
||||||
it('convert empty value to form', () => { |
|
||||||
let value = {}; |
|
||||||
let form = values.formFromValue(value); |
|
||||||
|
|
||||||
expect(value).to.not.have.key('keymaps'); |
|
||||||
expect(value).to.not.have.key('search'); |
|
||||||
expect(value).to.not.have.key('blacklist'); |
|
||||||
}); |
|
||||||
|
|
||||||
it('convert value to form', () => { |
|
||||||
let value = { |
|
||||||
keymaps: { |
|
||||||
j: { type: 'scroll.vertically', count: 1 }, |
|
||||||
JJ: { type: 'scroll.vertically', count: 100 }, |
|
||||||
0: { type: 'scroll.home' }, |
|
||||||
}, |
|
||||||
search: { default: 'google', engines: { google: 'https://google.com/search?q={}' }}, |
|
||||||
blacklist: [ '*.slack.com'], |
|
||||||
properties: { |
|
||||||
"mystr": "value", |
|
||||||
"mynum": 123, |
|
||||||
"mybool": true, |
|
||||||
} |
|
||||||
}; |
|
||||||
let allowed = ['scroll.vertically?{"count":1}', 'scroll.home' ]; |
|
||||||
let form = values.formFromValue(value, allowed); |
|
||||||
|
|
||||||
expect(form.keymaps).to.have.property('scroll.vertically?{"count":1}', 'j'); |
|
||||||
expect(form.keymaps).to.not.have.property('scroll.vertically?{"count":100}'); |
|
||||||
expect(form.keymaps).to.have.property('scroll.home', '0'); |
|
||||||
expect(Object.keys(form.keymaps)).to.have.lengthOf(2); |
|
||||||
expect(form.search).to.have.property('default', 'google'); |
|
||||||
expect(form.search).to.have.deep.property('engines', [['google', 'https://google.com/search?q={}']]); |
|
||||||
expect(form.blacklist).to.have.lengthOf(1); |
|
||||||
expect(form.blacklist).to.include('*.slack.com'); |
|
||||||
expect(form.properties).to.have.property('mystr', 'value'); |
|
||||||
expect(form.properties).to.have.property('mynum', 123); |
|
||||||
expect(form.properties).to.have.property('mybool', true); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}); |
|
Reference in new issue