// ---------------------------------------------------------------------------
// Key values are found here:
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
//
// Hardwired binding: event contextmenu bound to command contextualMenu
//
// Bindings not supported:
// - Triple-click = "selectText", null. (Cannot make it work reliably.)
//
// * On the Mac, almost all keyboard shortcuts seem to be reserved
// either by the browser or by macOS. For example, all ALT|MOD, that is,
// Command+Option, are.
//
// Hence, on the Mac, same *CTRL* bindings as on XXE Desktop.
//
// * On Windows and Linux, the following keyboard shortcuts
// are reserved by browsers:
//
// Firefox: Alt-Left=Back, Alt-Right=Forward
// Ctrl-R=Reload
// Ctrl-P=Print, Ctrl+Shift-P=New private window
// Ctrl-T=New tab
// Ctrl-W=Close Tab or Window
// Ctrl-Tab=Next tab, Ctrl+Shift-Tab=Previous tab
// Ctrl-U=Show page source
// Ctrl-I=Show page info (but not blocked)
// Ctrl+Shift-I=Show element inspector (but not blocked)
// Ctrl-B=Show bookmarks pane (but not blocked)
// Ctrl+Shift-B=Show/hide bookmark bar (but not blocked)
// Ctrl-J=Focus search bar (but not blocked)
// Ctrl+Shift-A=Show/hide addon manager (but not blocked)
//
// Chrome: Alt-Left=Back, Alt-Right=Forward
// Ctrl-R=Reload
// Ctrl-P=Print, Ctrl+Shift-P=Print settings
// Ctrl-T=New tab, Ctrl+Shift-T=Reopen closed tab
// Ctrl-W=Close Tab or Window
// Ctrl-Tab=New tab, Ctrl+Shift-Tab=Previous tab
// Ctrl-U=Show page source
// Ctrl+Shift-I=Show element inspector (but not blocked)
// Ctrl+Shift-B=Show/hide bookmark bar (but not blocked)
// Ctrl-J=Show downloads (but not blocked)
// Ctrl-E=Focus search bar (but not blocked)
//
// Edge: Ctrl-K=Open a search query in the address bar.
//
// Windows: Ctrl+Alt-E=Input Euro symbol.
//
// Linux (or KDE?): Ctrl+Shift-E=Emoji input method.
// Example: Ctrl+Shift-E e ==> e underlined,
// then type "joy" ENTER ==> ejoy emoji.
// Ctrl+Shift-T=Open Terminal
// Ctrl+Shift-R=Manually Invoke Action
// on Current Clipboard (??? ==> disabled)
//
// Hence, on Windows and Linux, binding changes compared to XXE Desktop:
//
// "insertControlChar" "\t": Ctrl-Tab --> Esc Tab
//
// "replace": Ctrl-R --> Ctrl+Alt-R
//
// "insert", "into": Ctrl-I --> Ctrl+Alt-I
// "insert", "before[implicitElement]": Ctrl-H --> Ctrl+Alt-H
// "insert", "after[implicitElement]": Ctrl-J --> Ctrl+Alt-J
//
// "convert", "[implicitElement]": Ctrl-T --> Ctrl+Alt-C
// "wrap", "[implicitElement]": Ctrl+Shift-T --> Ctrl+Alt-W
//
// "paste", "before[implicitElement]": Ctrl-U --> Ctrl+Shift-V
// "paste", "after[implicitElement]": Ctrl-W --> Ctrl+Alt-V
//
// "editAttributes", "[implicitElement]": Ctrl-E --> Ctrl+Alt-M
// ---------------------------------------------------------------------------
const COMMON_DEFAULT_BINDINGS = [
new Binding(new MouseInput(USER_INPUT_MOUSE_DOWN, USER_INPUT_MOUSE_BUTTON1),
"selectAt", "begin"),
new Binding(new MouseInput(USER_INPUT_MOUSE_DRAG, USER_INPUT_MOUSE_BUTTON1),
"selectAt", "extend"),
new Binding(new MouseInput(USER_INPUT_MOUSE_UP, USER_INPUT_MOUSE_BUTTON1),
"selectAt", "end"),
new Binding(new MouseInput(USER_INPUT_MOUSE_CLICK, USER_INPUT_MOUSE_BUTTON1,
/*modifier*/ 0, /*click*/ 2),
"selectText", "word"),
/* Cannot be tested using Esc x because modal dialog boxes causes
caretPositionFromPoint to fail.
new Binding([new KeyDownInput("Escape"),
new KeyPressInput('L')],
"selectText", "line"),
*/
// Tab ---
new Binding(new KeyDownInput("Tab"),
"moveDotTo", "nextTextNode"),
new Binding(new KeyDownInput("Tab", USER_INPUT_MODIFIER_SHIFT),
"moveDotTo", "previousTextNode"),
// Left, Right ---
new Binding(new KeyDownInput("ArrowLeft"),
"moveDotTo", "previousChar"),
new Binding(new KeyDownInput("ArrowLeft", USER_INPUT_MODIFIER_SHIFT),
"selectTo", "previousChar"),
new Binding(new KeyDownInput("ArrowRight"),
"moveDotTo", "nextChar"),
new Binding(new KeyDownInput("ArrowRight", USER_INPUT_MODIFIER_SHIFT),
"selectTo", "nextChar"),
// Up, Down ---
new Binding(new KeyDownInput("ArrowUp"),
"moveDotTo", "previousLine"),
new Binding(new KeyDownInput("ArrowUp", USER_INPUT_MODIFIER_SHIFT),
"selectTo", "previousLine"),
new Binding(new KeyDownInput("ArrowDown"),
"moveDotTo", "nextLine"),
new Binding(new KeyDownInput("ArrowDown", USER_INPUT_MODIFIER_SHIFT),
"selectTo", "nextLine"),
// Home, End ---
new Binding(new KeyDownInput("Home"),
"moveDotTo", "lineBegin"),
new Binding(new KeyDownInput("Home", USER_INPUT_MODIFIER_SHIFT),
"selectTo", "lineBegin"),
new Binding(new KeyDownInput("End"),
"moveDotTo", "lineEnd"),
new Binding(new KeyDownInput("End", USER_INPUT_MODIFIER_SHIFT),
"selectTo", "lineEnd"),
// Ctrl-Left, Ctrl-Right ---
new Binding(new KeyDownInput("ArrowLeft", USER_INPUT_MODIFIER_MOD),
"moveDotTo", "previousWord"),
new Binding(new KeyDownInput("ArrowLeft",
(USER_INPUT_MODIFIER_SHIFT |
USER_INPUT_MODIFIER_MOD)),
"selectTo", "previousWord"),
new Binding(new KeyDownInput("ArrowRight", USER_INPUT_MODIFIER_MOD),
"moveDotTo", "nextWord"),
new Binding(new KeyDownInput("ArrowRight",
(USER_INPUT_MODIFIER_SHIFT |
USER_INPUT_MODIFIER_MOD)),
"selectTo", "nextWord"),
// ---
new Binding(new MouseInput(USER_INPUT_MOUSE_CLICK, USER_INPUT_MOUSE_BUTTON1,
USER_INPUT_MODIFIER_MOD),
"selectNodeAt", "orParent"),
new Binding(new MouseInput(USER_INPUT_MOUSE_CLICK, USER_INPUT_MOUSE_BUTTON1,
USER_INPUT_MODIFIER_SHIFT),
"extendSelectionAt", null),
// Ctrl-Up, Ctrl-Down ---
new Binding(new KeyDownInput("ArrowUp", USER_INPUT_MODIFIER_MOD),
"selectNode", "parentOrNode"),
new Binding(new KeyDownInput("ArrowDown", USER_INPUT_MODIFIER_MOD),
"selectNode", "childOrNone"),
// Shift-Ctrl-Up, Shift-Ctrl-Down ---
new Binding(new KeyDownInput("ArrowUp",
(USER_INPUT_MODIFIER_SHIFT |
USER_INPUT_MODIFIER_MOD)),
"selectNode", "previousSibling[implicitElement]"),
new Binding(new KeyDownInput("ArrowDown",
(USER_INPUT_MODIFIER_SHIFT |
USER_INPUT_MODIFIER_MOD)),
"selectNode", "nextSibling[implicitElement]"),
// Esc Left, Esc Right, Esc Down ---
new Binding([new KeyDownInput("Escape"),
new KeyDownInput("ArrowLeft")],
"selectNode", "extendToPreviousSiblingOrElement"),
new Binding([new KeyDownInput("Escape"),
new KeyDownInput("ArrowRight")],
"selectNode", "extendToNextSiblingOrElement"),
new Binding([new KeyDownInput("Escape"),
new KeyDownInput("ArrowDown")],
"selectNode", "children[implicitElement]"),
// Esc Esc ---
new Binding([new KeyDownInput("Escape"),
new KeyDownInput("Escape")],
"cancelSelection", null),
// Ctrl-L ---
new Binding(new KeyDownInput("L", USER_INPUT_MODIFIER_MOD),
"center", null),
// ---
// "followLinkAt" client-side command invoking
// server-side "${c}followLinkAt".
new Binding(new MouseInput(USER_INPUT_MOUSE_CLICK, USER_INPUT_MOUSE_BUTTON1,
(USER_INPUT_MODIFIER_MOD|
USER_INPUT_MODIFIER_ALT)),
"followLinkAt", null),
// Copy ---
new Binding(new KeyDownInput("C", USER_INPUT_MODIFIER_MOD),
"copy", "[implicitElement]"),
new Binding(new KeyDownInput("C",
(USER_INPUT_MODIFIER_SHIFT |
USER_INPUT_MODIFIER_MOD)),
"copyChars", "[separateParagraphs]"),
// ---
new Binding(new KeyDownInput("Delete"),
"deleteSelectionOrDeleteChar", null),
new Binding(new KeyDownInput("Backspace"),
"deleteSelectionOrDeleteChar", "backwards"),
new Binding(new KeyDownInput("Delete",
USER_INPUT_MODIFIER_MOD),
"deleteWord", null),
new Binding(new KeyDownInput("Backspace",
USER_INPUT_MODIFIER_MOD),
"deleteWord", "backwards"),
// CTRL even on the Mac.
new Binding(new KeyDownInput(" ", USER_INPUT_MODIFIER_CTRL),
"insertString", "\u00A0"),
new Binding(new KeyDownInput("Enter"),
"insertControlChar", "\n"),
new Binding([new KeyDownInput("Escape"),
new KeyDownInput("Tab")],
"insertControlChar", "\t"),
new Binding(new KeyDownInput("K", USER_INPUT_MODIFIER_MOD),
"delete", "[implicitElement]"),
new Binding(new KeyDownInput("X", USER_INPUT_MODIFIER_MOD),
"cut", "[implicitElement]"),
new Binding(new KeyDownInput("V", USER_INPUT_MODIFIER_MOD),
"paste", "toOrInto"),
new Binding(new KeyDownInput("Z", USER_INPUT_MODIFIER_MOD),
"undo", null),
new Binding(new KeyDownInput("Y", USER_INPUT_MODIFIER_MOD),
"redo", null),
new Binding(new KeyDownInput("A", USER_INPUT_MODIFIER_MOD),
"repeat", null),
new Binding(new KeyDownInput("A", (USER_INPUT_MODIFIER_SHIFT |
USER_INPUT_MODIFIER_MOD)),
"listRepeatable", null),
new Binding([new KeyDownInput("Escape"),
new KeyPressInput('x')],
"execute", null),
// Character case ---
new Binding([new KeyDownInput("Escape"),
new KeyPressInput('l')],
"convertCase", "lower"),
new Binding([new KeyDownInput("Escape"),
new KeyPressInput('u')],
"convertCase", "upper"),
new Binding([new KeyDownInput("Escape"),
new KeyPressInput('c')],
"convertCase", "capital"),
// Insert char by name ---
new Binding([new KeyDownInput("Escape"),
new KeyPressInput('n')],
"insertCharByName", "[DocBookIfNone]"),
// ---
new Binding(new MouseInput(USER_INPUT_MOUSE_CLICK,USER_INPUT_MOUSE_BUTTON2),
"pastePrimarySelection", null),
new Binding(new MouseInput(USER_INPUT_CONTEXT_MENU, /*button*/ 0),
"contextualMenu", null),
];
const OTHER_DEFAULT_BINDINGS = [
// Ins key ---
new Binding(new KeyDownInput("Insert"),
"insertTextOrMoveDot", "after"),
new Binding(new KeyDownInput("Insert", USER_INPUT_MODIFIER_SHIFT),
"insertTextOrMoveDot", "before"),
new Binding(new KeyDownInput("Insert", USER_INPUT_MODIFIER_MOD),
"insertNode", "sameElementAfter[implicitElement]"),
new Binding(new KeyDownInput("Insert",
(USER_INPUT_MODIFIER_SHIFT |
USER_INPUT_MODIFIER_MOD)),
"insertNode", "sameElementBefore[implicitElement]"),
// Navigation ---
new Binding(new KeyDownInput("ArrowLeft",
(USER_INPUT_MODIFIER_ALT |
USER_INPUT_MODIFIER_CTRL)),
"go", "back"),
new Binding(new KeyDownInput("ArrowRight",
(USER_INPUT_MODIFIER_ALT |
USER_INPUT_MODIFIER_CTRL)),
"go", "forward"),
// Basic editing. See above comment. ---
new Binding(new KeyDownInput("R", (USER_INPUT_MODIFIER_ALT |
USER_INPUT_MODIFIER_CTRL)),
"replace", "[implicitElement]"),
new Binding(new KeyDownInput("I", (USER_INPUT_MODIFIER_ALT |
USER_INPUT_MODIFIER_CTRL)),
"insert", "into"),
new Binding(new KeyDownInput("H", (USER_INPUT_MODIFIER_ALT |
USER_INPUT_MODIFIER_CTRL)),
"insert", "before[implicitElement]"),
new Binding(new KeyDownInput("J", (USER_INPUT_MODIFIER_ALT |
USER_INPUT_MODIFIER_CTRL)),
"insert", "after[implicitElement]"),
new Binding(new KeyDownInput("C", (USER_INPUT_MODIFIER_ALT |
USER_INPUT_MODIFIER_CTRL)),
"convert", "[implicitElement]"),
new Binding(new KeyDownInput("W", (USER_INPUT_MODIFIER_ALT |
USER_INPUT_MODIFIER_CTRL)),
"wrap", "[implicitElement]"),
new Binding(new KeyDownInput("V", (USER_INPUT_MODIFIER_SHIFT |
USER_INPUT_MODIFIER_CTRL)),
"paste", "before[implicitElement]"),
new Binding(new KeyDownInput("V", (USER_INPUT_MODIFIER_ALT |
USER_INPUT_MODIFIER_CTRL)),
"paste", "after[implicitElement]"),
new Binding(new KeyDownInput("M", (USER_INPUT_MODIFIER_ALT |
USER_INPUT_MODIFIER_CTRL)),
"editAttributes", "[implicitElement]"),
];
const MAC_DEFAULT_BINDINGS = [
// The Mac keyboard has no Del key and no Ins key.
// Just having Backspace to delete is OK (and Ctrl-D seems to be
// equivalent to Del).
// Use F1 instead of Ins on the Mac. ---
new Binding(new KeyDownInput("F1"),
"insertTextOrMoveDot", "after"),
new Binding(new KeyDownInput("F1", USER_INPUT_MODIFIER_SHIFT),
"insertTextOrMoveDot", "before"),
new Binding([new KeyDownInput("Escape"),
new KeyPressInput('s')],
"insertNode", "sameElementAfter[implicitElement]"),
new Binding([new KeyDownInput("Escape"),
new KeyPressInput('S')],
"insertNode", "sameElementBefore[implicitElement]"),
// Navigation ---
new Binding([new KeyDownInput("Escape"),
new KeyPressInput('<')],
"go", "back"),
new Binding([new KeyDownInput("Escape"),
new KeyPressInput('>')],
"go", "forward"),
// Basic editing. Use the same *CTRL* bindings as on XXE Desktop. ---
new Binding(new KeyDownInput("R", USER_INPUT_MODIFIER_CTRL),
"replace", "[implicitElement]"),
new Binding(new KeyDownInput("I", USER_INPUT_MODIFIER_CTRL),
"insert", "into"),
new Binding(new KeyDownInput("H", USER_INPUT_MODIFIER_CTRL),
"insert", "before[implicitElement]"),
new Binding(new KeyDownInput("J", USER_INPUT_MODIFIER_CTRL),
"insert", "after[implicitElement]"),
new Binding(new KeyDownInput("T", USER_INPUT_MODIFIER_CTRL),
"convert", "[implicitElement]"),
new Binding(new KeyDownInput("T", (USER_INPUT_MODIFIER_SHIFT |
USER_INPUT_MODIFIER_CTRL)),
"wrap", "[implicitElement]"),
new Binding(new KeyDownInput("U", USER_INPUT_MODIFIER_CTRL),
"paste", "before[implicitElement]"),
new Binding(new KeyDownInput("W", USER_INPUT_MODIFIER_CTRL),
"paste", "after[implicitElement]"),
new Binding(new KeyDownInput("E", USER_INPUT_MODIFIER_CTRL),
"editAttributes", "[implicitElement]"),
];
/**
* The registry of all local commands.
* <p>Local commands auto-register themselves here as follows:
* <pre>ALL_LOCAL_COMMANDS[<i>command_name</i>] = new <i>class_name</i>()</pre>
*/
export const ALL_LOCAL_COMMANDS = {};
class Bindings {
static compile(bindings=null) {
let builtInBindings;
if (PLATFORM_IS_MAC_OS) {
builtInBindings =
COMMON_DEFAULT_BINDINGS.concat(MAC_DEFAULT_BINDINGS);
} else {
builtInBindings =
COMMON_DEFAULT_BINDINGS.concat(OTHER_DEFAULT_BINDINGS);
}
// ---
let allBindings = [];
if (bindings === null) {
// No configuration specific bindings.
for (let b of builtInBindings) {
allBindings.push(Bindings.bindCommand(b));
}
} else {
let userInputToBinding = {};
for (let b of builtInBindings) {
userInputToBinding[b.getUserInputId()] = Bindings.bindCommand(b);
}
for (let b of bindings) {
userInputToBinding[b.getUserInputId()] = Bindings.bindCommand(b);
}
allBindings = Object.values(userInputToBinding);
}
return allBindings;
}
static bindCommand(binding) {
let cmd = ALL_LOCAL_COMMANDS[binding.commandName];
if (!cmd) {
cmd = new RemoteCommand(binding.commandName);
}
return new Binding(binding.userInput,
binding.commandName, binding.commandParams, cmd);
}
}