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