Chromium Code Reviews| 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 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 Loading... | |
| 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 } |
| OLD | NEW |