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(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(...); |
48 * | 48 * |
49 * // Alternate method: | 49 * // Alternate method: |
50 * Element.keyDownEvent.forElement(element).capture(...); | 50 * Element.keyDownEvent.forElement(element).capture(...); |
51 * | 51 * |
52 * Or for listening to an event which will bubble through the DOM tree: | 52 * Or for listening to an event which will bubble through the DOM tree: |
53 * | 53 * |
54 * MediaElement.pauseEvent.forElement(document.body).listen(...); | 54 * MediaElement.pauseEvent.forElement(document.body).listen(...); |
55 * | 55 * |
56 * See also: | 56 * See also: |
57 * | 57 * |
58 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis
tener) | 58 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis
tener) |
59 */ | 59 */ |
60 ElementStream<T> forElement(Element e, {bool useCapture: false}) { | 60 ElementStream<T> forElement(Element e, {bool useCapture: false}) { |
61 return new _ElementEventStreamImpl(e, _eventType, useCapture); | 61 return new _ElementEventStreamImpl<T>(e, _eventType, useCapture); |
62 } | 62 } |
63 | 63 |
64 /** | 64 /** |
65 * Gets an [ElementEventStream] for this event type, on the list of elements. | 65 * Gets an [ElementEventStream] for this event type, on the list of elements. |
66 * | 66 * |
67 * This will always return a broadcast stream so multiple listeners can be | 67 * This will always return a broadcast stream so multiple listeners can be |
68 * used simultaneously. | 68 * used simultaneously. |
69 * | 69 * |
70 * This may be used to capture DOM events: | 70 * This may be used to capture DOM events: |
71 * | 71 * |
(...skipping 50 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 subscription), | 132 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription)
, |
133 void onCancel(StreamSubscription subscription)}) | 133 void onCancel(StreamSubscription<T> subscription)
}) |
134 => this; | 134 => this; |
135 bool get isBroadcast => true; | 135 bool get isBroadcast => true; |
136 | 136 |
137 StreamSubscription<T> listen(void onData(T event), | 137 StreamSubscription<T> listen(void onData(T event), |
138 { Function onError, | 138 { Function onError, |
139 void onDone(), | 139 void onDone(), |
140 bool cancelOnError}) { | 140 bool cancelOnError}) { |
141 | 141 |
142 return new _EventStreamSubscription<T>( | 142 return new _EventStreamSubscription<T>( |
143 this._target, this._eventType, onData, this._useCapture); | 143 this._target, this._eventType, onData, this._useCapture); |
144 } | 144 } |
145 } | 145 } |
146 | 146 |
| 147 bool _matchesWithAncestors(Event event, String selector) { |
| 148 var target = event.target; |
| 149 return target is Element ? target.matchesWithAncestors(selector) : false; |
| 150 } |
| 151 |
147 /** | 152 /** |
148 * Adapter for exposing DOM Element events as streams, while also allowing | 153 * Adapter for exposing DOM Element events as streams, while also allowing |
149 * event delegation. | 154 * event delegation. |
150 */ | 155 */ |
151 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T> | 156 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T> |
152 implements ElementStream<T> { | 157 implements ElementStream<T> { |
153 _ElementEventStreamImpl(target, eventType, useCapture) : | 158 _ElementEventStreamImpl(target, eventType, useCapture) : |
154 super(target, eventType, useCapture); | 159 super(target, eventType, useCapture); |
155 | 160 |
156 Stream<T> matches(String selector) => this.where( | 161 Stream<T> matches(String selector) => this.where( |
157 (event) => event.target.matchesWithAncestors(selector)).map((e) { | 162 (event) => _matchesWithAncestors(event, selector)).map((e) { |
158 e._selector = selector; | 163 e._selector = selector; |
159 return e; | 164 return e; |
160 }); | 165 }); |
161 | 166 |
162 StreamSubscription<T> capture(void onData(T event)) => | 167 StreamSubscription<T> capture(void onData(T event)) => |
163 new _EventStreamSubscription<T>( | 168 new _EventStreamSubscription<T>( |
164 this._target, this._eventType, onData, true); | 169 this._target, this._eventType, onData, true); |
165 } | 170 } |
166 | 171 |
167 /** | 172 /** |
168 * Adapter for exposing events on a collection of DOM Elements as streams, | 173 * Adapter for exposing events on a collection of DOM Elements as streams, |
169 * while also allowing event delegation. | 174 * while also allowing event delegation. |
170 */ | 175 */ |
171 class _ElementListEventStreamImpl<T extends Event> extends Stream<T> | 176 class _ElementListEventStreamImpl<T extends Event> extends Stream<T> |
172 implements ElementStream<T> { | 177 implements ElementStream<T> { |
173 final Iterable<Element> _targetList; | 178 final Iterable<Element> _targetList; |
174 final bool _useCapture; | 179 final bool _useCapture; |
175 final String _eventType; | 180 final String _eventType; |
176 | 181 |
177 _ElementListEventStreamImpl( | 182 _ElementListEventStreamImpl( |
178 this._targetList, this._eventType, this._useCapture); | 183 this._targetList, this._eventType, this._useCapture); |
179 | 184 |
180 Stream<T> matches(String selector) => this.where( | 185 Stream<T> matches(String selector) => this.where( |
181 (event) => event.target.matchesWithAncestors(selector)).map((e) { | 186 (event) => _matchesWithAncestors(event, selector)).map((e) { |
182 e._selector = selector; | 187 e._selector = selector; |
183 return e; | 188 return e; |
184 }); | 189 }); |
185 | 190 |
186 // Delegate all regular Stream behavior to a wrapped Stream. | 191 // Delegate all regular Stream behavior to a wrapped Stream. |
187 StreamSubscription<T> listen(void onData(T event), | 192 StreamSubscription<T> listen(void onData(T event), |
188 { Function onError, | 193 { Function onError, |
189 void onDone(), | 194 void onDone(), |
190 bool cancelOnError}) { | 195 bool cancelOnError}) { |
191 var pool = new _StreamPool.broadcast(); | 196 var pool = new _StreamPool<T>.broadcast(); |
192 for (var target in _targetList) { | 197 for (var target in _targetList) { |
193 pool.add(new _EventStream(target, _eventType, _useCapture)); | 198 pool.add(new _EventStream<T>(target, _eventType, _useCapture)); |
194 } | 199 } |
195 return pool.stream.listen(onData, onError: onError, onDone: onDone, | 200 return pool.stream.listen(onData, onError: onError, onDone: onDone, |
196 cancelOnError: cancelOnError); | 201 cancelOnError: cancelOnError); |
197 } | 202 } |
198 | 203 |
199 StreamSubscription<T> capture(void onData(T event)) { | 204 StreamSubscription<T> capture(void onData(T event)) { |
200 var pool = new _StreamPool.broadcast(); | 205 var pool = new _StreamPool<T>.broadcast(); |
201 for (var target in _targetList) { | 206 for (var target in _targetList) { |
202 pool.add(new _EventStream(target, _eventType, true)); | 207 pool.add(new _EventStream<T>(target, _eventType, true)); |
203 } | 208 } |
204 return pool.stream.listen(onData); | 209 return pool.stream.listen(onData); |
205 } | 210 } |
206 | 211 |
207 Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription), | 212 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription)
, |
208 void onCancel(StreamSubscription subscription)}) | 213 void onCancel(StreamSubscription<T> subscription)
}) |
209 => this; | 214 => this; |
210 bool get isBroadcast => true; | 215 bool get isBroadcast => true; |
211 } | 216 } |
212 | 217 |
| 218 // We would like this to just be EventListener<T> but that typdef cannot |
| 219 // use generics until dartbug/26276 is fixed. |
| 220 typedef _EventListener<T extends Event>(T event); |
| 221 |
213 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { | 222 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { |
214 int _pauseCount = 0; | 223 int _pauseCount = 0; |
215 EventTarget _target; | 224 EventTarget _target; |
216 final String _eventType; | 225 final String _eventType; |
217 var _onData; | 226 EventListener _onData; |
218 final bool _useCapture; | 227 final bool _useCapture; |
219 | 228 |
220 _EventStreamSubscription(this._target, this._eventType, onData, | 229 // TODO(jacobr): for full strong mode correctness we should write |
221 this._useCapture) : _onData = _wrapZone(onData) { | 230 // _onData = onData == null ? null : _wrapZone/*<Event, dynamic>*/((e) => onDa
ta(e as T)) |
| 231 // but that breaks 114 co19 tests as well as multiple html tests as it is reas
onable |
| 232 // to pass the wrong type of event object to an event listener as part of a |
| 233 // test. |
| 234 _EventStreamSubscription(this._target, this._eventType, void onData(T event), |
| 235 this._useCapture) : _onData = _wrapZone/*<Event, dynamic>*/(onData) { |
222 _tryResume(); | 236 _tryResume(); |
223 } | 237 } |
224 | 238 |
225 Future cancel() { | 239 Future cancel() { |
226 if (_canceled) return null; | 240 if (_canceled) return null; |
227 | 241 |
228 _unlisten(); | 242 _unlisten(); |
229 // Clear out the target to indicate this is complete. | 243 // Clear out the target to indicate this is complete. |
230 _target = null; | 244 _target = null; |
231 _onData = null; | 245 _onData = null; |
232 return null; | 246 return null; |
233 } | 247 } |
234 | 248 |
235 bool get _canceled => _target == null; | 249 bool get _canceled => _target == null; |
236 | 250 |
237 void onData(void handleData(T event)) { | 251 void onData(void handleData(T event)) { |
238 if (_canceled) { | 252 if (_canceled) { |
239 throw new StateError("Subscription has been canceled."); | 253 throw new StateError("Subscription has been canceled."); |
240 } | 254 } |
241 // Remove current event listener. | 255 // Remove current event listener. |
242 _unlisten(); | 256 _unlisten(); |
243 | 257 _onData = _wrapZone/*<Event, dynamic>*/(handleData); |
244 _onData = _wrapZone(handleData); | |
245 _tryResume(); | 258 _tryResume(); |
246 } | 259 } |
247 | 260 |
248 /// Has no effect. | 261 /// Has no effect. |
249 void onError(Function handleError) {} | 262 void onError(Function handleError) {} |
250 | 263 |
251 /// Has no effect. | 264 /// Has no effect. |
252 void onDone(void handleDone()) {} | 265 void onDone(void handleDone()) {} |
253 | 266 |
254 void pause([Future resumeSignal]) { | 267 void pause([Future resumeSignal]) { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
313 | 326 |
314 // Delegate all regular Stream behavior to our wrapped Stream. | 327 // Delegate all regular Stream behavior to our wrapped Stream. |
315 StreamSubscription<T> listen(void onData(T event), | 328 StreamSubscription<T> listen(void onData(T event), |
316 { Function onError, | 329 { Function onError, |
317 void onDone(), | 330 void onDone(), |
318 bool cancelOnError}) { | 331 bool cancelOnError}) { |
319 return _streamController.stream.listen(onData, onError: onError, | 332 return _streamController.stream.listen(onData, onError: onError, |
320 onDone: onDone, cancelOnError: cancelOnError); | 333 onDone: onDone, cancelOnError: cancelOnError); |
321 } | 334 } |
322 | 335 |
323 Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription), | 336 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription)
, |
324 void onCancel(StreamSubscription subscription)}) | 337 void onCancel(StreamSubscription<T> subscription)
}) |
325 => _streamController.stream; | 338 => _streamController.stream; |
326 | 339 |
327 bool get isBroadcast => true; | 340 bool get isBroadcast => true; |
328 | 341 |
329 void add(T event) { | 342 void add(T event) { |
330 if (event.type == _type) _streamController.add(event); | 343 if (event.type == _type) _streamController.add(event); |
331 } | 344 } |
332 } | 345 } |
333 | 346 |
334 class _CustomKeyEventStreamImpl extends _CustomEventStreamImpl<KeyEvent> | 347 class _CustomKeyEventStreamImpl extends _CustomEventStreamImpl<KeyEvent> |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 * A factory to expose DOM events as streams, where the DOM event name has to | 418 * 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). | 419 * be determined on the fly (for example, mouse wheel events). |
407 */ | 420 */ |
408 class _CustomEventStreamProvider<T extends Event> | 421 class _CustomEventStreamProvider<T extends Event> |
409 implements EventStreamProvider<T> { | 422 implements EventStreamProvider<T> { |
410 | 423 |
411 final _eventTypeGetter; | 424 final _eventTypeGetter; |
412 const _CustomEventStreamProvider(this._eventTypeGetter); | 425 const _CustomEventStreamProvider(this._eventTypeGetter); |
413 | 426 |
414 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { | 427 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { |
415 return new _EventStream(e, _eventTypeGetter(e), useCapture); | 428 return new _EventStream<T>(e, _eventTypeGetter(e), useCapture); |
416 } | 429 } |
417 | 430 |
418 ElementStream<T> forElement(Element e, {bool useCapture: false}) { | 431 ElementStream<T> forElement(Element e, {bool useCapture: false}) { |
419 return new _ElementEventStreamImpl(e, _eventTypeGetter(e), useCapture); | 432 return new _ElementEventStreamImpl<T>(e, _eventTypeGetter(e), useCapture); |
420 } | 433 } |
421 | 434 |
422 ElementStream<T> _forElementList(ElementList e, | 435 ElementStream<T> _forElementList(ElementList e, |
423 {bool useCapture: false}) { | 436 {bool useCapture: false}) { |
424 return new _ElementListEventStreamImpl(e, _eventTypeGetter(e), useCapture); | 437 return new _ElementListEventStreamImpl<T>(e, _eventTypeGetter(e), useCapture
); |
425 } | 438 } |
426 | 439 |
427 String getEventType(EventTarget target) { | 440 String getEventType(EventTarget target) { |
428 return _eventTypeGetter(target); | 441 return _eventTypeGetter(target); |
429 } | 442 } |
430 | 443 |
431 String get _eventType => | 444 String get _eventType => |
432 throw new UnsupportedError('Access type through getEventType method.'); | 445 throw new UnsupportedError('Access type through getEventType method.'); |
433 } | 446 } |
OLD | NEW |