[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\"react\", \"es2015\", \"stage-0\"],\n\n  \"plugins\": [\n    \"transform-runtime\",\n    \"add-module-exports\",\n    \"transform-decorators-legacy\",\n    \"transform-react-display-name\"\n  ],\n\n  \"env\": {\n    \"development\": {\n      \"plugins\": [\n        \"typecheck\",\n        [\"react-transform\", {\n            \"transforms\": [{\n                \"transform\": \"react-transform-catch-errors\",\n                \"imports\": [\"react\", \"redbox-react\"]\n              }\n            ]\n        }]\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "[*]\nindent_style = space\nend_of_line = lf\nindent_size = 2\ncharset = utf-8\ntrim_trailing_whitespace = true\n\n[*.md]\nmax_line_length = 0\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".eslintignore",
    "content": "webpack/*\nkarma.conf.js\ntests.webpack.js\n"
  },
  {
    "path": ".eslintrc",
    "content": "{ \"extends\": \"eslint-config-airbnb\",\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n    \"mocha\": true\n  },\n  \"rules\": {\n    \"new-cap\": [2, { \"capIsNewExceptions\": [\"List\", \"Map\", \"Set\"] }],\n    \"react/no-multi-comp\": 0,\n    \"import/default\": 0,\n    \"import/no-duplicates\": 0,\n    \"import/named\": 0,\n    \"import/namespace\": 0,\n    \"import/no-unresolved\": 0,\n    \"import/no-named-as-default\": 2,\n    \"comma-dangle\": 0,  // not sure why airbnb turned this on. gross!\n    \"indent\": [2, 2, {\"SwitchCase\": 1}],\n    \"no-console\": 0,\n    \"no-alert\": 0\n  },\n  \"plugins\": [\n    \"react\", \"import\"\n  ],\n  \"settings\": {\n    \"import/parser\": \"babel-eslint\",\n    \"import/resolve\": {\n      \"moduleDirectory\": [\"node_modules\", \"src\"]\n    }\n  },\n  \"globals\": {\n    \"__DEVELOPMENT__\": true,\n    \"__CLIENT__\": true,\n    \"__SERVER__\": true,\n    \"__DISABLE_SSR__\": true,\n    \"__DEVTOOLS__\": true,\n    \"socket\": true,\n    \"webpackIsomorphicTools\": true\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\nnode_modules/\ndist/\n*.iml\nwebpack-assets.json\nwebpack-stats.json\nnpm-debug.log\n*.swp\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\n\nnode_js:\n  - \"0.12\"\n  - \"4.0\"\n  - \"4\"\n  - \"5\"\n  - \"stable\"\n\nsudo: false\n\nbefore_script:\n  - export DISPLAY=:99.0\n  - sh -e /etc/init.d/xvfb start\n\nscript:\n  - npm run lint\n  - npm test\n  - npm run test-node\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing Guidelines\n\nSome basic conventions for contributing to this project.\n\n### General\n\nPlease make sure that there aren't existing pull requests attempting to address the issue mentioned. Likewise, please check for issues related to update, as someone else may be working on the issue in a branch or fork.\n\n* Non-trivial changes should be discussed in an issue first\n* Develop in a topic branch, not master\n* Squash your commits\n\n### Linting\n\nPlease check your code using `npm run lint` before submitting your pull requests, as the CI build will fail if `eslint` fails.\n\n### Commit Message Format\n\nEach commit message should include a **type**, a **scope** and a **subject**:\n\n```\n <type>(<scope>): <subject>\n```\n\nLines should not exceed 100 characters. This allows the message to be easier to read on github as well as in various git tools and produces a nice, neat commit log ie:\n\n```\n #459  refactor(utils): create url mapper utility function\n #463  chore(webpack): update to isomorphic tools v2\n #494  fix(babel): correct dependencies and polyfills\n #510  feat(app): add react-bootstrap responsive navbar\n``` \n\n#### Type\n\nMust be one of the following:\n\n* **feat**: A new feature\n* **fix**: A bug fix\n* **docs**: Documentation only changes\n* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing\n  semi-colons, etc)\n* **refactor**: A code change that neither fixes a bug or adds a feature\n* **test**: Adding missing tests\n* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation\n  generation\n\n#### Scope\n\nThe scope could be anything specifying place of the commit change. For example `webpack`,\n`helpers`, `api` etc...\n\n#### Subject\n\nThe subject contains succinct description of the change:\n\n* use the imperative, present tense: \"change\" not \"changed\" nor \"changes\"\n* don't capitalize first letter\n* no dot (.) at the end\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Erik Rasmussen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# ⚠️ DO NOT USE THIS!! ⚠️\n\nOnce upon a time, this repo helped a lot of people, but it's _waaaay_ out of date. It's now more of a historical artifact of what React development looked like in 2015.\n\n# React Redux Universal Hot Example\n\n[![build status](https://img.shields.io/travis/erikras/react-redux-universal-hot-example/master.svg?style=flat-square)](https://travis-ci.org/erikras/react-redux-universal-hot-example)\n[![Dependency Status](https://david-dm.org/erikras/react-redux-universal-hot-example.svg?style=flat-square)](https://david-dm.org/erikras/react-redux-universal-hot-example)\n[![devDependency Status](https://david-dm.org/erikras/react-redux-universal-hot-example/dev-status.svg?style=flat-square)](https://david-dm.org/erikras/react-redux-universal-hot-example#info=devDependencies)\n[![react-redux-universal channel on discord](https://img.shields.io/badge/discord-react--redux--universal%40reactiflux-brightgreen.svg?style=flat-square)](https://discord.gg/0ZcbPKXt5bZZb1Ko)\n[![Demo on Heroku](https://img.shields.io/badge/demo-heroku-brightgreen.svg?style=flat-square)](https://react-redux.herokuapp.com)\n[![PayPal donate button](https://img.shields.io/badge/donate-paypal-brightgreen.svg?style=flat-square)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=E2LK57ZQ9YRMN)\n\n---\n\n## About\n\nThis is a starter boilerplate app I've put together using the following technologies:\n\n* ~~Isomorphic~~ [Universal](https://medium.com/@mjackson/universal-javascript-4761051b7ae9) rendering\n* Both client and server make calls to load data from separate API server\n* [React](https://github.com/facebook/react)\n* [React Router](https://github.com/rackt/react-router)\n* [Express](http://expressjs.com)\n* [Babel](http://babeljs.io) for ES6 and ES7 magic\n* [Webpack](http://webpack.github.io) for bundling\n* [Webpack Dev Middleware](http://webpack.github.io/docs/webpack-dev-middleware.html)\n* [Webpack Hot Middleware](https://github.com/glenjamin/webpack-hot-middleware)\n* [Redux](https://github.com/rackt/redux)'s futuristic [Flux](https://facebook.github.io/react/blog/2014/05/06/flux.html) implementation\n* [Redux Dev Tools](https://github.com/gaearon/redux-devtools) for next generation DX (developer experience). Watch [Dan Abramov's talk](https://www.youtube.com/watch?v=xsSnOQynTHs).\n* [React Router Redux](https://github.com/reactjs/react-router-redux) Redux/React Router bindings.\n* [ESLint](http://eslint.org) to maintain a consistent code style\n* [redux-form](https://github.com/erikras/redux-form) to manage form state in Redux\n* [lru-memoize](https://github.com/erikras/lru-memoize) to speed up form validation\n* [multireducer](https://github.com/erikras/multireducer) to combine single reducers into one key-based reducer\n* [style-loader](https://github.com/webpack/style-loader), [sass-loader](https://github.com/jtangelder/sass-loader) and [less-loader](https://github.com/webpack/less-loader) to allow import of stylesheets in plain css, sass and less,\n* [bootstrap-sass-loader](https://github.com/shakacode/bootstrap-sass-loader) and [font-awesome-webpack](https://github.com/gowravshekar/font-awesome-webpack) to customize Bootstrap and FontAwesome\n* [react-helmet](https://github.com/nfl/react-helmet) to manage title and meta tag information on both server and client\n* [webpack-isomorphic-tools](https://github.com/halt-hammerzeit/webpack-isomorphic-tools) to allow require() work for statics both on client and server\n* [mocha](https://mochajs.org/) to allow writing unit tests for the project.\n\nI cobbled this together from a wide variety of similar \"starter\" repositories. As I post this in June 2015, all of these libraries are right at the bleeding edge of web development. They may fall out of fashion as quickly as they have come into it, but I personally believe that this stack is the future of web development and will survive for several years. I'm building my new projects like this, and I recommend that you do, too.\n\n## Installation\n\n```bash\nnpm install\n```\n\n## Running Dev Server\n\n```bash\nnpm run dev\n```\n\nThe first time it may take a little while to generate the first `webpack-assets.json` and complain with a few dozen `[webpack-isomorphic-tools] (waiting for the first Webpack build to finish)` printouts, but be patient. Give it 30 seconds.\n\n### Using Redux DevTools\n\n[Redux Devtools](https://github.com/gaearon/redux-devtools) are enabled by default in development.\n\n- <kbd>CTRL</kbd>+<kbd>H</kbd> Toggle DevTools Dock\n- <kbd>CTRL</kbd>+<kbd>Q</kbd> Move DevTools Dock Position\n- see [redux-devtools-dock-monitor](https://github.com/gaearon/redux-devtools-dock-monitor) for more detailed information.\n\nIf you have the \n[Redux DevTools chrome extension](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd) installed it will automatically be used on the client-side instead.\n\nIf you want to disable the dev tools during development, set `__DEVTOOLS__` to `false` in `/webpack/dev.config.js`.  \nDevTools are not enabled during production.\n\n## Building and Running Production Server\n\n```bash\nnpm run build\nnpm run start\n```\n\n## Demo\n\nA demonstration of this app can be seen [running on heroku](https://react-redux.herokuapp.com), which is a deployment of the [heroku branch](https://github.com/erikras/react-redux-universal-hot-example/tree/heroku).\n\n## Documentation\n\n* [Exploring the Demo App](docs/ExploringTheDemoApp/ExploringTheDemoApp.md) is a guide that can be used before you install the kit.\n* [Installing the Kit](docs/InstallingTheKit/InstallingTheKit.md) guides you through installation and running the development server locally.\n* [Adding Text to the Home Page](docs/AddingToHomePage/AddingToHomePage.md) guides you through adding \"Hello, World!\" to the home page.\n* [Adding A Page](docs/AddingAPage/AddingAPage.md) guides you through adding a new page.\n* [React Tutorial - Converting Reflux to Redux](http://engineering.wework.com/process/2015/10/01/react-reflux-to-redux/), by Matt Star\n   If you are the kind of person that learns best by following along a tutorial, I can recommend Matt Star's overview and examples.\n\n\n## Explanation\n\nWhat initially gets run is `bin/server.js`, which does little more than enable ES6 and ES7 awesomeness in the\nserver-side node code. It then initiates `server.js`. In `server.js` we proxy any requests to `/api/*` to the\n[API server](#api-server), running at `localhost:3030`. All the data fetching calls from the client go to `/api/*`.\nAside from serving the favicon and static content from `/static`, the only thing `server.js` does is initiate delegate\nrendering to `react-router`. At the bottom of `server.js`, we listen to port `3000` and initiate the API server.\n\n#### Routing and HTML return\n\nThe primary section of `server.js` generates an HTML page with the contents returned by `react-router`. First we instantiate an `ApiClient`, a facade that both server and client code use to talk to the API server. On the server side, `ApiClient` is given the request object so that it can pass along the session cookie to the API server to maintain session state. We pass this API client facade to the `redux` middleware so that the action creators have access to it.\n\nThen we perform [server-side data fetching](#server-side-data-fetching), wait for the data to be loaded, and render the page with the now-fully-loaded `redux` state.\n\nThe last interesting bit of the main routing section of `server.js` is that we swap in the hashed script and css from the `webpack-assets.json` that the Webpack Dev Server – or the Webpack build process on production – has spit out on its last run. You won't have to deal with `webpack-assets.json` manually because [webpack-isomorphic-tools](https://github.com/halt-hammerzeit/webpack-isomorphic-tools) take care of that.\n\nWe also spit out the `redux` state into a global `window.__data` variable in the webpage to be loaded by the client-side `redux` code.\n\n#### Server-side Data Fetching\n\nThe [redux-async-connect](https://www.npmjs.com/package/redux-async-connect) package exposes an API to return promises that need to be fulfilled before a route is rendered. It exposes a `<ReduxAsyncConnect />` container, which wraps our render tree on both [server](https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/server.js) and [client](https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/client.js). More documentation is available on the [redux-async-connect](https://www.npmjs.com/package/redux-async-connect) page.\n\n#### Client Side\n\nThe client side entry point is reasonably named `client.js`. All it does is load the routes, initiate `react-router`, rehydrate the redux state from the `window.__data` passed in from the server, and render the page over top of the server-rendered DOM. This makes React enable all its event listeners without having to re-render the DOM.\n\n#### Redux Middleware\n\nThe middleware, [`clientMiddleware.js`](https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/redux/middleware/clientMiddleware.js), serves two functions:\n\n1. To allow the action creators access to the client API facade. Remember this is the same on both the client and the server, and cannot simply be `import`ed because it holds the cookie needed to maintain session on server-to-server requests.\n2. To allow some actions to pass a \"promise generator\", a function that takes the API client and returns a promise. Such actions require three action types, the `REQUEST` action that initiates the data loading, and a `SUCCESS` and `FAILURE` action that will be fired depending on the result of the promise. There are other ways to accomplish this, some discussed [here](https://github.com/rackt/redux/issues/99), which you may prefer, but to the author of this example, the middleware way feels cleanest.\n\n#### Redux Modules... *What the Duck*?\n\nThe `src/redux/modules` folder contains \"modules\" to help\nisolate concerns within a Redux application (aka [Ducks](https://github.com/erikras/ducks-modular-redux), a Redux Style Proposal that I came up with). I encourage you to read the\n[Ducks Docs](https://github.com/erikras/ducks-modular-redux) and provide feedback.\n\n#### API Server\n\nThis is where the meat of your server-side application goes. It doesn't have to be implemented in Node or Express at all. This is where you connect to your database and provide authentication and session management. In this example, it's just spitting out some json with the current time stamp.\n\n#### Getting data and actions into components\n\nTo understand how the data and action bindings get into the components – there's only one, `InfoBar`, in this example – I'm going to refer to you to the [Redux](https://github.com/gaearon/redux) library. The only innovation I've made is to package the component and its wrapper in the same js file. This is to encapsulate the fact that the component is bound to the `redux` actions and state. The component using `InfoBar` needn't know or care if `InfoBar` uses the `redux` data or not.\n\n#### Images\n\nNow it's possible to render the image both on client and server. Please refer to issue [#39](https://github.com/erikras/react-redux-universal-hot-example/issues/39) for more detail discussion, the usage would be like below (super easy):\n\n```javascript\nlet logoImage = require('./logo.png');\n```\n\n#### Styles\n\nThis project uses [local styles](https://medium.com/seek-ui-engineering/the-end-of-global-css-90d2a4a06284) using [css-loader](https://github.com/webpack/css-loader). The way it works is that you import your stylesheet at the top of the `render()` function in your React Component, and then you use the classnames returned from that import. Like so:\n\n```javascript\nrender() {\nconst styles = require('./App.scss');\n...\n```\n\nThen you set the `className` of your element to match one of the CSS classes in your SCSS file, and you're good to go!\n\n```jsx\n<div className={styles.mySection}> ... </div>\n```\n\n#### Alternative to Local Styles\n\nIf you'd like to use plain inline styles this is possible with a few modifications to your webpack configuration.\n\n**1. Configure Isomorphic Tools to Accept CSS**\n\nIn `webpack-isomorphic-tools.js` add **css** to the list of style module extensions\n\n```javascript\n    style_modules: {\n      extensions: ['less','scss','css'],\n```\n\n**2. Add a CSS loader to webpack dev config**\n\nIn `dev.config.js` modify **module loaders** to include a test and loader for css\n\n```javascript\n  module: {\n    loaders: [\n      { test: /\\.css$/, loader: 'style-loader!css-loader'},\n```\n\n**3. Add a CSS loader to the webpack prod config**\n\nYou must use the **ExtractTextPlugin** in this loader. In `prod.config.js` modify **module loaders** to include a test and loader for css\n\n```javascript\n  module: {\n    loaders: [\n      { test: /\\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader')},\n```\n\n**Now you may simply omit assigning the `required` stylesheet to a variable and keep it at the top of your `render()` function.**\n\n```javascript\nrender() {\nrequire('./App.css');\nrequire('aModule/dist/style.css');\n...\n```\n\n**NOTE** In order to use this method with **scss or less** files one more modification must be made. In both `dev.config.js` and `prod.config.js` in the loaders for less and scss files remove \n\n1. `modules`\n2. `localIdentName...`\n\nBefore:\n```javascript\n{ test: /\\.less$/, loader: 'style!css?modules&importLoaders=2&sourceMap&localIdentName=[local]___[hash:base64:5]!autoprefixer?browsers=last 2 version!less?outputStyle=expanded&sourceMap' },\n```\nAfter:\n```javascript\n{ test: /\\.less$/, loader: 'style!css?importLoaders=2&sourceMap!autoprefixer?browsers=last 2 version!less?outputStyle=expanded&sourceMap' },\n```\n\nAfter this modification to both loaders you will be able to use scss and less files in the same way as css files.\n\n#### Unit Tests\n\nThe project uses [Mocha](https://mochajs.org/) to run your unit tests, it uses [Karma](http://karma-runner.github.io/0.13/index.html) as the test runner, it enables the feature that you are able to render your tests to the browser (e.g: Firefox, Chrome etc.), which means you are able to use the [Test Utilities](http://facebook.github.io/react/docs/test-utils.html) from Facebook api like `renderIntoDocument()`.\n\nTo run the tests in the project, just simply run `npm test` if you have `Chrome` installed, it will be automatically launched as a test service for you.\n\nTo keep watching your test suites that you are working on, just set `singleRun: false` in the `karma.conf.js` file. Please be sure set it to `true` if you are running `npm test` on a continuous integration server (travis-ci, etc).\n\n## Deployment on Heroku\n\nTo get this project to work on Heroku, you need to:\n\n1. Remove the `\"PORT\": 8080` line from the `betterScripts` / `start-prod` section of `package.json`.\n2. `heroku config:set NODE_ENV=production`\n3. `heroku config:set NODE_PATH=./src`\n4. `heroku config:set NPM_CONFIG_PRODUCTION=false`\n  * This is to enable webpack to run the build on deploy.\n\nThe first deploy might take a while, but after that your `node_modules` dir should be cached.\n\n## FAQ\n\nThis project moves fast and has an active community, so if you have a question that is not answered below please visit our [Discord channel](https://discord.gg/0ZcbPKXt5bZZb1Ko) or file an issue.\n\n\n## Roadmap \n\nAlthough this isn't a library, we recently started versioning to make it easier to track breaking changes and emerging best practices. \n\n* [Inline Styles](docs/InlineStyles.md) - CSS is dead\n\n## Contributing\n\nI am more than happy to accept external contributions to the project in the form of feedback, bug reports and even better - pull requests :) \n\nIf you would like to submit a pull request, please make an effort to follow the guide in [CONTRIBUTING.md](CONTRIBUTING.md). \n \n---\nThanks for checking this out.\n\n– Erik Rasmussen, [@erikras](https://twitter.com/erikras)\n"
  },
  {
    "path": "api/__tests__/api-test.js",
    "content": "import {expect} from 'chai';\nimport {mapUrl} from '../utils/url';\n\ndescribe('mapUrl', () => {\n  it('extracts nothing if both params are undefined', () => {\n    expect(mapUrl(undefined, undefined)).to.deep.equal({\n      action: null,\n      params: []\n    });\n  });\n\n  it('extracts nothing if the url is empty', () => {\n    const url = '';\n    const splittedUrlPath = url.split('?')[0].split('/').slice(1);\n    const availableActions = {a: 1, widget: {c: 1, load: () => 'baz'}};\n\n    expect(mapUrl(availableActions, splittedUrlPath)).to.deep.equal({\n      action: null,\n      params: []\n    });\n  });\n\n  it('extracts nothing if nothing was found', () => {\n    const url = '/widget/load/?foo=bar';\n    const splittedUrlPath = url.split('?')[0].split('/').slice(1);\n    const availableActions = {a: 1, info: {c: 1, load: () => 'baz'}};\n\n    expect(mapUrl(availableActions, splittedUrlPath)).to.deep.equal({\n      action: null,\n      params: []\n    });\n  });\n\n  it('extracts the available actions and the params from an relative url string with GET params', () => {\n    const url = '/widget/load/param1/xzy?foo=bar';\n    const splittedUrlPath = url.split('?')[0].split('/').slice(1);\n    const availableActions = {a: 1, widget: {c: 1, load: () => 'baz'}};\n\n    expect(mapUrl(availableActions, splittedUrlPath)).to.deep.equal({\n      action: availableActions.widget.load,\n      params: ['param1', 'xzy']\n    });\n  });\n\n  it('extracts the available actions from an url string without GET params', () => {\n    const url = '/widget/load/?foo=bar';\n    const splittedUrlPath = url.split('?')[0].split('/').slice(1);\n    const availableActions = {a: 1, widget: {c: 1, load: () => 'baz'}};\n\n    expect(mapUrl(availableActions, splittedUrlPath)).to.deep.equal({\n      action: availableActions.widget.load,\n      params: ['']\n    });\n  });\n\n  it('does not find the available action if deeper nesting is required', () => {\n    const url = '/widget';\n    const splittedUrlPath = url.split('?')[0].split('/').slice(1);\n    const availableActions = {a: 1, widget: {c: 1, load: () => 'baz'}};\n\n    expect(mapUrl(availableActions, splittedUrlPath)).to.deep.equal({\n      action: null,\n      params: []\n    });\n  });\n});\n"
  },
  {
    "path": "api/actions/__tests__/loadInfo-test.js",
    "content": "import {expect} from 'chai';\nimport loadInfo from '../loadInfo';\nimport timekeeper from 'timekeeper';\n\ndescribe('loadInfo', () => {\n  it('loads the current date', () => {\n    const now = Date.now();\n    timekeeper.freeze(now);\n\n    return loadInfo().then(data => {\n      expect(data).to.deep.equal({time: now, message: 'This came from the api server'});\n    });\n  });\n});\n"
  },
  {
    "path": "api/actions/__tests__/widget-load-test.js",
    "content": "import {expect} from 'chai';\nimport load from '../widget/load';\nimport sinon from 'sinon';\n\ndescribe('widget load', () => {\n  afterEach(()=> {\n    if ('restore' in Math.random) {\n      Math.random.restore(); // reset the Math.random fixture\n    }\n  });\n\n  describe('successful', () => {\n    beforeEach(()=> {\n      sinon.stub(Math, 'random').returns(0.4);\n    });\n\n    it('uses the widgets from the session', () => {\n      return load({session: {widgets: ['a', 'b', 'c']}}).then(widgets => {\n        expect(widgets.length).to.equal(3);\n      });\n    });\n\n    it('initializes the widgets ', () => {\n      return load({session: {}}).then(widgets => {\n        expect(widgets.length).to.equal(4);\n        expect(widgets[0].color).to.equal('Red');\n      });\n    });\n  });\n\n  describe('unsuccessful', () => {\n    beforeEach(()=> {\n      sinon.stub(Math, 'random').returns(0.2);\n    });\n\n    it('rejects the call', () => {\n      return load({session: {}}).\n      then(\n        ()=> {\n        },\n        (err)=> {\n          expect(err).to.equal('Widget load fails 33% of the time. You were unlucky.');\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "api/actions/__tests__/widget-update-test.js",
    "content": "import {expect} from 'chai';\nimport update from '../widget/update';\nimport * as load from '../widget/load';\nimport sinon from 'sinon';\n\ndescribe('widget update', () => {\n  afterEach(()=> {\n    if ('restore' in Math.random) {\n      Math.random.restore(); // reset the Math.random fixture\n    }\n  });\n\n  describe('randomly successful', () => {\n    const widgets = [{}, {id: 2, color: 'Red'}];\n\n    beforeEach(()=> {\n      sinon.stub(Math, 'random').returns(0.3);\n    });\n\n    afterEach(()=> {\n      if ('restore' in load.default) {\n        load.default.restore();\n      }\n    });\n\n    it('does not accept green widgets', () => {\n      sinon.stub(load, 'default').returns(new Promise((resolve) => {\n        resolve(widgets);\n      }));\n      return update({session: {}, body: {color: 'Green'}}).\n      then(\n        ()=> {\n        },\n        (err)=> {\n          expect(err.color).to.equal('We do not accept green widgets');\n        });\n    });\n\n    it('fails to load widgets', () => {\n      sinon.stub(load, 'default').returns(new Promise((resolve, reject) => {\n        reject('Widget fail to load.');\n      }));\n      return update({session: {}, body: {color: 'Blue'}}).\n      then(\n        ()=> {\n        },\n        (err)=> {\n          expect(err).to.equal('Widget fail to load.');\n        });\n    });\n\n    it('updates a widget', () => {\n      sinon.stub(load, 'default').returns(new Promise((resolve) => {\n        resolve(widgets);\n      }));\n      const widget = {id: 2, color: 'Blue'};\n      return update({session: {}, body: widget}).\n      then(\n        (res)=> {\n          expect(res).to.deep.equal(widget);\n          expect(widgets[1]).to.deep.equal(widget);\n        });\n    });\n  });\n\n  describe('randomly unsuccessful', () => {\n    beforeEach(()=> {\n      sinon.stub(Math, 'random').returns(0.1);\n    });\n\n    it('rejects the call in 20% of the time', () => {\n      return update().\n      then(\n        ()=> {\n        },\n        (err)=> {\n          expect(err).to.equal('Oh no! Widget save fails 20% of the time. Try again.');\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "api/actions/index.js",
    "content": "export loadInfo from './loadInfo';\nexport loadAuth from './loadAuth';\nexport login from './login';\nexport logout from './logout';\nexport * as widget from './widget/index';\nexport * as survey from './survey/index';\n"
  },
  {
    "path": "api/actions/loadAuth.js",
    "content": "export default function loadAuth(req) {\n  return Promise.resolve(req.session.user || null);\n}\n"
  },
  {
    "path": "api/actions/loadInfo.js",
    "content": "export default function loadInfo() {\n  return new Promise((resolve) => {\n    resolve({\n      message: 'This came from the api server',\n      time: Date.now()\n    });\n  });\n}\n"
  },
  {
    "path": "api/actions/login.js",
    "content": "export default function login(req) {\n  const user = {\n    name: req.body.name\n  };\n  req.session.user = user;\n  return Promise.resolve(user);\n}\n"
  },
  {
    "path": "api/actions/logout.js",
    "content": "export default function logout(req) {\n  return new Promise((resolve) => {\n    req.session.destroy(() => {\n      req.session = null;\n      return resolve(null);\n    });\n  });\n}\n"
  },
  {
    "path": "api/actions/survey/index.js",
    "content": "export isValid from './isValid';\n"
  },
  {
    "path": "api/actions/survey/isValid.js",
    "content": "export default function survey(req) {\n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      const errors = {};\n      let valid = true;\n      if (~['bobby@gmail.com', 'timmy@microsoft.com'].indexOf(req.body.email)) {\n        errors.email = 'Email address already used';\n        valid = false;\n      }\n      if (valid) {\n        resolve();\n      } else {\n        reject(errors);\n      }\n    }, 1000);\n  });\n}\n"
  },
  {
    "path": "api/actions/widget/index.js",
    "content": "export update from './update';\nexport load from './load';\n"
  },
  {
    "path": "api/actions/widget/load.js",
    "content": "const initialWidgets = [\n  {id: 1, color: 'Red', sprocketCount: 7, owner: 'John'},\n  {id: 2, color: 'Taupe', sprocketCount: 1, owner: 'George'},\n  {id: 3, color: 'Green', sprocketCount: 8, owner: 'Ringo'},\n  {id: 4, color: 'Blue', sprocketCount: 2, owner: 'Paul'}\n];\n\nexport function getWidgets(req) {\n  let widgets = req.session.widgets;\n  if (!widgets) {\n    widgets = initialWidgets;\n    req.session.widgets = widgets;\n  }\n  return widgets;\n}\n\nexport default function load(req) {\n  return new Promise((resolve, reject) => {\n    // make async call to database\n    setTimeout(() => {\n      if (Math.random() < 0.33) {\n        reject('Widget load fails 33% of the time. You were unlucky.');\n      } else {\n        resolve(getWidgets(req));\n      }\n    }, 1000); // simulate async load\n  });\n}\n"
  },
  {
    "path": "api/actions/widget/update.js",
    "content": "import load from './load';\n\nexport default function update(req) {\n  return new Promise((resolve, reject) => {\n    // write to database\n    setTimeout(() => {\n      if (Math.random() < 0.2) {\n        reject('Oh no! Widget save fails 20% of the time. Try again.');\n      } else {\n        load(req).then(data => {\n          const widgets = data;\n          const widget = req.body;\n          if (widget.color === 'Green') {\n            reject({\n              color: 'We do not accept green widgets' // example server-side validation error\n            });\n          }\n          if (widget.id) {\n            widgets[widget.id - 1] = widget;  // id is 1-based. please don't code like this in production! :-)\n            req.session.widgets = widgets;\n          }\n          resolve(widget);\n        }, err => {\n          reject(err);\n        });\n      }\n    }, 1500); // simulate async db write\n  });\n}\n"
  },
  {
    "path": "api/api.js",
    "content": "import express from 'express';\nimport session from 'express-session';\nimport bodyParser from 'body-parser';\nimport config from '../src/config';\nimport * as actions from './actions/index';\nimport {mapUrl} from 'utils/url.js';\nimport PrettyError from 'pretty-error';\nimport http from 'http';\nimport SocketIo from 'socket.io';\n\nconst pretty = new PrettyError();\nconst app = express();\n\nconst server = new http.Server(app);\n\nconst io = new SocketIo(server);\nio.path('/ws');\n\napp.use(session({\n  secret: 'react and redux rule!!!!',\n  resave: false,\n  saveUninitialized: false,\n  cookie: { maxAge: 60000 }\n}));\napp.use(bodyParser.json());\n\n\napp.use((req, res) => {\n  const splittedUrlPath = req.url.split('?')[0].split('/').slice(1);\n\n  const {action, params} = mapUrl(actions, splittedUrlPath);\n\n  if (action) {\n    action(req, params)\n      .then((result) => {\n        if (result instanceof Function) {\n          result(res);\n        } else {\n          res.json(result);\n        }\n      }, (reason) => {\n        if (reason && reason.redirect) {\n          res.redirect(reason.redirect);\n        } else {\n          console.error('API ERROR:', pretty.render(reason));\n          res.status(reason.status || 500).json(reason);\n        }\n      });\n  } else {\n    res.status(404).end('NOT FOUND');\n  }\n});\n\n\nconst bufferSize = 100;\nconst messageBuffer = new Array(bufferSize);\nlet messageIndex = 0;\n\nif (config.apiPort) {\n  const runnable = app.listen(config.apiPort, (err) => {\n    if (err) {\n      console.error(err);\n    }\n    console.info('----\\n==> 🌎  API is running on port %s', config.apiPort);\n    console.info('==> 💻  Send requests to http://%s:%s', config.apiHost, config.apiPort);\n  });\n\n  io.on('connection', (socket) => {\n    socket.emit('news', {msg: `'Hello World!' from server`});\n\n    socket.on('history', () => {\n      for (let index = 0; index < bufferSize; index++) {\n        const msgNo = (messageIndex + index) % bufferSize;\n        const msg = messageBuffer[msgNo];\n        if (msg) {\n          socket.emit('msg', msg);\n        }\n      }\n    });\n\n    socket.on('msg', (data) => {\n      data.id = messageIndex;\n      messageBuffer[messageIndex % bufferSize] = data;\n      messageIndex++;\n      io.emit('msg', data);\n    });\n  });\n  io.listen(runnable);\n} else {\n  console.error('==>     ERROR: No PORT environment variable has been specified');\n}\n"
  },
  {
    "path": "api/utils/url.js",
    "content": "export function mapUrl(availableActions = {}, url = []) {\n  const notFound = {action: null, params: []};\n\n  // test for empty input\n  if (url.length === 0 || Object.keys(availableActions).length === 0) {\n    return notFound;\n  }\n  /*eslint-disable */\n  const reducer = (prev, current) => {\n    if (prev.action && prev.action[current]) {\n      return {action: prev.action[current], params: []}; // go deeper\n    } else {\n      if (typeof prev.action === 'function') {\n        return {action: prev.action, params: prev.params.concat(current)}; // params are found\n      } else {\n        return notFound;\n      }\n    }\n  };\n  /*eslint-enable */\n\n  const actionAndParams = url.reduce(reducer, {action: availableActions, params: []});\n\n  return (typeof actionAndParams.action === 'function') ? actionAndParams : notFound;\n}\n"
  },
  {
    "path": "app.json",
    "content": "{\n  \"name\": \"react-redux-universal-hot-example\",\n  \"description\": \"Example of an isomorphic (universal) webapp using react redux and hot reloading\",\n  \"repository\": \"https://github.com/erikras/react-redux-universal-hot-example\",\n  \"logo\": \"http://node-js-sample.herokuapp.com/node.svg\",\n  \"keywords\": [\n    \"react\",\n    \"isomorphic\",\n    \"universal\",\n    \"webpack\",\n    \"express\",\n    \"hot reloading\",\n    \"react-hot-reloader\",\n    \"redux\",\n    \"starter\",\n    \"boilerplate\",\n    \"babel\"\n  ]\n}\n"
  },
  {
    "path": "bin/api.js",
    "content": "#!/usr/bin/env node\nif (process.env.NODE_ENV !== 'production') {\n  if (!require('piping')({\n    hook: true,\n    ignore: /(\\/\\.|~$|\\.json$)/i\n  })) {\n    return;\n  }\n}\nrequire('../server.babel'); // babel registration (runtime transpilation for node)\nrequire('../api/api');\n"
  },
  {
    "path": "bin/server.js",
    "content": "#!/usr/bin/env node\nrequire('../server.babel'); // babel registration (runtime transpilation for node)\nvar path = require('path');\nvar rootDir = path.resolve(__dirname, '..');\n/**\n * Define isomorphic constants.\n */\nglobal.__CLIENT__ = false;\nglobal.__SERVER__ = true;\nglobal.__DISABLE_SSR__ = false;  // <----- DISABLES SERVER SIDE RENDERING FOR ERROR DEBUGGING\nglobal.__DEVELOPMENT__ = process.env.NODE_ENV !== 'production';\n\nif (__DEVELOPMENT__) {\n  if (!require('piping')({\n      hook: true,\n      ignore: /(\\/\\.|~$|\\.json|\\.scss$)/i\n    })) {\n    return;\n  }\n}\n\n// https://github.com/halt-hammerzeit/webpack-isomorphic-tools\nvar WebpackIsomorphicTools = require('webpack-isomorphic-tools');\nglobal.webpackIsomorphicTools = new WebpackIsomorphicTools(require('../webpack/webpack-isomorphic-tools'))\n  .development(__DEVELOPMENT__)\n  .server(rootDir, function() {\n    require('../src/server');\n  });\n"
  },
  {
    "path": "circle.yml",
    "content": "machine:\n  node:\n    version: 4.0\n  environment:\n    CONTINUOUS_INTEGRATION: true\n\ndependencies:\n  cache_directories:\n    - node_modules\n  override:\n    - npm prune && npm install\n\ntest:\n  override:\n    - npm run lint\n    - npm test\n    - npm run test-node\n"
  },
  {
    "path": "docs/AddingAPage/AddingAPage.md",
    "content": "# Adding A Hello Page\n\nThis guide adds a `/hello` page to the sample application by \nfollowing the existing outline.\n\n## Using Ack on About\n\nSearching strings is one way to [grok](https://en.wikipedia.org/wiki/Grok) the structure\nof the kit and sample application.   You can use *grep* or [ack](http://beyondgrep.com) (`brew install ack`).\nI use *ack* with this alias:\n\n![ick Alias](ick_alias.png)\n\nLooking with `ick about` and ignoring documentation, the word *about* appears in these files:\n\n![ick for About](ick_about.png)\n\n## Add the Hello page container\n\nA new page requires new page renderer.  Copy the About page to a \nnew directory and trim out almost all of it:\n\n*  `cd ./src/containers && mkdir ./Hello` because each container goes in its own \n    directory by convention.\n*  `cp About/About.js Hello/Hello.js`\n\nEdit `Hello/Hello.js` into this file:\n\n![New Hello.js](new_hello.png)\n\n\n\n## Edit three files to add Hello\n\n#### Add to `./src/containers/index.js` to include and export the React component:\n\n![Edit index.js](edit_index.png)\n\n#### Add to `./routes.js` to connect the `/hello` url path to the component:\n\n![Edit routes.js 1](edit_route1.png)\n![Edit routes.js 2](edit_route2.png)\n\n#### Add to `./src/containers/App/App.js` to add \"Hello\" to the NavBar\n\n![Edit App.js](edit_app.png)\n\nAnd voila, the new 'Hello' page:\n\n![Show Hello](show_hello.png)\n\n# Take-away:  Notice the trade-offs\n\nThe task of adding a new page exemplifies two trade-offs in the kit:\n**code versus convention** and the **cut and paste** style.\n\nConvention is a set of constraining rules that automatically trigger\nroutine configuration tasks.  For example, WebPack automatically picked up the \nnew directory `./src/containers/Hello` without adding to any configuration files.\n\nOn the other hand, routine code was added to `./src/containers/index.js` and \n`./src/routes.js` to handle the new page.  A convention could automatically\naccomplish the same tasks at either compile or run time.  The cost is new \nconstraints, such as requiring `Hello/Hello.js` to be renamed\n`HelloPage/HelloPage.js`.\n\nFollowing a style in the code that has no automatic effects is just organic\ngrowth, not convention.  For example, developers reading `./src/containers/index.js`\nmust stop and figure out why all subdirectories except `DevTools` are exported.\n(`DevTools`)[`./src/containers/DevTools/DevTools.js`](https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/containers/DevTools/DevTools.js)\ncontains a single function which should be\n[randomly](https://github.com/erikras/react-redux-universal-hot-example/issues/808)\nmoved to `./src/utils` or `./src/helpers`.  Using a convention rule that all \ncontainers must contain an exported React component would raise an error.\nOrganic growth leads to disorder in a project.\n\nSimilarly, the **cut and paste** style of coding also degrades the project.\nFor example, In `App.js`, the new *NavItem* tag included a new value for the\n*eventkey* property.  The *eventkey*  property is\n[poorly](https://github.com/react-bootstrap/react-bootstrap/issues/320)\n[understood](https://github.com/react-bootstrap/react-bootstrap/issues/432).\nAll *eventkey* fields in `App.js` are unused and can be removed.  The \n**cut and paste** style just compounds an\n[old error](https://github.com/erikras/react-redux-universal-hot-example/commit/d67a79c1e7da5367dc8922019ca726e69d56bf0e)\nand reinforces confusion.\n\n![Edit App revisted](edit_app2.png)\n\nThe use of the **cut and paste** style raises well known issues in\nmaintenance, documentation, and code quality.  It is not for use in\nproduction code.  \n\nSome choices about trade-offs are easier than others.\n"
  },
  {
    "path": "docs/AddingToHomePage/AddingToHomePage.md",
    "content": "# Adding Hello, World as static text\n\nPrinting *Hello, World!* is a traditional task.  This guides you through adding the text \"Hello, World!\" to the\nhome page of the sample application.\n\n## Find the home page\n\nFirst, find the correct file to change by walking through the kit's directory tree:\n\n![Finding The Home Page 1](find_home1.png)\n\n\n![Finding The Home Page 2](find_home2.png)\n\n![Finding The Home Page 3](find_home3.png)\n\n![Finding The Home Page 4](find_home4.png)\n\nSo, the likely file is `src/containers/Home/Home.js`.\n\n## Start the server and open the browser\n\nExecute `npm run dev` and open http://localhost:3000:\n\n*  `./package.json`, using [concurrently](https://www.npmjs.com/package/concurrently)\nand [better-npm-run](https://www.npmjs.com/package/better-npm-run), runs \n `./webpack/webpack-dev-server.js` on port 3001; runs `./bin/server.js` for HTTP on port 3000; \n and runs `./bin/api.js` for the REST API on port 3030.\n  \n* `./bin/server.js` calls `./src/server.js` and uses the [HMR plugin](http://andrewhfarmer.com/webpack-hmr-tutorial/)\nfor hot reloading, meaning the browser refreshes automatically when any file in `./src` is changed.\n\n* `./webpack/webpack-dev-server` does teh actual compilation with the \n[webpack dev middleware package](https://github.com/webpack/webpack-dev-middleware) to provide a key feature found\nin Glup:  compilation without writing intermediate files to disk.  Configuring webpack\n[can be confusing](https://medium.com/@dtothefp/why-can-t-anyone-write-a-simple-webpack-tutorial-d0b075db35ed#.cle1vv5ql).\n\n* `./bin/api.js` calls `./api/api.js`.  It receives incoming REST requests as JSON objects and responds with\nother JSON objects.   \n\n## Change the text\n\nAdd the static text to (`src/containers/Home/Home.js`):\n\n![Add Hello Header to Home](add_home.png)\n\n\nWhen you save the file to disk, the change to the `./src` directory is picked up by the \n[piping](https://www.npmjs.com/package/piping) module, triggering the webpack-dev-server to rebuild \n`./static/dist/[checksum].js`, and triggering a stub injected into the HTML file served to the browser to \nreload.   The rebuilding processes through webpack middleware and plugins that compile `*.sccs` files, \ntranspile JAX and ES6 (or ES7), and bundles together all the resources into one package in about 6 seconds.\nThat is, the browser will show \"Hello, World!\" on your web page in about 6 seconds:\n\n![Hello World rendered on home page](hello_rendered.png)\n\n## Conclusion\n\nYou added **Hello, World!**.   The process is [as clear as is the summer's sun](https://www.youtube.com/watch?v=EhGiSfv5FJk&t=3m23s).\n\n"
  },
  {
    "path": "docs/ApiConfig.md",
    "content": "# Switching to a real API\n\nChances are, once you get comfortable with this setup, you'll want to hook into some existing API rather than use the simple one that comes with this project. Here's how:\n\n## Update `package.json`\n\nFirst things first, you need to add `APIHOST` settings in `package.json`. If you look in `src/config.js`, you'll see that it's already configured to read this `APIHOST` setting if it's present.\n\nIf the port you use differs between your dev & prod API hosts, you may want to get rid of the `APIPORT` setting, including it right in `APIHOST`. Same with the protocol – if you use HTTP in dev but HTTPS in prod, you may want to include the protocol right in `APIHOST`, and then get rid of the explicit `\"http://\"` found in the next section.\n\n## Update `ApiClient`\n\nOpen up `src/helpers/ApiClient.js`. You'll see this line:\n\n``` javascript\n   if (__SERVER__) {\n     // Prepend host and port of the API server to the path.\n    return 'http://' + config.apiHost + adjustedPath;\n   }\n```\n\nIf you added `http://` or `https://` to your APIHOST setting, then you need to remove it here.\n\nIn this file, you'll also see that there's a `/api` that gets prepended to the URL when on the client side. That gets routed through a proxy that's configured in server.js, which we'll get to next.\n\nWhy do you need a proxy? So that the `APIHOST` can be set as part of the Node environment, and your client side code can still work. A user's browser doesn't have access to your server's Node environment, so instead the client-side code makes all API calls to this `/api` proxy, which the server configures to hit your real API. That way you can control everything sanely, through environment variables, and set different API endpoints for your different environments.\n\n## Update `server.js`\n\nTo update the proxy, find this chunk in `src/server.js`:\n\n``` javascript\n  const proxy = httpProxy.createProxyServer({\n    target: 'http://' + config.apiHost,\n    ws: true\n  });\n```\n\nYou'll want to change this in a few ways:\n\n### 1. Update `target` protocol\n\nIf you changed APIHOST to include the `http://` or `https://` protocol, then get rid of the `'http://' +` in the `target` setting. Note that you'll need to restart your server after making these changes or things will break.\n\n### 2. Decide if you need WebSockets\n\nThe `ws: true` setting is there to support WebSockets connections, which this demo app supports using [socket.io](http://socket.io/). If your API doesn't use WebSockets, then you can remove this line.\n\n### 3. Add a `changeOrigin` setting\n\nThis might be the most important part! It's possible that your API lives at a totally different URL than your front-end app. If that's the case, then you need to add a `changeOrigin` setting. \n\nHere's an example of what the final chunk of code might look like:\n\n``` javascript\n  const proxy = httpProxy.createProxyServer({\n    target: config.apiHost,\n    changeOrigin: true\n  });\n```\n\nFinally, after doing all of that, you can get rid of the demo API.\n\n## Get rid of stuff\n\nYou can remove the whole `api` folder, as well as `bin/api.js`.\n\nOnce you do that, you'll also need to remove the lines in `package.json` that called those things. Remove all this:\n\n* ` \\\"npm run start-prod-api\\\"` from the `start` script command\n* ` \\\"npm run start-dev-api\\\"` from the `dev` script command\n* the `start-prod-api` and `start-dev-api` scripts altogether\n* the ` api` argument from the `lint` script\n* the `test-node` and `test-node-watch` scripts, which were there to test the demo API\n* the `start-prod-api` and `start-dev-api` settings in the `betterScripts` section\n\nIf you want, you can also remove all references to `socket`, if you're not using it.\n"
  },
  {
    "path": "docs/Ducks.md",
    "content": "This document has found [another, hopefully permanent, home](https://github.com/erikras/ducks-modular-redux).\n\nQuack.\n"
  },
  {
    "path": "docs/ExploringTheDemoApp/ExploringTheDemoApp.md",
    "content": "# Guide - Exploring the Demo App\n\nThis guide covers your first look and can be used even before installing software.  \n\n## Overview\n\nThis project is a kit for developing interactive applications in JavaScript centered\naround React and Redux.  Like all JavaScript kits, it includes a large number of configured\nmodules and a sample, or Demo App, from which to start your application.  This guide walks \nthrough that Demo App to show some features and code.\n\n### Open the demo in your browser\n\nThe project hosts a running demo on Heroku, a hosting company.  Open \n[https://react-redux.herokuapp.com/](https://react-redux.herokuapp.com/) in your browser to see this page:\n\n![Screenshot](frontpage.png)\n\nMuch of the text is cut-and-paste from the project's \n[README.md](https://github.com/erikras/react-redux-universal-hot-example/blob/master/README.md) file into \nthe code for this page [./src/containers/Home/Home.js](https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/containers/Home/Home.js).  \n\nThe text provides a one line overview of about twenty of the main \nmodules of hundreds shown during installation.   The selection \nand configuration of all these modules is the value of using a kit.   When you run across\na module you have never heard of and want to get a quick overview, the fastest way is\nto google for 'slideshare theModuleName' and skim someone's presentation.\n\nThe page is rendered from HTML including React components coded as custom HTML tags.\nThe components use properties to alter appearance and sets of data.  Notice some of the components on the page:\n\n![Screenshot with Annotations](frontpage_markup.png)\n\n### Explore the Widgets Page\n\nClick on *Widgets* link on the top of the screen.   You come to a page with some arbitrary widgets and more logic\nin the form.  Notice how much state affects the display and formatting of buttons:\n\n![Screenshot with Annotations](widgets_markup.png)\n\n### Explore the Survey Page\n\nClick on the *Survey* link.  Following the programming style of this kit, the code for this page is \nspread over a [Survey container][scont], a [SurveyForm component][scomp], mentioned in the \n[container][conlist] and [component][complist] lists, \nmentioned in the [routes][routes] function and the navigation of the main [App][app].  The code also uses\nvarious libraries for React, Redux, validation, memoize and other functions.   Learn to use [ack](http://beyondgrep.com) \nor the project wide search built into your editor.\n\nTry clicking on the 'Initialize Form' button and then hitting Submit.  You will see just an error under\n'Little Bobby Tables'.  Now click in the email field then the name field.   You now see errors in both\nthe name and the email.  Even with a good kit, forms can be difficult to code.\n\n![Screenshot with Annotations](survey_markup.png)\n[scont]: https://github.com/erikras/react-redux-universal-hot-example/tree/master/src/containers/Survey\n[scomp]: https://github.com/erikras/react-redux-universal-hot-example/tree/master/src/components/SurveyForm\n[conlist]: https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/containers/index.js\n[complist]: https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/components/index.js\n[routes]: https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/routes.js\n[app]: https://github.com/erikras/react-redux-universal-hot-example/tree/master/src/containers/App/App.js\n\n### Explore the About Page\n\nClick on the *About* link.   The source for this page \n[./src/containers/About/About.js](https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/containers/About/About.js)\nuses a casual mix of HTML, ECMA7 JavaScript, and React components.   This translates into \nsimple JavaScript code for the browser.   Notice how the local state `showKitten` being false causes no\n`div` or `img` tag in the output.\n\n![Screenshot with Annotations](about_markup.png)\n\n### Explore the Login Page\n\nFinally, click on the *Login* page and explore.   Looking at the styling for this page \n[./src/containers/Login/Login.scss]](https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/containers/Login/Login.scss)\nwill show an example of how the using styling files litters the code base with extra files.   \nConsider the alternative of using \n[Inline Styles](https://github.com/erikras/react-redux-universal-hot-example/blob/master/docs/InlineStyles.md).\n\n# The Take Away\n\nLooking through each page of the DemoApp will lead you to more questions which will keep you \nsearching and learning and you will slowly master this technology.\n\nHere are some additional quests you could undertake:\n\n* How do the About page MiniBar and the status bar share data about the time last loaded?\n* How would you add a fourth counter that incremented by two?   How many files would you need\n  to touch?\n* What order are calls made when you click \"Reload Widgets\" on the widgets page?\n* Why does surveyValidation use memoize?\n\nInstall, hack, explore!\n\n*All guides are works in progress, and pull requests are always welcome.  If you make an\naccepted pull request and live in Silicon Valley, I'll treat you to coffee.  -- Charles*\n\n\n"
  },
  {
    "path": "docs/InlineStyles.md",
    "content": "# Inline Styles\n\nIn the long term, CSS, LESS and SASS are dead. To keep this project on the bleeding edge, we should drop SASS support in favor of inline styles.\n\n## Why?\n\nI think the case is made pretty strongly in these three presentations.\n\nChristopher Chedeau | Michael Chan | Colin Megill\n--- | --- | ---\n[![CSS In Your JS by Christopher Chedeau](https://i.vimeocdn.com/video/502495328_295x166.jpg)](http://blog.vjeux.com/2014/javascript/react-css-in-js-nationjs.html) | [![Michael Chan - Inline Styles: themes, media queries, contexts, & when it's best to use CSS](https://i.ytimg.com/vi/ERB1TJBn32c/mqdefault.jpg)](https://www.youtube.com/watch?v=ERB1TJBn32c) | [![Colin Megill - Inline Styles are About to Kill CSS](https://i.ytimg.com/vi/NoaxsCi13yQ/mqdefault.jpg)](https://www.youtube.com/watch?v=NoaxsCi13yQ)\n\nClearly this is the direction in which web development is moving.\n\n## Why not?\n\nAt the moment, all the inline CSS libraries suffer from some or all of these problems:\n\n* Client side only\n* No vendor auto prefixing (requires `User-Agent` checking on server side)\n* No server side media queries, resulting in a flicker on load to adjust to client device width\n\nIdeally, a library would allow for all the benefits of inline calculable styles, but, in production, would allow some generation of a CSS block, with media queries to handle device width conditionals, to be inserted into the page with a `<style>` tag, and then each element that was using a style that was dependent on a media query would have a class name generated.\n\n## Contenders\n\nThis is a list of possible style libraries that we could implement into this project.\n\n* [Radium](https://github.com/FormidableLabs/radium)\n* [React-JSS](https://github.com/jsstyles/react-jss)\n* [jsxstyle](https://github.com/petehunt/jsxstyle)\n* [css-modules](https://github.com/css-modules/css-modules)\n* [babel-plugin-react-autoprefix](https://github.com/UXtemple/babel-plugin-react-autoprefix)\n* [babel-plugin-css-in-js](https://github.com/martinandert/babel-plugin-css-in-js)\n* _Add more if you know of others._\n\nUsers and contributors to this project should periodically go through this list and see if any of them have developed features that make them really worthy of server side rendering in a production environment.\n\n## Additional Reading\n\n* [How do we make “styles in components” play nicely with server-side rendering?](https://medium.com/@jedwatson/how-do-we-make-styles-in-components-play-nicely-with-server-side-rendering-25de9ecb1b49)\n"
  },
  {
    "path": "docs/InstallingTheKit/InstallingTheKit.md",
    "content": "# Installing the Kit\n\nAre you ready to install the kit and start playing with it?  No?  Good.  Let's begin.\n\nThere is no failure except when nothing is learned.\n\n##  Clone from github\n\nFirst you should clone the project from GitHub.  The easiest way is to use something like this:\n\n![Cloning from GitHub](git_clone.png)\n\nThis puts a copy of the whole project into your directory `mycopy`.   All the files you will change are\nin this directory.   You can `rm -rf mycopy`, clone it again, and continue on.   \n\nThat's the minimum view of git, equivalent to the [xkcd view](https://xkcd.com/1597/).   Git and \n[github](https://github.com) make every manipulation and automated workflow possible but none easy.\nNothing more complex than creating new copy is necessary until contributing code to a project.\n\n\n## Run npm install\n\nOnly part of the project is stored in github.  All the JavaScript libraries upon which the project depends \nare imported using npm, the node package manager.  It might be a good time to check that your version of `npm` is\nup to date; OS/X users type `brew upgrade && brew update`.  \n\nNpm installs libraries according to the [semvar](https://docs.npmjs.com/getting-started/semantic-versioning) \nminimum versions in the `dependencies` section of `package.json` file:\n\n![The Dependencies](dependencies.png)\n\nWe also install the packages listed in the `devDependencies` section,  \n[because we are installing from source](http://stackoverflow.com/questions/18875674/whats-the-difference-between-dependencies-devdependencies-and-peerdependencies).\nEvery package also has its own `package.json` file containing more dependencies.  Multiple versions \nof the same packages may be listed as dependencies causing copies to be installed in package subdirectories\n`./node_modules/*/node_modules`, `./node_modules/*/node_modules/*/node_modules`, etc.  This accumulates \nto over 1,000 packages.\n\n### Let's install a thousand packages:\n\n![Start of install](start_npm.png)\n\nAnd then you should see pages and pages of output.  Some packages suggest installing \"globally\" or \"-g\"; don't. \nGlobal packages install into  `/usr/local/lib/node_modules` and mix links to binaries into `/usr/local/bin`.\nLocal packages install into `./node_modules` with the links to binaries separate in `./node_modules/.bin`.\nThis provides you the to option of doing a `rm -rf node_modules && npm install` to get back to a known state.\n\nThis installation step fails some days; the kit juggles many moving parts and these packages are independently \ndeveloped.  Some packages have [peer dependencies](https://nodejs.org/en/blog/npm/peer-dependencies/)\nwhich end up conflicting with one another.   You can check the \n[open install issues](https://github.com/erikras/react-redux-universal-hot-example/issues?utf8=✓&q=is%3Aissue+is%3Aopen+install),\nrun `npm install` again, or try `npm outdated && npm update`.   Errors in installation are often not caught by \nrunning `npm run test`.  The only way to know it works is to run it.\n\n## Run the Development Server\n\nFirst, find the command. \n\n![List of scripts](npm_run.png)\n\nNow, run it!\n\n![Run dev, part 1](run_dev1.png)\n\nWe run:\n\n*  A *watch client* to trigger webpack to rebuild if we change code.\n*  A *restful api server* listening on port 3030 to handle requests in JSON format.\n*  A *webpack dev server* which serves your application on port 3000.  It also grabs port 3001\n   for status and internal information, such as [polling middleware](http://localhost:3001/__webpack_hmr).\n   You could try installing [BrowserSync](https://www.browsersync.io) to see something more interesting.\n   \nThe second part shows these running:\n\n![Run dev, part 2](run_dev2.png)\n\nYou can see WebPack rebuilding static assets into `./webpack-assets.json`.  If you check the id, you\ncan also view it on port 3001, http://localhost:3001/dist/main-b6c55eaa1c8d8efc7190.js in this example.\n\nNow, open up your browser to [port 3000](http://localhost:3000/):\n\n![Port 3000](port3000.png)\n\nThe Redux developer bar takes up much of the screen.  Hide it with 'control-H'.\n\nYou are now running the kit on your local machine.\n\n\n# The Take Away\n\nYou cloned the repository, installed all the packages, and ran the development server locally.  \nYou are now ready to hack on the application.\n"
  },
  {
    "path": "karma.conf.js",
    "content": "var webpack = require('webpack');\n\nmodule.exports = function (config) {\n  config.set({\n\n    browsers: ['PhantomJS'],\n\n    singleRun: !!process.env.CI,\n\n    frameworks: [ 'mocha' ],\n\n    files: [\n      './node_modules/phantomjs-polyfill/bind-polyfill.js',\n      'tests.webpack.js'\n    ],\n\n    preprocessors: {\n      'tests.webpack.js': [ 'webpack', 'sourcemap' ]\n    },\n\n    reporters: [ 'mocha' ],\n\n    plugins: [\n      require(\"karma-webpack\"),\n      require(\"karma-mocha\"),\n      require(\"karma-mocha-reporter\"),\n      require(\"karma-phantomjs-launcher\"),\n      require(\"karma-sourcemap-loader\")\n    ],\n\n    webpack: {\n      devtool: 'inline-source-map',\n      module: {\n        loaders: [\n          { test: /\\.(jpe?g|png|gif|svg)$/, loader: 'url', query: {limit: 10240} },\n          { test: /\\.js$/, exclude: /node_modules/, loaders: ['babel']},\n          { test: /\\.json$/, loader: 'json-loader' },\n          { test: /\\.less$/, loader: 'style!css!less' },\n          { test: /\\.scss$/, loader: 'style!css?modules&importLoaders=2&sourceMap&localIdentName=[local]___[hash:base64:5]!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded&sourceMap' }\n        ]\n      },\n      resolve: {\n        modulesDirectories: [\n          'src',\n          'node_modules'\n        ],\n        extensions: ['', '.json', '.js']\n      },\n      plugins: [\n        new webpack.IgnorePlugin(/\\.json$/),\n        new webpack.NoErrorsPlugin(),\n        new webpack.DefinePlugin({\n          __CLIENT__: true,\n          __SERVER__: false,\n          __DEVELOPMENT__: true,\n          __DEVTOOLS__: false  // <-------- DISABLE redux-devtools HERE\n        })\n      ]\n    },\n\n    webpackServer: {\n      noInfo: true\n    }\n\n  });\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-redux-universal-hot-example\",\n  \"description\": \"Example of an isomorphic (universal) webapp using react redux and hot reloading\",\n  \"author\": \"Erik Rasmussen <rasmussenerik@gmail.com> (http://github.com/erikras)\",\n  \"license\": \"MIT\",\n  \"version\": \"0.9.0\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/erikras/react-redux-universal-hot-example\"\n  },\n  \"homepage\": \"https://github.com/erikras/react-redux-universal-hot-example\",\n  \"keywords\": [\n    \"react\",\n    \"isomorphic\",\n    \"universal\",\n    \"webpack\",\n    \"express\",\n    \"hot reloading\",\n    \"react-hot-reloader\",\n    \"redux\",\n    \"starter\",\n    \"boilerplate\",\n    \"babel\"\n  ],\n  \"main\": \"bin/server.js\",\n  \"scripts\": {\n    \"start\": \"concurrent --kill-others \\\"npm run start-prod\\\" \\\"npm run start-prod-api\\\"\",\n    \"start-prod\": \"better-npm-run start-prod\",\n    \"start-prod-api\": \"better-npm-run start-prod-api\",\n    \"build\": \"better-npm-run build\",\n    \"postinstall\": \"npm run build\",\n    \"lint\": \"eslint -c .eslintrc src api\",\n    \"start-dev\": \"better-npm-run start-dev\",\n    \"start-dev-api\": \"better-npm-run start-dev-api\",\n    \"watch-client\": \"better-npm-run watch-client\",\n    \"dev\": \"concurrent --kill-others \\\"npm run watch-client\\\" \\\"npm run start-dev\\\" \\\"npm run start-dev-api\\\"\",\n    \"test\": \"karma start\",\n    \"test-node\": \"./node_modules/mocha/bin/mocha $(find api -name '*-test.js') --compilers js:babel-core/register\",\n    \"test-node-watch\": \"./node_modules/mocha/bin/mocha $(find api -name '*-test.js') --compilers js:babel-core/register --watch\"\n  },\n  \"betterScripts\": {\n    \"start-prod\": {\n      \"command\": \"node ./bin/server.js\",\n      \"env\": {\n        \"NODE_PATH\": \"./src\",\n        \"NODE_ENV\": \"production\",\n        \"PORT\": 8080,\n        \"APIPORT\": 3030\n      }\n    },\n    \"start-prod-api\": {\n      \"command\": \"node ./bin/api.js\",\n      \"env\": {\n        \"NODE_PATH\": \"./api\",\n        \"NODE_ENV\": \"production\",\n        \"APIPORT\": 3030\n      }\n    },\n    \"start-dev\": {\n      \"command\": \"node ./bin/server.js\",\n      \"env\": {\n        \"NODE_PATH\": \"./src\",\n        \"NODE_ENV\": \"development\",\n        \"PORT\": 3000,\n        \"APIPORT\": 3030\n      }\n    },\n    \"start-dev-api\": {\n      \"command\": \"node ./bin/api.js\",\n      \"env\": {\n        \"NODE_PATH\": \"./api\",\n        \"NODE_ENV\": \"development\",\n        \"APIPORT\": 3030\n      }\n    },\n    \"watch-client\": {\n      \"command\": \"node webpack/webpack-dev-server.js\",\n      \"env\": {\n        \"UV_THREADPOOL_SIZE\": 100,\n        \"NODE_PATH\": \"./src\",\n        \"PORT\": 3000,\n        \"APIPORT\": 3030\n      }\n    },\n    \"build\": {\n      \"command\": \"webpack --verbose --colors --display-error-details --config webpack/prod.config.js\",\n      \"env\": {\n        \"NODE_ENV\": \"production\"\n      }\n    }\n  },\n  \"dependencies\": {\n    \"babel-core\": \"^6.5.2\",\n    \"babel-loader\": \"^6.2.1\",\n    \"babel-plugin-add-module-exports\": \"^0.1.2\",\n    \"babel-plugin-transform-decorators-legacy\": \"^1.3.4\",\n    \"babel-plugin-transform-react-display-name\": \"^6.3.13\",\n    \"babel-plugin-transform-runtime\": \"^6.3.13\",\n    \"babel-polyfill\": \"^6.3.14\",\n    \"babel-preset-es2015\": \"^6.3.13\",\n    \"babel-preset-react\": \"^6.3.13\",\n    \"babel-preset-stage-0\": \"^6.3.13\",\n    \"babel-register\": \"^6.3.13\",\n    \"babel-runtime\": \"^6.3.19\",\n    \"body-parser\": \"^1.14.1\",\n    \"compression\": \"^1.6.0\",\n    \"express\": \"^4.13.3\",\n    \"express-session\": \"^1.12.1\",\n    \"file-loader\": \"^0.8.5\",\n    \"hoist-non-react-statics\": \"^1.0.3\",\n    \"http-proxy\": \"^1.12.0\",\n    \"immutable\": \"^3.8.1\",\n    \"invariant\": \"^2.2.0\",\n    \"less\": \"^2.5.3\",\n    \"less-loader\": \"^2.2.1\",\n    \"lru-memoize\": \"^1.0.0\",\n    \"map-props\": \"^1.0.0\",\n    \"multireducer\": \"^2.0.0\",\n    \"piping\": \"^0.3.0\",\n    \"pretty-error\": \"^1.2.0\",\n    \"react\": \"0.14.8\",\n    \"react-bootstrap\": \"^0.28.1\",\n    \"react-dom\": \"0.14.8\",\n    \"react-helmet\": \"^2.2.0\",\n    \"react-inline-css\": \"^2.0.0\",\n    \"react-redux\": \"^4.0.0\",\n    \"react-router\": \"2.0.0\",\n    \"react-router-bootstrap\": \"^0.20.1\",\n    \"react-router-redux\": \"^4.0.0\",\n    \"redux\": \"^3.0.4\",\n    \"redux-async-connect\": \"^1.0.0-rc2\",\n    \"redux-form\": \"^3.0.12\",\n    \"redux-thunk\": \"^2.1.0\",\n    \"scroll-behavior\": \"^0.3.2\",\n    \"serialize-javascript\": \"^1.1.2\",\n    \"serve-favicon\": \"^2.3.0\",\n    \"socket.io\": \"^1.3.7\",\n    \"socket.io-client\": \"^1.3.7\",\n    \"superagent\": \"^1.4.0\",\n    \"url-loader\": \"^0.5.7\",\n    \"violet-paginator\": \"^1.8.1\",\n    \"warning\": \"^2.1.0\",\n    \"webpack-isomorphic-tools\": \"^2.2.18\"\n  },\n  \"devDependencies\": {\n    \"autoprefixer-loader\": \"^3.1.0\",\n    \"babel-eslint\": \"^5.0.0-beta6\",\n    \"babel-plugin-react-transform\": \"^2.0.0\",\n    \"babel-plugin-typecheck\": \"^3.6.0\",\n    \"better-npm-run\": \"0.0.8\",\n    \"bootstrap-sass\": \"^3.3.5\",\n    \"bootstrap-sass-loader\": \"^1.0.9\",\n    \"chai\": \"^3.3.0\",\n    \"clean-webpack-plugin\": \"^0.1.6\",\n    \"concurrently\": \"^0.1.1\",\n    \"css-loader\": \"^0.23.1\",\n    \"eslint\": \"1.10.3\",\n    \"eslint-config-airbnb\": \"0.1.0\",\n    \"eslint-loader\": \"^1.0.0\",\n    \"eslint-plugin-import\": \"^0.8.0\",\n    \"eslint-plugin-react\": \"^3.5.0\",\n    \"extract-text-webpack-plugin\": \"^0.9.1\",\n    \"font-awesome\": \"^4.4.0\",\n    \"font-awesome-webpack\": \"0.0.4\",\n    \"json-loader\": \"^0.5.4\",\n    \"karma\": \"^0.13.10\",\n    \"karma-cli\": \"^0.1.1\",\n    \"karma-mocha\": \"^0.2.0\",\n    \"karma-mocha-reporter\": \"^1.1.1\",\n    \"karma-phantomjs-launcher\": \"^0.2.1\",\n    \"karma-sourcemap-loader\": \"^0.3.5\",\n    \"karma-webpack\": \"^1.7.0\",\n    \"mocha\": \"^2.3.3\",\n    \"node-sass\": \"^3.4.2\",\n    \"phantomjs\": \"^1.9.18\",\n    \"phantomjs-polyfill\": \"0.0.1\",\n    \"react-a11y\": \"^0.2.6\",\n    \"react-addons-test-utils\": \"0.14.8\",\n    \"react-transform-catch-errors\": \"^1.0.0\",\n    \"react-transform-hmr\": \"^1.0.1\",\n    \"redbox-react\": \"^1.1.1\",\n    \"redux-devtools\": \"^3.0.0-beta-3\",\n    \"redux-devtools-dock-monitor\": \"^1.0.0-beta-3\",\n    \"redux-devtools-log-monitor\": \"^1.0.0-beta-3\",\n    \"sass-loader\": \"^3.1.2\",\n    \"sinon\": \"^1.17.2\",\n    \"strip-loader\": \"^0.1.0\",\n    \"style-loader\": \"^0.13.0\",\n    \"timekeeper\": \"0.0.5\",\n    \"webpack\": \"^1.12.9\",\n    \"webpack-dev-middleware\": \"^1.4.0\",\n    \"webpack-hot-middleware\": \"^2.5.0\"\n  },\n  \"engines\": {\n    \"node\": \"5.6.0\"\n  }\n}\n"
  },
  {
    "path": "server.babel.js",
    "content": "//  enable runtime transpilation to use ES6/7 in node\n\nvar fs = require('fs');\n\nvar babelrc = fs.readFileSync('./.babelrc');\nvar config;\n\ntry {\n  config = JSON.parse(babelrc);\n} catch (err) {\n  console.error('==>     ERROR: Error parsing your .babelrc.');\n  console.error(err);\n}\n\nrequire('babel-register')(config);\n"
  },
  {
    "path": "src/client.js",
    "content": "/**\n * THIS IS THE ENTRY POINT FOR THE CLIENT, JUST LIKE server.js IS THE ENTRY POINT FOR THE SERVER.\n */\nimport 'babel-polyfill';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport createStore from './redux/create';\nimport ApiClient from './helpers/ApiClient';\nimport io from 'socket.io-client';\nimport {Provider} from 'react-redux';\nimport { Router, browserHistory } from 'react-router';\nimport { syncHistoryWithStore } from 'react-router-redux';\nimport { ReduxAsyncConnect } from 'redux-async-connect';\nimport useScroll from 'scroll-behavior/lib/useStandardScroll';\n\nimport getRoutes from './routes';\n\nconst client = new ApiClient();\nconst _browserHistory = useScroll(() => browserHistory)();\nconst dest = document.getElementById('content');\nconst store = createStore(_browserHistory, client, window.__data);\nconst history = syncHistoryWithStore(_browserHistory, store);\n\nfunction initSocket() {\n  const socket = io('', {path: '/ws'});\n  socket.on('news', (data) => {\n    console.log(data);\n    socket.emit('my other event', { my: 'data from client' });\n  });\n  socket.on('msg', (data) => {\n    console.log(data);\n  });\n\n  return socket;\n}\n\nglobal.socket = initSocket();\n\nconst component = (\n  <Router render={(props) =>\n        <ReduxAsyncConnect {...props} helpers={{client}} filter={item => !item.deferred} />\n      } history={history}>\n    {getRoutes(store)}\n  </Router>\n);\n\nReactDOM.render(\n  <Provider store={store} key=\"provider\">\n    {component}\n  </Provider>,\n  dest\n);\n\nif (process.env.NODE_ENV !== 'production') {\n  window.React = React; // enable debugger\n\n  if (!dest || !dest.firstChild || !dest.firstChild.attributes || !dest.firstChild.attributes['data-react-checksum']) {\n    console.error('Server-side React render was discarded. Make sure that your initial render does not contain any client-side code.');\n  }\n}\n\nif (__DEVTOOLS__ && !window.devToolsExtension) {\n  const DevTools = require('./containers/DevTools/DevTools');\n  ReactDOM.render(\n    <Provider store={store} key=\"provider\">\n      <div>\n        {component}\n        <DevTools />\n      </div>\n    </Provider>,\n    dest\n  );\n}\n"
  },
  {
    "path": "src/components/CounterButton/CounterButton.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport {connectMultireducer} from 'multireducer';\nimport {increment} from 'redux/modules/counter';\n\n@connectMultireducer(\n  (key, state) => ({count: state.multireducer[key].count}),\n  {increment}\n)\nexport default class CounterButton extends Component {\n  static propTypes = {\n    count: PropTypes.number,\n    increment: PropTypes.func.isRequired,\n    className: PropTypes.string\n  }\n\n  props = {\n    className: ''\n  }\n\n  render() {\n    const {count, increment} = this.props; // eslint-disable-line no-shadow\n    let {className} = this.props;\n    className += ' btn btn-default';\n    return (\n      <button className={className} onClick={increment}>\n        You have clicked me {count} time{count === 1 ? '' : 's'}.\n      </button>\n    );\n  }\n}\n\n"
  },
  {
    "path": "src/components/GithubButton/GithubButton.js",
    "content": "import React from 'react';\n\nconst GithubButton = (props) => {\n  const {user, repo, type, width, height, count, large} = props;\n  let src = `https://ghbtns.com/github-btn.html?user=${user}&repo=${repo}&type=${type}`;\n  if (count) src += '&count=true';\n  if (large) src += '&size=large';\n\n  return (\n    <iframe\n      src={src}\n      frameBorder=\"0\"\n      allowTransparency=\"true\"\n      scrolling=\"0\"\n      width={width}\n      height={height}\n      style={{border: 'none', width: width, height: height}}></iframe>\n  );\n};\n\nGithubButton.propTypes = {\n  user: React.PropTypes.string.isRequired,\n  repo: React.PropTypes.string.isRequired,\n  type: React.PropTypes.oneOf(['star', 'watch', 'fork', 'follow']).isRequired,\n  width: React.PropTypes.number.isRequired,\n  height: React.PropTypes.number.isRequired,\n  count: React.PropTypes.bool,\n  large: React.PropTypes.bool\n};\n\nexport default GithubButton;\n"
  },
  {
    "path": "src/components/InfoBar/InfoBar.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport {bindActionCreators} from 'redux';\nimport {connect} from 'react-redux';\nimport {load} from 'redux/modules/info';\n\n@connect(\n    state => ({info: state.info.data}),\n    dispatch => bindActionCreators({load}, dispatch))\nexport default class InfoBar extends Component {\n  static propTypes = {\n    info: PropTypes.object,\n    load: PropTypes.func.isRequired\n  }\n\n  render() {\n    const {info, load} = this.props; // eslint-disable-line no-shadow\n    const styles = require('./InfoBar.scss');\n    return (\n      <div className={styles.infoBar + ' well'}>\n        <div className=\"container\">\n          This is an info bar\n          {' '}\n          <strong>{info ? info.message : 'no info!'}</strong>\n          <span className={styles.time}>{info && new Date(info.time).toString()}</span>\n          <button className=\"btn btn-primary\" onClick={load}>Reload from server</button>\n        </div>\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "src/components/InfoBar/InfoBar.scss",
    "content": ".infoBar {\n  font-variant: italics;\n}\n\n.time {\n  margin: 0 30px;\n}\n"
  },
  {
    "path": "src/components/MiniInfoBar/MiniInfoBar.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport {connect} from 'react-redux';\n\n@connect(state => ({ time: state.info.data.time }))\nexport default class MiniInfoBar extends Component {\n  static propTypes = {\n    time: PropTypes.number\n  }\n\n  render() {\n    const {time} = this.props;\n    return (\n      <div className=\"mini-info-bar\">\n        The info bar was last loaded at\n        {' '}\n        <span>{time && new Date(time).toString()}</span>\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "src/components/SurveyForm/SurveyForm.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport {reduxForm} from 'redux-form';\nimport {connect} from 'react-redux';\nimport {bindActionCreators} from 'redux';\nimport surveyValidation from './surveyValidation';\nimport * as surveyActions from 'redux/modules/survey';\n\nfunction asyncValidate(data, dispatch, {isValidEmail}) {\n  if (!data.email) {\n    return Promise.resolve({});\n  }\n  return isValidEmail(data);\n}\n@connect(() => ({}),\n  dispatch => bindActionCreators(surveyActions, dispatch)\n)\n@reduxForm({\n  form: 'survey',\n  fields: ['name', 'email', 'occupation', 'currentlyEmployed', 'sex'],\n  validate: surveyValidation,\n  asyncValidate,\n  asyncBlurFields: ['email']\n})\nexport default\nclass SurveyForm extends Component {\n  static propTypes = {\n    active: PropTypes.string,\n    asyncValidating: PropTypes.bool.isRequired,\n    fields: PropTypes.object.isRequired,\n    dirty: PropTypes.bool.isRequired,\n    handleSubmit: PropTypes.func.isRequired,\n    resetForm: PropTypes.func.isRequired,\n    invalid: PropTypes.bool.isRequired,\n    pristine: PropTypes.bool.isRequired,\n    valid: PropTypes.bool.isRequired\n  }\n\n  render() {\n    const {\n      asyncValidating,\n      dirty,\n      fields: {name, email, occupation, currentlyEmployed, sex},\n      active,\n      handleSubmit,\n      invalid,\n      resetForm,\n      pristine,\n      valid\n      } = this.props;\n    const styles = require('./SurveyForm.scss');\n    const renderInput = (field, label, showAsyncValidating) =>\n      <div className={'form-group' + (field.error && field.touched ? ' has-error' : '')}>\n        <label htmlFor={field.name} className=\"col-sm-2\">{label}</label>\n        <div className={'col-sm-8 ' + styles.inputGroup}>\n          {showAsyncValidating && asyncValidating && <i className={'fa fa-cog fa-spin ' + styles.cog}/>}\n          <input type=\"text\" className=\"form-control\" id={field.name} {...field}/>\n          {field.error && field.touched && <div className=\"text-danger\">{field.error}</div>}\n          <div className={styles.flags}>\n            {field.dirty && <span className={styles.dirty} title=\"Dirty\">D</span>}\n            {field.active && <span className={styles.active} title=\"Active\">A</span>}\n            {field.visited && <span className={styles.visited} title=\"Visited\">V</span>}\n            {field.touched && <span className={styles.touched} title=\"Touched\">T</span>}\n          </div>\n        </div>\n      </div>;\n\n    return (\n      <div>\n        <form className=\"form-horizontal\" onSubmit={handleSubmit}>\n          {renderInput(name, 'Full Name')}\n          {renderInput(email, 'Email', true)}\n          {renderInput(occupation, 'Occupation')}\n          <div className=\"form-group\">\n            <label htmlFor=\"currentlyEmployed\" className=\"col-sm-2\">Currently Employed?</label>\n            <div className=\"col-sm-8\">\n              <input type=\"checkbox\" id=\"currentlyEmployed\" {...currentlyEmployed}/>\n            </div>\n          </div>\n          <div className=\"form-group\">\n            <label className=\"col-sm-2\">Sex</label>\n            <div className=\"col-sm-8\">\n              <input type=\"radio\" id=\"sex-male\" {...sex} value=\"male\" checked={sex.value === 'male'}/>\n              <label htmlFor=\"sex-male\" className={styles.radioLabel}>Male</label>\n              <input type=\"radio\" id=\"sex-female\" {...sex} value=\"female\" checked={sex.value === 'female'}/>\n              <label htmlFor=\"sex-female\" className={styles.radioLabel}>Female</label>\n            </div>\n          </div>\n          <div className=\"form-group\">\n            <div className=\"col-sm-offset-2 col-sm-10\">\n              <button className=\"btn btn-success\" onClick={handleSubmit}>\n                <i className=\"fa fa-paper-plane\"/> Submit\n              </button>\n              <button className=\"btn btn-warning\" onClick={resetForm} style={{marginLeft: 15}}>\n                <i className=\"fa fa-undo\"/> Reset\n              </button>\n            </div>\n          </div>\n        </form>\n\n        <h4>Props from redux-form</h4>\n\n        <table className=\"table table-striped\">\n          <tbody>\n          <tr>\n            <th>Active Field</th>\n            <td>{active}</td>\n          </tr>\n          <tr>\n            <th>Dirty</th>\n            <td className={dirty ? 'success' : 'danger'}>{dirty ? 'true' : 'false'}</td>\n          </tr>\n          <tr>\n            <th>Pristine</th>\n            <td className={pristine ? 'success' : 'danger'}>{pristine ? 'true' : 'false'}</td>\n          </tr>\n          <tr>\n            <th>Valid</th>\n            <td className={valid ? 'success' : 'danger'}>{valid ? 'true' : 'false'}</td>\n          </tr>\n          <tr>\n            <th>Invalid</th>\n            <td className={invalid ? 'success' : 'danger'}>{invalid ? 'true' : 'false'}</td>\n          </tr>\n          </tbody>\n        </table>\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "src/components/SurveyForm/SurveyForm.scss",
    "content": ".inputGroup {\n  position: relative;\n}\n\n.flags {\n  position: absolute;\n  right: 20px;\n  top: 7px;\n  & > * {\n    margin: 0 2px;\n    width: 20px;\n    height: 20px;\n    border-radius: 20px;\n    box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.4);\n    color: white;\n    float: right;\n    text-align: center;\n  }\n  .active {\n    background: linear-gradient(#cc0, #aa0);\n    color: black;\n  }\n  .dirty {\n    background: linear-gradient(#090, #060);\n  }\n  .visited {\n    background: linear-gradient(#009, #006);\n  }\n  .touched {\n    background: linear-gradient(#099, #066);\n  }\n}\n\n.radioLabel {\n  margin: 0 25px 0 5px;\n}\n.cog {\n  position: absolute;\n  left: 0;\n  top: 10px;\n}\n"
  },
  {
    "path": "src/components/SurveyForm/surveyValidation.js",
    "content": "import memoize from 'lru-memoize';\nimport {createValidator, required, maxLength, email} from 'utils/validation';\n\nconst surveyValidation = createValidator({\n  name: [required, maxLength(10)],\n  email: [required, email],\n  occupation: maxLength(20) // single rules don't have to be in an array\n});\nexport default memoize(10)(surveyValidation);\n"
  },
  {
    "path": "src/components/WidgetForm/WidgetForm.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport {connect} from 'react-redux';\nimport {bindActionCreators} from 'redux';\nimport {reduxForm} from 'redux-form';\nimport widgetValidation, {colors} from './widgetValidation';\nimport * as widgetActions from 'redux/modules/widgets';\n\n@connect(\n  state => ({\n    saveError: state.widgets.saveError\n  }),\n  dispatch => bindActionCreators(widgetActions, dispatch)\n)\n@reduxForm({\n  form: 'widget',\n  fields: ['id', 'color', 'sprocketCount', 'owner'],\n  validate: widgetValidation\n})\nexport default class WidgetForm extends Component {\n  static propTypes = {\n    fields: PropTypes.object.isRequired,\n    editStop: PropTypes.func.isRequired,\n    handleSubmit: PropTypes.func.isRequired,\n    invalid: PropTypes.bool.isRequired,\n    pristine: PropTypes.bool.isRequired,\n    save: PropTypes.func.isRequired,\n    submitting: PropTypes.bool.isRequired,\n    saveError: PropTypes.object,\n    formKey: PropTypes.string.isRequired,\n    values: PropTypes.object.isRequired\n  };\n\n  render() {\n    const { editStop, fields: {id, color, sprocketCount, owner}, formKey, handleSubmit, invalid,\n      pristine, save, submitting, saveError: { [formKey]: saveError }, values } = this.props;\n    const styles = require('containers/Widgets/Widgets.scss');\n    return (\n      <tr className={submitting ? styles.saving : ''}>\n        <td className={styles.idCol}>{id.value}</td>\n        <td className={styles.colorCol}>\n          <select name=\"color\" className=\"form-control\" {...color}>\n            {colors.map(valueColor => <option value={valueColor} key={valueColor}>{valueColor}</option>)}\n          </select>\n          {color.error && color.touched && <div className=\"text-danger\">{color.error}</div>}\n        </td>\n        <td className={styles.sprocketsCol}>\n          <input type=\"text\" className=\"form-control\" {...sprocketCount}/>\n          {sprocketCount.error && sprocketCount.touched && <div className=\"text-danger\">{sprocketCount.error}</div>}\n        </td>\n        <td className={styles.ownerCol}>\n          <input type=\"text\" className=\"form-control\" {...owner}/>\n          {owner.error && owner.touched && <div className=\"text-danger\">{owner.error}</div>}\n        </td>\n        <td className={styles.buttonCol}>\n          <button className=\"btn btn-default\"\n                  onClick={() => editStop(formKey)}\n                  disabled={submitting}>\n            <i className=\"fa fa-ban\"/> Cancel\n          </button>\n          <button className=\"btn btn-success\"\n                  onClick={handleSubmit(() => save(values)\n                    .then(result => {\n                      if (result && typeof result.error === 'object') {\n                        return Promise.reject(result.error);\n                      }\n                    })\n                  )}\n                  disabled={pristine || invalid || submitting}>\n            <i className={'fa ' + (submitting ? 'fa-cog fa-spin' : 'fa-cloud')}/> Save\n          </button>\n          {saveError && <div className=\"text-danger\">{saveError}</div>}\n        </td>\n      </tr>\n    );\n  }\n}\n"
  },
  {
    "path": "src/components/WidgetForm/widgetValidation.js",
    "content": "import {createValidator, required, maxLength, integer, oneOf} from 'utils/validation';\n\nexport const colors = ['Blue', 'Fuchsia', 'Green', 'Orange', 'Red', 'Taupe'];\n\nconst widgetValidation = createValidator({\n  color: [required, oneOf(colors)],\n  sprocketCount: [required, integer],\n  owner: [required, maxLength(30)]\n});\nexport default widgetValidation;\n"
  },
  {
    "path": "src/components/__tests__/InfoBar-test.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport {renderIntoDocument} from 'react-addons-test-utils';\nimport { expect} from 'chai';\nimport { InfoBar } from 'components';\nimport { Provider } from 'react-redux';\nimport { browserHistory } from 'react-router';\nimport createStore from 'redux/create';\nimport ApiClient from 'helpers/ApiClient';\nconst client = new ApiClient();\n\ndescribe('InfoBar', () => {\n  const mockStore = {\n    info: {\n      load: () => {},\n      loaded: true,\n      loading: false,\n      data: {\n        message: 'This came from the api server',\n        time: Date.now()\n      }\n    }\n  };\n  const store = createStore(browserHistory, client, mockStore);\n  const renderer = renderIntoDocument(\n    <Provider store={store} key=\"provider\">\n      <InfoBar/>\n    </Provider>\n  );\n  const dom = ReactDOM.findDOMNode(renderer);\n\n  it('should render correctly', () => {\n    return expect(renderer).to.be.ok;\n  });\n\n  it('should render with correct value', () => {\n    const text = dom.getElementsByTagName('strong')[0].textContent;\n    expect(text).to.equal(mockStore.info.data.message);\n  });\n\n  it('should render with a reload button', () => {\n    const text = dom.getElementsByTagName('button')[0].textContent;\n    expect(text).to.be.a('string');\n  });\n\n  it('should render the correct className', () => {\n    const styles = require('components/InfoBar/InfoBar.scss');\n    expect(styles.infoBar).to.be.a('string');\n    expect(dom.className).to.include(styles.infoBar);\n  });\n});\n"
  },
  {
    "path": "src/components/index.js",
    "content": "/**\n *  Point of contact for component modules\n *\n *  ie: import { CounterButton, InfoBar } from 'components';\n *\n */\n\nexport CounterButton from './CounterButton/CounterButton';\nexport GithubButton from './GithubButton/GithubButton';\nexport InfoBar from './InfoBar/InfoBar';\nexport MiniInfoBar from './MiniInfoBar/MiniInfoBar';\nexport SurveyForm from './SurveyForm/SurveyForm';\nexport WidgetForm from './WidgetForm/WidgetForm';\n"
  },
  {
    "path": "src/config.js",
    "content": "require('babel-polyfill');\n\nconst environment = {\n  development: {\n    isProduction: false\n  },\n  production: {\n    isProduction: true\n  }\n}[process.env.NODE_ENV || 'development'];\n\nmodule.exports = Object.assign({\n  host: process.env.HOST || 'localhost',\n  port: process.env.PORT,\n  apiHost: process.env.APIHOST || 'localhost',\n  apiPort: process.env.APIPORT,\n  app: {\n    title: 'React Redux Example',\n    description: 'All the modern best practices in one example.',\n    head: {\n      titleTemplate: 'React Redux Example: %s',\n      meta: [\n        {name: 'description', content: 'All the modern best practices in one example.'},\n        {charset: 'utf-8'},\n        {property: 'og:site_name', content: 'React Redux Example'},\n        {property: 'og:image', content: 'https://react-redux.herokuapp.com/logo.jpg'},\n        {property: 'og:locale', content: 'en_US'},\n        {property: 'og:title', content: 'React Redux Example'},\n        {property: 'og:description', content: 'All the modern best practices in one example.'},\n        {property: 'og:card', content: 'summary'},\n        {property: 'og:site', content: '@erikras'},\n        {property: 'og:creator', content: '@erikras'},\n        {property: 'og:image:width', content: '200'},\n        {property: 'og:image:height', content: '200'}\n      ]\n    }\n  },\n\n}, environment);\n"
  },
  {
    "path": "src/containers/About/About.js",
    "content": "import React, {Component} from 'react';\nimport Helmet from 'react-helmet';\nimport { MiniInfoBar } from 'components';\n\nexport default class About extends Component {\n\n  state = {\n    showKitten: false\n  }\n\n  handleToggleKitten = () => this.setState({showKitten: !this.state.showKitten});\n\n  render() {\n    const {showKitten} = this.state;\n    const kitten = require('./kitten.jpg');\n    return (\n      <div className=\"container\">\n        <h1>About Us</h1>\n        <Helmet title=\"About Us\"/>\n\n        <p>This project was originally created by Erik Rasmussen\n          (<a href=\"https://twitter.com/erikras\" target=\"_blank\">@erikras</a>), but has since seen many contributions\n          from the open source community. Thank you to <a\n            href=\"https://github.com/erikras/react-redux-universal-hot-example/graphs/contributors\"\n            target=\"_blank\">all the contributors</a>.\n        </p>\n\n        <h3>Mini Bar <span style={{color: '#aaa'}}>(not that kind)</span></h3>\n\n        <p>Hey! You found the mini info bar! The following component is display-only. Note that it shows the same\n          time as the info bar.</p>\n\n        <MiniInfoBar/>\n\n        <h3>Images</h3>\n\n        <p>\n          Psst! Would you like to see a kitten?\n\n          <button className={'btn btn-' + (showKitten ? 'danger' : 'success')}\n                  style={{marginLeft: 50}}\n                  onClick={this.handleToggleKitten}>\n            {showKitten ? 'No! Take it away!' : 'Yes! Please!'}</button>\n        </p>\n\n        {showKitten && <div><img src={kitten}/></div>}\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "src/containers/App/App.js",
    "content": "import React, { Component, PropTypes } from 'react';\nimport { connect } from 'react-redux';\nimport { IndexLink } from 'react-router';\nimport { LinkContainer } from 'react-router-bootstrap';\nimport Navbar from 'react-bootstrap/lib/Navbar';\nimport Nav from 'react-bootstrap/lib/Nav';\nimport NavItem from 'react-bootstrap/lib/NavItem';\nimport Helmet from 'react-helmet';\nimport { isLoaded as isInfoLoaded, load as loadInfo } from 'redux/modules/info';\nimport { isLoaded as isAuthLoaded, load as loadAuth, logout } from 'redux/modules/auth';\nimport { InfoBar } from 'components';\nimport { push } from 'react-router-redux';\nimport config from '../../config';\nimport { asyncConnect } from 'redux-async-connect';\n\n@asyncConnect([{\n  promise: ({store: {dispatch, getState}}) => {\n    const promises = [];\n\n    if (!isInfoLoaded(getState())) {\n      promises.push(dispatch(loadInfo()));\n    }\n    if (!isAuthLoaded(getState())) {\n      promises.push(dispatch(loadAuth()));\n    }\n\n    return Promise.all(promises);\n  }\n}])\n@connect(\n  state => ({user: state.auth.user}),\n  {logout, pushState: push})\nexport default class App extends Component {\n  static propTypes = {\n    children: PropTypes.object.isRequired,\n    user: PropTypes.object,\n    logout: PropTypes.func.isRequired,\n    pushState: PropTypes.func.isRequired\n  };\n\n  static contextTypes = {\n    store: PropTypes.object.isRequired\n  };\n\n  componentWillReceiveProps(nextProps) {\n    if (!this.props.user && nextProps.user) {\n      // login\n      this.props.pushState('/loginSuccess');\n    } else if (this.props.user && !nextProps.user) {\n      // logout\n      this.props.pushState('/');\n    }\n  }\n\n  handleLogout = (event) => {\n    event.preventDefault();\n    this.props.logout();\n  };\n\n  render() {\n    const {user} = this.props;\n    const styles = require('./App.scss');\n\n    return (\n      <div className={styles.app}>\n        <Helmet {...config.app.head}/>\n        <Navbar fixedTop>\n          <Navbar.Header>\n            <Navbar.Brand>\n              <IndexLink to=\"/\" activeStyle={{color: '#33e0ff'}}>\n                <div className={styles.brand}/>\n                <span>{config.app.title}</span>\n              </IndexLink>\n            </Navbar.Brand>\n            <Navbar.Toggle/>\n          </Navbar.Header>\n\n          <Navbar.Collapse eventKey={0}>\n            <Nav navbar>\n              {user && <LinkContainer to=\"/chat\">\n                <NavItem eventKey={1}>Chat</NavItem>\n              </LinkContainer>}\n\n              <LinkContainer to=\"/widgets\">\n                <NavItem eventKey={2}>Widgets</NavItem>\n              </LinkContainer>\n              <LinkContainer to=\"/survey\">\n                <NavItem eventKey={3}>Survey</NavItem>\n              </LinkContainer>\n              <LinkContainer to=\"/pagination\">\n                <NavItem eventKey={4}>Pagination</NavItem>\n              </LinkContainer>\n              <LinkContainer to=\"/about\">\n                <NavItem eventKey={5}>About Us</NavItem>\n              </LinkContainer>\n\n              {!user &&\n              <LinkContainer to=\"/login\">\n                <NavItem eventKey={6}>Login</NavItem>\n              </LinkContainer>}\n              {user &&\n              <LinkContainer to=\"/logout\">\n                <NavItem eventKey={7} className=\"logout-link\" onClick={this.handleLogout}>\n                  Logout\n                </NavItem>\n              </LinkContainer>}\n            </Nav>\n            {user &&\n            <p className={styles.loggedInMessage + ' navbar-text'}>Logged in as <strong>{user.name}</strong>.</p>}\n            <Nav navbar pullRight>\n              <NavItem eventKey={1} target=\"_blank\" title=\"View on Github\" href=\"https://github.com/erikras/react-redux-universal-hot-example\">\n                <i className=\"fa fa-github\"/>\n              </NavItem>\n            </Nav>\n          </Navbar.Collapse>\n        </Navbar>\n\n        <div className={styles.appContent}>\n          {this.props.children}\n        </div>\n        <InfoBar/>\n\n        <div className=\"well text-center\">\n          Have questions? Ask for help <a\n          href=\"https://github.com/erikras/react-redux-universal-hot-example/issues\"\n          target=\"_blank\">on Github</a> or in the <a\n          href=\"https://discord.gg/0ZcbPKXt5bZZb1Ko\" target=\"_blank\">#react-redux-universal</a> Discord channel.\n        </div>\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "src/containers/App/App.scss",
    "content": ".app {\n  .brand {\n    position: absolute;\n    $size: 40px;\n    top: 5px;\n    left: 5px;\n    display: inline-block;\n    background: #2d2d2d url('../Home/logo.png') no-repeat center center;\n    width: $size;\n    height: $size;\n    background-size: 80%;\n    margin: 0 10px 0 0;\n    border-radius: $size / 2;\n  }\n  nav :global(.fa) {\n    font-size: 2em;\n    line-height: 20px;\n  }\n}\n.appContent {\n  margin: 50px 0; // for fixed navbar\n}\n"
  },
  {
    "path": "src/containers/Chat/Chat.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport {connect} from 'react-redux';\n\n@connect(\n  state => ({user: state.auth.user})\n)\nexport default class Chat extends Component {\n\n  static propTypes = {\n    user: PropTypes.object\n  };\n\n  state = {\n    message: '',\n    messages: []\n  };\n\n  componentDidMount() {\n    if (socket) {\n      socket.on('msg', this.onMessageReceived);\n      setTimeout(() => {\n        socket.emit('history', {offset: 0, length: 100});\n      }, 100);\n    }\n  }\n\n  componentWillUnmount() {\n    if (socket) {\n      socket.removeListener('msg', this.onMessageReceived);\n    }\n  }\n\n  onMessageReceived = (data) => {\n    const messages = this.state.messages;\n    messages.push(data);\n    this.setState({messages});\n  }\n\n  handleSubmit = (event) => {\n    event.preventDefault();\n\n    const msg = this.state.message;\n\n    this.setState({message: ''});\n\n    socket.emit('msg', {\n      from: this.props.user.name,\n      text: msg\n    });\n  }\n\n  render() {\n    const style = require('./Chat.scss');\n    const {user} = this.props;\n\n    return (\n      <div className={style.chat + ' container'}>\n        <h1 className={style}>Chat</h1>\n\n        {user &&\n        <div>\n          <ul>\n          {this.state.messages.map((msg) => {\n            return <li key={`chat.msg.${msg.id}`}>{msg.from}: {msg.text}</li>;\n          })}\n          </ul>\n          <form className=\"login-form\" onSubmit={this.handleSubmit}>\n            <input type=\"text\" ref=\"message\" placeholder=\"Enter your message\"\n             value={this.state.message}\n             onChange={(event) => {\n               this.setState({message: event.target.value});\n             }\n            }/>\n            <button className=\"btn\" onClick={this.handleSubmit}>Send</button>\n          </form>\n        </div>\n        }\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "src/containers/Chat/Chat.scss",
    "content": ".chat {\n  input {\n    padding: 5px 10px;\n    border-radius: 5px;\n    border: 1px solid #ccc;\n  }\n  form {\n    margin: 30px 0;\n    :global(.btn) {\n      margin-left: 10px;\n    }\n  }\n}"
  },
  {
    "path": "src/containers/DevTools/DevTools.js",
    "content": "import React from 'react';\nimport { createDevTools } from 'redux-devtools';\nimport LogMonitor from 'redux-devtools-log-monitor';\nimport DockMonitor from 'redux-devtools-dock-monitor';\n\nexport default createDevTools(\n  <DockMonitor toggleVisibilityKey=\"ctrl-H\"\n               changePositionKey=\"ctrl-Q\">\n    <LogMonitor />\n  </DockMonitor>\n);\n"
  },
  {
    "path": "src/containers/Home/Home.js",
    "content": "import React, { Component } from 'react';\nimport { Link } from 'react-router';\nimport { CounterButton, GithubButton } from 'components';\nimport config from '../../config';\nimport Helmet from 'react-helmet';\n\nexport default class Home extends Component {\n  render() {\n    const styles = require('./Home.scss');\n    // require the logo image both from client and server\n    const logoImage = require('./logo.png');\n    return (\n      <div className={styles.home}>\n        <Helmet title=\"Home\"/>\n        <div className={styles.masthead}>\n          <div className=\"container\">\n            <div className={styles.logo}>\n              <p>\n                <img src={logoImage}/>\n              </p>\n            </div>\n            <h1>{config.app.title}</h1>\n\n            <h2>{config.app.description}</h2>\n\n            <p>\n              <a className={styles.github} href=\"https://github.com/erikras/react-redux-universal-hot-example\"\n                 target=\"_blank\">\n                <i className=\"fa fa-github\"/> View on Github\n              </a>\n            </p>\n            <GithubButton user=\"erikras\"\n                          repo=\"react-redux-universal-hot-example\"\n                          type=\"star\"\n                          width={160}\n                          height={30}\n                          count large/>\n            <GithubButton user=\"erikras\"\n                          repo=\"react-redux-universal-hot-example\"\n                          type=\"fork\"\n                          width={160}\n                          height={30}\n                          count large/>\n\n            <p className={styles.humility}>\n              Created and maintained by <a href=\"https://twitter.com/erikras\" target=\"_blank\">@erikras</a>.\n            </p>\n          </div>\n        </div>\n\n        <div className=\"container\">\n          <div className={styles.counterContainer}>\n            <CounterButton multireducerKey=\"counter1\"/>\n            <CounterButton multireducerKey=\"counter2\"/>\n            <CounterButton multireducerKey=\"counter3\"/>\n          </div>\n\n          <p>This starter boilerplate app uses the following technologies:</p>\n\n          <ul>\n            <li>\n              <del>Isomorphic</del>\n              {' '}\n              <a href=\"https://medium.com/@mjackson/universal-javascript-4761051b7ae9\">Universal</a> rendering\n            </li>\n            <li>Both client and server make calls to load data from separate API server</li>\n            <li><a href=\"https://github.com/facebook/react\" target=\"_blank\">React</a></li>\n            <li><a href=\"https://github.com/rackt/react-router\" target=\"_blank\">React Router</a></li>\n            <li><a href=\"http://expressjs.com\" target=\"_blank\">Express</a></li>\n            <li><a href=\"http://babeljs.io\" target=\"_blank\">Babel</a> for ES6 and ES7 magic</li>\n            <li><a href=\"http://webpack.github.io\" target=\"_blank\">Webpack</a> for bundling</li>\n            <li><a href=\"http://webpack.github.io/docs/webpack-dev-middleware.html\" target=\"_blank\">Webpack Dev Middleware</a>\n            </li>\n            <li><a href=\"https://github.com/glenjamin/webpack-hot-middleware\" target=\"_blank\">Webpack Hot Middleware</a></li>\n            <li><a href=\"https://github.com/rackt/redux\" target=\"_blank\">Redux</a>'s futuristic <a\n              href=\"https://facebook.github.io/react/blog/2014/05/06/flux.html\" target=\"_blank\">Flux</a> implementation\n            </li>\n            <li><a href=\"https://github.com/gaearon/redux-devtools\" target=\"_blank\">Redux Dev Tools</a> for next\n              generation DX (developer experience).\n              Watch <a href=\"https://www.youtube.com/watch?v=xsSnOQynTHs\" target=\"_blank\">Dan Abramov's talk</a>.\n            </li>\n            <li><a href=\"https://github.com/rackt/redux-router\" target=\"_blank\">Redux Router</a> Keep\n              your router state in your Redux store\n            </li>\n            <li><a href=\"http://eslint.org\" target=\"_blank\">ESLint</a> to maintain a consistent code style</li>\n            <li><a href=\"https://github.com/erikras/redux-form\" target=\"_blank\">redux-form</a> to manage form state\n              in Redux\n            </li>\n            <li><a href=\"https://github.com/sslotsky/violet-paginator\" target=\"_blank\">violet-paginator</a> to manage list state\n              in Redux, including pagination, sorting, filtering, updating, and more.\n            </li>\n            <li><a href=\"https://github.com/erikras/multireducer\" target=\"_blank\">multireducer</a> combine several\n              identical reducer states into one key-based reducer</li>\n            <li><a href=\"https://github.com/webpack/style-loader\" target=\"_blank\">style-loader</a> and <a\n              href=\"https://github.com/jtangelder/sass-loader\" target=\"_blank\">sass-loader</a> to allow import of\n              stylesheets\n            </li>\n            <li><a href=\"https://github.com/shakacode/bootstrap-sass-loader\" target=\"_blank\">bootstrap-sass-loader</a> and <a\n              href=\"https://github.com/gowravshekar/font-awesome-webpack\" target=\"_blank\">font-awesome-webpack</a> to customize Bootstrap and FontAwesome\n            </li>\n            <li><a href=\"http://socket.io/\">socket.io</a> for real-time communication</li>\n          </ul>\n\n          <h3>Features demonstrated in this project</h3>\n\n          <dl>\n            <dt>Multiple components subscribing to same redux store slice</dt>\n            <dd>\n              The <code>App.js</code> that wraps all the pages contains an <code>InfoBar</code> component\n              that fetches data from the server initially, but allows for the user to refresh the data from\n              the client. <code>About.js</code> contains a <code>MiniInfoBar</code> that displays the same\n              data.\n            </dd>\n            <dt>Server-side data loading</dt>\n            <dd>\n              The <Link to=\"/widgets\">Widgets page</Link> demonstrates how to fetch data asynchronously from\n              some source that is needed to complete the server-side rendering. <code>Widgets.js</code>'s\n              <code>asyncConnect()</code> function is called before the widgets page is loaded, on either the server\n              or the client, allowing all the widget data to be loaded and ready for the page to render.\n            </dd>\n            <dt>Data loading errors</dt>\n            <dd>\n              The <Link to=\"/widgets\">Widgets page</Link> also demonstrates how to deal with data loading\n              errors in Redux. The API endpoint that delivers the widget data intentionally fails 33% of\n              the time to highlight this. The <code>clientMiddleware</code> sends an error action which\n              the <code>widgets</code> reducer picks up and saves to the Redux state for presenting to the user.\n            </dd>\n            <dt>Session based login</dt>\n            <dd>\n              On the <Link to=\"/login\">Login page</Link> you can submit a username which will be sent to the server\n              and stored in the session. Subsequent refreshes will show that you are still logged in.\n            </dd>\n            <dt>Redirect after state change</dt>\n            <dd>\n              After you log in, you will be redirected to a Login Success page. This <strike>magic</strike> logic\n              is performed in <code>componentWillReceiveProps()</code> in <code>App.js</code>, but it could\n              be done in any component that listens to the appropriate store slice, via Redux's <code>@connect</code>,\n              and pulls the router from the context.\n            </dd>\n            <dt>Auth-required views</dt>\n            <dd>\n              The aforementioned Login Success page is only visible to you if you are logged in. If you try\n              to <Link to=\"/loginSuccess\">go there</Link> when you are not logged in, you will be forwarded back\n              to this home page. This <strike>magic</strike> logic is performed by the\n              <code>onEnter</code> hook within <code>routes.js</code>.\n            </dd>\n            <dt>Forms</dt>\n            <dd>\n              The <Link to=\"/survey\">Survey page</Link> uses the\n              still-experimental <a href=\"https://github.com/erikras/redux-form\" target=\"_blank\">redux-form</a> to\n              manage form state inside the Redux store. This includes immediate client-side validation.\n            </dd>\n            <dt>Pagination</dt>\n            <dd>\n              The <Link to=\"/pagination\">Pagination page</Link> uses\n              <a href=\"https://www.npmjs.com/package/violet-paginator\" target=\"_blank\">violet-paginator</a> to\n              paginate and sort records in a data table.\n            </dd>\n            <dt>WebSockets / socket.io</dt>\n            <dd>\n              The <Link to=\"/chat\">Chat</Link> uses the socket.io technology for real-time\n              communication between clients. You need to <Link to=\"/login\">login</Link> first.\n            </dd>\n          </dl>\n\n          <h3>From the author</h3>\n\n          <p>\n            I cobbled this together from a wide variety of similar \"starter\" repositories. As I post this in June 2015,\n            all of these libraries are right at the bleeding edge of web development. They may fall out of fashion as\n            quickly as they have come into it, but I personally believe that this stack is the future of web development\n            and will survive for several years. I'm building my new projects like this, and I recommend that you do,\n            too.\n          </p>\n\n          <p>Thanks for taking the time to check this out.</p>\n\n          <p>– Erik Rasmussen</p>\n        </div>\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "src/containers/Home/Home.scss",
    "content": "@import \"../../theme/variables.scss\";\n\n.home {\n  dd {\n    margin-bottom: 15px;\n  }\n}\n.masthead {\n  background: #2d2d2d;\n  padding: 40px 20px;\n  color: white;\n  text-align: center;\n  .logo {\n    $size: 200px;\n    margin: auto;\n    height: $size;\n    width: $size;\n    border-radius: $size / 2;\n    border: 1px solid $cyan;\n    box-shadow: inset 0 0 10px $cyan;\n    vertical-align: middle;\n    p {\n      line-height: $size;\n      margin: 0px;\n    }\n    img {\n      width: 75%;\n      margin: auto;\n    }\n  }\n  h1 {\n    color: $cyan;\n    font-size: 4em;\n  }\n  h2 {\n    color: #ddd;\n    font-size: 2em;\n    margin: 20px;\n  }\n  a {\n    color: #ddd;\n  }\n  p {\n    margin: 10px;\n  }\n  .humility {\n    color: $humility;\n    a {\n      color: $humility;\n    }\n  }\n  .github {\n    font-size: 1.5em;\n  }\n}\n\n.counterContainer {\n  text-align: center;\n  margin: 20px;\n}\n"
  },
  {
    "path": "src/containers/Login/Login.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport {connect} from 'react-redux';\nimport Helmet from 'react-helmet';\nimport * as authActions from 'redux/modules/auth';\n\n@connect(\n  state => ({user: state.auth.user}),\n  authActions)\nexport default class Login extends Component {\n  static propTypes = {\n    user: PropTypes.object,\n    login: PropTypes.func,\n    logout: PropTypes.func\n  }\n\n  handleSubmit = (event) => {\n    event.preventDefault();\n    const input = this.refs.username;\n    this.props.login(input.value);\n    input.value = '';\n  }\n\n  render() {\n    const {user, logout} = this.props;\n    const styles = require('./Login.scss');\n    return (\n      <div className={styles.loginPage + ' container'}>\n        <Helmet title=\"Login\"/>\n        <h1>Login</h1>\n        {!user &&\n        <div>\n          <form className=\"login-form form-inline\" onSubmit={this.handleSubmit}>\n            <div className=\"form-group\">\n              <input type=\"text\" ref=\"username\" placeholder=\"Enter a username\" className=\"form-control\"/>\n            </div>\n            <button className=\"btn btn-success\" onClick={this.handleSubmit}><i className=\"fa fa-sign-in\"/>{' '}Log In\n            </button>\n          </form>\n          <p>This will \"log you in\" as this user, storing the username in the session of the API server.</p>\n        </div>\n        }\n        {user &&\n        <div>\n          <p>You are currently logged in as {user.name}.</p>\n\n          <div>\n            <button className=\"btn btn-danger\" onClick={logout}><i className=\"fa fa-sign-out\"/>{' '}Log Out</button>\n          </div>\n        </div>\n        }\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "src/containers/Login/Login.scss",
    "content": ".loginPage {\n  input {\n    padding: 5px 10px;\n    border-radius: 5px;\n    border: 1px solid #ccc;\n  }\n  form {\n    margin: 30px 0;\n    :global(.btn) {\n      margin-left: 10px;\n    }\n  }\n}\n"
  },
  {
    "path": "src/containers/LoginSuccess/LoginSuccess.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport {connect} from 'react-redux';\nimport * as authActions from 'redux/modules/auth';\n\n@connect(\n    state => ({user: state.auth.user}),\n    authActions)\nexport default\nclass LoginSuccess extends Component {\n  static propTypes = {\n    user: PropTypes.object,\n    logout: PropTypes.func\n  }\n\n  render() {\n    const {user, logout} = this.props;\n    return (user &&\n      <div className=\"container\">\n        <h1>Login Success</h1>\n\n        <div>\n          <p>Hi, {user.name}. You have just successfully logged in, and were forwarded here\n            by <code>componentWillReceiveProps()</code> in <code>App.js</code>, which is listening to\n            the auth reducer via redux <code>@connect</code>. How exciting!\n          </p>\n\n          <p>\n            The same function will forward you to <code>/</code> should you chose to log out. The choice is yours...\n          </p>\n\n          <div>\n            <button className=\"btn btn-danger\" onClick={logout}><i className=\"fa fa-sign-out\"/>{' '}Log Out</button>\n          </div>\n        </div>\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "src/containers/NotFound/NotFound.js",
    "content": "import React from 'react';\n\nexport default function NotFound() {\n  return (\n    <div className=\"container\">\n      <h1>Doh! 404!</h1>\n      <p>These are <em>not</em> the droids you are looking for!</p>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/containers/Pagination/Pagination.jsx",
    "content": "import React from 'react';\nimport Helmet from 'react-helmet';\nimport { List } from 'immutable';\nimport { connect } from 'react-redux';\nimport { VioletDataTable, VioletPaginator } from 'violet-paginator';\n\nimport './violet.min.scss';\nimport './Pagination.scss';\n\nfunction paginate(list, page, pageSize) {\n  return list.skip((page - 1) * pageSize).take(pageSize);\n}\n\nfunction order(list, sort, sortOrder) {\n  if (sort) {\n    const sorted = list.sortBy(item => item[sort]);\n    if (sortOrder === 'desc') {\n      return sorted.reverse();\n    }\n\n    return sorted;\n  }\n\n  return list;\n}\n\nfunction mockFetch({ query: { pageSize, page, sort, sortOrder } }) {\n  const records = List([{\n    name: 'Ewe and IPA',\n    rank: 75\n  }, {\n    name: 'Pouty Stout',\n    rank: 86\n  }, {\n    name: 'WPA Evil Angel',\n    rank: 63\n  }, {\n    name: 'Maltster',\n    rank: 68\n  }, {\n    name: 'Beer Mosaic Pale',\n    rank: 92\n  }, {\n    name: 'Honey Porter IDK',\n    rank: 93\n  }, {\n    name: 'Puntification BeerSocialist Brown',\n    rank: 88\n  }, {\n    name: 'HefeLite Dubble All-Grain',\n    rank: 55\n  }]);\n\n  const filtered = paginate(\n    order(records, sort, sortOrder),\n    page,\n    pageSize\n  );\n\n  return () => Promise.resolve({\n    data: {\n      results: filtered.toJS(),\n      total_count: records.count()\n    }\n  });\n}\n\nexport function Pagination({ fetch }) {\n  const headers = [{\n    field: 'name',\n    text: 'Name'\n  }, {\n    field: 'rank',\n    text: 'Rank'\n  }];\n\n  const config = {\n    fetch,\n    listId: 'recipes',\n    pageSize: 3\n  };\n\n  return (\n    <section style={{ width: '50%' }}>\n      <h1>Pagination</h1>\n      <Helmet title=\"Pagination\" />\n\n      <p>\n        This is an example of a datatable in redux with sorting and pagination capability\n        provided by <a href=\"https://www.npmjs.com/package/violet-paginator\" target=\"_blank\">violet-paginator</a>.\n      </p>\n      <VioletPaginator {...config} />\n      <VioletDataTable\n        {...config}\n        headers={headers}\n      />\n    </section>\n  );\n}\n\nexport default connect(\n  undefined,\n  { fetch: mockFetch }\n)(Pagination);\n"
  },
  {
    "path": "src/containers/Pagination/Pagination.scss",
    "content": "a {\n  cursor: pointer;\n}\n"
  },
  {
    "path": "src/containers/Pagination/violet.min.scss",
    "content": "/*! normalize.css v3.0.1 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}\naudio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}\nabbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub{font-size:75%;line-height:0;position:relative;vertical-align:baseline}\nsup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}\npre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible;text-transform:none}\nselect{text-transform:none}button,html input[type=\"button\"]{-webkit-appearance:button;cursor:pointer}input[type=\"reset\"],input[type=\"submit\"]{-webkit-appearance:button;cursor:pointer}\nbutton[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input::-moz-focus-inner{border:0;padding:0}input[type=\"checkbox\"],input[type=\"radio\"]{box-sizing:border-box;padding:0}\ninput[type=\"number\"]::-webkit-inner-spin-button,input[type=\"number\"]::-webkit-outer-spin-button{height:auto}input[type=\"search\"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}\ninput[type=\"search\"]::-webkit-search-cancel-button,input[type=\"search\"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}\nlegend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}*{box-sizing:border-box}html{font-family:\"Work Sans\",\"Helvetica Neue\",Arial,sans-serif;font-size:14px;line-height:19px}\n@media(min-width:500px){html{font-size:16px;line-height:22px}}@media(min-width:900px){html{font-size:17px;line-height:24px}}img{margin:1rem 0}h1,h2,h3,h4,h5,h6{font-family:\"Work Sans\",\"Helvetica Neue\",Arial,sans-serif;font-weight:normal}\nh1 a,h2 a,h3 a,h4 a,h5 a,h6 a{border:0}h1{line-height:1.1}p{margin:0}p+p{margin-top:1rem}.align-center{text-align:center}a{color:#333;text-decoration:none;border-bottom:1px solid #555}footer{text-align:center}\nfooter a{border:0}pre{padding:1rem;font-family:\"Source Code Pro\",\"Menlo\",monospace;border-top:1px solid #ddd;border-left:1px solid #ddd;border-bottom:1px solid #eee;border-right:1px solid #eee;background:#fafafa}\n@media(min-width:500px){pre{padding:1.7rem}}@media(min-width:900px){pre{padding:2rem}}.highlight{background-color:#ffa;padding:.1rem}.form-group{padding-bottom:1rem}@media(min-width:767px){.form-group{padding-bottom:1.7rem}\n}@media(min-width:900px){.form-group{padding-bottom:2rem}}.form-group.submit{padding-top:.5rem;padding-bottom:0!important}@media(min-width:767px){.form-group.submit{padding-top:.85rem}}@media(min-width:900px){.form-group.submit{padding-top:1rem}\n}.form-group.checkbox label{padding-bottom:0!important;line-height:2rem}.form-group.checkbox input[type=checkbox],.form-group.checkbox input[type=radio]{height:2rem;margin-right:.5rem;float:left}label{display:block;padding-bottom:.25rem;font-family:\"Work Sans\",\"Helvetica Neue\",Arial,sans-serif;color:rgba(0,0,0,0.54);line-height:2rem}\n@media(min-width:767px){label{padding-bottom:.425rem}}@media(min-width:900px){label{padding-bottom:.5rem}}input[type=text],input[type=submit],textarea,select{-webkit-appearance:none}input[type=text],input[type=date],input[type=password],input[type=email],input[type=datetime-local],textarea,select{font-family:\"Work Sans\",\"Helvetica Neue\",Arial,sans-serif;border-top:1px solid #ddd;border-left:1px solid #ddd;border-bottom:1px solid #eee;border-right:1px solid #eee;width:100%;padding:8px;font-size:16px;background:#fff;border-radius:0}\ninput[type=text][disabled],input[type=date][disabled],input[type=password][disabled],input[type=email][disabled],input[type=datetime-local][disabled],textarea[disabled],select[disabled]{background:#fafafa}\n@media(min-width:500px){textarea{width:100%;height:10em}}.btn,input[type=submit],button{font-family:\"Work Sans\",\"Helvetica Neue\",Arial,sans-serif;-webkit-appearance:none;border:0;display:inline-block;padding:.5rem 1rem;text-decoration:none;color:#FFF;text-shadow:-1px -1px 0 rgba(0,0,0,0.2);background:#666;line-height:1.8rem;padding:.5rem 1rem}\n.btn i,input[type=submit] i,button i{margin-right:.5rem}@media(min-width:767px){.row{display:flex}}.col-1{flex:1}.col-2{flex:2}.col-3{flex:3}.col-4{flex:4}.col-5{flex:5}.col-6{flex:6}.col-7{flex:7}.col-8{flex:8}\n.col-9{flex:9}.col-10{flex:10}body{background:#fdfdfd;padding:1rem}@media(min-width:500px){body{padding:1.7rem}}@media(min-width:900px){body{padding:2rem}}@media(min-width:900px){body{padding:2rem}}@media(min-width:900px) and (min-width:500px){body{padding:3.4rem}\n}@media(min-width:900px) and (min-width:900px){body{padding:4rem}}.soft{padding:2rem}@media(min-width:500px){.soft{padding:3.4rem}}@media(min-width:900px){.soft{padding:4rem}}.soft-half{padding:1rem}@media(min-width:500px){.soft-half{padding:1.7rem}\n}@media(min-width:900px){.soft-half{padding:2rem}}.soft-quarter{padding:.5rem}@media(min-width:500px){.soft-quarter{padding:.85rem}}@media(min-width:900px){.soft-quarter{padding:1rem}}.soft-sides{padding-left:2rem;padding-right:2rem}\n@media(min-width:767px){.soft-sides{padding-left:3.4rem;padding-right:3.4rem}}@media(min-width:900px){.soft-sides{padding-left:4rem;padding-right:4rem}}.soft-half-sides{padding-left:1rem;padding-right:1rem}\n@media(min-width:767px){.soft-half-sides{padding-left:1.7rem;padding-right:1.7rem}}@media(min-width:900px){.soft-half-sides{padding-left:2rem;padding-right:2rem}}.soft-quarter-sides{padding-left:.5rem;padding-right:.5rem}\n@media(min-width:767px){.soft-quarter-sides{padding-left:.85rem;padding-right:.85rem}}@media(min-width:900px){.soft-quarter-sides{padding-left:1rem;padding-right:1rem}}.soft-ends{padding-top:2rem;padding-bottom:2rem}\n@media(min-width:767px){.soft-ends{padding-top:3.4rem;padding-bottom:3.4rem}}@media(min-width:900px){.soft-ends{padding-top:4rem;padding-bottom:4rem}}.soft-half-ends{padding-top:1rem;padding-bottom:1rem}\n@media(min-width:767px){.soft-half-ends{padding-top:1.7rem;padding-bottom:1.7rem}}@media(min-width:900px){.soft-half-ends{padding-top:2rem;padding-bottom:2rem}}.soft-quarter-ends{padding-top:.5rem;padding-bottom:.5rem}\n@media(min-width:767px){.soft-quarter-ends{padding-top:.85rem;padding-bottom:.85rem}}@media(min-width:900px){.soft-quarter-ends{padding-top:1rem;padding-bottom:1rem}}.soft-top{padding-top:2rem}@media(min-width:767px){.soft-top{padding-top:3.4rem}\n}@media(min-width:900px){.soft-top{padding-top:4rem}}.soft-half-top{padding-top:1rem}@media(min-width:767px){.soft-half-top{padding-top:1.7rem}}@media(min-width:900px){.soft-half-top{padding-top:2rem}\n}.soft-quarter-top{padding-top:.5rem}@media(min-width:767px){.soft-quarter-top{padding-top:.85rem}}@media(min-width:900px){.soft-quarter-top{padding-top:1rem}}.soft-bottom{padding-bottom:2rem}@media(min-width:767px){.soft-bottom{padding-bottom:3.4rem}\n}@media(min-width:900px){.soft-bottom{padding-bottom:4rem}}.soft-half-bottom{padding-bottom:1rem}@media(min-width:767px){.soft-half-bottom{padding-bottom:1.7rem}}@media(min-width:900px){.soft-half-bottom{padding-bottom:2rem}\n}.soft-quarter-bottom{padding-bottom:.5rem}@media(min-width:767px){.soft-quarter-bottom{padding-bottom:.85rem}}@media(min-width:900px){.soft-quarter-bottom{padding-bottom:1rem}}.soft-right{padding-right:2rem}\n@media(min-width:767px){.soft-right{padding-right:3.4rem}}@media(min-width:900px){.soft-right{padding-right:4rem}}.soft-half-right{padding-right:1rem}@media(min-width:767px){.soft-half-right{padding-right:1.7rem}\n}@media(min-width:900px){.soft-half-right{padding-right:2rem}}.soft-quarter-right{padding-right:.5rem}@media(min-width:767px){.soft-quarter-right{padding-right:.85rem}}@media(min-width:900px){.soft-quarter-right{padding-right:1rem}\n}.soft-left{padding-left:2rem}@media(min-width:767px){.soft-left{padding-left:3.4rem}}@media(min-width:900px){.soft-left{padding-left:4rem}}.soft-half-left{padding-left:1rem}@media(min-width:767px){.soft-half-left{padding-left:1.7rem}\n}@media(min-width:900px){.soft-half-left{padding-left:2rem}}.soft-quarter-left{padding-left:.5rem}@media(min-width:767px){.soft-quarter-left{padding-left:.85rem}}@media(min-width:900px){.soft-quarter-left{padding-left:1rem}\n}.hard{padding:0!important}.hard-ends{padding-top:0!important;padding-bottom:0!important}.hard-sides{padding-left:0!important;padding-right:0!important}.hard-top{padding-top:0!important}.hard-bottom{padding-bottom:0!important}\n.hard-left{padding-left:0!important}.hard-right{padding-right:0!important}.flush{margin:0!important}.flush-ends{margin-top:0!important;margin-bottom:0!important}.flush-sides{margin-left:0!important;margin-right:0!important}\n.flush-top{margin-top:0!important}.flush-bottom{margin-bottom:0!important}.flush-left{margin-left:0!important}.flush-right{margin-right:0!important}@media(max-width:500px){.hide-on-mobile{display:none}\n}@media(min-width:500px) and (max-width:900px){.hide-on-tablet{display:none}}@media(min-width:500px){.hide-above-mobile{display:none}}.sep{height:2px;background:#fff;border-top:1px solid #ccc}section{padding-top:.5rem;padding-bottom:.5rem}\n@media(min-width:767px){section{padding-top:.85rem;padding-bottom:.85rem}}@media(min-width:900px){section{padding-top:1rem;padding-bottom:1rem}}.inset{border-top:1px solid #ddd;border-left:1px solid #ddd;border-bottom:1px solid #eee;border-right:1px solid #eee}\n.outset{border-top:1px solid #eee;border-left:1px solid #eee;border-bottom:1px solid #ddd;border-right:1px solid #ddd}table{width:100%;margin-bottom:1.25rem}table th{text-align:left}table th,table td{padding-top:.25rem;padding-bottom:.25rem}\n@media(min-width:767px){table th,table td{padding-top:.425rem;padding-bottom:.425rem}}@media(min-width:900px){table th,table td{padding-top:.5rem;padding-bottom:.5rem}}table.border th,table.border td{padding:.33333rem;border:1px solid #dfdfdf}\n@media(min-width:500px){table.border th,table.border td{padding:.56667rem}}@media(min-width:900px){table.border th,table.border td{padding:.66667rem}}.alert{position:relative;background-color:#7e69c6;color:#fff;padding:4rem}\n.alert a.close{position:absolute;top:50%;right:4rem;border:0;color:#fff;width:2rem;text-align:center;line-height:2rem;margin-top:-1rem}.pagination{padding:0!important;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%;cursor:default;list-style:none;text-align:center}\n.pagination li{border-top:1px solid #eee;border-left:1px solid #eee;border-bottom:1px solid #ddd;border-right:1px solid #ddd;display:inline-block;text-align:center;margin:.25rem}.pagination li:active{border-top:1px solid #ddd;border-left:1px solid #ddd;border-bottom:1px solid #eee;border-right:1px solid #eee}\n.pagination li.disabled{padding:.5rem;color:rgba(0,0,0,0.54)}@media(min-width:500px){.pagination li.disabled{padding:.85rem}}@media(min-width:900px){.pagination li.disabled{padding:1rem}}.pagination li.disabled:active{border-top:1px solid #eee;border-left:1px solid #eee;border-bottom:1px solid #ddd;border-right:1px solid #ddd}\n.pagination li.skip,.pagination li.current{padding:.5rem;border:0;margin:0}@media(min-width:500px){.pagination li.skip,.pagination li.current{padding:.85rem}}@media(min-width:900px){.pagination li.skip,.pagination li.current{padding:1rem}\n}.pagination li a{display:block;padding:.5rem;cursor:pointer;border:0}@media(min-width:500px){.pagination li a{padding:.85rem}}@media(min-width:900px){.pagination li a{padding:1rem}}"
  },
  {
    "path": "src/containers/Survey/Survey.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport {connect} from 'react-redux';\nimport Helmet from 'react-helmet';\nimport {initialize} from 'redux-form';\nimport {SurveyForm} from 'components';\n\n@connect(\n  () => ({}),\n  {initialize})\nexport default class Survey extends Component {\n  static propTypes = {\n    initialize: PropTypes.func.isRequired\n  }\n\n  handleSubmit = (data) => {\n    window.alert('Data submitted! ' + JSON.stringify(data));\n    this.props.initialize('survey', {});\n  }\n\n  handleInitialize = () => {\n    this.props.initialize('survey', {\n      name: 'Little Bobby Tables',\n      email: 'bobby@gmail.com',\n      occupation: 'Redux Wizard',\n      currentlyEmployed: true,\n      sex: 'male'\n    });\n  }\n\n  render() {\n    return (\n      <div className=\"container\">\n        <h1>Survey</h1>\n        <Helmet title=\"Survey\"/>\n\n        <p>\n          This is an example of a form in redux in which all the state is kept within the redux store.\n          All the components are pure \"dumb\" components.\n        </p>\n\n        <p>\n          Things to notice:\n        </p>\n\n        <ul>\n          <li>No validation errors are shown initially.</li>\n          <li>Validation errors are only shown onBlur</li>\n          <li>Validation errors are hidden onChange when the error is rectified</li>\n          <li><code>valid</code>, <code>invalid</code>, <code>pristine</code> and <code>dirty</code> flags\n            are passed with each change\n          </li>\n          <li><em>Except</em> when you submit the form, in which case they are shown for all invalid fields.</li>\n          <li>If you click the Initialize Form button, the form will be prepopupated with some values and\n            the <code>pristine</code> and <code>dirty</code> flags will be based on those values.\n          </li>\n        </ul>\n\n        <p>\n          Pardon the use of <code>window.alert()</code>, but I wanted to keep this component stateless.\n        </p>\n\n        <div style={{textAlign: 'center', margin: 15}}>\n          <button className=\"btn btn-primary\" onClick={this.handleInitialize}>\n            <i className=\"fa fa-pencil\"/> Initialize Form\n          </button>\n        </div>\n\n        <p>The circles to the left of the inputs correspond to flags provided by <code>redux-form</code>:\n          Touched, Visited, Active, and Dirty.</p>\n\n        <SurveyForm onSubmit={this.handleSubmit}/>\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "src/containers/Widgets/Widgets.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport Helmet from 'react-helmet';\nimport {connect} from 'react-redux';\nimport * as widgetActions from 'redux/modules/widgets';\nimport {isLoaded, load as loadWidgets} from 'redux/modules/widgets';\nimport {initializeWithKey} from 'redux-form';\nimport { WidgetForm } from 'components';\nimport { asyncConnect } from 'redux-async-connect';\n\n@asyncConnect([{\n  deferred: true,\n  promise: ({store: {dispatch, getState}}) => {\n    if (!isLoaded(getState())) {\n      return dispatch(loadWidgets());\n    }\n  }\n}])\n@connect(\n  state => ({\n    widgets: state.widgets.data,\n    editing: state.widgets.editing,\n    error: state.widgets.error,\n    loading: state.widgets.loading\n  }),\n  {...widgetActions, initializeWithKey })\nexport default class Widgets extends Component {\n  static propTypes = {\n    widgets: PropTypes.array,\n    error: PropTypes.string,\n    loading: PropTypes.bool,\n    initializeWithKey: PropTypes.func.isRequired,\n    editing: PropTypes.object.isRequired,\n    load: PropTypes.func.isRequired,\n    editStart: PropTypes.func.isRequired\n  };\n\n  render() {\n    const handleEdit = (widget) => {\n      const {editStart} = this.props; // eslint-disable-line no-shadow\n      return () => editStart(String(widget.id));\n    };\n    const {widgets, error, editing, loading, load} = this.props;\n    let refreshClassName = 'fa fa-refresh';\n    if (loading) {\n      refreshClassName += ' fa-spin';\n    }\n    const styles = require('./Widgets.scss');\n    return (\n      <div className={styles.widgets + ' container'}>\n        <h1>\n          Widgets\n          <button className={styles.refreshBtn + ' btn btn-success'} onClick={load}>\n            <i className={refreshClassName}/> {' '} Reload Widgets\n          </button>\n        </h1>\n        <Helmet title=\"Widgets\"/>\n        <p>\n          If you hit refresh on your browser, the data loading will take place on the server before the page is returned.\n          If you navigated here from another page, the data was fetched from the client after the route transition.\n          This uses the decorator method <code>@asyncConnect</code> with the <code>deferred: true</code> flag. To block\n          a route transition until some data is loaded, remove the <code>deffered: true</code> flag.\n          To always render before loading data, even on the server, use <code>componentDidMount</code>.\n        </p>\n        <p>\n          This widgets are stored in your session, so feel free to edit it and refresh.\n        </p>\n        {error &&\n        <div className=\"alert alert-danger\" role=\"alert\">\n          <span className=\"glyphicon glyphicon-exclamation-sign\" aria-hidden=\"true\"></span>\n          {' '}\n          {error}\n        </div>}\n        {widgets && widgets.length &&\n        <table className=\"table table-striped\">\n          <thead>\n          <tr>\n            <th className={styles.idCol}>ID</th>\n            <th className={styles.colorCol}>Color</th>\n            <th className={styles.sprocketsCol}>Sprockets</th>\n            <th className={styles.ownerCol}>Owner</th>\n            <th className={styles.buttonCol}></th>\n          </tr>\n          </thead>\n          <tbody>\n          {\n            widgets.map((widget) => editing[widget.id] ?\n              <WidgetForm formKey={String(widget.id)} key={String(widget.id)} initialValues={widget}/> :\n              <tr key={widget.id}>\n                <td className={styles.idCol}>{widget.id}</td>\n                <td className={styles.colorCol}>{widget.color}</td>\n                <td className={styles.sprocketsCol}>{widget.sprocketCount}</td>\n                <td className={styles.ownerCol}>{widget.owner}</td>\n                <td className={styles.buttonCol}>\n                  <button className=\"btn btn-primary\" onClick={handleEdit(widget)}>\n                    <i className=\"fa fa-pencil\"/> Edit\n                  </button>\n                </td>\n              </tr>)\n          }\n          </tbody>\n        </table>}\n      </div>\n    );\n  }\n}\n\n"
  },
  {
    "path": "src/containers/Widgets/Widgets.scss",
    "content": ".widgets {\n  .refreshBtn {\n    margin-left: 20px;\n  }\n  .idCol {\n    width: 5%;\n  }\n  .colorCol {\n    width: 20%;\n  }\n  .sprocketsCol {\n    width: 20%;\n    text-align: right;\n    input {\n      text-align: right;\n    }\n  }\n  .ownerCol {\n    width: 30%;\n  }\n  .buttonCol {\n    width: 25%;\n    :global(.btn) {\n      margin: 0 5px;\n    }\n  }\n  tr.saving {\n    opacity: 0.8;\n    :global(.btn) {\n      &[disabled] {\n        opacity: 1;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/containers/index.js",
    "content": "export App from './App/App';\nexport Chat from './Chat/Chat';\nexport Home from './Home/Home';\nexport Widgets from './Widgets/Widgets';\nexport About from './About/About';\nexport Login from './Login/Login';\nexport LoginSuccess from './LoginSuccess/LoginSuccess';\nexport Survey from './Survey/Survey';\nexport NotFound from './NotFound/NotFound';\nexport Pagination from './Pagination/Pagination';\n"
  },
  {
    "path": "src/helpers/ApiClient.js",
    "content": "import superagent from 'superagent';\nimport config from '../config';\n\nconst methods = ['get', 'post', 'put', 'patch', 'del'];\n\nfunction formatUrl(path) {\n  const adjustedPath = path[0] !== '/' ? '/' + path : path;\n  if (__SERVER__) {\n    // Prepend host and port of the API server to the path.\n    return 'http://' + config.apiHost + ':' + config.apiPort + adjustedPath;\n  }\n  // Prepend `/api` to relative URL, to proxy to API server.\n  return '/api' + adjustedPath;\n}\n\nexport default class ApiClient {\n  constructor(req) {\n    methods.forEach((method) =>\n      this[method] = (path, { params, data } = {}) => new Promise((resolve, reject) => {\n        const request = superagent[method](formatUrl(path));\n\n        if (params) {\n          request.query(params);\n        }\n\n        if (__SERVER__ && req.get('cookie')) {\n          request.set('cookie', req.get('cookie'));\n        }\n\n        if (data) {\n          request.send(data);\n        }\n\n        request.end((err, { body } = {}) => err ? reject(body || err) : resolve(body));\n      }));\n  }\n  /*\n   * There's a V8 bug where, when using Babel, exporting classes with only\n   * constructors sometimes fails. Until it's patched, this is a solution to\n   * \"ApiClient is not defined\" from issue #14.\n   * https://github.com/erikras/react-redux-universal-hot-example/issues/14\n   *\n   * Relevant Babel bug (but they claim it's V8): https://phabricator.babeljs.io/T2455\n   *\n   * Remove it at your own risk.\n   */\n  empty() {}\n}\n"
  },
  {
    "path": "src/helpers/Html.js",
    "content": "import React, {Component, PropTypes} from 'react';\nimport ReactDOM from 'react-dom/server';\nimport serialize from 'serialize-javascript';\nimport Helmet from 'react-helmet';\n\n/**\n * Wrapper component containing HTML metadata and boilerplate tags.\n * Used in server-side code only to wrap the string output of the\n * rendered route component.\n *\n * The only thing this component doesn't (and can't) include is the\n * HTML doctype declaration, which is added to the rendered output\n * by the server.js file.\n */\nexport default class Html extends Component {\n  static propTypes = {\n    assets: PropTypes.object,\n    component: PropTypes.node,\n    store: PropTypes.object\n  };\n\n  render() {\n    const {assets, component, store} = this.props;\n    const content = component ? ReactDOM.renderToString(component) : '';\n    const head = Helmet.rewind();\n\n    return (\n      <html lang=\"en-us\">\n        <head>\n          {head.base.toComponent()}\n          {head.title.toComponent()}\n          {head.meta.toComponent()}\n          {head.link.toComponent()}\n          {head.script.toComponent()}\n\n          <link rel=\"shortcut icon\" href=\"/favicon.ico\" />\n          <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n          {/* styles (will be present only in production with webpack extract text plugin) */}\n          {Object.keys(assets.styles).map((style, key) =>\n            <link href={assets.styles[style]} key={key} media=\"screen, projection\"\n                  rel=\"stylesheet\" type=\"text/css\" charSet=\"UTF-8\"/>\n          )}\n\n          {/* (will be present only in development mode) */}\n          {/* outputs a <style/> tag with all bootstrap styles + App.scss + it could be CurrentPage.scss. */}\n          {/* can smoothen the initial style flash (flicker) on page load in development mode. */}\n          {/* ideally one could also include here the style for the current page (Home.scss, About.scss, etc) */}\n          { Object.keys(assets.styles).length === 0 ? <style dangerouslySetInnerHTML={{__html: require('../theme/bootstrap.config.js') + require('../containers/App/App.scss')._style}}/> : null }\n        </head>\n        <body>\n          <div id=\"content\" dangerouslySetInnerHTML={{__html: content}}/>\n          <script dangerouslySetInnerHTML={{__html: `window.__data=${serialize(store.getState())};`}} charSet=\"UTF-8\"/>\n          <script src={assets.javascript.main} charSet=\"UTF-8\"/>\n        </body>\n      </html>\n    );\n  }\n}\n"
  },
  {
    "path": "src/redux/create.js",
    "content": "import { createStore as _createStore, applyMiddleware, compose } from 'redux';\nimport createMiddleware from './middleware/clientMiddleware';\nimport { routerMiddleware } from 'react-router-redux';\nimport thunk from 'redux-thunk';\nimport Immutable from 'immutable';\n\nexport default function createStore(history, client, data) {\n  // Sync dispatched route actions to the history\n  const reduxRouterMiddleware = routerMiddleware(history);\n\n  const middleware = [createMiddleware(client), reduxRouterMiddleware, thunk];\n\n  let finalCreateStore;\n  if (__DEVELOPMENT__ && __CLIENT__ && __DEVTOOLS__) {\n    const { persistState } = require('redux-devtools');\n    const DevTools = require('../containers/DevTools/DevTools');\n    finalCreateStore = compose(\n      applyMiddleware(...middleware),\n      window.devToolsExtension ? window.devToolsExtension() : DevTools.instrument(),\n      persistState(window.location.href.match(/[?&]debug_session=([^&]+)\\b/))\n    )(_createStore);\n  } else {\n    finalCreateStore = applyMiddleware(...middleware)(_createStore);\n  }\n\n  const reducer = require('./modules/reducer');\n  if (data) {\n    data.pagination = Immutable.fromJS(data.pagination);\n  }\n  const store = finalCreateStore(reducer, data);\n\n\n  if (__DEVELOPMENT__ && module.hot) {\n    module.hot.accept('./modules/reducer', () => {\n      store.replaceReducer(require('./modules/reducer'));\n    });\n  }\n\n  return store;\n}\n"
  },
  {
    "path": "src/redux/middleware/clientMiddleware.js",
    "content": "export default function clientMiddleware(client) {\n  return ({dispatch, getState}) => {\n    return next => action => {\n      if (typeof action === 'function') {\n        return action(dispatch, getState);\n      }\n\n      const { promise, types, ...rest } = action; // eslint-disable-line no-redeclare\n      if (!promise) {\n        return next(action);\n      }\n\n      const [REQUEST, SUCCESS, FAILURE] = types;\n      next({...rest, type: REQUEST});\n\n      const actionPromise = promise(client);\n      actionPromise.then(\n        (result) => next({...rest, result, type: SUCCESS}),\n        (error) => next({...rest, error, type: FAILURE})\n      ).catch((error)=> {\n        console.error('MIDDLEWARE ERROR:', error);\n        next({...rest, error, type: FAILURE});\n      });\n\n      return actionPromise;\n    };\n  };\n}\n"
  },
  {
    "path": "src/redux/modules/auth.js",
    "content": "const LOAD = 'redux-example/auth/LOAD';\nconst LOAD_SUCCESS = 'redux-example/auth/LOAD_SUCCESS';\nconst LOAD_FAIL = 'redux-example/auth/LOAD_FAIL';\nconst LOGIN = 'redux-example/auth/LOGIN';\nconst LOGIN_SUCCESS = 'redux-example/auth/LOGIN_SUCCESS';\nconst LOGIN_FAIL = 'redux-example/auth/LOGIN_FAIL';\nconst LOGOUT = 'redux-example/auth/LOGOUT';\nconst LOGOUT_SUCCESS = 'redux-example/auth/LOGOUT_SUCCESS';\nconst LOGOUT_FAIL = 'redux-example/auth/LOGOUT_FAIL';\n\nconst initialState = {\n  loaded: false\n};\n\nexport default function reducer(state = initialState, action = {}) {\n  switch (action.type) {\n    case LOAD:\n      return {\n        ...state,\n        loading: true\n      };\n    case LOAD_SUCCESS:\n      return {\n        ...state,\n        loading: false,\n        loaded: true,\n        user: action.result\n      };\n    case LOAD_FAIL:\n      return {\n        ...state,\n        loading: false,\n        loaded: false,\n        error: action.error\n      };\n    case LOGIN:\n      return {\n        ...state,\n        loggingIn: true\n      };\n    case LOGIN_SUCCESS:\n      return {\n        ...state,\n        loggingIn: false,\n        user: action.result\n      };\n    case LOGIN_FAIL:\n      return {\n        ...state,\n        loggingIn: false,\n        user: null,\n        loginError: action.error\n      };\n    case LOGOUT:\n      return {\n        ...state,\n        loggingOut: true\n      };\n    case LOGOUT_SUCCESS:\n      return {\n        ...state,\n        loggingOut: false,\n        user: null\n      };\n    case LOGOUT_FAIL:\n      return {\n        ...state,\n        loggingOut: false,\n        logoutError: action.error\n      };\n    default:\n      return state;\n  }\n}\n\nexport function isLoaded(globalState) {\n  return globalState.auth && globalState.auth.loaded;\n}\n\nexport function load() {\n  return {\n    types: [LOAD, LOAD_SUCCESS, LOAD_FAIL],\n    promise: (client) => client.get('/loadAuth')\n  };\n}\n\nexport function login(name) {\n  return {\n    types: [LOGIN, LOGIN_SUCCESS, LOGIN_FAIL],\n    promise: (client) => client.post('/login', {\n      data: {\n        name: name\n      }\n    })\n  };\n}\n\nexport function logout() {\n  return {\n    types: [LOGOUT, LOGOUT_SUCCESS, LOGOUT_FAIL],\n    promise: (client) => client.get('/logout')\n  };\n}\n"
  },
  {
    "path": "src/redux/modules/counter.js",
    "content": "const INCREMENT = 'redux-example/counter/INCREMENT';\n\nconst initialState = {\n  count: 0\n};\n\nexport default function reducer(state = initialState, action = {}) {\n  switch (action.type) {\n    case INCREMENT:\n      const {count} = state;\n      return {\n        count: count + 1\n      };\n    default:\n      return state;\n  }\n}\n\nexport function increment() {\n  return {\n    type: INCREMENT\n  };\n}\n"
  },
  {
    "path": "src/redux/modules/info.js",
    "content": "const LOAD = 'redux-example/LOAD';\nconst LOAD_SUCCESS = 'redux-example/LOAD_SUCCESS';\nconst LOAD_FAIL = 'redux-example/LOAD_FAIL';\n\nconst initialState = {\n  loaded: false\n};\n\nexport default function info(state = initialState, action = {}) {\n  switch (action.type) {\n    case LOAD:\n      return {\n        ...state,\n        loading: true\n      };\n    case LOAD_SUCCESS:\n      return {\n        ...state,\n        loading: false,\n        loaded: true,\n        data: action.result\n      };\n    case LOAD_FAIL:\n      return {\n        ...state,\n        loading: false,\n        loaded: false,\n        error: action.error\n      };\n    default:\n      return state;\n  }\n}\n\nexport function isLoaded(globalState) {\n  return globalState.info && globalState.info.loaded;\n}\n\nexport function load() {\n  return {\n    types: [LOAD, LOAD_SUCCESS, LOAD_FAIL],\n    promise: (client) => client.get('/loadInfo')\n  };\n}\n"
  },
  {
    "path": "src/redux/modules/reducer.js",
    "content": "import { combineReducers } from 'redux';\nimport multireducer from 'multireducer';\nimport { routerReducer } from 'react-router-redux';\nimport {reducer as reduxAsyncConnect} from 'redux-async-connect';\nimport { pagination } from 'violet-paginator';\n\nimport auth from './auth';\nimport counter from './counter';\nimport {reducer as form} from 'redux-form';\nimport info from './info';\nimport widgets from './widgets';\n\nexport default combineReducers({\n  routing: routerReducer,\n  reduxAsyncConnect,\n  auth,\n  form,\n  multireducer: multireducer({\n    counter1: counter,\n    counter2: counter,\n    counter3: counter\n  }),\n  info,\n  pagination,\n  widgets\n});\n"
  },
  {
    "path": "src/redux/modules/survey.js",
    "content": "const IS_VALID = 'redux-example/survey/IS_VALID';\nconst IS_VALID_SUCCESS = 'redux-example/survey/IS_VALID_SUCCESS';\nconst IS_VALID_FAIL = 'redux-example/survey/IS_VALID_FAIL';\n\nconst initialState = {\n  saveError: null,\n};\n\nexport default function reducer(state = initialState, action = {}) {\n  switch (action.type) {\n    case IS_VALID:\n      return state; // 'saving' flag handled by redux-form\n    case IS_VALID_SUCCESS:\n      const data = [...state.data];\n      data[action.result.id - 1] = action.result;\n      return {\n        ...state,\n        data: data,\n        saveError: null,\n      };\n    case IS_VALID_FAIL:\n      return typeof action.error === 'string' ? {\n        ...state,\n        saveError: action.error\n      } : state;\n    default:\n      return state;\n  }\n}\n\nexport function isValidEmail(data) {\n  return {\n    types: [IS_VALID, IS_VALID_SUCCESS, IS_VALID_FAIL],\n    promise: (client) => client.post('/survey/isValid', {\n      data\n    })\n  };\n}\n"
  },
  {
    "path": "src/redux/modules/widgets.js",
    "content": "const LOAD = 'redux-example/widgets/LOAD';\nconst LOAD_SUCCESS = 'redux-example/widgets/LOAD_SUCCESS';\nconst LOAD_FAIL = 'redux-example/widgets/LOAD_FAIL';\nconst EDIT_START = 'redux-example/widgets/EDIT_START';\nconst EDIT_STOP = 'redux-example/widgets/EDIT_STOP';\nconst SAVE = 'redux-example/widgets/SAVE';\nconst SAVE_SUCCESS = 'redux-example/widgets/SAVE_SUCCESS';\nconst SAVE_FAIL = 'redux-example/widgets/SAVE_FAIL';\n\nconst initialState = {\n  loaded: false,\n  editing: {},\n  saveError: {}\n};\n\nexport default function reducer(state = initialState, action = {}) {\n  switch (action.type) {\n    case LOAD:\n      return {\n        ...state,\n        loading: true\n      };\n    case LOAD_SUCCESS:\n      return {\n        ...state,\n        loading: false,\n        loaded: true,\n        data: action.result,\n        error: null\n      };\n    case LOAD_FAIL:\n      return {\n        ...state,\n        loading: false,\n        loaded: false,\n        data: null,\n        error: action.error\n      };\n    case EDIT_START:\n      return {\n        ...state,\n        editing: {\n          ...state.editing,\n          [action.id]: true\n        }\n      };\n    case EDIT_STOP:\n      return {\n        ...state,\n        editing: {\n          ...state.editing,\n          [action.id]: false\n        }\n      };\n    case SAVE:\n      return state; // 'saving' flag handled by redux-form\n    case SAVE_SUCCESS:\n      const data = [...state.data];\n      data[action.result.id - 1] = action.result;\n      return {\n        ...state,\n        data: data,\n        editing: {\n          ...state.editing,\n          [action.id]: false\n        },\n        saveError: {\n          ...state.saveError,\n          [action.id]: null\n        }\n      };\n    case SAVE_FAIL:\n      return typeof action.error === 'string' ? {\n        ...state,\n        saveError: {\n          ...state.saveError,\n          [action.id]: action.error\n        }\n      } : state;\n    default:\n      return state;\n  }\n}\n\nexport function isLoaded(globalState) {\n  return globalState.widgets && globalState.widgets.loaded;\n}\n\nexport function load() {\n  return {\n    types: [LOAD, LOAD_SUCCESS, LOAD_FAIL],\n    promise: (client) => client.get('/widget/load/param1/param2') // params not used, just shown as demonstration\n  };\n}\n\nexport function save(widget) {\n  return {\n    types: [SAVE, SAVE_SUCCESS, SAVE_FAIL],\n    id: widget.id,\n    promise: (client) => client.post('/widget/update', {\n      data: widget\n    })\n  };\n}\n\nexport function editStart(id) {\n  return { type: EDIT_START, id };\n}\n\nexport function editStop(id) {\n  return { type: EDIT_STOP, id };\n}\n"
  },
  {
    "path": "src/routes.js",
    "content": "import React from 'react';\nimport {IndexRoute, Route} from 'react-router';\nimport { isLoaded as isAuthLoaded, load as loadAuth } from 'redux/modules/auth';\nimport {\n    App,\n    Chat,\n    Home,\n    Widgets,\n    About,\n    Login,\n    LoginSuccess,\n    Survey,\n    NotFound,\n    Pagination,\n  } from 'containers';\n\nexport default (store) => {\n  const requireLogin = (nextState, replace, cb) => {\n    function checkAuth() {\n      const { auth: { user }} = store.getState();\n      if (!user) {\n        // oops, not logged in, so can't be here!\n        replace('/');\n      }\n      cb();\n    }\n\n    if (!isAuthLoaded(store.getState())) {\n      store.dispatch(loadAuth()).then(checkAuth);\n    } else {\n      checkAuth();\n    }\n  };\n\n  /**\n   * Please keep routes in alphabetical order\n   */\n  return (\n    <Route path=\"/\" component={App}>\n      { /* Home (main) route */ }\n      <IndexRoute component={Home}/>\n\n      { /* Routes requiring login */ }\n      <Route onEnter={requireLogin}>\n        <Route path=\"chat\" component={Chat}/>\n        <Route path=\"loginSuccess\" component={LoginSuccess}/>\n      </Route>\n\n      { /* Routes */ }\n      <Route path=\"about\" component={About}/>\n      <Route path=\"login\" component={Login}/>\n      <Route path=\"pagination\" component={Pagination}/>\n      <Route path=\"survey\" component={Survey}/>\n      <Route path=\"widgets\" component={Widgets}/>\n\n      { /* Catch all route */ }\n      <Route path=\"*\" component={NotFound} status={404} />\n    </Route>\n  );\n};\n"
  },
  {
    "path": "src/server.js",
    "content": "import Express from 'express';\nimport React from 'react';\nimport ReactDOM from 'react-dom/server';\nimport config from './config';\nimport favicon from 'serve-favicon';\nimport compression from 'compression';\nimport httpProxy from 'http-proxy';\nimport path from 'path';\nimport createStore from './redux/create';\nimport ApiClient from './helpers/ApiClient';\nimport Html from './helpers/Html';\nimport PrettyError from 'pretty-error';\nimport http from 'http';\n\nimport { match } from 'react-router';\nimport { syncHistoryWithStore } from 'react-router-redux';\nimport { ReduxAsyncConnect, loadOnServer } from 'redux-async-connect';\nimport createHistory from 'react-router/lib/createMemoryHistory';\nimport {Provider} from 'react-redux';\nimport getRoutes from './routes';\n\nconst targetUrl = 'http://' + config.apiHost + ':' + config.apiPort;\nconst pretty = new PrettyError();\nconst app = new Express();\nconst server = new http.Server(app);\nconst proxy = httpProxy.createProxyServer({\n  target: targetUrl,\n  ws: true\n});\n\napp.use(compression());\napp.use(favicon(path.join(__dirname, '..', 'static', 'favicon.ico')));\n\napp.use(Express.static(path.join(__dirname, '..', 'static')));\n\n// Proxy to API server\napp.use('/api', (req, res) => {\n  proxy.web(req, res, {target: targetUrl});\n});\n\napp.use('/ws', (req, res) => {\n  proxy.web(req, res, {target: targetUrl + '/ws'});\n});\n\nserver.on('upgrade', (req, socket, head) => {\n  proxy.ws(req, socket, head);\n});\n\n// added the error handling to avoid https://github.com/nodejitsu/node-http-proxy/issues/527\nproxy.on('error', (error, req, res) => {\n  let json;\n  if (error.code !== 'ECONNRESET') {\n    console.error('proxy error', error);\n  }\n  if (!res.headersSent) {\n    res.writeHead(500, {'content-type': 'application/json'});\n  }\n\n  json = {error: 'proxy_error', reason: error.message};\n  res.end(JSON.stringify(json));\n});\n\napp.use((req, res) => {\n  if (__DEVELOPMENT__) {\n    // Do not cache webpack stats: the script file would change since\n    // hot module replacement is enabled in the development env\n    webpackIsomorphicTools.refresh();\n  }\n  const client = new ApiClient(req);\n  const memoryHistory = createHistory(req.originalUrl);\n  const store = createStore(memoryHistory, client);\n  const history = syncHistoryWithStore(memoryHistory, store);\n\n  function hydrateOnClient() {\n    res.send('<!doctype html>\\n' +\n      ReactDOM.renderToString(<Html assets={webpackIsomorphicTools.assets()} store={store}/>));\n  }\n\n  if (__DISABLE_SSR__) {\n    hydrateOnClient();\n    return;\n  }\n\n  match({ history, routes: getRoutes(store), location: req.originalUrl }, (error, redirectLocation, renderProps) => {\n    if (redirectLocation) {\n      res.redirect(redirectLocation.pathname + redirectLocation.search);\n    } else if (error) {\n      console.error('ROUTER ERROR:', pretty.render(error));\n      res.status(500);\n      hydrateOnClient();\n    } else if (renderProps) {\n      loadOnServer({...renderProps, store, helpers: {client}}).then(() => {\n        const component = (\n          <Provider store={store} key=\"provider\">\n            <ReduxAsyncConnect {...renderProps} />\n          </Provider>\n        );\n\n        res.status(200);\n\n        global.navigator = {userAgent: req.headers['user-agent']};\n\n        res.send('<!doctype html>\\n' +\n          ReactDOM.renderToString(<Html assets={webpackIsomorphicTools.assets()} component={component} store={store}/>));\n      });\n    } else {\n      res.status(404).send('Not found');\n    }\n  });\n});\n\nif (config.port) {\n  server.listen(config.port, (err) => {\n    if (err) {\n      console.error(err);\n    }\n    console.info('----\\n==> ✅  %s is running, talking to API server on %s.', config.app.title, config.apiPort);\n    console.info('==> 💻  Open http://%s:%s in a browser to view the app.', config.host, config.port);\n  });\n} else {\n  console.error('==>     ERROR: No PORT environment variable has been specified');\n}\n"
  },
  {
    "path": "src/theme/bootstrap.config.js",
    "content": "/**\n * Bootstrap configuration for bootstrap-sass-loader\n *\n * Scripts are disabled to not load jQuery.\n * If you depend on Bootstrap scripts consider react-bootstrap instead.\n * https://github.com/react-bootstrap/react-bootstrap\n *\n * In order to keep the bundle size low in production\n * disable components you don't use.\n *\n */\n\nmodule.exports = {\n  preBootstrapCustomizations: './src/theme/variables.scss',\n  mainSass: './src/theme/bootstrap.overrides.scss',\n  verbose: false,\n  debug: false,\n  scripts: {\n    transition: false,\n    alert: false,\n    button: false,\n    carousel: false,\n    collapse: false,\n    dropdown: false,\n    modal: false,\n    tooltip: false,\n    popover: false,\n    scrollspy: false,\n    tab: false,\n    affix: false\n  },\n  styles: {\n    mixins: true,\n    normalize: true,\n    print: true,\n    glyphicons: true,\n    scaffolding: true,\n    type: true,\n    code: true,\n    grid: true,\n    tables: true,\n    forms: true,\n    buttons: true,\n    'component-animations': true,\n    dropdowns: true,\n    'button-groups': true,\n    'input-groups': true,\n    navs: true,\n    navbar: true,\n    breadcrumbs: true,\n    pagination: true,\n    pager: true,\n    labels: true,\n    badges: true,\n    jumbotron: true,\n    thumbnails: true,\n    alerts: true,\n    'progress-bars': true,\n    media: true,\n    'list-group': true,\n    panels: true,\n    wells: true,\n    'responsive-embed': true,\n    close: true,\n    modals: true,\n    tooltip: true,\n    popovers: true,\n    carousel: true,\n    utilities: true,\n    'responsive-utilities': true\n  }\n};\n"
  },
  {
    "path": "src/theme/bootstrap.config.prod.js",
    "content": "const bootstrapConfig = require('./bootstrap.config.js');\nconst ExtractTextPlugin = require('extract-text-webpack-plugin');\nbootstrapConfig.styleLoader = ExtractTextPlugin.extract('style-loader', 'css-loader!sass-loader');\nmodule.exports = bootstrapConfig;\n\n"
  },
  {
    "path": "src/theme/bootstrap.overrides.scss",
    "content": "/**\n * Override Bootstrap styles that you can't modify via variables here.\n *\n */\n\n.navbar-brand {\n  position: relative;\n  padding-left: 50px;\n}\n\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n  color: #33e0ff;\n  background-color: transparent;\n}\n"
  },
  {
    "path": "src/theme/font-awesome.config.js",
    "content": "/**\n * Configuration file for font-awesome-webpack\n *\n * In order to keep the bundle size low in production,\n * disable components you don't use.\n *\n */\n\nmodule.exports = {\n  styles: {\n    mixins: true,\n    core: true,\n    icons: true,\n    larger: true,\n    path: true,\n    animated: true,\n  }\n};\n"
  },
  {
    "path": "src/theme/font-awesome.config.less",
    "content": "/**\n * Configuration file for font-awesome-webpack\n *\n */\n\n// Example:\n// @fa-border-color: #ddd;\n"
  },
  {
    "path": "src/theme/font-awesome.config.prod.js",
    "content": "const fontAwesomeConfig = require('./font-awesome.config.js');\nconst ExtractTextPlugin = require('extract-text-webpack-plugin');\nfontAwesomeConfig.styleLoader = ExtractTextPlugin.extract('style-loader', 'css-loader!less-loader');\nmodule.exports = fontAwesomeConfig;\n\n"
  },
  {
    "path": "src/theme/variables.scss",
    "content": "/**\n *  Define scss variables here.\n *\n *  Available options for Bootstrap:\n *  http://getbootstrap.com/customize/\n *\n */\n\n// Custom Colors\n$cyan: #33e0ff;\n$humility: #777;\n\n// Bootstrap Variables\n$brand-primary: darken(#428bca, 6.5%);\n$brand-secondary: #e25139;\n$brand-success: #5cb85c;\n$brand-warning: #f0ad4e;\n$brand-danger: #d9534f;\n$brand-info: #5bc0de;\n\n$text-color: #333;\n\n$font-size-base: 14px;\n$font-family-sans-serif: \"Helvetica Neue\", Helvetica, sans-serif;\n"
  },
  {
    "path": "src/utils/validation.js",
    "content": "const isEmpty = value => value === undefined || value === null || value === '';\nconst join = (rules) => (value, data) => rules.map(rule => rule(value, data)).filter(error => !!error)[0 /* first error */ ];\n\nexport function email(value) {\n  // Let's not start a debate on email regex. This is just for an example app!\n  if (!isEmpty(value) && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$/i.test(value)) {\n    return 'Invalid email address';\n  }\n}\n\nexport function required(value) {\n  if (isEmpty(value)) {\n    return 'Required';\n  }\n}\n\nexport function minLength(min) {\n  return value => {\n    if (!isEmpty(value) && value.length < min) {\n      return `Must be at least ${min} characters`;\n    }\n  };\n}\n\nexport function maxLength(max) {\n  return value => {\n    if (!isEmpty(value) && value.length > max) {\n      return `Must be no more than ${max} characters`;\n    }\n  };\n}\n\nexport function integer(value) {\n  if (!Number.isInteger(Number(value))) {\n    return 'Must be an integer';\n  }\n}\n\nexport function oneOf(enumeration) {\n  return value => {\n    if (!~enumeration.indexOf(value)) {\n      return `Must be one of: ${enumeration.join(', ')}`;\n    }\n  };\n}\n\nexport function match(field) {\n  return (value, data) => {\n    if (data) {\n      if (value !== data[field]) {\n        return 'Do not match';\n      }\n    }\n  };\n}\n\nexport function createValidator(rules) {\n  return (data = {}) => {\n    const errors = {};\n    Object.keys(rules).forEach((key) => {\n      const rule = join([].concat(rules[key])); // concat enables both functions and arrays of functions\n      const error = rule(data[key], data);\n      if (error) {\n        errors[key] = error;\n      }\n    });\n    return errors;\n  };\n}\n"
  },
  {
    "path": "tests.webpack.js",
    "content": "var context = require.context('./src', true, /-test\\.js$/);\ncontext.keys().forEach(context);\n"
  },
  {
    "path": "webpack/dev.config.js",
    "content": "require('babel-polyfill');\n\n// Webpack config for development\nvar fs = require('fs');\nvar path = require('path');\nvar webpack = require('webpack');\nvar assetsPath = path.resolve(__dirname, '../static/dist');\nvar host = (process.env.HOST || 'localhost');\nvar port = (+process.env.PORT + 1) || 3001;\n\n// https://github.com/halt-hammerzeit/webpack-isomorphic-tools\nvar WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin');\nvar webpackIsomorphicToolsPlugin = new WebpackIsomorphicToolsPlugin(require('./webpack-isomorphic-tools'));\n\nvar babelrc = fs.readFileSync('./.babelrc');\nvar babelrcObject = {};\n\ntry {\n  babelrcObject = JSON.parse(babelrc);\n} catch (err) {\n  console.error('==>     ERROR: Error parsing your .babelrc.');\n  console.error(err);\n}\n\n\nvar babelrcObjectDevelopment = babelrcObject.env && babelrcObject.env.development || {};\n\n// merge global and dev-only plugins\nvar combinedPlugins = babelrcObject.plugins || [];\ncombinedPlugins = combinedPlugins.concat(babelrcObjectDevelopment.plugins);\n\nvar babelLoaderQuery = Object.assign({}, babelrcObjectDevelopment, babelrcObject, {plugins: combinedPlugins});\ndelete babelLoaderQuery.env;\n\n// Since we use .babelrc for client and server, and we don't want HMR enabled on the server, we have to add\n// the babel plugin react-transform-hmr manually here.\n\n// make sure react-transform is enabled\nbabelLoaderQuery.plugins = babelLoaderQuery.plugins || [];\nvar reactTransform = null;\nfor (var i = 0; i < babelLoaderQuery.plugins.length; ++i) {\n  var plugin = babelLoaderQuery.plugins[i];\n  if (Array.isArray(plugin) && plugin[0] === 'react-transform') {\n    reactTransform = plugin;\n  }\n}\n\nif (!reactTransform) {\n  reactTransform = ['react-transform', {transforms: []}];\n  babelLoaderQuery.plugins.push(reactTransform);\n}\n\nif (!reactTransform[1] || !reactTransform[1].transforms) {\n  reactTransform[1] = Object.assign({}, reactTransform[1], {transforms: []});\n}\n\n// make sure react-transform-hmr is enabled\nreactTransform[1].transforms.push({\n  transform: 'react-transform-hmr',\n  imports: ['react'],\n  locals: ['module']\n});\n\nmodule.exports = {\n  devtool: 'inline-source-map',\n  context: path.resolve(__dirname, '..'),\n  entry: {\n    'main': [\n      'webpack-hot-middleware/client?path=http://' + host + ':' + port + '/__webpack_hmr',\n      'bootstrap-sass!./src/theme/bootstrap.config.js',\n      'font-awesome-webpack!./src/theme/font-awesome.config.js',\n      './src/client.js'\n    ]\n  },\n  output: {\n    path: assetsPath,\n    filename: '[name]-[hash].js',\n    chunkFilename: '[name]-[chunkhash].js',\n    publicPath: 'http://' + host + ':' + port + '/dist/'\n  },\n  module: {\n    loaders: [\n      { test: /\\.jsx?$/, exclude: /node_modules/, loaders: ['babel?' + JSON.stringify(babelLoaderQuery), 'eslint-loader']},\n      { test: /\\.json$/, loader: 'json-loader' },\n      { test: /\\.less$/, loader: 'style!css?modules&importLoaders=2&sourceMap&localIdentName=[local]___[hash:base64:5]!autoprefixer?browsers=last 2 version!less?outputStyle=expanded&sourceMap' },\n      { test: /\\.scss$/, loader: 'style!css?modules&importLoaders=2&sourceMap&localIdentName=[local]___[hash:base64:5]!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded&sourceMap' },\n      { test: /\\.woff(\\?v=\\d+\\.\\d+\\.\\d+)?$/, loader: \"url?limit=10000&mimetype=application/font-woff\" },\n      { test: /\\.woff2(\\?v=\\d+\\.\\d+\\.\\d+)?$/, loader: \"url?limit=10000&mimetype=application/font-woff\" },\n      { test: /\\.ttf(\\?v=\\d+\\.\\d+\\.\\d+)?$/, loader: \"url?limit=10000&mimetype=application/octet-stream\" },\n      { test: /\\.eot(\\?v=\\d+\\.\\d+\\.\\d+)?$/, loader: \"file\" },\n      { test: /\\.svg(\\?v=\\d+\\.\\d+\\.\\d+)?$/, loader: \"url?limit=10000&mimetype=image/svg+xml\" },\n      { test: webpackIsomorphicToolsPlugin.regular_expression('images'), loader: 'url-loader?limit=10240' }\n    ]\n  },\n  progress: true,\n  resolve: {\n    modulesDirectories: [\n      'src',\n      'node_modules'\n    ],\n    extensions: ['', '.json', '.js', '.jsx']\n  },\n  plugins: [\n    // hot reload\n    new webpack.HotModuleReplacementPlugin(),\n    new webpack.IgnorePlugin(/webpack-stats\\.json$/),\n    new webpack.DefinePlugin({\n      __CLIENT__: true,\n      __SERVER__: false,\n      __DEVELOPMENT__: true,\n      __DEVTOOLS__: true  // <-------- DISABLE redux-devtools HERE\n    }),\n    webpackIsomorphicToolsPlugin.development()\n  ]\n};\n"
  },
  {
    "path": "webpack/prod.config.js",
    "content": "require('babel-polyfill');\n\n// Webpack config for creating the production bundle.\nvar path = require('path');\nvar webpack = require('webpack');\nvar CleanPlugin = require('clean-webpack-plugin');\nvar ExtractTextPlugin = require('extract-text-webpack-plugin');\nvar strip = require('strip-loader');\n\nvar projectRootPath = path.resolve(__dirname, '../');\nvar assetsPath = path.resolve(projectRootPath, './static/dist');\n\n// https://github.com/halt-hammerzeit/webpack-isomorphic-tools\nvar WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin');\nvar webpackIsomorphicToolsPlugin = new WebpackIsomorphicToolsPlugin(require('./webpack-isomorphic-tools'));\n\nmodule.exports = {\n  devtool: 'source-map',\n  context: path.resolve(__dirname, '..'),\n  entry: {\n    'main': [\n      'bootstrap-sass!./src/theme/bootstrap.config.prod.js',\n      'font-awesome-webpack!./src/theme/font-awesome.config.prod.js',\n      './src/client.js'\n    ]\n  },\n  output: {\n    path: assetsPath,\n    filename: '[name]-[chunkhash].js',\n    chunkFilename: '[name]-[chunkhash].js',\n    publicPath: '/dist/'\n  },\n  module: {\n    loaders: [\n      { test: /\\.jsx?$/, exclude: /node_modules/, loaders: [strip.loader('debug'), 'babel']},\n      { test: /\\.json$/, loader: 'json-loader' },\n      { test: /\\.less$/, loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=2&sourceMap!autoprefixer?browsers=last 2 version!less?outputStyle=expanded&sourceMap=true&sourceMapContents=true') },\n      { test: /\\.scss$/, loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=2&sourceMap!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded&sourceMap=true&sourceMapContents=true') },\n      { test: /\\.woff(\\?v=\\d+\\.\\d+\\.\\d+)?$/, loader: \"url?limit=10000&mimetype=application/font-woff\" },\n      { test: /\\.woff2(\\?v=\\d+\\.\\d+\\.\\d+)?$/, loader: \"url?limit=10000&mimetype=application/font-woff\" },\n      { test: /\\.ttf(\\?v=\\d+\\.\\d+\\.\\d+)?$/, loader: \"url?limit=10000&mimetype=application/octet-stream\" },\n      { test: /\\.eot(\\?v=\\d+\\.\\d+\\.\\d+)?$/, loader: \"file\" },\n      { test: /\\.svg(\\?v=\\d+\\.\\d+\\.\\d+)?$/, loader: \"url?limit=10000&mimetype=image/svg+xml\" },\n      { test: webpackIsomorphicToolsPlugin.regular_expression('images'), loader: 'url-loader?limit=10240' }\n    ]\n  },\n  progress: true,\n  resolve: {\n    modulesDirectories: [\n      'src',\n      'node_modules'\n    ],\n    extensions: ['', '.json', '.js', '.jsx']\n  },\n  plugins: [\n    new CleanPlugin([assetsPath], { root: projectRootPath }),\n\n    // css files from the extract-text-plugin loader\n    new ExtractTextPlugin('[name]-[chunkhash].css', {allChunks: true}),\n    new webpack.DefinePlugin({\n      'process.env': {\n        NODE_ENV: '\"production\"'\n      },\n\n      __CLIENT__: true,\n      __SERVER__: false,\n      __DEVELOPMENT__: false,\n      __DEVTOOLS__: false\n    }),\n\n    // ignore dev config\n    new webpack.IgnorePlugin(/\\.\\/dev/, /\\/config$/),\n\n    // optimizations\n    new webpack.optimize.DedupePlugin(),\n    new webpack.optimize.OccurenceOrderPlugin(),\n    new webpack.optimize.UglifyJsPlugin({\n      compress: {\n        warnings: false\n      }\n    }),\n\n    webpackIsomorphicToolsPlugin\n  ]\n};\n"
  },
  {
    "path": "webpack/webpack-dev-server.js",
    "content": "var Express = require('express');\nvar webpack = require('webpack');\n\nvar config = require('../src/config');\nvar webpackConfig = require('./dev.config');\nvar compiler = webpack(webpackConfig);\n\nvar host = config.host || 'localhost';\nvar port = (Number(config.port) + 1) || 3001;\nvar serverOptions = {\n  contentBase: 'http://' + host + ':' + port,\n  quiet: true,\n  noInfo: true,\n  hot: true,\n  inline: true,\n  lazy: false,\n  publicPath: webpackConfig.output.publicPath,\n  headers: {'Access-Control-Allow-Origin': '*'},\n  stats: {colors: true}\n};\n\nvar app = new Express();\n\napp.use(require('webpack-dev-middleware')(compiler, serverOptions));\napp.use(require('webpack-hot-middleware')(compiler));\n\napp.listen(port, function onAppListening(err) {\n  if (err) {\n    console.error(err);\n  } else {\n    console.info('==> 🚧  Webpack development server listening on port %s', port);\n  }\n});\n"
  },
  {
    "path": "webpack/webpack-isomorphic-tools.js",
    "content": "var WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin');\n\n// see this link for more info on what all of this means\n// https://github.com/halt-hammerzeit/webpack-isomorphic-tools\nmodule.exports = {\n\n  // when adding \"js\" extension to asset types \n  // and then enabling debug mode, it may cause a weird error:\n  //\n  // [0] npm run start-prod exited with code 1\n  // Sending SIGTERM to other processes..\n  //\n  // debug: true, \n\n  assets: {\n    images: {\n      extensions: [\n        'jpeg',\n        'jpg',\n        'png',\n        'gif'\n      ],\n      parser: WebpackIsomorphicToolsPlugin.url_loader_parser\n    },\n    fonts: {\n      extensions: [\n        'woff',\n        'woff2',\n        'ttf',\n        'eot'\n      ],\n      parser: WebpackIsomorphicToolsPlugin.url_loader_parser\n    },\n    svg: {\n      extension: 'svg',\n      parser: WebpackIsomorphicToolsPlugin.url_loader_parser\n    },\n    // this whole \"bootstrap\" asset type is only used once in development mode.\n    // the only place it's used is the Html.js file\n    // where a <style/> tag is created with the contents of the\n    // './src/theme/bootstrap.config.js' file.\n    // (the aforementioned <style/> tag can reduce the white flash \n    //  when refreshing page in development mode)\n    //\n    // hooking into 'js' extension require()s isn't the best solution\n    // and I'm leaving this comment here in case anyone finds a better idea.\n    bootstrap: {\n      extension: 'js',\n      include: ['./src/theme/bootstrap.config.js'],\n      filter: function(module, regex, options, log) {\n        function is_bootstrap_style(name) {\n          return name.indexOf('./src/theme/bootstrap.config.js') >= 0;\n        }\n        if (options.development) {\n          return is_bootstrap_style(module.name) && WebpackIsomorphicToolsPlugin.style_loader_filter(module, regex, options, log);\n        }\n        // no need for it in production mode\n      },\n      // in development mode there's webpack \"style-loader\",\n      // so the module.name is not equal to module.name\n      path: WebpackIsomorphicToolsPlugin.style_loader_path_extractor,\n      parser: WebpackIsomorphicToolsPlugin.css_loader_parser\n    },\n    style_modules: {\n      extensions: ['less','scss'],\n      filter: function(module, regex, options, log) {\n        if (options.development) {\n          // in development mode there's webpack \"style-loader\",\n          // so the module.name is not equal to module.name\n          return WebpackIsomorphicToolsPlugin.style_loader_filter(module, regex, options, log);\n        } else {\n          // in production mode there's no webpack \"style-loader\",\n          // so the module.name will be equal to the asset path\n          return regex.test(module.name);\n        }\n      },\n      path: function(module, options, log) {\n        if (options.development) {\n          // in development mode there's webpack \"style-loader\",\n          // so the module.name is not equal to module.name\n          return WebpackIsomorphicToolsPlugin.style_loader_path_extractor(module, options, log);\n        } else {\n          // in production mode there's no webpack \"style-loader\",\n          // so the module.name will be equal to the asset path\n          return module.name;\n        }\n      },\n      parser: function(module, options, log) {\n        if (options.development) {\n          return WebpackIsomorphicToolsPlugin.css_modules_loader_parser(module, options, log);\n        } else {\n          // in production mode there's Extract Text Loader which extracts CSS text away\n          return module.source;\n        }\n      }\n    }\n  }\n}\n"
  }
]