-
-
Notifications
You must be signed in to change notification settings - Fork 151
/
Copy pathKeyHandler.ts
108 lines (100 loc) · 4.39 KB
/
KeyHandler.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { EventEmitter } from "./EventEmitter";
import { GlobalSettings, NvimMode } from "./utils/configuration";
import { nonLiteralKeys, addModifier, translateKey } from "./utils/keys";
import { isChrome } from "./utils/utils";
export class KeyHandler extends EventEmitter<"input", (s: string) => void> {
private currentMode : NvimMode;
constructor(private elem: HTMLElement, settings: GlobalSettings) {
super();
const ignoreKeys = settings.ignoreKeys;
this.elem.addEventListener("keydown", (evt) => {
// This is a workaround for osx where pressing non-alphanumeric
// characters like "@" requires pressing <A-a>, which results
// in the browser sending an <A-@> event, which we want to
// treat as a regular @.
// So if we're seeing an alt on a non-alphanumeric character,
// we just ignore it and let the input event handler do its
// magic. This can only be tested on OSX, as generating an
// <A-@> keydown event with selenium won't result in an input
// event.
// Since coverage reports are only retrieved on linux, we don't
// instrument this condition.
/* istanbul ignore next */
if (evt.altKey && settings.alt === "alphanum" && !/[a-zA-Z0-9]/.test(evt.key)) {
return;
}
// Note: order of this array is important, we need to check OS before checking meta
const specialKeys = [["Alt", "A"], ["Control", "C"], ["OS", "D"], ["Meta", "D"]];
// The event has to be trusted and either have a modifier or a non-literal representation
if (evt.isTrusted
&& (nonLiteralKeys[evt.key] !== undefined
|| specialKeys.find(([mod, _]: [string, string]) =>
evt.key !== mod && (evt as any).getModifierState(mod)))) {
const text = specialKeys.concat([["Shift", "S"]])
.reduce((key: string, [attr, mod]: [string, string]) => {
if ((evt as any).getModifierState(attr)) {
return addModifier(mod, key);
}
return key;
}, translateKey(evt.key));
let keys : string[] = [];
if (ignoreKeys[this.currentMode] !== undefined) {
keys = ignoreKeys[this.currentMode].slice();
}
if (ignoreKeys.all !== undefined) {
keys.push.apply(keys, ignoreKeys.all);
}
if (!keys.includes(text)) {
this.emit("input", text);
evt.preventDefault();
evt.stopImmediatePropagation();
}
}
})
const acceptInput = ((evt: any) => {
this.emit("input", evt.target.value);
evt.preventDefault();
evt.stopImmediatePropagation();
evt.target.innerText = "";
evt.target.value = "";
}).bind(this);
this.elem.addEventListener("input", (evt: any) => {
if (evt.isTrusted && !evt.isComposing) {
acceptInput(evt);
}
});
// On Firefox, Pinyin input method for a single chinese character will
// result in the following sequence of events:
// - compositionstart
// - input (character)
// - compositionend
// - input (result)
// But on Chrome, we'll get this order:
// - compositionstart
// - input (character)
// - input (result)
// - compositionend
// So Chrome's input event will still have its isComposing flag set to
// true! This means that we need to add a chrome-specific event
// listener on compositionend to do what happens on input events for
// Firefox.
// Don't instrument this branch as coverage is only generated on
// Firefox.
/* istanbul ignore next */
if (isChrome()) {
this.elem.addEventListener("compositionend", (e: CompositionEvent) => {
acceptInput(e);
});
}
}
focus() {
this.elem.focus();
}
moveTo(x: number, y: number) {
this.elem.style.left = `${x}px`;
this.elem.style.top = `${y}px`;
}
setMode(s: NvimMode) {
this.currentMode = s;
}
}