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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
KeyboardUtils =
keyCodes:
{ ESC: 27, backspace: 8, deleteKey: 46, enter: 13, ctrlEnter: 10, space: 32, shiftKey: 16, ctrlKey: 17, f1: 112,
f12: 123, tab: 9, downArrow: 40, upArrow: 38 }
keyNames:
{ 37: "left", 38: "up", 39: "right", 40: "down", 32: "space", 8: "backspace" }
# This is a mapping of the incorrect keyIdentifiers generated by Webkit on Windows during keydown events to
# the correct identifiers, which are correctly generated on Mac. We require this mapping to properly handle
# these keys on Windows. See https://bugs.webkit.org/show_bug.cgi?id=19906 for more details.
keyIdentifierCorrectionMap:
"U+00C0": ["U+0060", "U+007E"] # `~
"U+00BD": ["U+002D", "U+005F"] # -_
"U+00BB": ["U+003D", "U+002B"] # =+
"U+00DB": ["U+005B", "U+007B"] # [{
"U+00DD": ["U+005D", "U+007D"] # ]}
"U+00DC": ["U+005C", "U+007C"] # \|
"U+00BA": ["U+003B", "U+003A"] # ;:
"U+00DE": ["U+0027", "U+0022"] # '"
"U+00BC": ["U+002C", "U+003C"] # ,<
"U+00BE": ["U+002E", "U+003E"] # .>
"U+00BF": ["U+002F", "U+003F"] # /?
init: ->
if (navigator.userAgent.indexOf("Mac") != -1)
@platform = "Mac"
else if (navigator.userAgent.indexOf("Linux") != -1)
@platform = "Linux"
else
@platform = "Windows"
# We are migrating from using event.keyIdentifier to using event.key. For some period of time, we must
# support both. This wrapper can be removed once Chrome 52 is considered too old to support.
getKeyChar: (event) ->
# We favor using event.keyIdentifier due to Chromium's currently (Chrome 51) incorrect implementataion of
# event.key; see #2147.
if event.keyIdentifier?
@getKeyCharUsingKeyIdentifier event
else
@getKeyCharUsingKey event
getKeyCharUsingKey: (event) ->
if event.keyCode of @keyNames
@keyNames[event.keyCode]
else if event.key.length == 1
event.key
else if event.key.length == 2 and "F1" <= event.key <= "F9"
event.key.toLowerCase() # F1 to F9.
else if event.key.length == 3 and "F10" <= event.key <= "F12"
event.key.toLowerCase() # F10 to F12.
else
""
getKeyCharUsingKeyIdentifier: (event) ->
# Handle named keys.
keyCode = event.keyCode
if keyCode
if keyCode of @keyNames
return @keyNames[keyCode]
# Function keys.
if @keyCodes.f1 <= keyCode <= @keyCodes.f12
return "f" + (1 + keyCode - keyCodes.f1)
keyIdentifier = event.keyIdentifier
# Not a letter.
if not keyIdentifier.startsWith "U+"
return ""
# On Windows, the keyIdentifiers for non-letter keys are incorrect. See
# https://bugs.webkit.org/show_bug.cgi?id=19906 for more details.
if ((@platform == "Windows" || @platform == "Linux") && @keyIdentifierCorrectionMap[keyIdentifier])
correctedIdentifiers = @keyIdentifierCorrectionMap[keyIdentifier]
keyIdentifier = if event.shiftKey then correctedIdentifiers[1] else correctedIdentifiers[0]
unicodeKeyInHex = "0x" + keyIdentifier.substring(2)
character = String.fromCharCode(parseInt(unicodeKeyInHex)).toLowerCase()
if event.shiftKey then character.toUpperCase() else character
isPrimaryModifierKey: (event) -> if (@platform == "Mac") then event.metaKey else event.ctrlKey
isEscape: do ->
keyTranslationRegistry = {}
# NOTE: "?" here for the tests.
Utils?.monitorChromeStorage "keyTranslationRegistry", (value) => keyTranslationRegistry = value
(event) ->
event.keyCode == @keyCodes.ESC || do =>
keyChar = @getKeyCharString event, true
keyChar = keyTranslationRegistry[keyChar] ? keyChar
# <c-[> is mapped to Escape in Vim by default.
keyChar == "<c-[>"
# TODO. This is probably a poor way of detecting printable characters. However, it shouldn't incorrectly
# identify any of chrome's own keyboard shortcuts as printable.
isPrintable: (event) ->
return false if event.metaKey or event.ctrlKey or event.altKey
keyChar =
if event.type == "keypress"
String.fromCharCode event.charCode
else
@getKeyChar event
keyChar.length == 1
# Return the Vimium key representation for this keyboard event. Return a falsy value (the empty string or
# undefined) when no Vimium representation is appropriate.
getKeyCharString: (event, allKeydownEvents = false) ->
switch event.type
when "keypress"
# Ignore modifier keys by themselves.
if 31 < event.keyCode
String.fromCharCode event.charCode
when "keydown"
# Handle special keys and normal input keys with modifiers being pressed.
keyChar = @getKeyChar event
if 1 < keyChar.length or (keyChar.length == 1 and (event.metaKey or event.ctrlKey or event.altKey)) or allKeydownEvents
modifiers = []
keyChar = keyChar.toUpperCase() if event.shiftKey
# These must be in alphabetical order (to match the sorted modifier order in Commands.normalizeKey).
modifiers.push "a" if event.altKey
modifiers.push "c" if event.ctrlKey
modifiers.push "m" if event.metaKey
keyChar = [modifiers..., keyChar].join "-"
if 1 < keyChar.length then "<#{keyChar}>" else keyChar
KeyboardUtils.init()
root = exports ? window
root.KeyboardUtils = KeyboardUtils
# TODO(philc): A lot of code uses this keyCodes hash... maybe we shouldn't export it as a global.
root.keyCodes = KeyboardUtils.keyCodes
|