Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: tools/dom/src/EventStreamProvider.dart

Issue 21607003: Add Event delegation to Elements and groups of elements. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 * Adapter for exposing DOM events as Dart streams.
9 */ 9 */
10 class _EventStream<T extends Event> extends Stream<T> { 10 class _EventStream<T extends Event> extends Stream<T> {
(...skipping 12 matching lines...) Expand all
23 StreamSubscription<T> listen(void onData(T event), 23 StreamSubscription<T> listen(void onData(T event),
24 { void onError(error), 24 { void onError(error),
25 void onDone(), 25 void onDone(),
26 bool cancelOnError}) { 26 bool cancelOnError}) {
27 27
28 return new _EventStreamSubscription<T>( 28 return new _EventStreamSubscription<T>(
29 this._target, this._eventType, onData, this._useCapture); 29 this._target, this._eventType, onData, this._useCapture);
30 } 30 }
31 } 31 }
32 32
33 /** A specialized Stream available to [Element]s to enable event delegation. */
34 abstract class ElementStream<T extends Event> implements Stream<T> {
35 /**
36 * Return a stream that only fires when the particular event fires for
37 * elements matching the specified CSS selector.
38 *
39 * This is the Dart equivalent to jQuery's
40 * [delegate](http://api.jquery.com/delegate/).
41 */
42 Stream<T> matches(String selector);
43 }
44
45 /**
46 * Adapter for exposing DOM Element events as streams, while also allowing
47 * event delegation.
48 */
49 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
50 implements ElementStream<T> {
51 _ElementEventStreamImpl(target, eventType, useCapture) :
52 super(target, eventType, useCapture);
53
54 Stream<T> matches(String selector) =>
55 this.where((event) => event.target.matches(selector, true));
56 }
57
58 /**
59 * Adapter for exposing events on a collection of DOM Elements as streams,
60 * while also allowing event delegation.
61 */
62 class _ElementListEventStreamImpl<T extends Event> extends Stream<T>
63 implements ElementStream<T> {
64 final _StreamPool _pool;
65 Stream<T> _stream;
66
67 _ElementListEventStreamImpl(targetList, eventType, useCapture) :
68 _pool = new _StreamPool.broadcast() {
69 for (Element target in targetList) {
70 var stream = new _EventStream(target, eventType, useCapture);
71 _pool.add(stream);
72 }
73 _stream = _pool.stream;
74 }
75
76 Stream<T> matches(String selector) =>
77 this.where((event) => event.target.matches(selector, true));
78
79 // Delegate all regular Stream behavor to our wrapped Stream.
80 StreamSubscription<T> listen(void onData(T event),
81 { void onError(error),
82 void onDone(),
83 bool cancelOnError}) =>
84 _stream.listen(onData, onError: onError, onDone: onDone,
85 cancelOnError: cancelOnError);
86 Stream<T> asBroadcastStream({void onListen(StreamSubscription subscription),
87 void onCancel(StreamSubscription subscription)})
88 => _stream;
89 bool get isBroadcast => true;
90 }
91
92 /**
93 * A pool of streams whose events are unified and emitted through a central
94 * stream.
95 */
96 // TODO (efortuna): Remove this when Issue 12218 is addressed.
97 class _StreamPool<T> {
98 StreamController<T> _controller;
99
100 /// Subscriptions to the streams that make up the pool.
101 var _subscriptions = new Map<Stream<T>, StreamSubscription<T>>();
102
103 /**
104 * Creates a new stream pool where [stream] can be listened to more than
105 * once.
106 *
107 * Any events from buffered streams in the pool will be emitted immediately,
108 * regardless of whether [stream] has any subscribers.
109 */
110 _StreamPool.broadcast() {
111 _controller = new StreamController<T>.broadcast(sync: true,
112 onCancel: close);
113 }
114
115 /**
116 * The stream through which all events from streams in the pool are emitted.
117 */
118 Stream<T> get stream => _controller.stream;
119
120 /**
121 * Adds [stream] as a member of this pool.
122 *
123 * Any events from [stream] will be emitted through [this.stream]. If
124 * [stream] is sync, they'll be emitted synchronously; if [stream] is async,
125 * they'll be emitted asynchronously.
126 */
127 void add(Stream<T> stream) {
128 if (_subscriptions.containsKey(stream)) return;
129 _subscriptions[stream] = stream.listen(_controller.add,
130 onError: _controller.addError,
131 onDone: () => remove(stream));
132 }
133
134 /** Removes [stream] as a member of this pool. */
135 void remove(Stream<T> stream) {
136 var subscription = _subscriptions.remove(stream);
137 if (subscription != null) subscription.cancel();
138 }
139
140 /** Removes all streams from this pool and closes [stream]. */
141 void close() {
142 for (var subscription in _subscriptions.values) {
143 subscription.cancel();
144 }
145 _subscriptions.clear();
146 _controller.close();
147 }
148 }
149
33 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { 150 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
34 int _pauseCount = 0; 151 int _pauseCount = 0;
35 EventTarget _target; 152 EventTarget _target;
36 final String _eventType; 153 final String _eventType;
37 var _onData; 154 var _onData;
38 final bool _useCapture; 155 final bool _useCapture;
39 156
40 _EventStreamSubscription(this._target, this._eventType, this._onData, 157 _EventStreamSubscription(this._target, this._eventType, this._onData,
41 this._useCapture) { 158 this._useCapture) {
42 _tryResume(); 159 _tryResume();
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
100 } 217 }
101 } 218 }
102 219
103 Future asFuture([var futureValue]) { 220 Future asFuture([var futureValue]) {
104 // We just need a future that will never succeed or fail. 221 // We just need a future that will never succeed or fail.
105 Completer completer = new Completer(); 222 Completer completer = new Completer();
106 return completer.future; 223 return completer.future;
107 } 224 }
108 } 225 }
109 226
110
111 /** 227 /**
112 * A factory to expose DOM events as Streams. 228 * A factory to expose DOM events as Streams.
113 */ 229 */
114 class EventStreamProvider<T extends Event> { 230 class EventStreamProvider<T extends Event> {
115 final String _eventType; 231 final String _eventType;
116 232
117 const EventStreamProvider(this._eventType); 233 const EventStreamProvider(this._eventType);
118 234
119 /** 235 /**
120 * Gets a [Stream] for this event type, on the specified target. 236 * Gets a [Stream] for this event type, on the specified target.
121 * 237 *
122 * This will always return a broadcast stream so multiple listeners can be 238 * This will always return a broadcast stream so multiple listeners can be
123 * used simultaneously. 239 * used simultaneously.
124 * 240 *
125 * This may be used to capture DOM events: 241 * This may be used to capture DOM events:
126 * 242 *
127 * Element.keyDownEvent.forTarget(element, useCapture: true).listen(...); 243 * Element.keyDownEvent.forTarget(element, useCapture: true).listen(...);
128 * 244 *
129 * Or for listening to an event which will bubble through the DOM tree: 245 * Or for listening to an event which will bubble through the DOM tree:
130 * 246 *
131 * MediaElement.pauseEvent.forTarget(document.body).listen(...); 247 * MediaElement.pauseEvent.forTarget(document.body).listen(...);
132 * 248 *
133 * See also: 249 * See also:
134 * 250 *
135 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis tener) 251 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis tener)
136 */ 252 */
137 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { 253 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) =>
138 return new _EventStream(e, _eventType, useCapture); 254 new _EventStream(e, _eventType, useCapture);
255
256 /**
257 * Gets an [ElementEventStream] for this event type, on the specified element.
258 *
259 * This will always return a broadcast stream so multiple listeners can be
260 * used simultaneously.
261 *
262 * This may be used to capture DOM events:
263 *
264 * Element.keyDownEvent.forElement(element, useCapture: true).listen(...);
265 *
266 * See also:
267 *
268 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis tener)
269 */
270 ElementStream<T> forElement(Element e, {bool useCapture: false}) {
271 return new _ElementEventStreamImpl(e, _eventType, useCapture);
139 } 272 }
140 273
141 /** 274 /**
275 * Gets an [ElementEventStream] for this event type, on the list of elements.
276 *
277 * This will always return a broadcast stream so multiple listeners can be
278 * used simultaneously.
279 *
280 * This may be used to capture DOM events:
281 *
282 * Element.keyDownEvent._forElementList(element, useCapture: true).listen( ...);
283 *
284 * See also:
285 *
286 * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventLis tener)
287 */
288 ElementStream<T> _forElementList(ElementList e, {bool useCapture: false}) {
289 return new _ElementListEventStreamImpl(e, _eventType, useCapture);
290 }
291
292 /**
142 * Gets the type of the event which this would listen for on the specified 293 * Gets the type of the event which this would listen for on the specified
143 * event target. 294 * event target.
144 * 295 *
145 * The target is necessary because some browsers may use different event names 296 * The target is necessary because some browsers may use different event names
146 * for the same purpose and the target allows differentiating browser support. 297 * for the same purpose and the target allows differentiating browser support.
147 */ 298 */
148 String getEventType(EventTarget target) { 299 String getEventType(EventTarget target) {
149 return _eventType; 300 return _eventType;
150 } 301 }
151 } 302 }
152 303
153 /** 304 /**
154 * A factory to expose DOM events as streams, where the DOM event name has to 305 * 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). 306 * be determined on the fly (for example, mouse wheel events).
156 */ 307 */
157 class _CustomEventStreamProvider<T extends Event> 308 class _CustomEventStreamProvider<T extends Event>
158 implements EventStreamProvider<T> { 309 implements EventStreamProvider<T> {
159 310
160 final _eventTypeGetter; 311 final _eventTypeGetter;
161 const _CustomEventStreamProvider(this._eventTypeGetter); 312 const _CustomEventStreamProvider(this._eventTypeGetter);
162 313
163 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { 314 Stream<T> forTarget(EventTarget e, {bool useCapture: false}) {
164 return new _EventStream(e, _eventTypeGetter(e), useCapture); 315 return new _EventStream(e, _eventTypeGetter(e), useCapture);
165 } 316 }
166 317
318 ElementStream<T> forElement(Element e, {bool useCapture: false}) {
319 return new _ElementEventStreamImpl(e, _eventTypeGetter(e), useCapture);
320 }
321
322 ElementStream<T> _forElementList(ElementList e,
323 {bool useCapture: false}) {
324 return new _ElementListEventStreamImpl(e, _eventTypeGetter(e), useCapture);
325 }
326
167 String getEventType(EventTarget target) { 327 String getEventType(EventTarget target) {
168 return _eventTypeGetter(target); 328 return _eventTypeGetter(target);
169 } 329 }
170 } 330 }
OLDNEW
« no previous file with comments | « tools/dom/scripts/systemhtml.py ('k') | tools/dom/templates/html/impl/impl_Element.darttemplate » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698