Skip to content

Commit

Permalink
feat(fromEvent): Pass through event listener options (#1845)
Browse files Browse the repository at this point in the history
Updating `fromEvent` to accept event listeners options to reflect W3C
DOM specification.

CLOSES: #1836
  • Loading branch information
Oskar Karlsson authored and jayphelps committed Jul 26, 2016
1 parent 1590304 commit 8f0dc01
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 8 deletions.
23 changes: 23 additions & 0 deletions spec/observables/fromEvent-spec.ts
Expand Up @@ -115,6 +115,29 @@ describe('Observable.fromEvent', () => {
expect(offHandler).to.equal(onHandler);
});

it('should pass through options to addEventListener', () => {
let actualOptions;
const expectedOptions = { capture: true, passive: true };

const obj = {
addEventListener: (a: string, b: EventListenerOrEventListenerObject, c?: any) => {
actualOptions = c;
},
removeEventListener: (a: string, b: EventListenerOrEventListenerObject, c?: any) => {
//noop
}
};

const subscription = Observable.fromEvent(<any>obj, 'click', expectedOptions)
.subscribe(() => {
//noop
});

subscription.unsubscribe();

expect(actualOptions).to.equal(expectedOptions);
});

it('should pass through events that occur', (done: MochaDone) => {
let send;
const obj = {
Expand Down
41 changes: 33 additions & 8 deletions src/observable/FromEventObservable.ts
@@ -1,5 +1,6 @@
import {Observable} from '../Observable';
import {tryCatch} from '../util/tryCatch';
import {isFunction} from '../util/isFunction';
import {errorObject} from '../util/errorObject';
import {Subscription} from '../Subscription';
import {Subscriber} from '../Subscriber';
Expand Down Expand Up @@ -34,13 +35,28 @@ function isEventTarget(sourceObj: any): sourceObj is EventTarget {

export type EventTargetLike = EventTarget | NodeStyleEventEmmitter | JQueryStyleEventEmitter | NodeList | HTMLCollection;

export type EventListenerOptions = {
capture?: boolean;
passive?: boolean;
once?: boolean;
} | boolean;

export type SelectorMethodSignature<T> = (...args: Array<any>) => T;

/**
* We need this JSDoc comment for affecting ESDoc.
* @extends {Ignored}
* @hide true
*/
export class FromEventObservable<T, R> extends Observable<T> {

/* tslint:disable:max-line-length */
static create<T>(target: EventTargetLike, eventName: string): Observable<T>;
static create<T>(target: EventTargetLike, eventName: string, selector: SelectorMethodSignature<T>): Observable<T>;
static create<T>(target: EventTargetLike, eventName: string, options: EventListenerOptions): Observable<T>;
static create<T>(target: EventTargetLike, eventName: string, options: EventListenerOptions, selector: SelectorMethodSignature<T>): Observable<T>;
/* tslint:enable:max-line-length */

/**
* Creates an Observable that emits events of a specific type coming from the
* given event target.
Expand Down Expand Up @@ -68,7 +84,8 @@ export class FromEventObservable<T, R> extends Observable<T> {
* EventEmitter, NodeList or HTMLCollection to attach the event handler to.
* @param {string} eventName The event name of interest, being emitted by the
* `target`.
* @param {function(...args: any): T} [selector] An optional function to
* @parm {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>}
Expand All @@ -78,28 +95,35 @@ export class FromEventObservable<T, R> extends Observable<T> {
*/
static create<T>(target: EventTargetLike,
eventName: string,
selector?: (...args: Array<any>) => T): Observable<T> {
return new FromEventObservable(target, eventName, selector);
options?: EventListenerOptions,
selector?: SelectorMethodSignature<T>): Observable<T> {
if (isFunction(options)) {
selector = <any>options;
options = undefined;
}
return new FromEventObservable(target, eventName, selector, options);
}

constructor(private sourceObj: EventTargetLike,
private eventName: string,
private selector?: (...args: Array<any>) => T) {
private selector?: SelectorMethodSignature<T>,
private options?: EventListenerOptions) {
super();
}

private static setupSubscription<T>(sourceObj: EventTargetLike,
eventName: string,
handler: Function,
subscriber: Subscriber<T>) {
subscriber: Subscriber<T>,
options?: EventListenerOptions) {
let unsubscribe: () => void;
if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
for (let i = 0, len = sourceObj.length; i < len; i++) {
FromEventObservable.setupSubscription(sourceObj[i], eventName, handler, subscriber);
FromEventObservable.setupSubscription(sourceObj[i], eventName, handler, subscriber, options);
}
} else if (isEventTarget(sourceObj)) {
const source = sourceObj;
sourceObj.addEventListener(eventName, <EventListener>handler);
sourceObj.addEventListener(eventName, <EventListener>handler, <boolean>options);
unsubscribe = () => source.removeEventListener(eventName, <EventListener>handler);
} else if (isJQueryStyleEventEmitter(sourceObj)) {
const source = sourceObj;
Expand All @@ -117,6 +141,7 @@ export class FromEventObservable<T, R> extends Observable<T> {
protected _subscribe(subscriber: Subscriber<T>) {
const sourceObj = this.sourceObj;
const eventName = this.eventName;
const options = this.options;
const selector = this.selector;
let handler = selector ? (...args: any[]) => {
let result = tryCatch(selector)(...args);
Expand All @@ -127,6 +152,6 @@ export class FromEventObservable<T, R> extends Observable<T> {
}
} : (e: any) => subscriber.next(e);

FromEventObservable.setupSubscription(sourceObj, eventName, handler, subscriber);
FromEventObservable.setupSubscription(sourceObj, eventName, handler, subscriber, options);
}
}

0 comments on commit 8f0dc01

Please sign in to comment.