| 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 * Adapter for exposing DOM events as Dart streams. | 8 * A factory to expose DOM events as Streams. |
| 9 */ | 9 */ |
| 10 class _EventStream<T extends Event> extends Stream<T> { | 10 class EventStreamProvider<T extends Event> { |
| 11 final EventTarget _target; | |
| 12 final String _eventType; | 11 final String _eventType; |
| 13 final bool _useCapture; | |
| 14 | 12 |
| 15 _EventStream(this._target, this._eventType, this._useCapture); | 13 const EventStreamProvider(this._eventType); |
| 16 | 14 |
| 17 // DOM events are inherently multi-subscribers. | 15 /** |
| 18 Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription), | 16 * Gets a [Stream] for this event type, on the specified target. |
| 19 void onCancel(StreamSubscription subscription)}) | 17 * |
| 20 => this; | 18 * This will always return a broadcast stream so multiple listeners can be |
| 21 bool get isBroadcast => true; | 19 * used simultaneously. |
| 20 * |
| 21 * This may be used to capture DOM events: |
| 22 * |
| 23 * Element.keyDownEvent.forTarget(element, useCapture: true).listen(...); |
| 24 * |
| 25 * // Alternate method: |
| 26 * Element.keyDownEvent.forTarget(element).capture(...); |
| 27 * |
| 28 * Or for listening to an event which will bubble through the DOM tree: |
| 29 * |
| 30 * MediaElement.pauseEvent.forTarget(document.body).listen(...); |
| 31 * |
| 32 * See also: |
| 33 * |
| 34 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis
tener) |
| 35 */ |
| 36 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) => |
| 37 new _EventStream(e, _eventType, useCapture); |
| 22 | 38 |
| 23 StreamSubscription<T> listen(void onData(T event), | 39 /** |
| 24 { Function onError, | 40 * Gets an [ElementEventStream] for this event type, on the specified element. |
| 25 void onDone(), | 41 * |
| 26 bool cancelOnError}) { | 42 * This will always return a broadcast stream so multiple listeners can be |
| 43 * used simultaneously. |
| 44 * |
| 45 * This may be used to capture DOM events: |
| 46 * |
| 47 * Element.keyDownEvent.forElement(element, useCapture: true).listen(...); |
| 48 * |
| 49 * // Alternate method: |
| 50 * Element.keyDownEvent.forElement(element).capture(...); |
| 51 * |
| 52 * Or for listening to an event which will bubble through the DOM tree: |
| 53 * |
| 54 * MediaElement.pauseEvent.forElement(document.body).listen(...); |
| 55 * |
| 56 * See also: |
| 57 * |
| 58 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis
tener) |
| 59 */ |
| 60 ElementStream<T> forElement(Element e, {bool useCapture: false}) { |
| 61 return new _ElementEventStreamImpl(e, _eventType, useCapture); |
| 62 } |
| 27 | 63 |
| 28 return new _EventStreamSubscription<T>( | 64 /** |
| 29 this._target, this._eventType, onData, this._useCapture); | 65 * Gets an [ElementEventStream] for this event type, on the list of elements. |
| 66 * |
| 67 * This will always return a broadcast stream so multiple listeners can be |
| 68 * used simultaneously. |
| 69 * |
| 70 * This may be used to capture DOM events: |
| 71 * |
| 72 * Element.keyDownEvent._forElementList(element, useCapture: true).listen(
...); |
| 73 * |
| 74 * See also: |
| 75 * |
| 76 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis
tener) |
| 77 */ |
| 78 ElementStream<T> _forElementList(ElementList e, {bool useCapture: false}) { |
| 79 return new _ElementListEventStreamImpl(e, _eventType, useCapture); |
| 80 } |
| 81 |
| 82 /** |
| 83 * Gets the type of the event which this would listen for on the specified |
| 84 * event target. |
| 85 * |
| 86 * The target is necessary because some browsers may use different event names |
| 87 * for the same purpose and the target allows differentiating browser support. |
| 88 */ |
| 89 String getEventType(EventTarget target) { |
| 90 return _eventType; |
| 30 } | 91 } |
| 31 } | 92 } |
| 32 | 93 |
| 33 /** A specialized Stream available to [Element]s to enable event delegation. */ | 94 /** A specialized Stream available to [Element]s to enable event delegation. */ |
| 34 abstract class ElementStream<T extends Event> implements Stream<T> { | 95 abstract class ElementStream<T extends Event> implements Stream<T> { |
| 35 /** | 96 /** |
| 36 * Return a stream that only fires when the particular event fires for | 97 * Return a stream that only fires when the particular event fires for |
| 37 * elements matching the specified CSS selector. | 98 * elements matching the specified CSS selector. |
| 38 * | 99 * |
| 39 * This is the Dart equivalent to jQuery's | 100 * This is the Dart equivalent to jQuery's |
| (...skipping 12 matching lines...) Expand all Loading... |
| 52 * ## Other resources | 113 * ## Other resources |
| 53 * | 114 * |
| 54 * * [Event Capture] | 115 * * [Event Capture] |
| 55 * (http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow-capture) | 116 * (http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow-capture) |
| 56 * from the W3C DOM Events specification. | 117 * from the W3C DOM Events specification. |
| 57 */ | 118 */ |
| 58 StreamSubscription<T> capture(void onData(T event)); | 119 StreamSubscription<T> capture(void onData(T event)); |
| 59 } | 120 } |
| 60 | 121 |
| 61 /** | 122 /** |
| 123 * Adapter for exposing DOM events as Dart streams. |
| 124 */ |
| 125 class _EventStream<T extends Event> extends Stream<T> { |
| 126 final EventTarget _target; |
| 127 final String _eventType; |
| 128 final bool _useCapture; |
| 129 |
| 130 _EventStream(this._target, this._eventType, this._useCapture); |
| 131 |
| 132 // DOM events are inherently multi-subscribers. |
| 133 Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription), |
| 134 void onCancel(StreamSubscription subscription)}) |
| 135 => this; |
| 136 bool get isBroadcast => true; |
| 137 |
| 138 StreamSubscription<T> listen(void onData(T event), |
| 139 { Function onError, |
| 140 void onDone(), |
| 141 bool cancelOnError}) { |
| 142 |
| 143 return new _EventStreamSubscription<T>( |
| 144 this._target, this._eventType, onData, this._useCapture); |
| 145 } |
| 146 } |
| 147 |
| 148 /** |
| 62 * Adapter for exposing DOM Element events as streams, while also allowing | 149 * Adapter for exposing DOM Element events as streams, while also allowing |
| 63 * event delegation. | 150 * event delegation. |
| 64 */ | 151 */ |
| 65 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T> | 152 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T> |
| 66 implements ElementStream<T> { | 153 implements ElementStream<T> { |
| 67 _ElementEventStreamImpl(target, eventType, useCapture) : | 154 _ElementEventStreamImpl(target, eventType, useCapture) : |
| 68 super(target, eventType, useCapture); | 155 super(target, eventType, useCapture); |
| 69 | 156 |
| 70 Stream<T> matches(String selector) => this.where( | 157 Stream<T> matches(String selector) => this.where( |
| 71 (event) => event.target.matchesWithAncestors(selector)).map((e) { | 158 (event) => event.target.matchesWithAncestors(selector)).map((e) { |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 117 } | 204 } |
| 118 return pool.stream.listen(onData); | 205 return pool.stream.listen(onData); |
| 119 } | 206 } |
| 120 | 207 |
| 121 Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription), | 208 Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription), |
| 122 void onCancel(StreamSubscription subscription)}) | 209 void onCancel(StreamSubscription subscription)}) |
| 123 => this; | 210 => this; |
| 124 bool get isBroadcast => true; | 211 bool get isBroadcast => true; |
| 125 } | 212 } |
| 126 | 213 |
| 214 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { |
| 215 int _pauseCount = 0; |
| 216 EventTarget _target; |
| 217 final String _eventType; |
| 218 var _onData; |
| 219 final bool _useCapture; |
| 220 |
| 221 _EventStreamSubscription(this._target, this._eventType, onData, |
| 222 this._useCapture) : _onData = _wrapZone(onData) { |
| 223 _tryResume(); |
| 224 } |
| 225 |
| 226 void cancel() { |
| 227 if (_canceled) return; |
| 228 |
| 229 _unlisten(); |
| 230 // Clear out the target to indicate this is complete. |
| 231 _target = null; |
| 232 _onData = null; |
| 233 } |
| 234 |
| 235 bool get _canceled => _target == null; |
| 236 |
| 237 void onData(void handleData(T event)) { |
| 238 if (_canceled) { |
| 239 throw new StateError("Subscription has been canceled."); |
| 240 } |
| 241 // Remove current event listener. |
| 242 _unlisten(); |
| 243 |
| 244 _onData = _wrapZone(handleData); |
| 245 _tryResume(); |
| 246 } |
| 247 |
| 248 /// Has no effect. |
| 249 void onError(Function handleError) {} |
| 250 |
| 251 /// Has no effect. |
| 252 void onDone(void handleDone()) {} |
| 253 |
| 254 void pause([Future resumeSignal]) { |
| 255 if (_canceled) return; |
| 256 ++_pauseCount; |
| 257 _unlisten(); |
| 258 |
| 259 if (resumeSignal != null) { |
| 260 resumeSignal.whenComplete(resume); |
| 261 } |
| 262 } |
| 263 |
| 264 bool get isPaused => _pauseCount > 0; |
| 265 |
| 266 void resume() { |
| 267 if (_canceled || !isPaused) return; |
| 268 --_pauseCount; |
| 269 _tryResume(); |
| 270 } |
| 271 |
| 272 void _tryResume() { |
| 273 if (_onData != null && !isPaused) { |
| 274 _target.addEventListener(_eventType, _onData, _useCapture); |
| 275 } |
| 276 } |
| 277 |
| 278 void _unlisten() { |
| 279 if (_onData != null) { |
| 280 _target.removeEventListener(_eventType, _onData, _useCapture); |
| 281 } |
| 282 } |
| 283 |
| 284 Future asFuture([var futureValue]) { |
| 285 // We just need a future that will never succeed or fail. |
| 286 Completer completer = new Completer(); |
| 287 return completer.future; |
| 288 } |
| 289 } |
| 290 |
| 127 /** | 291 /** |
| 128 * A stream of custom events, which enables the user to "fire" (add) their own | 292 * A stream of custom events, which enables the user to "fire" (add) their own |
| 129 * custom events to a stream. | 293 * custom events to a stream. |
| 130 */ | 294 */ |
| 131 abstract class CustomStream<T extends Event> implements Stream<T> { | 295 abstract class CustomStream<T extends Event> implements Stream<T> { |
| 132 /** | 296 /** |
| 133 * Add the following custom event to the stream for dispatching to interested | 297 * Add the following custom event to the stream for dispatching to interested |
| 134 * listeners. | 298 * listeners. |
| 135 */ | 299 */ |
| 136 void add(T event); | 300 void add(T event); |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 230 /** Removes all streams from this pool and closes [stream]. */ | 394 /** Removes all streams from this pool and closes [stream]. */ |
| 231 void close() { | 395 void close() { |
| 232 for (var subscription in _subscriptions.values) { | 396 for (var subscription in _subscriptions.values) { |
| 233 subscription.cancel(); | 397 subscription.cancel(); |
| 234 } | 398 } |
| 235 _subscriptions.clear(); | 399 _subscriptions.clear(); |
| 236 _controller.close(); | 400 _controller.close(); |
| 237 } | 401 } |
| 238 } | 402 } |
| 239 | 403 |
| 240 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { | |
| 241 int _pauseCount = 0; | |
| 242 EventTarget _target; | |
| 243 final String _eventType; | |
| 244 var _onData; | |
| 245 final bool _useCapture; | |
| 246 | |
| 247 _EventStreamSubscription(this._target, this._eventType, onData, | |
| 248 this._useCapture) : _onData = _wrapZone(onData) { | |
| 249 _tryResume(); | |
| 250 } | |
| 251 | |
| 252 void cancel() { | |
| 253 if (_canceled) return; | |
| 254 | |
| 255 _unlisten(); | |
| 256 // Clear out the target to indicate this is complete. | |
| 257 _target = null; | |
| 258 _onData = null; | |
| 259 } | |
| 260 | |
| 261 bool get _canceled => _target == null; | |
| 262 | |
| 263 void onData(void handleData(T event)) { | |
| 264 if (_canceled) { | |
| 265 throw new StateError("Subscription has been canceled."); | |
| 266 } | |
| 267 // Remove current event listener. | |
| 268 _unlisten(); | |
| 269 | |
| 270 _onData = _wrapZone(handleData); | |
| 271 _tryResume(); | |
| 272 } | |
| 273 | |
| 274 /// Has no effect. | |
| 275 void onError(Function handleError) {} | |
| 276 | |
| 277 /// Has no effect. | |
| 278 void onDone(void handleDone()) {} | |
| 279 | |
| 280 void pause([Future resumeSignal]) { | |
| 281 if (_canceled) return; | |
| 282 ++_pauseCount; | |
| 283 _unlisten(); | |
| 284 | |
| 285 if (resumeSignal != null) { | |
| 286 resumeSignal.whenComplete(resume); | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 bool get isPaused => _pauseCount > 0; | |
| 291 | |
| 292 void resume() { | |
| 293 if (_canceled || !isPaused) return; | |
| 294 --_pauseCount; | |
| 295 _tryResume(); | |
| 296 } | |
| 297 | |
| 298 void _tryResume() { | |
| 299 if (_onData != null && !isPaused) { | |
| 300 _target.addEventListener(_eventType, _onData, _useCapture); | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 void _unlisten() { | |
| 305 if (_onData != null) { | |
| 306 _target.removeEventListener(_eventType, _onData, _useCapture); | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 Future asFuture([var futureValue]) { | |
| 311 // We just need a future that will never succeed or fail. | |
| 312 Completer completer = new Completer(); | |
| 313 return completer.future; | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 /** | |
| 318 * A factory to expose DOM events as Streams. | |
| 319 */ | |
| 320 class EventStreamProvider<T extends Event> { | |
| 321 final String _eventType; | |
| 322 | |
| 323 const EventStreamProvider(this._eventType); | |
| 324 | |
| 325 /** | |
| 326 * Gets a [Stream] for this event type, on the specified target. | |
| 327 * | |
| 328 * This will always return a broadcast stream so multiple listeners can be | |
| 329 * used simultaneously. | |
| 330 * | |
| 331 * This may be used to capture DOM events: | |
| 332 * | |
| 333 * Element.keyDownEvent.forTarget(element, useCapture: true).listen(...); | |
| 334 * | |
| 335 * // Alternate method: | |
| 336 * Element.keyDownEvent.forTarget(element).capture(...); | |
| 337 * | |
| 338 * Or for listening to an event which will bubble through the DOM tree: | |
| 339 * | |
| 340 * MediaElement.pauseEvent.forTarget(document.body).listen(...); | |
| 341 * | |
| 342 * See also: | |
| 343 * | |
| 344 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis
tener) | |
| 345 */ | |
| 346 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) => | |
| 347 new _EventStream(e, _eventType, useCapture); | |
| 348 | |
| 349 /** | |
| 350 * Gets an [ElementEventStream] for this event type, on the specified element. | |
| 351 * | |
| 352 * This will always return a broadcast stream so multiple listeners can be | |
| 353 * used simultaneously. | |
| 354 * | |
| 355 * This may be used to capture DOM events: | |
| 356 * | |
| 357 * Element.keyDownEvent.forElement(element, useCapture: true).listen(...); | |
| 358 * | |
| 359 * // Alternate method: | |
| 360 * Element.keyDownEvent.forElement(element).capture(...); | |
| 361 * | |
| 362 * Or for listening to an event which will bubble through the DOM tree: | |
| 363 * | |
| 364 * MediaElement.pauseEvent.forElement(document.body).listen(...); | |
| 365 * | |
| 366 * See also: | |
| 367 * | |
| 368 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis
tener) | |
| 369 */ | |
| 370 ElementStream<T> forElement(Element e, {bool useCapture: false}) { | |
| 371 return new _ElementEventStreamImpl(e, _eventType, useCapture); | |
| 372 } | |
| 373 | |
| 374 /** | |
| 375 * Gets an [ElementEventStream] for this event type, on the list of elements. | |
| 376 * | |
| 377 * This will always return a broadcast stream so multiple listeners can be | |
| 378 * used simultaneously. | |
| 379 * | |
| 380 * This may be used to capture DOM events: | |
| 381 * | |
| 382 * Element.keyDownEvent._forElementList(element, useCapture: true).listen(
...); | |
| 383 * | |
| 384 * See also: | |
| 385 * | |
| 386 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis
tener) | |
| 387 */ | |
| 388 ElementStream<T> _forElementList(ElementList e, {bool useCapture: false}) { | |
| 389 return new _ElementListEventStreamImpl(e, _eventType, useCapture); | |
| 390 } | |
| 391 | |
| 392 /** | |
| 393 * Gets the type of the event which this would listen for on the specified | |
| 394 * event target. | |
| 395 * | |
| 396 * The target is necessary because some browsers may use different event names | |
| 397 * for the same purpose and the target allows differentiating browser support. | |
| 398 */ | |
| 399 String getEventType(EventTarget target) { | |
| 400 return _eventType; | |
| 401 } | |
| 402 } | |
| 403 | |
| 404 /** | 404 /** |
| 405 * A factory to expose DOM events as streams, where the DOM event name has to | 405 * A factory to expose DOM events as streams, where the DOM event name has to |
| 406 * be determined on the fly (for example, mouse wheel events). | 406 * be determined on the fly (for example, mouse wheel events). |
| 407 */ | 407 */ |
| 408 class _CustomEventStreamProvider<T extends Event> | 408 class _CustomEventStreamProvider<T extends Event> |
| 409 implements EventStreamProvider<T> { | 409 implements EventStreamProvider<T> { |
| 410 | 410 |
| 411 final _eventTypeGetter; | 411 final _eventTypeGetter; |
| 412 const _CustomEventStreamProvider(this._eventTypeGetter); | 412 const _CustomEventStreamProvider(this._eventTypeGetter); |
| 413 | 413 |
| 414 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { | 414 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { |
| 415 return new _EventStream(e, _eventTypeGetter(e), useCapture); | 415 return new _EventStream(e, _eventTypeGetter(e), useCapture); |
| 416 } | 416 } |
| 417 | 417 |
| 418 ElementStream<T> forElement(Element e, {bool useCapture: false}) { | 418 ElementStream<T> forElement(Element e, {bool useCapture: false}) { |
| 419 return new _ElementEventStreamImpl(e, _eventTypeGetter(e), useCapture); | 419 return new _ElementEventStreamImpl(e, _eventTypeGetter(e), useCapture); |
| 420 } | 420 } |
| 421 | 421 |
| 422 ElementStream<T> _forElementList(ElementList e, | 422 ElementStream<T> _forElementList(ElementList e, |
| 423 {bool useCapture: false}) { | 423 {bool useCapture: false}) { |
| 424 return new _ElementListEventStreamImpl(e, _eventTypeGetter(e), useCapture); | 424 return new _ElementListEventStreamImpl(e, _eventTypeGetter(e), useCapture); |
| 425 } | 425 } |
| 426 | 426 |
| 427 String getEventType(EventTarget target) { | 427 String getEventType(EventTarget target) { |
| 428 return _eventTypeGetter(target); | 428 return _eventTypeGetter(target); |
| 429 } | 429 } |
| 430 } | 430 } |
| OLD | NEW |