mirror of
https://github.com/vale981/tridactyl
synced 2025-03-05 17:41:40 -05:00
Merge branch 'master' into i236-common-ex-aliases
This commit is contained in:
commit
2441fba8c0
17 changed files with 269 additions and 72 deletions
|
@ -76,7 +76,7 @@ NOTE: key modifiers (eg: control, alt) are not supported yet. See the FAQ below.
|
||||||
|
|
||||||
- Why doesn't Tridactyl work on websites with frames?
|
- Why doesn't Tridactyl work on websites with frames?
|
||||||
|
|
||||||
It should work on some frames now. See [#122](https://github.com/cmcaine/tridactyl/issues/122).
|
It should work on some frames now. See [#122](https://github.com/cmcaine/tridactyl/issues/122).
|
||||||
|
|
||||||
- Can I change proxy via commands?
|
- Can I change proxy via commands?
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ NOTE: key modifiers (eg: control, alt) are not supported yet. See the FAQ below.
|
||||||
|
|
||||||
- Why doesn't Tridactyl work on some pages?
|
- Why doesn't Tridactyl work on some pages?
|
||||||
|
|
||||||
One possible reason is that the site has a strict content security policy. We can rewrite these to make Tridactyl work, but we do not want to worsen the security of sensitive pages, so it is taking us a little while. See #112.
|
One possible reason is that the site has a strict content security policy. We can rewrite these to make Tridactyl work, but we do not want to worsen the security of sensitive pages, so it is taking us a little while. See [#112](https://github.com/cmcaine/tridactyl/issues/112).
|
||||||
|
|
||||||
- How can I know which mode I'm in/have a status line?
|
- How can I know which mode I'm in/have a status line?
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
/** Inject an input element into unsuspecting webpages and provide an API for interaction with tridactyl */
|
/** Inject an input element into unsuspecting webpages and provide an API for interaction with tridactyl */
|
||||||
|
|
||||||
|
import Logger from './logging'
|
||||||
|
const logger = new Logger('messaging')
|
||||||
|
|
||||||
/* TODO:
|
/* TODO:
|
||||||
CSS
|
CSS
|
||||||
Friendliest-to-webpage way of injecting commandline bar?
|
Friendliest-to-webpage way of injecting commandline bar?
|
||||||
|
@ -16,14 +19,13 @@ let cmdline_iframe: HTMLIFrameElement = undefined
|
||||||
function init(){
|
function init(){
|
||||||
if (cmdline_iframe === undefined && window.document.body !== null) {
|
if (cmdline_iframe === undefined && window.document.body !== null) {
|
||||||
try {
|
try {
|
||||||
console.log("INIT")
|
|
||||||
cmdline_iframe = window.document.createElement("iframe")
|
cmdline_iframe = window.document.createElement("iframe")
|
||||||
cmdline_iframe.setAttribute("src", browser.extension.getURL("static/commandline.html"))
|
cmdline_iframe.setAttribute("src", browser.extension.getURL("static/commandline.html"))
|
||||||
cmdline_iframe.setAttribute("id", "cmdline_iframe")
|
cmdline_iframe.setAttribute("id", "cmdline_iframe")
|
||||||
hide()
|
hide()
|
||||||
window.document.body.appendChild(cmdline_iframe)
|
window.document.body.appendChild(cmdline_iframe)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Couldn't initialise cmdline_iframe!", e)
|
logger.error("Couldn't initialise cmdline_iframe!", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import * as SELF from './commandline_frame'
|
||||||
import './number.clamp'
|
import './number.clamp'
|
||||||
import state from './state'
|
import state from './state'
|
||||||
import * as Config from './config'
|
import * as Config from './config'
|
||||||
|
import Logger from './logging'
|
||||||
|
const logger = new Logger('cmdline')
|
||||||
|
|
||||||
let activeCompletions: Completions.CompletionSource[] = undefined
|
let activeCompletions: Completions.CompletionSource[] = undefined
|
||||||
let completionsDiv = window.document.getElementById("completions") as HTMLElement
|
let completionsDiv = window.document.getElementById("completions") as HTMLElement
|
||||||
|
@ -161,7 +163,7 @@ clInput.addEventListener("input", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire each completion and add a callback to resize area
|
// Fire each completion and add a callback to resize area
|
||||||
console.log(activeCompletions)
|
logger.debug(activeCompletions)
|
||||||
activeCompletions.forEach(comp =>
|
activeCompletions.forEach(comp =>
|
||||||
comp.filter(newCmd).then(() => resizeArea())
|
comp.filter(newCmd).then(() => resizeArea())
|
||||||
)
|
)
|
||||||
|
@ -212,9 +214,7 @@ function history(n){
|
||||||
|
|
||||||
/* Send the commandline to the background script and await response. */
|
/* Send the commandline to the background script and await response. */
|
||||||
function process() {
|
function process() {
|
||||||
console.log(clInput.value)
|
|
||||||
const command = getCompletion() || clInput.value
|
const command = getCompletion() || clInput.value
|
||||||
console.log(command)
|
|
||||||
|
|
||||||
hide_and_clear()
|
hide_and_clear()
|
||||||
|
|
||||||
|
@ -225,7 +225,6 @@ function process() {
|
||||||
) {
|
) {
|
||||||
state.cmdHistory = state.cmdHistory.concat([command])
|
state.cmdHistory = state.cmdHistory.concat([command])
|
||||||
}
|
}
|
||||||
console.log(state.cmdHistory)
|
|
||||||
cmdline_history_position = 0
|
cmdline_history_position = 0
|
||||||
|
|
||||||
sendExstr(command)
|
sendExstr(command)
|
||||||
|
@ -263,7 +262,7 @@ export function setClipboard(content: string) {
|
||||||
scratchpad.select()
|
scratchpad.select()
|
||||||
if (document.execCommand("Copy")) {
|
if (document.execCommand("Copy")) {
|
||||||
// // todo: Maybe we can consider to using some logger and show it with status bar in the future
|
// // todo: Maybe we can consider to using some logger and show it with status bar in the future
|
||||||
console.log('set clipboard:', scratchpad.value)
|
logger.info('set clipboard:', scratchpad.value)
|
||||||
} else throw "Failed to copy!"
|
} else throw "Failed to copy!"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -272,7 +271,6 @@ export function getClipboard() {
|
||||||
return applyWithTmpTextArea(scratchpad => {
|
return applyWithTmpTextArea(scratchpad => {
|
||||||
scratchpad.focus()
|
scratchpad.focus()
|
||||||
document.execCommand("Paste")
|
document.execCommand("Paste")
|
||||||
console.log('get clipboard', scratchpad.textContent)
|
|
||||||
return scratchpad.textContent
|
return scratchpad.textContent
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -622,17 +622,21 @@ export class BufferCompletionSource extends CompletionSourceFuse {
|
||||||
super.setStateFromScore(scoredOpts, true)
|
super.setStateFromScore(scoredOpts, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Score with fuse unless query is an integer or a single # */
|
/** Score with fuse unless query is a single # or looks like a buffer index */
|
||||||
scoredOptions(query: string, options = this.options): ScoredOption[] {
|
scoredOptions(query: string, options = this.options): ScoredOption[] {
|
||||||
const args = query.split(/\s+/gu)
|
const args = query.trim().split(/\s+/gu)
|
||||||
if (args.length <= 2) {
|
if (args.length === 1) {
|
||||||
|
// if query is an integer n and |n| < options.length
|
||||||
if (Number.isInteger(Number(args[0]))) {
|
if (Number.isInteger(Number(args[0]))) {
|
||||||
const index = (Number(args[0]) - 1).mod(options.length)
|
let index = Number(args[0]) - 1
|
||||||
return [{
|
if (Math.abs(index) < options.length) {
|
||||||
index,
|
index = index.mod(options.length)
|
||||||
option: options[index],
|
return [{
|
||||||
score: 0,
|
index,
|
||||||
}]
|
option: options[index],
|
||||||
|
score: 0,
|
||||||
|
}]
|
||||||
|
}
|
||||||
} else if (args[0] === '#') {
|
} else if (args[0] === '#') {
|
||||||
for (const [index, option] of enumerate(options)) {
|
for (const [index, option] of enumerate(options)) {
|
||||||
if (option.isAlternative) {
|
if (option.isAlternative) {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
//
|
//
|
||||||
// Really, we'd like a way of just letting things use the variables
|
// Really, we'd like a way of just letting things use the variables
|
||||||
//
|
//
|
||||||
|
|
||||||
const CONFIGNAME = "userconfig"
|
const CONFIGNAME = "userconfig"
|
||||||
|
|
||||||
type StorageMap = browser.storage.StorageMap
|
type StorageMap = browser.storage.StorageMap
|
||||||
|
@ -147,6 +148,16 @@ const DEFAULTS = o({
|
||||||
"ttsrate": 1, // 0.1 to 10
|
"ttsrate": 1, // 0.1 to 10
|
||||||
"ttspitch": 1, // 0 to 2
|
"ttspitch": 1, // 0 to 2
|
||||||
"vimium-gi": true,
|
"vimium-gi": true,
|
||||||
|
|
||||||
|
// Default logging levels - 2 === WARNING
|
||||||
|
"logging": o({
|
||||||
|
"messaging": 2,
|
||||||
|
"cmdline": 2,
|
||||||
|
"controller": 2,
|
||||||
|
"hinting": 2,
|
||||||
|
"state": 2,
|
||||||
|
"excmd": 1,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
// currently only supports 2D or 1D storage
|
// currently only supports 2D or 1D storage
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {isTextEditable} from './dom'
|
||||||
import {isSimpleKey} from './keyseq'
|
import {isSimpleKey} from './keyseq'
|
||||||
import state from "./state"
|
import state from "./state"
|
||||||
import {repeat} from './excmds_background'
|
import {repeat} from './excmds_background'
|
||||||
|
import Logger from "./logging"
|
||||||
|
|
||||||
import {parser as exmode_parser} from './parsers/exmode'
|
import {parser as exmode_parser} from './parsers/exmode'
|
||||||
import {parser as hintmode_parser} from './hinting_background'
|
import {parser as hintmode_parser} from './hinting_background'
|
||||||
|
@ -13,6 +14,7 @@ import * as gobblemode from './parsers/gobblemode'
|
||||||
import * as inputmode from './parsers/inputmode'
|
import * as inputmode from './parsers/inputmode'
|
||||||
|
|
||||||
|
|
||||||
|
const logger = new Logger('controller')
|
||||||
|
|
||||||
/** Accepts keyevents, resolves them to maps, maps to exstrs, executes exstrs */
|
/** Accepts keyevents, resolves them to maps, maps to exstrs, executes exstrs */
|
||||||
function *ParserController () {
|
function *ParserController () {
|
||||||
|
@ -41,7 +43,7 @@ function *ParserController () {
|
||||||
state.mode = "normal"
|
state.mode = "normal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(keyevent, state.mode)
|
logger.debug(keyevent, state.mode)
|
||||||
|
|
||||||
// Special keys (e.g. Backspace) are not handled properly
|
// Special keys (e.g. Backspace) are not handled properly
|
||||||
// yet. So drop them. This also drops all modifier keys.
|
// yet. So drop them. This also drops all modifier keys.
|
||||||
|
@ -61,7 +63,7 @@ function *ParserController () {
|
||||||
response = (parsers[state.mode] as any)([keyevent])
|
response = (parsers[state.mode] as any)([keyevent])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
console.debug(keys, response)
|
logger.debug(keys, response)
|
||||||
|
|
||||||
if (response.ex_str){
|
if (response.ex_str){
|
||||||
ex_str = response.ex_str
|
ex_str = response.ex_str
|
||||||
|
|
|
@ -66,13 +66,9 @@ export async function downloadUrl(url: string, saveAs: boolean) {
|
||||||
|
|
||||||
// TODO: at this point, could give feeback using the promise returned
|
// TODO: at this point, could give feeback using the promise returned
|
||||||
// by downloads.download(), needs status bar to show it (#90)
|
// by downloads.download(), needs status bar to show it (#90)
|
||||||
downloadPromise.then(id => {
|
// By awaiting the promise, we ensure that if it errors, the error will be
|
||||||
//console.log("Downloaded OK: ", urlToSave)
|
// thrown by this function too.
|
||||||
},
|
await downloadPromise
|
||||||
error => {
|
|
||||||
console.log("Failed to download: ", urlToDownload, error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
import * as Messaging from "./messaging"
|
import * as Messaging from "./messaging"
|
||||||
|
|
|
@ -95,6 +95,8 @@ import * as CommandLineBackground from './commandline_background'
|
||||||
import * as DOM from './dom'
|
import * as DOM from './dom'
|
||||||
|
|
||||||
import * as config from './config'
|
import * as config from './config'
|
||||||
|
import * as Logging from "./logging"
|
||||||
|
const logger = new Logging.Logger('excmds')
|
||||||
|
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
|
@ -115,18 +117,17 @@ function hasScheme(uri: string) {
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
function searchURL(provider: string, query: string) {
|
function searchURL(provider: string, query: string) {
|
||||||
if (provider == "search") provider = config.get("searchengine")
|
if (provider == "search") provider = config.get("searchengine")
|
||||||
let searchurlprovider = config.get("searchurls", provider)
|
const searchurlprovider = config.get("searchurls", provider)
|
||||||
if (searchurlprovider !== undefined){
|
if (searchurlprovider === undefined){
|
||||||
const url = new URL(searchurlprovider + encodeURIComponent(query))
|
|
||||||
// URL constructor doesn't convert +s because they're valid literals in
|
|
||||||
// the standard it adheres to. But they are special characters in
|
|
||||||
// x-www-form-urlencoded and e.g. google excepts query parameters in
|
|
||||||
// that format.
|
|
||||||
url.search = url.search.replace(/\+/g, '%2B')
|
|
||||||
return url
|
|
||||||
} else {
|
|
||||||
throw new TypeError(`Unknown provider: '${provider}'`)
|
throw new TypeError(`Unknown provider: '${provider}'`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// build search URL: either replace "%s" in URL with query or append query to URL
|
||||||
|
const url = searchurlprovider.includes("%s") ?
|
||||||
|
new URL(searchurlprovider.replace("%s", encodeURIComponent(query))) :
|
||||||
|
new URL(searchurlprovider + encodeURIComponent(query))
|
||||||
|
|
||||||
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If maybeURI doesn't have a schema, affix http:// */
|
/** If maybeURI doesn't have a schema, affix http:// */
|
||||||
|
@ -145,7 +146,6 @@ function forceURI(maybeURI: string): string {
|
||||||
const args = maybeURI.split(' ')
|
const args = maybeURI.split(' ')
|
||||||
return searchURL(args[0], args.slice(1).join(' ')).href
|
return searchURL(args[0], args.slice(1).join(' ')).href
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
|
||||||
if (e.name !== 'TypeError') throw e
|
if (e.name !== 'TypeError') throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +172,33 @@ function tabSetActive(id: number) {
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
|
// {{{ INTERNAL/DEBUG
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the logging level for a given logging module.
|
||||||
|
*
|
||||||
|
* @param logModule the logging module to set the level on
|
||||||
|
* @param level the level to log at: in increasing verbosity, one of
|
||||||
|
* "never", "error", "warning", "info", "debug"
|
||||||
|
*/
|
||||||
|
//#background
|
||||||
|
export function loggingsetlevel(logModule: string, level: string) {
|
||||||
|
const map = {
|
||||||
|
"never": Logging.LEVEL.NEVER,
|
||||||
|
"error": Logging.LEVEL.ERROR,
|
||||||
|
"warning": Logging.LEVEL.WARNING,
|
||||||
|
"info": Logging.LEVEL.INFO,
|
||||||
|
"debug": Logging.LEVEL.DEBUG,
|
||||||
|
}
|
||||||
|
|
||||||
|
let newLevel = map[level.toLowerCase()]
|
||||||
|
|
||||||
|
if (newLevel !== undefined) {
|
||||||
|
config.set("logging", newLevel, logModule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
// {{{ PAGE CONTEXT
|
// {{{ PAGE CONTEXT
|
||||||
|
|
||||||
|
@ -270,7 +297,6 @@ export async function reloadhard(n = 1) {
|
||||||
//#content
|
//#content
|
||||||
export function open(...urlarr: string[]) {
|
export function open(...urlarr: string[]) {
|
||||||
let url = urlarr.join(" ")
|
let url = urlarr.join(" ")
|
||||||
console.log("open url:" + url)
|
|
||||||
window.location.href = forceURI(url)
|
window.location.href = forceURI(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +310,6 @@ export function open(...urlarr: string[]) {
|
||||||
//#background
|
//#background
|
||||||
export function home(all: "false" | "true" = "false"){
|
export function home(all: "false" | "true" = "false"){
|
||||||
let homepages = config.get("homepages")
|
let homepages = config.get("homepages")
|
||||||
console.log(homepages)
|
|
||||||
if (homepages.length > 0){
|
if (homepages.length > 0){
|
||||||
if (all === "false") open(homepages[homepages.length - 1])
|
if (all === "false") open(homepages[homepages.length - 1])
|
||||||
else {
|
else {
|
||||||
|
@ -904,7 +929,7 @@ export function repeat(n = 1, ...exstr: string[]) {
|
||||||
let cmd = state.last_ex_str
|
let cmd = state.last_ex_str
|
||||||
if (exstr.length > 0)
|
if (exstr.length > 0)
|
||||||
cmd = exstr.join(" ")
|
cmd = exstr.join(" ")
|
||||||
console.log("repeating " + cmd + " " + n + " times")
|
logger.debug("repeating " + cmd + " " + n + " times")
|
||||||
for (let i = 0; i < n; i++)
|
for (let i = 0; i < n; i++)
|
||||||
controller.acceptExCmd(cmd)
|
controller.acceptExCmd(cmd)
|
||||||
}
|
}
|
||||||
|
@ -1177,12 +1202,10 @@ export async function sanitize(...args: string[]) {
|
||||||
}
|
}
|
||||||
since = { "since": (new Date()).getTime() - millis }
|
since = { "since": (new Date()).getTime() - millis }
|
||||||
} else {
|
} else {
|
||||||
console.log(":sanitize error: expected time format: ^([0-9])+(m|h|d|w)$, given format:" + args[flagpos+1])
|
throw new Error(":sanitize error: expected time format: ^([0-9])+(m|h|d|w)$, given format:" + args[flagpos+1])
|
||||||
return
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(":sanitize error: -t given but no following arguments")
|
throw new Error(":sanitize error: -t given but no following arguments")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1418,10 +1441,10 @@ export async function ttsread(mode: "-t" | "-c", ...args: string[]) {
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
tssReadFromCss(args[0])
|
tssReadFromCss(args[0])
|
||||||
} else {
|
} else {
|
||||||
console.log("Error: no CSS selector supplied")
|
throw "Error: no CSS selector supplied"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("Unknown mode for ttsread command: " + mode)
|
throw "Unknown mode for ttsread command: " + mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1460,7 +1483,7 @@ export async function ttscontrol(action: string) {
|
||||||
if (ttsAction) {
|
if (ttsAction) {
|
||||||
TTS.doAction(ttsAction)
|
TTS.doAction(ttsAction)
|
||||||
} else {
|
} else {
|
||||||
console.log("Unknown text-to-speech action: " + action)
|
throw new Error("Unknown text-to-speech action: " + action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ import {messageActiveTab, message} from './messaging'
|
||||||
import * as config from './config'
|
import * as config from './config'
|
||||||
import * as TTS from './text_to_speech'
|
import * as TTS from './text_to_speech'
|
||||||
import {HintSaveType} from './hinting_background'
|
import {HintSaveType} from './hinting_background'
|
||||||
|
import Logger from './logging'
|
||||||
|
const logger = new Logger('hinting')
|
||||||
|
|
||||||
/** Simple container for the state of a single frame's hints. */
|
/** Simple container for the state of a single frame's hints. */
|
||||||
class HintState {
|
class HintState {
|
||||||
|
@ -53,13 +55,13 @@ export function hintPage(
|
||||||
state.mode = 'hint'
|
state.mode = 'hint'
|
||||||
modeState = new HintState()
|
modeState = new HintState()
|
||||||
for (let [el, name] of izip( hintableElements, names)) {
|
for (let [el, name] of izip( hintableElements, names)) {
|
||||||
console.log({el, name})
|
logger.debug({el, name})
|
||||||
modeState.hintchars += name
|
modeState.hintchars += name
|
||||||
modeState.hints.push(new Hint(el, name, onSelect))
|
modeState.hints.push(new Hint(el, name, onSelect))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modeState.hints.length) {
|
if (modeState.hints.length) {
|
||||||
console.log("HINTS", modeState.hints)
|
logger.debug("hints", modeState.hints)
|
||||||
modeState.focusedHint = modeState.hints[0]
|
modeState.focusedHint = modeState.hints[0]
|
||||||
modeState.focusedHint.focused = true
|
modeState.focusedHint.focused = true
|
||||||
document.body.appendChild(modeState.hintHost)
|
document.body.appendChild(modeState.hintHost)
|
||||||
|
@ -455,7 +457,7 @@ function hintSave(hintType: HintSaveType, saveAs: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectFocusedHint() {
|
function selectFocusedHint() {
|
||||||
console.log("Selecting hint.", state.mode)
|
logger.debug("Selecting hint.", state.mode)
|
||||||
const focused = modeState.focusedHint
|
const focused = modeState.focusedHint
|
||||||
reset()
|
reset()
|
||||||
focused.select()
|
focused.select()
|
||||||
|
|
|
@ -70,7 +70,6 @@ import {MsgSafeKeyboardEvent} from './msgsafe'
|
||||||
|
|
||||||
*/
|
*/
|
||||||
export function parser(keys: MsgSafeKeyboardEvent[]) {
|
export function parser(keys: MsgSafeKeyboardEvent[]) {
|
||||||
console.log("hintparser", keys)
|
|
||||||
const key = keys[0].key
|
const key = keys[0].key
|
||||||
if (key === 'Escape') {
|
if (key === 'Escape') {
|
||||||
reset()
|
reset()
|
||||||
|
|
72
src/logging.ts
Normal file
72
src/logging.ts
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* Helper functions for logging
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Config from "./config"
|
||||||
|
|
||||||
|
export enum LEVEL {
|
||||||
|
NEVER = 0, // don't use this in calls to log()
|
||||||
|
ERROR = 1,
|
||||||
|
WARNING = 2,
|
||||||
|
INFO = 3,
|
||||||
|
DEBUG = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Logger {
|
||||||
|
/**
|
||||||
|
* Config-aware Logger class.
|
||||||
|
*
|
||||||
|
* @param logModule the logging module name: this is ued to look up the
|
||||||
|
* configured/default level in the user config
|
||||||
|
*/
|
||||||
|
constructor(private logModule) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config-aware logging function.
|
||||||
|
*
|
||||||
|
* @param level the level of the logging - if <= configured, the message
|
||||||
|
* will be shown
|
||||||
|
*
|
||||||
|
* @return logging function: this is returned as a function to
|
||||||
|
* retain the call site
|
||||||
|
*/
|
||||||
|
private log(level: LEVEL) {
|
||||||
|
let configedLevel = Config.get("logging", this.logModule) || LEVEL.WARNING
|
||||||
|
|
||||||
|
if (level <= configedLevel) {
|
||||||
|
// hand over to console.log, error or debug as needed
|
||||||
|
switch (level) {
|
||||||
|
|
||||||
|
case LEVEL.ERROR:
|
||||||
|
return console.error
|
||||||
|
case LEVEL.WARNING:
|
||||||
|
return console.warn
|
||||||
|
case LEVEL.INFO:
|
||||||
|
return console.log
|
||||||
|
case LEVEL.DEBUG:
|
||||||
|
return console.debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do nothing with the message
|
||||||
|
return function(...args) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are all getters so that logger.debug = console.debug and
|
||||||
|
// logger.debug('blah') translates into console.debug('blah') with the
|
||||||
|
// filename and line correct.
|
||||||
|
public get debug() {
|
||||||
|
return this.log(LEVEL.DEBUG)
|
||||||
|
}
|
||||||
|
public get info() {
|
||||||
|
return this.log(LEVEL.INFO)
|
||||||
|
}
|
||||||
|
public get warning() {
|
||||||
|
return this.log(LEVEL.WARNING)
|
||||||
|
}
|
||||||
|
public get error() {
|
||||||
|
return this.log(LEVEL.ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Logger
|
|
@ -1,4 +1,6 @@
|
||||||
import {l, browserBg, activeTabId} from './lib/webext'
|
import {l, browserBg, activeTabId} from './lib/webext'
|
||||||
|
import Logger from './logging'
|
||||||
|
const logger = new Logger('messaging')
|
||||||
|
|
||||||
export type TabMessageType =
|
export type TabMessageType =
|
||||||
"excmd_content" |
|
"excmd_content" |
|
||||||
|
@ -24,7 +26,8 @@ export type listener = (message: Message, sender?, sendResponse?) => void|Promis
|
||||||
// Calls methods on obj that match .command and sends responses back
|
// Calls methods on obj that match .command and sends responses back
|
||||||
export function attributeCaller(obj) {
|
export function attributeCaller(obj) {
|
||||||
function handler(message: Message, sender, sendResponse) {
|
function handler(message: Message, sender, sendResponse) {
|
||||||
console.log("Message:", message)
|
|
||||||
|
logger.debug(message)
|
||||||
|
|
||||||
// Args may be undefined, but you can't spread undefined...
|
// Args may be undefined, but you can't spread undefined...
|
||||||
if (message.args === undefined) message.args = []
|
if (message.args === undefined) message.args = []
|
||||||
|
@ -35,17 +38,17 @@ export function attributeCaller(obj) {
|
||||||
|
|
||||||
// Return response to sender
|
// Return response to sender
|
||||||
if (response instanceof Promise) {
|
if (response instanceof Promise) {
|
||||||
console.log("Returning promise...", response)
|
logger.debug("Returning promise...", response)
|
||||||
sendResponse(response)
|
sendResponse(response)
|
||||||
// Docs say you should be able to return a promise, but that
|
// Docs say you should be able to return a promise, but that
|
||||||
// doesn't work.
|
// doesn't work.
|
||||||
/* return response */
|
/* return response */
|
||||||
} else if (response !== undefined) {
|
} else if (response !== undefined) {
|
||||||
console.log("Returning synchronously...", response)
|
logger.debug("Returning synchronously...", response)
|
||||||
sendResponse(response)
|
sendResponse(response)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Error processing ${message.command}(${message.args})`, e)
|
logger.error(`Error processing ${message.command}(${message.args})`, e)
|
||||||
return new Promise((resolve, error)=>error(e))
|
return new Promise((resolve, error)=>error(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +81,7 @@ export async function messageAllTabs(type: TabMessageType, command: string, args
|
||||||
let responses = []
|
let responses = []
|
||||||
for (let tab of await browserBg.tabs.query({})) {
|
for (let tab of await browserBg.tabs.query({})) {
|
||||||
try { responses.push(await messageTab(tab.id, type, command, args)) }
|
try { responses.push(await messageTab(tab.id, type, command, args)) }
|
||||||
catch (e) { console.error(e) }
|
catch (e) { logger.error(e) }
|
||||||
}
|
}
|
||||||
return responses
|
return responses
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
If this turns out to be expensive there are improvements available.
|
If this turns out to be expensive there are improvements available.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import Logger from './logging'
|
||||||
|
const logger = new Logger('state')
|
||||||
|
|
||||||
export type ModeName = 'normal' | 'insert' | 'hint' | 'ignore' | 'gobble' | 'input'
|
export type ModeName = 'normal' | 'insert' | 'hint' | 'ignore' | 'gobble' | 'input'
|
||||||
class State {
|
class State {
|
||||||
mode: ModeName = 'normal'
|
mode: ModeName = 'normal'
|
||||||
|
@ -27,10 +30,10 @@ const defaults = Object.freeze(new State())
|
||||||
const overlay = {} as any
|
const overlay = {} as any
|
||||||
browser.storage.local.get('state').then(res=>{
|
browser.storage.local.get('state').then(res=>{
|
||||||
if ('state' in res) {
|
if ('state' in res) {
|
||||||
console.log("Loaded initial state:", res.state)
|
logger.debug("Loaded initial state:", res.state)
|
||||||
Object.assign(overlay, res.state)
|
Object.assign(overlay, res.state)
|
||||||
}
|
}
|
||||||
}).catch(console.error)
|
}).catch((...args) => logger.error(...args))
|
||||||
|
|
||||||
const state = new Proxy(overlay, {
|
const state = new Proxy(overlay, {
|
||||||
|
|
||||||
|
@ -45,7 +48,7 @@ const state = new Proxy(overlay, {
|
||||||
|
|
||||||
/** Persist sets to storage immediately */
|
/** Persist sets to storage immediately */
|
||||||
set: function(target, property, value) {
|
set: function(target, property, value) {
|
||||||
console.log("State changed!", property, value)
|
logger.debug("State changed!", property, value)
|
||||||
target[property] = value
|
target[property] = value
|
||||||
browser.storage.local.set({state: target})
|
browser.storage.local.set({state: target})
|
||||||
return true
|
return true
|
||||||
|
|
84
src/static/logo/tridactyl.svg
Normal file
84
src/static/logo/tridactyl.svg
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="48mm"
|
||||||
|
height="48mm"
|
||||||
|
viewBox="0 0 48 48"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
|
||||||
|
sodipodi:docname="tridactyl.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="2.2879385"
|
||||||
|
inkscape:cx="99.585653"
|
||||||
|
inkscape:cy="71.989006"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="1340"
|
||||||
|
inkscape:window-height="915"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="144"
|
||||||
|
inkscape:window-maximized="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-249)">
|
||||||
|
<circle
|
||||||
|
style="opacity:1;fill:#1f9947;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="path5917"
|
||||||
|
cx="24"
|
||||||
|
cy="273"
|
||||||
|
r="19" />
|
||||||
|
<path
|
||||||
|
style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 33.14566,271.14395 c 2.905194,-2.00919 3.118816,-2.94966 3.631495,-3.67638 0.512688,-0.72674 1.196262,0.17099 1.196262,0.94047 0,0.76946 -0.640857,5.38632 -1.196262,5.89931 -0.555405,0.51298 -1.324423,0.68397 -1.324423,0.68397 l -3.076099,-3.1634 z"
|
||||||
|
id="path5923"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 14.032233,262.59755 c -0.755255,-3.83894 -0.725044,-4.53419 -0.513574,-5.22942 0.21147,-0.69524 -0.573988,-1.6323 -1.208407,-0.87661 -0.634409,0.7557 -3.413739,5.01783 -2.869963,5.53169 0.543785,0.51388 -0.120837,1.17889 0.936513,1.17889 1.057359,0 3.655431,-0.48365 3.655431,-0.60455 z"
|
||||||
|
id="path5925"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 24.498991,256.83184 c 0.138757,-1.35684 6.121005,-5.9549 6.566457,-5.79613 0.44545,0.15876 -0.0039,7.94853 -0.473385,8.34084 -0.469496,0.39233 -6.093072,-2.54471 -6.093072,-2.54471 z"
|
||||||
|
id="path5921"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
d="m 13.993892,293.90696 c 5.169539,2.7359 5.169548,-1.28246 8.331091,-2.13744 3.161542,-0.85497 5.425887,-0.76947 7.134828,-4.78784 1.708941,-4.01837 4.443248,-3.1634 5.554066,-4.70235 1.11081,-1.53895 4.051903,-7.19695 0.512679,-9.74668 -1.716916,-1.23691 -3.872923,-2.11206 -5.725261,-0.61954 -1.604661,1.29296 -2.768054,4.49046 -4.870175,7.28832 0.85447,-6.88253 4.497485,-9.76253 5.981292,-13.25208 1.100194,-2.58737 0.299066,-6.28405 -2.69158,-7.99399 -4.150603,-2.37316 -8.175477,0.98531 -8.758326,4.10386 -1.235154,6.60881 -0.939923,10.72992 -3.076099,14.57729 -0.411817,-6.03522 2.459883,-7.09235 0.512688,-11.45663 -1.068093,-2.39392 -3.845126,-3.59088 -6.237641,-2.65042 -2.3925238,0.94048 -3.7169555,4.40312 -3.5460597,7.13903 0.1708959,2.73591 2.1592337,5.05118 1.0680925,8.42148 -0.98264,3.03515 0,4.44585 1.9652792,8.03674 1.965289,3.59088 -0.683574,4.53136 3.845126,7.78025 z"
|
||||||
|
id="path5919"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccsssscssscsccssc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.5 KiB |
|
@ -24,8 +24,7 @@ export function readText(text: string): void {
|
||||||
if (window.speechSynthesis.getVoices().length === 0) {
|
if (window.speechSynthesis.getVoices().length === 0) {
|
||||||
// should try to warn user? This apparently can happen on some machines
|
// should try to warn user? This apparently can happen on some machines
|
||||||
// TODO: Implement when there's an error feedback mechanism
|
// TODO: Implement when there's an error feedback mechanism
|
||||||
console.log("No voice found: cannot use Text-To-Speech API")
|
throw new Error("No voice found: cannot use Text-To-Speech API")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let utterance = new SpeechSynthesisUtterance(text);
|
let utterance = new SpeechSynthesisUtterance(text);
|
||||||
|
|
|
@ -61,17 +61,19 @@ function test_parent() {
|
||||||
// single level path
|
// single level path
|
||||||
["http://example.com/path", "http://example.com/"],
|
["http://example.com/path", "http://example.com/"],
|
||||||
// multi-level path
|
// multi-level path
|
||||||
["http://example.com/path1/path2", "http://example.com/path1"],
|
["http://example.com/path1/path2", "http://example.com/path1/"],
|
||||||
|
["http://example.com/path1/path2/path3", "http://example.com/path1/path2/"],
|
||||||
// subdomains
|
// subdomains
|
||||||
["http://sub.example.com", "http://example.com/"],
|
["http://sub.example.com", "http://example.com/"],
|
||||||
// subdom with path, leave subdom
|
// subdom with path, leave subdom
|
||||||
["http://sub.example.com/path", "http://sub.example.com/"],
|
["http://sub.example.com/path", "http://sub.example.com/"],
|
||||||
// trailing slash
|
// trailing slash
|
||||||
["http://sub.example.com/path/", "http://sub.example.com/"],
|
["http://sub.example.com/path/", "http://sub.example.com/"],
|
||||||
|
["http://sub.example.com/path/to/", "http://sub.example.com/path/"],
|
||||||
// repeated slash
|
// repeated slash
|
||||||
["http://example.com/path//", "http://example.com/"],
|
["http://example.com/path//", "http://example.com/"],
|
||||||
// repeated slash
|
["http://example.com//path//", "http://example.com//"],
|
||||||
["http://example.com//path//", "http://example.com/"],
|
["http://example.com//path//", "http://example.com//"],
|
||||||
]
|
]
|
||||||
|
|
||||||
for (let [url, exp_parent] of cases) {
|
for (let [url, exp_parent] of cases) {
|
||||||
|
|
|
@ -78,13 +78,10 @@ export function getUrlParent(url, count = 1) {
|
||||||
return gup(parent, count - 1)
|
return gup(parent, count - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathname always starts '/'
|
// empty path is '/'
|
||||||
if (parent.pathname !== '/') {
|
if (parent.pathname !== '/') {
|
||||||
// Split on '/' and remove empty substrings
|
// Remove trailing slashes and everything to the next slash:
|
||||||
// (handles initial and trailing slashes, repeated slashes, etc.)
|
parent.pathname = parent.pathname.replace(/\/[^\/]*?\/*$/, '/')
|
||||||
let path = parent.pathname.split('/').filter(sub => sub !== "")
|
|
||||||
path.pop()
|
|
||||||
parent.pathname = path.join('/')
|
|
||||||
return gup(parent, count - 1)
|
return gup(parent, count - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue