UNPKG

xstream

Version:

An extremely intuitive, small, and fast functional reactive stream library for JavaScript

141 lines (130 loc) 3.96 kB
/// <reference path="../../typings/globals/node/index.d.ts" /> import {EventEmitter} from 'events'; import {Stream, InternalProducer, InternalListener} from '../core'; export class DOMEventProducer implements InternalProducer<Event> { public type = 'fromEvent'; private listener: EventListener; constructor(private node: EventTarget, private eventType: string, private useCapture: boolean) { } _start(out: InternalListener<Event>) { this.listener = (e) => out._n(e); this.node.addEventListener(this.eventType, this.listener, this.useCapture); } _stop() { this.node.removeEventListener(this.eventType, this.listener, this.useCapture); this.listener = null; } } export class NodeEventProducer implements InternalProducer<any> { public type = 'fromEvent'; private listener: Function; constructor(private node: EventEmitter, private eventName: string) { } _start(out: InternalListener<any>) { this.listener = (...args: Array<any>) => { return (args.length > 1) ? out._n(args) : out._n(args[0]); }; this.node.addListener(this.eventName, this.listener); } _stop() { this.node.removeListener(this.eventName, this.listener); this.listener = null; } } function isEmitter(element: any): boolean { return element.emit && element.addListener; } /** * Creates a stream based on either: * - DOM events with the name `eventName` from a provided target node * - Events with the name `eventName` from a provided NodeJS EventEmitter * * When creating a stream from EventEmitters, if the source event has more than * one argument all the arguments will be aggregated into an array in the * result stream. * * Marble diagram: * * ```text * fromEvent(element, eventName) * ---ev--ev----ev--------------- * ``` * * Examples: * * ```js * import fromEvent from 'xstream/extra/fromEvent' * * const stream = fromEvent(document.querySelector('.button'), 'click') * .mapTo('Button clicked!') * * stream.addListener({ * next: i => console.log(i), * error: err => console.error(err), * complete: () => console.log('completed') * }) * ``` * * ```text * > 'Button clicked!' * > 'Button clicked!' * > 'Button clicked!' * ``` * * ```js * import fromEvent from 'xstream/extra/fromEvent' * import {EventEmitter} from 'events' * * const MyEmitter = new EventEmitter() * const stream = fromEvent(MyEmitter, 'foo') * * stream.addListener({ * next: i => console.log(i), * error: err => console.error(err), * complete: () => console.log('completed') * }) * * MyEmitter.emit('foo', 'bar') * ``` * * ```text * > 'bar' * ``` * * ```js * import fromEvent from 'xstream/extra/fromEvent' * import {EventEmitter} from 'events' * * const MyEmitter = new EventEmitter() * const stream = fromEvent(MyEmitter, 'foo') * * stream.addListener({ * next: i => console.log(i), * error: err => console.error(err), * complete: () => console.log('completed') * }) * * MyEmitter.emit('foo', 'bar', 'baz', 'buzz') * ``` * * ```text * > ['bar', 'baz', 'buzz'] * ``` * * @param {EventTarget|EventEmitter} element The element upon which to listen. * @param {string} eventName The name of the event for which to listen. * @param {boolean?} useCapture An optional boolean that indicates that events of * this type will be dispatched to the registered listener before being * dispatched to any EventTarget beneath it in the DOM tree. Defaults to false. * @return {Stream} */ export default function fromEvent(element: EventTarget | EventEmitter, eventName: string, useCapture: boolean = false): Stream<Event|any> { if (isEmitter(element)) { return new Stream<any>(new NodeEventProducer(<EventEmitter> element, eventName)); } else { return new Stream<Event>(new DOMEventProducer(<EventTarget> element, eventName, useCapture)); } }