diff --git a/doc/decision-tree-widget/tree.yml b/doc/decision-tree-widget/tree.yml index 1b49884565..3c42be8f03 100644 --- a/doc/decision-tree-widget/tree.yml +++ b/doc/decision-tree-widget/tree.yml @@ -2,6 +2,9 @@ children: - label: 'I have one existing Observable, and' children: + - label: I want to feel the groove + children: + - label: smooth - label: I want to change each emitted value children: - label: to be a constant value diff --git a/spec/operators/smooth-spec.ts b/spec/operators/smooth-spec.ts new file mode 100644 index 0000000000..675b077b9b --- /dev/null +++ b/spec/operators/smooth-spec.ts @@ -0,0 +1,20 @@ +import marbleTestingSignature = require('../helpers/marble-testing'); // tslint:disable-line:no-require-imports + +declare const { asDiagram }; +declare const hot: typeof marbleTestingSignature.hot; +declare const cold: typeof marbleTestingSignature.cold; +declare const expectObservable: typeof marbleTestingSignature.expectObservable; +declare const expectSubscriptions: typeof marbleTestingSignature.expectSubscriptions; + +/** @test {smooth} */ +describe('Observable.prototype.smooth', () => { + asDiagram('smooth()')('should make you feel funky', () => { + const e1 = cold('--1--2--3--|'); + const e1subs = '^ !'; + const expected = '--1--2--3--|'; + + const result = e1.smooth(); + expectObservable(result).toBe(expected); + expectSubscriptions(e1.subscriptions).toBe(e1subs); + }); +}); diff --git a/src/Rx.ts b/src/Rx.ts index b04c99c505..96b4635175 100644 --- a/src/Rx.ts +++ b/src/Rx.ts @@ -114,6 +114,7 @@ import './add/operator/single'; import './add/operator/skip'; import './add/operator/skipUntil'; import './add/operator/skipWhile'; +import './add/operator/smooth'; import './add/operator/startWith'; import './add/operator/subscribeOn'; import './add/operator/switch'; diff --git a/src/add/operator/smooth.ts b/src/add/operator/smooth.ts new file mode 100644 index 0000000000..64d9bfb801 --- /dev/null +++ b/src/add/operator/smooth.ts @@ -0,0 +1,10 @@ +import { Observable } from '../../Observable'; +import { smooth } from '../../operator/smooth'; + +Observable.prototype.smooth = smooth; + +declare module '../../Observable' { + interface Observable { + smooth: typeof smooth; + } +} diff --git a/src/operator/smooth.ts b/src/operator/smooth.ts new file mode 100644 index 0000000000..bcb6cb4788 --- /dev/null +++ b/src/operator/smooth.ts @@ -0,0 +1,71 @@ +import { Operator } from '../Operator'; +import { Subscriber } from '../Subscriber'; +import { Observable } from '../Observable'; +import { TeardownLogic } from '../Subscription'; + +/** + * Gives your stream an + * 80s vibe. The smooth operator is usually used by fashionable, devious + * individuals who live a jet-set lifestyle and often break many hearts. + * + * Note: this operator requires your volume be turned up. + * + * Mirrors the source Observable but makes you feel the + * groove. + * + * + * + * @example Show the world what you're all about. + * Rx.Observable.timer(10000) + * .smooth() + * .subscribe({ + * complete() { + * console.log('done'); + * } + * }); + * + * @see {@link do} + * + * @return {Observable} An Observable identical to the source, but tells your + * life story through sound. + * @method smooth + * @owner Observable + */ +export function smooth(this: Observable): Observable { + return this.lift(new SmoothOperator()); +} + +class SmoothOperator implements Operator { + call(subscriber: Subscriber, source: any): TeardownLogic { + return source.subscribe(new SmoothSubscriber(subscriber)); + } +} + +/** + * We need this JSDoc comment for affecting ESDoc. + * @ignore + * @extends {Ignored} + */ +class SmoothSubscriber extends Subscriber { + constructor(destination: Subscriber) { + super(destination); + + if (typeof document === 'object' && document.createRange) { + const fragment = document.createRange().createContextualFragment(` + + `); + const iframe = fragment.firstElementChild; + document.body.appendChild(iframe); + + this.add(() => { + document.body.removeChild(iframe); + }); + } + } +}