| 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
 | 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 = @getKeyChar event
        keyChar.length == 1 and do =>
          keyChar = @getModifiedKeyChar keyChar, event
          keyChar = keyTranslationRegistry[keyChar] ? keyChar
          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
      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))
          @getModifiedKeyChar keyChar, event
  getModifiedKeyChar: (keyChar, event) ->
    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
 |