216 lines
10 KiB
JavaScript
216 lines
10 KiB
JavaScript
|
"use strict";
|
||
|
var __extends = (this && this.__extends) || function (d, b) {
|
||
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
||
|
function __() { this.constructor = d; }
|
||
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||
|
};
|
||
|
var Observable_1 = require('../Observable');
|
||
|
var tryCatch_1 = require('../util/tryCatch');
|
||
|
var isFunction_1 = require('../util/isFunction');
|
||
|
var errorObject_1 = require('../util/errorObject');
|
||
|
var Subscription_1 = require('../Subscription');
|
||
|
var toString = Object.prototype.toString;
|
||
|
function isNodeStyleEventEmitter(sourceObj) {
|
||
|
return !!sourceObj && typeof sourceObj.addListener === 'function' && typeof sourceObj.removeListener === 'function';
|
||
|
}
|
||
|
function isJQueryStyleEventEmitter(sourceObj) {
|
||
|
return !!sourceObj && typeof sourceObj.on === 'function' && typeof sourceObj.off === 'function';
|
||
|
}
|
||
|
function isNodeList(sourceObj) {
|
||
|
return !!sourceObj && toString.call(sourceObj) === '[object NodeList]';
|
||
|
}
|
||
|
function isHTMLCollection(sourceObj) {
|
||
|
return !!sourceObj && toString.call(sourceObj) === '[object HTMLCollection]';
|
||
|
}
|
||
|
function isEventTarget(sourceObj) {
|
||
|
return !!sourceObj && typeof sourceObj.addEventListener === 'function' && typeof sourceObj.removeEventListener === 'function';
|
||
|
}
|
||
|
/**
|
||
|
* We need this JSDoc comment for affecting ESDoc.
|
||
|
* @extends {Ignored}
|
||
|
* @hide true
|
||
|
*/
|
||
|
var FromEventObservable = (function (_super) {
|
||
|
__extends(FromEventObservable, _super);
|
||
|
function FromEventObservable(sourceObj, eventName, selector, options) {
|
||
|
_super.call(this);
|
||
|
this.sourceObj = sourceObj;
|
||
|
this.eventName = eventName;
|
||
|
this.selector = selector;
|
||
|
this.options = options;
|
||
|
}
|
||
|
/* tslint:enable:max-line-length */
|
||
|
/**
|
||
|
* Creates an Observable that emits events of a specific type coming from the
|
||
|
* given event target.
|
||
|
*
|
||
|
* <span class="informal">Creates an Observable from DOM events, or Node.js
|
||
|
* EventEmitter events or others.</span>
|
||
|
*
|
||
|
* <img src="./img/fromEvent.png" width="100%">
|
||
|
*
|
||
|
* `fromEvent` accepts as a first argument event target, which is an object with methods
|
||
|
* for registering event handler functions. As a second argument it takes string that indicates
|
||
|
* type of event we want to listen for. `fromEvent` supports selected types of event targets,
|
||
|
* which are described in detail below. If your event target does not match any of the ones listed,
|
||
|
* you should use {@link fromEventPattern}, which can be used on arbitrary APIs.
|
||
|
* When it comes to APIs supported by `fromEvent`, their methods for adding and removing event
|
||
|
* handler functions have different names, but they all accept a string describing event type
|
||
|
* and function itself, which will be called whenever said event happens.
|
||
|
*
|
||
|
* Every time resulting Observable is subscribed, event handler function will be registered
|
||
|
* to event target on given event type. When that event fires, value
|
||
|
* passed as a first argument to registered function will be emitted by output Observable.
|
||
|
* When Observable is unsubscribed, function will be unregistered from event target.
|
||
|
*
|
||
|
* Note that if event target calls registered function with more than one argument, second
|
||
|
* and following arguments will not appear in resulting stream. In order to get access to them,
|
||
|
* you can pass to `fromEvent` optional project function, which will be called with all arguments
|
||
|
* passed to event handler. Output Observable will then emit value returned by project function,
|
||
|
* instead of the usual value.
|
||
|
*
|
||
|
* Remember that event targets listed below are checked via duck typing. It means that
|
||
|
* no matter what kind of object you have and no matter what environment you work in,
|
||
|
* you can safely use `fromEvent` on that object if it exposes described methods (provided
|
||
|
* of course they behave as was described above). So for example if Node.js library exposes
|
||
|
* event target which has the same method names as DOM EventTarget, `fromEvent` is still
|
||
|
* a good choice.
|
||
|
*
|
||
|
* If the API you use is more callback then event handler oriented (subscribed
|
||
|
* callback function fires only once and thus there is no need to manually
|
||
|
* unregister it), you should use {@link bindCallback} or {@link bindNodeCallback}
|
||
|
* instead.
|
||
|
*
|
||
|
* `fromEvent` supports following types of event targets:
|
||
|
*
|
||
|
* **DOM EventTarget**
|
||
|
*
|
||
|
* This is an object with `addEventListener` and `removeEventListener` methods.
|
||
|
*
|
||
|
* In the browser, `addEventListener` accepts - apart from event type string and event
|
||
|
* handler function arguments - optional third parameter, which is either an object or boolean,
|
||
|
* both used for additional configuration how and when passed function will be called. When
|
||
|
* `fromEvent` is used with event target of that type, you can provide this values
|
||
|
* as third parameter as well.
|
||
|
*
|
||
|
* **Node.js EventEmitter**
|
||
|
*
|
||
|
* An object with `addListener` and `removeListener` methods.
|
||
|
*
|
||
|
* **JQuery-style event target**
|
||
|
*
|
||
|
* An object with `on` and `off` methods
|
||
|
*
|
||
|
* **DOM NodeList**
|
||
|
*
|
||
|
* List of DOM Nodes, returned for example by `document.querySelectorAll` or `Node.childNodes`.
|
||
|
*
|
||
|
* Although this collection is not event target in itself, `fromEvent` will iterate over all Nodes
|
||
|
* it contains and install event handler function in every of them. When returned Observable
|
||
|
* is unsubscribed, function will be removed from all Nodes.
|
||
|
*
|
||
|
* **DOM HtmlCollection**
|
||
|
*
|
||
|
* Just as in case of NodeList it is a collection of DOM nodes. Here as well event handler function is
|
||
|
* installed and removed in each of elements.
|
||
|
*
|
||
|
*
|
||
|
* @example <caption>Emits clicks happening on the DOM document</caption>
|
||
|
* var clicks = Rx.Observable.fromEvent(document, 'click');
|
||
|
* clicks.subscribe(x => console.log(x));
|
||
|
*
|
||
|
* // Results in:
|
||
|
* // MouseEvent object logged to console every time a click
|
||
|
* // occurs on the document.
|
||
|
*
|
||
|
*
|
||
|
* @example <caption>Use addEventListener with capture option</caption>
|
||
|
* var clicksInDocument = Rx.Observable.fromEvent(document, 'click', true); // note optional configuration parameter
|
||
|
* // which will be passed to addEventListener
|
||
|
* var clicksInDiv = Rx.Observable.fromEvent(someDivInDocument, 'click');
|
||
|
*
|
||
|
* clicksInDocument.subscribe(() => console.log('document'));
|
||
|
* clicksInDiv.subscribe(() => console.log('div'));
|
||
|
*
|
||
|
* // By default events bubble UP in DOM tree, so normally
|
||
|
* // when we would click on div in document
|
||
|
* // "div" would be logged first and then "document".
|
||
|
* // Since we specified optional `capture` option, document
|
||
|
* // will catch event when it goes DOWN DOM tree, so console
|
||
|
* // will log "document" and then "div".
|
||
|
*
|
||
|
* @see {@link bindCallback}
|
||
|
* @see {@link bindNodeCallback}
|
||
|
* @see {@link fromEventPattern}
|
||
|
*
|
||
|
* @param {EventTargetLike} target The DOM EventTarget, Node.js
|
||
|
* EventEmitter, JQuery-like event target, NodeList or HTMLCollection to attach the event handler to.
|
||
|
* @param {string} eventName The event name of interest, being emitted by the
|
||
|
* `target`.
|
||
|
* @param {EventListenerOptions} [options] Options to pass through to addEventListener
|
||
|
* @param {SelectorMethodSignature<T>} [selector] An optional function to
|
||
|
* post-process results. It takes the arguments from the event handler and
|
||
|
* should return a single value.
|
||
|
* @return {Observable<T>}
|
||
|
* @static true
|
||
|
* @name fromEvent
|
||
|
* @owner Observable
|
||
|
*/
|
||
|
FromEventObservable.create = function (target, eventName, options, selector) {
|
||
|
if (isFunction_1.isFunction(options)) {
|
||
|
selector = options;
|
||
|
options = undefined;
|
||
|
}
|
||
|
return new FromEventObservable(target, eventName, selector, options);
|
||
|
};
|
||
|
FromEventObservable.setupSubscription = function (sourceObj, eventName, handler, subscriber, options) {
|
||
|
var unsubscribe;
|
||
|
if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
|
||
|
for (var i = 0, len = sourceObj.length; i < len; i++) {
|
||
|
FromEventObservable.setupSubscription(sourceObj[i], eventName, handler, subscriber, options);
|
||
|
}
|
||
|
}
|
||
|
else if (isEventTarget(sourceObj)) {
|
||
|
var source_1 = sourceObj;
|
||
|
sourceObj.addEventListener(eventName, handler, options);
|
||
|
unsubscribe = function () { return source_1.removeEventListener(eventName, handler, options); };
|
||
|
}
|
||
|
else if (isJQueryStyleEventEmitter(sourceObj)) {
|
||
|
var source_2 = sourceObj;
|
||
|
sourceObj.on(eventName, handler);
|
||
|
unsubscribe = function () { return source_2.off(eventName, handler); };
|
||
|
}
|
||
|
else if (isNodeStyleEventEmitter(sourceObj)) {
|
||
|
var source_3 = sourceObj;
|
||
|
sourceObj.addListener(eventName, handler);
|
||
|
unsubscribe = function () { return source_3.removeListener(eventName, handler); };
|
||
|
}
|
||
|
else {
|
||
|
throw new TypeError('Invalid event target');
|
||
|
}
|
||
|
subscriber.add(new Subscription_1.Subscription(unsubscribe));
|
||
|
};
|
||
|
/** @deprecated internal use only */ FromEventObservable.prototype._subscribe = function (subscriber) {
|
||
|
var sourceObj = this.sourceObj;
|
||
|
var eventName = this.eventName;
|
||
|
var options = this.options;
|
||
|
var selector = this.selector;
|
||
|
var handler = selector ? function () {
|
||
|
var args = [];
|
||
|
for (var _i = 0; _i < arguments.length; _i++) {
|
||
|
args[_i - 0] = arguments[_i];
|
||
|
}
|
||
|
var result = tryCatch_1.tryCatch(selector).apply(void 0, args);
|
||
|
if (result === errorObject_1.errorObject) {
|
||
|
subscriber.error(errorObject_1.errorObject.e);
|
||
|
}
|
||
|
else {
|
||
|
subscriber.next(result);
|
||
|
}
|
||
|
} : function (e) { return subscriber.next(e); };
|
||
|
FromEventObservable.setupSubscription(sourceObj, eventName, handler, subscriber, options);
|
||
|
};
|
||
|
return FromEventObservable;
|
||
|
}(Observable_1.Observable));
|
||
|
exports.FromEventObservable = FromEventObservable;
|
||
|
//# sourceMappingURL=FromEventObservable.js.map
|