| // 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> asBroadcastStream() => this; |
| bool get isBroadcast => true; |
| |
| StreamSubscription<T> listen(void onData(T event), |
| { void onError(error), |
| void onDone(), |
| bool cancelOnError}) { |
| |
| 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) return; |
| |
| _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(error)) {} |
| |
| /// Has no effect. |
| void onDone(void handleDone()) {} |
| |
| void pause([Future resumeSignal]) { |
| if (_canceled) return; |
| ++_pauseCount; |
| _unlisten(); |
| |
| if (resumeSignal != null) { |
| resumeSignal.whenComplete(resume); |
| } |
| } |
| |
| bool get isPaused => _pauseCount > 0; |
| |
| void resume() { |
| if (_canceled || !isPaused) return; |
| --_pauseCount; |
| _tryResume(); |
| } |
| |
| void _tryResume() { |
| if (_onData != null && !isPaused) { |
| _target.$dom_addEventListener(_eventType, _onData, _useCapture); |
| } |
| } |
| |
| void _unlisten() { |
| if (_onData != null) { |
| _target.$dom_removeEventListener(_eventType, _onData, _useCapture); |
| } |
| } |
| |
| Future asFuture([var futureValue]) { |
| // We just need a future that will never succeed or fail. |
| Completer completer = new Completer(); |
| return completer.future; |
| } |
| } |
| |
| |
| /** |
| * 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 will always return a broadcast stream so multiple listeners can be |
| * used simultaneously. |
| * |
| * 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); |
| } |
| |
| /** |
| * Gets the type of the event which this would listen for on the specified |
| * event target. |
| * |
| * The target is necessary because some browsers may use different event names |
| * for the same purpose and the target allows differentiating browser support. |
| */ |
| String getEventType(EventTarget target) { |
| return _eventType; |
| } |
| } |
| |
| /** |
| * A factory to expose DOM events as streams, where the DOM event name has to |
| * be determined on the fly (for example, mouse wheel events). |
| */ |
| class _CustomEventStreamProvider<T extends Event> |
| implements EventStreamProvider<T> { |
| |
| final _eventTypeGetter; |
| const _CustomEventStreamProvider(this._eventTypeGetter); |
| |
| Stream<T> forTarget(EventTarget e, {bool useCapture: false}) { |
| return new _EventStream(e, _eventTypeGetter(e), useCapture); |
| } |
| |
| String getEventType(EventTarget target) { |
| return _eventTypeGetter(target); |
| } |
| } |