| 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
 |