blob: a74e998ea816542d8bf6409af38891ab5d694bf8 [file] [log] [blame]
// 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(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);
}
/**
* 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);
}
}