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

Unified Diff: tools/dom/src/EventStreamProvider.dart

Issue 21607003: Add Event delegation to Elements and groups of elements. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/dom/scripts/systemhtml.py ('k') | tools/dom/templates/html/impl/impl_Element.darttemplate » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/dom/src/EventStreamProvider.dart
diff --git a/tools/dom/src/EventStreamProvider.dart b/tools/dom/src/EventStreamProvider.dart
index 8e3b0bce8f20cfc6a9ab1d7fb1deb4839cee82b3..a4ad4c0ff32f57134dd44ca4db26fb86b00a614c 100644
--- a/tools/dom/src/EventStreamProvider.dart
+++ b/tools/dom/src/EventStreamProvider.dart
@@ -30,6 +30,123 @@ class _EventStream<T extends Event> extends Stream<T> {
}
}
+/** A specialized Stream available to [Element]s to enable event delegation. */
+abstract class ElementStream<T extends Event> implements Stream<T> {
+ /**
+ * Return a stream that only fires when the particular event fires for
+ * elements matching the specified CSS selector.
+ *
+ * This is the Dart equivalent to jQuery's
+ * [delegate](http://api.jquery.com/delegate/).
+ */
+ Stream<T> matches(String selector);
+}
+
+/**
+ * Adapter for exposing DOM Element events as streams, while also allowing
+ * event delegation.
+ */
+class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
+ implements ElementStream<T> {
+ _ElementEventStreamImpl(target, eventType, useCapture) :
+ super(target, eventType, useCapture);
+
+ Stream<T> matches(String selector) =>
+ this.where((event) => event.target.matches(selector, true));
+}
+
+/**
+ * Adapter for exposing events on a collection of DOM Elements as streams,
+ * while also allowing event delegation.
+ */
+class _ElementListEventStreamImpl<T extends Event> extends Stream<T>
+ implements ElementStream<T> {
+ final _StreamPool _pool;
+ Stream<T> _stream;
+
+ _ElementListEventStreamImpl(targetList, eventType, useCapture) :
+ _pool = new _StreamPool.broadcast() {
+ for (Element target in targetList) {
+ var stream = new _EventStream(target, eventType, useCapture);
+ _pool.add(stream);
+ }
+ _stream = _pool.stream;
+ }
+
+ Stream<T> matches(String selector) =>
+ this.where((event) => event.target.matches(selector, true));
+
+ // Delegate all regular Stream behavor to our wrapped Stream.
+ StreamSubscription<T> listen(void onData(T event),
+ { void onError(error),
+ void onDone(),
+ bool cancelOnError}) =>
+ _stream.listen(onData, onError: onError, onDone: onDone,
+ cancelOnError: cancelOnError);
+ Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription),
+ void onCancel(StreamSubscription subscription)})
+ => _stream;
+ bool get isBroadcast => true;
+}
+
+/**
+ * A pool of streams whose events are unified and emitted through a central
+ * stream.
+ */
+// TODO (efortuna): Remove this when Issue 12218 is addressed.
+class _StreamPool<T> {
+ StreamController<T> _controller;
+
+ /// Subscriptions to the streams that make up the pool.
+ var _subscriptions = new Map<Stream<T>, StreamSubscription<T>>();
+
+ /**
+ * Creates a new stream pool where [stream] can be listened to more than
+ * once.
+ *
+ * Any events from buffered streams in the pool will be emitted immediately,
+ * regardless of whether [stream] has any subscribers.
+ */
+ _StreamPool.broadcast() {
+ _controller = new StreamController<T>.broadcast(sync: true,
+ onCancel: close);
+ }
+
+ /**
+ * The stream through which all events from streams in the pool are emitted.
+ */
+ Stream<T> get stream => _controller.stream;
+
+ /**
+ * Adds [stream] as a member of this pool.
+ *
+ * Any events from [stream] will be emitted through [this.stream]. If
+ * [stream] is sync, they'll be emitted synchronously; if [stream] is async,
+ * they'll be emitted asynchronously.
+ */
+ void add(Stream<T> stream) {
+ if (_subscriptions.containsKey(stream)) return;
+ _subscriptions[stream] = stream.listen(_controller.add,
+ onError: _controller.addError,
+ onDone: () => remove(stream));
+ }
+
+ /** Removes [stream] as a member of this pool. */
+ void remove(Stream<T> stream) {
+ var subscription = _subscriptions.remove(stream);
+ if (subscription != null) subscription.cancel();
+ }
+
+ /** Removes all streams from this pool and closes [stream]. */
+ void close() {
+ for (var subscription in _subscriptions.values) {
+ subscription.cancel();
+ }
+ _subscriptions.clear();
+ _controller.close();
+ }
+}
+
class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
int _pauseCount = 0;
EventTarget _target;
@@ -107,7 +224,6 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
}
}
-
/**
* A factory to expose DOM events as Streams.
*/
@@ -134,8 +250,43 @@ class EventStreamProvider<T extends Event> {
*
* [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventListener)
*/
- Stream<T> forTarget(EventTarget e, {bool useCapture: false}) {
- return new _EventStream(e, _eventType, useCapture);
+ Stream<T> forTarget(EventTarget e, {bool useCapture: false}) =>
+ new _EventStream(e, _eventType, useCapture);
+
+ /**
+ * Gets an [ElementEventStream] for this event type, on the specified element.
+ *
+ * This will always return a broadcast stream so multiple listeners can be
+ * used simultaneously.
+ *
+ * This may be used to capture DOM events:
+ *
+ * Element.keyDownEvent.forElement(element, useCapture: true).listen(...);
+ *
+ * See also:
+ *
+ * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventListener)
+ */
+ ElementStream<T> forElement(Element e, {bool useCapture: false}) {
+ return new _ElementEventStreamImpl(e, _eventType, useCapture);
+ }
+
+ /**
+ * Gets an [ElementEventStream] for this event type, on the list of elements.
+ *
+ * This will always return a broadcast stream so multiple listeners can be
+ * used simultaneously.
+ *
+ * This may be used to capture DOM events:
+ *
+ * Element.keyDownEvent._forElementList(element, useCapture: true).listen(...);
+ *
+ * See also:
+ *
+ * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventListener)
+ */
+ ElementStream<T> _forElementList(ElementList e, {bool useCapture: false}) {
+ return new _ElementListEventStreamImpl(e, _eventType, useCapture);
}
/**
@@ -164,6 +315,15 @@ class _CustomEventStreamProvider<T extends Event>
return new _EventStream(e, _eventTypeGetter(e), useCapture);
}
+ ElementStream<T> forElement(Element e, {bool useCapture: false}) {
+ return new _ElementEventStreamImpl(e, _eventTypeGetter(e), useCapture);
+ }
+
+ ElementStream<T> _forElementList(ElementList e,
+ {bool useCapture: false}) {
+ return new _ElementListEventStreamImpl(e, _eventTypeGetter(e), useCapture);
+ }
+
String getEventType(EventTarget target) {
return _eventTypeGetter(target);
}
« no previous file with comments | « tools/dom/scripts/systemhtml.py ('k') | tools/dom/templates/html/impl/impl_Element.darttemplate » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698