Chromium Code Reviews| 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); |
| } |