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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of html; 5 part of html;
6 6
7 /** 7 /**
8 * A pool of streams whose events are unified and emitted through a central
9 * stream.
10 */
11 // TODO (efortuna): Remove this when Issue 12218 is addressed.
12 class StreamPool<T> {
blois 2013/08/08 00:12:11 Should not be public, can we move it down in the f
Emily Fortuna 2013/08/09 00:22:06 Done.
13 StreamController<T> _controller;
blois 2013/08/08 00:12:11 final?
Emily Fortuna 2013/08/09 00:22:06 can't be because _controller has the onCancel meth
14
15 /// Subscriptions to the streams that make up the pool.
16 var _subscriptions = new Map<Stream<T>, StreamSubscription<T>>();
17
18 /**
19 * Creates a new stream pool that only supports a single subscriber.
20 *
21 * Any events from broadcast streams in the pool will be buffered until a
22 * listener is subscribed.
23 */
24 StreamPool() {
25 _controller = new StreamController<T>(sync: true, onCancel: close);
26 }
27
28 /**
29 * Creates a new stream pool where [stream] can be listened to more than
30 * once.
31 *
32 * Any events from buffered streams in the pool will be emitted immediately,
33 * regardless of whether [stream] has any subscribers.
34 */
35 StreamPool.broadcast() {
36 _controller = new StreamController<T>.broadcast(sync: true,
37 onCancel: close);
38 }
39
40 /**
41 * The stream through which all events from streams in the pool are emitted.
42 */
43 Stream<T> get stream => _controller.stream;
44
45 /**
46 * Adds [stream] as a member of this pool.
47 *
48 * Any events from [stream] will be emitted through [this.stream]. If
49 * [stream] is sync, they'll be emitted synchronously; if [stream] is async,
50 * they'll be emitted asynchronously.
51 */
52 void add(Stream<T> stream) {
53 if (_subscriptions.containsKey(stream)) return;
54 _subscriptions[stream] = stream.listen(_controller.add,
55 onError: _controller.addError,
56 onDone: () => remove(stream));
57 }
58
59 /** Removes [stream] as a member of this pool. */
60 void remove(Stream<T> stream) {
61 var subscription = _subscriptions.remove(stream);
62 if (subscription != null) subscription.cancel();
63 }
64
65 /** Removes all streams from this pool and closes [stream]. */
66 void close() {
67 for (var subscription in _subscriptions.values) {
68 subscription.cancel();
69 }
70 _subscriptions.clear();
71 _controller.close();
72 }
73 }
74
75
76 /**
8 * Adapter for exposing DOM events as Dart streams. 77 * Adapter for exposing DOM events as Dart streams.
9 */ 78 */
10 class _EventStream<T extends Event> extends Stream<T> { 79 class _EventStream<T extends Event> extends Stream<T> {
11 final EventTarget _target; 80 final EventTarget _target;
12 final String _eventType; 81 final String _eventType;
13 final bool _useCapture; 82 final bool _useCapture;
14 83
15 _EventStream(this._target, this._eventType, this._useCapture); 84 _EventStream(this._target, this._eventType, this._useCapture);
16 85
17 // DOM events are inherently multi-subscribers. 86 // DOM events are inherently multi-subscribers.
18 Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription), 87 Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription),
19 void onCancel(StreamSubscription subscription)}) 88 void onCancel(StreamSubscription subscription)})
20 => this; 89 => this;
21 bool get isBroadcast => true; 90 bool get isBroadcast => true;
22 91
23 StreamSubscription<T> listen(void onData(T event), 92 StreamSubscription<T> listen(void onData(T event),
24 { void onError(error), 93 { void onError(error),
25 void onDone(), 94 void onDone(),
26 bool cancelOnError}) { 95 bool cancelOnError}) {
27 96
28 return new _EventStreamSubscription<T>( 97 return new _EventStreamSubscription<T>(
29 this._target, this._eventType, onData, this._useCapture); 98 this._target, this._eventType, onData, this._useCapture);
30 } 99 }
31 } 100 }
32 101
102 /** A specialized Stream available to [Element]s to enable event delegation. */
103 abstract class ElementStream<T extends Event> extends _EventStream<T> {
104 ElementStream(target, eventType, useCapture) :
105 super(target, eventType, useCapture);
106
107 /**
108 * Return a stream that only fires when the particular event fires for
109 * elements matching the specified CSS selector.
110 *
111 * This is the Dart equivalent to jQuery's
112 * [delegate](http://api.jquery.com/delegate/).
113 */
114 Stream<T> matches(String selector);
115 }
116
117 /**
118 * Adapter for exposing DOM Element events as streams, while also allowing
119 * event delegation.
120 */
121 class _ElementEventStreamImpl<T extends Event> extends ElementStream<T> {
122 _ElementEventStreamImpl(target, eventType, useCapture) :
123 super(target, eventType, useCapture);
124
125 Stream<T> matches(String selector) =>
126 this.where((event) => event.target.matches(selector, true));
127 }
128
129 /**
130 * Adapter for exposing events on a collection of DOM Elements as streams,
131 * while also allowing event delegation.
132 */
133 class _ElementListEventStreamImpl<T extends Event> implements ElementStream<T> {
blois 2013/08/08 00:12:11 I'm still unclear why this cannot extend Stream an
Emily Fortuna 2013/08/09 00:22:06 How would this extend Stream and not need to call
134 //implements Stream<T> {
blois 2013/08/08 00:12:11 Comment
Emily Fortuna 2013/08/09 00:22:06 Done.
135
136 StreamPool _pool;
blois 2013/08/08 00:12:11 final.
Emily Fortuna 2013/08/09 00:22:06 Done.
137 Stream<T> _stream;
blois 2013/08/08 00:12:11 Is it necessary to cache this?
Emily Fortuna 2013/08/09 00:22:06 It's helpful, because otherwise get stream in Stre
138
139 _ElementListEventStreamImpl(targetList, eventType, useCapture) {
140 _pool = new StreamPool.broadcast();
141 for (Element target in targetList) {
142 var stream = new _EventStream(target, eventType, useCapture);
143 _pool.add(stream);
144 }
145 _stream = _pool.stream;
146 }
147
148 Stream<T> matches(String selector) =>
149 this.where((event) => event.target.matches(selector, true));
150
151 // Delegate all regular Stream behavor to our wrapped Stream.
152 StreamSubscription<T> listen(void onData(T event),
153 { void onError(error),
154 void onDone(),
155 bool cancelOnError}) =>
156 _stream.listen(onData, onError: onError, onDone: onDone,
157 cancelOnError: cancelOnError);
158 Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription),
159 void onCancel(StreamSubscription subscription)})
160 => _stream;
161 bool get isBroadcast => true;
162 Stream<T> where(bool test(T event)) => _stream.where(test);
163 Stream map(convert(T event)) => _stream.map(convert);
164 Stream<T> handleError(void handle( error), { bool test(error) }) =>
165 _stream.handleError(handle, test: test);
166 Stream expand(Iterable convert(T value)) =>
167 _stream.expand(convert);
168 Future pipe(StreamConsumer<T> streamConsumer) =>
169 _stream.pipe(streamConsumer);
170 Stream transform(StreamTransformer<T, dynamic> streamTransformer) =>
171 _stream.transform(streamTransformer);
172 Future<T> reduce(T combine(T previous, T element)) =>
173 _stream.reduce(combine);
174 Future fold(var initialValue, combine(var previous, T element)) =>
175 _stream.fold(initialValue, combine);
176 Future<String> join([String separator = ""]) =>
177 _stream.join(separator);
178 Future<bool> contains(Object needle) => _stream.contains(needle);
179 Future forEach(void action(T element)) => _stream.forEach(action);
180 Future<bool> every(bool test(T element)) => _stream.every(test);
181 Future<bool> any(bool test(T element)) => _stream.any(test);
182 Future<int> get length => _stream.length;
183 Future<bool> get isEmpty => _stream.isEmpty;
184 Future<List<T>> toList() => _stream.toList();
185 Future<Set<T>> toSet() => _stream.toSet();
186 Future drain([var futureValue]) => _stream.drain(futureValue);
187 Stream<T> take(int count) => _stream.take(count);
188 Stream<T> takeWhile(bool test(T element)) =>
189 _stream.takeWhile(test);
190 Stream<T> skip(int count) => _stream.skip(count);
191 Stream<T> skipWhile(bool test(T element)) =>
192 _stream.skipWhile(test);
193 Stream<T> distinct([bool equals(T previous, T next)]) =>
194 _stream.distinct(equals);
195 Future<T> get first => _stream.first;
196 Future<T> get last => _stream.last;
197 Future<T> get single => _stream.single;
198 Future<dynamic> firstWhere(bool test(T element), {Object defaultValue()}) =>
199 _stream.firstWhere(test, defaultValue: defaultValue);
200 Future<dynamic> lastWhere(bool test(T element), {Object defaultValue()}) =>
201 _stream.lastWhere(test, defaultValue: defaultValue);
202 Future<T> singleWhere(bool test(T element)) =>
203 _stream.singleWhere(test);
204 Future<T> elementAt(int index) => _stream.elementAt(index);
205 }
206
33 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { 207 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
34 int _pauseCount = 0; 208 int _pauseCount = 0;
35 EventTarget _target; 209 EventTarget _target;
36 final String _eventType; 210 final String _eventType;
37 var _onData; 211 var _onData;
38 final bool _useCapture; 212 final bool _useCapture;
39 213
40 _EventStreamSubscription(this._target, this._eventType, this._onData, 214 _EventStreamSubscription(this._target, this._eventType, this._onData,
41 this._useCapture) { 215 this._useCapture) {
42 _tryResume(); 216 _tryResume();
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
100 } 274 }
101 } 275 }
102 276
103 Future asFuture([var futureValue]) { 277 Future asFuture([var futureValue]) {
104 // We just need a future that will never succeed or fail. 278 // We just need a future that will never succeed or fail.
105 Completer completer = new Completer(); 279 Completer completer = new Completer();
106 return completer.future; 280 return completer.future;
107 } 281 }
108 } 282 }
109 283
110
111 /** 284 /**
112 * A factory to expose DOM events as Streams. 285 * A factory to expose DOM events as Streams.
113 */ 286 */
114 class EventStreamProvider<T extends Event> { 287 class EventStreamProvider<T extends Event> {
115 final String _eventType; 288 final String _eventType;
116 289
117 const EventStreamProvider(this._eventType); 290 const EventStreamProvider(this._eventType);
118 291
119 /** 292 /**
120 * Gets a [Stream] for this event type, on the specified target. 293 * Gets a [Stream] for this event type, on the specified target.
121 * 294 *
122 * This will always return a broadcast stream so multiple listeners can be 295 * This will always return a broadcast stream so multiple listeners can be
123 * used simultaneously. 296 * used simultaneously.
124 * 297 *
125 * This may be used to capture DOM events: 298 * This may be used to capture DOM events:
126 * 299 *
127 * Element.keyDownEvent.forTarget(element, useCapture: true).listen(...); 300 * Element.keyDownEvent.forTarget(element, useCapture: true).listen(...);
128 * 301 *
129 * Or for listening to an event which will bubble through the DOM tree: 302 * Or for listening to an event which will bubble through the DOM tree:
130 * 303 *
131 * MediaElement.pauseEvent.forTarget(document.body).listen(...); 304 * MediaElement.pauseEvent.forTarget(document.body).listen(...);
132 * 305 *
133 * See also: 306 * See also:
134 * 307 *
135 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis tener) 308 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis tener)
136 */ 309 */
137 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { 310 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) {
138 return new _EventStream(e, _eventType, useCapture); 311 if (e is Element) {
312 return new _ElementEventStreamImpl(e, _eventType, useCapture);
blois 2013/08/08 00:12:11 Does this need to be here given that there's alrea
Emily Fortuna 2013/08/09 00:22:06 I want Elements to always have the ability to do e
313 } else {
314 return new _EventStream(e, _eventType, useCapture);
315 }
139 } 316 }
140 317
141 /** 318 /**
319 * Gets an [ElementEventStream] for this event type, on the specified element.
320 *
321 * This will always return a broadcast stream so multiple listeners can be
322 * used simultaneously.
323 *
324 * This may be used to capture DOM events:
325 *
326 * Element.keyDownEvent.forElementTarget(element, useCapture: true).listen (...);
327 *
328 * See also:
329 *
330 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis tener)
331 */
332 ElementStream<T> forElementTarget(Element e, {bool useCapture: false}) {
blois 2013/08/08 00:12:11 How about just 'forElement'?
333 return new _ElementEventStreamImpl(e, _eventType, useCapture);
334 }
335
336 /**
337 * Gets an [ElementEventStream] for this event type, on the list of elements.
338 *
339 * This will always return a broadcast stream so multiple listeners can be
340 * used simultaneously.
341 *
342 * This may be used to capture DOM events:
343 *
344 * Element.keyDownEvent._forElementTargetList(element, useCapture: true).l isten(...);
345 *
346 * See also:
347 *
348 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis tener)
349 */
350 ElementStream<T> _forElementTargetList(ElementList e,
351 {bool useCapture: false}) {
352 return new _ElementListEventStreamImpl(e, _eventType, useCapture);
353 }
354
355 /**
142 * Gets the type of the event which this would listen for on the specified 356 * Gets the type of the event which this would listen for on the specified
143 * event target. 357 * event target.
144 * 358 *
145 * The target is necessary because some browsers may use different event names 359 * The target is necessary because some browsers may use different event names
146 * for the same purpose and the target allows differentiating browser support. 360 * for the same purpose and the target allows differentiating browser support.
147 */ 361 */
148 String getEventType(EventTarget target) { 362 String getEventType(EventTarget target) {
149 return _eventType; 363 return _eventType;
150 } 364 }
151 } 365 }
152 366
153 /** 367 /**
154 * A factory to expose DOM events as streams, where the DOM event name has to 368 * A factory to expose DOM events as streams, where the DOM event name has to
155 * be determined on the fly (for example, mouse wheel events). 369 * be determined on the fly (for example, mouse wheel events).
156 */ 370 */
157 class _CustomEventStreamProvider<T extends Event> 371 class _CustomEventStreamProvider<T extends Event>
158 implements EventStreamProvider<T> { 372 implements EventStreamProvider<T> {
159 373
160 final _eventTypeGetter; 374 final _eventTypeGetter;
161 const _CustomEventStreamProvider(this._eventTypeGetter); 375 const _CustomEventStreamProvider(this._eventTypeGetter);
162 376
163 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { 377 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) {
164 return new _EventStream(e, _eventTypeGetter(e), useCapture); 378 return new _EventStream(e, _eventTypeGetter(e), useCapture);
165 } 379 }
166 380
381 ElementStream<T> forElementTarget(Element e, {bool useCapture: false}) {
382 return new _ElementEventStreamImpl(e, _eventTypeGetter(e), useCapture);
383 }
384
385 ElementStream<T> _forElementTargetList(ElementList e,
386 {bool useCapture: false}) {
387 return new _ElementListEventStreamImpl(e, _eventTypeGetter(e), useCapture);
388 }
389
167 String getEventType(EventTarget target) { 390 String getEventType(EventTarget target) {
168 return _eventTypeGetter(target); 391 return _eventTypeGetter(target);
169 } 392 }
170 } 393 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698