OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of html; | 5 part of html; |
6 | 6 |
7 /** | 7 /** |
8 * Internal class that does the actual calculations to determine keyCode and | 8 * Internal class that does the actual calculations to determine keyCode and |
9 * charCode for keydown, keypress, and keyup events for all browsers. | 9 * charCode for keydown, keypress, and keyup events for all browsers. |
10 */ | 10 */ |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
59 'Home': KeyCode.HOME, | 59 'Home': KeyCode.HOME, |
60 'End': KeyCode.END, | 60 'End': KeyCode.END, |
61 'PageUp': KeyCode.PAGE_UP, | 61 'PageUp': KeyCode.PAGE_UP, |
62 'PageDown': KeyCode.PAGE_DOWN, | 62 'PageDown': KeyCode.PAGE_DOWN, |
63 'Insert': KeyCode.INSERT | 63 'Insert': KeyCode.INSERT |
64 }; | 64 }; |
65 | 65 |
66 /** Return a stream for KeyEvents for the specified target. */ | 66 /** Return a stream for KeyEvents for the specified target. */ |
67 // Note: this actually functions like a factory constructor. | 67 // Note: this actually functions like a factory constructor. |
68 CustomStream<KeyEvent> forTarget(EventTarget e, {bool useCapture: false}) { | 68 CustomStream<KeyEvent> forTarget(EventTarget e, {bool useCapture: false}) { |
69 var handler = new _KeyboardEventHandler.initializeAllEventListeners( | 69 var handler = |
70 _type, e); | 70 new _KeyboardEventHandler.initializeAllEventListeners(_type, e); |
71 return handler._stream; | 71 return handler._stream; |
72 } | 72 } |
73 | 73 |
74 /** | 74 /** |
75 * General constructor, performs basic initialization for our improved | 75 * General constructor, performs basic initialization for our improved |
76 * KeyboardEvent controller. | 76 * KeyboardEvent controller. |
77 */ | 77 */ |
78 _KeyboardEventHandler(this._type): | 78 _KeyboardEventHandler(this._type) |
79 _stream = new _CustomKeyEventStreamImpl('event'), _target = null, | 79 : _stream = new _CustomKeyEventStreamImpl('event'), |
80 super(_EVENT_TYPE); | 80 _target = null, |
| 81 super(_EVENT_TYPE); |
81 | 82 |
82 /** | 83 /** |
83 * Hook up all event listeners under the covers so we can estimate keycodes | 84 * Hook up all event listeners under the covers so we can estimate keycodes |
84 * and charcodes when they are not provided. | 85 * and charcodes when they are not provided. |
85 */ | 86 */ |
86 _KeyboardEventHandler.initializeAllEventListeners(this._type, this._target) : | 87 _KeyboardEventHandler.initializeAllEventListeners(this._type, this._target) |
87 super(_EVENT_TYPE) { | 88 : super(_EVENT_TYPE) { |
88 Element.keyDownEvent.forTarget(_target, useCapture: true).listen( | 89 Element.keyDownEvent |
89 processKeyDown); | 90 .forTarget(_target, useCapture: true) |
90 Element.keyPressEvent.forTarget(_target, useCapture: true).listen( | 91 .listen(processKeyDown); |
91 processKeyPress); | 92 Element.keyPressEvent |
92 Element.keyUpEvent.forTarget(_target, useCapture: true).listen( | 93 .forTarget(_target, useCapture: true) |
93 processKeyUp); | 94 .listen(processKeyPress); |
| 95 Element.keyUpEvent |
| 96 .forTarget(_target, useCapture: true) |
| 97 .listen(processKeyUp); |
94 _stream = new _CustomKeyEventStreamImpl(_type); | 98 _stream = new _CustomKeyEventStreamImpl(_type); |
95 } | 99 } |
96 | 100 |
97 /** Determine if caps lock is one of the currently depressed keys. */ | 101 /** Determine if caps lock is one of the currently depressed keys. */ |
98 bool get _capsLockOn => | 102 bool get _capsLockOn => |
99 _keyDownList.any((var element) => element.keyCode == KeyCode.CAPS_LOCK); | 103 _keyDownList.any((var element) => element.keyCode == KeyCode.CAPS_LOCK); |
100 | 104 |
101 /** | 105 /** |
102 * Given the previously recorded keydown key codes, see if we can determine | 106 * Given the previously recorded keydown key codes, see if we can determine |
103 * the keycode of this keypress [event]. (Generally browsers only provide | 107 * the keycode of this keypress [event]. (Generally browsers only provide |
104 * charCode information for keypress events, but with a little | 108 * charCode information for keypress events, but with a little |
105 * reverse-engineering, we can also determine the keyCode.) Returns | 109 * reverse-engineering, we can also determine the keyCode.) Returns |
106 * KeyCode.UNKNOWN if the keycode could not be determined. | 110 * KeyCode.UNKNOWN if the keycode could not be determined. |
107 */ | 111 */ |
108 int _determineKeyCodeForKeypress(KeyboardEvent event) { | 112 int _determineKeyCodeForKeypress(KeyboardEvent event) { |
109 // Note: This function is a work in progress. We'll expand this function | 113 // Note: This function is a work in progress. We'll expand this function |
110 // once we get more information about other keyboards. | 114 // once we get more information about other keyboards. |
111 for (var prevEvent in _keyDownList) { | 115 for (var prevEvent in _keyDownList) { |
112 if (prevEvent._shadowCharCode == event.charCode) { | 116 if (prevEvent._shadowCharCode == event.charCode) { |
113 return prevEvent.keyCode; | 117 return prevEvent.keyCode; |
114 } | 118 } |
115 if ((event.shiftKey || _capsLockOn) && event.charCode >= "A".codeUnits[0] | 119 if ((event.shiftKey || _capsLockOn) && |
116 && event.charCode <= "Z".codeUnits[0] && event.charCode + | 120 event.charCode >= "A".codeUnits[0] && |
117 _ROMAN_ALPHABET_OFFSET == prevEvent._shadowCharCode) { | 121 event.charCode <= "Z".codeUnits[0] && |
| 122 event.charCode + _ROMAN_ALPHABET_OFFSET == |
| 123 prevEvent._shadowCharCode) { |
118 return prevEvent.keyCode; | 124 return prevEvent.keyCode; |
119 } | 125 } |
120 } | 126 } |
121 return KeyCode.UNKNOWN; | 127 return KeyCode.UNKNOWN; |
122 } | 128 } |
123 | 129 |
124 /** | 130 /** |
125 * Given the charater code returned from a keyDown [event], try to ascertain | 131 * Given the charater code returned from a keyDown [event], try to ascertain |
126 * and return the corresponding charCode for the character that was pressed. | 132 * and return the corresponding charCode for the character that was pressed. |
127 * This information is not shown to the user, but used to help polyfill | 133 * This information is not shown to the user, but used to help polyfill |
128 * keypress events. | 134 * keypress events. |
129 */ | 135 */ |
130 int _findCharCodeKeyDown(KeyboardEvent event) { | 136 int _findCharCodeKeyDown(KeyboardEvent event) { |
131 if (event.keyLocation == 3) { // Numpad keys. | 137 if (event.keyLocation == 3) { |
| 138 // Numpad keys. |
132 switch (event.keyCode) { | 139 switch (event.keyCode) { |
133 case KeyCode.NUM_ZERO: | 140 case KeyCode.NUM_ZERO: |
134 // Even though this function returns _charCodes_, for some cases the | 141 // Even though this function returns _charCodes_, for some cases the |
135 // KeyCode == the charCode we want, in which case we use the keycode | 142 // KeyCode == the charCode we want, in which case we use the keycode |
136 // constant for readability. | 143 // constant for readability. |
137 return KeyCode.ZERO; | 144 return KeyCode.ZERO; |
138 case KeyCode.NUM_ONE: | 145 case KeyCode.NUM_ONE: |
139 return KeyCode.ONE; | 146 return KeyCode.ONE; |
140 case KeyCode.NUM_TWO: | 147 case KeyCode.NUM_TWO: |
141 return KeyCode.TWO; | 148 return KeyCode.TWO; |
(...skipping 21 matching lines...) Expand all Loading... |
163 return 46; // . | 170 return 46; // . |
164 case KeyCode.NUM_DIVISION: | 171 case KeyCode.NUM_DIVISION: |
165 return 47; // / | 172 return 47; // / |
166 } | 173 } |
167 } else if (event.keyCode >= 65 && event.keyCode <= 90) { | 174 } else if (event.keyCode >= 65 && event.keyCode <= 90) { |
168 // Set the "char code" for key down as the lower case letter. Again, this | 175 // Set the "char code" for key down as the lower case letter. Again, this |
169 // will not show up for the user, but will be helpful in estimating | 176 // will not show up for the user, but will be helpful in estimating |
170 // keyCode locations and other information during the keyPress event. | 177 // keyCode locations and other information during the keyPress event. |
171 return event.keyCode + _ROMAN_ALPHABET_OFFSET; | 178 return event.keyCode + _ROMAN_ALPHABET_OFFSET; |
172 } | 179 } |
173 switch(event.keyCode) { | 180 switch (event.keyCode) { |
174 case KeyCode.SEMICOLON: | 181 case KeyCode.SEMICOLON: |
175 return KeyCode.FF_SEMICOLON; | 182 return KeyCode.FF_SEMICOLON; |
176 case KeyCode.EQUALS: | 183 case KeyCode.EQUALS: |
177 return KeyCode.FF_EQUALS; | 184 return KeyCode.FF_EQUALS; |
178 case KeyCode.COMMA: | 185 case KeyCode.COMMA: |
179 return 44; // Ascii value for , | 186 return 44; // Ascii value for , |
180 case KeyCode.DASH: | 187 case KeyCode.DASH: |
181 return 45; // - | 188 return 45; // - |
182 case KeyCode.PERIOD: | 189 case KeyCode.PERIOD: |
183 return 46; // . | 190 return 46; // . |
(...skipping 26 matching lines...) Expand all Loading... |
210 } | 217 } |
211 | 218 |
212 // Alt but not AltGr which is represented as Alt+Ctrl. | 219 // Alt but not AltGr which is represented as Alt+Ctrl. |
213 if (event.altKey && !event.ctrlKey) { | 220 if (event.altKey && !event.ctrlKey) { |
214 return false; | 221 return false; |
215 } | 222 } |
216 | 223 |
217 // Saves Ctrl or Alt + key for IE and WebKit, which won't fire keypress. | 224 // Saves Ctrl or Alt + key for IE and WebKit, which won't fire keypress. |
218 if (!event.shiftKey && | 225 if (!event.shiftKey && |
219 (_keyDownList.last.keyCode == KeyCode.CTRL || | 226 (_keyDownList.last.keyCode == KeyCode.CTRL || |
220 _keyDownList.last.keyCode == KeyCode.ALT || | 227 _keyDownList.last.keyCode == KeyCode.ALT || |
221 Device.userAgent.contains('Mac') && | 228 Device.userAgent.contains('Mac') && |
222 _keyDownList.last.keyCode == KeyCode.META)) { | 229 _keyDownList.last.keyCode == KeyCode.META)) { |
223 return false; | 230 return false; |
224 } | 231 } |
225 | 232 |
226 // Some keys with Ctrl/Shift do not issue keypress in WebKit. | 233 // Some keys with Ctrl/Shift do not issue keypress in WebKit. |
227 if (Device.isWebKit && event.ctrlKey && event.shiftKey && ( | 234 if (Device.isWebKit && |
228 event.keyCode == KeyCode.BACKSLASH || | 235 event.ctrlKey && |
229 event.keyCode == KeyCode.OPEN_SQUARE_BRACKET || | 236 event.shiftKey && |
230 event.keyCode == KeyCode.CLOSE_SQUARE_BRACKET || | 237 (event.keyCode == KeyCode.BACKSLASH || |
231 event.keyCode == KeyCode.TILDE || | 238 event.keyCode == KeyCode.OPEN_SQUARE_BRACKET || |
232 event.keyCode == KeyCode.SEMICOLON || event.keyCode == KeyCode.DASH || | 239 event.keyCode == KeyCode.CLOSE_SQUARE_BRACKET || |
233 event.keyCode == KeyCode.EQUALS || event.keyCode == KeyCode.COMMA || | 240 event.keyCode == KeyCode.TILDE || |
234 event.keyCode == KeyCode.PERIOD || event.keyCode == KeyCode.SLASH || | 241 event.keyCode == KeyCode.SEMICOLON || |
235 event.keyCode == KeyCode.APOSTROPHE || | 242 event.keyCode == KeyCode.DASH || |
236 event.keyCode == KeyCode.SINGLE_QUOTE)) { | 243 event.keyCode == KeyCode.EQUALS || |
| 244 event.keyCode == KeyCode.COMMA || |
| 245 event.keyCode == KeyCode.PERIOD || |
| 246 event.keyCode == KeyCode.SLASH || |
| 247 event.keyCode == KeyCode.APOSTROPHE || |
| 248 event.keyCode == KeyCode.SINGLE_QUOTE)) { |
237 return false; | 249 return false; |
238 } | 250 } |
239 | 251 |
240 switch (event.keyCode) { | 252 switch (event.keyCode) { |
241 case KeyCode.ENTER: | 253 case KeyCode.ENTER: |
242 // IE9 does not fire keypress on ENTER. | 254 // IE9 does not fire keypress on ENTER. |
243 return !Device.isIE; | 255 return !Device.isIE; |
244 case KeyCode.ESC: | 256 case KeyCode.ESC: |
245 return !Device.isWebKit; | 257 return !Device.isWebKit; |
246 } | 258 } |
247 | 259 |
248 return KeyCode.isCharacterKey(event.keyCode); | 260 return KeyCode.isCharacterKey(event.keyCode); |
249 } | 261 } |
250 | 262 |
251 /** | 263 /** |
252 * Normalize the keycodes to the IE KeyCodes (this is what Chrome, IE, and | 264 * Normalize the keycodes to the IE KeyCodes (this is what Chrome, IE, and |
253 * Opera all use). | 265 * Opera all use). |
254 */ | 266 */ |
255 int _normalizeKeyCodes(KeyboardEvent event) { | 267 int _normalizeKeyCodes(KeyboardEvent event) { |
256 // Note: This may change once we get input about non-US keyboards. | 268 // Note: This may change once we get input about non-US keyboards. |
257 if (Device.isFirefox) { | 269 if (Device.isFirefox) { |
258 switch(event.keyCode) { | 270 switch (event.keyCode) { |
259 case KeyCode.FF_EQUALS: | 271 case KeyCode.FF_EQUALS: |
260 return KeyCode.EQUALS; | 272 return KeyCode.EQUALS; |
261 case KeyCode.FF_SEMICOLON: | 273 case KeyCode.FF_SEMICOLON: |
262 return KeyCode.SEMICOLON; | 274 return KeyCode.SEMICOLON; |
263 case KeyCode.MAC_FF_META: | 275 case KeyCode.MAC_FF_META: |
264 return KeyCode.META; | 276 return KeyCode.META; |
265 case KeyCode.WIN_KEY_FF_LINUX: | 277 case KeyCode.WIN_KEY_FF_LINUX: |
266 return KeyCode.WIN_KEY; | 278 return KeyCode.WIN_KEY; |
267 } | 279 } |
268 } | 280 } |
269 return event.keyCode; | 281 return event.keyCode; |
270 } | 282 } |
271 | 283 |
272 /** Handle keydown events. */ | 284 /** Handle keydown events. */ |
273 void processKeyDown(KeyboardEvent e) { | 285 void processKeyDown(KeyboardEvent e) { |
274 // Ctrl-Tab and Alt-Tab can cause the focus to be moved to another window | 286 // Ctrl-Tab and Alt-Tab can cause the focus to be moved to another window |
275 // before we've caught a key-up event. If the last-key was one of these | 287 // before we've caught a key-up event. If the last-key was one of these |
276 // we reset the state. | 288 // we reset the state. |
277 if (_keyDownList.length > 0 && | 289 if (_keyDownList.length > 0 && |
278 (_keyDownList.last.keyCode == KeyCode.CTRL && !e.ctrlKey || | 290 (_keyDownList.last.keyCode == KeyCode.CTRL && !e.ctrlKey || |
279 _keyDownList.last.keyCode == KeyCode.ALT && !e.altKey || | 291 _keyDownList.last.keyCode == KeyCode.ALT && !e.altKey || |
280 Device.userAgent.contains('Mac') && | 292 Device.userAgent.contains('Mac') && |
281 _keyDownList.last.keyCode == KeyCode.META && !e.metaKey)) { | 293 _keyDownList.last.keyCode == KeyCode.META && |
| 294 !e.metaKey)) { |
282 _keyDownList.clear(); | 295 _keyDownList.clear(); |
283 } | 296 } |
284 | 297 |
285 var event = new KeyEvent.wrap(e); | 298 var event = new KeyEvent.wrap(e); |
286 event._shadowKeyCode = _normalizeKeyCodes(event); | 299 event._shadowKeyCode = _normalizeKeyCodes(event); |
287 // Technically a "keydown" event doesn't have a charCode. This is | 300 // Technically a "keydown" event doesn't have a charCode. This is |
288 // calculated nonetheless to provide us with more information in giving | 301 // calculated nonetheless to provide us with more information in giving |
289 // as much information as possible on keypress about keycode and also | 302 // as much information as possible on keypress about keycode and also |
290 // charCode. | 303 // charCode. |
291 event._shadowCharCode = _findCharCodeKeyDown(event); | 304 event._shadowCharCode = _findCharCodeKeyDown(event); |
292 if (_keyDownList.length > 0 && event.keyCode != _keyDownList.last.keyCode && | 305 if (_keyDownList.length > 0 && |
| 306 event.keyCode != _keyDownList.last.keyCode && |
293 !_firesKeyPressEvent(event)) { | 307 !_firesKeyPressEvent(event)) { |
294 // Some browsers have quirks not firing keypress events where all other | 308 // Some browsers have quirks not firing keypress events where all other |
295 // browsers do. This makes them more consistent. | 309 // browsers do. This makes them more consistent. |
296 processKeyPress(e); | 310 processKeyPress(e); |
297 } | 311 } |
298 _keyDownList.add(event); | 312 _keyDownList.add(event); |
299 _stream.add(event); | 313 _stream.add(event); |
300 } | 314 } |
301 | 315 |
302 /** Handle keypress events. */ | 316 /** Handle keypress events. */ |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 } else if (_keyDownList.length > 0) { | 357 } else if (_keyDownList.length > 0) { |
344 // This happens when we've reached some international keyboard case we | 358 // This happens when we've reached some international keyboard case we |
345 // haven't accounted for or we haven't correctly eliminated all browser | 359 // haven't accounted for or we haven't correctly eliminated all browser |
346 // inconsistencies. Filing bugs on when this is reached is welcome! | 360 // inconsistencies. Filing bugs on when this is reached is welcome! |
347 _keyDownList.removeLast(); | 361 _keyDownList.removeLast(); |
348 } | 362 } |
349 _stream.add(e); | 363 _stream.add(e); |
350 } | 364 } |
351 } | 365 } |
352 | 366 |
353 | |
354 /** | 367 /** |
355 * Records KeyboardEvents that occur on a particular element, and provides a | 368 * Records KeyboardEvents that occur on a particular element, and provides a |
356 * stream of outgoing KeyEvents with cross-browser consistent keyCode and | 369 * stream of outgoing KeyEvents with cross-browser consistent keyCode and |
357 * charCode values despite the fact that a multitude of browsers that have | 370 * charCode values despite the fact that a multitude of browsers that have |
358 * varying keyboard default behavior. | 371 * varying keyboard default behavior. |
359 * | 372 * |
360 * Example usage: | 373 * Example usage: |
361 * | 374 * |
362 * KeyboardEventStream.onKeyDown(document.body).listen( | 375 * KeyboardEventStream.onKeyDown(document.body).listen( |
363 * keydownHandlerTest); | 376 * keydownHandlerTest); |
364 * | 377 * |
365 * This class is very much a work in progress, and we'd love to get information | 378 * This class is very much a work in progress, and we'd love to get information |
366 * on how we can make this class work with as many international keyboards as | 379 * on how we can make this class work with as many international keyboards as |
367 * possible. Bugs welcome! | 380 * possible. Bugs welcome! |
368 */ | 381 */ |
369 class KeyboardEventStream { | 382 class KeyboardEventStream { |
370 | |
371 /** Named constructor to produce a stream for onKeyPress events. */ | 383 /** Named constructor to produce a stream for onKeyPress events. */ |
372 static CustomStream<KeyEvent> onKeyPress(EventTarget target) => | 384 static CustomStream<KeyEvent> onKeyPress(EventTarget target) => |
373 new _KeyboardEventHandler('keypress').forTarget(target); | 385 new _KeyboardEventHandler('keypress').forTarget(target); |
374 | 386 |
375 /** Named constructor to produce a stream for onKeyUp events. */ | 387 /** Named constructor to produce a stream for onKeyUp events. */ |
376 static CustomStream<KeyEvent> onKeyUp(EventTarget target) => | 388 static CustomStream<KeyEvent> onKeyUp(EventTarget target) => |
377 new _KeyboardEventHandler('keyup').forTarget(target); | 389 new _KeyboardEventHandler('keyup').forTarget(target); |
378 | 390 |
379 /** Named constructor to produce a stream for onKeyDown events. */ | 391 /** Named constructor to produce a stream for onKeyDown events. */ |
380 static CustomStream<KeyEvent> onKeyDown(EventTarget target) => | 392 static CustomStream<KeyEvent> onKeyDown(EventTarget target) => |
381 new _KeyboardEventHandler('keydown').forTarget(target); | 393 new _KeyboardEventHandler('keydown').forTarget(target); |
382 } | 394 } |
OLD | NEW |