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
135
136
137
138
139
140
141
142
|
mapKeyRegistry = {}
# NOTE: "?" here for the tests.
Utils?.monitorChromeStorage "mapKeyRegistry", (value) => mapKeyRegistry = value
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]
# It appears that event.key is not always defined (see #2453).
else if not event.key?
""
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 ->
# TODO(smblott) Change this to use event.key.
(event) ->
event.keyCode == @keyCodes.ESC || do =>
keyChar = @getKeyCharString event
# <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) ->
switch event.type
when "keypress"
# Ignore modifier keys by themselves.
if 31 < event.keyCode
String.fromCharCode event.charCode
# TODO(smblott). Currently all (almost?) keyhandling is being done on keydown. All legacy code related
# to key handling on keypress should be reviewed and probably removed. This is not being done right now
# (2017-03-22) because it is better to wait until we've verified that the change to keydown is indeed
# correct and reliable.
when "keydown"
if keyChar = @getKeyChar event
modifiers = []
keyChar = keyChar.toUpperCase() if event.shiftKey and keyChar.length == 1
# 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 "-"
keyChar = "<#{keyChar}>" if 1 < keyChar.length
keyChar = mapKeyRegistry[keyChar] ? keyChar
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
|