Index: tools/dom/src/EventStreamProvider.dart |
diff --git a/tools/dom/src/EventStreamProvider.dart b/tools/dom/src/EventStreamProvider.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9c52a5dd7241f2f03d9b9c863e89c67b7fb64186 |
--- /dev/null |
+++ b/tools/dom/src/EventStreamProvider.dart |
@@ -0,0 +1,137 @@ |
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+part of html; |
+ |
+/** |
+ * Adapter for exposing DOM events as Dart streams. |
+ */ |
+class _EventStream<T extends Event> extends Stream<T> { |
+ final EventTarget _target; |
+ final String _eventType; |
+ final bool _useCapture; |
+ |
+ _EventStream(this._target, this._eventType, this._useCapture); |
+ |
+ // DOM events are inherently multi-subscribers. |
+ Stream<T> asMultiSubscriberStream() => this; |
+ |
+ StreamSubscription<T> listen(void onData(T event), |
+ { void onError(AsyncError error), |
+ void onDone(), |
+ bool unsubscribeOnError}) { |
+ |
+ return new _EventStreamSubscription<T>( |
+ this._target, this._eventType, onData, this._useCapture); |
+ } |
+} |
+ |
+class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { |
+ int _pauseCount = 0; |
+ EventTarget _target; |
+ final String _eventType; |
+ var _onData; |
+ final bool _useCapture; |
+ |
+ _EventStreamSubscription(this._target, this._eventType, this._onData, |
+ this._useCapture) { |
+ _tryResume(); |
+ } |
+ |
+ void cancel() { |
+ if (_canceled) { |
+ throw new StateError("Subscription has been canceled."); |
+ } |
+ |
+ _unlisten(); |
+ // Clear out the target to indicate this is complete. |
+ _target = null; |
+ _onData = null; |
+ } |
+ |
+ bool get _canceled => _target == null; |
+ |
+ void onData(void handleData(T event)) { |
+ if (_canceled) { |
+ throw new StateError("Subscription has been canceled."); |
+ } |
+ // Remove current event listener. |
+ _unlisten(); |
+ |
+ _onData = handleData; |
+ _tryResume(); |
+ } |
+ |
+ /// Has no effect. |
+ void onError(void handleError(AsyncError error)) {} |
+ |
+ /// Has no effect. |
+ void onDone(void handleDone()) {} |
+ |
+ void pause([Future resumeSignal]) { |
+ if (_canceled) { |
+ throw new StateError("Subscription has been canceled."); |
+ } |
+ ++_pauseCount; |
+ _unlisten(); |
+ |
+ if (resumeSignal != null) { |
+ resumeSignal.whenComplete(resume); |
+ } |
+ } |
+ |
+ bool get _paused => _pauseCount > 0; |
+ |
+ void resume() { |
+ if (_canceled) { |
+ throw new StateError("Subscription has been canceled."); |
+ } |
+ if (!_paused) { |
+ throw new StateError("Subscription is not paused."); |
+ } |
+ --_pauseCount; |
+ _tryResume(); |
+ } |
+ |
+ void _tryResume() { |
+ if (_onData != null && !_paused) { |
+ _target.$dom_addEventListener(_eventType, _onData, _useCapture); |
+ } |
+ } |
+ |
+ void _unlisten() { |
+ if (_onData != null) { |
+ _target.$dom_removeEventListener(_eventType, _onData, _useCapture); |
+ } |
+ } |
+} |
+ |
+ |
+/** |
+ * A factory to expose DOM events as Streams. |
+ */ |
+class EventStreamProvider<T extends Event> { |
+ final String _eventType; |
+ |
+ const EventStreamProvider(this._eventType); |
+ |
+ /** |
+ * Gets a [Stream] for this event type, on the specified target. |
+ * |
+ * This may be used to capture DOM events: |
+ * |
+ * Element.keyDownEvent.forTarget(element, useCapture: true).listen(...); |
+ * |
+ * Or for listening to an event which will bubble through the DOM tree: |
+ * |
+ * MediaElement.pauseEvent.forTarget(document.body).listen(...); |
+ * |
+ * See also: |
+ * |
+ * [addEventListener](http://docs.webplatform.org/wiki/dom/methods/addEventListener) |
+ */ |
+ Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { |
+ return new _EventStream(e, _eventType, useCapture); |
+ } |
+} |