tridactyl/src/help.ts
Oliver Blanthorn 905f0c50fe
Add unchecked gets for runtime usage.
Also make config.getAsync use the same type checking
and document a hitherto undocumented setting that it
surfaced.
2019-05-28 12:49:45 +01:00

228 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// This file is only loaded in tridacyl's help pages
import * as config from "@src/lib/config"
/** Create the element that should contain keybinding information */
function initTridactylSettingElem(
elem: HTMLElement,
kind: string,
): HTMLElement {
let bindingNode = elem.getElementsByClassName(`Tridactyl${kind}`)[0]
if (bindingNode) {
Array.from(bindingNode.children)
.filter(e => e.tagName === "SPAN")
.forEach(e => e.parentNode.removeChild(e))
} else {
// Otherwise, create it
bindingNode = document.createElement("p")
bindingNode.className = `TridactylSetting Tridactyl${kind}`
bindingNode.textContent = kind + ": "
elem.insertBefore(bindingNode, elem.children[2])
}
return bindingNode as HTMLElement
}
/** Return an object that maps excmd names to excmd documentation element */
function getCommandElements() {
return Array.from(
document.querySelectorAll(
".tsd-panel.tsd-member.tsd-kind-function.tsd-parent-kind-external-module",
),
).reduce((all, elem) => {
const fnName = Array.from(elem.children).find(e => e.tagName === "H3")
if (fnName) all[fnName.textContent] = elem
return all
}, {})
}
/** Update the doc with aliases fetched from the config */
async function addSetting(settingName: string) {
const commandElems = getCommandElements()
// We're ignoring composite because it combines multiple excmds
delete (commandElems as any).composite
// Initialize or reset the <p> element that will contain settings in each commandElem
const settingElems = Object.keys(commandElems).reduce(
(settingElems, cmdName) => {
settingElems[cmdName] = initTridactylSettingElem(
commandElems[cmdName],
settingName,
)
return settingElems
},
{},
)
const settings = await config.getAsyncDynamic(settingName)
// For each setting
for (const setting of Object.keys(settings)) {
let excmd = settings[setting].split(" ")
// How can we automatically detect what commands can be skipped?
excmd = ["composite", "fillcmdline", "current_url"].includes(excmd[0])
? excmd[1]
: excmd[0]
// Find the corresponding setting
while (settings[excmd]) {
excmd = settings[excmd].split(" ")
excmd = ["fillcmdline", "current_url"].includes(excmd[0])
? excmd[1]
: excmd[0]
}
// If there is an HTML element for settings that correspond to the excmd we just found
if (settingElems[excmd]) {
const settingSpan = document.createElement("span")
settingSpan.innerText = setting
settingSpan.title = settings[setting]
// Add the setting to the element
settingElems[excmd].appendChild(settingSpan)
settingElems[excmd].appendChild(document.createTextNode(" "))
}
}
// Remove all settingElems that do not have at least one setting
Object.values(settingElems)
.filter(
(e: HTMLElement) =>
!Array.from(e.children).find(c => c.tagName === "SPAN"),
)
.forEach((e: HTMLElement) => e.parentNode.removeChild(e))
}
async function onExcmdPageLoad() {
browser.storage.onChanged.addListener((changes, areaname) => {
if ("userconfig" in changes) {
// JSON.stringify for comparisons like it's 2012
["nmaps", "imaps", "ignoremaps", "inputmaps", "exaliases"].forEach(
kind => {
if (
JSON.stringify(changes.userconfig.newValue[kind]) !==
JSON.stringify(changes.userconfig.oldValue[kind])
)
addSetting(kind)
},
)
}
})
await Promise.all(
[
"nmaps",
"imaps",
"ignoremaps",
"inputmaps",
"exaliases",
"exmaps",
].map(addSetting),
)
// setCommandSetting() can change the height of nodes in the page so we need to scroll to the right place again
if (document.location.hash) {
/* tslint:disable:no-self-assignment */
document.location.hash = document.location.hash
}
}
function addSettingInputs() {
const inputClassName = " TridactylSettingInput "
const inputClassNameModified =
inputClassName + " TridactylSettingInputModified "
const onKeyUp = async ev => {
const input = ev.target
if (ev.key === "Enter") {
(window as any).tri.messaging.message(
"controller_background",
"acceptExCmd",
["set " + input.name + " " + input.value],
)
} else {
if (input.value === (await config.getAsync(input.name.split(".")))) {
input.className = inputClassName
} else {
input.className = inputClassNameModified
}
}
}
return Promise.all(
Array.from(document.querySelectorAll("a.tsd-anchor")).map(
async (a: HTMLAnchorElement) => {
const section = a.parentNode
const settingName = a.name.split(".")
const value = await config.getAsyncDynamic(...settingName)
if (!value) return console.log("Failed to grab value of ", a)
if (!["number", "boolean", "string"].includes(typeof value))
return console.log(
"Not embedding value of ",
a,
value,
" because not easily represented as string",
)
const input = document.createElement("input")
input.name = a.name
input.value = value
input.id = "TridactylSettingInput_" + input.name
input.className = inputClassName
input.addEventListener("keyup", onKeyUp)
const div = document.createElement("div")
div.appendChild(document.createTextNode("Current value:"))
div.appendChild(input)
section.appendChild(div)
config.addChangeListener(input.name as any, (_, newValue) => {
input.value = newValue
input.className = inputClassName
})
},
// Adding elements expands sections so if the user wants to see a specific hash, we need to focus it again
),
).then(_ => {
if (document.location.hash) {
/* tslint:disable:no-self-assignment */
document.location.hash = document.location.hash
}
})
}
function addResetConfigButton() {
const button = document.createElement("button")
button.innerText = "Reset Tridactyl config"
button.style.margin = "auto 50%"
button.style.minWidth = "200pt"
button.addEventListener("click", () => {
const sentence = "sanitise tridactylsync tridactyllocal tridactylhistory"
const p = prompt(
`Please write '${sentence}' without quotes in the following input field if you really want to reset your Tridactyl config.`,
)
if (p === sentence) {
(window as any).tri.messaging
.message("controller_background", "acceptExCmd", [sentence])
.then(_ => alert("Config reset!"))
} else {
alert(`Config not reset because '${p}' !== '${sentence}'`)
}
})
document.querySelector("div.container.container-main").appendChild(button)
}
function onSettingsPageLoad() {
addResetConfigButton()
return addSettingInputs()
}
addEventListener(
"load",
(() => {
switch (document.location.pathname) {
case "/static/docs/modules/_src_excmds_.html":
return onExcmdPageLoad
case "/static/docs/classes/_src_lib_config_.default_config.html":
return onSettingsPageLoad
}
return () => {}
})(),
)