Skip to content

Commit d0a899e

Browse files
committed
Add keyboard support and polyfill
1 parent 7d225e8 commit d0a899e

File tree

1 file changed

+81
-78
lines changed

1 file changed

+81
-78
lines changed

src/main/scala/org/scalajs/dom/extensions/KeyboardExt.scala renamed to src/main/scala/org/scalajs/dom/extensions/Keyboard.scala

Lines changed: 81 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,28 @@ import scala.scalajs.js.UndefOr
99
object KeyboardPolyfill {
1010

1111
/**
12-
* Primarily it allows you to abandon onpress events that have cross-browser incompatible behavior and
13-
* that are to be deprecated in favor of beforeinput events in W3C DOM4. Calling polyfill method on keydown/keyup event
14-
* gives you normalized keyCode across platforms and browsers and estimated charCode in case of a key representing
15-
* printable character. pfKeyCode & optional pfCharCode properties are added to Event object for possible latter use
16-
*/
12+
* Primarily it allows you to abandon onpress events that have cross-browser incompatible behavior and
13+
* that are to be deprecated in favor of beforeinput events in W3C DOM4. Calling polyfill method on keydown/keyup event
14+
* gives you normalized keyCode across platforms and browsers and estimated charCode in case of a key representing
15+
* printable character. pfKeyCode & optional pfCharCode properties are added to Event object for possible latter use
16+
*/
1717
implicit class PfEvent(e: KeyboardEvent) {
18-
import KCode._
19-
import ChCode._
2018

2119
/** to retrieve polyfilled code later */
2220
def pfKeyCode: Option[Int] = getDynamic("pfKeyCode")
2321
def pfCharCode: Option[Int] = getDynamic("pfCharCode")
2422

2523
/**
26-
* Basically attempts to unite keyCodes across variety of platforms and browsers and
27-
* find a corresponding charCode in case of a printable unicode point
28-
* @return (keyCode, Option[charCode])
29-
*/
24+
* Basically attempts to unite keyCodes across variety of platforms and browsers and
25+
* find a corresponding charCode in case of a printable unicode point
26+
* @return (keyCode, Option[charCode])
27+
*/
3028
def polyfill(): (Int, Option[Int]) = {
3129
require(e.`type` != "keypress", "This polyfill only works with keydown/keyup events")
3230
val keyCode = normalize(e.keyCode)
33-
val result = shiftableKey2Char.lift(keyCode) match {
31+
val result = ChCode.shiftableKey2Char.lift(keyCode) match {
3432
case Some(shift) => (keyCode, Option(shift(e.shiftKey)))
35-
case None => (keyCode, key2char.lift(keyCode))
33+
case None => (keyCode, ChCode.key2char.lift(keyCode))
3634
}
3735
pfKeyCode(keyCode)
3836
result._2.foreach(pfCharCode(_))
@@ -46,63 +44,92 @@ object KeyboardPolyfill {
4644
private def pfCharCode(charCode: Int) = setDynamic("pfCharCode", charCode)
4745

4846
/**
49-
* To be improved continuously, most of the other stuff concerns Mac atypical keyboard layout and ancient browsers
50-
* You're welcome to contribute
51-
*/
47+
* To be improved continuously, most of the other stuff concerns Mac atypical keyboard layout and ancient browsers
48+
* You're welcome to contribute
49+
*/
5250
private def normalize(keyCode: Int): Int = {
5351
if (Device.isGecko)
5452
keyCode match {
55-
case 173 => Dash
56-
case 59 => Semicolon
57-
case 61 => Equals
58-
case 0 => Win
53+
case 173 => KCode.Dash
54+
case 59 => KCode.Semicolon
55+
case 61 => KCode.Equals
56+
case 0 => KCode.Win
5957
case other => keyCode
6058
}
6159
else if (Device.isMac)
6260
keyCode match {
63-
case 224 => Meta
64-
case 12 => NumLock
61+
case 224 => KCode.Meta
62+
case 12 => KCode.NumLock
6563
case other => keyCode
6664
}
6765
else
6866
keyCode
6967
}
7068
}
69+
70+
object Device {
71+
val IOSRegex = "iPhone|iPod|iPad".r
72+
73+
val userAgent = dom.window.navigator.userAgent
74+
val platform = dom.window.navigator.platform
75+
76+
val isIOS = IOSRegex.findFirstIn(userAgent).isDefined
77+
val isIPad = userAgent.contains("iPad")
78+
val isIPod = userAgent.contains("iPod")
79+
val isIPhone = userAgent.contains("iPhone")
80+
val isAndroid = userAgent.contains("Android")
81+
82+
val isGecko = userAgent.contains("Gecko/")
83+
val isWebKit = userAgent.contains("WebKit/")
84+
val isIE = userAgent.contains("Trident/")
85+
val isOpera = userAgent.contains("Opera/")
86+
val isChrome = userAgent.contains("Chrome/")
87+
88+
val isLinux = platform.contains("Linux")
89+
val isWin = platform.contains("Win")
90+
val isMac = platform.contains("Mac")
91+
val isChrOS = platform.contains("CrOS")
92+
93+
val isTouchable = isIOS || isAndroid
94+
}
7195
}
7296

97+
/**
98+
* @note ([0-9] * + - / .) are the only characters shared by 2 keys on keyboard, this duplication is caused by existence of numpad.
99+
*/
73100
object ChCode {
74101
import KCode._
75102

76103
/** shift changes charCode */
77104
private def >(w: Int, wo: Int)(shift: Boolean) = if (shift) w else wo
78105
/** add offset to a lower case letter which gives you it's char code */
79-
private def >>(keyCode: Int)(shift: Boolean) = if (shift) keyCode else keyCode + charSizeOffset
106+
private def >>(keyCode: Int)(shift: Boolean) = if (shift) keyCode else letterKeyToLowerCaseCharCode(keyCode)
80107

81108
/** keys that have different charCode representation when shift key is pressed */
82109
private val letterKey2Char = for(letterKeyCode <- A to Z) yield (letterKeyCode, >>(letterKeyCode)_)
83-
private implicit def Char2Int(ch: Char) = ch.toInt
110+
private implicit def Char2Int(ch: Char): Int = ch.toInt
84111
val shiftableKey2Char = Map[Int, Boolean => Int](
85-
(Num0, >(')', Num0)),
86-
(Num1, >('!', Num1)),
87-
(Num2, >('@', Num2)),
88-
(Num3, >('#', Num3)),
89-
(Num4, >('$', Num4)),
90-
(Num5, >('%', Num5)),
91-
(Num6, >('^', Num6)),
92-
(Num7, >('&', Num7)),
93-
(Num8, >('*', Num8)),
94-
(Num9, >('(', Num9)),
95-
(Comma, >('<', ',')),
96-
(Dash, >('_', '-')),
97-
(Period, >('>', '.')),
98-
(Slash, >('?', '/')),
99-
(GraveAccent, >('~', '`')),
100-
(SquareBracketOpen, >('{', '[')),
101-
(Backslash, >('|', '\\')),
102-
(SquareBracketClose, >('}', ']')),
103-
(SingleQuote, >('"', ''')),
104-
(Semicolon, >(':', ';')),
105-
(Equals, >('+', '='))
112+
(Num0, >(')', Num0)),
113+
(Num1, >('!', Num1)),
114+
(Num2, >('@', Num2)),
115+
(Num3, >('#', Num3)),
116+
(Num4, >('$', Num4)),
117+
(Num5, >('%', Num5)),
118+
(Num6, >('^', Num6)),
119+
(Num7, >('&', Num7)),
120+
(Num8, >('*', Num8)),
121+
(Num9, >('(', Num9)),
122+
(Comma, >('<', ',')),
123+
(Dash, >('_', '-')),
124+
(Period, >('>', '.')),
125+
(Slash, >('?', '/')),
126+
(Backtick, >('~', '`')),
127+
(SquareBracketOpen, >('{', '[')),
128+
(Backslash, >('|', '\\')),
129+
(SquareBracketClose, >('}', ']')),
130+
(SingleQuote, >('"', ''')),
131+
(Semicolon, >(':', ';')),
132+
(Equals, >('+', '='))
106133
) ++ letterKey2Char
107134

108135
val key2char = Map[Int, Int](
@@ -144,8 +171,10 @@ object KCode {
144171
/** [A-Z] charCode is equal to [a-z] keyCode, thus I won't duplicate constants */
145172
val charSizeOffset = 'a'.toInt - 'A'.toInt
146173

147-
def isLetter(keyCode: Int) = keyCode >= A && keyCode <= Z
148-
def isUpperCaseLetter(charCode: Int) = isLetter(charCode)
174+
def isLetterKey(keyCode: Int) = keyCode >= A && keyCode <= Z
175+
def isUpperCaseLetter(charCode: Int) = isLetterKey(charCode)
176+
def letterKeyToLowerCaseCharCode(keyCode: Int) = keyCode + charSizeOffset
177+
def letterKeyToUpperCaseCharCode(keyCode: Int) = keyCode // informative method
149178

150179
/** Upper case letters have CharCode equal to KeyCode */
151180
val A = 'A'.toInt // 65
@@ -175,22 +204,22 @@ object KCode {
175204
val Y = 'Y'.toInt // 89
176205
val Z = 'Z'.toInt // 90
177206

178-
/** Space & Enter have KeyCode equal to CharCode */
179-
val Space = ' '.toInt // 32 both charCode and keyCode
180-
val Enter = 13 // kind of
181-
182207
val Comma = 188
183208
val Dash = 189
184209
val Period = 190
185210
val Slash = 191
186-
val GraveAccent = 192
211+
val Backtick = 192
187212
val SquareBracketOpen = 219
188213
val Backslash = 220
189214
val SquareBracketClose = 221
190215
val SingleQuote = 222
191216
val Semicolon = 186
192217
val Equals = 187
193218

219+
/** Space & Enter have KeyCode equal to CharCode */
220+
val Space = 32 // both charCode and keyCode
221+
val Enter = 13 // both charCode and keyCode
222+
194223
/** Numpad numbers share common numbers charCode */
195224
val Numpad0 = (96, Num0)
196225
val Numpad1 = (97, Num1)
@@ -242,30 +271,4 @@ object KCode {
242271
val F11 = 122
243272
val F12 = 123
244273
val NumLock = 144
245-
}
246-
247-
object Device {
248-
val IOSRegex = "iPhone|iPod|iPad".r
249-
250-
val userAgent = dom.window.navigator.userAgent
251-
val platform = dom.window.navigator.platform
252-
253-
val isIOS = IOSRegex.findFirstIn(userAgent).isDefined
254-
val isIPad = userAgent.contains("iPad")
255-
val isIPod = userAgent.contains("iPod")
256-
val isIPhone = userAgent.contains("iPhone")
257-
val isAndroid = userAgent.contains("Android")
258-
259-
val isGecko = userAgent.contains("Gecko/")
260-
val isWebKit = userAgent.contains("WebKit/")
261-
val isIE = userAgent.contains("Trident/")
262-
val isOpera = userAgent.contains("Opera/")
263-
val isChrome = userAgent.contains("Chrome/")
264-
265-
val isLinux = platform.contains("Linux")
266-
val isWin = platform.contains("Win")
267-
val isMac = platform.contains("Mac")
268-
val isChrOS = platform.contains("CrOS")
269-
270-
val isTouchable = isIOS || isAndroid
271274
}

0 commit comments

Comments
 (0)