| 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);
|
| }
|
|
|