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 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 * is first processed for the event target and then bubbles upward. | 111 * is first processed for the event target and then bubbles upward. |
112 * | 112 * |
113 * ## Other resources | 113 * ## Other resources |
114 * | 114 * |
115 * * [Event Capture](http://www.w3.org/TR/DOM-Level-2-Events/events.html#Event
s-flow-capture) | 115 * * [Event Capture](http://www.w3.org/TR/DOM-Level-2-Events/events.html#Event
s-flow-capture) |
116 * from the W3C DOM Events specification. | 116 * from the W3C DOM Events specification. |
117 */ | 117 */ |
118 StreamSubscription<T> capture(void onData(T event)); | 118 StreamSubscription<T> capture(void onData(T event)); |
119 } | 119 } |
120 | 120 |
121 /// Task specification for DOM Events. | |
122 /// | |
123 /// *Experimental*. May disappear without notice. | |
124 class EventSubscriptionSpecification<T extends Event> | |
125 implements TaskSpecification { | |
126 @override | |
127 final String name; | |
128 @override | |
129 final bool isOneShot; | |
130 | |
131 final EventTarget target; | |
132 /// The event-type of the event. For example 'click' for click events. | |
133 final String eventType; | |
134 // TODO(floitsch): the first generic argument should be 'void'. | |
135 final ZoneUnaryCallback<dynamic, T> onData; | |
136 final bool useCapture; | |
137 | |
138 EventSubscriptionSpecification({this.name, this.isOneShot, this.target, | |
139 this.eventType, void this.onData(T event), this.useCapture}); | |
140 | |
141 /// Returns a copy of this instance, with every non-null argument replaced | |
142 /// by the given value. | |
143 EventSubscriptionSpecification<T> replace( | |
144 {String name, bool isOneShot, EventTarget target, | |
145 String eventType, void onData(T event), bool useCapture}) { | |
146 return new EventSubscriptionSpecification<T>( | |
147 name: name ?? this.name, | |
148 isOneShot: isOneShot ?? this.isOneShot, | |
149 target: target ?? this.target, | |
150 eventType: eventType ?? this.eventType, | |
151 onData: onData ?? this.onData, | |
152 useCapture: useCapture ?? this.useCapture); | |
153 } | |
154 } | |
155 | |
156 /** | 121 /** |
157 * Adapter for exposing DOM events as Dart streams. | 122 * Adapter for exposing DOM events as Dart streams. |
158 */ | 123 */ |
159 class _EventStream<T extends Event> extends Stream<T> { | 124 class _EventStream<T extends Event> extends Stream<T> { |
160 final EventTarget _target; | 125 final EventTarget _target; |
161 final String _eventType; | 126 final String _eventType; |
162 final bool _useCapture; | 127 final bool _useCapture; |
163 /// The name that is used in the task specification. | |
164 final String _name; | |
165 /// Whether the stream can trigger multiple times. | |
166 final bool _isOneShot; | |
167 | 128 |
168 _EventStream(this._target, String eventType, this._useCapture, | 129 _EventStream(this._target, this._eventType, this._useCapture); |
169 {String name, bool isOneShot: false}) | |
170 : _eventType = eventType, | |
171 _isOneShot = isOneShot, | |
172 _name = name ?? "dart.html.event.$eventType"; | |
173 | 130 |
174 // DOM events are inherently multi-subscribers. | 131 // DOM events are inherently multi-subscribers. |
175 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription)
, | 132 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription)
, |
176 void onCancel(StreamSubscription<T> subscription)
}) | 133 void onCancel(StreamSubscription<T> subscription)
}) |
177 => this; | 134 => this; |
178 bool get isBroadcast => true; | 135 bool get isBroadcast => true; |
179 | 136 |
180 StreamSubscription<T> _listen( | |
181 void onData(T event), {bool useCapture}) { | |
182 | |
183 if (identical(Zone.current, Zone.ROOT)) { | |
184 return new _EventStreamSubscription<T>( | |
185 this._target, this._eventType, onData, this._useCapture, | |
186 Zone.current); | |
187 } | |
188 | |
189 var specification = new EventSubscriptionSpecification<T>( | |
190 name: this._name, isOneShot: this._isOneShot, | |
191 target: this._target, eventType: this._eventType, | |
192 onData: onData, useCapture: useCapture); | |
193 // We need to wrap the _createStreamSubscription call, since a tear-off | |
194 // would not bind the generic type 'T'. | |
195 return Zone.current.createTask((spec, Zone zone) { | |
196 return _createStreamSubscription/*<T>*/(spec, zone); | |
197 }, specification); | |
198 } | |
199 | |
200 StreamSubscription<T> listen(void onData(T event), | 137 StreamSubscription<T> listen(void onData(T event), |
201 { Function onError, | 138 { Function onError, |
202 void onDone(), | 139 void onDone(), |
203 bool cancelOnError}) { | 140 bool cancelOnError}) { |
204 return _listen(onData, useCapture: this._useCapture); | 141 |
| 142 return new _EventStreamSubscription<T>( |
| 143 this._target, this._eventType, onData, this._useCapture); |
205 } | 144 } |
206 } | 145 } |
207 | 146 |
208 bool _matchesWithAncestors(Event event, String selector) { | 147 bool _matchesWithAncestors(Event event, String selector) { |
209 var target = event.target; | 148 var target = event.target; |
210 return target is Element ? target.matchesWithAncestors(selector) : false; | 149 return target is Element ? target.matchesWithAncestors(selector) : false; |
211 } | 150 } |
212 | 151 |
213 /** | 152 /** |
214 * Adapter for exposing DOM Element events as streams, while also allowing | 153 * Adapter for exposing DOM Element events as streams, while also allowing |
215 * event delegation. | 154 * event delegation. |
216 */ | 155 */ |
217 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T> | 156 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T> |
218 implements ElementStream<T> { | 157 implements ElementStream<T> { |
219 _ElementEventStreamImpl(target, eventType, useCapture, | 158 _ElementEventStreamImpl(target, eventType, useCapture) : |
220 {String name, bool isOneShot: false}) : | 159 super(target, eventType, useCapture); |
221 super(target, eventType, useCapture, name: name, isOneShot: isOneShot); | |
222 | 160 |
223 Stream<T> matches(String selector) => this.where( | 161 Stream<T> matches(String selector) => this.where( |
224 (event) => _matchesWithAncestors(event, selector)).map((e) { | 162 (event) => _matchesWithAncestors(event, selector)).map((e) { |
225 e._selector = selector; | 163 e._selector = selector; |
226 return e; | 164 return e; |
227 }); | 165 }); |
228 | 166 |
229 StreamSubscription<T> capture(void onData(T event)) { | 167 StreamSubscription<T> capture(void onData(T event)) => |
230 return _listen(onData, useCapture: true); | 168 new _EventStreamSubscription<T>( |
231 } | 169 this._target, this._eventType, onData, true); |
232 } | 170 } |
233 | 171 |
234 /** | 172 /** |
235 * 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, |
236 * while also allowing event delegation. | 174 * while also allowing event delegation. |
237 */ | 175 */ |
238 class _ElementListEventStreamImpl<T extends Event> extends Stream<T> | 176 class _ElementListEventStreamImpl<T extends Event> extends Stream<T> |
239 implements ElementStream<T> { | 177 implements ElementStream<T> { |
240 final Iterable<Element> _targetList; | 178 final Iterable<Element> _targetList; |
241 final bool _useCapture; | 179 final bool _useCapture; |
(...skipping 28 matching lines...) Expand all Loading... |
270 } | 208 } |
271 return pool.stream.listen(onData); | 209 return pool.stream.listen(onData); |
272 } | 210 } |
273 | 211 |
274 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription)
, | 212 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription)
, |
275 void onCancel(StreamSubscription<T> subscription)
}) | 213 void onCancel(StreamSubscription<T> subscription)
}) |
276 => this; | 214 => this; |
277 bool get isBroadcast => true; | 215 bool get isBroadcast => true; |
278 } | 216 } |
279 | 217 |
280 StreamSubscription/*<T>*/ _createStreamSubscription/*<T>*/( | 218 // We would like this to just be EventListener<T> but that typdef cannot |
281 EventSubscriptionSpecification/*<T>*/ spec, Zone zone) { | |
282 return new _EventStreamSubscription/*<T>*/(spec.target, spec.eventType, | |
283 spec.onData, spec.useCapture, zone); | |
284 } | |
285 | |
286 // We would like this to just be EventListener<T> but that typedef cannot | |
287 // use generics until dartbug/26276 is fixed. | 219 // use generics until dartbug/26276 is fixed. |
288 typedef _EventListener<T extends Event>(T event); | 220 typedef _EventListener<T extends Event>(T event); |
289 | 221 |
290 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { | 222 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { |
291 int _pauseCount = 0; | 223 int _pauseCount = 0; |
292 EventTarget _target; | 224 EventTarget _target; |
293 final String _eventType; | 225 final String _eventType; |
294 EventListener _onData; | 226 EventListener _onData; |
295 EventListener _domCallback; | |
296 final bool _useCapture; | 227 final bool _useCapture; |
297 final Zone _zone; | |
298 | 228 |
299 // TODO(jacobr): for full strong mode correctness we should write | 229 // TODO(jacobr): for full strong mode correctness we should write |
300 // _onData = onData == null ? null : _wrapZone/*<dynamic, Event>*/((e) => onDa
ta(e as T)) | 230 // _onData = onData == null ? null : _wrapZone/*<Event, dynamic>*/((e) => onDa
ta(e as T)) |
301 // but that breaks 114 co19 tests as well as multiple html tests as it is reas
onable | 231 // but that breaks 114 co19 tests as well as multiple html tests as it is reas
onable |
302 // to pass the wrong type of event object to an event listener as part of a | 232 // to pass the wrong type of event object to an event listener as part of a |
303 // test. | 233 // test. |
304 _EventStreamSubscription(this._target, this._eventType, void onData(T event), | 234 _EventStreamSubscription(this._target, this._eventType, void onData(T event), |
305 this._useCapture, Zone zone) | 235 this._useCapture) : _onData = _wrapZone/*<Event, dynamic>*/(onData) { |
306 : _zone = zone, | |
307 _onData = _registerZone/*<dynamic, Event>*/(zone, onData) { | |
308 _tryResume(); | 236 _tryResume(); |
309 } | 237 } |
310 | 238 |
311 Future cancel() { | 239 Future cancel() { |
312 if (_canceled) return null; | 240 if (_canceled) return null; |
313 | 241 |
314 _unlisten(); | 242 _unlisten(); |
315 // Clear out the target to indicate this is complete. | 243 // Clear out the target to indicate this is complete. |
316 _target = null; | 244 _target = null; |
317 _onData = null; | 245 _onData = null; |
318 return null; | 246 return null; |
319 } | 247 } |
320 | 248 |
321 bool get _canceled => _target == null; | 249 bool get _canceled => _target == null; |
322 | 250 |
323 void onData(void handleData(T event)) { | 251 void onData(void handleData(T event)) { |
324 if (_canceled) { | 252 if (_canceled) { |
325 throw new StateError("Subscription has been canceled."); | 253 throw new StateError("Subscription has been canceled."); |
326 } | 254 } |
327 // Remove current event listener. | 255 // Remove current event listener. |
328 _unlisten(); | 256 _unlisten(); |
329 _onData = _registerZone/*<dynamic, Event>*/(_zone, handleData); | 257 _onData = _wrapZone/*<Event, dynamic>*/(handleData); |
330 _tryResume(); | 258 _tryResume(); |
331 } | 259 } |
332 | 260 |
333 /// Has no effect. | 261 /// Has no effect. |
334 void onError(Function handleError) {} | 262 void onError(Function handleError) {} |
335 | 263 |
336 /// Has no effect. | 264 /// Has no effect. |
337 void onDone(void handleDone()) {} | 265 void onDone(void handleDone()) {} |
338 | 266 |
339 void pause([Future resumeSignal]) { | 267 void pause([Future resumeSignal]) { |
340 if (_canceled) return; | 268 if (_canceled) return; |
341 ++_pauseCount; | 269 ++_pauseCount; |
342 _unlisten(); | 270 _unlisten(); |
343 | 271 |
344 if (resumeSignal != null) { | 272 if (resumeSignal != null) { |
345 resumeSignal.whenComplete(resume); | 273 resumeSignal.whenComplete(resume); |
346 } | 274 } |
347 } | 275 } |
348 | 276 |
349 bool get isPaused => _pauseCount > 0; | 277 bool get isPaused => _pauseCount > 0; |
350 | 278 |
351 void resume() { | 279 void resume() { |
352 if (_canceled || !isPaused) return; | 280 if (_canceled || !isPaused) return; |
353 --_pauseCount; | 281 --_pauseCount; |
354 _tryResume(); | 282 _tryResume(); |
355 } | 283 } |
356 | 284 |
357 void _tryResume() { | 285 void _tryResume() { |
358 if (_onData == null || isPaused) return; | 286 if (_onData != null && !isPaused) { |
359 if (identical(_zone, Zone.ROOT)) { | 287 _target.addEventListener(_eventType, _onData, _useCapture); |
360 _domCallback = _onData; | |
361 } else { | |
362 _domCallback = (event) { | |
363 _zone.runTask(_runEventNotification, this, event); | |
364 }; | |
365 } | 288 } |
366 _target.addEventListener(_eventType, _domCallback, _useCapture); | |
367 } | |
368 | |
369 static void _runEventNotification/*<T>*/( | |
370 _EventStreamSubscription/*<T>*/ subscription, /*=T*/ event) { | |
371 subscription._onData(event); | |
372 } | 289 } |
373 | 290 |
374 void _unlisten() { | 291 void _unlisten() { |
375 if (_onData != null) { | 292 if (_onData != null) { |
376 _target.removeEventListener(_eventType, _domCallback, _useCapture); | 293 _target.removeEventListener(_eventType, _onData, _useCapture); |
377 } | 294 } |
378 } | 295 } |
379 | 296 |
380 Future/*<E>*/ asFuture/*<E>*/([var/*=E*/ futureValue]) { | 297 Future/*<E>*/ asFuture/*<E>*/([var/*=E*/ futureValue]) { |
381 // We just need a future that will never succeed or fail. | 298 // We just need a future that will never succeed or fail. |
382 var completer = new Completer/*<E>*/(); | 299 var completer = new Completer/*<E>*/(); |
383 return completer.future; | 300 return completer.future; |
384 } | 301 } |
385 } | 302 } |
386 | 303 |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
520 return new _ElementListEventStreamImpl<T>(e, _eventTypeGetter(e), useCapture
); | 437 return new _ElementListEventStreamImpl<T>(e, _eventTypeGetter(e), useCapture
); |
521 } | 438 } |
522 | 439 |
523 String getEventType(EventTarget target) { | 440 String getEventType(EventTarget target) { |
524 return _eventTypeGetter(target); | 441 return _eventTypeGetter(target); |
525 } | 442 } |
526 | 443 |
527 String get _eventType => | 444 String get _eventType => |
528 throw new UnsupportedError('Access type through getEventType method.'); | 445 throw new UnsupportedError('Access type through getEventType method.'); |
529 } | 446 } |
OLD | NEW |