FromEventObservable.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /** PURE_IMPORTS_START .._Observable,.._util_tryCatch,.._util_isFunction,.._util_errorObject,.._Subscription PURE_IMPORTS_END */
  2. var __extends = (this && this.__extends) || function (d, b) {
  3. for (var p in b)
  4. if (b.hasOwnProperty(p))
  5. d[p] = b[p];
  6. function __() { this.constructor = d; }
  7. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  8. };
  9. import { Observable } from '../Observable';
  10. import { tryCatch } from '../util/tryCatch';
  11. import { isFunction } from '../util/isFunction';
  12. import { errorObject } from '../util/errorObject';
  13. import { Subscription } from '../Subscription';
  14. var toString = Object.prototype.toString;
  15. function isNodeStyleEventEmitter(sourceObj) {
  16. return !!sourceObj && typeof sourceObj.addListener === 'function' && typeof sourceObj.removeListener === 'function';
  17. }
  18. function isJQueryStyleEventEmitter(sourceObj) {
  19. return !!sourceObj && typeof sourceObj.on === 'function' && typeof sourceObj.off === 'function';
  20. }
  21. function isNodeList(sourceObj) {
  22. return !!sourceObj && toString.call(sourceObj) === '[object NodeList]';
  23. }
  24. function isHTMLCollection(sourceObj) {
  25. return !!sourceObj && toString.call(sourceObj) === '[object HTMLCollection]';
  26. }
  27. function isEventTarget(sourceObj) {
  28. return !!sourceObj && typeof sourceObj.addEventListener === 'function' && typeof sourceObj.removeEventListener === 'function';
  29. }
  30. /**
  31. * We need this JSDoc comment for affecting ESDoc.
  32. * @extends {Ignored}
  33. * @hide true
  34. */
  35. export var FromEventObservable = /*@__PURE__*/ (/*@__PURE__*/ function (_super) {
  36. __extends(FromEventObservable, _super);
  37. function FromEventObservable(sourceObj, eventName, selector, options) {
  38. _super.call(this);
  39. this.sourceObj = sourceObj;
  40. this.eventName = eventName;
  41. this.selector = selector;
  42. this.options = options;
  43. }
  44. /* tslint:enable:max-line-length */
  45. /**
  46. * Creates an Observable that emits events of a specific type coming from the
  47. * given event target.
  48. *
  49. * <span class="informal">Creates an Observable from DOM events, or Node.js
  50. * EventEmitter events or others.</span>
  51. *
  52. * <img src="./img/fromEvent.png" width="100%">
  53. *
  54. * `fromEvent` accepts as a first argument event target, which is an object with methods
  55. * for registering event handler functions. As a second argument it takes string that indicates
  56. * type of event we want to listen for. `fromEvent` supports selected types of event targets,
  57. * which are described in detail below. If your event target does not match any of the ones listed,
  58. * you should use {@link fromEventPattern}, which can be used on arbitrary APIs.
  59. * When it comes to APIs supported by `fromEvent`, their methods for adding and removing event
  60. * handler functions have different names, but they all accept a string describing event type
  61. * and function itself, which will be called whenever said event happens.
  62. *
  63. * Every time resulting Observable is subscribed, event handler function will be registered
  64. * to event target on given event type. When that event fires, value
  65. * passed as a first argument to registered function will be emitted by output Observable.
  66. * When Observable is unsubscribed, function will be unregistered from event target.
  67. *
  68. * Note that if event target calls registered function with more than one argument, second
  69. * and following arguments will not appear in resulting stream. In order to get access to them,
  70. * you can pass to `fromEvent` optional project function, which will be called with all arguments
  71. * passed to event handler. Output Observable will then emit value returned by project function,
  72. * instead of the usual value.
  73. *
  74. * Remember that event targets listed below are checked via duck typing. It means that
  75. * no matter what kind of object you have and no matter what environment you work in,
  76. * you can safely use `fromEvent` on that object if it exposes described methods (provided
  77. * of course they behave as was described above). So for example if Node.js library exposes
  78. * event target which has the same method names as DOM EventTarget, `fromEvent` is still
  79. * a good choice.
  80. *
  81. * If the API you use is more callback then event handler oriented (subscribed
  82. * callback function fires only once and thus there is no need to manually
  83. * unregister it), you should use {@link bindCallback} or {@link bindNodeCallback}
  84. * instead.
  85. *
  86. * `fromEvent` supports following types of event targets:
  87. *
  88. * **DOM EventTarget**
  89. *
  90. * This is an object with `addEventListener` and `removeEventListener` methods.
  91. *
  92. * In the browser, `addEventListener` accepts - apart from event type string and event
  93. * handler function arguments - optional third parameter, which is either an object or boolean,
  94. * both used for additional configuration how and when passed function will be called. When
  95. * `fromEvent` is used with event target of that type, you can provide this values
  96. * as third parameter as well.
  97. *
  98. * **Node.js EventEmitter**
  99. *
  100. * An object with `addListener` and `removeListener` methods.
  101. *
  102. * **JQuery-style event target**
  103. *
  104. * An object with `on` and `off` methods
  105. *
  106. * **DOM NodeList**
  107. *
  108. * List of DOM Nodes, returned for example by `document.querySelectorAll` or `Node.childNodes`.
  109. *
  110. * Although this collection is not event target in itself, `fromEvent` will iterate over all Nodes
  111. * it contains and install event handler function in every of them. When returned Observable
  112. * is unsubscribed, function will be removed from all Nodes.
  113. *
  114. * **DOM HtmlCollection**
  115. *
  116. * Just as in case of NodeList it is a collection of DOM nodes. Here as well event handler function is
  117. * installed and removed in each of elements.
  118. *
  119. *
  120. * @example <caption>Emits clicks happening on the DOM document</caption>
  121. * var clicks = Rx.Observable.fromEvent(document, 'click');
  122. * clicks.subscribe(x => console.log(x));
  123. *
  124. * // Results in:
  125. * // MouseEvent object logged to console every time a click
  126. * // occurs on the document.
  127. *
  128. *
  129. * @example <caption>Use addEventListener with capture option</caption>
  130. * var clicksInDocument = Rx.Observable.fromEvent(document, 'click', true); // note optional configuration parameter
  131. * // which will be passed to addEventListener
  132. * var clicksInDiv = Rx.Observable.fromEvent(someDivInDocument, 'click');
  133. *
  134. * clicksInDocument.subscribe(() => console.log('document'));
  135. * clicksInDiv.subscribe(() => console.log('div'));
  136. *
  137. * // By default events bubble UP in DOM tree, so normally
  138. * // when we would click on div in document
  139. * // "div" would be logged first and then "document".
  140. * // Since we specified optional `capture` option, document
  141. * // will catch event when it goes DOWN DOM tree, so console
  142. * // will log "document" and then "div".
  143. *
  144. * @see {@link bindCallback}
  145. * @see {@link bindNodeCallback}
  146. * @see {@link fromEventPattern}
  147. *
  148. * @param {EventTargetLike} target The DOM EventTarget, Node.js
  149. * EventEmitter, JQuery-like event target, NodeList or HTMLCollection to attach the event handler to.
  150. * @param {string} eventName The event name of interest, being emitted by the
  151. * `target`.
  152. * @param {EventListenerOptions} [options] Options to pass through to addEventListener
  153. * @param {SelectorMethodSignature<T>} [selector] An optional function to
  154. * post-process results. It takes the arguments from the event handler and
  155. * should return a single value.
  156. * @return {Observable<T>}
  157. * @static true
  158. * @name fromEvent
  159. * @owner Observable
  160. */
  161. FromEventObservable.create = function (target, eventName, options, selector) {
  162. if (isFunction(options)) {
  163. selector = options;
  164. options = undefined;
  165. }
  166. return new FromEventObservable(target, eventName, selector, options);
  167. };
  168. FromEventObservable.setupSubscription = function (sourceObj, eventName, handler, subscriber, options) {
  169. var unsubscribe;
  170. if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
  171. for (var i = 0, len = sourceObj.length; i < len; i++) {
  172. FromEventObservable.setupSubscription(sourceObj[i], eventName, handler, subscriber, options);
  173. }
  174. }
  175. else if (isEventTarget(sourceObj)) {
  176. var source_1 = sourceObj;
  177. sourceObj.addEventListener(eventName, handler, options);
  178. unsubscribe = function () { return source_1.removeEventListener(eventName, handler, options); };
  179. }
  180. else if (isJQueryStyleEventEmitter(sourceObj)) {
  181. var source_2 = sourceObj;
  182. sourceObj.on(eventName, handler);
  183. unsubscribe = function () { return source_2.off(eventName, handler); };
  184. }
  185. else if (isNodeStyleEventEmitter(sourceObj)) {
  186. var source_3 = sourceObj;
  187. sourceObj.addListener(eventName, handler);
  188. unsubscribe = function () { return source_3.removeListener(eventName, handler); };
  189. }
  190. else {
  191. throw new TypeError('Invalid event target');
  192. }
  193. subscriber.add(new Subscription(unsubscribe));
  194. };
  195. /** @deprecated internal use only */ FromEventObservable.prototype._subscribe = function (subscriber) {
  196. var sourceObj = this.sourceObj;
  197. var eventName = this.eventName;
  198. var options = this.options;
  199. var selector = this.selector;
  200. var handler = selector ? function () {
  201. var args = [];
  202. for (var _i = 0; _i < arguments.length; _i++) {
  203. args[_i - 0] = arguments[_i];
  204. }
  205. var result = tryCatch(selector).apply(void 0, args);
  206. if (result === errorObject) {
  207. subscriber.error(errorObject.e);
  208. }
  209. else {
  210. subscriber.next(result);
  211. }
  212. } : function (e) { return subscriber.next(e); };
  213. FromEventObservable.setupSubscription(sourceObj, eventName, handler, subscriber, options);
  214. };
  215. return FromEventObservable;
  216. }(Observable));
  217. //# sourceMappingURL=FromEventObservable.js.map