| 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 |