├── .babelrc
├── .gitignore
├── API.md
├── README.md
├── demo
├── nqueen
│ ├── actions
│ │ └── nqueen
│ │ │ └── nqueen.js
│ ├── components
│ │ ├── App.js
│ │ ├── Blinker.js
│ │ ├── Counter.js
│ │ ├── Nqueen.js
│ │ ├── Slider.js
│ │ └── Spinner.js
│ ├── containers
│ │ └── NqueenContainer.js
│ ├── dist
│ │ ├── main.bundle.js
│ │ └── worker.bundle.js
│ ├── index.html
│ ├── main.js
│ ├── package.json
│ ├── reducers
│ │ ├── index.js
│ │ ├── nqueen
│ │ │ └── nqueen.js
│ │ └── worker.js
│ ├── solver.js
│ └── webpack.config.js
└── sortTable
│ ├── actions
│ └── users
│ │ └── users.js
│ ├── components
│ ├── App.js
│ ├── Generator.js
│ ├── Spinner.js
│ └── UserTable.js
│ ├── containers
│ ├── GeneratorContainer.js
│ └── UserTableContainer.js
│ ├── dist
│ ├── main.bundle.js
│ └── worker.bundle.js
│ ├── index.html
│ ├── main.js
│ ├── package.json
│ ├── reducers
│ ├── index.js
│ ├── users
│ │ └── users.js
│ └── worker.js
│ ├── usersFixtures.js
│ └── webpack.config.js
├── dist
└── reduxWorker.js
├── karma.conf.js
├── package.json
├── spec
├── applyWorker.spec.js
├── createWorker.spec.js
└── test-context.js
├── src
├── applyWorker.js
├── createWorker.js
└── reduxWorker.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react"],
3 | "plugins": ["transform-object-assign"]
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | *.log
4 |
--------------------------------------------------------------------------------
/API.md:
--------------------------------------------------------------------------------
1 | # API
2 |
3 | ## createWorker
4 | This helper function should be called within your worker. It will return an instance of `ReduxWorker`, which provides the following methods for you to set up your web worker:
5 |
6 | - registerReducer(reducer)
7 | - registerTask(taskName, taskCallback)
8 |
9 | Example:
10 | ```js
11 | import reducer from '../reducers'
12 | import solve from '../solver'
13 | import { createWorker } from 'redux-worker'
14 |
15 | // Instantiate ReduxWorker
16 | let worker = createWorker();
17 |
18 | // Registering your reducer.
19 | worker.registerReducer(reducer);
20 |
21 | // Register tasks to be executable on web worker, if needed
22 | worker.registerTask('NQUEEN_TASK', function(a) {
23 | let n = a.number;
24 | return +n < 16 ? solve(+n).length : 'N is too large...';
25 | });
26 | ```
27 |
28 | ## applyWorker
29 | This helper function is a store enhancer that decorates the Redux store. It is intended to be applied to your store configuration as a middleware. If an invalid web worker is provided here, the middleware will fall back to creating a regular redux store.
30 |
31 | Example:
32 | ```js
33 | import React from 'react'
34 | import { render } from 'react-dom'
35 | import { Provider } from 'react-redux'
36 | import { createStore, applyMiddleware, compose } from 'redux'
37 | import thunk from 'redux-thunk'
38 | import rootReducer from './reducers'
39 | import App from './components/App'
40 | import { applyWorker } from 'redux-worker'
41 |
42 | // Spinning up the web worker.
43 | // worker.bundle.js comes from a build process that I set up to compile and serve worker.js
44 | // created using createWorker.
45 | // If you do not want to have a separate build process for this, look into the following:
46 | // - webworker-loader for Web Pack (https://github.com/bjyoungblood/webworker-loader)
47 | // - webworkify for browerify (https://github.com/substack/webworkify)
48 |
49 | const worker = window.Worker ? new Worker('./dist/worker.bundle.js') : null;
50 |
51 | // Apply worker middleware
52 | const enhancerWithWorker = compose(
53 | applyMiddleware(thunk),
54 | applyWorker(worker)
55 | );
56 |
57 | const store = createStore(rootReducer, {}, enhancerWithWorker);
58 |
59 | render(
60 |
61 |
62 | ,
63 | document.getElementById('app')
64 | )
65 | ```
66 |
67 | ## dispatch
68 | The new dispatcher will have the following flow:
69 | 1. Post a message to redux-worker
70 | 2. Redux-worker uses the reducer to change the state of the store
71 | 3. Redux-worker posts a message back to main thread
72 | 4. Main thread updates the new state
73 | 5. All subscribed components are rendered
74 |
75 | You can now also dispatch a task to be executed in `redux-worker` outside the context of Redux: useful for tasks like encryption/decryption, rich text formatting, etc.
76 |
77 | Task Creator
78 | ```js
79 | // This is basically the same as your action creators, except that instead of 'type', it
80 | // expects 'task'. This 'task' attribute must equal to what's provided to registerTask
81 | const calcNqueenTask = (n) => {
82 | return {
83 | task: 'NQUEEN_TASK',
84 | number: n
85 | };
86 | }
87 | ```
88 |
89 | Usage
90 | ```js
91 | // When dispatching a task, the dispatcher will return a promise that
92 | // will be resolved when the task is completed.
93 | this.props.actions
94 | .calcNqueenTask(this.state.inputValue)
95 | .then(function(returnObj) {
96 | // The returnObj contains _taskId and response
97 | // response is whatever the return value is based on
98 | // the callback provided to registerTask
99 | alert(
100 | `
101 | This task is run directly on the web worker without going thru Redux.\n
102 | The taskId is ${returnObj._taskId}.\n
103 | The answer is ${returnObj.response}.
104 | `
105 | );
106 | });
107 | ```
108 |
109 |
110 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # redux-worker
2 |
3 | Redux-Worker is a [middleware](http://redux.js.org/docs/advanced/Middleware.html) for [Redux](https://github.com/reactjs/redux).
4 |
5 | It helps you build multi-threaded JavaScript applications by moving your reducer into a Web Worker. It also provides a simple [API](https://github.com/chikeichan/redux-worker/blob/master/API.md) for you to register tasks to be executed in the web worker outside of Redux.
6 |
7 | Check out the demo:
8 | 
9 | *(Right) Performing synchronous calculations on a separate worker thread, without blocking the main thread.*
10 |
11 |
12 | ## Documentation
13 | See the [API docs](https://github.com/chikeichan/redux-worker/blob/master/API.md).
14 |
15 | ## Inspiration
16 |
17 | The [Web Workers API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) has been around for several years now, but we have yet to see much adoption from the community. I think this is mostly due to setup complexity, asynchronous message passing, and the need to think in a different context (one can't share functions/references between Worker threads).
18 |
19 | It used to be difficult to conceptualize how to harness the power of Web Workers in an MVC application due to the constraint above. However, Redux has changed the way we think about building applications in JavaScript. Its [three principles](https://github.com/reactjs/redux/blob/master/docs/introduction/ThreePrinciples.md) make it easy to move your app logic into a separate task.
20 |
21 | If you are not familiar with web workers, I'd recommend starting with the following materials.
22 | - [How fast are web workers?](https://hacks.mozilla.org/2015/07/how-fast-are-web-workers/) - by Guillaume Cedric Marty
23 | - [Using Web Workers for more responsive apps](https://www.youtube.com/watch?v=Kz_zKXiNGSE) - by Jason Teplitz
24 |
25 | ## Limitations
26 |
27 | `Redux-Worker` helps you use web workers in your Redux applications. It does so by enhancing your Redux store with:
28 | - A dispatcher that posts messages to a worker thread
29 | - A replacement reducer that listens to messages from a Web Worker and then updates state
30 | - A mechanism to register and execute tasks in a Web Worker using the dispatcher, which returns a promise that will resolve when the task completes.
31 |
32 | It spawns just one Web Worker instance, so don't give it more credit than it deserves. It does not provide an interface for spawning multiple workers and balancing work between them: this is a much more complicated problem that Redux-Worker is not attempting to solve.
33 |
34 | ## Demos
35 | - [Solver for N-Queens](http://chikeichan.github.io/redux-worker/demo/nqueen/index.html)
36 |
37 | To run a demo locally, cd into a folder in the demo directory, and then:
38 | ```bash
39 | npm i
40 | npm run watch
41 | ```
42 |
--------------------------------------------------------------------------------
/demo/nqueen/actions/nqueen/nqueen.js:
--------------------------------------------------------------------------------
1 | const calculateNQueen = (n) => {
2 | return function(dispatch) {
3 | dispatch({
4 | type: 'START_NQUEEN'
5 | });
6 |
7 | setTimeout(function() {
8 | dispatch({
9 | type: 'CALCULATE_NQUEEN',
10 | number: n
11 | });
12 |
13 | dispatch({
14 | type: 'COMPLETE_NQUEEN'
15 | });
16 | }, 200);
17 |
18 | }
19 | }
20 |
21 | const testWorker = (n) => {
22 | return {
23 | task: 'NQUEEN_TASK',
24 | number: n
25 | };
26 | }
27 |
28 | export default {
29 | calculateNQueen,
30 | testWorker
31 | }
--------------------------------------------------------------------------------
/demo/nqueen/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import NqueenContainer from '../containers/NqueenContainer'
3 | import Counter from '../components/Counter'
4 | import Blinker from '../components/Blinker'
5 | import Spinner from '../components/Spinner'
6 | import Slider from '../components/Slider'
7 |
8 | const App = () => (
9 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
34 | )
35 |
36 | export default App
--------------------------------------------------------------------------------
/demo/nqueen/components/Blinker.js:
--------------------------------------------------------------------------------
1 | import React, { createClass } from 'react'
2 |
3 | export default createClass({
4 | getInitialState() {
5 | return {
6 | blink: true
7 | };
8 | },
9 |
10 | componentDidMount() {
11 | setInterval(function() {
12 | this.setState({
13 | blink: !this.state.blink
14 | });
15 | }.bind(this), 500);
16 | },
17 |
18 | render() {
19 | return (
20 |
33 |
Blinker
34 |
35 | )
36 | },
37 | });
--------------------------------------------------------------------------------
/demo/nqueen/components/Counter.js:
--------------------------------------------------------------------------------
1 | import React, { createClass } from 'react'
2 |
3 | export default createClass({
4 | getInitialState() {
5 | return {
6 | counter: 0
7 | };
8 | },
9 |
10 | componentDidMount() {
11 | setInterval(function() {
12 | this.setState({
13 | counter: this.state.counter + 1
14 | });
15 | }.bind(this), 16);
16 | },
17 |
18 | render() {
19 | return (
20 |
32 |
Counter
33 | { this.state.counter }
34 |
35 | )
36 | },
37 | });
--------------------------------------------------------------------------------
/demo/nqueen/components/Nqueen.js:
--------------------------------------------------------------------------------
1 | import React, { createClass } from 'react'
2 |
3 | export default createClass({
4 | getDefaultProps() {
5 | return {
6 | numberOfSquares: 1,
7 | answer: null,
8 | actions: {
9 | calculateNQueen: function() {}
10 | }
11 | };
12 | },
13 |
14 | getInitialState() {
15 | return {
16 | inputValue: 1
17 | };
18 | },
19 |
20 | toggleWebWorker() {
21 | var disableWebWorker = window.disableWebWorker;
22 | window.disableWebWorker = !disableWebWorker;
23 | this.forceUpdate();
24 | },
25 |
26 | onChange(e) {
27 | this.setState({
28 | inputValue: e.target.value
29 | });
30 | },
31 |
32 | onClick() {
33 | if (this.props.isCalculating) {
34 | return;
35 | }
36 | this.props.actions.calculateNQueen(this.state.inputValue);
37 | },
38 |
39 | runTask() {
40 | this.props.actions.testWorker(this.state.inputValue)
41 | .then(function(e) {
42 | alert(
43 | `This task is run directly on the web worker without going thru Redux.\nThe taskId is ${e._taskId}.\nThe answer is ${e.response}.`
44 | );
45 | });
46 | },
47 |
48 | render() {
49 | const { numberOfSquares, answer, isCalculating } = this.props
50 |
51 | let ans, bgColor
52 |
53 | if (isCalculating) {
54 | ans = 'calculating...'
55 | bgColor = 'rgba(255, 0, 0, 0.2)'
56 | } else {
57 | ans = 'Answer is ' + answer;
58 | bgColor = 'rgba(0, 255, 0, 0.2)'
59 | }
60 |
61 | return (
62 |
73 |
N-Queen Solver
74 |
87 |
Web Worker
88 |
{ window.disableWebWorker ? 'OFF' : 'ON'}
93 |
94 |
{ 'N = ' + numberOfSquares}
95 |
{ ans }
96 |
107 |
116 | Calc
117 |
118 |
Run without Redux
127 |
128 | )
129 | }
130 | })
--------------------------------------------------------------------------------
/demo/nqueen/components/Slider.js:
--------------------------------------------------------------------------------
1 | import React, { createClass } from 'react'
2 |
3 | export default createClass({
4 | getInitialState() {
5 | return {
6 | steps: 0
7 | };
8 | },
9 |
10 | componentDidMount() {
11 | var v = 5;
12 | setInterval(function() {
13 | if (this.state.steps > 460) {
14 | v = -5;
15 | }
16 |
17 | if (this.state.steps < 0) {
18 | v = 5;
19 | }
20 |
21 | this.setState({
22 | steps: this.state.steps + v
23 | });
24 | }.bind(this), 16);
25 | },
26 |
27 | render() {
28 | return (
29 |
42 |
Slider
43 |
44 | )
45 | },
46 | });
--------------------------------------------------------------------------------
/demo/nqueen/components/Spinner.js:
--------------------------------------------------------------------------------
1 | import React, { createClass } from 'react'
2 |
3 | export default createClass({
4 | getInitialState() {
5 | return {
6 | degree: 0
7 | };
8 | },
9 |
10 | componentDidMount() {
11 | setInterval(function() {
12 | this.setState({
13 | degree: this.state.degree + 5
14 | });
15 | }.bind(this), 16);
16 | },
17 |
18 | render() {
19 | return (
20 |
33 |
Spinner
34 |
35 | )
36 | },
37 | });
--------------------------------------------------------------------------------
/demo/nqueen/containers/NqueenContainer.js:
--------------------------------------------------------------------------------
1 | import React, { createClass } from 'react'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators} from 'redux'
4 | import Nqueen from '../components/Nqueen'
5 | import actions from '../actions/nqueen/nqueen.js'
6 |
7 | const NqueenContainer = createClass({
8 | render() {
9 | return
10 | }
11 | })
12 |
13 | function mapStateToProps(state) {
14 | return {
15 | answer: state.nqueen.answer,
16 | numberOfSquares: state.nqueen.numberOfSquares,
17 | isCalculating: state.nqueen.isCalculating
18 | }
19 | }
20 |
21 | function mapDispatcherToProps(dispatch) {
22 | return {
23 | actions: bindActionCreators(actions, dispatch)
24 | }
25 | }
26 |
27 | export default connect(
28 | mapStateToProps,
29 | mapDispatcherToProps
30 | )(NqueenContainer);
31 |
--------------------------------------------------------------------------------
/demo/nqueen/dist/worker.bundle.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 |
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 |
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId])
10 | /******/ return installedModules[moduleId].exports;
11 |
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ exports: {},
15 | /******/ id: moduleId,
16 | /******/ loaded: false
17 | /******/ };
18 |
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 |
22 | /******/ // Flag the module as loaded
23 | /******/ module.loaded = true;
24 |
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 |
29 |
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 |
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 |
36 | /******/ // __webpack_public_path__
37 | /******/ __webpack_require__.p = "";
38 |
39 | /******/ // Load entry module and return exports
40 | /******/ return __webpack_require__(0);
41 | /******/ })
42 | /************************************************************************/
43 | /******/ ({
44 |
45 | /***/ 0:
46 | /***/ function(module, exports, __webpack_require__) {
47 |
48 | 'use strict';
49 |
50 | var _reducers = __webpack_require__(178);
51 |
52 | var _reducers2 = _interopRequireDefault(_reducers);
53 |
54 | var _solver = __webpack_require__(180);
55 |
56 | var _solver2 = _interopRequireDefault(_solver);
57 |
58 | var _reduxWorker = __webpack_require__(189);
59 |
60 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
61 |
62 | var worker = (0, _reduxWorker.createWorker)();
63 |
64 | worker.registerReducer(_reducers2.default);
65 |
66 | worker.registerTask('NQUEEN_TASK', function (a) {
67 | var n = a.number;
68 | return +n < 16 ? (0, _solver2.default)(+n).length : 'N is too large...';
69 | });
70 |
71 | /***/ },
72 |
73 | /***/ 4:
74 | /***/ function(module, exports) {
75 |
76 | // shim for using process in browser
77 |
78 | var process = module.exports = {};
79 | var queue = [];
80 | var draining = false;
81 | var currentQueue;
82 | var queueIndex = -1;
83 |
84 | function cleanUpNextTick() {
85 | draining = false;
86 | if (currentQueue.length) {
87 | queue = currentQueue.concat(queue);
88 | } else {
89 | queueIndex = -1;
90 | }
91 | if (queue.length) {
92 | drainQueue();
93 | }
94 | }
95 |
96 | function drainQueue() {
97 | if (draining) {
98 | return;
99 | }
100 | var timeout = setTimeout(cleanUpNextTick);
101 | draining = true;
102 |
103 | var len = queue.length;
104 | while(len) {
105 | currentQueue = queue;
106 | queue = [];
107 | while (++queueIndex < len) {
108 | if (currentQueue) {
109 | currentQueue[queueIndex].run();
110 | }
111 | }
112 | queueIndex = -1;
113 | len = queue.length;
114 | }
115 | currentQueue = null;
116 | draining = false;
117 | clearTimeout(timeout);
118 | }
119 |
120 | process.nextTick = function (fun) {
121 | var args = new Array(arguments.length - 1);
122 | if (arguments.length > 1) {
123 | for (var i = 1; i < arguments.length; i++) {
124 | args[i - 1] = arguments[i];
125 | }
126 | }
127 | queue.push(new Item(fun, args));
128 | if (queue.length === 1 && !draining) {
129 | setTimeout(drainQueue, 0);
130 | }
131 | };
132 |
133 | // v8 likes predictible objects
134 | function Item(fun, array) {
135 | this.fun = fun;
136 | this.array = array;
137 | }
138 | Item.prototype.run = function () {
139 | this.fun.apply(null, this.array);
140 | };
141 | process.title = 'browser';
142 | process.browser = true;
143 | process.env = {};
144 | process.argv = [];
145 | process.version = ''; // empty string to avoid regexp issues
146 | process.versions = {};
147 |
148 | function noop() {}
149 |
150 | process.on = noop;
151 | process.addListener = noop;
152 | process.once = noop;
153 | process.off = noop;
154 | process.removeListener = noop;
155 | process.removeAllListeners = noop;
156 | process.emit = noop;
157 |
158 | process.binding = function (name) {
159 | throw new Error('process.binding is not supported');
160 | };
161 |
162 | process.cwd = function () { return '/' };
163 | process.chdir = function (dir) {
164 | throw new Error('process.chdir is not supported');
165 | };
166 | process.umask = function() { return 0; };
167 |
168 |
169 | /***/ },
170 |
171 | /***/ 165:
172 | /***/ function(module, exports, __webpack_require__) {
173 |
174 | /* WEBPACK VAR INJECTION */(function(process) {'use strict';
175 |
176 | exports.__esModule = true;
177 | exports.compose = exports.applyMiddleware = exports.bindActionCreators = exports.combineReducers = exports.createStore = undefined;
178 |
179 | var _createStore = __webpack_require__(166);
180 |
181 | var _createStore2 = _interopRequireDefault(_createStore);
182 |
183 | var _combineReducers = __webpack_require__(170);
184 |
185 | var _combineReducers2 = _interopRequireDefault(_combineReducers);
186 |
187 | var _bindActionCreators = __webpack_require__(172);
188 |
189 | var _bindActionCreators2 = _interopRequireDefault(_bindActionCreators);
190 |
191 | var _applyMiddleware = __webpack_require__(173);
192 |
193 | var _applyMiddleware2 = _interopRequireDefault(_applyMiddleware);
194 |
195 | var _compose = __webpack_require__(174);
196 |
197 | var _compose2 = _interopRequireDefault(_compose);
198 |
199 | var _warning = __webpack_require__(171);
200 |
201 | var _warning2 = _interopRequireDefault(_warning);
202 |
203 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
204 |
205 | /*
206 | * This is a dummy function to check if the function name has been altered by minification.
207 | * If the function has been minified and NODE_ENV !== 'production', warn the user.
208 | */
209 | function isCrushed() {}
210 |
211 | if (process.env.NODE_ENV !== 'production' && typeof isCrushed.name === 'string' && isCrushed.name !== 'isCrushed') {
212 | (0, _warning2["default"])('You are currently using minified code outside of NODE_ENV === \'production\'. ' + 'This means that you are running a slower development build of Redux. ' + 'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' + 'or DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) ' + 'to ensure you have the correct code for your production build.');
213 | }
214 |
215 | exports.createStore = _createStore2["default"];
216 | exports.combineReducers = _combineReducers2["default"];
217 | exports.bindActionCreators = _bindActionCreators2["default"];
218 | exports.applyMiddleware = _applyMiddleware2["default"];
219 | exports.compose = _compose2["default"];
220 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
221 |
222 | /***/ },
223 |
224 | /***/ 166:
225 | /***/ function(module, exports, __webpack_require__) {
226 |
227 | 'use strict';
228 |
229 | exports.__esModule = true;
230 | exports.ActionTypes = undefined;
231 | exports["default"] = createStore;
232 |
233 | var _isPlainObject = __webpack_require__(167);
234 |
235 | var _isPlainObject2 = _interopRequireDefault(_isPlainObject);
236 |
237 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
238 |
239 | /**
240 | * These are private action types reserved by Redux.
241 | * For any unknown actions, you must return the current state.
242 | * If the current state is undefined, you must return the initial state.
243 | * Do not reference these action types directly in your code.
244 | */
245 | var ActionTypes = exports.ActionTypes = {
246 | INIT: '@@redux/INIT'
247 | };
248 |
249 | /**
250 | * Creates a Redux store that holds the state tree.
251 | * The only way to change the data in the store is to call `dispatch()` on it.
252 | *
253 | * There should only be a single store in your app. To specify how different
254 | * parts of the state tree respond to actions, you may combine several reducers
255 | * into a single reducer function by using `combineReducers`.
256 | *
257 | * @param {Function} reducer A function that returns the next state tree, given
258 | * the current state tree and the action to handle.
259 | *
260 | * @param {any} [initialState] The initial state. You may optionally specify it
261 | * to hydrate the state from the server in universal apps, or to restore a
262 | * previously serialized user session.
263 | * If you use `combineReducers` to produce the root reducer function, this must be
264 | * an object with the same shape as `combineReducers` keys.
265 | *
266 | * @param {Function} enhancer The store enhancer. You may optionally specify it
267 | * to enhance the store with third-party capabilities such as middleware,
268 | * time travel, persistence, etc. The only store enhancer that ships with Redux
269 | * is `applyMiddleware()`.
270 | *
271 | * @returns {Store} A Redux store that lets you read the state, dispatch actions
272 | * and subscribe to changes.
273 | */
274 | function createStore(reducer, initialState, enhancer) {
275 | if (typeof initialState === 'function' && typeof enhancer === 'undefined') {
276 | enhancer = initialState;
277 | initialState = undefined;
278 | }
279 |
280 | if (typeof enhancer !== 'undefined') {
281 | if (typeof enhancer !== 'function') {
282 | throw new Error('Expected the enhancer to be a function.');
283 | }
284 |
285 | return enhancer(createStore)(reducer, initialState);
286 | }
287 |
288 | if (typeof reducer !== 'function') {
289 | throw new Error('Expected the reducer to be a function.');
290 | }
291 |
292 | var currentReducer = reducer;
293 | var currentState = initialState;
294 | var currentListeners = [];
295 | var nextListeners = currentListeners;
296 | var isDispatching = false;
297 |
298 | function ensureCanMutateNextListeners() {
299 | if (nextListeners === currentListeners) {
300 | nextListeners = currentListeners.slice();
301 | }
302 | }
303 |
304 | /**
305 | * Reads the state tree managed by the store.
306 | *
307 | * @returns {any} The current state tree of your application.
308 | */
309 | function getState() {
310 | return currentState;
311 | }
312 |
313 | /**
314 | * Adds a change listener. It will be called any time an action is dispatched,
315 | * and some part of the state tree may potentially have changed. You may then
316 | * call `getState()` to read the current state tree inside the callback.
317 | *
318 | * You may call `dispatch()` from a change listener, with the following
319 | * caveats:
320 | *
321 | * 1. The subscriptions are snapshotted just before every `dispatch()` call.
322 | * If you subscribe or unsubscribe while the listeners are being invoked, this
323 | * will not have any effect on the `dispatch()` that is currently in progress.
324 | * However, the next `dispatch()` call, whether nested or not, will use a more
325 | * recent snapshot of the subscription list.
326 | *
327 | * 2. The listener should not expect to see all states changes, as the state
328 | * might have been updated multiple times during a nested `dispatch()` before
329 | * the listener is called. It is, however, guaranteed that all subscribers
330 | * registered before the `dispatch()` started will be called with the latest
331 | * state by the time it exits.
332 | *
333 | * @param {Function} listener A callback to be invoked on every dispatch.
334 | * @returns {Function} A function to remove this change listener.
335 | */
336 | function subscribe(listener) {
337 | if (typeof listener !== 'function') {
338 | throw new Error('Expected listener to be a function.');
339 | }
340 |
341 | var isSubscribed = true;
342 |
343 | ensureCanMutateNextListeners();
344 | nextListeners.push(listener);
345 |
346 | return function unsubscribe() {
347 | if (!isSubscribed) {
348 | return;
349 | }
350 |
351 | isSubscribed = false;
352 |
353 | ensureCanMutateNextListeners();
354 | var index = nextListeners.indexOf(listener);
355 | nextListeners.splice(index, 1);
356 | };
357 | }
358 |
359 | /**
360 | * Dispatches an action. It is the only way to trigger a state change.
361 | *
362 | * The `reducer` function, used to create the store, will be called with the
363 | * current state tree and the given `action`. Its return value will
364 | * be considered the **next** state of the tree, and the change listeners
365 | * will be notified.
366 | *
367 | * The base implementation only supports plain object actions. If you want to
368 | * dispatch a Promise, an Observable, a thunk, or something else, you need to
369 | * wrap your store creating function into the corresponding middleware. For
370 | * example, see the documentation for the `redux-thunk` package. Even the
371 | * middleware will eventually dispatch plain object actions using this method.
372 | *
373 | * @param {Object} action A plain object representing “what changed”. It is
374 | * a good idea to keep actions serializable so you can record and replay user
375 | * sessions, or use the time travelling `redux-devtools`. An action must have
376 | * a `type` property which may not be `undefined`. It is a good idea to use
377 | * string constants for action types.
378 | *
379 | * @returns {Object} For convenience, the same action object you dispatched.
380 | *
381 | * Note that, if you use a custom middleware, it may wrap `dispatch()` to
382 | * return something else (for example, a Promise you can await).
383 | */
384 | function dispatch(action) {
385 | if (!(0, _isPlainObject2["default"])(action)) {
386 | throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
387 | }
388 |
389 | if (typeof action.type === 'undefined') {
390 | throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
391 | }
392 |
393 | if (isDispatching) {
394 | throw new Error('Reducers may not dispatch actions.');
395 | }
396 |
397 | try {
398 | isDispatching = true;
399 | currentState = currentReducer(currentState, action);
400 | } finally {
401 | isDispatching = false;
402 | }
403 |
404 | var listeners = currentListeners = nextListeners;
405 | for (var i = 0; i < listeners.length; i++) {
406 | listeners[i]();
407 | }
408 |
409 | return action;
410 | }
411 |
412 | /**
413 | * Replaces the reducer currently used by the store to calculate the state.
414 | *
415 | * You might need this if your app implements code splitting and you want to
416 | * load some of the reducers dynamically. You might also need this if you
417 | * implement a hot reloading mechanism for Redux.
418 | *
419 | * @param {Function} nextReducer The reducer for the store to use instead.
420 | * @returns {void}
421 | */
422 | function replaceReducer(nextReducer) {
423 | if (typeof nextReducer !== 'function') {
424 | throw new Error('Expected the nextReducer to be a function.');
425 | }
426 |
427 | currentReducer = nextReducer;
428 | dispatch({ type: ActionTypes.INIT });
429 | }
430 |
431 | // When a store is created, an "INIT" action is dispatched so that every
432 | // reducer returns their initial state. This effectively populates
433 | // the initial state tree.
434 | dispatch({ type: ActionTypes.INIT });
435 |
436 | return {
437 | dispatch: dispatch,
438 | subscribe: subscribe,
439 | getState: getState,
440 | replaceReducer: replaceReducer
441 | };
442 | }
443 |
444 | /***/ },
445 |
446 | /***/ 167:
447 | /***/ function(module, exports, __webpack_require__) {
448 |
449 | var isHostObject = __webpack_require__(168),
450 | isObjectLike = __webpack_require__(169);
451 |
452 | /** `Object#toString` result references. */
453 | var objectTag = '[object Object]';
454 |
455 | /** Used for built-in method references. */
456 | var objectProto = Object.prototype;
457 |
458 | /** Used to resolve the decompiled source of functions. */
459 | var funcToString = Function.prototype.toString;
460 |
461 | /** Used to infer the `Object` constructor. */
462 | var objectCtorString = funcToString.call(Object);
463 |
464 | /**
465 | * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
466 | * of values.
467 | */
468 | var objectToString = objectProto.toString;
469 |
470 | /** Built-in value references. */
471 | var getPrototypeOf = Object.getPrototypeOf;
472 |
473 | /**
474 | * Checks if `value` is a plain object, that is, an object created by the
475 | * `Object` constructor or one with a `[[Prototype]]` of `null`.
476 | *
477 | * @static
478 | * @memberOf _
479 | * @category Lang
480 | * @param {*} value The value to check.
481 | * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
482 | * @example
483 | *
484 | * function Foo() {
485 | * this.a = 1;
486 | * }
487 | *
488 | * _.isPlainObject(new Foo);
489 | * // => false
490 | *
491 | * _.isPlainObject([1, 2, 3]);
492 | * // => false
493 | *
494 | * _.isPlainObject({ 'x': 0, 'y': 0 });
495 | * // => true
496 | *
497 | * _.isPlainObject(Object.create(null));
498 | * // => true
499 | */
500 | function isPlainObject(value) {
501 | if (!isObjectLike(value) ||
502 | objectToString.call(value) != objectTag || isHostObject(value)) {
503 | return false;
504 | }
505 | var proto = getPrototypeOf(value);
506 | if (proto === null) {
507 | return true;
508 | }
509 | var Ctor = proto.constructor;
510 | return (typeof Ctor == 'function' &&
511 | Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
512 | }
513 |
514 | module.exports = isPlainObject;
515 |
516 |
517 | /***/ },
518 |
519 | /***/ 168:
520 | /***/ function(module, exports) {
521 |
522 | /**
523 | * Checks if `value` is a host object in IE < 9.
524 | *
525 | * @private
526 | * @param {*} value The value to check.
527 | * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
528 | */
529 | function isHostObject(value) {
530 | // Many host objects are `Object` objects that can coerce to strings
531 | // despite having improperly defined `toString` methods.
532 | var result = false;
533 | if (value != null && typeof value.toString != 'function') {
534 | try {
535 | result = !!(value + '');
536 | } catch (e) {}
537 | }
538 | return result;
539 | }
540 |
541 | module.exports = isHostObject;
542 |
543 |
544 | /***/ },
545 |
546 | /***/ 169:
547 | /***/ function(module, exports) {
548 |
549 | /**
550 | * Checks if `value` is object-like. A value is object-like if it's not `null`
551 | * and has a `typeof` result of "object".
552 | *
553 | * @static
554 | * @memberOf _
555 | * @category Lang
556 | * @param {*} value The value to check.
557 | * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
558 | * @example
559 | *
560 | * _.isObjectLike({});
561 | * // => true
562 | *
563 | * _.isObjectLike([1, 2, 3]);
564 | * // => true
565 | *
566 | * _.isObjectLike(_.noop);
567 | * // => false
568 | *
569 | * _.isObjectLike(null);
570 | * // => false
571 | */
572 | function isObjectLike(value) {
573 | return !!value && typeof value == 'object';
574 | }
575 |
576 | module.exports = isObjectLike;
577 |
578 |
579 | /***/ },
580 |
581 | /***/ 170:
582 | /***/ function(module, exports, __webpack_require__) {
583 |
584 | /* WEBPACK VAR INJECTION */(function(process) {'use strict';
585 |
586 | exports.__esModule = true;
587 | exports["default"] = combineReducers;
588 |
589 | var _createStore = __webpack_require__(166);
590 |
591 | var _isPlainObject = __webpack_require__(167);
592 |
593 | var _isPlainObject2 = _interopRequireDefault(_isPlainObject);
594 |
595 | var _warning = __webpack_require__(171);
596 |
597 | var _warning2 = _interopRequireDefault(_warning);
598 |
599 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
600 |
601 | function getUndefinedStateErrorMessage(key, action) {
602 | var actionType = action && action.type;
603 | var actionName = actionType && '"' + actionType.toString() + '"' || 'an action';
604 |
605 | return 'Reducer "' + key + '" returned undefined handling ' + actionName + '. ' + 'To ignore an action, you must explicitly return the previous state.';
606 | }
607 |
608 | function getUnexpectedStateShapeWarningMessage(inputState, reducers, action) {
609 | var reducerKeys = Object.keys(reducers);
610 | var argumentName = action && action.type === _createStore.ActionTypes.INIT ? 'initialState argument passed to createStore' : 'previous state received by the reducer';
611 |
612 | if (reducerKeys.length === 0) {
613 | return 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.';
614 | }
615 |
616 | if (!(0, _isPlainObject2["default"])(inputState)) {
617 | return 'The ' + argumentName + ' has unexpected type of "' + {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + '". Expected argument to be an object with the following ' + ('keys: "' + reducerKeys.join('", "') + '"');
618 | }
619 |
620 | var unexpectedKeys = Object.keys(inputState).filter(function (key) {
621 | return !reducers.hasOwnProperty(key);
622 | });
623 |
624 | if (unexpectedKeys.length > 0) {
625 | return 'Unexpected ' + (unexpectedKeys.length > 1 ? 'keys' : 'key') + ' ' + ('"' + unexpectedKeys.join('", "') + '" found in ' + argumentName + '. ') + 'Expected to find one of the known reducer keys instead: ' + ('"' + reducerKeys.join('", "') + '". Unexpected keys will be ignored.');
626 | }
627 | }
628 |
629 | function assertReducerSanity(reducers) {
630 | Object.keys(reducers).forEach(function (key) {
631 | var reducer = reducers[key];
632 | var initialState = reducer(undefined, { type: _createStore.ActionTypes.INIT });
633 |
634 | if (typeof initialState === 'undefined') {
635 | throw new Error('Reducer "' + key + '" returned undefined during initialization. ' + 'If the state passed to the reducer is undefined, you must ' + 'explicitly return the initial state. The initial state may ' + 'not be undefined.');
636 | }
637 |
638 | var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.');
639 | if (typeof reducer(undefined, { type: type }) === 'undefined') {
640 | throw new Error('Reducer "' + key + '" returned undefined when probed with a random type. ' + ('Don\'t try to handle ' + _createStore.ActionTypes.INIT + ' or other actions in "redux/*" ') + 'namespace. They are considered private. Instead, you must return the ' + 'current state for any unknown actions, unless it is undefined, ' + 'in which case you must return the initial state, regardless of the ' + 'action type. The initial state may not be undefined.');
641 | }
642 | });
643 | }
644 |
645 | /**
646 | * Turns an object whose values are different reducer functions, into a single
647 | * reducer function. It will call every child reducer, and gather their results
648 | * into a single state object, whose keys correspond to the keys of the passed
649 | * reducer functions.
650 | *
651 | * @param {Object} reducers An object whose values correspond to different
652 | * reducer functions that need to be combined into one. One handy way to obtain
653 | * it is to use ES6 `import * as reducers` syntax. The reducers may never return
654 | * undefined for any action. Instead, they should return their initial state
655 | * if the state passed to them was undefined, and the current state for any
656 | * unrecognized action.
657 | *
658 | * @returns {Function} A reducer function that invokes every reducer inside the
659 | * passed object, and builds a state object with the same shape.
660 | */
661 | function combineReducers(reducers) {
662 | var reducerKeys = Object.keys(reducers);
663 | var finalReducers = {};
664 | for (var i = 0; i < reducerKeys.length; i++) {
665 | var key = reducerKeys[i];
666 | if (typeof reducers[key] === 'function') {
667 | finalReducers[key] = reducers[key];
668 | }
669 | }
670 | var finalReducerKeys = Object.keys(finalReducers);
671 |
672 | var sanityError;
673 | try {
674 | assertReducerSanity(finalReducers);
675 | } catch (e) {
676 | sanityError = e;
677 | }
678 |
679 | return function combination() {
680 | var state = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
681 | var action = arguments[1];
682 |
683 | if (sanityError) {
684 | throw sanityError;
685 | }
686 |
687 | if (process.env.NODE_ENV !== 'production') {
688 | var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action);
689 | if (warningMessage) {
690 | (0, _warning2["default"])(warningMessage);
691 | }
692 | }
693 |
694 | var hasChanged = false;
695 | var nextState = {};
696 | for (var i = 0; i < finalReducerKeys.length; i++) {
697 | var key = finalReducerKeys[i];
698 | var reducer = finalReducers[key];
699 | var previousStateForKey = state[key];
700 | var nextStateForKey = reducer(previousStateForKey, action);
701 | if (typeof nextStateForKey === 'undefined') {
702 | var errorMessage = getUndefinedStateErrorMessage(key, action);
703 | throw new Error(errorMessage);
704 | }
705 | nextState[key] = nextStateForKey;
706 | hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
707 | }
708 | return hasChanged ? nextState : state;
709 | };
710 | }
711 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
712 |
713 | /***/ },
714 |
715 | /***/ 171:
716 | /***/ function(module, exports) {
717 |
718 | 'use strict';
719 |
720 | exports.__esModule = true;
721 | exports["default"] = warning;
722 | /**
723 | * Prints a warning in the console if it exists.
724 | *
725 | * @param {String} message The warning message.
726 | * @returns {void}
727 | */
728 | function warning(message) {
729 | /* eslint-disable no-console */
730 | if (typeof console !== 'undefined' && typeof console.error === 'function') {
731 | console.error(message);
732 | }
733 | /* eslint-enable no-console */
734 | try {
735 | // This error was thrown as a convenience so that you can use this stack
736 | // to find the callsite that caused this warning to fire.
737 | throw new Error(message);
738 | /* eslint-disable no-empty */
739 | } catch (e) {}
740 | /* eslint-enable no-empty */
741 | }
742 |
743 | /***/ },
744 |
745 | /***/ 172:
746 | /***/ function(module, exports) {
747 |
748 | 'use strict';
749 |
750 | exports.__esModule = true;
751 | exports["default"] = bindActionCreators;
752 | function bindActionCreator(actionCreator, dispatch) {
753 | return function () {
754 | return dispatch(actionCreator.apply(undefined, arguments));
755 | };
756 | }
757 |
758 | /**
759 | * Turns an object whose values are action creators, into an object with the
760 | * same keys, but with every function wrapped into a `dispatch` call so they
761 | * may be invoked directly. This is just a convenience method, as you can call
762 | * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
763 | *
764 | * For convenience, you can also pass a single function as the first argument,
765 | * and get a function in return.
766 | *
767 | * @param {Function|Object} actionCreators An object whose values are action
768 | * creator functions. One handy way to obtain it is to use ES6 `import * as`
769 | * syntax. You may also pass a single function.
770 | *
771 | * @param {Function} dispatch The `dispatch` function available on your Redux
772 | * store.
773 | *
774 | * @returns {Function|Object} The object mimicking the original object, but with
775 | * every action creator wrapped into the `dispatch` call. If you passed a
776 | * function as `actionCreators`, the return value will also be a single
777 | * function.
778 | */
779 | function bindActionCreators(actionCreators, dispatch) {
780 | if (typeof actionCreators === 'function') {
781 | return bindActionCreator(actionCreators, dispatch);
782 | }
783 |
784 | if (typeof actionCreators !== 'object' || actionCreators === null) {
785 | throw new Error('bindActionCreators expected an object or a function, instead received ' + (actionCreators === null ? 'null' : typeof actionCreators) + '. ' + 'Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?');
786 | }
787 |
788 | var keys = Object.keys(actionCreators);
789 | var boundActionCreators = {};
790 | for (var i = 0; i < keys.length; i++) {
791 | var key = keys[i];
792 | var actionCreator = actionCreators[key];
793 | if (typeof actionCreator === 'function') {
794 | boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
795 | }
796 | }
797 | return boundActionCreators;
798 | }
799 |
800 | /***/ },
801 |
802 | /***/ 173:
803 | /***/ function(module, exports, __webpack_require__) {
804 |
805 | 'use strict';
806 |
807 | 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; };
808 |
809 | exports.__esModule = true;
810 | exports["default"] = applyMiddleware;
811 |
812 | var _compose = __webpack_require__(174);
813 |
814 | var _compose2 = _interopRequireDefault(_compose);
815 |
816 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
817 |
818 | /**
819 | * Creates a store enhancer that applies middleware to the dispatch method
820 | * of the Redux store. This is handy for a variety of tasks, such as expressing
821 | * asynchronous actions in a concise manner, or logging every action payload.
822 | *
823 | * See `redux-thunk` package as an example of the Redux middleware.
824 | *
825 | * Because middleware is potentially asynchronous, this should be the first
826 | * store enhancer in the composition chain.
827 | *
828 | * Note that each middleware will be given the `dispatch` and `getState` functions
829 | * as named arguments.
830 | *
831 | * @param {...Function} middlewares The middleware chain to be applied.
832 | * @returns {Function} A store enhancer applying the middleware.
833 | */
834 | function applyMiddleware() {
835 | for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
836 | middlewares[_key] = arguments[_key];
837 | }
838 |
839 | return function (createStore) {
840 | return function (reducer, initialState, enhancer) {
841 | var store = createStore(reducer, initialState, enhancer);
842 | var _dispatch = store.dispatch;
843 | var chain = [];
844 |
845 | var middlewareAPI = {
846 | getState: store.getState,
847 | dispatch: function dispatch(action) {
848 | return _dispatch(action);
849 | }
850 | };
851 | chain = middlewares.map(function (middleware) {
852 | return middleware(middlewareAPI);
853 | });
854 | _dispatch = _compose2["default"].apply(undefined, chain)(store.dispatch);
855 |
856 | return _extends({}, store, {
857 | dispatch: _dispatch
858 | });
859 | };
860 | };
861 | }
862 |
863 | /***/ },
864 |
865 | /***/ 174:
866 | /***/ function(module, exports) {
867 |
868 | "use strict";
869 |
870 | exports.__esModule = true;
871 | exports["default"] = compose;
872 | /**
873 | * Composes single-argument functions from right to left.
874 | *
875 | * @param {...Function} funcs The functions to compose.
876 | * @returns {Function} A function obtained by composing functions from right to
877 | * left. For example, compose(f, g, h) is identical to arg => f(g(h(arg))).
878 | */
879 | function compose() {
880 | for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
881 | funcs[_key] = arguments[_key];
882 | }
883 |
884 | return function () {
885 | if (funcs.length === 0) {
886 | return arguments.length <= 0 ? undefined : arguments[0];
887 | }
888 |
889 | var last = funcs[funcs.length - 1];
890 | var rest = funcs.slice(0, -1);
891 |
892 | return rest.reduceRight(function (composed, f) {
893 | return f(composed);
894 | }, last.apply(undefined, arguments));
895 | };
896 | }
897 |
898 | /***/ },
899 |
900 | /***/ 178:
901 | /***/ function(module, exports, __webpack_require__) {
902 |
903 | 'use strict';
904 |
905 | Object.defineProperty(exports, "__esModule", {
906 | value: true
907 | });
908 |
909 | var _redux = __webpack_require__(165);
910 |
911 | var _nqueen = __webpack_require__(179);
912 |
913 | var _nqueen2 = _interopRequireDefault(_nqueen);
914 |
915 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
916 |
917 | var nqueenApp = (0, _redux.combineReducers)({
918 | nqueen: _nqueen2.default
919 | });
920 |
921 | exports.default = nqueenApp;
922 |
923 | /***/ },
924 |
925 | /***/ 179:
926 | /***/ function(module, exports, __webpack_require__) {
927 |
928 | 'use strict';
929 |
930 | Object.defineProperty(exports, "__esModule", {
931 | value: true
932 | });
933 |
934 | var _solver = __webpack_require__(180);
935 |
936 | var _solver2 = _interopRequireDefault(_solver);
937 |
938 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
939 |
940 | var intState = {
941 | isCalculating: false,
942 | numberOfSquares: 1,
943 | answer: null
944 | };
945 |
946 | var startNQueen = function startNQueen(state) {
947 | return Object.assign({}, state, {
948 | isCalculating: true,
949 | answer: null
950 | });
951 | };
952 |
953 | var calculateNQueen = function calculateNQueen(state, n) {
954 | return {
955 | numberOfSquares: n,
956 | isCalculating: true,
957 | answer: +n < 16 ? (0, _solver2.default)(+n).length : 'N is too large...'
958 | };
959 | };
960 |
961 | var completeNQueen = function completeNQueen(state) {
962 | return Object.assign({}, state, {
963 | isCalculating: false
964 | });
965 | };
966 |
967 | exports.default = function () {
968 | var state = arguments.length <= 0 || arguments[0] === undefined ? intState : arguments[0];
969 | var action = arguments[1];
970 |
971 | switch (action.type) {
972 | case 'START_NQUEEN':
973 | return startNQueen(state);
974 | case 'CALCULATE_NQUEEN':
975 | return calculateNQueen(state, action.number);
976 | case 'COMPLETE_NQUEEN':
977 | return completeNQueen(state);
978 | default:
979 | return state;
980 | }
981 | };
982 |
983 | /***/ },
984 |
985 | /***/ 180:
986 | /***/ function(module, exports) {
987 |
988 | "use strict";
989 |
990 | Object.defineProperty(exports, "__esModule", {
991 | value: true
992 | });
993 |
994 | exports.default = function (n, z) {
995 | var sol = [];
996 |
997 | var _solve = function _solve(board) {
998 | var nx = board.length;
999 |
1000 | if (board.length === n) {
1001 | sol.push(board);
1002 | return;
1003 | }
1004 |
1005 | for (var i = 0; i < n; i++) {
1006 | var legal = true;
1007 | for (var j = 0; j < board.length; j++) {
1008 | var ox = j;
1009 | var oy = board[j];
1010 | var slope = Math.abs((nx - ox) / (i - oy));
1011 | if (i === oy || slope === 1) {
1012 | legal = false;
1013 | break;
1014 | }
1015 | }
1016 | if (legal) {
1017 | board.push(i);
1018 | _solve(board);
1019 | board.pop();
1020 | }
1021 | }
1022 | };
1023 |
1024 | if (z !== undefined) {
1025 | _solve([z]);
1026 | } else {
1027 | _solve([]);
1028 | }
1029 |
1030 | return sol;
1031 | };
1032 |
1033 | /***/ },
1034 |
1035 | /***/ 189:
1036 | /***/ function(module, exports, __webpack_require__) {
1037 |
1038 | var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module) {'use strict';
1039 |
1040 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
1041 |
1042 | (function webpackUniversalModuleDefinition(root, factory) {
1043 | if (( false ? 'undefined' : _typeof(exports)) === 'object' && ( false ? 'undefined' : _typeof(module)) === 'object') module.exports = factory();else if (true) !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));else if ((typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object') exports["ReduxWorker"] = factory();else root["ReduxWorker"] = factory();
1044 | })(undefined, function () {
1045 | return (/******/function (modules) {
1046 | // webpackBootstrap
1047 | /******/ // The module cache
1048 | /******/var installedModules = {};
1049 |
1050 | /******/ // The require function
1051 | /******/function __webpack_require__(moduleId) {
1052 |
1053 | /******/ // Check if module is in cache
1054 | /******/if (installedModules[moduleId])
1055 | /******/return installedModules[moduleId].exports;
1056 |
1057 | /******/ // Create a new module (and put it into the cache)
1058 | /******/var module = installedModules[moduleId] = {
1059 | /******/exports: {},
1060 | /******/id: moduleId,
1061 | /******/loaded: false
1062 | /******/ };
1063 |
1064 | /******/ // Execute the module function
1065 | /******/modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
1066 |
1067 | /******/ // Flag the module as loaded
1068 | /******/module.loaded = true;
1069 |
1070 | /******/ // Return the exports of the module
1071 | /******/return module.exports;
1072 | /******/
1073 | }
1074 |
1075 | /******/ // expose the modules object (__webpack_modules__)
1076 | /******/__webpack_require__.m = modules;
1077 |
1078 | /******/ // expose the module cache
1079 | /******/__webpack_require__.c = installedModules;
1080 |
1081 | /******/ // __webpack_public_path__
1082 | /******/__webpack_require__.p = "";
1083 |
1084 | /******/ // Load entry module and return exports
1085 | /******/return __webpack_require__(0);
1086 | /******/
1087 | }(
1088 | /************************************************************************/
1089 | /******/[
1090 | /* 0 */
1091 | /***/function (module, exports, __webpack_require__) {
1092 |
1093 | 'use strict';
1094 |
1095 | Object.defineProperty(exports, "__esModule", {
1096 | value: true
1097 | });
1098 | exports.createWorker = exports.applyWorker = undefined;
1099 |
1100 | var _createWorker = __webpack_require__(1);
1101 |
1102 | var _createWorker2 = _interopRequireDefault(_createWorker);
1103 |
1104 | var _applyWorker = __webpack_require__(2);
1105 |
1106 | var _applyWorker2 = _interopRequireDefault(_applyWorker);
1107 |
1108 | function _interopRequireDefault(obj) {
1109 | return obj && obj.__esModule ? obj : { default: obj };
1110 | }
1111 |
1112 | exports.applyWorker = _applyWorker2.default;
1113 | exports.createWorker = _createWorker2.default;
1114 | exports.default = { applyWorker: _applyWorker2.default, createWorker: _createWorker2.default };
1115 |
1116 | /***/
1117 | },
1118 | /* 1 */
1119 | /***/function (module, exports) {
1120 |
1121 | 'use strict';
1122 |
1123 | Object.defineProperty(exports, "__esModule", {
1124 | value: true
1125 | });
1126 |
1127 | var _createClass = function () {
1128 | function defineProperties(target, props) {
1129 | for (var i = 0; i < props.length; i++) {
1130 | 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);
1131 | }
1132 | }return function (Constructor, protoProps, staticProps) {
1133 | if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;
1134 | };
1135 | }();
1136 |
1137 | function _classCallCheck(instance, Constructor) {
1138 | if (!(instance instanceof Constructor)) {
1139 | throw new TypeError("Cannot call a class as a function");
1140 | }
1141 | }
1142 |
1143 | var createWorker = function createWorker(reducer) {
1144 | // Initialize ReduxWorekr
1145 | var worker = new ReduxWorker();
1146 |
1147 | self.addEventListener('message', function (e) {
1148 | var action = e.data;
1149 |
1150 | if (typeof action.type === 'string') {
1151 | if (!worker.reducer || typeof worker.reducer !== 'function') {
1152 | throw new Error('Expect reducer to be function. Have you registerReducer yet?');
1153 | }
1154 |
1155 | // Set new state
1156 | var state = worker.state;
1157 | state = worker.state = worker.reducer(state, action);
1158 | state = worker.transform(state);
1159 |
1160 | // Send new state to main thread
1161 | self.postMessage({
1162 | type: action.type,
1163 | state: state,
1164 | action: action
1165 | });
1166 |
1167 | return;
1168 | }
1169 |
1170 | if (typeof action.task === 'string' && typeof action._taskId === 'number') {
1171 | var taskRunner = worker.tasks[action.task];
1172 |
1173 | if (!taskRunner || typeof taskRunner !== 'function') {
1174 | throw new Error('Cannot find runner for task ' + action.task + '. Have you registerTask yet?');
1175 | }
1176 |
1177 | // Send new state to main thread
1178 | self.postMessage({
1179 | _taskId: action._taskId,
1180 | response: taskRunner(action)
1181 | });
1182 | }
1183 | });
1184 |
1185 | return worker;
1186 | };
1187 |
1188 | var ReduxWorker = function () {
1189 | function ReduxWorker() {
1190 | _classCallCheck(this, ReduxWorker);
1191 |
1192 | // Taskrunners
1193 | this.tasks = {};
1194 |
1195 | // Redux-specific variables
1196 | this.state = {};
1197 | this.reducer = null;
1198 | this.transform = function (state) {
1199 | return state;
1200 | };
1201 | }
1202 |
1203 | _createClass(ReduxWorker, [{
1204 | key: 'registerReducer',
1205 | value: function registerReducer(reducer, transform) {
1206 | this.reducer = reducer;
1207 | this.state = reducer({}, {});
1208 | }
1209 | }, {
1210 | key: 'registerTask',
1211 | value: function registerTask(name, taskFn) {
1212 | this.tasks[name] = taskFn;
1213 | }
1214 | }]);
1215 |
1216 | return ReduxWorker;
1217 | }();
1218 |
1219 | exports.default = createWorker;
1220 |
1221 | /***/
1222 | },
1223 | /* 2 */
1224 | /***/function (module, exports) {
1225 |
1226 | 'use strict';
1227 |
1228 | Object.defineProperty(exports, "__esModule", {
1229 | value: true
1230 | });
1231 | var defer = function defer() {
1232 | var result = {};
1233 | result.promise = new Promise(function (resolve, reject) {
1234 | result.resolve = resolve;
1235 | result.reject = reject;
1236 | });
1237 | return result;
1238 | };
1239 |
1240 | var applyWorker = function applyWorker(worker) {
1241 | return function (createStore) {
1242 | return function (reducer, initialState, enhancer) {
1243 | if (!(worker instanceof Worker)) {
1244 | console.error('Expect input to be a Web Worker. Fall back to normal store.');
1245 | return createStore(reducer, initialState, enhancer);
1246 | }
1247 |
1248 | // New reducer for workified store
1249 | var replacementReducer = function replacementReducer(state, action) {
1250 | if (action.state) {
1251 | return action.state;
1252 | }
1253 | return state;
1254 | };
1255 |
1256 | // Start task id;
1257 | var taskId = 0;
1258 | var taskCompleteCallbacks = {};
1259 |
1260 | // Create store using new reducer
1261 | var store = createStore(replacementReducer, reducer({}, {}), enhancer);
1262 |
1263 | // Store reference of old dispatcher
1264 | var next = store.dispatch;
1265 |
1266 | // Replace dispatcher
1267 | store.dispatch = function (action) {
1268 | if (typeof action.type === 'string') {
1269 | if (window.disableWebWorker) {
1270 | return next({
1271 | type: action.type,
1272 | state: reducer(store.getState(), action)
1273 | });
1274 | }
1275 | worker.postMessage(action);
1276 | }
1277 |
1278 | if (typeof action.task === 'string') {
1279 | var task = Object.assign({}, action, { _taskId: taskId });
1280 | var deferred = defer();
1281 |
1282 | taskCompleteCallbacks[taskId] = deferred;
1283 | taskId++;
1284 | worker.postMessage(task);
1285 | return deferred.promise;
1286 | }
1287 | };
1288 |
1289 | store.isWorker = true;
1290 |
1291 | // Add worker events listener
1292 | worker.addEventListener('message', function (e) {
1293 | var action = e.data;
1294 | if (typeof action.type === 'string') {
1295 | next(action);
1296 | }
1297 |
1298 | if (typeof action._taskId === 'number') {
1299 | var wrapped = taskCompleteCallbacks[action._taskId];
1300 |
1301 | if (wrapped) {
1302 | wrapped.resolve(action);
1303 | delete taskCompleteCallbacks[action._taskId];
1304 | }
1305 | }
1306 | });
1307 |
1308 | return store;
1309 | };
1310 | };
1311 | };
1312 |
1313 | exports.default = applyWorker;
1314 |
1315 | /***/
1316 | }
1317 | /******/])
1318 | );
1319 | });
1320 | ;
1321 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(190)(module)))
1322 |
1323 | /***/ },
1324 |
1325 | /***/ 190:
1326 | /***/ function(module, exports) {
1327 |
1328 | module.exports = function(module) {
1329 | if(!module.webpackPolyfill) {
1330 | module.deprecate = function() {};
1331 | module.paths = [];
1332 | // module.parent = undefined by default
1333 | module.children = [];
1334 | module.webpackPolyfill = 1;
1335 | }
1336 | return module;
1337 | }
1338 |
1339 |
1340 | /***/ }
1341 |
1342 | /******/ });
--------------------------------------------------------------------------------
/demo/nqueen/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Redux Worker
5 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/demo/nqueen/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { Provider } from 'react-redux'
4 | import { createStore, applyMiddleware, compose } from 'redux'
5 | import thunk from 'redux-thunk'
6 | import rootReducer from './reducers'
7 | import App from './components/App'
8 | import { applyWorker } from 'redux-worker'
9 | import createLogger from 'redux-logger';
10 |
11 | const logger = createLogger();
12 |
13 | const worker = new Worker('./dist/worker.bundle.js');
14 |
15 | const enhancerWithWorker = compose(
16 | // Middleware you want to use in development:
17 | applyMiddleware(thunk, logger),
18 | applyWorker(worker)
19 | );
20 |
21 | const store = createStore(rootReducer, {}, enhancerWithWorker);
22 |
23 | render(
24 |
25 |
26 | ,
27 | document.getElementById('app')
28 | )
29 |
--------------------------------------------------------------------------------
/demo/nqueen/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-worker-demo-nqueen",
3 | "version": "1.0.0",
4 | "description": "Demo for Redux-Worker",
5 | "main": "main.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "watch": "./node_modules/.bin/webpack-dev-server"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "babel-polyfill": "^6.6.1",
14 | "babel-runtime": "^6.6.1",
15 | "react": "^0.14.7",
16 | "react-dom": "^0.14.7",
17 | "react-redux": "^4.4.0",
18 | "redux": "^3.3.1",
19 | "redux-logger": "^2.6.1",
20 | "redux-thunk": "^1.0.3",
21 | "redux-worker": "^0.1.0"
22 | },
23 | "devDependencies": {
24 | "babel-core": "^6.6.4",
25 | "babel-loader": "^6.2.4",
26 | "babel-preset-es2015": "6.3.13",
27 | "babel-preset-react": "^6.5.0",
28 | "webpack": "^1.12.14",
29 | "webpack-dev-server": "^1.14.1"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/demo/nqueen/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import nqueen from './nqueen/nqueen'
3 |
4 | const nqueenApp = combineReducers({
5 | nqueen
6 | });
7 |
8 | export default nqueenApp
--------------------------------------------------------------------------------
/demo/nqueen/reducers/nqueen/nqueen.js:
--------------------------------------------------------------------------------
1 | import solve from '../../solver'
2 |
3 | const intState = {
4 | isCalculating: false,
5 | numberOfSquares: 1,
6 | answer: null
7 | }
8 |
9 | const startNQueen = (state) => {
10 | return Object.assign({}, state, {
11 | isCalculating: true,
12 | answer: null
13 | });
14 | }
15 |
16 | const calculateNQueen = (state, n) => {
17 | return {
18 | numberOfSquares: n,
19 | isCalculating: true,
20 | answer: +n < 16 ? solve(+n).length : 'N is too large...'
21 | };
22 | }
23 |
24 | const completeNQueen = (state) => {
25 | return Object.assign({}, state, {
26 | isCalculating: false
27 | });
28 | }
29 |
30 | export default (state = intState, action) => {
31 | switch (action.type) {
32 | case 'START_NQUEEN':
33 | return startNQueen(state);
34 | case 'CALCULATE_NQUEEN':
35 | return calculateNQueen(state, action.number);
36 | case 'COMPLETE_NQUEEN':
37 | return completeNQueen(state);
38 | default:
39 | return state;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/demo/nqueen/reducers/worker.js:
--------------------------------------------------------------------------------
1 | import reducer from '../reducers'
2 | import solve from '../solver'
3 | import { createWorker } from 'redux-worker'
4 |
5 | let worker = createWorker();
6 |
7 | worker.registerReducer(reducer);
8 |
9 | worker.registerTask('NQUEEN_TASK', function(a) {
10 | let n = a.number;
11 | return +n < 16 ? solve(+n).length : 'N is too large...';
12 | });
--------------------------------------------------------------------------------
/demo/nqueen/solver.js:
--------------------------------------------------------------------------------
1 | export default (n,z) => {
2 | var sol = [];
3 |
4 | var _solve = function(board){
5 | var nx = board.length;
6 |
7 | if (board.length === n){
8 | sol.push(board);
9 | return;
10 | }
11 |
12 | for (var i = 0; i
22 |
23 |
24 |
25 | );
26 | }
27 | }
28 |
29 | export default App;
--------------------------------------------------------------------------------
/demo/sortTable/components/Generator.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react';
2 |
3 | class Generator extends Component {
4 | constructor(props) {
5 | super(props);
6 | }
7 |
8 | testWorker() {
9 | this.props.actions.testWorker(10000)
10 | .then(console.log.bind(console))
11 | }
12 |
13 | render() {
14 | return (
15 |
16 |
Sort Table
17 |
22 | 100
23 | 1000
24 | 10000
25 | test
26 |
27 |
28 | );
29 | }
30 | }
31 |
32 | export default Generator;
--------------------------------------------------------------------------------
/demo/sortTable/components/Spinner.js:
--------------------------------------------------------------------------------
1 | import React, { createClass } from 'react'
2 |
3 | export default createClass({
4 | getInitialState() {
5 | return {
6 | degree: 0
7 | };
8 | },
9 |
10 | componentDidMount() {
11 | this.interval = setInterval(function() {
12 | this.setState({
13 | degree: this.state.degree + 5
14 | });
15 | }.bind(this), 16);
16 | },
17 |
18 | componentWillUnmount() {
19 | const interval = this.interval;
20 | if (interval) {
21 | clearInterval(interval);
22 | }
23 | },
24 |
25 | render() {
26 | return (
27 |
28 |
43 |
44 |
45 |
60 |
61 |
62 |
77 |
78 |
93 |
94 |
109 |
110 |
111 |
112 | )
113 | },
114 | });
--------------------------------------------------------------------------------
/demo/sortTable/components/UserTable.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react';
2 | import LoadingSpinner from 'react-spinner';
3 | import Infinite from 'react-infinite';
4 |
5 | class Table extends Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | isFirstNameReverse: false,
10 | isLastNameReverse: false,
11 | isDoBReverse: false,
12 | isSorting: false
13 | }
14 | }
15 |
16 | makeOneRow(user, i) {
17 | return (
18 |
23 |
{user.firstName}
24 |
{user.lastName}
25 |
{user.dateOfBirth.slice(4, 15)}
26 |
27 | )
28 | }
29 |
30 | componentWillReceiveProps() {
31 | this.setState({ isSorting: false });
32 | }
33 |
34 | sortFirstName() {
35 | let {isFirstNameReverse} = this.state;
36 |
37 | this.setState({
38 | isFirstNameReverse: !isFirstNameReverse,
39 | isSorting: true
40 | });
41 |
42 | setTimeout(() => {
43 | this.props.onClickFirstName(isFirstNameReverse);
44 | }, 200);
45 | }
46 |
47 | sortLastName() {
48 | let {isLastNameReverse} = this.state;
49 |
50 | this.setState({
51 | isLastNameReverse: !isLastNameReverse,
52 | isSorting: true
53 | });
54 |
55 | setTimeout(() => {
56 | this.props.onClickLastName(isLastNameReverse);
57 | }, 200);
58 | }
59 |
60 | sortDoB() {
61 | let {isDoBReverse} = this.state;
62 |
63 | this.setState({
64 | isDoBReverse: !isDoBReverse,
65 | isSorting: true
66 | });
67 |
68 | setTimeout(() => {
69 | this.props.onClickDoB(isDoBReverse);
70 | }, 200);
71 | }
72 |
73 | render() {
74 | const {
75 | users = []
76 | } = this.props;
77 |
78 | const {
79 | isSorting
80 | } = this.state;
81 |
82 | return (
83 |
91 | {isSorting ?
: null}
92 |
98 |
100 | First Name
101 |
102 |
104 | Last Name
105 |
106 |
108 | Date of Birth
109 |
110 |
111 |
113 | {users.map(this.makeOneRow)}
114 |
115 |
116 | );
117 | }
118 | }
119 |
120 | export default Table;
--------------------------------------------------------------------------------
/demo/sortTable/containers/GeneratorContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import { bindActionCreators } from 'redux'
3 | import { connect } from 'react-redux'
4 | import Generator from '../components/Generator'
5 | import * as UserActions from '../actions/users/users'
6 |
7 | class GeneratorContainer extends Component {
8 | constructor(props) {
9 | super(props);
10 | }
11 |
12 | render() {
13 | return (
14 |
18 | )
19 | }
20 | }
21 |
22 | function mapStateToProps(state) {
23 | return {
24 | users: state.users
25 | }
26 | }
27 |
28 | function mapDispatchToProps(dispatch) {
29 | return {
30 | actions: bindActionCreators(UserActions, dispatch)
31 | }
32 | }
33 |
34 | export default connect(
35 | mapStateToProps,
36 | mapDispatchToProps
37 | )(GeneratorContainer)
--------------------------------------------------------------------------------
/demo/sortTable/containers/UserTableContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import { bindActionCreators } from 'redux'
3 | import { connect } from 'react-redux'
4 | import UserTable from '../components/UserTable'
5 | import * as UserActions from '../actions/users/users'
6 |
7 | class Table extends Component {
8 | constructor(props) {
9 | super(props);
10 | }
11 |
12 | render() {
13 | const { users, actions } = this.props;
14 | const { sortFirstName, sortLastName, sortDateOfBirth } = actions;
15 | return (
16 |
22 | )
23 | }
24 | }
25 |
26 | function mapStateToProps(state) {
27 | return {
28 | users: state.users
29 | }
30 | }
31 |
32 | function mapDispatchToProps(dispatch) {
33 | return {
34 | actions: bindActionCreators(UserActions, dispatch)
35 | }
36 | }
37 |
38 | export default connect(
39 | mapStateToProps,
40 | mapDispatchToProps
41 | )(Table)
--------------------------------------------------------------------------------
/demo/sortTable/dist/worker.bundle.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 |
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 |
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId])
10 | /******/ return installedModules[moduleId].exports;
11 |
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ exports: {},
15 | /******/ id: moduleId,
16 | /******/ loaded: false
17 | /******/ };
18 |
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 |
22 | /******/ // Flag the module as loaded
23 | /******/ module.loaded = true;
24 |
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 |
29 |
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 |
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 |
36 | /******/ // __webpack_public_path__
37 | /******/ __webpack_require__.p = "";
38 |
39 | /******/ // Load entry module and return exports
40 | /******/ return __webpack_require__(0);
41 | /******/ })
42 | /************************************************************************/
43 | /******/ ({
44 |
45 | /***/ 0:
46 | /***/ function(module, exports, __webpack_require__) {
47 |
48 | 'use strict';
49 |
50 | var _reducers = __webpack_require__(178);
51 |
52 | var _reducers2 = _interopRequireDefault(_reducers);
53 |
54 | var _solver = __webpack_require__(180);
55 |
56 | var _solver2 = _interopRequireDefault(_solver);
57 |
58 | var _reduxWorker = __webpack_require__(189);
59 |
60 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
61 |
62 | var worker = (0, _reduxWorker.createWorker)();
63 |
64 | worker.registerReducer(_reducers2.default);
65 |
66 | worker.registerTask('NQUEEN_TASK', function (a) {
67 | var n = a.number;
68 | return +n < 16 ? (0, _solver2.default)(+n).length : 'N is too large...';
69 | });
70 |
71 | /***/ },
72 |
73 | /***/ 4:
74 | /***/ function(module, exports) {
75 |
76 | // shim for using process in browser
77 |
78 | var process = module.exports = {};
79 | var queue = [];
80 | var draining = false;
81 | var currentQueue;
82 | var queueIndex = -1;
83 |
84 | function cleanUpNextTick() {
85 | draining = false;
86 | if (currentQueue.length) {
87 | queue = currentQueue.concat(queue);
88 | } else {
89 | queueIndex = -1;
90 | }
91 | if (queue.length) {
92 | drainQueue();
93 | }
94 | }
95 |
96 | function drainQueue() {
97 | if (draining) {
98 | return;
99 | }
100 | var timeout = setTimeout(cleanUpNextTick);
101 | draining = true;
102 |
103 | var len = queue.length;
104 | while(len) {
105 | currentQueue = queue;
106 | queue = [];
107 | while (++queueIndex < len) {
108 | if (currentQueue) {
109 | currentQueue[queueIndex].run();
110 | }
111 | }
112 | queueIndex = -1;
113 | len = queue.length;
114 | }
115 | currentQueue = null;
116 | draining = false;
117 | clearTimeout(timeout);
118 | }
119 |
120 | process.nextTick = function (fun) {
121 | var args = new Array(arguments.length - 1);
122 | if (arguments.length > 1) {
123 | for (var i = 1; i < arguments.length; i++) {
124 | args[i - 1] = arguments[i];
125 | }
126 | }
127 | queue.push(new Item(fun, args));
128 | if (queue.length === 1 && !draining) {
129 | setTimeout(drainQueue, 0);
130 | }
131 | };
132 |
133 | // v8 likes predictible objects
134 | function Item(fun, array) {
135 | this.fun = fun;
136 | this.array = array;
137 | }
138 | Item.prototype.run = function () {
139 | this.fun.apply(null, this.array);
140 | };
141 | process.title = 'browser';
142 | process.browser = true;
143 | process.env = {};
144 | process.argv = [];
145 | process.version = ''; // empty string to avoid regexp issues
146 | process.versions = {};
147 |
148 | function noop() {}
149 |
150 | process.on = noop;
151 | process.addListener = noop;
152 | process.once = noop;
153 | process.off = noop;
154 | process.removeListener = noop;
155 | process.removeAllListeners = noop;
156 | process.emit = noop;
157 |
158 | process.binding = function (name) {
159 | throw new Error('process.binding is not supported');
160 | };
161 |
162 | process.cwd = function () { return '/' };
163 | process.chdir = function (dir) {
164 | throw new Error('process.chdir is not supported');
165 | };
166 | process.umask = function() { return 0; };
167 |
168 |
169 | /***/ },
170 |
171 | /***/ 165:
172 | /***/ function(module, exports, __webpack_require__) {
173 |
174 | /* WEBPACK VAR INJECTION */(function(process) {'use strict';
175 |
176 | exports.__esModule = true;
177 | exports.compose = exports.applyMiddleware = exports.bindActionCreators = exports.combineReducers = exports.createStore = undefined;
178 |
179 | var _createStore = __webpack_require__(166);
180 |
181 | var _createStore2 = _interopRequireDefault(_createStore);
182 |
183 | var _combineReducers = __webpack_require__(170);
184 |
185 | var _combineReducers2 = _interopRequireDefault(_combineReducers);
186 |
187 | var _bindActionCreators = __webpack_require__(172);
188 |
189 | var _bindActionCreators2 = _interopRequireDefault(_bindActionCreators);
190 |
191 | var _applyMiddleware = __webpack_require__(173);
192 |
193 | var _applyMiddleware2 = _interopRequireDefault(_applyMiddleware);
194 |
195 | var _compose = __webpack_require__(174);
196 |
197 | var _compose2 = _interopRequireDefault(_compose);
198 |
199 | var _warning = __webpack_require__(171);
200 |
201 | var _warning2 = _interopRequireDefault(_warning);
202 |
203 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
204 |
205 | /*
206 | * This is a dummy function to check if the function name has been altered by minification.
207 | * If the function has been minified and NODE_ENV !== 'production', warn the user.
208 | */
209 | function isCrushed() {}
210 |
211 | if (process.env.NODE_ENV !== 'production' && typeof isCrushed.name === 'string' && isCrushed.name !== 'isCrushed') {
212 | (0, _warning2["default"])('You are currently using minified code outside of NODE_ENV === \'production\'. ' + 'This means that you are running a slower development build of Redux. ' + 'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' + 'or DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) ' + 'to ensure you have the correct code for your production build.');
213 | }
214 |
215 | exports.createStore = _createStore2["default"];
216 | exports.combineReducers = _combineReducers2["default"];
217 | exports.bindActionCreators = _bindActionCreators2["default"];
218 | exports.applyMiddleware = _applyMiddleware2["default"];
219 | exports.compose = _compose2["default"];
220 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
221 |
222 | /***/ },
223 |
224 | /***/ 166:
225 | /***/ function(module, exports, __webpack_require__) {
226 |
227 | 'use strict';
228 |
229 | exports.__esModule = true;
230 | exports.ActionTypes = undefined;
231 | exports["default"] = createStore;
232 |
233 | var _isPlainObject = __webpack_require__(167);
234 |
235 | var _isPlainObject2 = _interopRequireDefault(_isPlainObject);
236 |
237 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
238 |
239 | /**
240 | * These are private action types reserved by Redux.
241 | * For any unknown actions, you must return the current state.
242 | * If the current state is undefined, you must return the initial state.
243 | * Do not reference these action types directly in your code.
244 | */
245 | var ActionTypes = exports.ActionTypes = {
246 | INIT: '@@redux/INIT'
247 | };
248 |
249 | /**
250 | * Creates a Redux store that holds the state tree.
251 | * The only way to change the data in the store is to call `dispatch()` on it.
252 | *
253 | * There should only be a single store in your app. To specify how different
254 | * parts of the state tree respond to actions, you may combine several reducers
255 | * into a single reducer function by using `combineReducers`.
256 | *
257 | * @param {Function} reducer A function that returns the next state tree, given
258 | * the current state tree and the action to handle.
259 | *
260 | * @param {any} [initialState] The initial state. You may optionally specify it
261 | * to hydrate the state from the server in universal apps, or to restore a
262 | * previously serialized user session.
263 | * If you use `combineReducers` to produce the root reducer function, this must be
264 | * an object with the same shape as `combineReducers` keys.
265 | *
266 | * @param {Function} enhancer The store enhancer. You may optionally specify it
267 | * to enhance the store with third-party capabilities such as middleware,
268 | * time travel, persistence, etc. The only store enhancer that ships with Redux
269 | * is `applyMiddleware()`.
270 | *
271 | * @returns {Store} A Redux store that lets you read the state, dispatch actions
272 | * and subscribe to changes.
273 | */
274 | function createStore(reducer, initialState, enhancer) {
275 | if (typeof initialState === 'function' && typeof enhancer === 'undefined') {
276 | enhancer = initialState;
277 | initialState = undefined;
278 | }
279 |
280 | if (typeof enhancer !== 'undefined') {
281 | if (typeof enhancer !== 'function') {
282 | throw new Error('Expected the enhancer to be a function.');
283 | }
284 |
285 | return enhancer(createStore)(reducer, initialState);
286 | }
287 |
288 | if (typeof reducer !== 'function') {
289 | throw new Error('Expected the reducer to be a function.');
290 | }
291 |
292 | var currentReducer = reducer;
293 | var currentState = initialState;
294 | var currentListeners = [];
295 | var nextListeners = currentListeners;
296 | var isDispatching = false;
297 |
298 | function ensureCanMutateNextListeners() {
299 | if (nextListeners === currentListeners) {
300 | nextListeners = currentListeners.slice();
301 | }
302 | }
303 |
304 | /**
305 | * Reads the state tree managed by the store.
306 | *
307 | * @returns {any} The current state tree of your application.
308 | */
309 | function getState() {
310 | return currentState;
311 | }
312 |
313 | /**
314 | * Adds a change listener. It will be called any time an action is dispatched,
315 | * and some part of the state tree may potentially have changed. You may then
316 | * call `getState()` to read the current state tree inside the callback.
317 | *
318 | * You may call `dispatch()` from a change listener, with the following
319 | * caveats:
320 | *
321 | * 1. The subscriptions are snapshotted just before every `dispatch()` call.
322 | * If you subscribe or unsubscribe while the listeners are being invoked, this
323 | * will not have any effect on the `dispatch()` that is currently in progress.
324 | * However, the next `dispatch()` call, whether nested or not, will use a more
325 | * recent snapshot of the subscription list.
326 | *
327 | * 2. The listener should not expect to see all states changes, as the state
328 | * might have been updated multiple times during a nested `dispatch()` before
329 | * the listener is called. It is, however, guaranteed that all subscribers
330 | * registered before the `dispatch()` started will be called with the latest
331 | * state by the time it exits.
332 | *
333 | * @param {Function} listener A callback to be invoked on every dispatch.
334 | * @returns {Function} A function to remove this change listener.
335 | */
336 | function subscribe(listener) {
337 | if (typeof listener !== 'function') {
338 | throw new Error('Expected listener to be a function.');
339 | }
340 |
341 | var isSubscribed = true;
342 |
343 | ensureCanMutateNextListeners();
344 | nextListeners.push(listener);
345 |
346 | return function unsubscribe() {
347 | if (!isSubscribed) {
348 | return;
349 | }
350 |
351 | isSubscribed = false;
352 |
353 | ensureCanMutateNextListeners();
354 | var index = nextListeners.indexOf(listener);
355 | nextListeners.splice(index, 1);
356 | };
357 | }
358 |
359 | /**
360 | * Dispatches an action. It is the only way to trigger a state change.
361 | *
362 | * The `reducer` function, used to create the store, will be called with the
363 | * current state tree and the given `action`. Its return value will
364 | * be considered the **next** state of the tree, and the change listeners
365 | * will be notified.
366 | *
367 | * The base implementation only supports plain object actions. If you want to
368 | * dispatch a Promise, an Observable, a thunk, or something else, you need to
369 | * wrap your store creating function into the corresponding middleware. For
370 | * example, see the documentation for the `redux-thunk` package. Even the
371 | * middleware will eventually dispatch plain object actions using this method.
372 | *
373 | * @param {Object} action A plain object representing “what changed”. It is
374 | * a good idea to keep actions serializable so you can record and replay user
375 | * sessions, or use the time travelling `redux-devtools`. An action must have
376 | * a `type` property which may not be `undefined`. It is a good idea to use
377 | * string constants for action types.
378 | *
379 | * @returns {Object} For convenience, the same action object you dispatched.
380 | *
381 | * Note that, if you use a custom middleware, it may wrap `dispatch()` to
382 | * return something else (for example, a Promise you can await).
383 | */
384 | function dispatch(action) {
385 | if (!(0, _isPlainObject2["default"])(action)) {
386 | throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
387 | }
388 |
389 | if (typeof action.type === 'undefined') {
390 | throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
391 | }
392 |
393 | if (isDispatching) {
394 | throw new Error('Reducers may not dispatch actions.');
395 | }
396 |
397 | try {
398 | isDispatching = true;
399 | currentState = currentReducer(currentState, action);
400 | } finally {
401 | isDispatching = false;
402 | }
403 |
404 | var listeners = currentListeners = nextListeners;
405 | for (var i = 0; i < listeners.length; i++) {
406 | listeners[i]();
407 | }
408 |
409 | return action;
410 | }
411 |
412 | /**
413 | * Replaces the reducer currently used by the store to calculate the state.
414 | *
415 | * You might need this if your app implements code splitting and you want to
416 | * load some of the reducers dynamically. You might also need this if you
417 | * implement a hot reloading mechanism for Redux.
418 | *
419 | * @param {Function} nextReducer The reducer for the store to use instead.
420 | * @returns {void}
421 | */
422 | function replaceReducer(nextReducer) {
423 | if (typeof nextReducer !== 'function') {
424 | throw new Error('Expected the nextReducer to be a function.');
425 | }
426 |
427 | currentReducer = nextReducer;
428 | dispatch({ type: ActionTypes.INIT });
429 | }
430 |
431 | // When a store is created, an "INIT" action is dispatched so that every
432 | // reducer returns their initial state. This effectively populates
433 | // the initial state tree.
434 | dispatch({ type: ActionTypes.INIT });
435 |
436 | return {
437 | dispatch: dispatch,
438 | subscribe: subscribe,
439 | getState: getState,
440 | replaceReducer: replaceReducer
441 | };
442 | }
443 |
444 | /***/ },
445 |
446 | /***/ 167:
447 | /***/ function(module, exports, __webpack_require__) {
448 |
449 | var isHostObject = __webpack_require__(168),
450 | isObjectLike = __webpack_require__(169);
451 |
452 | /** `Object#toString` result references. */
453 | var objectTag = '[object Object]';
454 |
455 | /** Used for built-in method references. */
456 | var objectProto = Object.prototype;
457 |
458 | /** Used to resolve the decompiled source of functions. */
459 | var funcToString = Function.prototype.toString;
460 |
461 | /** Used to infer the `Object` constructor. */
462 | var objectCtorString = funcToString.call(Object);
463 |
464 | /**
465 | * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
466 | * of values.
467 | */
468 | var objectToString = objectProto.toString;
469 |
470 | /** Built-in value references. */
471 | var getPrototypeOf = Object.getPrototypeOf;
472 |
473 | /**
474 | * Checks if `value` is a plain object, that is, an object created by the
475 | * `Object` constructor or one with a `[[Prototype]]` of `null`.
476 | *
477 | * @static
478 | * @memberOf _
479 | * @category Lang
480 | * @param {*} value The value to check.
481 | * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
482 | * @example
483 | *
484 | * function Foo() {
485 | * this.a = 1;
486 | * }
487 | *
488 | * _.isPlainObject(new Foo);
489 | * // => false
490 | *
491 | * _.isPlainObject([1, 2, 3]);
492 | * // => false
493 | *
494 | * _.isPlainObject({ 'x': 0, 'y': 0 });
495 | * // => true
496 | *
497 | * _.isPlainObject(Object.create(null));
498 | * // => true
499 | */
500 | function isPlainObject(value) {
501 | if (!isObjectLike(value) ||
502 | objectToString.call(value) != objectTag || isHostObject(value)) {
503 | return false;
504 | }
505 | var proto = getPrototypeOf(value);
506 | if (proto === null) {
507 | return true;
508 | }
509 | var Ctor = proto.constructor;
510 | return (typeof Ctor == 'function' &&
511 | Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
512 | }
513 |
514 | module.exports = isPlainObject;
515 |
516 |
517 | /***/ },
518 |
519 | /***/ 168:
520 | /***/ function(module, exports) {
521 |
522 | /**
523 | * Checks if `value` is a host object in IE < 9.
524 | *
525 | * @private
526 | * @param {*} value The value to check.
527 | * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
528 | */
529 | function isHostObject(value) {
530 | // Many host objects are `Object` objects that can coerce to strings
531 | // despite having improperly defined `toString` methods.
532 | var result = false;
533 | if (value != null && typeof value.toString != 'function') {
534 | try {
535 | result = !!(value + '');
536 | } catch (e) {}
537 | }
538 | return result;
539 | }
540 |
541 | module.exports = isHostObject;
542 |
543 |
544 | /***/ },
545 |
546 | /***/ 169:
547 | /***/ function(module, exports) {
548 |
549 | /**
550 | * Checks if `value` is object-like. A value is object-like if it's not `null`
551 | * and has a `typeof` result of "object".
552 | *
553 | * @static
554 | * @memberOf _
555 | * @category Lang
556 | * @param {*} value The value to check.
557 | * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
558 | * @example
559 | *
560 | * _.isObjectLike({});
561 | * // => true
562 | *
563 | * _.isObjectLike([1, 2, 3]);
564 | * // => true
565 | *
566 | * _.isObjectLike(_.noop);
567 | * // => false
568 | *
569 | * _.isObjectLike(null);
570 | * // => false
571 | */
572 | function isObjectLike(value) {
573 | return !!value && typeof value == 'object';
574 | }
575 |
576 | module.exports = isObjectLike;
577 |
578 |
579 | /***/ },
580 |
581 | /***/ 170:
582 | /***/ function(module, exports, __webpack_require__) {
583 |
584 | /* WEBPACK VAR INJECTION */(function(process) {'use strict';
585 |
586 | exports.__esModule = true;
587 | exports["default"] = combineReducers;
588 |
589 | var _createStore = __webpack_require__(166);
590 |
591 | var _isPlainObject = __webpack_require__(167);
592 |
593 | var _isPlainObject2 = _interopRequireDefault(_isPlainObject);
594 |
595 | var _warning = __webpack_require__(171);
596 |
597 | var _warning2 = _interopRequireDefault(_warning);
598 |
599 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
600 |
601 | function getUndefinedStateErrorMessage(key, action) {
602 | var actionType = action && action.type;
603 | var actionName = actionType && '"' + actionType.toString() + '"' || 'an action';
604 |
605 | return 'Reducer "' + key + '" returned undefined handling ' + actionName + '. ' + 'To ignore an action, you must explicitly return the previous state.';
606 | }
607 |
608 | function getUnexpectedStateShapeWarningMessage(inputState, reducers, action) {
609 | var reducerKeys = Object.keys(reducers);
610 | var argumentName = action && action.type === _createStore.ActionTypes.INIT ? 'initialState argument passed to createStore' : 'previous state received by the reducer';
611 |
612 | if (reducerKeys.length === 0) {
613 | return 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.';
614 | }
615 |
616 | if (!(0, _isPlainObject2["default"])(inputState)) {
617 | return 'The ' + argumentName + ' has unexpected type of "' + {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + '". Expected argument to be an object with the following ' + ('keys: "' + reducerKeys.join('", "') + '"');
618 | }
619 |
620 | var unexpectedKeys = Object.keys(inputState).filter(function (key) {
621 | return !reducers.hasOwnProperty(key);
622 | });
623 |
624 | if (unexpectedKeys.length > 0) {
625 | return 'Unexpected ' + (unexpectedKeys.length > 1 ? 'keys' : 'key') + ' ' + ('"' + unexpectedKeys.join('", "') + '" found in ' + argumentName + '. ') + 'Expected to find one of the known reducer keys instead: ' + ('"' + reducerKeys.join('", "') + '". Unexpected keys will be ignored.');
626 | }
627 | }
628 |
629 | function assertReducerSanity(reducers) {
630 | Object.keys(reducers).forEach(function (key) {
631 | var reducer = reducers[key];
632 | var initialState = reducer(undefined, { type: _createStore.ActionTypes.INIT });
633 |
634 | if (typeof initialState === 'undefined') {
635 | throw new Error('Reducer "' + key + '" returned undefined during initialization. ' + 'If the state passed to the reducer is undefined, you must ' + 'explicitly return the initial state. The initial state may ' + 'not be undefined.');
636 | }
637 |
638 | var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.');
639 | if (typeof reducer(undefined, { type: type }) === 'undefined') {
640 | throw new Error('Reducer "' + key + '" returned undefined when probed with a random type. ' + ('Don\'t try to handle ' + _createStore.ActionTypes.INIT + ' or other actions in "redux/*" ') + 'namespace. They are considered private. Instead, you must return the ' + 'current state for any unknown actions, unless it is undefined, ' + 'in which case you must return the initial state, regardless of the ' + 'action type. The initial state may not be undefined.');
641 | }
642 | });
643 | }
644 |
645 | /**
646 | * Turns an object whose values are different reducer functions, into a single
647 | * reducer function. It will call every child reducer, and gather their results
648 | * into a single state object, whose keys correspond to the keys of the passed
649 | * reducer functions.
650 | *
651 | * @param {Object} reducers An object whose values correspond to different
652 | * reducer functions that need to be combined into one. One handy way to obtain
653 | * it is to use ES6 `import * as reducers` syntax. The reducers may never return
654 | * undefined for any action. Instead, they should return their initial state
655 | * if the state passed to them was undefined, and the current state for any
656 | * unrecognized action.
657 | *
658 | * @returns {Function} A reducer function that invokes every reducer inside the
659 | * passed object, and builds a state object with the same shape.
660 | */
661 | function combineReducers(reducers) {
662 | var reducerKeys = Object.keys(reducers);
663 | var finalReducers = {};
664 | for (var i = 0; i < reducerKeys.length; i++) {
665 | var key = reducerKeys[i];
666 | if (typeof reducers[key] === 'function') {
667 | finalReducers[key] = reducers[key];
668 | }
669 | }
670 | var finalReducerKeys = Object.keys(finalReducers);
671 |
672 | var sanityError;
673 | try {
674 | assertReducerSanity(finalReducers);
675 | } catch (e) {
676 | sanityError = e;
677 | }
678 |
679 | return function combination() {
680 | var state = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
681 | var action = arguments[1];
682 |
683 | if (sanityError) {
684 | throw sanityError;
685 | }
686 |
687 | if (process.env.NODE_ENV !== 'production') {
688 | var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action);
689 | if (warningMessage) {
690 | (0, _warning2["default"])(warningMessage);
691 | }
692 | }
693 |
694 | var hasChanged = false;
695 | var nextState = {};
696 | for (var i = 0; i < finalReducerKeys.length; i++) {
697 | var key = finalReducerKeys[i];
698 | var reducer = finalReducers[key];
699 | var previousStateForKey = state[key];
700 | var nextStateForKey = reducer(previousStateForKey, action);
701 | if (typeof nextStateForKey === 'undefined') {
702 | var errorMessage = getUndefinedStateErrorMessage(key, action);
703 | throw new Error(errorMessage);
704 | }
705 | nextState[key] = nextStateForKey;
706 | hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
707 | }
708 | return hasChanged ? nextState : state;
709 | };
710 | }
711 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
712 |
713 | /***/ },
714 |
715 | /***/ 171:
716 | /***/ function(module, exports) {
717 |
718 | 'use strict';
719 |
720 | exports.__esModule = true;
721 | exports["default"] = warning;
722 | /**
723 | * Prints a warning in the console if it exists.
724 | *
725 | * @param {String} message The warning message.
726 | * @returns {void}
727 | */
728 | function warning(message) {
729 | /* eslint-disable no-console */
730 | if (typeof console !== 'undefined' && typeof console.error === 'function') {
731 | console.error(message);
732 | }
733 | /* eslint-enable no-console */
734 | try {
735 | // This error was thrown as a convenience so that you can use this stack
736 | // to find the callsite that caused this warning to fire.
737 | throw new Error(message);
738 | /* eslint-disable no-empty */
739 | } catch (e) {}
740 | /* eslint-enable no-empty */
741 | }
742 |
743 | /***/ },
744 |
745 | /***/ 172:
746 | /***/ function(module, exports) {
747 |
748 | 'use strict';
749 |
750 | exports.__esModule = true;
751 | exports["default"] = bindActionCreators;
752 | function bindActionCreator(actionCreator, dispatch) {
753 | return function () {
754 | return dispatch(actionCreator.apply(undefined, arguments));
755 | };
756 | }
757 |
758 | /**
759 | * Turns an object whose values are action creators, into an object with the
760 | * same keys, but with every function wrapped into a `dispatch` call so they
761 | * may be invoked directly. This is just a convenience method, as you can call
762 | * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
763 | *
764 | * For convenience, you can also pass a single function as the first argument,
765 | * and get a function in return.
766 | *
767 | * @param {Function|Object} actionCreators An object whose values are action
768 | * creator functions. One handy way to obtain it is to use ES6 `import * as`
769 | * syntax. You may also pass a single function.
770 | *
771 | * @param {Function} dispatch The `dispatch` function available on your Redux
772 | * store.
773 | *
774 | * @returns {Function|Object} The object mimicking the original object, but with
775 | * every action creator wrapped into the `dispatch` call. If you passed a
776 | * function as `actionCreators`, the return value will also be a single
777 | * function.
778 | */
779 | function bindActionCreators(actionCreators, dispatch) {
780 | if (typeof actionCreators === 'function') {
781 | return bindActionCreator(actionCreators, dispatch);
782 | }
783 |
784 | if (typeof actionCreators !== 'object' || actionCreators === null) {
785 | throw new Error('bindActionCreators expected an object or a function, instead received ' + (actionCreators === null ? 'null' : typeof actionCreators) + '. ' + 'Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?');
786 | }
787 |
788 | var keys = Object.keys(actionCreators);
789 | var boundActionCreators = {};
790 | for (var i = 0; i < keys.length; i++) {
791 | var key = keys[i];
792 | var actionCreator = actionCreators[key];
793 | if (typeof actionCreator === 'function') {
794 | boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
795 | }
796 | }
797 | return boundActionCreators;
798 | }
799 |
800 | /***/ },
801 |
802 | /***/ 173:
803 | /***/ function(module, exports, __webpack_require__) {
804 |
805 | 'use strict';
806 |
807 | 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; };
808 |
809 | exports.__esModule = true;
810 | exports["default"] = applyMiddleware;
811 |
812 | var _compose = __webpack_require__(174);
813 |
814 | var _compose2 = _interopRequireDefault(_compose);
815 |
816 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
817 |
818 | /**
819 | * Creates a store enhancer that applies middleware to the dispatch method
820 | * of the Redux store. This is handy for a variety of tasks, such as expressing
821 | * asynchronous actions in a concise manner, or logging every action payload.
822 | *
823 | * See `redux-thunk` package as an example of the Redux middleware.
824 | *
825 | * Because middleware is potentially asynchronous, this should be the first
826 | * store enhancer in the composition chain.
827 | *
828 | * Note that each middleware will be given the `dispatch` and `getState` functions
829 | * as named arguments.
830 | *
831 | * @param {...Function} middlewares The middleware chain to be applied.
832 | * @returns {Function} A store enhancer applying the middleware.
833 | */
834 | function applyMiddleware() {
835 | for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
836 | middlewares[_key] = arguments[_key];
837 | }
838 |
839 | return function (createStore) {
840 | return function (reducer, initialState, enhancer) {
841 | var store = createStore(reducer, initialState, enhancer);
842 | var _dispatch = store.dispatch;
843 | var chain = [];
844 |
845 | var middlewareAPI = {
846 | getState: store.getState,
847 | dispatch: function dispatch(action) {
848 | return _dispatch(action);
849 | }
850 | };
851 | chain = middlewares.map(function (middleware) {
852 | return middleware(middlewareAPI);
853 | });
854 | _dispatch = _compose2["default"].apply(undefined, chain)(store.dispatch);
855 |
856 | return _extends({}, store, {
857 | dispatch: _dispatch
858 | });
859 | };
860 | };
861 | }
862 |
863 | /***/ },
864 |
865 | /***/ 174:
866 | /***/ function(module, exports) {
867 |
868 | "use strict";
869 |
870 | exports.__esModule = true;
871 | exports["default"] = compose;
872 | /**
873 | * Composes single-argument functions from right to left.
874 | *
875 | * @param {...Function} funcs The functions to compose.
876 | * @returns {Function} A function obtained by composing functions from right to
877 | * left. For example, compose(f, g, h) is identical to arg => f(g(h(arg))).
878 | */
879 | function compose() {
880 | for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
881 | funcs[_key] = arguments[_key];
882 | }
883 |
884 | return function () {
885 | if (funcs.length === 0) {
886 | return arguments.length <= 0 ? undefined : arguments[0];
887 | }
888 |
889 | var last = funcs[funcs.length - 1];
890 | var rest = funcs.slice(0, -1);
891 |
892 | return rest.reduceRight(function (composed, f) {
893 | return f(composed);
894 | }, last.apply(undefined, arguments));
895 | };
896 | }
897 |
898 | /***/ },
899 |
900 | /***/ 178:
901 | /***/ function(module, exports, __webpack_require__) {
902 |
903 | 'use strict';
904 |
905 | Object.defineProperty(exports, "__esModule", {
906 | value: true
907 | });
908 |
909 | var _redux = __webpack_require__(165);
910 |
911 | var _nqueen = __webpack_require__(179);
912 |
913 | var _nqueen2 = _interopRequireDefault(_nqueen);
914 |
915 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
916 |
917 | var nqueenApp = (0, _redux.combineReducers)({
918 | nqueen: _nqueen2.default
919 | });
920 |
921 | exports.default = nqueenApp;
922 |
923 | /***/ },
924 |
925 | /***/ 179:
926 | /***/ function(module, exports, __webpack_require__) {
927 |
928 | 'use strict';
929 |
930 | Object.defineProperty(exports, "__esModule", {
931 | value: true
932 | });
933 |
934 | var _solver = __webpack_require__(180);
935 |
936 | var _solver2 = _interopRequireDefault(_solver);
937 |
938 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
939 |
940 | var intState = {
941 | isCalculating: false,
942 | numberOfSquares: 1,
943 | answer: null
944 | };
945 |
946 | var startNQueen = function startNQueen(state) {
947 | return Object.assign({}, state, {
948 | isCalculating: true,
949 | answer: null
950 | });
951 | };
952 |
953 | var calculateNQueen = function calculateNQueen(state, n) {
954 | return {
955 | numberOfSquares: n,
956 | isCalculating: true,
957 | answer: +n < 16 ? (0, _solver2.default)(+n).length : 'N is too large...'
958 | };
959 | };
960 |
961 | var completeNQueen = function completeNQueen(state) {
962 | return Object.assign({}, state, {
963 | isCalculating: false
964 | });
965 | };
966 |
967 | exports.default = function () {
968 | var state = arguments.length <= 0 || arguments[0] === undefined ? intState : arguments[0];
969 | var action = arguments[1];
970 |
971 | switch (action.type) {
972 | case 'START_NQUEEN':
973 | return startNQueen(state);
974 | case 'CALCULATE_NQUEEN':
975 | return calculateNQueen(state, action.number);
976 | case 'COMPLETE_NQUEEN':
977 | return completeNQueen(state);
978 | default:
979 | return state;
980 | }
981 | };
982 |
983 | /***/ },
984 |
985 | /***/ 180:
986 | /***/ function(module, exports) {
987 |
988 | "use strict";
989 |
990 | Object.defineProperty(exports, "__esModule", {
991 | value: true
992 | });
993 |
994 | exports.default = function (n, z) {
995 | var sol = [];
996 |
997 | var _solve = function _solve(board) {
998 | var nx = board.length;
999 |
1000 | if (board.length === n) {
1001 | sol.push(board);
1002 | return;
1003 | }
1004 |
1005 | for (var i = 0; i < n; i++) {
1006 | var legal = true;
1007 | for (var j = 0; j < board.length; j++) {
1008 | var ox = j;
1009 | var oy = board[j];
1010 | var slope = Math.abs((nx - ox) / (i - oy));
1011 | if (i === oy || slope === 1) {
1012 | legal = false;
1013 | break;
1014 | }
1015 | }
1016 | if (legal) {
1017 | board.push(i);
1018 | _solve(board);
1019 | board.pop();
1020 | }
1021 | }
1022 | };
1023 |
1024 | if (z !== undefined) {
1025 | _solve([z]);
1026 | } else {
1027 | _solve([]);
1028 | }
1029 |
1030 | return sol;
1031 | };
1032 |
1033 | /***/ },
1034 |
1035 | /***/ 189:
1036 | /***/ function(module, exports, __webpack_require__) {
1037 |
1038 | var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module) {'use strict';
1039 |
1040 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
1041 |
1042 | (function webpackUniversalModuleDefinition(root, factory) {
1043 | if (( false ? 'undefined' : _typeof(exports)) === 'object' && ( false ? 'undefined' : _typeof(module)) === 'object') module.exports = factory();else if (true) !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));else if ((typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object') exports["ReduxWorker"] = factory();else root["ReduxWorker"] = factory();
1044 | })(undefined, function () {
1045 | return (/******/function (modules) {
1046 | // webpackBootstrap
1047 | /******/ // The module cache
1048 | /******/var installedModules = {};
1049 |
1050 | /******/ // The require function
1051 | /******/function __webpack_require__(moduleId) {
1052 |
1053 | /******/ // Check if module is in cache
1054 | /******/if (installedModules[moduleId])
1055 | /******/return installedModules[moduleId].exports;
1056 |
1057 | /******/ // Create a new module (and put it into the cache)
1058 | /******/var module = installedModules[moduleId] = {
1059 | /******/exports: {},
1060 | /******/id: moduleId,
1061 | /******/loaded: false
1062 | /******/ };
1063 |
1064 | /******/ // Execute the module function
1065 | /******/modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
1066 |
1067 | /******/ // Flag the module as loaded
1068 | /******/module.loaded = true;
1069 |
1070 | /******/ // Return the exports of the module
1071 | /******/return module.exports;
1072 | /******/
1073 | }
1074 |
1075 | /******/ // expose the modules object (__webpack_modules__)
1076 | /******/__webpack_require__.m = modules;
1077 |
1078 | /******/ // expose the module cache
1079 | /******/__webpack_require__.c = installedModules;
1080 |
1081 | /******/ // __webpack_public_path__
1082 | /******/__webpack_require__.p = "";
1083 |
1084 | /******/ // Load entry module and return exports
1085 | /******/return __webpack_require__(0);
1086 | /******/
1087 | }(
1088 | /************************************************************************/
1089 | /******/[
1090 | /* 0 */
1091 | /***/function (module, exports, __webpack_require__) {
1092 |
1093 | 'use strict';
1094 |
1095 | Object.defineProperty(exports, "__esModule", {
1096 | value: true
1097 | });
1098 | exports.createWorker = exports.applyWorker = undefined;
1099 |
1100 | var _createWorker = __webpack_require__(1);
1101 |
1102 | var _createWorker2 = _interopRequireDefault(_createWorker);
1103 |
1104 | var _applyWorker = __webpack_require__(2);
1105 |
1106 | var _applyWorker2 = _interopRequireDefault(_applyWorker);
1107 |
1108 | function _interopRequireDefault(obj) {
1109 | return obj && obj.__esModule ? obj : { default: obj };
1110 | }
1111 |
1112 | exports.applyWorker = _applyWorker2.default;
1113 | exports.createWorker = _createWorker2.default;
1114 | exports.default = { applyWorker: _applyWorker2.default, createWorker: _createWorker2.default };
1115 |
1116 | /***/
1117 | },
1118 | /* 1 */
1119 | /***/function (module, exports) {
1120 |
1121 | 'use strict';
1122 |
1123 | Object.defineProperty(exports, "__esModule", {
1124 | value: true
1125 | });
1126 |
1127 | var _createClass = function () {
1128 | function defineProperties(target, props) {
1129 | for (var i = 0; i < props.length; i++) {
1130 | 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);
1131 | }
1132 | }return function (Constructor, protoProps, staticProps) {
1133 | if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;
1134 | };
1135 | }();
1136 |
1137 | function _classCallCheck(instance, Constructor) {
1138 | if (!(instance instanceof Constructor)) {
1139 | throw new TypeError("Cannot call a class as a function");
1140 | }
1141 | }
1142 |
1143 | var createWorker = function createWorker(reducer) {
1144 | // Initialize ReduxWorekr
1145 | var worker = new ReduxWorker();
1146 |
1147 | self.addEventListener('message', function (e) {
1148 | var action = e.data;
1149 |
1150 | if (typeof action.type === 'string') {
1151 | if (!worker.reducer || typeof worker.reducer !== 'function') {
1152 | throw new Error('Expect reducer to be function. Have you registerReducer yet?');
1153 | }
1154 |
1155 | // Set new state
1156 | var state = worker.state;
1157 | state = worker.state = worker.reducer(state, action);
1158 | state = worker.transform(state);
1159 |
1160 | // Send new state to main thread
1161 | self.postMessage({
1162 | type: action.type,
1163 | state: state,
1164 | action: action
1165 | });
1166 |
1167 | return;
1168 | }
1169 |
1170 | if (typeof action.task === 'string' && typeof action._taskId === 'number') {
1171 | var taskRunner = worker.tasks[action.task];
1172 |
1173 | if (!taskRunner || typeof taskRunner !== 'function') {
1174 | throw new Error('Cannot find runner for task ' + action.task + '. Have you registerTask yet?');
1175 | }
1176 |
1177 | // Send new state to main thread
1178 | self.postMessage({
1179 | _taskId: action._taskId,
1180 | response: taskRunner(action)
1181 | });
1182 | }
1183 | });
1184 |
1185 | return worker;
1186 | };
1187 |
1188 | var ReduxWorker = function () {
1189 | function ReduxWorker() {
1190 | _classCallCheck(this, ReduxWorker);
1191 |
1192 | // Taskrunners
1193 | this.tasks = {};
1194 |
1195 | // Redux-specific variables
1196 | this.state = {};
1197 | this.reducer = null;
1198 | this.transform = function (state) {
1199 | return state;
1200 | };
1201 | }
1202 |
1203 | _createClass(ReduxWorker, [{
1204 | key: 'registerReducer',
1205 | value: function registerReducer(reducer, transform) {
1206 | this.reducer = reducer;
1207 | this.state = reducer({}, {});
1208 | }
1209 | }, {
1210 | key: 'registerTask',
1211 | value: function registerTask(name, taskFn) {
1212 | this.tasks[name] = taskFn;
1213 | }
1214 | }]);
1215 |
1216 | return ReduxWorker;
1217 | }();
1218 |
1219 | exports.default = createWorker;
1220 |
1221 | /***/
1222 | },
1223 | /* 2 */
1224 | /***/function (module, exports) {
1225 |
1226 | 'use strict';
1227 |
1228 | Object.defineProperty(exports, "__esModule", {
1229 | value: true
1230 | });
1231 | var defer = function defer() {
1232 | var result = {};
1233 | result.promise = new Promise(function (resolve, reject) {
1234 | result.resolve = resolve;
1235 | result.reject = reject;
1236 | });
1237 | return result;
1238 | };
1239 |
1240 | var applyWorker = function applyWorker(worker) {
1241 | return function (createStore) {
1242 | return function (reducer, initialState, enhancer) {
1243 | if (!(worker instanceof Worker)) {
1244 | console.error('Expect input to be a Web Worker. Fall back to normal store.');
1245 | return createStore(reducer, initialState, enhancer);
1246 | }
1247 |
1248 | // New reducer for workified store
1249 | var replacementReducer = function replacementReducer(state, action) {
1250 | if (action.state) {
1251 | return action.state;
1252 | }
1253 | return state;
1254 | };
1255 |
1256 | // Start task id;
1257 | var taskId = 0;
1258 | var taskCompleteCallbacks = {};
1259 |
1260 | // Create store using new reducer
1261 | var store = createStore(replacementReducer, reducer({}, {}), enhancer);
1262 |
1263 | // Store reference of old dispatcher
1264 | var next = store.dispatch;
1265 |
1266 | // Replace dispatcher
1267 | store.dispatch = function (action) {
1268 | if (typeof action.type === 'string') {
1269 | if (window.disableWebWorker) {
1270 | return next({
1271 | type: action.type,
1272 | state: reducer(store.getState(), action)
1273 | });
1274 | }
1275 | worker.postMessage(action);
1276 | }
1277 |
1278 | if (typeof action.task === 'string') {
1279 | var task = Object.assign({}, action, { _taskId: taskId });
1280 | var deferred = defer();
1281 |
1282 | taskCompleteCallbacks[taskId] = deferred;
1283 | taskId++;
1284 | worker.postMessage(task);
1285 | return deferred.promise;
1286 | }
1287 | };
1288 |
1289 | store.isWorker = true;
1290 |
1291 | // Add worker events listener
1292 | worker.addEventListener('message', function (e) {
1293 | var action = e.data;
1294 | if (typeof action.type === 'string') {
1295 | next(action);
1296 | }
1297 |
1298 | if (typeof action._taskId === 'number') {
1299 | var wrapped = taskCompleteCallbacks[action._taskId];
1300 |
1301 | if (wrapped) {
1302 | wrapped.resolve(action);
1303 | delete taskCompleteCallbacks[action._taskId];
1304 | }
1305 | }
1306 | });
1307 |
1308 | return store;
1309 | };
1310 | };
1311 | };
1312 |
1313 | exports.default = applyWorker;
1314 |
1315 | /***/
1316 | }
1317 | /******/])
1318 | );
1319 | });
1320 | ;
1321 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(190)(module)))
1322 |
1323 | /***/ },
1324 |
1325 | /***/ 190:
1326 | /***/ function(module, exports) {
1327 |
1328 | module.exports = function(module) {
1329 | if(!module.webpackPolyfill) {
1330 | module.deprecate = function() {};
1331 | module.paths = [];
1332 | // module.parent = undefined by default
1333 | module.children = [];
1334 | module.webpackPolyfill = 1;
1335 | }
1336 | return module;
1337 | }
1338 |
1339 |
1340 | /***/ }
1341 |
1342 | /******/ });
--------------------------------------------------------------------------------
/demo/sortTable/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Redux Worker
5 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/demo/sortTable/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { Provider } from 'react-redux'
4 | import { createStore, applyMiddleware, compose } from 'redux'
5 | import thunk from 'redux-thunk'
6 | import rootReducer from './reducers'
7 | import App from './components/App'
8 | import { applyWorker } from 'redux-worker'
9 | import createLogger from 'redux-logger';
10 |
11 | const logger = createLogger();
12 |
13 | const worker = new Worker('./dist/worker.bundle.js');
14 |
15 | const enhancerWithWorker = compose(
16 | // Middleware you want to use in development:
17 | applyMiddleware(thunk, logger),
18 | applyWorker(worker)
19 | );
20 |
21 | const store = createStore(rootReducer, {}, enhancerWithWorker);
22 |
23 | render(
24 |
25 |
26 | ,
27 | document.getElementById('app')
28 | )
29 |
30 |
31 |
--------------------------------------------------------------------------------
/demo/sortTable/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-worker-demo-sort-table",
3 | "version": "1.0.0",
4 | "description": "Demo for Redux-Worker",
5 | "main": "main.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "watch": "./node_modules/.bin/webpack-dev-server"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "babel-polyfill": "^6.6.1",
14 | "babel-runtime": "^6.6.1",
15 | "faker": "^3.1.0",
16 | "react": "^0.14.7",
17 | "react-dom": "^0.14.7",
18 | "react-infinite": "^0.9.2",
19 | "react-redux": "^4.4.0",
20 | "react-virtual-list": "^1.8.0",
21 | "redux": "^3.3.1",
22 | "redux-logger": "^2.6.1",
23 | "redux-thunk": "^1.0.3",
24 | "redux-worker": "^0.1.0"
25 | },
26 | "devDependencies": {
27 | "babel-core": "^6.6.4",
28 | "babel-loader": "^6.2.4",
29 | "babel-preset-es2015": "6.3.13",
30 | "babel-preset-react": "^6.5.0",
31 | "webpack": "^1.12.14",
32 | "webpack-dev-server": "^1.14.1"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/demo/sortTable/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import users from './users/users'
3 |
4 | const sortTableApp = combineReducers({
5 | users
6 | });
7 |
8 | export default sortTableApp
--------------------------------------------------------------------------------
/demo/sortTable/reducers/users/users.js:
--------------------------------------------------------------------------------
1 | import Faker, {fake} from 'faker'
2 | import USERS from '../../usersFixtures'
3 |
4 | const generateUser = () => {
5 | return {
6 | uuid: Faker.random.uuid(),
7 | firstName: fake('{{name.firstName}}'),
8 | lastName: fake('{{name.lastName}}'),
9 | dateOfBirth: fake('{{date.past}}')
10 | };
11 | }
12 |
13 | const generateUsers = (n) => {
14 | let users = [];
15 | for (let i = 0; i < n; i++) {
16 | users.push(generateUser());
17 | }
18 |
19 | return users;
20 | }
21 |
22 | const intState = [];
23 |
24 | const sortByFirstName = (users, reverse) => {
25 | return users.sort(function(a, b){
26 | if(a.firstName < b.firstName) return reverse ? 1 : -1;
27 | if(a.firstName > b.firstName) return reverse ? -1 : 1;
28 | return 0;
29 | });
30 | }
31 |
32 | const sortByLastName = (users, reverse) => {
33 | return users.sort(function(a, b){
34 | if(a.lastName < b.lastName) return reverse ? 1 : -1;
35 | if(a.lastName > b.lastName) return reverse ? -1 : 1;
36 | return 0;
37 | });
38 | }
39 |
40 | const sortByDOB = (users, reverse) => {
41 | return users.sort(function(userA, userB) {
42 | let result = reverse ?
43 | getTime(userA.dateOfBirth) - getTime(userB.dateOfBirth) :
44 | getTime(userB.dateOfBirth) - getTime(userA.dateOfBirth)
45 | return result;
46 | })
47 |
48 | function getTime(timestamp) {
49 | return new Date(timestamp).getTime();
50 | }
51 | }
52 |
53 | export default (state = intState, action) => {
54 | switch (action.type) {
55 | case 'SORT_BY_FIRST':
56 | return sortByFirstName(state, action.reverse);
57 | case 'SORT_BY_LAST':
58 | return sortByLastName(state, action.reverse);
59 | case 'SORT_BY_DOB':
60 | return sortByDOB(state, action.reverse);
61 | case 'GENERATE_USERS':
62 | return generateUsers(action.number || 10);
63 | default:
64 | return state;
65 | }
66 |
67 | return state;
68 | };
--------------------------------------------------------------------------------
/demo/sortTable/reducers/worker.js:
--------------------------------------------------------------------------------
1 | import reducer from '../reducers'
2 | import { createWorker } from 'redux-worker'
3 |
4 | import Faker, {fake} from 'faker'
5 |
6 | const generateUser = () => {
7 | return {
8 | uuid: Faker.random.uuid(),
9 | firstName: fake('{{name.firstName}}'),
10 | lastName: fake('{{name.lastName}}'),
11 | dateOfBirth: fake('{{date.past}}')
12 | };
13 | }
14 |
15 | const generateUsers = (n) => {
16 | let users = [];
17 | for (let i = 0; i < n; i++) {
18 | users.push(generateUser());
19 | }
20 |
21 | return users;
22 | }
23 |
24 |
25 | let worker = createWorker();
26 |
27 | worker.registerReducer(reducer);
28 |
29 | worker.registerTask('GENERATE', function(a) {
30 | let num = a.number;
31 | return generateUsers(num);
32 | });
33 |
34 | function getTime(timestamp) {
35 | return new Date(timestamp).getTime();
36 | }
--------------------------------------------------------------------------------
/demo/sortTable/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: {
6 | main: './main.js',
7 | worker: './reducers/worker.js'
8 | },
9 | output: {
10 | path: __dirname,
11 | filename: './dist/[name].bundle.js'
12 | },
13 | module: {
14 | loaders: [
15 | {
16 | test: /.jsx?$/,
17 | loader: 'babel-loader',
18 | exclude: /node_modules/,
19 | query: {
20 | presets: ['es2015', 'react']
21 | }
22 | }
23 | ]
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/dist/reduxWorker.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["ReduxWorker"] = factory();
8 | else
9 | root["ReduxWorker"] = 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 | Object.defineProperty(exports, "__esModule", {
60 | value: true
61 | });
62 | exports.createWorker = exports.applyWorker = undefined;
63 |
64 | var _createWorker = __webpack_require__(1);
65 |
66 | var _createWorker2 = _interopRequireDefault(_createWorker);
67 |
68 | var _applyWorker = __webpack_require__(2);
69 |
70 | var _applyWorker2 = _interopRequireDefault(_applyWorker);
71 |
72 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
73 |
74 | exports.applyWorker = _applyWorker2.default;
75 | exports.createWorker = _createWorker2.default;
76 | exports.default = { applyWorker: _applyWorker2.default, createWorker: _createWorker2.default };
77 |
78 | /***/ },
79 | /* 1 */
80 | /***/ function(module, exports) {
81 |
82 | 'use strict';
83 |
84 | Object.defineProperty(exports, "__esModule", {
85 | value: true
86 | });
87 |
88 | 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; }; }();
89 |
90 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
91 |
92 | var createWorker = function createWorker(reducer) {
93 | // Initialize ReduxWorekr
94 | var worker = new ReduxWorker();
95 |
96 | self.addEventListener('message', function (e) {
97 | var action = e.data;
98 |
99 | if (typeof action.type === 'string') {
100 | if (!worker.reducer || typeof worker.reducer !== 'function') {
101 | throw new Error('Expect reducer to be function. Have you registerReducer yet?');
102 | }
103 |
104 | // Set new state
105 | var state = worker.state;
106 | state = worker.state = worker.reducer(state, action);
107 | state = worker.transform(state);
108 |
109 | // Send new state to main thread
110 | self.postMessage({
111 | type: action.type,
112 | state: state,
113 | action: action
114 | });
115 |
116 | return;
117 | }
118 |
119 | if (typeof action.task === 'string' && typeof action._taskId === 'number') {
120 | var taskRunner = worker.tasks[action.task];
121 |
122 | if (!taskRunner || typeof taskRunner !== 'function') {
123 | throw new Error('Cannot find runner for task ' + action.task + '. Have you registerTask yet?');
124 | }
125 |
126 | // Send new state to main thread
127 | self.postMessage({
128 | _taskId: action._taskId,
129 | response: taskRunner(action)
130 | });
131 | }
132 | });
133 |
134 | return worker;
135 | };
136 |
137 | var ReduxWorker = function () {
138 | function ReduxWorker() {
139 | _classCallCheck(this, ReduxWorker);
140 |
141 | // Taskrunners
142 | this.tasks = {};
143 |
144 | // Redux-specific variables
145 | this.state = {};
146 | this.reducer = null;
147 | this.transform = function (state) {
148 | return state;
149 | };
150 | }
151 |
152 | _createClass(ReduxWorker, [{
153 | key: 'registerReducer',
154 | value: function registerReducer(reducer, transform) {
155 | this.reducer = reducer;
156 | this.state = reducer({}, {});
157 | }
158 | }, {
159 | key: 'registerTask',
160 | value: function registerTask(name, taskFn) {
161 | this.tasks[name] = taskFn;
162 | }
163 | }]);
164 |
165 | return ReduxWorker;
166 | }();
167 |
168 | exports.default = createWorker;
169 |
170 | /***/ },
171 | /* 2 */
172 | /***/ function(module, exports) {
173 |
174 | 'use strict';
175 |
176 | Object.defineProperty(exports, "__esModule", {
177 | value: true
178 | });
179 | var defer = function defer() {
180 | var result = {};
181 | result.promise = new Promise(function (resolve, reject) {
182 | result.resolve = resolve;
183 | result.reject = reject;
184 | });
185 | return result;
186 | };
187 |
188 | var applyWorker = function applyWorker(worker) {
189 | return function (createStore) {
190 | return function (reducer, initialState, enhancer) {
191 | if (!(worker instanceof Worker)) {
192 | console.error('Expect input to be a Web Worker. Fall back to normal store.');
193 | return createStore(reducer, initialState, enhancer);
194 | }
195 |
196 | // New reducer for workified store
197 | var replacementReducer = function replacementReducer(state, action) {
198 | if (action.state) {
199 | return action.state;
200 | }
201 | return state;
202 | };
203 |
204 | // Start task id;
205 | var taskId = 0;
206 | var taskCompleteCallbacks = {};
207 |
208 | // Create store using new reducer
209 | var store = createStore(replacementReducer, reducer({}, {}), enhancer);
210 |
211 | // Store reference of old dispatcher
212 | var next = store.dispatch;
213 |
214 | // Replace dispatcher
215 | store.dispatch = function (action) {
216 | if (typeof action.type === 'string') {
217 | if (window.disableWebWorker) {
218 | return next({
219 | type: action.type,
220 | state: reducer(store.getState(), action)
221 | });
222 | }
223 | worker.postMessage(action);
224 | }
225 |
226 | if (typeof action.task === 'string') {
227 | var task = Object.assign({}, action, { _taskId: taskId });
228 | var deferred = defer();
229 |
230 | taskCompleteCallbacks[taskId] = deferred;
231 | taskId++;
232 | worker.postMessage(task);
233 | return deferred.promise;
234 | }
235 | };
236 |
237 | store.isWorker = true;
238 |
239 | // Add worker events listener
240 | worker.addEventListener('message', function (e) {
241 | var action = e.data;
242 | if (typeof action.type === 'string') {
243 | next(action);
244 | }
245 |
246 | if (typeof action._taskId === 'number') {
247 | var wrapped = taskCompleteCallbacks[action._taskId];
248 |
249 | if (wrapped) {
250 | wrapped.resolve(action);
251 | delete taskCompleteCallbacks[action._taskId];
252 | }
253 | }
254 | });
255 |
256 | return store;
257 | };
258 | };
259 | };
260 |
261 | exports.default = applyWorker;
262 |
263 | /***/ }
264 | /******/ ])
265 | });
266 | ;
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | browsers: [ 'PhantomJS' ],
4 |
5 | files: [
6 | {
7 | pattern: './spec/test-context.js',
8 | watched: false
9 | }
10 | ],
11 |
12 | frameworks: [ 'jasmine' ],
13 |
14 | preprocessors: {
15 | 'spec/test-context.js': [ 'webpack' ]
16 | },
17 |
18 | reporters: [ 'mocha' ],
19 |
20 | webpack: {
21 | module: {
22 | loaders: [
23 | {
24 | test: /\.js/,
25 | exclude: /node_modules/,
26 | loader: 'babel-loader'
27 | }
28 | ]
29 | },
30 |
31 | watch: true
32 | },
33 |
34 | webpackServer: {
35 | noInfo: true
36 | }
37 | });
38 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-worker",
3 | "version": "0.1.2",
4 | "description": "",
5 | "main": "./dist/reduxWorker.js",
6 | "scripts": {
7 | "test": "./node_modules/.bin/karma start --browsers PhantomJS",
8 | "build": "./node_modules/.bin/webpack src/reduxWorker.js dist/reduxWorker.js --color --progress"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "babel-polyfill": "^6.7.4",
14 | "babel-runtime": "^6.6.1"
15 | },
16 | "devDependencies": {
17 | "babel-cli": "^6.6.5",
18 | "babel-core": "^6.6.4",
19 | "babel-loader": "^6.2.4",
20 | "babel-plugin-transform-object-assign": "^6.5.0",
21 | "babel-polyfill": "^6.7.4",
22 | "babel-preset-es2015": "6.3.13",
23 | "babel-preset-react": "^6.5.0",
24 | "jasmine": "^2.4.1",
25 | "jasmine-core": "^2.4.1",
26 | "karma": "^0.13.22",
27 | "karma-jasmine": "^0.3.8",
28 | "karma-junit-reporter": "^0.4.1",
29 | "karma-mocha-reporter": "^2.0.0",
30 | "karma-phantomjs-launcher": "^1.0.0",
31 | "karma-webpack": "^1.7.0",
32 | "phantomjs-prebuilt": "^2.1.7",
33 | "webpack": "^1.12.14"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/spec/applyWorker.spec.js:
--------------------------------------------------------------------------------
1 | import applyWorker from '../src/applyWorker';
2 | import 'babel-polyfill';
3 |
4 | const blob = new Blob([ 'console.log(\'I am a worker!\');'], { type: 'text/javascript' });
5 | const url = window.URL.createObjectURL(blob);
6 |
7 | describe('applyWorker', () => {
8 | const retState = {};
9 | const reducer = () => retState;
10 | it('should return a function', () => {
11 | let result = applyWorker();
12 | expect(typeof result).toBe('function');
13 | });
14 |
15 | it('should fallback to regular createStore method if worker is not passed in', () => {
16 | let makeStoreCreator = applyWorker();
17 | let intState = {};
18 | let enhancer = {};
19 | let createStore = makeStoreCreator((reducer, intState, enhancer) => {
20 | return {
21 | reducer,
22 | intState,
23 | enhancer
24 | };
25 | });
26 | let store = createStore(reducer, intState, enhancer);
27 | expect(store.reducer).toBe(reducer);
28 | expect(store.intState).toBe(intState);
29 | expect(store.enhancer).toBe(enhancer);
30 | });
31 |
32 | it('should replace reducer and intialState when worker is passed in', () => {
33 | let worker = new Worker(url);
34 | let makeStoreCreator = applyWorker(worker);
35 | let intState = {};
36 | let enhancer = {};
37 | let createStore = makeStoreCreator((reducer, intState, enhancer) => {
38 | return {
39 | reducer,
40 | intState,
41 | enhancer
42 | };
43 | });
44 | let store = createStore(reducer, intState, enhancer);
45 | expect(store.reducer).not.toBe(reducer);
46 | expect(store.intState).toBe(retState);
47 | expect(store.enhancer).toBe(enhancer);
48 | expect(typeof store.dispatch).toBe('function');
49 | });
50 |
51 | describe('behavior of stubbed dispatch', () => {
52 | let store, worker;
53 |
54 | beforeEach(function() {
55 | worker = new Worker(url);
56 |
57 | let makeStoreCreator = applyWorker(worker);
58 | let intState = {};
59 | let enhancer = {};
60 | let createStore = makeStoreCreator((reducer, intState, enhancer) => {
61 | return {
62 | reducer,
63 | intState,
64 | enhancer
65 | };
66 | });
67 |
68 | store = createStore(reducer, intState, enhancer);
69 | spyOn(worker, 'postMessage').and.callThrough();
70 | });
71 |
72 | it('should postMessage when dispatching an action', () => {
73 | const action = {
74 | type: 'ACTION',
75 | data: 'DATA'
76 | };
77 |
78 | store.dispatch(action);
79 | expect(worker.postMessage).toHaveBeenCalledWith(action);
80 | });
81 |
82 | it('should return a promise when dispatching a task', function() {
83 | const task = {
84 | task: 'TASK',
85 | data: 'DATA'
86 | };
87 |
88 | store.dispatch(task);
89 | expect(worker.postMessage).toHaveBeenCalledWith({
90 | task: 'TASK',
91 | data: 'DATA',
92 | _taskId: jasmine.any(Number)
93 | });
94 | });
95 | })
96 | });
--------------------------------------------------------------------------------
/spec/createWorker.spec.js:
--------------------------------------------------------------------------------
1 | import createWorker from '../src/createWorker';
2 | import 'babel-polyfill';
3 |
4 | describe('createWorker', () => {
5 | let worker;
6 |
7 | beforeEach(() => {
8 | worker = createWorker();
9 | })
10 |
11 | afterEach(() => {
12 | worker.destroy();
13 | });
14 |
15 | it('should have create ReduxWorker', () => {
16 | expect(typeof worker.tasks).toBe('object');
17 | expect(typeof worker.state).toBe('object');
18 | expect(worker.reducer).toBe(null);
19 | expect(typeof worker.transform).toBe('function');
20 | });
21 |
22 | it('should registerReducer', () => {
23 | let reducer = () => {};
24 | worker.registerReducer(reducer);
25 | expect(worker.reducer).toBe(reducer);
26 | });
27 |
28 | it('should registerTask', () => {
29 | let task = () => {};
30 | worker.registerTask('testTask', task);
31 | expect(worker.tasks.testTask).toBe(task);
32 | });
33 |
34 | describe('Behavior of ReduxWorker', () => {
35 | let taskOne = (data) => {
36 | return data.num + 1;
37 | };
38 |
39 | let reducer;
40 |
41 | beforeEach(() => {
42 | reducer = jasmine.createSpy('reducer');
43 | worker.registerReducer(reducer);
44 | worker.registerTask('taskOne', taskOne);
45 | spyOn(self, 'postMessage');
46 | reducer.calls.reset();
47 | self.postMessage.calls.reset();
48 | });
49 |
50 | it('should run action through reducer', () => {
51 | let event = new Event('message');
52 |
53 | event.data = {
54 | type: 'ACTION',
55 | data: 'DATA'
56 | }
57 |
58 | self.dispatchEvent(event);
59 | let args = reducer.calls.first().args;
60 |
61 | expect(self.postMessage).toHaveBeenCalledWith({
62 | type: 'ACTION',
63 | state: undefined,
64 | action: {
65 | type: 'ACTION',
66 | data: 'DATA'
67 | }
68 | });
69 |
70 | expect(args[ 0 ]).toBe(undefined);
71 | expect(args[ 1 ]).toEqual({
72 | type: 'ACTION',
73 | data: 'DATA'
74 | });
75 | });
76 |
77 | it('should run task through tasksRunner', () => {
78 | let event = new Event('message');
79 |
80 | event.data = {
81 | task: 'taskOne',
82 | num: 2,
83 | _taskId: 0
84 | }
85 |
86 | self.dispatchEvent(event);
87 |
88 | expect(self.postMessage).toHaveBeenCalledWith({
89 | _taskId: 0,
90 | response: 3
91 | });
92 | });
93 | });
94 |
95 |
96 | });
--------------------------------------------------------------------------------
/spec/test-context.js:
--------------------------------------------------------------------------------
1 | var context = require.context('./', true, /\.spec\.js$/);
2 | context.keys().forEach(context);
3 |
--------------------------------------------------------------------------------
/src/applyWorker.js:
--------------------------------------------------------------------------------
1 | const defer = function() {
2 | let result = {};
3 | result.promise = new Promise(function(resolve, reject) {
4 | result.resolve = resolve;
5 | result.reject = reject;
6 | });
7 | return result;
8 | };
9 |
10 | const applyWorker = (worker) => {
11 | return createStore => (reducer, initialState, enhancer) => {
12 | if (!(worker instanceof Worker)) {
13 | console.error('Expect input to be a Web Worker. Fall back to normal store.');
14 | return createStore(reducer, initialState, enhancer);
15 | }
16 |
17 | // New reducer for workified store
18 | let replacementReducer = (state, action) => {
19 | if (action.state) {
20 | return action.state;
21 | }
22 | return state;
23 | }
24 |
25 | // Start task id;
26 | let taskId = 0;
27 | let taskCompleteCallbacks = {};
28 |
29 | // Create store using new reducer
30 | let store = createStore(replacementReducer, reducer({}, {}), enhancer);
31 |
32 | // Store reference of old dispatcher
33 | let next = store.dispatch;
34 |
35 | // Replace dispatcher
36 | store.dispatch = (action) => {
37 | if (typeof action.type === 'string') {
38 | if (window.disableWebWorker) {
39 | return next({
40 | type: action.type,
41 | state: reducer(store.getState(), action)
42 | });
43 | }
44 | worker.postMessage(action);
45 | }
46 |
47 | if (typeof action.task === 'string') {
48 | let task = Object.assign({}, action, { _taskId: taskId });
49 | let deferred = defer();
50 |
51 | taskCompleteCallbacks[ taskId ] = deferred
52 | taskId++;
53 | worker.postMessage(task);
54 | return deferred.promise;
55 | }
56 | }
57 |
58 | store.isWorker = true;
59 |
60 | // Add worker events listener
61 | worker.addEventListener('message', function(e) {
62 | let action = e.data;
63 | if (typeof action.type === 'string') {
64 | next(action);
65 | }
66 |
67 | if (typeof action._taskId === 'number') {
68 | let wrapped = taskCompleteCallbacks[ action._taskId ];
69 |
70 | if (wrapped) {
71 | wrapped.resolve(action);
72 | delete taskCompleteCallbacks[ action._taskId ];
73 | }
74 | }
75 | });
76 |
77 | return store;
78 | }
79 | }
80 |
81 | export default applyWorker
--------------------------------------------------------------------------------
/src/createWorker.js:
--------------------------------------------------------------------------------
1 | const createWorker = (reducer) => {
2 | // Initialize ReduxWorekr
3 | let worker = new ReduxWorker();
4 |
5 | let messageHandler = (e) => {
6 | var action = e.data;
7 |
8 | if (typeof action.type === 'string') {
9 | if (!worker.reducer || typeof worker.reducer !== 'function') {
10 | throw new Error('Expect reducer to be function. Have you registerReducer yet?');
11 | }
12 |
13 | // Set new state
14 | let state = worker.state;
15 | state = worker.state = worker.reducer(state, action);
16 | state = worker.transform(state);
17 |
18 | // Send new state to main thread
19 | self.postMessage({
20 | type: action.type,
21 | state: state,
22 | action: action
23 | });
24 |
25 | return;
26 | }
27 |
28 | if (typeof action.task === 'string' && typeof action._taskId === 'number') {
29 | let taskRunner = worker.tasks[ action.task ];
30 |
31 | if (!taskRunner || typeof taskRunner !== 'function') {
32 | throw new Error('Cannot find runner for task ' + action.task + '. Have you registerTask yet?');
33 | }
34 |
35 | // Send new state to main thread
36 | self.postMessage({
37 | _taskId: action._taskId,
38 | response: taskRunner(action)
39 | });
40 | }
41 | }
42 |
43 | worker.destroy = () => {
44 | self.removeEventListener('message', messageHandler);
45 | }
46 |
47 | self.addEventListener('message', messageHandler);
48 |
49 |
50 | return worker;
51 | }
52 |
53 | class ReduxWorker {
54 | constructor() {
55 | // Taskrunners
56 | this.tasks = {};
57 |
58 | // Redux-specific variables
59 | this.state = {};
60 | this.reducer = null;
61 | this.transform = function(state) { return state; }
62 | }
63 |
64 | registerReducer(reducer, transform) {
65 | this.reducer = reducer;
66 | this.state = reducer({}, {});
67 | }
68 |
69 | registerTask(name, taskFn) {
70 | this.tasks[ name ] = taskFn;
71 | }
72 |
73 | }
74 |
75 | export default createWorker
--------------------------------------------------------------------------------
/src/reduxWorker.js:
--------------------------------------------------------------------------------
1 | import createWorker from './createWorker'
2 | import applyWorker from './applyWorker'
3 |
4 | export { applyWorker, createWorker }
5 | export default { applyWorker, createWorker }
6 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: {
6 | lib: './src/reduxWorker.js'
7 | },
8 | output: {
9 | library: 'ReduxWorker',
10 | libraryTarget: 'umd'
11 | },
12 | module: {
13 | loaders: [
14 | {
15 | test: /.jsx?$/,
16 | loader: 'babel-loader',
17 | exclude: /node_modules/,
18 | query: {
19 | presets: ['es2015', 'react']
20 | }
21 | }
22 | ]
23 | },
24 | };
25 |
--------------------------------------------------------------------------------