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

Unified Diff: sdk/lib/html/dart2js/html_dart2js.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:
Download patch
« no previous file with comments | « no previous file | sdk/lib/html/dartium/html_dartium.dart » ('j') | tools/dom/src/dart2js_KeyEvent.dart » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/html/dart2js/html_dart2js.dart
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index 3641bda4c9364dafe2543784bc322b07f7f8f04f..6784962dbc5393c3dd89b66b598f369c44db7fad 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -52,10 +52,11 @@ import 'dart:_foreign_helper' show JS;
import 'dart:_js_helper' show
convertDartClosureToJS, Creates, JavaScriptIndexingBehavior,
JSName, Null, Returns,
- findDispatchTagForInterceptorClass, setNativeSubclassDispatchRecord;
+ findDispatchTagForInterceptorClass, setNativeSubclassDispatchRecord,
+ makeLeafDispatchRecord;
import 'dart:_interceptors' show
Interceptor, JSExtendableArray, findInterceptorConstructorForType,
- getNativeInterceptor;
+ getNativeInterceptor, setDispatchProperty;
export 'dart:math' show Rectangle, Point;
@@ -11061,6 +11062,8 @@ class Event extends Interceptor native "Event" {
return e;
}
+ Event._private();
+
@DomName('Event.AT_TARGET')
@DocsEditable()
static const int AT_TARGET = 2;
@@ -14811,6 +14814,12 @@ class KeyboardEvent extends UIEvent native "KeyboardEvent" {
return e;
}
+ /**
+ * Used for KeyboardEvent polyfilling when programmatically constructing
+ * KeyEvents.
+ */
+ KeyboardEvent._private() : super._private();
+
@DomName('KeyboardEvent.initKeyboardEvent')
void _initKeyboardEvent(String type, bool canBubble, bool cancelable,
Window view, String keyIdentifier, int keyLocation, bool ctrlKey,
@@ -24325,6 +24334,8 @@ class UIEvent extends Event native "UIEvent" {
e._initUIEvent(type, canBubble, cancelable, view, detail);
return e;
}
+
+ UIEvent._private() : super._private();
// To suppress missing implicit constructor warnings.
factory UIEvent._() { throw new UnsupportedError("Not supported"); }
@@ -29107,6 +29118,61 @@ class _ElementListEventStreamImpl<T extends Event> extends Stream<T>
}
/**
+ * A stream of custom events, which enables the user to "fire" (add) their own
+ * custom events to a stream.
+ */
+abstract class CustomStream<T extends Event> implements Stream<T> {
+ /**
+ * Add the following custom event to the stream for dispatching to interested
+ * listeners.
+ */
+ void add(T event);
+}
+
+class _CustomEventStreamImpl<T extends Event> extends Stream<T>
+ implements CustomStream<T> {
+ StreamController<T> _streamController;
+ /** The type of event this stream is providing (e.g. "keydown"). */
+ String _type;
+
+ _CustomEventStreamImpl(String type) {
+ _type = type;
+ _streamController = new StreamController.broadcast(sync: true);
+ }
+
+ // Delegate all regular Stream behavior to our wrapped Stream.
+ StreamSubscription<T> listen(void onData(T event),
+ { void onError(error),
+ void onDone(),
+ bool cancelOnError}) {
+ return _streamController.stream.listen(onData, onError: onError,
+ onDone: onDone, cancelOnError: cancelOnError);
+ }
+
+ Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription),
+ void onCancel(StreamSubscription subscription)})
+ => _streamController.stream;
+
+ bool get isBroadcast => true;
+
+ void add(T event) {
+ if (event.type == _type) _streamController.add(event);
+ }
+}
+
+class _CustomKeyEventStreamImpl extends _CustomEventStreamImpl<KeyEvent>
+ implements CustomStream<KeyEvent> {
+ _CustomKeyEventStreamImpl(String type) : super(type);
+
+ void add(KeyEvent event) {
+ if (event.type == _type) {
+ event.currentTarget.dispatchEvent(event._parent);
+ _streamController.add(event);
+ }
+ }
+}
+
+/**
* A pool of streams whose events are unified and emitted through a central
* stream.
*/
@@ -30327,6 +30393,61 @@ abstract class KeyCode {
keyCode == OPEN_SQUARE_BRACKET || keyCode == BACKSLASH ||
keyCode == CLOSE_SQUARE_BRACKET);
}
+
+ /**
+ * Experimental helper function for converting keyCodes to keyNames for the
+ * keyIdentifier attribute still used in browsers not updated with current
+ * spec. This is an imperfect conversion! It will need to be refined, but
+ * hopefully it can just completely go away once all the browsers update to
+ * follow the DOM3 spec.
+ */
+ static String _convertKeyCodeToKeyName(int keyCode) {
+ switch(keyCode) {
+ case KeyCode.ALT: return _KeyName.ALT;
+ case KeyCode.BACKSPACE: return _KeyName.BACKSPACE;
+ case KeyCode.CAPS_LOCK: return _KeyName.CAPS_LOCK;
+ case KeyCode.CTRL: return _KeyName.CONTROL;
+ case KeyCode.DELETE: return _KeyName.DEL;
+ case KeyCode.DOWN: return _KeyName.DOWN;
+ case KeyCode.END: return _KeyName.END;
+ case KeyCode.ENTER: return _KeyName.ENTER;
+ case KeyCode.ESC: return _KeyName.ESC;
+ case KeyCode.F1: return _KeyName.F1;
+ case KeyCode.F2: return _KeyName.F2;
+ case KeyCode.F3: return _KeyName.F3;
+ case KeyCode.F4: return _KeyName.F4;
+ case KeyCode.F5: return _KeyName.F5;
+ case KeyCode.F6: return _KeyName.F6;
+ case KeyCode.F7: return _KeyName.F7;
+ case KeyCode.F8: return _KeyName.F8;
+ case KeyCode.F9: return _KeyName.F9;
+ case KeyCode.F10: return _KeyName.F10;
+ case KeyCode.F11: return _KeyName.F11;
+ case KeyCode.F12: return _KeyName.F12;
+ case KeyCode.HOME: return _KeyName.HOME;
+ case KeyCode.INSERT: return _KeyName.INSERT;
+ case KeyCode.LEFT: return _KeyName.LEFT;
+ case KeyCode.META: return _KeyName.META;
+ case KeyCode.NUMLOCK: return _KeyName.NUM_LOCK;
+ case KeyCode.PAGE_DOWN: return _KeyName.PAGE_DOWN;
+ case KeyCode.PAGE_UP: return _KeyName.PAGE_UP;
+ case KeyCode.PAUSE: return _KeyName.PAUSE;
+ case KeyCode.PRINT_SCREEN: return _KeyName.PRINT_SCREEN;
+ case KeyCode.RIGHT: return _KeyName.RIGHT;
+ case KeyCode.SCROLL_LOCK: return _KeyName.SCROLL;
+ case KeyCode.SHIFT: return _KeyName.SHIFT;
+ case KeyCode.SPACE: return _KeyName.SPACEBAR;
+ case KeyCode.TAB: return _KeyName.TAB;
+ case KeyCode.UP: return _KeyName.UP;
+ case KeyCode.WIN_IME:
+ case KeyCode.WIN_KEY:
+ case KeyCode.WIN_KEY_LEFT:
+ case KeyCode.WIN_KEY_RIGHT:
+ return _KeyName.WIN;
+ default: return _KeyName.UNIDENTIFIED;
+ }
+ return _KeyName.UNIDENTIFIED;
+ }
}
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -30381,10 +30502,10 @@ abstract class KeyLocation {
/**
* Defines the standard keyboard identifier names for keys that are returned
- * by KeyEvent.getKeyboardIdentifier when the key does not have a direct
+ * by KeyboardEvent.getKeyboardIdentifier when the key does not have a direct
* unicode mapping.
*/
-abstract class KeyName {
+abstract class _KeyName {
/** The Accept (Commit, OK) key */
static const String ACCEPT = "Accept";
@@ -30896,8 +31017,8 @@ class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> {
// The distance to shift from upper case alphabet Roman letters to lower case.
static final int _ROMAN_ALPHABET_OFFSET = "a".codeUnits[0] - "A".codeUnits[0];
- /** Controller to produce KeyEvents for the stream. */
- final StreamController _controller = new StreamController(sync: true);
+ /** Custom Stream (Controller) to produce KeyEvents for the stream. */
+ _CustomKeyEventStreamImpl _stream;
static const _EVENT_TYPE = 'KeyEvent';
@@ -30933,56 +31054,33 @@ class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> {
};
/** Return a stream for KeyEvents for the specified target. */
- Stream<KeyEvent> forTarget(EventTarget e, {bool useCapture: false}) {
- return new _KeyboardEventHandler.initializeAllEventListeners(
- _type, e).stream;
- }
-
- /**
- * Accessor to the stream associated with a particular KeyboardEvent
- * EventTarget.
- *
- * [forTarget] must be called to initialize this stream to listen to a
- * particular EventTarget.
- */
- Stream<KeyEvent> get stream {
- if(_target != null) {
- return _controller.stream;
- } else {
- throw new StateError("Not initialized. Call forTarget to access a stream "
- "initialized with a particular EventTarget.");
- }
+ // Note: this actually functions like a factory constructor.
+ CustomStream<KeyEvent> forTarget(EventTarget e, {bool useCapture: false}) {
+ var handler = new _KeyboardEventHandler.initializeAllEventListeners(
+ _type, e);
+ return handler._stream;
}
/**
* General constructor, performs basic initialization for our improved
* KeyboardEvent controller.
*/
- _KeyboardEventHandler(this._type) :
- _target = null, super(_EVENT_TYPE) {
- }
+ _KeyboardEventHandler(this._type): super(_EVENT_TYPE),
+ _stream = new _CustomKeyEventStreamImpl('event');
/**
* Hook up all event listeners under the covers so we can estimate keycodes
* and charcodes when they are not provided.
*/
_KeyboardEventHandler.initializeAllEventListeners(this._type, this._target) :
- super(_EVENT_TYPE) {
+ super(_EVENT_TYPE) {
Element.keyDownEvent.forTarget(_target, useCapture: true).listen(
processKeyDown);
Element.keyPressEvent.forTarget(_target, useCapture: true).listen(
processKeyPress);
Element.keyUpEvent.forTarget(_target, useCapture: true).listen(
processKeyUp);
- }
-
- /**
- * Notify all callback listeners that a KeyEvent of the relevant type has
- * occurred.
- */
- bool _dispatch(KeyEvent event) {
- if (event.type == _type)
- _controller.add(event);
+ _stream = new _CustomKeyEventStreamImpl(_type);
}
/** Determine if caps lock is one of the currently depressed keys. */
@@ -31173,7 +31271,7 @@ class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> {
_keyDownList.clear();
}
- var event = new KeyEvent(e);
+ var event = new KeyEvent.wrap(e);
event._shadowKeyCode = _normalizeKeyCodes(event);
// Technically a "keydown" event doesn't have a charCode. This is
// calculated nonetheless to provide us with more information in giving
@@ -31187,12 +31285,12 @@ class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> {
processKeyPress(e);
}
_keyDownList.add(event);
- _dispatch(event);
+ _stream.add(event);
}
/** Handle keypress events. */
void processKeyPress(KeyboardEvent event) {
- var e = new KeyEvent(event);
+ var e = new KeyEvent.wrap(event);
// IE reports the character code in the keyCode field for keypress events.
// There are two exceptions however, Enter and Escape.
if (Device.isIE) {
@@ -31217,12 +31315,12 @@ class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> {
e._shadowKeyCode = _keyIdentifier[e._shadowKeyIdentifier];
}
e._shadowAltKey = _keyDownList.any((var element) => element.altKey);
- _dispatch(e);
+ _stream.add(e);
}
/** Handle keyup events. */
void processKeyUp(KeyboardEvent event) {
- var e = new KeyEvent(event);
+ var e = new KeyEvent.wrap(event);
KeyboardEvent toRemove = null;
for (var key in _keyDownList) {
if (key.keyCode == e.keyCode) {
@@ -31237,7 +31335,7 @@ class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> {
// inconsistencies. Filing bugs on when this is reached is welcome!
_keyDownList.removeLast();
}
- _dispatch(e);
+ _stream.add(e);
}
}
@@ -31260,16 +31358,16 @@ class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> {
class KeyboardEventStream {
/** Named constructor to produce a stream for onKeyPress events. */
- static Stream<KeyEvent> onKeyPress(EventTarget target) =>
+ static CustomStream<KeyEvent> onKeyPress(EventTarget target) =>
new _KeyboardEventHandler('keypress').forTarget(target);
/** Named constructor to produce a stream for onKeyUp events. */
- static Stream<KeyEvent> onKeyUp(EventTarget target) =>
+ static CustomStream<KeyEvent> onKeyUp(EventTarget target) =>
new _KeyboardEventHandler('keyup').forTarget(target);
/** Named constructor to produce a stream for onKeyDown events. */
- static Stream<KeyEvent> onKeyDown(EventTarget target) =>
- new _KeyboardEventHandler('keydown').forTarget(target);
+ static CustomStream<KeyEvent> onKeyDown(EventTarget target) =>
+ new _KeyboardEventHandler('keydown').forTarget(target);
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -32608,6 +32706,7 @@ class _HistoryCrossFrame implements HistoryBase {
* on how we can make this class work with as many international keyboards as
* possible. Bugs welcome!
*/
+@Experimental()
class KeyEvent extends _WrappedEvent implements KeyboardEvent {
/** The parent KeyboardEvent that this KeyEvent is wrapping and "fixing". */
KeyboardEvent _parent;
@@ -32642,25 +32741,134 @@ class KeyEvent extends _WrappedEvent implements KeyboardEvent {
/** Accessor to the underlying altKey value is the parent event. */
bool get _realAltKey => JS('bool', '#.altKey', _parent);
+ /** Shadows on top of the parent's currentTarget. */
+ EventTarget _currentTarget;
+
+ /**
+ * The value we want to use for this object's dispatch. Created here so it is
+ * only invoked once.
+ */
+ static final _keyboardEventDispatchRecord = _makeRecord();
+
+ /** Helper to statically create the dispatch record. */
+ _makeRecord() {
+ // TODO(efortuna): Use better way to get interceptor and remove _private()
+ // constructors; Issue 13873.
+ var interceptor = new KeyboardEvent._private();
+ return makeLeafDispatchRecord(interceptor);
+ }
+
/** Construct a KeyEvent with [parent] as the event we're emulating. */
- KeyEvent(KeyboardEvent parent): super(parent) {
+ KeyEvent.wrap(KeyboardEvent parent): super(parent) {
_parent = parent;
_shadowAltKey = _realAltKey;
_shadowCharCode = _realCharCode;
_shadowKeyCode = _realKeyCode;
+ _currentTarget = _parent.currentTarget;
+ }
+
+ /** Programmatically create a new KeyEvent (and KeyboardEvent). */
+ factory KeyEvent(String type,
+ {Window view, bool canBubble: true, bool cancelable: true, int keyCode: 0,
+ int charCode: 0, int keyLocation: 1, bool ctrlKey: false,
+ bool altKey: false, bool shiftKey: false, bool metaKey: false,
+ bool altGraphKey: false, EventTarget currentTarget}) {
+ if (view == null) {
+ view = window;
+ }
+
+ var eventObj;
+ // In these two branches we create an underlying native KeyboardEvent, but
+ // we set it with our specified values. Because we are doing custom setting
+ // of certain values (charCode/keyCode, etc) only in this class (as opposed
+ // to KeyboardEvent) and the way we set these custom values depends on the
+ // type of underlying JS object, we do all the contruction for the
+ // underlying KeyboardEvent here.
+ if (canUseDispatchEvent) {
+ // Currently works in everything but Internet Explorer.
+ eventObj = new Event.eventType('Event', type,
+ canBubble: canBubble, cancelable: cancelable);
+
+ JS('void', '#.keyCode = #', eventObj, keyCode);
+ JS('void', '#.which = #', eventObj, keyCode);
+ JS('void', '#.charCode = #', eventObj, charCode);
+
+ JS('void', '#.keyLocation = #', eventObj, keyLocation);
+ JS('void', '#.ctrlKey = #', eventObj, ctrlKey);
+ JS('void', '#.altKey = #', eventObj, altKey);
+ JS('void', '#.shiftKey = #', eventObj, shiftKey);
+ JS('void', '#.metaKey = #', eventObj, metaKey);
+ JS('void', '#.altGraphKey = #', eventObj, altGraphKey);
+ } else {
+ // Currently this works on everything but Safari. Safari throws an
+ // "Attempting to change access mechanism for an unconfigurable property"
+ // TypeError when trying to do the Object.defineProperty hack, so we avoid
+ // this branch if possible.
+ // Also, if we want this branch to work in FF, we also need to modify
+ // _initKeyboardEvent to also take charCode and keyCode values to
+ // initialize initKeyEvent.
+
+ eventObj = new Event.eventType('KeyboardEvent', type,
+ canBubble: canBubble, cancelable: cancelable);
+
+ // Chromium Hack
+ JS('void', "Object.defineProperty(#, 'keyCode', {"
+ " get : function() { return this.keyCodeVal; } })", eventObj);
+ JS('void', "Object.defineProperty(#, 'which', {"
+ " get : function() { return this.keyCodeVal; } })", eventObj);
+ JS('void', "Object.defineProperty(#, 'charCode', {"
+ " get : function() { return this.charCodeVal; } })", eventObj);
+
+ var keyIdentifier = _convertToHexString(charCode, keyCode);
+ eventObj._initKeyboardEvent(type, canBubble, cancelable, view,
+ keyIdentifier, keyLocation, ctrlKey, altKey, shiftKey, metaKey,
+ altGraphKey);
+ JS('void', '#.keyCodeVal = #', eventObj, keyCode);
+ JS('void', '#.charCodeVal = #', eventObj, charCode);
+ }
+ // Tell dart2js that it smells like a KeyboardEvent!
+ setDispatchProperty(eventObj, _keyboardEventDispatchRecord);
+
+ var keyEvent = new KeyEvent.wrap(eventObj);
+ if (keyEvent._currentTarget == null) {
+ keyEvent._currentTarget = currentTarget == null ? window : currentTarget;
+ }
+ return keyEvent;
+ }
+
+ // Currently known to work on all browsers but IE.
+ static bool get canUseDispatchEvent =>
+ JS('bool',
+ '(typeof document.body.dispatchEvent == "function")'
+ '&& document.body.dispatchEvent.length > 0');
+
+ /** The currently registered target for this event. */
+ EventTarget get currentTarget => _currentTarget;
+
+ // This is an experimental method to be sure.
+ static String _convertToHexString(int charCode, int keyCode) {
+ if (charCode != -1) {
+ var hex = charCode.toRadixString(16); // Convert to hexadecimal.
+ StringBuffer sb = new StringBuffer('U+');
+ for (int i = 0; i < 4 - hex.length; i++) sb.write('0');
+ sb.write(hex);
+ return sb.toString();
+ } else {
+ return KeyCode._convertKeyCodeToKeyName(keyCode);
+ }
}
// TODO(efortuna): If KeyEvent is sufficiently successful that we want to make
// it the default keyboard event handling, move these methods over to Element.
/** Accessor to provide a stream of KeyEvents on the desired target. */
static EventStreamProvider<KeyEvent> keyDownEvent =
- new _KeyboardEventHandler('keydown');
+ new _KeyboardEventHandler('keydown');
/** Accessor to provide a stream of KeyEvents on the desired target. */
static EventStreamProvider<KeyEvent> keyUpEvent =
- new _KeyboardEventHandler('keyup');
+ new _KeyboardEventHandler('keyup');
/** Accessor to provide a stream of KeyEvents on the desired target. */
static EventStreamProvider<KeyEvent> keyPressEvent =
- new _KeyboardEventHandler('keypress');
+ new _KeyboardEventHandler('keypress');
/** True if the altGraphKey is pressed during this event. */
bool get altGraphKey => _parent.altGraphKey;
« no previous file with comments | « no previous file | sdk/lib/html/dartium/html_dartium.dart » ('j') | tools/dom/src/dart2js_KeyEvent.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698