Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(654)

Side by Side Diff: tools/dom/src/KeyboardEventStream.dart

Issue 23455033: Fully polyfill KeyEvent so that you can programmatically create your own "keyboard" events. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 */
11 class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> { 11 class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> {
12 // This code inspired by Closure's KeyHandling library. 12 // This code inspired by Closure's KeyHandling library.
13 // http://closure-library.googlecode.com/svn/docs/closure_goog_events_keyhandl er.js.source.html 13 // http://closure-library.googlecode.com/svn/docs/closure_goog_events_keyhandl er.js.source.html
14 14
15 /** 15 /**
16 * The set of keys that have been pressed down without seeing their 16 * The set of keys that have been pressed down without seeing their
17 * corresponding keyup event. 17 * corresponding keyup event.
18 */ 18 */
19 final List<KeyboardEvent> _keyDownList = <KeyboardEvent>[]; 19 final List<KeyboardEvent> _keyDownList = <KeyboardEvent>[];
20 20
21 /** The type of KeyEvent we are tracking (keyup, keydown, keypress). */ 21 /** The type of KeyEvent we are tracking (keyup, keydown, keypress). */
22 final String _type; 22 final String _type;
23 23
24 /** The element we are watching for events to happen on. */ 24 /** The element we are watching for events to happen on. */
25 final EventTarget _target; 25 final EventTarget _target;
26 26
27 // The distance to shift from upper case alphabet Roman letters to lower case. 27 // The distance to shift from upper case alphabet Roman letters to lower case.
28 static final int _ROMAN_ALPHABET_OFFSET = "a".codeUnits[0] - "A".codeUnits[0]; 28 static final int _ROMAN_ALPHABET_OFFSET = "a".codeUnits[0] - "A".codeUnits[0];
29 29
30 /** Controller to produce KeyEvents for the stream. */ 30 /** Custom Stream (Controller) to produce KeyEvents for the stream. */
31 final StreamController _controller = new StreamController(sync: true); 31 _CustomKeyEventStreamImpl _stream;
32 32
33 static const _EVENT_TYPE = 'KeyEvent'; 33 static const _EVENT_TYPE = 'KeyEvent';
34 34
35 /** 35 /**
36 * An enumeration of key identifiers currently part of the W3C draft for DOM3 36 * An enumeration of key identifiers currently part of the W3C draft for DOM3
37 * and their mappings to keyCodes. 37 * and their mappings to keyCodes.
38 * http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set 38 * http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set
39 */ 39 */
40 static const Map<String, int> _keyIdentifier = const { 40 static const Map<String, int> _keyIdentifier = const {
41 'Up': KeyCode.UP, 41 'Up': KeyCode.UP,
(...skipping 15 matching lines...) Expand all
57 'F12': KeyCode.F12, 57 'F12': KeyCode.F12,
58 'U+007F': KeyCode.DELETE, 58 'U+007F': KeyCode.DELETE,
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 Stream<KeyEvent> forTarget(EventTarget e, {bool useCapture: false}) { 67 // Note: this actually functions like a factory constructor.
68 return new _KeyboardEventHandler.initializeAllEventListeners( 68 CustomStream<KeyEvent> forTarget(EventTarget e, {bool useCapture: false}) {
69 _type, e).stream; 69 var handler = new _KeyboardEventHandler.initializeAllEventListeners(
70 } 70 _type, e);
71 71 return handler._stream;
72 /**
73 * Accessor to the stream associated with a particular KeyboardEvent
74 * EventTarget.
75 *
76 * [forTarget] must be called to initialize this stream to listen to a
77 * particular EventTarget.
78 */
79 Stream<KeyEvent> get stream {
80 if(_target != null) {
81 return _controller.stream;
82 } else {
83 throw new StateError("Not initialized. Call forTarget to access a stream "
84 "initialized with a particular EventTarget.");
85 }
86 } 72 }
87 73
88 /** 74 /**
89 * General constructor, performs basic initialization for our improved 75 * General constructor, performs basic initialization for our improved
90 * KeyboardEvent controller. 76 * KeyboardEvent controller.
91 */ 77 */
92 _KeyboardEventHandler(this._type) : 78 _KeyboardEventHandler(this._type): super(_EVENT_TYPE),
93 _target = null, super(_EVENT_TYPE) { 79 _stream = new _CustomKeyEventStreamImpl('event');
94 }
95 80
96 /** 81 /**
97 * Hook up all event listeners under the covers so we can estimate keycodes 82 * Hook up all event listeners under the covers so we can estimate keycodes
98 * and charcodes when they are not provided. 83 * and charcodes when they are not provided.
99 */ 84 */
100 _KeyboardEventHandler.initializeAllEventListeners(this._type, this._target) : 85 _KeyboardEventHandler.initializeAllEventListeners(this._type, this._target) :
101 super(_EVENT_TYPE) { 86 super(_EVENT_TYPE) {
102 Element.keyDownEvent.forTarget(_target, useCapture: true).listen( 87 Element.keyDownEvent.forTarget(_target, useCapture: true).listen(
103 processKeyDown); 88 processKeyDown);
104 Element.keyPressEvent.forTarget(_target, useCapture: true).listen( 89 Element.keyPressEvent.forTarget(_target, useCapture: true).listen(
105 processKeyPress); 90 processKeyPress);
106 Element.keyUpEvent.forTarget(_target, useCapture: true).listen( 91 Element.keyUpEvent.forTarget(_target, useCapture: true).listen(
107 processKeyUp); 92 processKeyUp);
108 } 93 _stream = new _CustomKeyEventStreamImpl(_type);
109
110 /**
111 * Notify all callback listeners that a KeyEvent of the relevant type has
112 * occurred.
113 */
114 bool _dispatch(KeyEvent event) {
115 if (event.type == _type)
116 _controller.add(event);
117 } 94 }
118 95
119 /** Determine if caps lock is one of the currently depressed keys. */ 96 /** Determine if caps lock is one of the currently depressed keys. */
120 bool get _capsLockOn => 97 bool get _capsLockOn =>
121 _keyDownList.any((var element) => element.keyCode == KeyCode.CAPS_LOCK); 98 _keyDownList.any((var element) => element.keyCode == KeyCode.CAPS_LOCK);
122 99
123 /** 100 /**
124 * Given the previously recorded keydown key codes, see if we can determine 101 * Given the previously recorded keydown key codes, see if we can determine
125 * the keycode of this keypress [event]. (Generally browsers only provide 102 * the keycode of this keypress [event]. (Generally browsers only provide
126 * charCode information for keypress events, but with a little 103 * charCode information for keypress events, but with a little
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after
297 // before we've caught a key-up event. If the last-key was one of these 274 // before we've caught a key-up event. If the last-key was one of these
298 // we reset the state. 275 // we reset the state.
299 if (_keyDownList.length > 0 && 276 if (_keyDownList.length > 0 &&
300 (_keyDownList.last.keyCode == KeyCode.CTRL && !e.ctrlKey || 277 (_keyDownList.last.keyCode == KeyCode.CTRL && !e.ctrlKey ||
301 _keyDownList.last.keyCode == KeyCode.ALT && !e.altKey || 278 _keyDownList.last.keyCode == KeyCode.ALT && !e.altKey ||
302 Device.userAgent.contains('Mac') && 279 Device.userAgent.contains('Mac') &&
303 _keyDownList.last.keyCode == KeyCode.META && !e.metaKey)) { 280 _keyDownList.last.keyCode == KeyCode.META && !e.metaKey)) {
304 _keyDownList.clear(); 281 _keyDownList.clear();
305 } 282 }
306 283
307 var event = new KeyEvent(e); 284 var event = new KeyEvent.wrap(e);
308 event._shadowKeyCode = _normalizeKeyCodes(event); 285 event._shadowKeyCode = _normalizeKeyCodes(event);
309 // Technically a "keydown" event doesn't have a charCode. This is 286 // Technically a "keydown" event doesn't have a charCode. This is
310 // calculated nonetheless to provide us with more information in giving 287 // calculated nonetheless to provide us with more information in giving
311 // as much information as possible on keypress about keycode and also 288 // as much information as possible on keypress about keycode and also
312 // charCode. 289 // charCode.
313 event._shadowCharCode = _findCharCodeKeyDown(event); 290 event._shadowCharCode = _findCharCodeKeyDown(event);
314 if (_keyDownList.length > 0 && event.keyCode != _keyDownList.last.keyCode && 291 if (_keyDownList.length > 0 && event.keyCode != _keyDownList.last.keyCode &&
315 !_firesKeyPressEvent(event)) { 292 !_firesKeyPressEvent(event)) {
316 // Some browsers have quirks not firing keypress events where all other 293 // Some browsers have quirks not firing keypress events where all other
317 // browsers do. This makes them more consistent. 294 // browsers do. This makes them more consistent.
318 processKeyPress(e); 295 processKeyPress(e);
319 } 296 }
320 _keyDownList.add(event); 297 _keyDownList.add(event);
321 _dispatch(event); 298 _stream.add(event);
322 } 299 }
323 300
324 /** Handle keypress events. */ 301 /** Handle keypress events. */
325 void processKeyPress(KeyboardEvent event) { 302 void processKeyPress(KeyboardEvent event) {
326 var e = new KeyEvent(event); 303 var e = new KeyEvent.wrap(event);
327 // IE reports the character code in the keyCode field for keypress events. 304 // IE reports the character code in the keyCode field for keypress events.
328 // There are two exceptions however, Enter and Escape. 305 // There are two exceptions however, Enter and Escape.
329 if (Device.isIE) { 306 if (Device.isIE) {
330 if (e.keyCode == KeyCode.ENTER || e.keyCode == KeyCode.ESC) { 307 if (e.keyCode == KeyCode.ENTER || e.keyCode == KeyCode.ESC) {
331 e._shadowCharCode = 0; 308 e._shadowCharCode = 0;
332 } else { 309 } else {
333 e._shadowCharCode = e.keyCode; 310 e._shadowCharCode = e.keyCode;
334 } 311 }
335 } else if (Device.isOpera) { 312 } else if (Device.isOpera) {
336 // Opera reports the character code in the keyCode field. 313 // Opera reports the character code in the keyCode field.
337 e._shadowCharCode = KeyCode.isCharacterKey(e.keyCode) ? e.keyCode : 0; 314 e._shadowCharCode = KeyCode.isCharacterKey(e.keyCode) ? e.keyCode : 0;
338 } 315 }
339 // Now we guestimate about what the keycode is that was actually 316 // Now we guestimate about what the keycode is that was actually
340 // pressed, given previous keydown information. 317 // pressed, given previous keydown information.
341 e._shadowKeyCode = _determineKeyCodeForKeypress(e); 318 e._shadowKeyCode = _determineKeyCodeForKeypress(e);
342 319
343 // Correct the key value for certain browser-specific quirks. 320 // Correct the key value for certain browser-specific quirks.
344 if (e._shadowKeyIdentifier != null && 321 if (e._shadowKeyIdentifier != null &&
345 _keyIdentifier.containsKey(e._shadowKeyIdentifier)) { 322 _keyIdentifier.containsKey(e._shadowKeyIdentifier)) {
346 // This is needed for Safari Windows because it currently doesn't give a 323 // This is needed for Safari Windows because it currently doesn't give a
347 // keyCode/which for non printable keys. 324 // keyCode/which for non printable keys.
348 e._shadowKeyCode = _keyIdentifier[e._shadowKeyIdentifier]; 325 e._shadowKeyCode = _keyIdentifier[e._shadowKeyIdentifier];
349 } 326 }
350 e._shadowAltKey = _keyDownList.any((var element) => element.altKey); 327 e._shadowAltKey = _keyDownList.any((var element) => element.altKey);
351 _dispatch(e); 328 _stream.add(e);
352 } 329 }
353 330
354 /** Handle keyup events. */ 331 /** Handle keyup events. */
355 void processKeyUp(KeyboardEvent event) { 332 void processKeyUp(KeyboardEvent event) {
356 var e = new KeyEvent(event); 333 var e = new KeyEvent.wrap(event);
357 KeyboardEvent toRemove = null; 334 KeyboardEvent toRemove = null;
358 for (var key in _keyDownList) { 335 for (var key in _keyDownList) {
359 if (key.keyCode == e.keyCode) { 336 if (key.keyCode == e.keyCode) {
360 toRemove = key; 337 toRemove = key;
361 } 338 }
362 } 339 }
363 if (toRemove != null) { 340 if (toRemove != null) {
364 _keyDownList.removeWhere((element) => element == toRemove); 341 _keyDownList.removeWhere((element) => element == toRemove);
365 } else if (_keyDownList.length > 0) { 342 } else if (_keyDownList.length > 0) {
366 // This happens when we've reached some international keyboard case we 343 // This happens when we've reached some international keyboard case we
367 // haven't accounted for or we haven't correctly eliminated all browser 344 // haven't accounted for or we haven't correctly eliminated all browser
368 // inconsistencies. Filing bugs on when this is reached is welcome! 345 // inconsistencies. Filing bugs on when this is reached is welcome!
369 _keyDownList.removeLast(); 346 _keyDownList.removeLast();
370 } 347 }
371 _dispatch(e); 348 _stream.add(e);
372 } 349 }
373 } 350 }
374 351
375 352
376 /** 353 /**
377 * Records KeyboardEvents that occur on a particular element, and provides a 354 * Records KeyboardEvents that occur on a particular element, and provides a
378 * stream of outgoing KeyEvents with cross-browser consistent keyCode and 355 * stream of outgoing KeyEvents with cross-browser consistent keyCode and
379 * charCode values despite the fact that a multitude of browsers that have 356 * charCode values despite the fact that a multitude of browsers that have
380 * varying keyboard default behavior. 357 * varying keyboard default behavior.
381 * 358 *
382 * Example usage: 359 * Example usage:
383 * 360 *
384 * KeyboardEventStream.onKeyDown(document.body).listen( 361 * KeyboardEventStream.onKeyDown(document.body).listen(
385 * keydownHandlerTest); 362 * keydownHandlerTest);
386 * 363 *
387 * This class is very much a work in progress, and we'd love to get information 364 * This class is very much a work in progress, and we'd love to get information
388 * on how we can make this class work with as many international keyboards as 365 * on how we can make this class work with as many international keyboards as
389 * possible. Bugs welcome! 366 * possible. Bugs welcome!
390 */ 367 */
391 class KeyboardEventStream { 368 class KeyboardEventStream {
392 369
393 /** Named constructor to produce a stream for onKeyPress events. */ 370 /** Named constructor to produce a stream for onKeyPress events. */
394 static Stream<KeyEvent> onKeyPress(EventTarget target) => 371 static CustomStream<KeyEvent> onKeyPress(EventTarget target) =>
395 new _KeyboardEventHandler('keypress').forTarget(target); 372 new _KeyboardEventHandler('keypress').forTarget(target);
396 373
397 /** Named constructor to produce a stream for onKeyUp events. */ 374 /** Named constructor to produce a stream for onKeyUp events. */
398 static Stream<KeyEvent> onKeyUp(EventTarget target) => 375 static CustomStream<KeyEvent> onKeyUp(EventTarget target) =>
399 new _KeyboardEventHandler('keyup').forTarget(target); 376 new _KeyboardEventHandler('keyup').forTarget(target);
400 377
401 /** Named constructor to produce a stream for onKeyDown events. */ 378 /** Named constructor to produce a stream for onKeyDown events. */
402 static Stream<KeyEvent> onKeyDown(EventTarget target) => 379 static CustomStream<KeyEvent> onKeyDown(EventTarget target) =>
403 new _KeyboardEventHandler('keydown').forTarget(target); 380 new _KeyboardEventHandler('keydown').forTarget(target);
404 } 381 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698