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

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, 5 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
Index: tools/dom/src/EventStreamProvider.dart
diff --git a/tools/dom/src/EventStreamProvider.dart b/tools/dom/src/EventStreamProvider.dart
index 8e3b0bce8f20cfc6a9ab1d7fb1deb4839cee82b3..4ef16e9611b1639ee1be9c16ab6227d8f82a9d67 100644
--- a/tools/dom/src/EventStreamProvider.dart
+++ b/tools/dom/src/EventStreamProvider.dart
@@ -30,6 +30,106 @@ 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> filter(String selector) =>
blois 2013/08/02 20:13:02 Filter seems a bit generic for Streams (I could ea
Emily Fortuna 2013/08/07 23:38:25 Done.
+ this.where((event) => event.target.matches(selector));
blois 2013/08/02 20:21:25 Also, see the discussion in the bug about the ance
Emily Fortuna 2013/08/07 23:38:25 Done.
+}
+
+/**
+ * Adapter for exposing DOM Element events as streams, while also allowing
+ * event delegation.
+ */
+class _ElementEventStreamImpl<T extends Event> extends _EventStream<T> with
blois 2013/08/02 20:13:02 Rather than the mixin, how about just declare filt
Emily Fortuna 2013/08/07 23:38:25 True, but then we have to write the identical code
+ ElementStream<T> {
+ _ElementEventStreamImpl(target, eventType, useCapture) :
+ super(target, eventType, useCapture);
+}
+
+/**
+ * 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> with
Jennifer Messerly 2013/08/02 19:31:59 should this be "implements Stream"? it looks like
blois 2013/08/02 20:13:02 Can this be made more generic- a Stream which comb
Emily Fortuna 2013/08/07 23:38:25 Done.
Emily Fortuna 2013/08/07 23:38:25 Done.
+ ElementStream<T> {
+
+ StreamController _controller;
+
+ _ElementListEventStreamImpl(targetList, eventType, useCapture) {
+ _controller = new StreamController(sync: true);
Jennifer Messerly 2013/08/02 19:31:59 should this be a broadcast stream?
Emily Fortuna 2013/08/07 23:38:25 yes, thank you. done.
+ for (Element target in targetList) {
+ var stream = new _EventStream(target, eventType, useCapture);
Jennifer Messerly 2013/08/02 19:31:59 hmm, it looks like these listeners are never relea
blois 2013/08/02 20:13:02 See https://codereview.chromium.org/12220003/diff/
Emily Fortuna 2013/08/07 23:38:25 Done.
+ stream.listen((event) {
+ _controller.add(event);
+ return event;
+ });
+ }
+ }
+
+ // Delegate all regular Stream behavor to our wrapped Stream.
+ StreamSubscription<T> listen(void onData(T event),
+ { void onError(error),
+ void onDone(),
+ bool cancelOnError}) =>
+ _controller.stream.listen(onData, onError: onError, onDone: onDone,
Jennifer Messerly 2013/08/02 19:31:59 it is probably worth caching "_controller.stream".
Emily Fortuna 2013/08/07 23:38:25 Done.
+ cancelOnError: cancelOnError);
+ Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription),
+ void onCancel(StreamSubscription subscription)})
+ => _controller.stream;
+ bool get isBroadcast => true;
+ Stream<T> where(bool test(T event)) => _controller.stream.where(test);
+ Stream map(convert(T event)) => _controller.stream.map(convert);
blois 2013/08/02 20:13:02 Is it necessary to override all of these? Seems li
Emily Fortuna 2013/08/07 23:38:25 Since it's not actually a Stream and just implemen
+ Stream<T> handleError(void handle( error), { bool test(error) }) =>
+ _controller.stream.handleError(handle, test: test);
+ Stream expand(Iterable convert(T value)) =>
+ _controller.stream.expand(convert);
+ Future pipe(StreamConsumer<T> streamConsumer) =>
+ _controller.stream.pipe(streamConsumer);
+ Stream transform(StreamTransformer<T, dynamic> streamTransformer) =>
+ _controller.stream.transform(streamTransformer);
+ Future<T> reduce(T combine(T previous, T element)) =>
+ _controller.stream.reduce(combine);
+ Future fold(var initialValue, combine(var previous, T element)) =>
+ _controller.stream.fold(initialValue, combine);
+ Future<String> join([String separator = ""]) =>
+ _controller.stream.join(separator);
+ Future<bool> contains(Object needle) => _controller.stream.contains(needle);
+ Future forEach(void action(T element)) => _controller.stream.forEach(action);
+ Future<bool> every(bool test(T element)) => _controller.stream.every(test);
+ Future<bool> any(bool test(T element)) => _controller.stream.any(test);
+ Future<int> get length => _controller.stream.length;
+ Future<bool> get isEmpty => _controller.stream.isEmpty;
+ Future<List<T>> toList() => _controller.stream.toList();
+ Future<Set<T>> toSet() => _controller.stream.toSet();
+ Future drain([var futureValue]) => _controller.stream.drain(futureValue);
+ Stream<T> take(int count) => _controller.stream.take(count);
+ Stream<T> takeWhile(bool test(T element)) =>
+ _controller.stream.takeWhile(test);
+ Stream<T> skip(int count) => _controller.stream.skip(count);
+ Stream<T> skipWhile(bool test(T element)) =>
+ _controller.stream.skipWhile(test);
+ Stream<T> distinct([bool equals(T previous, T next)]) =>
+ _controller.stream.distinct(equals);
+ Future<T> get first => _controller.stream.first;
+ Future<T> get last => _controller.stream.last;
+ Future<T> get single => _controller.stream.single;
+ Future<dynamic> firstWhere(bool test(T element), {Object defaultValue()}) =>
+ _controller.stream.firstWhere(test, defaultValue: defaultValue);
+ Future<dynamic> lastWhere(bool test(T element), {Object defaultValue()}) =>
+ _controller.stream.lastWhere(test, defaultValue: defaultValue);
+ Future<T> singleWhere(bool test(T element)) =>
+ _controller.stream.singleWhere(test);
+ Future<T> elementAt(int index) => _controller.stream.elementAt(index);
+}
+
class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
int _pauseCount = 0;
EventTarget _target;
@@ -107,7 +207,6 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
}
}
-
/**
* A factory to expose DOM events as Streams.
*/
@@ -135,7 +234,48 @@ class EventStreamProvider<T extends Event> {
* [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventListener)
*/
Stream<T> forTarget(EventTarget e, {bool useCapture: false}) {
blois 2013/08/02 20:13:02 Since the signature is still returning Stream<T>,
Emily Fortuna 2013/08/07 23:38:25 But we should expect Element Event streams to alwa
- return new _EventStream(e, _eventType, useCapture);
+ if (e is Element) {
+ return new _ElementEventStreamImpl(e, _eventType, useCapture);
+ } else {
+ return 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.forElementTarget(element, useCapture: true).listen(...);
+ *
+ * See also:
+ *
+ * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventListener)
+ */
+ ElementStream<T> forElementTarget(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._forElementTargetList(element, useCapture: true).listen(...);
+ *
+ * See also:
+ *
+ * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventListener)
+ */
+ ElementStream<T> _forElementTargetList(ElementList e,
+ {bool useCapture: false}) {
+ return new _ElementListEventStreamImpl(e, _eventType, useCapture);
}
/**
@@ -164,6 +304,15 @@ class _CustomEventStreamProvider<T extends Event>
return new _EventStream(e, _eventTypeGetter(e), useCapture);
}
+ ElementStream<T> forElementTarget(Element e, {bool useCapture: false}) {
+ return new _ElementEventStreamImpl(e, _eventTypeGetter(e), useCapture);
+ }
+
+ ElementStream<T> _forElementTargetList(ElementList e,
+ {bool useCapture: false}) {
+ return new _ElementListEventStreamImpl(e, _eventTypeGetter(e), useCapture);
+ }
+
String getEventType(EventTarget target) {
return _eventTypeGetter(target);
}

Powered by Google App Engine
This is Rietveld 408576698