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