├── .gitignore
├── .npmignore
├── .editorconfig
├── webpack.umd.js
├── webpack.client-watch.js
├── webpack.client.js
├── src
├── lib
│ ├── PausableObservable.js
│ ├── BetterObservable.js
│ ├── fetchObservable.js
│ └── Observable.js
└── example.js
├── LICENSE.md
├── package.json
├── README.md
└── dist
└── lib
├── fetchObservable.min.js
└── fetchObservable.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | dist/*
4 | !dist/lib
5 | *.log
6 | *.map
7 | .DS_Store
8 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | dist/*
4 | !dist/lib
5 | *.log
6 | *.map
7 | .DS_Store
8 |
9 | src/example/
10 | src/example.*
11 | tmp/
12 | webpack.*.js
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig: http://EditorConfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | end_of_line = crlf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 | indent_style = tab
11 | tab_width = 4
12 |
--------------------------------------------------------------------------------
/webpack.umd.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 | var path = require("path");
3 |
4 | module.exports = {
5 | cache: false,
6 | context: __dirname,
7 | output: {
8 | library: "fetchObservable",
9 | libraryTarget: "umd"
10 | },
11 | plugins: [
12 | new webpack.DefinePlugin({"process.env": {NODE_ENV: '"production"'}}),
13 | new webpack.optimize.DedupePlugin(),
14 | new webpack.optimize.OccurenceOrderPlugin()
15 | ],
16 | module: {
17 | loaders: [
18 | {test: /\.json$/, loaders: ["json"]},
19 | {test: /\.js$/,
20 | loaders: ["babel?cacheDirectory&presets[]=es2015&presets[]=stage-0"],
21 | exclude: /node_modules/
22 | }
23 | ],
24 | noParse: /\.min\.js$/
25 | },
26 | resolve: {
27 | modulesDirectories: [
28 | "src",
29 | "node_modules",
30 | "web_modules"
31 | ],
32 | extensions: ["", ".json", ".js"]
33 | },
34 | node: {
35 | __dirname: true,
36 | fs: 'empty'
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/webpack.client-watch.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 | var config = require("./webpack.client.js");
3 |
4 | config.cache = true;
5 | config.debug = true;
6 | config.devtool = "eval-sourcemap";
7 |
8 | config.entry._wds = "webpack-dev-server/client?http://localhost:8080";
9 | config.entry._hmr = "webpack/hot/only-dev-server";
10 |
11 | config.output.publicPath = "http://localhost:8080/";
12 | config.output.hotUpdateMainFilename = "update/[hash]/update.json";
13 | config.output.hotUpdateChunkFilename = "update/[hash]/[id].update.js";
14 |
15 | config.plugins = [
16 | new webpack.HotModuleReplacementPlugin()
17 | ];
18 |
19 | config.devServer = {
20 | publicPath: "http://localhost:8080/",
21 | contentBase: "./dist",
22 | hot: true,
23 | inline: true,
24 | lazy: false,
25 | quiet: true,
26 | noInfo: false,
27 | headers: {"Access-Control-Allow-Origin": "*"},
28 | stats: {colors: true}
29 | };
30 |
31 | module.exports = config;
32 |
--------------------------------------------------------------------------------
/webpack.client.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 | var path = require("path");
3 |
4 | module.exports = {
5 | target: "web",
6 | cache: false,
7 | context: __dirname,
8 | devtool: false,
9 | entry: {
10 | "example": "./src/example"
11 | },
12 | output: {
13 | path: path.join(__dirname, "dist"),
14 | filename: "[name].js",
15 | chunkFilename: "[name].[id].js",
16 | publicPath: "/"
17 | },
18 | plugins: [
19 | new webpack.DefinePlugin({"process.env": {NODE_ENV: '"production"'}}),
20 | new webpack.optimize.DedupePlugin(),
21 | new webpack.optimize.OccurenceOrderPlugin(),
22 | new webpack.optimize.UglifyJsPlugin()
23 | ],
24 | module: {
25 | loaders: [
26 | {test: /\.json$/, loaders: ["json"]},
27 | {test: /\.js$/, loaders: ["babel?cacheDirectory&presets[]=es2015&presets[]=react&presets[]=stage-0"], exclude: /node_modules/}
28 | ],
29 | noParse: /\.min\.js$/
30 | },
31 | resolve: {
32 | modulesDirectories: [
33 | "src",
34 | "node_modules",
35 | "web_modules"
36 | ],
37 | extensions: ["", ".json", ".js"]
38 | },
39 | node: {
40 | __dirname: true,
41 | fs: 'empty'
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/src/lib/PausableObservable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @copyright © 2015, Rick Wong. All rights reserved.
3 | */
4 | import BetterObservable from "lib/BetterObservable";
5 |
6 | class PausableObservable extends BetterObservable {
7 | constructor (subscriber, {onPause, onResume} = {}) {
8 | super(subscriber);
9 |
10 | this.state = "paused";
11 |
12 | this.onPause = onPause;
13 | this.onResume = onResume;
14 | }
15 |
16 | pause (...args) {
17 | this.state = "paused";
18 |
19 | return this.onPause && this.onPause(...args);
20 | }
21 |
22 | resume (...args) {
23 | this.state = "resumed";
24 |
25 | return this.onResume && this.onResume(...args);
26 | }
27 |
28 | paused () {
29 | return this.state === "paused";
30 | }
31 |
32 | map (callback) {
33 | const pausableObservable = super.map(callback);
34 |
35 | // Child observable must track parent's state, so bind its pause, resume, and paused.
36 | Object.assign(pausableObservable, {
37 | pause: (...args) => this.pause(...args),
38 | resume: (...args) => this.resume(...args),
39 | paused: () => this.paused()
40 | });
41 |
42 | return pausableObservable;
43 | }
44 | }
45 |
46 | module.exports = PausableObservable;
47 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # BSD 3-Clause License
2 |
3 | Copyright © 2015, Rick Wong
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. Neither the name of the copyright holder nor the
15 | names of its contributors may be used to endorse or promote products
16 | derived from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fetch-observable",
3 | "description": "Observable-based Fetch API",
4 | "version": "1.2.1",
5 | "license": "BSD-3-Clause",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/RickWong/fetch-observable.git"
9 | },
10 | "homepage": "https://github.com/RickWong/fetch-observable",
11 | "keywords": [
12 | "fetch",
13 | "observable",
14 | "promise"
15 | ],
16 | "main": "dist/lib/fetchobservable.js",
17 | "scripts": {
18 | "build": "concurrent --kill-others 'npm run build-full' 'npm run build-min'",
19 | "build-full": "webpack --verbose --colors --display-error-details --config webpack.umd.js src/lib/fetchObservable.js dist/lib/fetchObservable.js",
20 | "build-min": "webpack --verbose --colors --display-error-details --config webpack.umd.js -p src/lib/fetchObservable.js dist/lib/fetchObservable.min.js",
21 | "watch": "webpack --verbose --colors --display-error-details --config webpack.client-watch.js && webpack-dev-server --config webpack.client-watch.js",
22 | "prepublish": "npm run build"
23 | },
24 | "dependencies": {},
25 | "devDependencies": {
26 | "babel": "6.3.13",
27 | "babel-core": "6.3.21",
28 | "babel-loader": "6.2.0",
29 | "babel-preset-es2015": "6.3.13",
30 | "babel-preset-stage-0": "6.3.13",
31 | "concurrently": "1.0.0",
32 | "isomorphic-fetch": "2.2.0",
33 | "json-loader": "0.5.4",
34 | "react": "0.14.3",
35 | "react-dom": "0.14.3",
36 | "webpack": "1.12.9",
37 | "webpack-dev-server": "1.14.0"
38 | },
39 | "engines": {
40 | "node": ">=0.10.32"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/example.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @copyright © 2015, Rick Wong. All rights reserved.
3 | */
4 | import __fetch from "isomorphic-fetch";
5 | import fetchObservable from "lib/fetchObservable";
6 | import React from "react";
7 | import ReactDOM from "react-dom";
8 |
9 | try {
10 | let observable = fetchObservable(
11 | "http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=2de143494c0b295cca9337e1e96b00e0", {
12 | refreshDelay: 1500
13 | }
14 | ).map(a=>a).map(a=>a).map(a=>a).map(a=>a).map(a=>a).map((response) => response.json());
15 |
16 | let subscriptions = {};
17 |
18 | global.o = observable;
19 |
20 | function toggleFetching (index) {
21 | if (subscriptions[index]) {
22 | subscriptions[index].unsubscribe();
23 | delete subscriptions[index];
24 | }
25 | else {
26 | subscriptions[index] = observable.subscribe({
27 | next: (...args) => {
28 | console.log(`subscriptions[${index}] next:`, ...args);
29 | },
30 | error: (...args) => {
31 | console.warn(`subscriptions[${index}] error:`, ...args);
32 | },
33 | complete: () => {
34 | console.log(`subscriptions[${index}] complete`);
35 | toggleFetching(index);
36 | }
37 | });
38 | }
39 |
40 | render();
41 | }
42 |
43 | function render () {
44 | ReactDOM.render(
45 |
46 |
47 |
48 |
51 |
54 | ,
55 | document.getElementById("react-root")
56 | );
57 | }
58 |
59 | render();
60 | }
61 | catch (error)
62 | {
63 | throw error;
64 | }
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # fetchObservable()
2 |
3 | Observable-based [Fetch API](https://github.com/whatwg/fetch) that automatically refreshes data and notifies subscribers.
4 |
5 | ## Features
6 |
7 | - Uses the standard Fetch API.
8 | - Uses Observable syntax from [ES Observable proposal](https://github.com/zenparsing/es-observable).
9 | - Runs in Node and browsers. (BYO Fetch API and Promises polyfills though)
10 |
11 | ## Installation
12 |
13 | ```bash
14 | npm install --save fetch-observable
15 | ```
16 |
17 | ## Usage
18 |
19 | ````js
20 | import fetchObservable from "fetch-observable";
21 |
22 | // Creates a single observable for one or multiple URLs.
23 | const liveFeed = fetchObservable(
24 | "http://example.org/live-feed.json", // <-- URL or array of URLs.
25 | {
26 | fetch: fetch, // <-- Replacable fetch implementation.
27 | refreshDelay: (iteration) => iteration * 1000, // <-- Callback or just integer ms.
28 | method: "POST" // <-- Basic Fetch API options.
29 | }
30 | ).map((response) => response.json()); // map() resolves Promises.
31 |
32 | // Subscribe-syntax of ES Observables activates the observable.
33 | const subscription1 = liveFeed.subscribe({
34 | next (response) {
35 | console.dir(response.json());
36 | },
37 | error (error) {
38 | console.warn(error.stack || error);
39 | }
40 | });
41 |
42 | // Multiple subscriptions allowed. They all get the result.
43 | const subscription2 = liveFeed.subscribe({next () {}});
44 |
45 | // Observable can be paused and resumed manually.
46 | liveFeed.pause();
47 | liveFeed.resume();
48 |
49 | // Observable will be paused automatically when no subscriptions left.
50 | subscription1.unsubscribe();
51 | subscription2.unsubscribe();
52 |
53 | ````
54 |
55 | ## Community
56 |
57 | Let's start one together! After you ★Star this project, follow me [@Rygu](https://twitter.com/rygu)
58 | on Twitter.
59 |
60 | ## License
61 |
62 | BSD 3-Clause license. Copyright © 2015, Rick Wong. All rights reserved.
63 |
--------------------------------------------------------------------------------
/src/lib/BetterObservable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @copyright © 2015, Rick Wong. All rights reserved.
3 | */
4 | import {Observable} from "lib/Observable";
5 |
6 | function isPromise (thing) {
7 | return (
8 | typeof thing === "object" &&
9 | typeof thing["then"] === "function" &&
10 | typeof thing["catch"] === "function"
11 | );
12 | }
13 |
14 | class BetterObservable extends Observable {
15 | map (callback) {
16 | if (typeof callback !== "function") {
17 | throw new TypeError(callback + " is not a function");
18 | }
19 |
20 | let parentSubscription = null;
21 | let childObservers = [];
22 |
23 | const createParentSubscription = () => {
24 | if (parentSubscription) {
25 | return;
26 | }
27 |
28 | parentSubscription = this.subscribe({
29 | next (value) {
30 | try {
31 | value = callback(value);
32 | }
33 | catch (e) {
34 | return childObservers.map((o) => o.error(e));
35 | }
36 |
37 | // Support Promises.
38 | if (isPromise(value)) {
39 | return value.then(
40 | (v) => childObservers.map((o) => o.next(v))
41 | ).catch(
42 | (e) => childObservers.map((o) => o.error(e))
43 | );
44 | }
45 |
46 | childObservers.map((o) => o.next(value));
47 | },
48 | error: (e) => childObservers.map((o) => o.error(e)),
49 | complete: () => childObservers.map((o) => o.complete())
50 | });
51 | };
52 |
53 | const destroyParentSubscription = () => {
54 | parentSubscription && parentSubscription.unsubscribe();
55 | parentSubscription = null;
56 | };
57 |
58 | return new this.constructor((observer) => {
59 | childObservers.push(observer);
60 | createParentSubscription();
61 |
62 | return () => {
63 | childObservers.splice(childObservers.indexOf(observer), 1);
64 |
65 | if (!childObservers.length) {
66 | destroyParentSubscription();
67 | }
68 | };
69 | });
70 | }
71 | }
72 |
73 | module.exports = BetterObservable;
74 |
--------------------------------------------------------------------------------
/src/lib/fetchObservable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @copyright © 2015, Rick Wong. All rights reserved.
3 | */
4 | import PausableObservable from "lib/PausableObservable";
5 |
6 | function isString (thing) {
7 | return typeof thing === "string";
8 | }
9 |
10 | function isFunction (thing) {
11 | return typeof thing === "function";
12 | }
13 |
14 | /**
15 | * Calls the Fetch API and returns an Observable.
16 | *
17 | * @param {string|string[]} urls URL or URLs array.
18 | * @param {object} options
19 | * @returns {PausableObservable|Observable}
20 | */
21 | function fetchObservable (urls, options = {}) {
22 | const {refreshDelay = false} = options;
23 | const fetchFunc = options.fetch || fetch;
24 |
25 | let observers = [];
26 | let timeout = null;
27 | let singleResult = false;
28 | let iteration = 0;
29 |
30 | if (singleResult = isString(urls)) {
31 | urls = [urls];
32 | }
33 |
34 | const performFetch = function () {
35 | // Don't do anything if there are no observers.
36 | if (observers.length === 0 ||
37 | observable.paused()) {
38 | return;
39 | }
40 |
41 | const _finally = function () {
42 | // If refreshing is disabled, complete observers and pause observable.
43 | if (!refreshDelay) {
44 | observable.pause();
45 | observers.map((observer) => observer.complete());
46 | observers = [];
47 | }
48 | // If refreshing is enabled, set a timeout.
49 | else {
50 | timeout = setTimeout(
51 | performFetch,
52 | isFunction(refreshDelay) ? refreshDelay(iteration++) : refreshDelay
53 | );
54 | }
55 | };
56 |
57 | // Map all URLs to Fetch API calls.
58 | let fetches = urls.map(
59 | (url) => fetchFunc(url, {...options, refreshDelay: undefined, fetch: undefined})
60 | );
61 |
62 | // Wait for all the results to come in, then notify observers.
63 | Promise.all(fetches).then(function (results) {
64 | observers.map((observer) => observer.next(singleResult ? results[0] : results));
65 | _finally();
66 | }).catch(function (error) {
67 | observers.map((observer) => observer.error(error));
68 | _finally();
69 | });
70 | };
71 |
72 | const observable = new PausableObservable(function (observer) {
73 | observers.push(observer);
74 | observable.resume();
75 |
76 | return function () {
77 | observers.splice(observers.indexOf(observer), 1);
78 |
79 | if (!observers.length) {
80 | observable.pause();
81 | }
82 | };
83 | }, {
84 | onPause () {
85 | if (timeout) {
86 | clearTimeout(timeout);
87 | timeout = null;
88 | }
89 | },
90 | onResume () {
91 | if (!timeout) {
92 | performFetch();
93 | }
94 | }
95 | });
96 |
97 | return observable;
98 | }
99 |
100 | module.exports = fetchObservable;
101 |
--------------------------------------------------------------------------------
/dist/lib/fetchObservable.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.fetchObservable=e():t.fetchObservable=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return t[r].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t){return"string"==typeof t}function u(t){return"function"==typeof t}function i(t){var e=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],n=e.refreshDelay,r=void 0===n?!1:n,i=e.fetch||fetch,f=[],a=null,l=!1,p=0;(l=o(t))&&(t=[t]);var h=function y(){if(0!==f.length&&!b.paused()){var n=function(){r?a=setTimeout(y,u(r)?r(p++):r):(b.pause(),f.map(function(t){return t.complete()}),f=[])},o=t.map(function(t){return i(t,c({},e,{refreshDelay:void 0,fetch:void 0}))});Promise.all(o).then(function(t){f.map(function(e){return e.next(l?t[0]:t)}),n()})["catch"](function(t){f.map(function(e){return e.error(t)}),n()})}},b=new s["default"](function(t){return f.push(t),b.resume(),function(){f.splice(f.indexOf(t),1),f.length||b.pause()}},{onPause:function(){a&&(clearTimeout(a),a=null)},onResume:function(){a||h()}});return b}var c=Object.assign||function(t){for(var e=1;e1&&n();r.length>0;)r.shift()()});return o.observe(t,{attributes:!0}),{v:function(t){r.push(t),1===r.length&&n()}}}();if("object"===("undefined"==typeof o?"undefined":r(o)))return o.v}return function(t){setTimeout(t,0)}}();o("observable"),a(v.prototype={},{get closed(){return p(this)},next:function(t){if(!p(this)){var e=this._observer;try{var n=f(e,"next");if(!n)return;return n.call(e,t)}catch(r){try{h(this)}finally{throw r}}}},error:function(t){if(p(this))throw t;var e=this._observer;this._observer=void 0;try{var n=f(e,"error");if(!n)throw t;t=n.call(e,t)}catch(r){try{l(this)}finally{throw r}}return l(this),t},complete:function(t){if(!p(this)){var e=this._observer;this._observer=void 0;try{var n=f(e,"complete");t=n?n.call(e,t):void 0}catch(r){try{l(this)}finally{throw r}}return l(this),t}}}),a(m.prototype={},{unsubscribe:function(){h(this._observer)}}),a(d.prototype,{subscribe:function(t){return y(t,this._subscriber)},forEach:function(t){var e=this;return new Promise(function(n,r){if("function"!=typeof t)throw new TypeError(t+" is not a function");e.subscribe({next:function(e){try{return t(e)}catch(n){r(n)}},error:r,complete:n})})},map:function(t){var e=this;if("function"!=typeof t)throw new TypeError(t+" is not a function");var n=s(this.constructor);return new n(function(n){return e.subscribe({next:function(e){try{e=t(e)}catch(r){return n.error(r)}return n.next(e)},error:function(t){return n.error(t)},complete:function(t){return n.complete(t)}})})},filter:function(t){var e=this;if("function"!=typeof t)throw new TypeError(t+" is not a function");var n=s(this.constructor);return new n(function(n){return e.subscribe({next:function(e){try{if(!t(e))return}catch(r){return n.error(r)}return n.next(e)},error:function(t){return n.error(t)},complete:function(t){return n.complete(t)}})})}}),Object.defineProperty(d.prototype,c("observable"),{value:function(){return this},writable:!0,configurable:!0}),a(d,{from:function(t){var e="function"==typeof this?this:d;if(null==t)throw new TypeError(t+" is not an object");var n=f(t,c("observable"));if(n){var o=function(){var r=n.call(t);if(Object(r)!==r)throw new TypeError(r+" is not an object");return r.constructor===e?{v:r}:{v:new e(function(t){return r.subscribe(t)})}}();if("object"===("undefined"==typeof o?"undefined":r(o)))return o.v}return new e(function(e){w(function(n){if(!e.closed){try{if(i("iterator")){var r=!0,o=!1,u=void 0;try{for(var c,f=t[Symbol.iterator]();!(r=(c=f.next()).done);r=!0){var s=c.value;if(e.next(s),e.closed)return}}catch(a){o=!0,u=a}finally{try{!r&&f["return"]&&f["return"]()}finally{if(o)throw u}}}else{if(!Array.isArray(t))throw new Error(t+" is not an Array");for(var l=0;ln;n++)e[n]=arguments[n];var r="function"==typeof this?this:d;return new r(function(t){w(function(n){if(!t.closed){for(var r=0;r1)for(var n=1;n { global.setImmediate(fn) } :
19 | fn => { process.nextTick(fn) };
20 | }
21 |
22 | // Newish Browsers
23 | let Observer = self.MutationObserver || self.WebKitMutationObserver;
24 |
25 | if (Observer) {
26 |
27 | let div = document.createElement("div"),
28 | twiddle = _=> div.classList.toggle("x"),
29 | queue = [];
30 |
31 | let observer = new Observer(_=> {
32 |
33 | if (queue.length > 1)
34 | twiddle();
35 |
36 | while (queue.length > 0)
37 | queue.shift()();
38 | });
39 |
40 | observer.observe(div, { attributes: true });
41 |
42 | return fn => {
43 |
44 | queue.push(fn);
45 |
46 | if (queue.length === 1)
47 | twiddle();
48 | };
49 | }
50 |
51 | // Fallback
52 | return fn => { setTimeout(fn, 0) };
53 |
54 | })();
55 |
56 | // === Symbol Polyfills ===
57 |
58 | function polyfillSymbol(name) {
59 |
60 | if (symbolsSupported() && !Symbol[name])
61 | Object.defineProperty(Symbol, name, { value: Symbol(name) });
62 | }
63 |
64 | function symbolsSupported() {
65 |
66 | return typeof Symbol === "function";
67 | }
68 |
69 | function hasSymbol(name) {
70 |
71 | return symbolsSupported() && Boolean(Symbol[name]);
72 | }
73 |
74 | function getSymbol(name) {
75 |
76 | return hasSymbol(name) ? Symbol[name] : "@@" + name;
77 | }
78 |
79 | polyfillSymbol("observable");
80 |
81 | // === Abstract Operations ===
82 |
83 | function getMethod(obj, key) {
84 |
85 | let value = obj[key];
86 |
87 | if (value == null)
88 | return undefined;
89 |
90 | if (typeof value !== "function")
91 | throw new TypeError(value + " is not a function");
92 |
93 | return value;
94 | }
95 |
96 | function getSpecies(ctor) {
97 |
98 | let symbol = getSymbol("species");
99 | return symbol ? ctor[symbol] : ctor;
100 | }
101 |
102 | function addMethods(target, methods) {
103 |
104 | Object.keys(methods).forEach(k => {
105 |
106 | let desc = Object.getOwnPropertyDescriptor(methods, k);
107 | desc.enumerable = false;
108 | Object.defineProperty(target, k, desc);
109 | });
110 | }
111 |
112 | function cleanupSubscription(observer) {
113 |
114 | // Assert: observer._observer is undefined
115 |
116 | let cleanup = observer._cleanup;
117 |
118 | if (!cleanup)
119 | return;
120 |
121 | // Drop the reference to the cleanup function so that we won't call it
122 | // more than once
123 | observer._cleanup = undefined;
124 |
125 | // Call the cleanup function
126 | cleanup();
127 | }
128 |
129 | function subscriptionClosed(observer) {
130 |
131 | return observer._observer === undefined;
132 | }
133 |
134 | function closeSubscription(observer) {
135 |
136 | if (subscriptionClosed(observer))
137 | return;
138 |
139 | observer._observer = undefined;
140 | cleanupSubscription(observer);
141 | }
142 |
143 | function cleanupFromSubscription(subscription) {
144 | // TODO: Should we get the method out and apply it here, instead of
145 | // looking up the method at call time?
146 | return _=> { subscription.unsubscribe() };
147 | }
148 |
149 | function createSubscription(observer, subscriber) {
150 |
151 | // Assert: subscriber is callable
152 |
153 | // The observer must be an object
154 | if (Object(observer) !== observer)
155 | throw new TypeError("Observer must be an object");
156 |
157 | // TODO: Should we check for a "next" method here?
158 |
159 | let subscriptionObserver = new SubscriptionObserver(observer),
160 | subscription = new Subscription(subscriptionObserver),
161 | start = getMethod(observer, "start");
162 |
163 | if (start)
164 | start.call(observer, subscription);
165 |
166 | if (subscriptionClosed(subscriptionObserver))
167 | return subscription;
168 |
169 | try {
170 |
171 | // Call the subscriber function
172 | let cleanup = subscriber.call(undefined, subscriptionObserver);
173 |
174 | // The return value must be undefined, null, a subscription object, or a function
175 | if (cleanup != null) {
176 |
177 | if (typeof cleanup.unsubscribe === "function")
178 | cleanup = cleanupFromSubscription(cleanup);
179 | else if (typeof cleanup !== "function")
180 | throw new TypeError(cleanup + " is not a function");
181 |
182 | subscriptionObserver._cleanup = cleanup;
183 | }
184 |
185 | } catch (e) {
186 |
187 | // If an error occurs during startup, then attempt to send the error
188 | // to the observer
189 | subscriptionObserver.error(e);
190 | return subscription;
191 | }
192 |
193 | // If the stream is already finished, then perform cleanup
194 | if (subscriptionClosed(subscriptionObserver))
195 | cleanupSubscription(subscriptionObserver);
196 |
197 | return subscription;
198 | }
199 |
200 | function SubscriptionObserver(observer) {
201 |
202 | this._observer = observer;
203 | this._cleanup = undefined;
204 | }
205 |
206 | addMethods(SubscriptionObserver.prototype = {}, {
207 |
208 | get closed() { return subscriptionClosed(this) },
209 |
210 | next(value) {
211 |
212 | // If the stream if closed, then return undefined
213 | if (subscriptionClosed(this))
214 | return undefined;
215 |
216 | let observer = this._observer;
217 |
218 | try {
219 |
220 | let m = getMethod(observer, "next");
221 |
222 | // If the observer doesn't support "next", then return undefined
223 | if (!m)
224 | return undefined;
225 |
226 | // Send the next value to the sink
227 | return m.call(observer, value);
228 |
229 | } catch (e) {
230 |
231 | // If the observer throws, then close the stream and rethrow the error
232 | try { closeSubscription(this) }
233 | finally { throw e }
234 | }
235 | },
236 |
237 | error(value) {
238 |
239 | // If the stream is closed, throw the error to the caller
240 | if (subscriptionClosed(this))
241 | throw value;
242 |
243 | let observer = this._observer;
244 | this._observer = undefined;
245 |
246 | try {
247 |
248 | let m = getMethod(observer, "error");
249 |
250 | // If the sink does not support "error", then throw the error to the caller
251 | if (!m)
252 | throw value;
253 |
254 | value = m.call(observer, value);
255 |
256 | } catch (e) {
257 |
258 | try { cleanupSubscription(this) }
259 | finally { throw e }
260 | }
261 |
262 | cleanupSubscription(this);
263 |
264 | return value;
265 | },
266 |
267 | complete(value) {
268 |
269 | // If the stream is closed, then return undefined
270 | if (subscriptionClosed(this))
271 | return undefined;
272 |
273 | let observer = this._observer;
274 | this._observer = undefined;
275 |
276 | try {
277 |
278 | let m = getMethod(observer, "complete");
279 |
280 | // If the sink does not support "complete", then return undefined
281 | value = m ? m.call(observer, value) : undefined;
282 |
283 | } catch (e) {
284 |
285 | try { cleanupSubscription(this) }
286 | finally { throw e }
287 | }
288 |
289 | cleanupSubscription(this);
290 |
291 | return value;
292 | },
293 |
294 | });
295 |
296 | function Subscription(observer) {
297 | this._observer = observer;
298 | }
299 |
300 | addMethods(Subscription.prototype = {}, {
301 | unsubscribe() { closeSubscription(this._observer) }
302 | });
303 |
304 | export function Observable(subscriber) {
305 |
306 | // The stream subscriber must be a function
307 | if (typeof subscriber !== "function")
308 | throw new TypeError("Observable initializer must be a function");
309 |
310 | this._subscriber = subscriber;
311 | }
312 |
313 | addMethods(Observable.prototype, {
314 |
315 | subscribe(observer) {
316 |
317 | return createSubscription(observer, this._subscriber);
318 | },
319 |
320 | forEach(fn) {
321 |
322 | return new Promise((resolve, reject) => {
323 |
324 | if (typeof fn !== "function")
325 | throw new TypeError(fn + " is not a function");
326 |
327 | this.subscribe({
328 |
329 | next(value) {
330 |
331 | try { return fn(value) }
332 | catch (e) { reject(e) }
333 | },
334 |
335 | error: reject,
336 | complete: resolve,
337 | });
338 | });
339 | },
340 |
341 | map(fn) {
342 |
343 | if (typeof fn !== "function")
344 | throw new TypeError(fn + " is not a function");
345 |
346 | let C = getSpecies(this.constructor);
347 |
348 | return new C(observer => this.subscribe({
349 |
350 | next(value) {
351 |
352 | try { value = fn(value) }
353 | catch (e) { return observer.error(e) }
354 |
355 | return observer.next(value);
356 | },
357 |
358 | error(value) { return observer.error(value) },
359 | complete(value) { return observer.complete(value) },
360 | }));
361 | },
362 |
363 | filter(fn) {
364 |
365 | if (typeof fn !== "function")
366 | throw new TypeError(fn + " is not a function");
367 |
368 | let C = getSpecies(this.constructor);
369 |
370 | return new C(observer => this.subscribe({
371 |
372 | next(value) {
373 |
374 | try { if (!fn(value)) return undefined; }
375 | catch (e) { return observer.error(e) }
376 |
377 | return observer.next(value);
378 | },
379 |
380 | error(value) { return observer.error(value) },
381 | complete(value) { return observer.complete(value) },
382 | }));
383 | },
384 |
385 | });
386 |
387 | Object.defineProperty(Observable.prototype, getSymbol("observable"), {
388 | value: function() { return this },
389 | writable: true,
390 | configurable: true,
391 | });
392 |
393 | addMethods(Observable, {
394 |
395 | from(x) {
396 |
397 | let C = typeof this === "function" ? this : Observable;
398 |
399 | if (x == null)
400 | throw new TypeError(x + " is not an object");
401 |
402 | let method = getMethod(x, getSymbol("observable"));
403 |
404 | if (method) {
405 |
406 | let observable = method.call(x);
407 |
408 | if (Object(observable) !== observable)
409 | throw new TypeError(observable + " is not an object");
410 |
411 | if (observable.constructor === C)
412 | return observable;
413 |
414 | return new C(observer => observable.subscribe(observer));
415 | }
416 |
417 | return new C(observer => {
418 |
419 | enqueueJob(_=> {
420 |
421 | if (observer.closed)
422 | return;
423 |
424 | // Assume that the object is iterable. If not, then the observer
425 | // will receive an error.
426 | try {
427 |
428 | if (hasSymbol("iterator")) {
429 |
430 | for (let item of x) {
431 |
432 | observer.next(item);
433 |
434 | if (observer.closed)
435 | return;
436 | }
437 |
438 | } else {
439 |
440 | if (!Array.isArray(x))
441 | throw new Error(x + " is not an Array");
442 |
443 | for (let i = 0; i < x.length; ++i) {
444 |
445 | observer.next(x[i]);
446 |
447 | if (observer.closed)
448 | return;
449 | }
450 | }
451 |
452 | } catch (e) {
453 |
454 | // If observer.next throws an error, then the subscription will
455 | // be closed and the error method will simply rethrow
456 | observer.error(e);
457 | return;
458 | }
459 |
460 | observer.complete();
461 | });
462 | });
463 | },
464 |
465 | of(...items) {
466 |
467 | let C = typeof this === "function" ? this : Observable;
468 |
469 | return new C(observer => {
470 |
471 | enqueueJob(_=> {
472 |
473 | if (observer.closed)
474 | return;
475 |
476 | for (let i = 0; i < items.length; ++i) {
477 |
478 | observer.next(items[i]);
479 |
480 | if (observer.closed)
481 | return;
482 | }
483 |
484 | observer.complete();
485 | });
486 | });
487 | },
488 |
489 | });
490 |
491 | Object.defineProperty(Observable, getSymbol("species"), {
492 | get() { return this },
493 | configurable: true,
494 | });
495 |
--------------------------------------------------------------------------------
/dist/lib/fetchObservable.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory();
4 | else if(typeof define === 'function' && define.amd)
5 | define([], factory);
6 | else if(typeof exports === 'object')
7 | exports["fetchObservable"] = factory();
8 | else
9 | root["fetchObservable"] = factory();
10 | })(this, function() {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 |
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 |
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId])
20 | /******/ return installedModules[moduleId].exports;
21 |
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ exports: {},
25 | /******/ id: moduleId,
26 | /******/ loaded: false
27 | /******/ };
28 |
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 |
32 | /******/ // Flag the module as loaded
33 | /******/ module.loaded = true;
34 |
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 |
39 |
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 |
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 |
46 | /******/ // __webpack_public_path__
47 | /******/ __webpack_require__.p = "";
48 |
49 | /******/ // Load entry module and return exports
50 | /******/ return __webpack_require__(0);
51 | /******/ })
52 | /************************************************************************/
53 | /******/ ([
54 | /* 0 */
55 | /***/ function(module, exports, __webpack_require__) {
56 |
57 | "use strict";
58 |
59 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /**
60 | * @copyright © 2015, Rick Wong. All rights reserved.
61 | */
62 |
63 | var _PausableObservable = __webpack_require__(3);
64 |
65 | var _PausableObservable2 = _interopRequireDefault(_PausableObservable);
66 |
67 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
68 |
69 | function isString(thing) {
70 | return typeof thing === "string";
71 | }
72 |
73 | function isFunction(thing) {
74 | return typeof thing === "function";
75 | }
76 |
77 | /**
78 | * Calls the Fetch API and returns an Observable.
79 | *
80 | * @param {string|string[]} urls URL or URLs array.
81 | * @param {object} options
82 | * @returns {PausableObservable|Observable}
83 | */
84 | function fetchObservable(urls) {
85 | var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
86 | var _options$refreshDelay = options.refreshDelay;
87 | var refreshDelay = _options$refreshDelay === undefined ? false : _options$refreshDelay;
88 |
89 | var fetchFunc = options.fetch || fetch;
90 |
91 | var observers = [];
92 | var timeout = null;
93 | var singleResult = false;
94 | var iteration = 0;
95 |
96 | if (singleResult = isString(urls)) {
97 | urls = [urls];
98 | }
99 |
100 | var performFetch = function performFetch() {
101 | // Don't do anything if there are no observers.
102 | if (observers.length === 0 || observable.paused()) {
103 | return;
104 | }
105 |
106 | var _finally = function _finally() {
107 | // If refreshing is disabled, complete observers and pause observable.
108 | if (!refreshDelay) {
109 | observable.pause();
110 | observers.map(function (observer) {
111 | return observer.complete();
112 | });
113 | observers = [];
114 | }
115 | // If refreshing is enabled, set a timeout.
116 | else {
117 | timeout = setTimeout(performFetch, isFunction(refreshDelay) ? refreshDelay(iteration++) : refreshDelay);
118 | }
119 | };
120 |
121 | // Map all URLs to Fetch API calls.
122 | var fetches = urls.map(function (url) {
123 | return fetchFunc(url, _extends({}, options, { refreshDelay: undefined, fetch: undefined }));
124 | });
125 |
126 | // Wait for all the results to come in, then notify observers.
127 | Promise.all(fetches).then(function (results) {
128 | observers.map(function (observer) {
129 | return observer.next(singleResult ? results[0] : results);
130 | });
131 | _finally();
132 | }).catch(function (error) {
133 | observers.map(function (observer) {
134 | return observer.error(error);
135 | });
136 | _finally();
137 | });
138 | };
139 |
140 | var observable = new _PausableObservable2.default(function (observer) {
141 | observers.push(observer);
142 | observable.resume();
143 |
144 | return function () {
145 | observers.splice(observers.indexOf(observer), 1);
146 |
147 | if (!observers.length) {
148 | observable.pause();
149 | }
150 | };
151 | }, {
152 | onPause: function onPause() {
153 | if (timeout) {
154 | clearTimeout(timeout);
155 | timeout = null;
156 | }
157 | },
158 | onResume: function onResume() {
159 | if (!timeout) {
160 | performFetch();
161 | }
162 | }
163 | });
164 |
165 | return observable;
166 | }
167 |
168 | module.exports = fetchObservable;
169 |
170 | /***/ },
171 | /* 1 */
172 | /***/ function(module, exports, __webpack_require__) {
173 |
174 | "use strict";
175 |
176 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
177 |
178 | var _Observable2 = __webpack_require__(2);
179 |
180 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
181 |
182 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
183 |
184 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
185 |
186 | function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; } /**
187 | * @copyright © 2015, Rick Wong. All rights reserved.
188 | */
189 |
190 | function isPromise(thing) {
191 | return (typeof thing === "undefined" ? "undefined" : _typeof(thing)) === "object" && typeof thing["then"] === "function" && typeof thing["catch"] === "function";
192 | }
193 |
194 | var BetterObservable = (function (_Observable) {
195 | _inherits(BetterObservable, _Observable);
196 |
197 | function BetterObservable() {
198 | _classCallCheck(this, BetterObservable);
199 |
200 | return _possibleConstructorReturn(this, Object.getPrototypeOf(BetterObservable).apply(this, arguments));
201 | }
202 |
203 | _createClass(BetterObservable, [{
204 | key: "map",
205 | value: function map(callback) {
206 | var _this2 = this;
207 |
208 | if (typeof callback !== "function") {
209 | throw new TypeError(callback + " is not a function");
210 | }
211 |
212 | var parentSubscription = null;
213 | var childObservers = [];
214 |
215 | var createParentSubscription = function createParentSubscription() {
216 | if (parentSubscription) {
217 | return;
218 | }
219 |
220 | parentSubscription = _this2.subscribe({
221 | next: function next(value) {
222 | try {
223 | value = callback(value);
224 | } catch (e) {
225 | return childObservers.map(function (o) {
226 | return o.error(e);
227 | });
228 | }
229 |
230 | // Support Promises.
231 | if (isPromise(value)) {
232 | return value.then(function (v) {
233 | return childObservers.map(function (o) {
234 | return o.next(v);
235 | });
236 | }).catch(function (e) {
237 | return childObservers.map(function (o) {
238 | return o.error(e);
239 | });
240 | });
241 | }
242 |
243 | childObservers.map(function (o) {
244 | return o.next(value);
245 | });
246 | },
247 |
248 | error: function error(e) {
249 | return childObservers.map(function (o) {
250 | return o.error(e);
251 | });
252 | },
253 | complete: function complete() {
254 | return childObservers.map(function (o) {
255 | return o.complete();
256 | });
257 | }
258 | });
259 | };
260 |
261 | var destroyParentSubscription = function destroyParentSubscription() {
262 | parentSubscription && parentSubscription.unsubscribe();
263 | parentSubscription = null;
264 | };
265 |
266 | return new this.constructor(function (observer) {
267 | childObservers.push(observer);
268 | createParentSubscription();
269 |
270 | return function () {
271 | childObservers.splice(childObservers.indexOf(observer), 1);
272 |
273 | if (!childObservers.length) {
274 | destroyParentSubscription();
275 | }
276 | };
277 | });
278 | }
279 | }]);
280 |
281 | return BetterObservable;
282 | })(_Observable2.Observable);
283 |
284 | module.exports = BetterObservable;
285 |
286 | /***/ },
287 | /* 2 */
288 | /***/ function(module, exports, __webpack_require__) {
289 |
290 | /* WEBPACK VAR INJECTION */(function(global, process) {"use strict";
291 |
292 | Object.defineProperty(exports, "__esModule", {
293 | value: true
294 | });
295 | exports.Observable = Observable;
296 |
297 | function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; }
298 |
299 | /**
300 | * Observable.js from zenparsing/zen-observable
301 | *
302 | * @copyright © zenparsing
303 | * @homepage https://github.com/zenparsing/zen-observable
304 | * @file https://github.com/zenparsing/zen-observable/blob/de80d63fb166421226bb3c918b111cac40bd672a/src/Observable.js
305 | */
306 | // === Non-Promise Job Queueing ===
307 |
308 | var enqueueJob = (function () {
309 |
310 | // Node
311 | if (typeof global !== "undefined" && typeof process !== "undefined" && process.nextTick) {
312 |
313 | return global.setImmediate ? function (fn) {
314 | global.setImmediate(fn);
315 | } : function (fn) {
316 | process.nextTick(fn);
317 | };
318 | }
319 |
320 | // Newish Browsers
321 | var Observer = self.MutationObserver || self.WebKitMutationObserver;
322 |
323 | if (Observer) {
324 | var _ret = (function () {
325 |
326 | var div = document.createElement("div"),
327 | twiddle = function twiddle(_) {
328 | return div.classList.toggle("x");
329 | },
330 | queue = [];
331 |
332 | var observer = new Observer(function (_) {
333 |
334 | if (queue.length > 1) twiddle();
335 |
336 | while (queue.length > 0) {
337 | queue.shift()();
338 | }
339 | });
340 |
341 | observer.observe(div, { attributes: true });
342 |
343 | return {
344 | v: function v(fn) {
345 |
346 | queue.push(fn);
347 |
348 | if (queue.length === 1) twiddle();
349 | }
350 | };
351 | })();
352 |
353 | if ((typeof _ret === "undefined" ? "undefined" : _typeof(_ret)) === "object") return _ret.v;
354 | }
355 |
356 | // Fallback
357 | return function (fn) {
358 | setTimeout(fn, 0);
359 | };
360 | })();
361 |
362 | // === Symbol Polyfills ===
363 |
364 | function polyfillSymbol(name) {
365 |
366 | if (symbolsSupported() && !Symbol[name]) Object.defineProperty(Symbol, name, { value: Symbol(name) });
367 | }
368 |
369 | function symbolsSupported() {
370 |
371 | return typeof Symbol === "function";
372 | }
373 |
374 | function hasSymbol(name) {
375 |
376 | return symbolsSupported() && Boolean(Symbol[name]);
377 | }
378 |
379 | function getSymbol(name) {
380 |
381 | return hasSymbol(name) ? Symbol[name] : "@@" + name;
382 | }
383 |
384 | polyfillSymbol("observable");
385 |
386 | // === Abstract Operations ===
387 |
388 | function getMethod(obj, key) {
389 |
390 | var value = obj[key];
391 |
392 | if (value == null) return undefined;
393 |
394 | if (typeof value !== "function") throw new TypeError(value + " is not a function");
395 |
396 | return value;
397 | }
398 |
399 | function getSpecies(ctor) {
400 |
401 | var symbol = getSymbol("species");
402 | return symbol ? ctor[symbol] : ctor;
403 | }
404 |
405 | function addMethods(target, methods) {
406 |
407 | Object.keys(methods).forEach(function (k) {
408 |
409 | var desc = Object.getOwnPropertyDescriptor(methods, k);
410 | desc.enumerable = false;
411 | Object.defineProperty(target, k, desc);
412 | });
413 | }
414 |
415 | function cleanupSubscription(observer) {
416 |
417 | // Assert: observer._observer is undefined
418 |
419 | var cleanup = observer._cleanup;
420 |
421 | if (!cleanup) return;
422 |
423 | // Drop the reference to the cleanup function so that we won't call it
424 | // more than once
425 | observer._cleanup = undefined;
426 |
427 | // Call the cleanup function
428 | cleanup();
429 | }
430 |
431 | function subscriptionClosed(observer) {
432 |
433 | return observer._observer === undefined;
434 | }
435 |
436 | function closeSubscription(observer) {
437 |
438 | if (subscriptionClosed(observer)) return;
439 |
440 | observer._observer = undefined;
441 | cleanupSubscription(observer);
442 | }
443 |
444 | function cleanupFromSubscription(subscription) {
445 | // TODO: Should we get the method out and apply it here, instead of
446 | // looking up the method at call time?
447 | return function (_) {
448 | subscription.unsubscribe();
449 | };
450 | }
451 |
452 | function createSubscription(observer, subscriber) {
453 |
454 | // Assert: subscriber is callable
455 |
456 | // The observer must be an object
457 | if (Object(observer) !== observer) throw new TypeError("Observer must be an object");
458 |
459 | // TODO: Should we check for a "next" method here?
460 |
461 | var subscriptionObserver = new SubscriptionObserver(observer),
462 | subscription = new Subscription(subscriptionObserver),
463 | start = getMethod(observer, "start");
464 |
465 | if (start) start.call(observer, subscription);
466 |
467 | if (subscriptionClosed(subscriptionObserver)) return subscription;
468 |
469 | try {
470 |
471 | // Call the subscriber function
472 | var cleanup = subscriber.call(undefined, subscriptionObserver);
473 |
474 | // The return value must be undefined, null, a subscription object, or a function
475 | if (cleanup != null) {
476 |
477 | if (typeof cleanup.unsubscribe === "function") cleanup = cleanupFromSubscription(cleanup);else if (typeof cleanup !== "function") throw new TypeError(cleanup + " is not a function");
478 |
479 | subscriptionObserver._cleanup = cleanup;
480 | }
481 | } catch (e) {
482 |
483 | // If an error occurs during startup, then attempt to send the error
484 | // to the observer
485 | subscriptionObserver.error(e);
486 | return subscription;
487 | }
488 |
489 | // If the stream is already finished, then perform cleanup
490 | if (subscriptionClosed(subscriptionObserver)) cleanupSubscription(subscriptionObserver);
491 |
492 | return subscription;
493 | }
494 |
495 | function SubscriptionObserver(observer) {
496 |
497 | this._observer = observer;
498 | this._cleanup = undefined;
499 | }
500 |
501 | addMethods(SubscriptionObserver.prototype = {}, {
502 |
503 | get closed() {
504 | return subscriptionClosed(this);
505 | },
506 |
507 | next: function next(value) {
508 |
509 | // If the stream if closed, then return undefined
510 | if (subscriptionClosed(this)) return undefined;
511 |
512 | var observer = this._observer;
513 |
514 | try {
515 |
516 | var m = getMethod(observer, "next");
517 |
518 | // If the observer doesn't support "next", then return undefined
519 | if (!m) return undefined;
520 |
521 | // Send the next value to the sink
522 | return m.call(observer, value);
523 | } catch (e) {
524 |
525 | // If the observer throws, then close the stream and rethrow the error
526 | try {
527 | closeSubscription(this);
528 | } finally {
529 | throw e;
530 | }
531 | }
532 | },
533 | error: function error(value) {
534 |
535 | // If the stream is closed, throw the error to the caller
536 | if (subscriptionClosed(this)) throw value;
537 |
538 | var observer = this._observer;
539 | this._observer = undefined;
540 |
541 | try {
542 |
543 | var m = getMethod(observer, "error");
544 |
545 | // If the sink does not support "error", then throw the error to the caller
546 | if (!m) throw value;
547 |
548 | value = m.call(observer, value);
549 | } catch (e) {
550 |
551 | try {
552 | cleanupSubscription(this);
553 | } finally {
554 | throw e;
555 | }
556 | }
557 |
558 | cleanupSubscription(this);
559 |
560 | return value;
561 | },
562 | complete: function complete(value) {
563 |
564 | // If the stream is closed, then return undefined
565 | if (subscriptionClosed(this)) return undefined;
566 |
567 | var observer = this._observer;
568 | this._observer = undefined;
569 |
570 | try {
571 |
572 | var m = getMethod(observer, "complete");
573 |
574 | // If the sink does not support "complete", then return undefined
575 | value = m ? m.call(observer, value) : undefined;
576 | } catch (e) {
577 |
578 | try {
579 | cleanupSubscription(this);
580 | } finally {
581 | throw e;
582 | }
583 | }
584 |
585 | cleanupSubscription(this);
586 |
587 | return value;
588 | }
589 | });
590 |
591 | function Subscription(observer) {
592 | this._observer = observer;
593 | }
594 |
595 | addMethods(Subscription.prototype = {}, {
596 | unsubscribe: function unsubscribe() {
597 | closeSubscription(this._observer);
598 | }
599 | });
600 |
601 | function Observable(subscriber) {
602 |
603 | // The stream subscriber must be a function
604 | if (typeof subscriber !== "function") throw new TypeError("Observable initializer must be a function");
605 |
606 | this._subscriber = subscriber;
607 | }
608 |
609 | addMethods(Observable.prototype, {
610 | subscribe: function subscribe(observer) {
611 |
612 | return createSubscription(observer, this._subscriber);
613 | },
614 | forEach: function forEach(fn) {
615 | var _this = this;
616 |
617 | return new Promise(function (resolve, reject) {
618 |
619 | if (typeof fn !== "function") throw new TypeError(fn + " is not a function");
620 |
621 | _this.subscribe({
622 | next: function next(value) {
623 |
624 | try {
625 | return fn(value);
626 | } catch (e) {
627 | reject(e);
628 | }
629 | },
630 |
631 | error: reject,
632 | complete: resolve
633 | });
634 | });
635 | },
636 | map: function map(fn) {
637 | var _this2 = this;
638 |
639 | if (typeof fn !== "function") throw new TypeError(fn + " is not a function");
640 |
641 | var C = getSpecies(this.constructor);
642 |
643 | return new C(function (observer) {
644 | return _this2.subscribe({
645 | next: function next(value) {
646 |
647 | try {
648 | value = fn(value);
649 | } catch (e) {
650 | return observer.error(e);
651 | }
652 |
653 | return observer.next(value);
654 | },
655 | error: function error(value) {
656 | return observer.error(value);
657 | },
658 | complete: function complete(value) {
659 | return observer.complete(value);
660 | }
661 | });
662 | });
663 | },
664 | filter: function filter(fn) {
665 | var _this3 = this;
666 |
667 | if (typeof fn !== "function") throw new TypeError(fn + " is not a function");
668 |
669 | var C = getSpecies(this.constructor);
670 |
671 | return new C(function (observer) {
672 | return _this3.subscribe({
673 | next: function next(value) {
674 |
675 | try {
676 | if (!fn(value)) return undefined;
677 | } catch (e) {
678 | return observer.error(e);
679 | }
680 |
681 | return observer.next(value);
682 | },
683 | error: function error(value) {
684 | return observer.error(value);
685 | },
686 | complete: function complete(value) {
687 | return observer.complete(value);
688 | }
689 | });
690 | });
691 | }
692 | });
693 |
694 | Object.defineProperty(Observable.prototype, getSymbol("observable"), {
695 | value: function value() {
696 | return this;
697 | },
698 | writable: true,
699 | configurable: true
700 | });
701 |
702 | addMethods(Observable, {
703 | from: function from(x) {
704 |
705 | var C = typeof this === "function" ? this : Observable;
706 |
707 | if (x == null) throw new TypeError(x + " is not an object");
708 |
709 | var method = getMethod(x, getSymbol("observable"));
710 |
711 | if (method) {
712 | var _ret2 = (function () {
713 |
714 | var observable = method.call(x);
715 |
716 | if (Object(observable) !== observable) throw new TypeError(observable + " is not an object");
717 |
718 | if (observable.constructor === C) return {
719 | v: observable
720 | };
721 |
722 | return {
723 | v: new C(function (observer) {
724 | return observable.subscribe(observer);
725 | })
726 | };
727 | })();
728 |
729 | if ((typeof _ret2 === "undefined" ? "undefined" : _typeof(_ret2)) === "object") return _ret2.v;
730 | }
731 |
732 | return new C(function (observer) {
733 |
734 | enqueueJob(function (_) {
735 |
736 | if (observer.closed) return;
737 |
738 | // Assume that the object is iterable. If not, then the observer
739 | // will receive an error.
740 | try {
741 |
742 | if (hasSymbol("iterator")) {
743 | var _iteratorNormalCompletion = true;
744 | var _didIteratorError = false;
745 | var _iteratorError = undefined;
746 |
747 | try {
748 |
749 | for (var _iterator = x[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
750 | var item = _step.value;
751 |
752 | observer.next(item);
753 |
754 | if (observer.closed) return;
755 | }
756 | } catch (err) {
757 | _didIteratorError = true;
758 | _iteratorError = err;
759 | } finally {
760 | try {
761 | if (!_iteratorNormalCompletion && _iterator.return) {
762 | _iterator.return();
763 | }
764 | } finally {
765 | if (_didIteratorError) {
766 | throw _iteratorError;
767 | }
768 | }
769 | }
770 | } else {
771 |
772 | if (!Array.isArray(x)) throw new Error(x + " is not an Array");
773 |
774 | for (var i = 0; i < x.length; ++i) {
775 |
776 | observer.next(x[i]);
777 |
778 | if (observer.closed) return;
779 | }
780 | }
781 | } catch (e) {
782 |
783 | // If observer.next throws an error, then the subscription will
784 | // be closed and the error method will simply rethrow
785 | observer.error(e);
786 | return;
787 | }
788 |
789 | observer.complete();
790 | });
791 | });
792 | },
793 | of: function of() {
794 | for (var _len = arguments.length, items = Array(_len), _key = 0; _key < _len; _key++) {
795 | items[_key] = arguments[_key];
796 | }
797 |
798 | var C = typeof this === "function" ? this : Observable;
799 |
800 | return new C(function (observer) {
801 |
802 | enqueueJob(function (_) {
803 |
804 | if (observer.closed) return;
805 |
806 | for (var i = 0; i < items.length; ++i) {
807 |
808 | observer.next(items[i]);
809 |
810 | if (observer.closed) return;
811 | }
812 |
813 | observer.complete();
814 | });
815 | });
816 | }
817 | });
818 |
819 | Object.defineProperty(Observable, getSymbol("species"), {
820 | get: function get() {
821 | return this;
822 | },
823 |
824 | configurable: true
825 | });
826 | /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(4)))
827 |
828 | /***/ },
829 | /* 3 */
830 | /***/ function(module, exports, __webpack_require__) {
831 |
832 | "use strict";
833 |
834 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
835 |
836 | var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
837 |
838 | var _BetterObservable2 = __webpack_require__(1);
839 |
840 | var _BetterObservable3 = _interopRequireDefault(_BetterObservable2);
841 |
842 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
843 |
844 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
845 |
846 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
847 |
848 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
849 | * @copyright © 2015, Rick Wong. All rights reserved.
850 | */
851 |
852 | var PausableObservable = (function (_BetterObservable) {
853 | _inherits(PausableObservable, _BetterObservable);
854 |
855 | function PausableObservable(subscriber) {
856 | var _ref = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
857 |
858 | var onPause = _ref.onPause;
859 | var onResume = _ref.onResume;
860 |
861 | _classCallCheck(this, PausableObservable);
862 |
863 | var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(PausableObservable).call(this, subscriber));
864 |
865 | _this.state = "paused";
866 |
867 | _this.onPause = onPause;
868 | _this.onResume = onResume;
869 | return _this;
870 | }
871 |
872 | _createClass(PausableObservable, [{
873 | key: "pause",
874 | value: function pause() {
875 | this.state = "paused";
876 |
877 | return this.onPause && this.onPause.apply(this, arguments);
878 | }
879 | }, {
880 | key: "resume",
881 | value: function resume() {
882 | this.state = "resumed";
883 |
884 | return this.onResume && this.onResume.apply(this, arguments);
885 | }
886 | }, {
887 | key: "paused",
888 | value: function paused() {
889 | return this.state === "paused";
890 | }
891 | }, {
892 | key: "map",
893 | value: function map(callback) {
894 | var _this2 = this;
895 |
896 | var pausableObservable = _get(Object.getPrototypeOf(PausableObservable.prototype), "map", this).call(this, callback);
897 |
898 | // Child observable must track parent's state, so bind its pause, resume, and paused.
899 | Object.assign(pausableObservable, {
900 | pause: function pause() {
901 | return _this2.pause.apply(_this2, arguments);
902 | },
903 | resume: function resume() {
904 | return _this2.resume.apply(_this2, arguments);
905 | },
906 | paused: function paused() {
907 | return _this2.paused();
908 | }
909 | });
910 |
911 | return pausableObservable;
912 | }
913 | }]);
914 |
915 | return PausableObservable;
916 | })(_BetterObservable3.default);
917 |
918 | module.exports = PausableObservable;
919 |
920 | /***/ },
921 | /* 4 */
922 | /***/ function(module, exports) {
923 |
924 | // shim for using process in browser
925 |
926 | var process = module.exports = {};
927 | var queue = [];
928 | var draining = false;
929 | var currentQueue;
930 | var queueIndex = -1;
931 |
932 | function cleanUpNextTick() {
933 | draining = false;
934 | if (currentQueue.length) {
935 | queue = currentQueue.concat(queue);
936 | } else {
937 | queueIndex = -1;
938 | }
939 | if (queue.length) {
940 | drainQueue();
941 | }
942 | }
943 |
944 | function drainQueue() {
945 | if (draining) {
946 | return;
947 | }
948 | var timeout = setTimeout(cleanUpNextTick);
949 | draining = true;
950 |
951 | var len = queue.length;
952 | while(len) {
953 | currentQueue = queue;
954 | queue = [];
955 | while (++queueIndex < len) {
956 | if (currentQueue) {
957 | currentQueue[queueIndex].run();
958 | }
959 | }
960 | queueIndex = -1;
961 | len = queue.length;
962 | }
963 | currentQueue = null;
964 | draining = false;
965 | clearTimeout(timeout);
966 | }
967 |
968 | process.nextTick = function (fun) {
969 | var args = new Array(arguments.length - 1);
970 | if (arguments.length > 1) {
971 | for (var i = 1; i < arguments.length; i++) {
972 | args[i - 1] = arguments[i];
973 | }
974 | }
975 | queue.push(new Item(fun, args));
976 | if (queue.length === 1 && !draining) {
977 | setTimeout(drainQueue, 0);
978 | }
979 | };
980 |
981 | // v8 likes predictible objects
982 | function Item(fun, array) {
983 | this.fun = fun;
984 | this.array = array;
985 | }
986 | Item.prototype.run = function () {
987 | this.fun.apply(null, this.array);
988 | };
989 | process.title = 'browser';
990 | process.browser = true;
991 | process.env = {};
992 | process.argv = [];
993 | process.version = ''; // empty string to avoid regexp issues
994 | process.versions = {};
995 |
996 | function noop() {}
997 |
998 | process.on = noop;
999 | process.addListener = noop;
1000 | process.once = noop;
1001 | process.off = noop;
1002 | process.removeListener = noop;
1003 | process.removeAllListeners = noop;
1004 | process.emit = noop;
1005 |
1006 | process.binding = function (name) {
1007 | throw new Error('process.binding is not supported');
1008 | };
1009 |
1010 | process.cwd = function () { return '/' };
1011 | process.chdir = function (dir) {
1012 | throw new Error('process.chdir is not supported');
1013 | };
1014 | process.umask = function() { return 0; };
1015 |
1016 |
1017 | /***/ }
1018 | /******/ ])
1019 | });
1020 | ;
--------------------------------------------------------------------------------