OLD | NEW |
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 factory to expose DOM events as Streams. | 8 * A factory to expose DOM events as Streams. |
9 */ | 9 */ |
10 class EventStreamProvider<T extends Event> { | 10 class EventStreamProvider<T extends Event> { |
(...skipping 16 matching lines...) Expand all Loading... |
27 * | 27 * |
28 * Or for listening to an event which will bubble through the DOM tree: | 28 * Or for listening to an event which will bubble through the DOM tree: |
29 * | 29 * |
30 * MediaElement.pauseEvent.forTarget(document.body).listen(...); | 30 * MediaElement.pauseEvent.forTarget(document.body).listen(...); |
31 * | 31 * |
32 * See also: | 32 * See also: |
33 * | 33 * |
34 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis
tener) | 34 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis
tener) |
35 */ | 35 */ |
36 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) => | 36 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) => |
37 new _EventStream<T>(e, _eventType, useCapture); | 37 new _EventStream<T>(e, _eventType, useCapture); |
38 | 38 |
39 /** | 39 /** |
40 * Gets an [ElementEventStream] for this event type, on the specified element. | 40 * Gets an [ElementEventStream] for this event type, on the specified element. |
41 * | 41 * |
42 * This will always return a broadcast stream so multiple listeners can be | 42 * This will always return a broadcast stream so multiple listeners can be |
43 * used simultaneously. | 43 * used simultaneously. |
44 * | 44 * |
45 * This may be used to capture DOM events: | 45 * This may be used to capture DOM events: |
46 * | 46 * |
47 * Element.keyDownEvent.forElement(element, useCapture: true).listen(...); | 47 * Element.keyDownEvent.forElement(element, useCapture: true).listen(...); |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 * Adapter for exposing DOM events as Dart streams. | 122 * Adapter for exposing DOM events as Dart streams. |
123 */ | 123 */ |
124 class _EventStream<T extends Event> extends Stream<T> { | 124 class _EventStream<T extends Event> extends Stream<T> { |
125 final EventTarget _target; | 125 final EventTarget _target; |
126 final String _eventType; | 126 final String _eventType; |
127 final bool _useCapture; | 127 final bool _useCapture; |
128 | 128 |
129 _EventStream(this._target, this._eventType, this._useCapture); | 129 _EventStream(this._target, this._eventType, this._useCapture); |
130 | 130 |
131 // DOM events are inherently multi-subscribers. | 131 // DOM events are inherently multi-subscribers. |
132 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription)
, | 132 Stream<T> asBroadcastStream( |
133 void onCancel(StreamSubscription<T> subscription)
}) | 133 {void onListen(StreamSubscription<T> subscription), |
134 => this; | 134 void onCancel(StreamSubscription<T> subscription)}) => |
| 135 this; |
135 bool get isBroadcast => true; | 136 bool get isBroadcast => true; |
136 | 137 |
137 StreamSubscription<T> listen(void onData(T event), | 138 StreamSubscription<T> listen(void onData(T event), |
138 { Function onError, | 139 {Function onError, void onDone(), bool cancelOnError}) { |
139 void onDone(), | |
140 bool cancelOnError}) { | |
141 | |
142 return new _EventStreamSubscription<T>( | 140 return new _EventStreamSubscription<T>( |
143 this._target, this._eventType, onData, this._useCapture); | 141 this._target, this._eventType, onData, this._useCapture); |
144 } | 142 } |
145 } | 143 } |
146 | 144 |
147 bool _matchesWithAncestors(Event event, String selector) { | 145 bool _matchesWithAncestors(Event event, String selector) { |
148 var target = event.target; | 146 var target = event.target; |
149 return target is Element ? target.matchesWithAncestors(selector) : false; | 147 return target is Element ? target.matchesWithAncestors(selector) : false; |
150 } | 148 } |
151 | 149 |
152 /** | 150 /** |
153 * Adapter for exposing DOM Element events as streams, while also allowing | 151 * Adapter for exposing DOM Element events as streams, while also allowing |
154 * event delegation. | 152 * event delegation. |
155 */ | 153 */ |
156 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T> | 154 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T> |
157 implements ElementStream<T> { | 155 implements ElementStream<T> { |
158 _ElementEventStreamImpl(target, eventType, useCapture) : | 156 _ElementEventStreamImpl(target, eventType, useCapture) |
159 super(target, eventType, useCapture); | 157 : super(target, eventType, useCapture); |
160 | 158 |
161 Stream<T> matches(String selector) => this.where( | 159 Stream<T> matches(String selector) => |
162 (event) => _matchesWithAncestors(event, selector)).map((e) { | 160 this.where((event) => _matchesWithAncestors(event, selector)).map((e) { |
163 e._selector = selector; | 161 e._selector = selector; |
164 return e; | 162 return e; |
165 }); | 163 }); |
166 | 164 |
167 StreamSubscription<T> capture(void onData(T event)) => | 165 StreamSubscription<T> capture(void onData(T event)) => |
168 new _EventStreamSubscription<T>( | 166 new _EventStreamSubscription<T>( |
169 this._target, this._eventType, onData, true); | 167 this._target, this._eventType, onData, true); |
170 } | 168 } |
171 | 169 |
172 /** | 170 /** |
173 * Adapter for exposing events on a collection of DOM Elements as streams, | 171 * Adapter for exposing events on a collection of DOM Elements as streams, |
174 * while also allowing event delegation. | 172 * while also allowing event delegation. |
175 */ | 173 */ |
176 class _ElementListEventStreamImpl<T extends Event> extends Stream<T> | 174 class _ElementListEventStreamImpl<T extends Event> extends Stream<T> |
177 implements ElementStream<T> { | 175 implements ElementStream<T> { |
178 final Iterable<Element> _targetList; | 176 final Iterable<Element> _targetList; |
179 final bool _useCapture; | 177 final bool _useCapture; |
180 final String _eventType; | 178 final String _eventType; |
181 | 179 |
182 _ElementListEventStreamImpl( | 180 _ElementListEventStreamImpl( |
183 this._targetList, this._eventType, this._useCapture); | 181 this._targetList, this._eventType, this._useCapture); |
184 | 182 |
185 Stream<T> matches(String selector) => this.where( | 183 Stream<T> matches(String selector) => |
186 (event) => _matchesWithAncestors(event, selector)).map((e) { | 184 this.where((event) => _matchesWithAncestors(event, selector)).map((e) { |
187 e._selector = selector; | 185 e._selector = selector; |
188 return e; | 186 return e; |
189 }); | 187 }); |
190 | 188 |
191 // Delegate all regular Stream behavior to a wrapped Stream. | 189 // Delegate all regular Stream behavior to a wrapped Stream. |
192 StreamSubscription<T> listen(void onData(T event), | 190 StreamSubscription<T> listen(void onData(T event), |
193 { Function onError, | 191 {Function onError, void onDone(), bool cancelOnError}) { |
194 void onDone(), | |
195 bool cancelOnError}) { | |
196 var pool = new _StreamPool<T>.broadcast(); | 192 var pool = new _StreamPool<T>.broadcast(); |
197 for (var target in _targetList) { | 193 for (var target in _targetList) { |
198 pool.add(new _EventStream<T>(target, _eventType, _useCapture)); | 194 pool.add(new _EventStream<T>(target, _eventType, _useCapture)); |
199 } | 195 } |
200 return pool.stream.listen(onData, onError: onError, onDone: onDone, | 196 return pool.stream.listen(onData, |
201 cancelOnError: cancelOnError); | 197 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
202 } | 198 } |
203 | 199 |
204 StreamSubscription<T> capture(void onData(T event)) { | 200 StreamSubscription<T> capture(void onData(T event)) { |
205 var pool = new _StreamPool<T>.broadcast(); | 201 var pool = new _StreamPool<T>.broadcast(); |
206 for (var target in _targetList) { | 202 for (var target in _targetList) { |
207 pool.add(new _EventStream<T>(target, _eventType, true)); | 203 pool.add(new _EventStream<T>(target, _eventType, true)); |
208 } | 204 } |
209 return pool.stream.listen(onData); | 205 return pool.stream.listen(onData); |
210 } | 206 } |
211 | 207 |
212 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription)
, | 208 Stream<T> asBroadcastStream( |
213 void onCancel(StreamSubscription<T> subscription)
}) | 209 {void onListen(StreamSubscription<T> subscription), |
214 => this; | 210 void onCancel(StreamSubscription<T> subscription)}) => |
| 211 this; |
215 bool get isBroadcast => true; | 212 bool get isBroadcast => true; |
216 } | 213 } |
217 | 214 |
218 // We would like this to just be EventListener<T> but that typdef cannot | 215 // We would like this to just be EventListener<T> but that typdef cannot |
219 // use generics until dartbug/26276 is fixed. | 216 // use generics until dartbug/26276 is fixed. |
220 typedef _EventListener<T extends Event>(T event); | 217 typedef _EventListener<T extends Event>(T event); |
221 | 218 |
222 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { | 219 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { |
223 int _pauseCount = 0; | 220 int _pauseCount = 0; |
224 EventTarget _target; | 221 EventTarget _target; |
225 final String _eventType; | 222 final String _eventType; |
226 EventListener _onData; | 223 EventListener _onData; |
227 final bool _useCapture; | 224 final bool _useCapture; |
228 | 225 |
229 // TODO(leafp): It would be better to write this as | 226 // TODO(leafp): It would be better to write this as |
230 // _onData = onData == null ? null : | 227 // _onData = onData == null ? null : |
231 // onData is _wrapZoneCallback<Event, dynamic> | 228 // onData is _wrapZoneCallback<Event, dynamic> |
232 // ? _wrapZone/*<Event, dynamic>*/(onData) | 229 // ? _wrapZone/*<Event, dynamic>*/(onData) |
233 // : _wrapZone/*<Event, dynamic>*/((e) => onData(e as T)) | 230 // : _wrapZone/*<Event, dynamic>*/((e) => onData(e as T)) |
234 // In order to support existing tests which pass the wrong type of events but | 231 // In order to support existing tests which pass the wrong type of events but |
235 // use a more general listener, without causing as much slowdown for things | 232 // use a more general listener, without causing as much slowdown for things |
236 // which are typed correctly. But this currently runs afoul of restrictions | 233 // which are typed correctly. But this currently runs afoul of restrictions |
237 // on is checks for compatibility with the VM. | 234 // on is checks for compatibility with the VM. |
238 _EventStreamSubscription(this._target, this._eventType, void onData(T event), | 235 _EventStreamSubscription( |
239 this._useCapture) : | 236 this._target, this._eventType, void onData(T event), this._useCapture) |
240 _onData = onData == null | 237 : _onData = onData == null |
241 ? null | 238 ? null |
242 : _wrapZone/*<Event, dynamic>*/((e) => (onData as dynamic)(e)) | 239 : _wrapZone/*<Event, dynamic>*/((e) => (onData as dynamic)(e)) { |
243 { | |
244 _tryResume(); | 240 _tryResume(); |
245 } | 241 } |
246 | 242 |
247 Future cancel() { | 243 Future cancel() { |
248 if (_canceled) return null; | 244 if (_canceled) return null; |
249 | 245 |
250 _unlisten(); | 246 _unlisten(); |
251 // Clear out the target to indicate this is complete. | 247 // Clear out the target to indicate this is complete. |
252 _target = null; | 248 _target = null; |
253 _onData = null; | 249 _onData = null; |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 /** The type of event this stream is providing (e.g. "keydown"). */ | 323 /** The type of event this stream is providing (e.g. "keydown"). */ |
328 String _type; | 324 String _type; |
329 | 325 |
330 _CustomEventStreamImpl(String type) { | 326 _CustomEventStreamImpl(String type) { |
331 _type = type; | 327 _type = type; |
332 _streamController = new StreamController.broadcast(sync: true); | 328 _streamController = new StreamController.broadcast(sync: true); |
333 } | 329 } |
334 | 330 |
335 // Delegate all regular Stream behavior to our wrapped Stream. | 331 // Delegate all regular Stream behavior to our wrapped Stream. |
336 StreamSubscription<T> listen(void onData(T event), | 332 StreamSubscription<T> listen(void onData(T event), |
337 { Function onError, | 333 {Function onError, void onDone(), bool cancelOnError}) { |
338 void onDone(), | 334 return _streamController.stream.listen(onData, |
339 bool cancelOnError}) { | 335 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
340 return _streamController.stream.listen(onData, onError: onError, | |
341 onDone: onDone, cancelOnError: cancelOnError); | |
342 } | 336 } |
343 | 337 |
344 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription)
, | 338 Stream<T> asBroadcastStream( |
345 void onCancel(StreamSubscription<T> subscription)
}) | 339 {void onListen(StreamSubscription<T> subscription), |
346 => _streamController.stream; | 340 void onCancel(StreamSubscription<T> subscription)}) => |
| 341 _streamController.stream; |
347 | 342 |
348 bool get isBroadcast => true; | 343 bool get isBroadcast => true; |
349 | 344 |
350 void add(T event) { | 345 void add(T event) { |
351 if (event.type == _type) _streamController.add(event); | 346 if (event.type == _type) _streamController.add(event); |
352 } | 347 } |
353 } | 348 } |
354 | 349 |
355 class _CustomKeyEventStreamImpl extends _CustomEventStreamImpl<KeyEvent> | 350 class _CustomKeyEventStreamImpl extends _CustomEventStreamImpl<KeyEvent> |
356 implements CustomStream<KeyEvent> { | 351 implements CustomStream<KeyEvent> { |
(...skipping 19 matching lines...) Expand all Loading... |
376 var _subscriptions = new Map<Stream<T>, StreamSubscription<T>>(); | 371 var _subscriptions = new Map<Stream<T>, StreamSubscription<T>>(); |
377 | 372 |
378 /** | 373 /** |
379 * Creates a new stream pool where [stream] can be listened to more than | 374 * Creates a new stream pool where [stream] can be listened to more than |
380 * once. | 375 * once. |
381 * | 376 * |
382 * Any events from buffered streams in the pool will be emitted immediately, | 377 * Any events from buffered streams in the pool will be emitted immediately, |
383 * regardless of whether [stream] has any subscribers. | 378 * regardless of whether [stream] has any subscribers. |
384 */ | 379 */ |
385 _StreamPool.broadcast() { | 380 _StreamPool.broadcast() { |
386 _controller = new StreamController<T>.broadcast(sync: true, | 381 _controller = |
387 onCancel: close); | 382 new StreamController<T>.broadcast(sync: true, onCancel: close); |
388 } | 383 } |
389 | 384 |
390 /** | 385 /** |
391 * The stream through which all events from streams in the pool are emitted. | 386 * The stream through which all events from streams in the pool are emitted. |
392 */ | 387 */ |
393 Stream<T> get stream => _controller.stream; | 388 Stream<T> get stream => _controller.stream; |
394 | 389 |
395 /** | 390 /** |
396 * Adds [stream] as a member of this pool. | 391 * Adds [stream] as a member of this pool. |
397 * | 392 * |
398 * Any events from [stream] will be emitted through [this.stream]. If | 393 * Any events from [stream] will be emitted through [this.stream]. If |
399 * [stream] is sync, they'll be emitted synchronously; if [stream] is async, | 394 * [stream] is sync, they'll be emitted synchronously; if [stream] is async, |
400 * they'll be emitted asynchronously. | 395 * they'll be emitted asynchronously. |
401 */ | 396 */ |
402 void add(Stream<T> stream) { | 397 void add(Stream<T> stream) { |
403 if (_subscriptions.containsKey(stream)) return; | 398 if (_subscriptions.containsKey(stream)) return; |
404 _subscriptions[stream] = stream.listen(_controller.add, | 399 _subscriptions[stream] = stream.listen(_controller.add, |
405 onError: _controller.addError, | 400 onError: _controller.addError, onDone: () => remove(stream)); |
406 onDone: () => remove(stream)); | |
407 } | 401 } |
408 | 402 |
409 /** Removes [stream] as a member of this pool. */ | 403 /** Removes [stream] as a member of this pool. */ |
410 void remove(Stream<T> stream) { | 404 void remove(Stream<T> stream) { |
411 var subscription = _subscriptions.remove(stream); | 405 var subscription = _subscriptions.remove(stream); |
412 if (subscription != null) subscription.cancel(); | 406 if (subscription != null) subscription.cancel(); |
413 } | 407 } |
414 | 408 |
415 /** Removes all streams from this pool and closes [stream]. */ | 409 /** Removes all streams from this pool and closes [stream]. */ |
416 void close() { | 410 void close() { |
417 for (var subscription in _subscriptions.values) { | 411 for (var subscription in _subscriptions.values) { |
418 subscription.cancel(); | 412 subscription.cancel(); |
419 } | 413 } |
420 _subscriptions.clear(); | 414 _subscriptions.clear(); |
421 _controller.close(); | 415 _controller.close(); |
422 } | 416 } |
423 } | 417 } |
424 | 418 |
425 /** | 419 /** |
426 * A factory to expose DOM events as streams, where the DOM event name has to | 420 * A factory to expose DOM events as streams, where the DOM event name has to |
427 * be determined on the fly (for example, mouse wheel events). | 421 * be determined on the fly (for example, mouse wheel events). |
428 */ | 422 */ |
429 class _CustomEventStreamProvider<T extends Event> | 423 class _CustomEventStreamProvider<T extends Event> |
430 implements EventStreamProvider<T> { | 424 implements EventStreamProvider<T> { |
431 | |
432 final _eventTypeGetter; | 425 final _eventTypeGetter; |
433 const _CustomEventStreamProvider(this._eventTypeGetter); | 426 const _CustomEventStreamProvider(this._eventTypeGetter); |
434 | 427 |
435 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { | 428 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { |
436 return new _EventStream<T>(e, _eventTypeGetter(e), useCapture); | 429 return new _EventStream<T>(e, _eventTypeGetter(e), useCapture); |
437 } | 430 } |
438 | 431 |
439 ElementStream<T> forElement(Element e, {bool useCapture: false}) { | 432 ElementStream<T> forElement(Element e, {bool useCapture: false}) { |
440 return new _ElementEventStreamImpl<T>(e, _eventTypeGetter(e), useCapture); | 433 return new _ElementEventStreamImpl<T>(e, _eventTypeGetter(e), useCapture); |
441 } | 434 } |
442 | 435 |
443 ElementStream<T> _forElementList(ElementList e, | 436 ElementStream<T> _forElementList(ElementList e, {bool useCapture: false}) { |
444 {bool useCapture: false}) { | 437 return new _ElementListEventStreamImpl<T>( |
445 return new _ElementListEventStreamImpl<T>(e, _eventTypeGetter(e), useCapture
); | 438 e, _eventTypeGetter(e), useCapture); |
446 } | 439 } |
447 | 440 |
448 String getEventType(EventTarget target) { | 441 String getEventType(EventTarget target) { |
449 return _eventTypeGetter(target); | 442 return _eventTypeGetter(target); |
450 } | 443 } |
451 | 444 |
452 String get _eventType => | 445 String get _eventType => |
453 throw new UnsupportedError('Access type through getEventType method.'); | 446 throw new UnsupportedError('Access type through getEventType method.'); |
454 } | 447 } |
OLD | NEW |