[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [ \"es2015\", \"stage-0\", \"react\" ],\n  \"plugins\": [ \"add-module-exports\", \"transform-decorators-legacy\" ]\n}\n"
  },
  {
    "path": ".bookignore",
    "content": "src/\nbuild/\ndev/\nexamples/\nnpm-package/\ntest/\npackage.json\nwebpack/"
  },
  {
    "path": ".eslintignore",
    "content": "node_modules\nbuild\ndev\nwebpack/replace\nexamples\ntest/app/setup.js\nnpm-package\n_book\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"extends\": \"eslint-config-airbnb\",\n  \"globals\": {\n    \"chrome\": true,\n    \"__DEVELOPMENT__\": true\n  },\n  \"env\": {\n    \"browser\": true,\n    \"node\": true\n  },\n  \"rules\": {\n    \"react/jsx-uses-react\": 2,\n    \"react/jsx-uses-vars\": 2,\n    \"react/react-in-jsx-scope\": 2,\n    \"react/jsx-quotes\": 0,\n    \"block-scoped-var\": 0,\n    \"padded-blocks\": 0,\n    \"quotes\": [ 1, \"single\" ],\n    \"comma-style\": [ 2, \"last\" ],\n    \"no-use-before-define\": [0, \"nofunc\"],\n    \"func-names\": 0,\n    \"prefer-const\": 0,\n    \"comma-dangle\": 0,\n    \"id-length\": 0,\n    \"indent\": [2, 2, {\"SwitchCase\": 1}],\n    \"new-cap\": [2, { \"capIsNewExceptions\": [\"Test\"] }],\n    \"default-case\": 0\n  },\n  \"plugins\": [\n    \"react\"\n  ]\n}"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\nnpm-debug.log\n.DS_Store\n.idea/\ndist/\nbuild/\ndev/\ntmp/\n_book\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: required\ndist: trusty\nlanguage: node_js\nnode_js:\n  - \"6\"\ncache:\n  directories:\n    - $HOME/.yarn-cache\n    - node_modules\nenv:\n  - CXX=g++-4.8\naddons:\n  apt:\n    sources:\n      - google-chrome\n      - ubuntu-toolchain-r-test\n    packages:\n      - google-chrome-stable\n      - g++-4.8\n\ninstall:\n  - \"/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16\"\n  - npm install -g yarn\n  - yarn install\n\nbefore_script:\n  - export DISPLAY=:99.0\n  - sh -e /etc/init.d/xvfb start &\n  - sleep 3\n\nscript:\n  - yarn test\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\nThis project adheres to [Semantic Versioning](http://semver.org/).  \nEvery release, along with the migration instructions, is documented on the Github [Releases](https://github.com/zalmoxisus/redux-devtools-extension/releases) page.\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015-present Mihail Diordiev\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."
  },
  {
    "path": "README.md",
    "content": "⚠️⚠️⚠️🚨🚨🚨⚠️⚠️⚠️\n## This repo is no longer the home of the redux-devtools-extension. The new home is https://github.com/reduxjs/redux-devtools. Please file your issues and PRs there. \n⚠️⚠️⚠️🚨🚨🚨⚠️⚠️⚠️\n\n# Redux DevTools Extension\n\n[![Join the chat at https://gitter.im/zalmoxisus/redux-devtools-extension](https://badges.gitter.im/zalmoxisus/redux-devtools-extension.svg)](https://gitter.im/zalmoxisus/redux-devtools-extension?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=round-square)](http://makeapullrequest.com)\n[![OpenCollective](https://opencollective.com/redux-devtools-extension/backers/badge.svg)](#backers) \n[![OpenCollective](https://opencollective.com/redux-devtools-extension/sponsors/badge.svg)](#sponsors)\n\n![Demo](https://cloud.githubusercontent.com/assets/7957859/18002950/aacb82fc-6b93-11e6-9ae9-609862c18302.png)\n\n## Installation\n\n### 1. For Chrome\n - from [Chrome Web Store](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd);\n - or download `extension.zip` from [last releases](https://github.com/zalmoxisus/redux-devtools-extension/releases), unzip, open `chrome://extensions` url and turn on developer mode from top left and then click; on `Load Unpacked` and select the extracted folder for use\n - or build it with `npm i && npm run build:extension` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./build/extension`;\n - or run it in dev mode with `npm i && npm start` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./dev`.\n\n### 2. For Firefox\n - from [Mozilla Add-ons](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/);\n - or build it with `npm i && npm run build:firefox` and [load the extension's folder](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Temporary_Installation_in_Firefox) `./build/firefox` (just select a file from inside the dir).\n\n### 3. For Electron\n  - just specify `REDUX_DEVTOOLS` in [`electron-devtools-installer`](https://github.com/GPMDP/electron-devtools-installer).\n\n### 4. For other browsers and non-browser environment\n  - use [`remote-redux-devtools`](https://github.com/zalmoxisus/remote-redux-devtools). \n\n## Usage\n\n> Note that starting from v2.7, `window.devToolsExtension` was renamed to `window.__REDUX_DEVTOOLS_EXTENSION__` / `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__`. \n\n## 1. With Redux\n### 1.1 Basic store\n  \nFor a basic [Redux store](https://redux.js.org/api/createstore#createstorereducer-preloadedstate-enhancer) simply add:\n```diff\n const store = createStore(\n   reducer, /* preloadedState, */\n+  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()\n );\n```\n\nNote that [`preloadedState`](https://redux.js.org/api/createstore#createstorereducer-preloadedstate-enhancer) argument is optional in Redux's [`createStore`](https://redux.js.org/api/createstore#createstorereducer-preloadedstate-enhancer).\n\n> For universal (\"isomorphic\") apps, prefix it with `typeof window !== 'undefined' &&`.\n```js\nconst composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;\n```\n\n> For TypeScript use [`redux-devtools-extension` npm package](#13-use-redux-devtools-extension-package-from-npm), which contains all the definitions, or just use `(window as any)` (see [Recipes](/docs/Recipes.md#using-in-a-typescript-project) for an example).\n```js\nconst composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;\n```\n\nIn case ESLint is configured to not allow using the underscore dangle, wrap it like so:\n```diff\n+ /* eslint-disable no-underscore-dangle */\n  const store = createStore(\n   reducer, /* preloadedState, */\n   window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()\n  );\n+ /* eslint-enable */\n```\n\n> **Note**: Passing enhancer as last argument requires **redux@>=3.1.0**. For older versions apply it like [here](https://github.com/zalmoxisus/redux-devtools-extension/blob/v0.4.2/examples/todomvc/store/configureStore.js) or [here](https://github.com/zalmoxisus/redux-devtools-extension/blob/v0.4.2/examples/counter/store/configureStore.js#L7-L12). Don't mix the old Redux API with the new one.\n\n> You don't need to npm install [`redux-devtools`](https://github.com/gaearon/redux-devtools) when using the extension (that's a different lib).\n\n### 1.2 Advanced store setup\nIf you setup your store with [middleware and enhancers](http://redux.js.org/docs/api/applyMiddleware.html), change:\n```diff\n  import { createStore, applyMiddleware, compose } from 'redux';\n\n+ const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;\n+ const store = createStore(reducer, /* preloadedState, */ composeEnhancers(\n- const store = createStore(reducer, /* preloadedState, */ compose(\n    applyMiddleware(...middleware)\n  ));\n```\n> Note that when the extension is not installed, we’re using Redux compose here.\n  \nTo specify [extension’s options](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md), use it like so:\n```js\nconst composeEnhancers =\n  typeof window === 'object' &&\n  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   \n    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({\n      // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...\n    }) : compose;\n\nconst enhancer = composeEnhancers(\n  applyMiddleware(...middleware),\n  // other store enhancers if any\n);\nconst store = createStore(reducer, enhancer);\n```\n\n> [See the post for more details](https://medium.com/@zalmoxis/improve-your-development-workflow-with-redux-devtools-extension-f0379227ff83).\n\n### 1.3 Use `redux-devtools-extension` package from npm\n\nTo make things easier, there's an npm package to install:\n```\nnpm install --save redux-devtools-extension\n```\nand to use like so:\n```js\nimport { createStore, applyMiddleware } from 'redux';\nimport { composeWithDevTools } from 'redux-devtools-extension';\n\nconst store = createStore(reducer, composeWithDevTools(\n  applyMiddleware(...middleware),\n  // other store enhancers if any\n));\n```\nTo specify [extension’s options](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#windowdevtoolsextensionconfig):\n```js\nimport { createStore, applyMiddleware } from 'redux';\nimport { composeWithDevTools } from 'redux-devtools-extension';\n\nconst composeEnhancers = composeWithDevTools({\n  // Specify name here, actionsBlacklist, actionsCreators and other options if needed\n});\nconst store = createStore(reducer, /* preloadedState, */ composeEnhancers(\n  applyMiddleware(...middleware),\n  // other store enhancers if any\n));\n```  \n> There’re just [few lines of code](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/index.js) added to your bundle.\n\nIn case you don't include other enhancers and middlewares, just use `devToolsEnhancer`:\n```js\nimport { createStore } from 'redux';\nimport { devToolsEnhancer } from 'redux-devtools-extension';\n\nconst store = createStore(reducer, /* preloadedState, */ devToolsEnhancer(\n  // Specify name here, actionsBlacklist, actionsCreators and other options if needed\n));\n```    \n\n### 1.4 Using in production\nIt's useful to include the extension in production as well. Usually you [can use it for development](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f). \n\nIf you want to restrict it there, use `redux-devtools-extension/logOnlyInProduction`:\n```js\nimport { createStore } from 'redux';\nimport { devToolsEnhancer } from 'redux-devtools-extension/logOnlyInProduction';\n\nconst store = createStore(reducer, /* preloadedState, */ devToolsEnhancer(\n  // options like actionSanitizer, stateSanitizer\n));\n```\nor with middlewares and enhancers:\n ```js\n import { createStore, applyMiddleware } from 'redux';\n import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction';\n\n const composeEnhancers = composeWithDevTools({\n   // options like actionSanitizer, stateSanitizer\n });\n const store = createStore(reducer, /* preloadedState, */ composeEnhancers(\n   applyMiddleware(...middleware),\n   // other store enhancers if any\n ));\n ```\n>  You'll have to add `'process.env.NODE_ENV': JSON.stringify('production')` in your Webpack config for the production bundle ([to envify](https://github.com/gaearon/redux-devtools/blob/master/docs/Walkthrough.md#exclude-devtools-from-production-builds)). If you use `create-react-app`, [it already does it for you.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/config/webpack.config.prod.js#L253-L257)\n\n If you're already checking `process.env.NODE_ENV` when creating the store, include `redux-devtools-extension/logOnly` for production environment.\n\n If you don’t want to allow the extension in production, just use `redux-devtools-extension/developmentOnly`.\n\n> See [the article](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f) for more details. \n\n### 1.5 For React Native, hybrid, desktop and server side Redux apps\nFor React Native we can use [`react-native-debugger`](https://github.com/jhen0409/react-native-debugger), which already included [the same API](https://github.com/jhen0409/react-native-debugger/blob/master/docs/redux-devtools-integration.md) with Redux DevTools Extension.\n\nFor most platforms, include [`Remote Redux DevTools`](https://github.com/zalmoxisus/remote-redux-devtools)'s store enhancer, and from the extension's context menu choose 'Open Remote DevTools' for remote monitoring.\n\n## 2. Without Redux\nSee [integrations](docs/Integrations.md) and [the blog post](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f) for more details on how to use the extension with any architecture.\n  \n## Docs\n  - [Options (arguments)](docs/API/Arguments.md)\n  - [Methods (advanced API)](docs/API/Methods.md)\n  - [FAQ](docs/FAQ.md)\n  - Features\n    - [Trace actions calls](/docs/Features/Trace.md)\n  - [Troubleshooting](docs/Troubleshooting.md)\n  - [Articles](docs/Articles.md)\n  - [Videos](docs/Videos.md)\n  - [Feedback](docs/Feedback.md)\n\n## Demo\nLive demos to use the extension with:\n\n - [Counter](http://zalmoxisus.github.io/examples/counter/)\n - [TodoMVC](http://zalmoxisus.github.io/examples/todomvc/)\n - [Redux Form](http://redux-form.com/6.5.0/examples/simple/)\n - [React Tetris](https://chvin.github.io/react-tetris/?lan=en)\n - [Book Collection (Angular ngrx store)](https://ngrx.github.io/platform/example-app/)\n\nAlso see [`./examples` folder](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/examples).\n\n## Backers\nSupport us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/redux-devtools-extension#backer)]\n\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/9/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/10/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/10/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/11/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/11/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/12/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/12/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/13/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/13/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/14/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/14/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/15/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/15/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/16/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/16/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/17/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/17/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/18/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/18/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/19/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/19/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/20/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/20/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/21/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/21/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/22/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/22/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/23/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/23/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/24/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/24/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/25/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/25/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/26/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/26/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/27/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/27/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/28/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/28/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/backer/29/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/backer/29/avatar.svg\"></a>\n\n\n## Sponsors\nBecome a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/redux-devtools-extension#sponsor)]\n\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/9/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/10/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/10/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/11/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/11/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/12/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/12/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/13/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/13/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/14/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/14/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/15/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/15/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/16/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/16/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/17/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/17/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/18/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/18/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/19/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/19/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/20/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/20/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/21/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/21/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/22/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/22/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/23/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/23/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/24/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/24/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/25/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/25/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/26/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/26/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/27/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/27/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/28/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/28/avatar.svg\"></a>\n<a href=\"https://opencollective.com/redux-devtools-extension/sponsor/29/website\" target=\"_blank\"><img src=\"https://opencollective.com/redux-devtools-extension/sponsor/29/avatar.svg\"></a>\n\n## License\n\nMIT\n\n## Created By\n\nIf you like this, follow [@mdiordiev](https://twitter.com/mdiordiev) on twitter.\n"
  },
  {
    "path": "appveyor.yml",
    "content": "environment:\n  matrix:\n    - nodejs_version: '6'\n\ncache:\n  - \"%LOCALAPPDATA%/Yarn\"\n  - node_modules\n\ninstall:\n  - ps: Install-Product node $env:nodejs_version\n  - yarn install\n\ntest_script:\n  - node --version\n  - yarn --version\n  - yarn test\n\nbuild: off\n"
  },
  {
    "path": "book.json",
    "content": "{\n  \"gitbook\": \"3.2.2\",\n  \"title\": \"Redux DevTools Extension\",\n  \"plugins\": [\"edit-link\", \"prism\", \"-highlight\", \"github\", \"anchorjs\"],\n  \"pluginsConfig\": {\n    \"edit-link\": {\n      \"base\": \"https://github.com/zalmoxisus/redux-devtools-extension/tree/master\",\n      \"label\": \"Edit This Page\"\n    },\n    \"github\": {\n      \"url\": \"https://github.com/zalmoxisus/redux-devtools-extension/\"\n    },\n    \"sharing\": {\n      \"facebook\": true,\n      \"twitter\": true\n    }\n  }\n}"
  },
  {
    "path": "docs/API/Arguments.md",
    "content": "# Options\n\nUse with\n - `window.__REDUX_DEVTOOLS_EXTENSION__([options])`\n - `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__([options])()`\n - `window.__REDUX_DEVTOOLS_EXTENSION__.connect([options])`\n - `redux-devtools-extension` npm package:\n  ```js\n  import { composeWithDevTools } from 'redux-devtools-extension';\n\n  const composeEnhancers = composeWithDevTools(options);\n  const store = createStore(reducer, /* preloadedState, */ composeEnhancers(\n    applyMiddleware(...middleware),\n    // other store enhancers if any\n  ));\n  ```\n\nThe `options` object is optional, and can include any of the following.\n\n### `name`\n*string* - the instance name to be shown on the monitor page. Default value is `document.title`. If not specified and there's no document title, it will consist of `tabId` and `instanceId`.\n  \n### `actionCreators`\n*array* or *object* - action creators functions to be available in the Dispatcher. See [the example](https://github.com/zalmoxisus/redux-devtools-extension/commit/477e69d8649dfcdc9bf84dd45605dab7d9775c03).\n\n### `latency`\n*number (in ms)* - if more than one action is dispatched in the indicated interval, all new actions will be collected and sent at once. It is the joint between performance and speed. When set to `0`, all actions will be sent instantly. Set it to a higher value when experiencing perf issues (also `maxAge` to a lower value). Default is `500 ms`.\n  \n### `maxAge`\n*number* (>1) - maximum allowed actions to be stored in the history tree. The oldest actions are removed once maxAge is reached. It's critical for performance. Default is `50`.\n\n### `trace`\n*boolean* or *function* - if set to `true`, will include stack trace for every dispatched action, so you can see it in trace tab jumping directly to that part of code ([more details](../Features/Trace.md)). You can use a function (with action object as argument) which should return `new Error().stack` string, getting the stack outside of reducers. Default to `false`.\n\n### `traceLimit`\n*number* - maximum stack trace frames to be stored (in case `trace` option was provided as `true`). By default it's `10`. Note that, because extension's calls are excluded, the resulted frames could be 1 less. If `trace` option is a function, `traceLimit` will have no effect, as it's supposed to be handled there.\n\n### `serialize`\n*boolean* or *object* which contains:\n\n- **options** `object or boolean`:\n   - `undefined` - will use regular `JSON.stringify` to send data (it's the fast mode).\n   - `false` - will handle also circular references.\n   - `true` - will handle also date, regex, undefined, primitives, error objects, symbols, maps, sets and functions.\n   - object, which contains `date`, `regex`, `undefined`, `nan`, `infinity`, `error`, `symbol`, `map`, `set` and `function` keys. For each of them you can indicate if to include (by setting as `true`). For `function` key you can also specify a custom function which handles serialization. See [`jsan`](https://github.com/kolodny/jsan) for more details. Example:\n\n     ```js\n     const store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({\n       serialize: { \n         options: {\n          undefined: true,\n          function: function(fn) { return fn.toString() }\n         }\n       }\n     }));\n     ```\n\n- **replacer** `function(key, value)` - [JSON `replacer` function](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter) used for both actions and states stringify.\n\n  Example of usage with [mori data structures](https://github.com/swannodette/mori):\n  ```js\n  const store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({\n    serialize: {\n      replacer: (key, value) => value && mori.isMap(value) ? mori.toJs(value) : value\n    }\n  }));\n  ```\n  In addition, you can specify a data type by adding a [`__serializedType__`](https://github.com/zalmoxisus/remotedev-serialize/blob/master/helpers/index.js#L4) key. So you can deserialize it back while importing or persisting data. Moreover, it will also [show a nice preview showing the provided custom type](https://cloud.githubusercontent.com/assets/7957859/21814330/a17d556a-d761-11e6-85ef-159dd12f36c5.png):\n  ```js\n  const store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({\n    serialize: {\n      replacer: (key, value) => {\n        if (Immutable.List.isList(value)) { // use your custom data type checker\n          return {\n            data: value.toArray(), // ImmutableJS custom method to get JS data as array\n            __serializedType__: 'ImmutableList' // mark you custom data type to show and retrieve back\n          }\n        }\n      }\n    }\n  }));\n  ```\n- **reviver** `function(key, value)` - [JSON `reviver` function](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Using_the_reviver_parameter) used for parsing the imported actions and states. See [`remotedev-serialize`](https://github.com/zalmoxisus/remotedev-serialize/blob/master/immutable/serialize.js#L8-L41) as an example on how to serialize special data types and get them back:\n  ```js\n  const store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({\n    serialize: {\n      reviver: (key, value) => {\n        if (typeof value === 'object' && value !== null && '__serializedType__'  in value) {\n          switch (value.__serializedType__) {\n            case 'ImmutableList': return Immutable.List(value.data);\n          }\n        }\n      }\n    }\n  }));\n  ```\n\n- **immutable** `object` - automatically serialize/deserialize immutablejs via [remotedev-serialize](https://github.com/zalmoxisus/remotedev-serialize). Just pass the Immutable library like so:\n\n  ```js\n  import Immutable from 'immutable'; // https://facebook.github.io/immutable-js/\n  // ...\n  // Like above, only showing off compose this time. Reminder you might not want this in prod.\n  const composeEnhancers = typeof window === 'object' && typeof window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ !== 'undefined' ?\n   window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({\n     serialize: {\n       immutable: Immutable\n     }\n  }) : compose;\n  ```\n  It will support all ImmutableJS structures. You can even export them into a file and get them back. The only exception is `Record` class, for which you should pass in addition the references to your classes in `refs`.\n- **refs** `array` - ImmutableJS `Record` classes used to make possible restore its instances back when importing, persisting... Example of usage:\n  ``` js\n  import Immutable from 'immutable';\n  // ...\n  \n  const ABRecord = Immutable.Record({ a:1, b:2 });\n  const myRecord = new ABRecord({ b:3 }); // used in the reducers\n  \n  const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({\n    serialize: {\n      immutable: Immutable,\n      refs: [ABRecord]\n    }\n  }));\n  ```\n  \nAlso you can specify alternative values right in the state object (in the initial state of the reducer) by adding `toJSON` function:\n\nIn the example bellow it will always send `{ component: '[React]' }`, regardless of the state's `component` value (useful when you don't want to send lots of unnecessary data):\n```js\nfunction component(\n  state = { component: null, toJSON: () => ({ component: '[React]' }) }, \n  action\n) {\n  switch (action.type) {\n    case 'ADD_COMPONENT': return { component: action.component };\n    default: return state;\n  }\n}\n```\n\nYou could also alter the value. For example when state is `{ count: 1 }`, we'll send `{ counter: 10 }` (notice we don't have an arrow function this time to use the object's `this`):  \n```js\nfunction counter(\n  state = { count: 0, toJSON: function (){ return { conter: this.count * 10 }; } },\n  action\n) {\n  switch (action.type) {\n    case 'INCREMENT': return { count: state.count + 1 };\n    default: return state;\n  }\n}\n```\n\n### `actionSanitizer` / `stateSanitizer`\n- **actionSanitizer** (*function*) - function which takes `action` object and id number as arguments, and should return `action` object back. See the example bellow.\n- **stateSanitizer** (*function*) - function which takes `state` object and index as arguments, and should return `state` object back.\n\nExample of usage:\n\n```js\nconst actionSanitizer = (action) => (\n  action.type === 'FILE_DOWNLOAD_SUCCESS' && action.data ?\n  { ...action, data: '<<LONG_BLOB>>' } : action\n);\nconst store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({\n  actionSanitizer,\n  stateSanitizer: (state) => state.data ? { ...state, data: '<<LONG_BLOB>>' } : state\n}));\n```\n\n### `actionsBlacklist` / `actionsWhitelist`\n*string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers). If `actionsWhitelist` specified, `actionsBlacklist` is ignored.\n\nExample:\n```js\ncreateStore(reducer, remotedev({\n  sendTo: 'http://localhost:8000',\n  actionsBlacklist: 'SOME_ACTION'\n  // or actionsBlacklist: ['SOME_ACTION', 'SOME_OTHER_ACTION']\n  // or just actionsBlacklist: 'SOME_' to omit both\n}))\n```\n\n### `predicate`\n*function* - called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor. Use it as a more advanced version of `actionsBlacklist`/`actionsWhitelist` parameters.\nExample of usage:\n\n```js\nconst store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({\n  predicate: (state, action) => state.dev.logLevel === VERBOSE && !action.forwarded\n}));\n```\n\n### `shouldRecordChanges`\n*boolean* - if specified as `false`, it will not record the changes till clicking on `Start recording` button. Default is `true`. Available only for Redux enhancer, for others use `autoPause`.\n\n### `pauseActionType`\n*string* - if specified, whenever clicking on `Pause recording` button and there are actions in the history log, will add this action type. If not specified, will commit when paused. Available only for Redux enhancer. Default is `@@PAUSED`.\n\n### `autoPause`\n*boolean* - auto pauses when the extension’s window is not opened, and so has zero impact on your app when not in use. Not available for Redux enhancer (as it already does it but storing the data to be sent). Default is `false`.\n\n### `shouldStartLocked`\n*boolean* - if specified as `true`, it will not allow any non-monitor actions to be dispatched till clicking on `Unlock changes` button. Available only for Redux enhancer. Default is `false`.\n\n### `shouldHotReload`\n*boolean* - if set to `false`, will not recompute the states on hot reloading (or on replacing the reducers). Available only for Redux enhancer. Default to `true`.\n\n### `shouldCatchErrors`\n*boolean* - if specified as `true`, whenever there's an exception in reducers, the monitors will show the error message, and next actions will not be dispatched.\n\n### `features`\nIf you want to restrict the extension, just specify the features you allow:\n```js\nconst composeEnhancers = composeWithDevTools({\n  features: {\n    pause: true, // start/pause recording of dispatched actions\n    lock: true, // lock/unlock dispatching actions and side effects    \n    persist: true, // persist states on page reloading\n    export: true, // export history of actions in a file\n    import: 'custom', // import history of actions from a file\n    jump: true, // jump back and forth (time travelling)\n    skip: true, // skip (cancel) actions\n    reorder: true, // drag and drop actions in the history list \n    dispatch: true, // dispatch custom actions or action creators\n    test: true // generate tests for the selected actions\n  },\n  // other options like actionSanitizer, stateSanitizer\n});\n```\nIf not specified, all of the features are enabled. When set as an object, only those included as `true` will be allowed.\nNote that except `true`/`false`, `import` and `export` can be set as `custom` (which is by default for Redux enhancer), meaning that the importing/exporting occurs on the client side. Otherwise, you'll get/set the data right from the monitor part.\n"
  },
  {
    "path": "docs/API/Methods.md",
    "content": "## Communicate with the extension directly\n\n> Note this is advanced API, which you usually don't need to use with Redux enhancer. \n\nUse the following methods of `window.__REDUX_DEVTOOLS_EXTENSION__`:\n\n- [connect](#connect)\n- [disconnect](#disconnect)\n- [send](#send)\n- [listen](#listen)\n- [open](#open)\n- [notifyErrors](#notifyerrors)\n\n<a id=\"connect\"></a>\n### connect([options])\n\n##### Arguments\n\n- [`options`] *Object* - [see the available options](Arguments.md).\n\n##### Returns\n*Object* containing the following methods:\n\n- `subscribe(listener)` - adds a change listener. It will be called any time an action is dispatched from the monitor. Returns a function to unsubscribe the current listener. \n- `unsubscribe()` - unsubscribes all listeners.\n- `send(action, state)` - sends a new action and state manually to be shown on the monitor. If action is `null` then we suppose we send `liftedState`. \n- `init(state)` - sends the initial state to the monitor.\n- `error(message)` - sends the error message to be shown in the extension's monitor.\n\nExample of usage:\n\n```js\nconst devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect(config);\ndevTools.subscribe((message) => {\n  if (message.type === 'DISPATCH' && message.state) {\n    console.log('DevTools requested to change the state to', message.state);\n  }\n});\ndevTools.init({ value: 'initial state' });\ndevTools.send('change state', { value: 'state changed' })\n```\n\nSee [redux enhancer's example](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/logOnly.js), [react example](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/react-counter-messaging/components/Counter.js) and [blog post](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f) for more details.\n\n### disconnect()\n\nRemove extensions listener and disconnect extensions background script connection. Usually just unsubscribing the listener inside the `connect` is enough.  \n\n<a id=\"send\"></a>\n### send(action, state, [options, instanceId])\n\nSend a new action and state manually to be shown on the monitor. It's recommended to use [`connect`](connect), unless you want to hook into an already created instance.\n\n##### Arguments\n\n- `action` *String* (action type) or *Object* with required `type` key.\n- `state` *any* - usually object to expand. \n- [`options`] *Object* - [see the available options](Arguments.md).\n- [`instanceId`] *String* - instance id for which to include the log. If not specified and not present in the `options` object, will be the first available instance.\n\n<a id=\"listen\"></a>\n### listen(onMessage, instanceId)\n\nListen for messages dispatched for specific `instanceId`. For most cases it's better to use `subcribe` inside the [`connect`](connect).\n\n##### Arguments\n\n- `onMessage` *Function* to call when there's an action from the monitor.\n- `instanceId` *String* - instance id for which to handle actions.  \n\n<a id=\"open\"></a>\n### open([position])\n\nOpen the extension's window. This should be conditional (usually you don't need to open extension's window automatically).\n\n##### Arguments\n\n- [`position`] *String* - window position: `left`, `right`, `bottom`. Also can be `panel` to [open it in a Chrome panel](../FAQ.md#how-to-keep-devtools-window-focused-all-the-time-in-a-chrome-panel). Or `remote` to [open remote monitor](../FAQ.md#how-to-get-it-work-with-webworkers-react-native-hybrid-desktop-and-server-side-apps). By default is `left`.\n\n<a id=\"notifyErrors\"></a>\n### notifyErrors([onError])\n\nWhen called, the extension will listen for uncaught exceptions on the page, and, if any, will show native notifications. Optionally, you can provide a function to be called when an exception occurs.\n\n##### Arguments\n\n- [`onError`] *Function* to call when there's an exceptions.\n"
  },
  {
    "path": "docs/API/README.md",
    "content": "# API Reference\n\n- [Parameters](Arguments.md)\n- [Methods](Methods.md)\n"
  },
  {
    "path": "docs/Articles.md",
    "content": "# Articles\n\n- [Improve your development workflow with Redux DevTools Extension](https://medium.com/@zalmoxis/improve-your-development-workflow-with-redux-devtools-extension-f0379227ff83)\n- [Using Redux DevTools in production](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f)\n- [Redux DevTools without Redux](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f)\n"
  },
  {
    "path": "docs/Credits.md",
    "content": "# Credits\n\n - Built using [Crossbuilder](https://github.com/zalmoxisus/crossbuilder) boilerplate.\n - Includes [Dan Abramov](https://github.com/gaearon)'s [redux-devtools](https://github.com/gaearon/redux-devtools) and the following monitors:\n    - [Log Monitor](https://github.com/gaearon/redux-devtools-log-monitor)\n    - [Inspector](https://github.com/alexkuz/redux-devtools-inspector)\n    - [Dispatch](https://github.com/YoruNoHikage/redux-devtools-dispatch)\n    - [Slider](https://github.com/calesce/redux-slider-monitor)\n    - [Chart](https://github.com/romseguy/redux-devtools-chart-monitor)\n - [The logo icon](https://github.com/reactjs/redux/issues/151) made by [Keith Yong](https://github.com/keithyong) .\n - Examples from [Redux](https://github.com/rackt/redux/tree/master/examples).\n"
  },
  {
    "path": "docs/FAQ.md",
    "content": "# Redux DevTools Extension FAQ\n\n## Table of Contents\n- [How to get it work](#how-to-get-it-work)\n- [How to disable/enable it in production](#how-to-disable-it-in-production)\n- [How to persist debug sessions across page reloads](#how-to-persist-debug-sessions-across-page-reloads)\n- [How to open DevTools programmatically](#how-to-open-devtools-programmatically)\n- [How to enable/disable errors notifying](#how-to-enabledisable-errors-notifying)\n- [How to get it work with WebWorkers, React Native, hybrid, desktop and server side apps](#how-to-get-it-work-with-webworkers-react-native-hybrid-desktop-and-server-side-apps)\n- [Keyboard shortcuts](#keyboard-shortcuts)\n\n#### How to get it work\n- Check the extension with [Counter](http://zalmoxisus.github.io/examples/counter/) or [TodoMVC](http://zalmoxisus.github.io/examples/todomvc/) demo.\n- Reload the extension on the extensions page (`chrome://extensions/`).\n- If something goes wrong, [open an issue](https://github.com/zalmoxisus/redux-devtools-extension/issues) or tweet me: [@mdiordiev](https://twitter.com/mdiordiev).\n\n#### How to disable it in production\nUsually you don't have to. See [the article for details on how to include it in production](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f). \n#### How to persist debug sessions across page reloads\nJust click the `Persist` button or add `?debug_session=<session_name>` to the url.\n#### How to open DevTools programmatically\n```js\nwindow.__REDUX_DEVTOOLS_EXTENSION__.open();\n```\nMake sure to have it conditionally. Auto opening windows is a bad DX. See the [API](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Methods.md#open) for details.\n#### How to enable/disable errors notifying\nJust find `Redux DevTools` on the extensions page (`chrome://extensions/`) and click the `Options` link to customize everything. The errors notifying is disabled by default. If enabled, it works only when the store enhancer is called (in order not to show notifications for any sites you visit). In case you want notifications for a non-redux app, init it explicitly by calling `window.__REDUX_DEVTOOLS_EXTENSION__.notifyErrors()` (probably you'll check if `window.__REDUX_DEVTOOLS_EXTENSION__` exists before calling it).\n#### How to get it work with WebWorkers, React Native, hybrid, desktop and server side apps\nIt is not possible to inject extension's script there and to communicate directly. To solve this, use [Remote Redux DevTools](https://github.com/zalmoxisus/remote-redux-devtools). After including it inside the app, click `Remote` button for remote monitoring. \n#### Keyboard shortcuts\nTo set/change the keyboard shortcuts, click \"Keyboard shortcuts\" button on the bottom of the extensions page (`chrome://extensions/`). By default only `Cmd` (`Ctrl`) + `Shift` + `E` is available, which will open the extension popup (only when the Redux store is available in the current page).\n"
  },
  {
    "path": "docs/Features/Trace.md",
    "content": "## Trace actions calls\n\n![trace-demo](https://user-images.githubusercontent.com/7957859/50161148-a1639300-02e3-11e9-80e7-18d3215a0bf8.gif)\n\nOne of the features of Redux DevTools is to select an action in the history and see the callstack that triggered it. It aims to solve the problem of finding the source of events in the event list.\n\nBy default it's disabled as, depending of the use case, generating and serializing stack traces for every action can impact the performance. To enable it, set `trace` option to `true` as in [examples](https://github.com/zalmoxisus/redux-devtools-extension/commit/64717bb9b3534ff616d9db56c2be680627c7b09d). See [the API](../API/Arguments.md#trace) for more details.\n\nFor some edge cases where stack trace cannot be obtained with just `Error().stack`, you can pass a function as `trace` with your implementation. It's useful for cases where the stack is broken, like, for example, [when calling `setTimeout`](https://github.com/zalmoxisus/redux-devtools-instrument/blob/e7c05c98e7e9654cb7db92a2f56c6b5f3ff2452b/test/instrument.spec.js#L735-L737). It takes `action` object as argument and should return `stack` string. This way it can be also used to provide stack conditionally only for certain actions.\n\nThere's also an optional `traceLimit` parameter, which is `10` by default, to prevent consuming too much memory and serializing large stacks and also allows you to get larger stacks than limited by the browser (it will overpass default limit of `10` imposed by Chrome in `Error.stackTraceLimit`). If `trace` option is a function, `traceLimit` will have no effect, that should be handled there like so: `trace: () => new Error().stack.split('\\n').slice(0, limit+1).join('\\n')` (`+1` is needed for Chrome where's an extra 1st frame for `Error\\n`).\n\nApart from opening resources in Chrome DevTools, as seen in the demo above, it can open the file (and jump to the line-column) right in your editor. Pretty useful for debugging, and also as an alternative when it's not possible to use openResource (for Firefox or when using the extension from window or for remote debugging). You can click Settings button and enable that, also adding the path to your project root directory to use. It works out of the box for VSCode, Atom, Webstorm/Phpstorm/IntelliJ, Sublime, Emacs, MacVim, Textmate on Mac and Windows. For Linux you can use [`atom-url-handler`](https://github.com/eclemens/atom-url-handler).\n"
  },
  {
    "path": "docs/Feedback.md",
    "content": "# Feedback wanted\n\n[File an issue](https://github.com/zalmoxisus/redux-devtools-extension/issues) or [submit a PR](https://github.com/zalmoxisus/redux-devtools-extension/pulls) if you have suggestions, rate us and leave a review on [Chrome Store](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd/reviews), post feature requests and bug reports on [Product Pains](https://productpains.com/product/redux-devtools-extension), or ping me on Twitter as [@mdiordiev](https://twitter.com/mdiordiev).\n\n<iframe width=\"100%\" height=\"400px\" scrolling=\"no\" style=\"border:0\" src=\"https://productpains.com/widget.html?token=fc7887ce-f3f9-105a-e5cb-43b9eeb668d5\"></iframe><script type=\"text/javascript\" src=\"https://productpains.com/js/lib/iframeResizer.min.js\"></script>\n"
  },
  {
    "path": "docs/Integrations.md",
    "content": "# Integrations for js and non-js frameworks\n\nMostly functional:\n- [React](#react)\n- [Angular](#angular)\n- [Cycle](#cycle)\n- [Ember](#ember)\n- [Fable](#fable)\n- [Freezer](#freezer)\n- [Mobx](#mobx)\n- [PureScript](#purescript)\n- [Reductive](#reductive)\n- [Aurelia](#aurelia)\n\nIn progress:\n- [ClojureScript](#clojurescript)\n- [Horizon](#horizon)\n- [Python](#python)\n- [Swift](#swift)\n\n### [React](https://github.com/facebook/react)\n#### Inspect React props\n##### [`react-inspect-props`](https://github.com/lucasconstantino/react-inspect-props)\n```js\nimport { compose, withState } from 'recompose'\nimport { inspectProps } from 'react-inspect-props'\n\ncompose(\n  withState('count', 'setCount', 0),\n  inspectProps('Counter inspector')\n)(Counter)\n```\n\n#### Inspect React states\n##### [`remotedev-react-state`](https://github.com/jhen0409/remotedev-react-state)\n```js\nimport connectToDevTools from 'remotedev-react-state'\n\ncomponentWillMount() {\n    // Connect to devtools after setup initial state\n    connectToDevTools(this/*, options */)\n  }\n```\n\n#### Inspect React hooks (useState and useReducer)\n##### [`reinspect`](https://github.com/troch/reinspect)\n```js\nimport { useState } from 'reinspect'\n\nexport function CounterWithUseState({ id }) {\n    const [count, setCount] = useState(0, id)\n    // ...\n}\n```\n\n### [Mobx](https://github.com/mobxjs/mobx)\n#### [`mobx-remotedev`](https://github.com/zalmoxisus/mobx-remotedev)\n```js\nimport remotedev from 'mobx-remotedev';\n// or import remotedev from 'mobx-remotedev/lib/dev'\n// in case you want to use it in production or don't have process.env.NODE_ENV === 'development'\n\nconst appStore = observable({\n  // ...\n});\n\n// Or\nclass appStore {\n    // ...\n}\n\nexport default remotedev(appStore);\n````\n\n### [Angular](https://github.com/angular/angular)\n#### [ng2-redux](https://github.com/angular-redux/ng2-redux)\n```js\nimport { NgReduxModule, NgRedux, DevToolsExtension } from 'ng2-redux';\n\n@NgModule({\n  /* ... */\n  imports: [ /* ... */, NgReduxModule ]\n})export class AppModule {\n  constructor(\n    private ngRedux: NgRedux,\n    private devTools: DevToolsExtension) {\n\n    let enhancers = [];\n    // ... add whatever other enhancers you want.\n\n    // You probably only want to expose this tool in devMode.\n    if (__DEVMODE__ && devTools.isEnabled()) {\n      enhancers = [ ...enhancers, devTools.enhancer() ];\n    }\n\n    this.ngRedux.configureStore(\n      rootReducer,\n      initialState,\n      [],\n      enhancers);\n  }\n}\n```\nFor Angular 1 see [ng-redux](https://github.com/angular-redux/ng-redux).\n\n#### [Angular @ngrx/store](https://ngrx.io/) + [`@ngrx/store-devtools`](https://ngrx.io/guide/store-devtools)\n```js\nimport { StoreDevtoolsModule } from '@ngrx/store-devtools';\n\n@NgModule({\n  imports: [\n    StoreModule.forRoot(rootReducer),\n    // Instrumentation must be imported after importing StoreModule (config is optional)\n    StoreDevtoolsModule.instrument({\n      maxAge: 5\n    })\n  ]\n})\nexport class AppModule { }\n```\n\n[`Example of integration`](https://github.com/ngrx/platform/tree/master/projects/example-app/) ([live demo](https://ngrx.github.io/platform/example-app/)).\n\n### [Ember](http://emberjs.com/)\n#### [`ember-redux`](https://github.com/ember-redux/ember-redux)\n```js\n//app/enhancers/index.js\nimport { compose } from 'redux';\nvar devtools = window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : f => f;\nexport default compose(devtools);\n```\n\n### [Cycle](https://github.com/cyclejs/cyclejs)\n#### [`@culli/store`](https://github.com/milankinen/culli/tree/master/packages/store)\n```js\nimport {run} from \"@cycle/most-run\"\nimport {makeDOMDriver as DOM} from \"@cycle/dom\"\nimport Store, {ReduxDevtools} from \"@culli/store\"\nimport App, {newId} from \"./App\"\n\n\nrun(App, {\n  DOM: DOM(\"#app\"),\n  Store: Store(ReduxDevtools({items: [{id: newId(), num: 0}, {id: newId(), num: 0}]}))\n})\n```\n\n### [Freezer](https://github.com/arqex/freezer)\n#### [`freezer-redux-devtools`](https://github.com/arqex/freezer-redux-devtools)\n```js\nimport React, { Component } from 'react';\nimport { supportChromeExtension } from 'freezer-redux-devtools/freezer-redux-middleware';\nimport Freezer from 'freezer-js';\n\n// Our state is a freezer object\nvar State = new Freezer({hello: 'world'});\n\n// Enable the extension\nsupportChromeExtension( State );\n```\n\n### [Horizon](https://github.com/rethinkdb/horizon)\n#### [`horizon-remotedev`](https://github.com/zalmoxisus/horizon-remotedev)\n```js\n// import hzRemotedev from 'horizon-remotedev';\n// or import hzRemotedev from 'horizon-remotedev/lib/dev'\n// in case you want to use it in production or don't have process.env.NODE_ENV === 'development'\n\n//Setup Horizon connection\nconst horizon = Horizon();\n\n// ...\n// Specify the horizon instance to monitor\nhzRemotedev(horizon(\"react_messages\"))\n```\n\n### [Fable](https://github.com/fable-compiler/Fable)\n#### [`fable-elmish/debugger`](https://github.com/fable-elmish/debugger)\n```fsharp\nopen Elmish.Debug\n\nProgram.mkProgram init update view\n|> Program.withDebugger // connect to a devtools monitor via Chrome extension if available\n|> Program.run\n\n```\n\nor\n\n```fsharp\nopen Elmish.Debug\n\nProgram.mkProgram init update view\n|> Program.withDebuggerAt (Remote(\"localhost\",8000)) // connect to a server running on localhost:8000\n|> Program.run\n```\n\n### [PureScript](https://github.com/purescript/purescript)\n#### [`purescript-react-redux`](https://github.com/ethul/purescript-react-redux)\n[`Example of integration`](https://github.com/ethul/purescript-react-redux-example).\n\n### [ClojureScript](https://github.com/clojure/clojurescript)\n[`Example of integration`](http://gitlab.xet.ru:9999/publicpr/clojurescript-redux/tree/master#dev-setup)\n\n### [Python](https://www.python.org/)\n#### [`pyredux`](https://github.com/peterpeter5/pyredux)\n[WIP](https://github.com/zalmoxisus/remotedev-server/issues/34)\n\n### [Swift](https://github.com/apple/swift)\n#### [`katanaMonitor`](https://github.com/bolismauro/katanaMonitor-lib-swift) for [`katana-swift`](https://github.com/BendingSpoons/katana-swift)\n```swift\nimport KatanaMonitor\n\nvar middleware: [StoreMiddleware] = [\n// other middleware\n]\n\n#if DEBUG\nmiddleware.append(MonitorMiddleware.create(using: .defaultConfiguration))\n#endif\n```\n\n### [Reductive](https://github.com/reasonml-community/reductive)\n#### [`reductive-dev-tools`](https://github.com/ambientlight/reductive-dev-tools)\n```reason\nlet storeEnhancer =\n  ReductiveDevTools.(\n    Connectors.reductiveEnhancer(\n      Extension.enhancerOptions(~name=\"MyApp\", ()),\n    )\n  );\n  \nlet storeCreator = storeEnhancer @@ Reductive.Store.create;\n```\n\n### [Aurelia](http://aurelia.io)\n#### [`aurelia-store`](https://aurelia.io/docs/plugins/store)\n```ts\nimport {Aurelia} from 'aurelia-framework';\nimport {initialState} from './state';\n  \nexport function configure(aurelia: Aurelia) {\n  aurelia.use\n    .standardConfiguration()\n    .feature('resources');\n  \n    ...\n  \n  aurelia.use.plugin('aurelia-store', {\n    initialState,\n    devToolsOptions: { // optional\n      ... // see https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md\n    },\n  });\n  \n  aurelia.start().then(() => aurelia.setRoot());\n}\n```\n"
  },
  {
    "path": "docs/README.md",
    "content": "# Documentation\n\n* [Extension](/README.md)\n  * [Installation](/README.md#installation)\n  * [Usage](/README.md#usage)\n  * [Demo](/README.md#demo)\n* [API Reference](/docs/API/README.md)\n  * [Options (arguments)](/docs/API/Arguments.md)\n  * [Methods (advanced API)](/docs/API/Methods.md)\n* Features\n  * [Trace actions calls](/docs/Features/Trace.md)\n* [Integrations](/docs/Integrations.md)\n* [FAQ](/docs/FAQ.md)\n* [Troubleshooting](/docs/Troubleshooting.md)\n* [Recipes](/docs/Recipes.md)\n* [Articles](/docs/Articles.md)\n* [Videos](/docs/Videos.md)\n* [Credits](/docs/Credits.md)\n* [Support us](/README.md#backers)\n* [Feedback](/docs/Feedback.md)\n* [Change Log](https://github.com/zalmoxisus/redux-devtools-extension/releases)\n"
  },
  {
    "path": "docs/Recipes.md",
    "content": "# Recipes\n\n### Using in a typescript project\n\nThe recommended way is to use [`redux-devtools-extension` npm package](/README.md#13-use-redux-devtools-extension-package-from-npm), which contains all typescript definitions. Or you can just use `window as any`:\n\n```js\nconst store = createStore(\n  rootReducer,\n  initialState,\n  (window as any).__REDUX_DEVTOOLS_EXTENSION__ &&\n    (window as any).__REDUX_DEVTOOLS_EXTENSION__()\n);\n```\nNote that you many need to set `no-any` to false in your `tslint.json` file.\n\nAlternatively you can use typeguard in order to avoid \ncasting to any.\n\n```typescript\nimport { createStore, StoreEnhancer } from \"redux\";\n\n// ... \n\ntype WindowWithDevTools = Window & {\n __REDUX_DEVTOOLS_EXTENSION__: () => StoreEnhancer<unknown, {}>\n}\n\nconst isReduxDevtoolsExtenstionExist = \n(arg: Window | WindowWithDevTools): \n  arg is WindowWithDevTools  => {\n    return  '__REDUX_DEVTOOLS_EXTENSION__' in arg;\n}\n\n// ...\n\nconst store = createStore(rootReducer, initialState,\n  isReduxDevtoolsExtenstionExist(window) ? \n  window.__REDUX_DEVTOOLS_EXTENSION__() : undefined)\n```\n\n### Export from browser console or from application\n\n```js\nstore.liftedStore.getState()\n```\n\nThe extension is not sharing `store` object, so you should take care of that.\n\n### Applying multiple times with different sets of options\n\nWe're [not allowing that from instrumentation part](https://github.com/zalmoxisus/redux-devtools-instrument/blob/master/src/instrument.js#L676), because that would re-dispatch every app action in case we'd have many liftedStores, but there's [a helper for logging only](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/logOnly.js), which can be used it like so:\n\n```js\nimport { createStore, compose } from 'redux';\nimport { devToolsEnhancer } from 'redux-devtools-extension/logOnly';\n\nconst store = createStore(reducer, /* preloadedState, */ compose(\ndevToolsEnhancer({\n  instaceID: 1,\n  name: 'Blacklisted',\n  actionsBlacklist: '...'\n}),\ndevToolsEnhancer({\n  instaceID: 2,\n  name: 'Whitelisted',\n  actionsWhitelist: '...'\n})\n));\n```\n"
  },
  {
    "path": "docs/Troubleshooting.md",
    "content": "# Troubleshooting\n\n### I just see empty log or \"No store found\"\n\nMake sure you [applied the enhancer](https://github.com/zalmoxisus/redux-devtools-extension#2-use-with-redux). Note that passing enhancer as last argument requires redux@>=3.1.0. For older versions apply it like [here](https://github.com/zalmoxisus/redux-devtools-extension/blob/v0.4.2/examples/todomvc/store/configureStore.js) or [here](https://github.com/zalmoxisus/redux-devtools-extension/blob/v0.4.2/examples/counter/store/configureStore.js#L7-L12).\n\nDon't mix the old Redux API with the new one. Pass enhancers and applyMiddleware as last createStore argument.\n\n### Access file url (`file:///`) \n\nIf you develop on your local filesystem, make sure to allow Redux DevTools access to `file:///` URLs in the settings of this extension:\n\n<img width=\"746\" alt=\"extensions\" src=\"https://cloud.githubusercontent.com/assets/7957859/19075220/a0fad99e-8a4c-11e6-8b87-757f2dc179cb.png\">\n\n### It shows only the `@@INIT` action or moving back and forth doesn't update the state\n\nMost likely you mutate the state. Check it by [adding `redux-immutable-state-invariant` middleware](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/counter/store/configureStore.js#L3).\n\n### @@INIT or REPLACE action resets the state of the app or last actions RE-APPLIED\n\n`@@redux/REPLACE` (or `@@INIT`) is used internally when the application is hot reloaded. When you use `store.replaceReducer` the effect will be the same as for hot-reloading, where the extension is recomputing all the history again. To avoid that set [`shouldHotReload`](/docs/API/Arguments.md#shouldhotreload) parameter to `false`. \n\n### It doesn't work with other store enhancers \n\nUsually the extension's store enhancer should be last in the compose. When you're using [`window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__`](/README.md#12-advanced-store-setup) or [`composeWithDevTools`](/README.md#13-use-redux-devtools-extension-package-from-npm) helper you don't have to worry about the enhancers order. However some enhancers ([like `redux-batched-subscribe`](https://github.com/zalmoxisus/redux-devtools-extension/issues/261)) also have this requirement to be the last in the compose. In this case you can use it like so:\n\n```js\nconst store = createStore(reducer, preloadedState, compose(\n  // applyMiddleware(thunk),\n  window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : noop => noop,\n  batchedSubscribe(/* ... */)\n));\n```\n\nWhere `batchedSubscribe` is `redux-batched-subscribe` store enhancer.\n\n### Excessive use of memory and CPU\n\nThat is happening due to serialization of some huge objects included in the state or action. The solution is to [sanitize them](/docs/API/Arguments.md#actionsanitizer--statesanitizer).\n\nYou can do that by including/omitting data containing specific values, having specific types... In the example below we're omitting parts of action and state objects with the key `data` (in case of action only when was dispatched action `FILE_DOWNLOAD_SUCCESS`):\n\n```js\nconst actionSanitizer = (action) => (\n  action.type === 'FILE_DOWNLOAD_SUCCESS' && action.data ?\n  { ...action, data: '<<LONG_BLOB>>' } : action\n);\nconst store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({\n  actionSanitizer,\n  stateSanitizer: (state) => state.data ? { ...state, data: '<<LONG_BLOB>>' } : state\n}));\n```\n\nThere's a more advanced [example on how to implement that for `ui-router`](https://github.com/zalmoxisus/redux-devtools-extension/issues/455#issuecomment-404538385).\n\nThe extension is in different process and cannot access the store object directly, unlike vanilla [`redux-devtools`](https://github.com/reduxjs/redux-devtools) which doesn't have this issue. In case sanitizing doesn't fit your use case, you might consider including it directly as a react component, so there will be no need to serialize the data, but it would add some complexity.\n\n### It fails to serialize data when [passing synthetic events](https://github.com/zalmoxisus/redux-devtools-extension/issues/275) or [calling an action directly with `redux-actions`](https://github.com/zalmoxisus/redux-devtools-extension/issues/287)\n\nReact synthetic event cannot be reused for performance reason. So, it's not possible to serialize event objects you pass to action payloads.\n \n1. The best solution is **not to pass the whole event object to reducers, but the data you need**:\n  ```diff\n  function click(event) {\n    return {\n      type: ELEMENT_CLICKED,\n  -    event: event\n  +    value: event.target.value\n    };\n  }\n  ```\n\n2. If you cannot pick data from the event object or, for some reason, you need the whole object, use `event.persist()` as suggested in [React Docs](https://facebook.github.io/react/docs/events.html#event-pooling), but it will consume RAM while not needed.\n   \n   ```diff\n   function increment(event) {\n   + event.persist();\n     return {\n       type: ELEMENT_CLICKED,\n       event: event,\n     };\n   }\n   ```\n\n3. A workaround, to pass the whole object and at the same time not to persist it, is to override this key of the stringified payload in your action creator. Add a custom `toJSON` function right in the action object (which will be called by the extension before accessing the object):\n   \n   ```diff\n   function increment(event) {\n     return {\n       type: ELEMENT_CLICKED,\n       event: event,\n   +   toJSON: function (){\n   +     return { ...this, event: '[Event]' };\n   +   }\n     };\n   }\n   ```\n   Note that it shouldn't be arrow function as we want to have access to the function's `this`.\n   \n   As we don't have access to the original object, skipping and recomputing actions during hot reloading will not work in this case. We recommend to use the first solution whenever possible.\n\n### Symbols or other unserializable data not shown \n\nTo get data which cannot be serialized by `JSON.stringify`, set [`serialize` parameter](/docs/API/Arguments.md#serialize):\n```js\nconst store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({\n   serialize: true\n}));\n```\n\nIt will handle also date, regex, undefined, error objects, symbols, maps, sets and functions.\n"
  },
  {
    "path": "docs/Videos.md",
    "content": "# Videos\n\n- [Debugging flux applications in production at React Europe 2016](https://youtu.be/YU8jQ2HtqH4)\n- [Hot Reloading with Time Travel at React Europe 2015](https://youtu.be/xsSnOQynTHs)\n- [Getting Started with Redux DevTools Extension](https://egghead.io/lessons/javascript-getting-started-with-redux-dev-tools)\n- [React & Redux With ExpressJS](https://www.youtube.com/watch?v=6ygcbRpZFR4)\n"
  },
  {
    "path": "examples/buildAll.js",
    "content": "/**\n * Runs an ordered set of commands within each of the build directories.\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport { spawnSync } from 'child_process';\n\nvar exampleDirs = fs.readdirSync(__dirname).filter((file) => {\n  return fs.statSync(path.join(__dirname, file)).isDirectory();\n});\n\n// Ordering is important here. `npm install` must come first.\nvar cmdArgs = [\n  { cmd: 'npm', args: ['install'] },\n  { cmd: 'webpack', args: ['index.js'] }\n];\n\nfor (const dir of exampleDirs) {\n  for (const cmdArg of cmdArgs) {\n    // declare opts in this scope to avoid https://github.com/joyent/node/issues/9158\n    const opts = {\n      cwd: path.join(__dirname, dir),\n      stdio: 'inherit'\n    };\n    let result = {};\n    if (process.platform === 'win32') {\n      result = spawnSync(cmdArg.cmd + '.cmd', cmdArg.args, opts);\n    } else {\n      result = spawnSync(cmdArg.cmd, cmdArg.args, opts);\n    }\n    if (result.status !== 0) {\n      throw new Error('Building examples exited with non-zero');\n    }\n  }\n}\n"
  },
  {
    "path": "examples/counter/.babelrc",
    "content": "{\n  \"presets\": [ \"es2015\", \"stage-0\", \"react\" ]\n}\n"
  },
  {
    "path": "examples/counter/actions/counter.js",
    "content": "export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';\nexport const DECREMENT_COUNTER = 'DECREMENT_COUNTER';\n\nlet t;\n\nexport function increment() {\n  return {\n    type: INCREMENT_COUNTER\n  };\n}\n\nexport function decrement() {\n  return {\n    type: DECREMENT_COUNTER\n  };\n}\n\nexport function autoIncrement(delay = 10) {\n  return dispatch => {\n    if (t) {\n      clearInterval(t);\n      t = undefined;\n      return;\n    }\n    t = setInterval(() => {\n      dispatch(increment());\n    }, delay);\n  };\n}\n\nexport function incrementAsync(delay = 1000) {\n  return dispatch => {\n    setTimeout(() => {\n      dispatch(increment());\n    }, delay);\n  };\n}\n"
  },
  {
    "path": "examples/counter/components/Counter.js",
    "content": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\n\nclass Counter extends Component {\n  render() {\n    const { increment, autoIncrement, incrementAsync, decrement, counter } = this.props;\n    return (\n      <p>\n        Clicked: {counter} times\n        {' '}\n        <button onClick={increment}>+</button>\n        {' '}\n        <button onClick={decrement}>-</button>\n        {' '}\n        <button onClick={incrementAsync}>Increment async</button>\n        {' '}\n        <button onClick={autoIncrement}>Auto increment</button>\n      </p>\n    );\n  }\n}\n\nCounter.propTypes = {\n  increment: PropTypes.func.isRequired,\n  autoIncrement: PropTypes.func.isRequired,\n  incrementAsync: PropTypes.func.isRequired,\n  decrement: PropTypes.func.isRequired,\n  counter: PropTypes.number.isRequired\n};\n\nexport default Counter;\n"
  },
  {
    "path": "examples/counter/containers/App.js",
    "content": "import { bindActionCreators } from 'redux';\nimport { connect } from 'react-redux';\nimport Counter from '../components/Counter';\nimport * as CounterActions from '../actions/counter';\n\nfunction mapStateToProps(state) {\n  return {\n    counter: state.counter\n  };\n}\n\nfunction mapDispatchToProps(dispatch) {\n  return bindActionCreators(CounterActions, dispatch);\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)(Counter);\n"
  },
  {
    "path": "examples/counter/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Redux counter example</title>\n  </head>\n  <body>\n    <div id=\"root\">\n    </div>\n    <script src=\"/static/bundle.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/counter/index.js",
    "content": "import React from 'react';\nimport { render } from 'react-dom';\nimport { Provider } from 'react-redux';\nimport App from './containers/App';\nimport configureStore from './store/configureStore';\n\nconst store = configureStore();\n\nrender(\n  <Provider store={store}>\n    <App />\n  </Provider>,\n  document.getElementById('root')\n);\n"
  },
  {
    "path": "examples/counter/package.json",
    "content": "{\n  \"name\": \"redux-counter-example\",\n  \"version\": \"0.0.0\",\n  \"description\": \"Redux counter example\",\n  \"scripts\": {\n    \"start\": \"node server.js\",\n    \"test\": \"NODE_ENV=test mocha --recursive --compilers js:babel-core/register --require ./test/setup.js\",\n    \"test:watch\": \"npm test -- --watch\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/rackt/redux.git\"\n  },\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/rackt/redux/issues\"\n  },\n  \"homepage\": \"http://rackt.github.io/redux\",\n  \"dependencies\": {\n    \"prop-types\": \"^15.6.2\",\n    \"react\": \"^16.7.0\",\n    \"react-dom\": \"^16.7.0\",\n    \"react-redux\": \"^6.0.0\",\n    \"redux\": \"^4.0.1\",\n    \"redux-devtools-extension\": \"^2.13.7\",\n    \"redux-thunk\": \"^2.3.0\"\n  },\n  \"devDependencies\": {\n    \"babel-cli\": \"^6.3.17\",\n    \"babel-core\": \"^6.3.17\",\n    \"babel-loader\": \"^7.0.0\",\n    \"babel-preset-es2015\": \"^6.0.0\",\n    \"babel-preset-react\": \"6.3.13\",\n    \"babel-preset-stage-0\": \"^6.3.13\",\n    \"express\": \"^4.13.3\",\n    \"redux-immutable-state-invariant\": \"^2.1.0\",\n    \"webpack\": \"^4.0.0\",\n    \"webpack-dev-server\": \"^3.0.0\",\n    \"webpack-hot-middleware\": \"^2.2.0\"\n  }\n}\n"
  },
  {
    "path": "examples/counter/reducers/counter.js",
    "content": "import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions/counter';\n\nexport default function counter(state = 0, action) {\n  switch (action.type) {\n  case INCREMENT_COUNTER:\n    return state + 1;\n  case DECREMENT_COUNTER:\n    return state - 1;\n  default:\n    return state;\n  }\n}\n"
  },
  {
    "path": "examples/counter/reducers/index.js",
    "content": "import { combineReducers } from 'redux';\nimport counter from './counter';\n\nconst rootReducer = combineReducers({\n  counter\n});\n\nexport default rootReducer;\n"
  },
  {
    "path": "examples/counter/server.js",
    "content": "var webpack = require('webpack');\nvar webpackDevMiddleware = require('webpack-dev-middleware');\nvar webpackHotMiddleware = require('webpack-hot-middleware');\nvar config = require('./webpack.config');\n\nvar app = new require('express')();\nvar port = 4001;\n\nvar compiler = webpack(config);\napp.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));\napp.use(webpackHotMiddleware(compiler));\n\napp.get(\"/\", function(req, res) {\n  res.sendFile(__dirname + '/index.html');\n});\n\napp.listen(port, function(error) {\n  if (error) {\n    console.error(error);\n  } else {\n    console.info(\"==> 🌎  Listening on port %s. Open up http://localhost:%s/ in your browser.\", port, port);\n  }\n});\n"
  },
  {
    "path": "examples/counter/store/configureStore.js",
    "content": "import { createStore, applyMiddleware, compose } from 'redux';\nimport { composeWithDevTools } from 'redux-devtools-extension';\nimport thunk from 'redux-thunk';\nimport invariant from 'redux-immutable-state-invariant';\nimport reducer from '../reducers';\nimport * as actionCreators from '../actions/counter'; \n\nexport default function configureStore(preloadedState) {\n  const composeEnhancers = composeWithDevTools({ actionCreators, trace: true, traceLimit: 25 });\n  const store = createStore(reducer, preloadedState, composeEnhancers(\n    applyMiddleware(invariant(), thunk)\n  ));\n\n  if (module.hot) {\n    // Enable Webpack hot module replacement for reducers\n    module.hot.accept('../reducers', () => {\n      store.replaceReducer(require('../reducers').default)\n    });\n  }\n\n  return store;\n}\n"
  },
  {
    "path": "examples/counter/test/actions/counter.spec.js",
    "content": "import expect from 'expect';\nimport { applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\nimport * as actions from '../../actions/counter';\n\nconst middlewares = [thunk];\n\n/*\n * Creates a mock of Redux store with middleware.\n */\nfunction mockStore(getState, expectedActions, onLastAction) {\n  if (!Array.isArray(expectedActions)) {\n    throw new Error('expectedActions should be an array of expected actions.');\n  }\n  if (typeof onLastAction !== 'undefined' && typeof onLastAction !== 'function') {\n    throw new Error('onLastAction should either be undefined or function.');\n  }\n\n  function mockStoreWithoutMiddleware() {\n    return {\n      getState() {\n        return typeof getState === 'function' ?\n          getState() :\n          getState;\n      },\n\n      dispatch(action) {\n        const expectedAction = expectedActions.shift();\n        expect(action).toEqual(expectedAction);\n        if (onLastAction && !expectedActions.length) {\n          onLastAction();\n        }\n        return action;\n      }\n    };\n  }\n\n  const mockStoreWithMiddleware = applyMiddleware(\n    ...middlewares\n  )(mockStoreWithoutMiddleware);\n\n  return mockStoreWithMiddleware();\n}\n\ndescribe('actions', () => {\n  it('increment should create increment action', () => {\n    expect(actions.increment()).toEqual({ type: actions.INCREMENT_COUNTER });\n  });\n\n  it('decrement should create decrement action', () => {\n    expect(actions.decrement()).toEqual({ type: actions.DECREMENT_COUNTER });\n  });\n\n  it('incrementIfOdd should create increment action', (done) => {\n    const expectedActions = [\n      { type: actions.INCREMENT_COUNTER }\n    ];\n    const store = mockStore({ counter: 1 }, expectedActions, done);\n    store.dispatch(actions.incrementIfOdd());\n  });\n\n  it('incrementIfOdd shouldnt create increment action if counter is even', (done) => {\n    const expectedActions = [];\n    const store = mockStore({ counter: 2 }, expectedActions);\n    store.dispatch(actions.incrementIfOdd());\n    done();\n  });\n\n  it('incrementAsync should create increment action', (done) => {\n    const expectedActions = [\n      { type: actions.INCREMENT_COUNTER }\n    ];\n    const store = mockStore({ counter: 0 }, expectedActions, done);\n    store.dispatch(actions.incrementAsync(100));\n  });\n});\n"
  },
  {
    "path": "examples/counter/test/components/Counter.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport Counter from '../../components/Counter';\n\nfunction setup() {\n  const actions = {\n    increment: expect.createSpy(),\n    incrementIfOdd: expect.createSpy(),\n    incrementAsync: expect.createSpy(),\n    decrement: expect.createSpy()\n  };\n  const component = TestUtils.renderIntoDocument(<Counter counter={1} {...actions} />);\n  return {\n    component: component,\n    actions: actions,\n    buttons: TestUtils.scryRenderedDOMComponentsWithTag(component, 'button'),\n    p: TestUtils.findRenderedDOMComponentWithTag(component, 'p')\n  };\n}\n\ndescribe('Counter component', () => {\n  it('should display count', () => {\n    const { p } = setup();\n    expect(p.textContent).toMatch(/^Clicked: 1 times/);\n  });\n\n  it('first button should call increment', () => {\n    const { buttons, actions } = setup();\n    TestUtils.Simulate.click(buttons[0]);\n    expect(actions.increment).toHaveBeenCalled();\n  });\n\n  it('second button should call decrement', () => {\n    const { buttons, actions } = setup();\n    TestUtils.Simulate.click(buttons[1]);\n    expect(actions.decrement).toHaveBeenCalled();\n  });\n\n  it('third button should call incrementIfOdd', () => {\n    const { buttons, actions } = setup();\n    TestUtils.Simulate.click(buttons[2]);\n    expect(actions.incrementIfOdd).toHaveBeenCalled();\n  });\n\n  it('fourth button should call incrementAsync', () => {\n    const { buttons, actions } = setup();\n    TestUtils.Simulate.click(buttons[3]);\n    expect(actions.incrementAsync).toHaveBeenCalled();\n  });\n});\n"
  },
  {
    "path": "examples/counter/test/containers/App.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport { Provider } from 'react-redux';\nimport App from '../../containers/App';\nimport configureStore from '../../store/configureStore';\n\nfunction setup(initialState) {\n  const store = configureStore(initialState);\n  const app = TestUtils.renderIntoDocument(\n    <Provider store={store}>\n      <App />\n    </Provider>\n  );\n  return {\n    app: app,\n    buttons: TestUtils.scryRenderedDOMComponentsWithTag(app, 'button'),\n    p: TestUtils.findRenderedDOMComponentWithTag(app, 'p')\n  };\n}\n\ndescribe('containers', () => {\n  describe('App', () => {\n    it('should display initial count', () => {\n      const { p } = setup();\n      expect(p.textContent).toMatch(/^Clicked: 0 times/);\n    });\n\n    it('should display updated count after increment button click', () => {\n      const { buttons, p } = setup();\n      TestUtils.Simulate.click(buttons[0]);\n      expect(p.textContent).toMatch(/^Clicked: 1 times/);\n    });\n\n    it('should display updated count after decrement button click', () => {\n      const { buttons, p } = setup();\n      TestUtils.Simulate.click(buttons[1]);\n      expect(p.textContent).toMatch(/^Clicked: -1 times/);\n    });\n\n    it('shouldnt change if even and if odd button clicked', () => {\n      const { buttons, p } = setup();\n      TestUtils.Simulate.click(buttons[2]);\n      expect(p.textContent).toMatch(/^Clicked: 0 times/);\n    });\n\n    it('should change if odd and if odd button clicked', () => {\n      const { buttons, p } = setup({ counter: 1 });\n      TestUtils.Simulate.click(buttons[2]);\n      expect(p.textContent).toMatch(/^Clicked: 2 times/);\n    });\n  });\n});\n"
  },
  {
    "path": "examples/counter/test/reducers/counter.spec.js",
    "content": "import expect from 'expect';\nimport counter from '../../reducers/counter';\nimport { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../../actions/counter';\n\ndescribe('reducers', () => {\n  describe('counter', () => {\n    it('should handle initial state', () => {\n      expect(counter(undefined, {})).toBe(0);\n    });\n\n    it('should handle INCREMENT_COUNTER', () => {\n      expect(counter(1, { type: INCREMENT_COUNTER })).toBe(2);\n    });\n\n    it('should handle DECREMENT_COUNTER', () => {\n      expect(counter(1, { type: DECREMENT_COUNTER })).toBe(0);\n    });\n\n    it('should handle unknown action type', () => {\n      expect(counter(1, { type: 'unknown' })).toBe(1);\n    });\n  });\n});\n"
  },
  {
    "path": "examples/counter/test/setup.js",
    "content": "import { jsdom } from 'jsdom';\n\nglobal.document = jsdom('<!doctype html><html><body></body></html>');\nglobal.window = document.defaultView;\nglobal.navigator = global.window.navigator;\n"
  },
  {
    "path": "examples/counter/webpack.config.js",
    "content": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n  mode: 'development',\n  devtool: 'source-map',\n  entry: [\n    'webpack-hot-middleware/client',\n    './index'\n  ],\n  output: {\n    path: path.join(__dirname, 'dist'),\n    filename: 'bundle.js',\n    publicPath: '/static/'\n  },\n  plugins: [\n    new webpack.HotModuleReplacementPlugin()\n  ],\n  module: {\n    rules: [{\n      test: /\\.js$/,\n      loaders: ['babel-loader'],\n      exclude: /node_modules/\n    }]\n  }\n};\n"
  },
  {
    "path": "examples/react-counter-messaging/.babelrc",
    "content": "{\n  \"presets\": [ \"es2015\", \"stage-0\", \"react\" ]\n}\n"
  },
  {
    "path": "examples/react-counter-messaging/components/Counter.js",
    "content": "import React, { Component } from 'react';\n\nconst withDevTools = (\n  // process.env.NODE_ENV === 'development' &&\n  typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__\n);\n\nclass Counter extends Component {\n  constructor() {\n    super();\n    this.state = { counter: 0 };\n    \n    this.increment = this.increment.bind(this);\n    this.decrement = this.decrement.bind(this);\n  }\n\n  componentWillMount() {\n    if (withDevTools) {\n      this.devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect();\n      this.unsubscribe = this.devTools.subscribe((message) => {\n        // Implement monitors actions.\n        // For example time traveling:\n        if (message.type === 'DISPATCH' && message.payload.type === 'JUMP_TO_STATE') {\n          this.setState(message.state);\n        }\n      });\n    }\n  }\n\n  componentWillUnmount() {\n    if (withDevTools) {\n      this.unsubscribe(); // Use if you have other subscribers from other components.\n      window.__REDUX_DEVTOOLS_EXTENSION__.disconnect(); // If there aren't other subscribers.\n    }\n  }\n\n  increment() {\n    const state = { counter: this.state.counter + 1 };\n    if (withDevTools) this.devTools.send('increment', state);\n    this.setState(state);\n  }\n\n  decrement() {\n    const state = { counter: this.state.counter - 1 };\n    if (withDevTools) this.devTools.send('decrement', state);\n    this.setState(state);\n  }\n\n  render() {\n    const { counter } = this.state;\n    return (\n      <p>\n        Clicked: {counter} times\n        {' '}\n        <button onClick={this.increment}>+</button>\n        {' '}\n        <button onClick={this.decrement}>-</button>\n      </p>\n    );\n  }\n}\n\nexport default Counter;\n"
  },
  {
    "path": "examples/react-counter-messaging/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>React counter example</title>\n  </head>\n  <body>\n    <div id=\"root\">\n    </div>\n    <script src=\"/static/bundle.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/react-counter-messaging/index.js",
    "content": "import React from 'react';\nimport { render } from 'react-dom';\nimport Counter from './components/Counter';\n\nrender(\n  <Counter />,\n  document.getElementById('root')\n);\n"
  },
  {
    "path": "examples/react-counter-messaging/package.json",
    "content": "{\n  \"name\": \"react-counter-example\",\n  \"version\": \"0.0.0\",\n  \"description\": \"React counter example\",\n  \"scripts\": {\n    \"start\": \"webpack-dev-server --progress\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/zalmoxisus/redux-devtools-extension.git\"\n  },\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/zalmoxisus/redux-devtools-extension/issues\"\n  },\n  \"homepage\": \"https://github.com/zalmoxisus/redux-devtools-extension\",\n  \"dependencies\": {\n    \"react\": \"^16.0.0\",\n    \"react-dom\": \"^16.0.0\"\n  },\n  \"devDependencies\": {\n    \"babel-cli\": \"^6.3.17\",\n    \"babel-core\": \"^6.3.17\",\n    \"babel-loader\": \"^7.0.0\",\n    \"babel-preset-es2015\": \"^6.0.0\",\n    \"babel-preset-react\": \"6.3.13\",\n    \"babel-preset-stage-0\": \"^6.3.13\",\n    \"webpack\": \"^4.0.0\",\n    \"webpack-cli\": \"^3.2.0\",\n    \"webpack-dev-server\": \"^3.0.0\",\n    \"webpack-hot-middleware\": \"^2.2.0\"\n  }\n}\n"
  },
  {
    "path": "examples/react-counter-messaging/webpack.config.js",
    "content": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n  mode: 'development',\n  devtool: 'source-map',\n  entry: [\n    './index'\n  ],\n  output: {\n    path: path.join(__dirname, 'dist'),\n    filename: 'bundle.js',\n    publicPath: '/static/'\n  },\n  module: {\n    rules: [{\n      test: /\\.js$/,\n      loaders: ['babel-loader'],\n      exclude: /node_modules/\n    }]\n  },\n  devServer: {\n    port: 4004\n  }\n};\n"
  },
  {
    "path": "examples/router/.babelrc",
    "content": "{\n  \"presets\": [ \"es2015\", \"stage-0\", \"react\" ],\n  \"plugins\": [ \"add-module-exports\", \"transform-decorators-legacy\" ]\n}"
  },
  {
    "path": "examples/router/actions/todos.js",
    "content": "import * as types from '../constants/ActionTypes';\n\nexport function addTodo(text) {\n  return { type: types.ADD_TODO, text };\n}\n\nexport function deleteTodo(id) {\n  return { type: types.DELETE_TODO, id };\n}\n\nexport function editTodo(id, text) {\n  return { type: types.EDIT_TODO, id, text };\n}\n\nexport function completeTodo(id) {\n  return { type: types.COMPLETE_TODO, id };\n}\n\nexport function completeAll() {\n  return { type: types.COMPLETE_ALL };\n}\n\nexport function clearCompleted() {\n  return { type: types.CLEAR_COMPLETED };\n}\n"
  },
  {
    "path": "examples/router/components/Footer.js",
    "content": "import React, { PropTypes, Component } from 'react';\nimport classnames from 'classnames';\nimport { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters';\n\nconst FILTER_TITLES = {\n  [SHOW_ALL]: 'All',\n  [SHOW_ACTIVE]: 'Active',\n  [SHOW_COMPLETED]: 'Completed'\n};\n\nclass Footer extends Component {\n  renderTodoCount() {\n    const { activeCount } = this.props;\n    const itemWord = activeCount === 1 ? 'item' : 'items';\n\n    return (\n      <span className=\"todo-count\">\n        <strong>{activeCount || 'No'}</strong> {itemWord} left\n      </span>\n    );\n  }\n\n  renderFilterLink(filter) {\n    const title = FILTER_TITLES[filter];\n    const { filter: selectedFilter, onShow } = this.props;\n\n    return (\n      <a className={classnames({ selected: filter === selectedFilter })}\n         style={{ cursor: 'pointer' }}\n         onClick={() => onShow(filter)}>\n        {title}\n      </a>\n    );\n  }\n\n  renderClearButton() {\n    const { completedCount, onClearCompleted } = this.props;\n    if (completedCount > 0) {\n      return (\n        <button className=\"clear-completed\"\n                onClick={onClearCompleted} >\n          Clear completed\n        </button>\n      );\n    }\n  }\n\n  render() {\n    return (\n      <footer className=\"footer\">\n        {this.renderTodoCount()}\n        <ul className=\"filters\">\n          {[SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED].map(filter =>\n            <li key={filter}>\n              {this.renderFilterLink(filter)}\n            </li>\n          )}\n        </ul>\n        {this.renderClearButton()}\n      </footer>\n    );\n  }\n}\n\nFooter.propTypes = {\n  completedCount: PropTypes.number.isRequired,\n  activeCount: PropTypes.number.isRequired,\n  filter: PropTypes.string.isRequired,\n  onClearCompleted: PropTypes.func.isRequired,\n  onShow: PropTypes.func.isRequired\n};\n\nexport default Footer;\n"
  },
  {
    "path": "examples/router/components/Header.js",
    "content": "import React, { PropTypes, Component } from 'react';\nimport TodoTextInput from './TodoTextInput';\n\nclass Header extends Component {\n  handleSave(text) {\n    if (text.length !== 0) {\n      this.props.addTodo(text);\n    }\n  }\n\n  render() {\n    const { path } = this.props;\n    return (\n      <header className=\"header\">\n          <h1 style={{ fontSize: 80 }}>{path}</h1>\n          <TodoTextInput newTodo\n                         onSave={this.handleSave.bind(this)}\n                         placeholder=\"What needs to be done?\" />\n      </header>\n    );\n  }\n}\n\nHeader.propTypes = {\n  addTodo: PropTypes.func.isRequired\n};\n\nexport default Header;\n"
  },
  {
    "path": "examples/router/components/MainSection.js",
    "content": "import React, { Component, PropTypes } from 'react';\nimport TodoItem from './TodoItem';\nimport Footer from './Footer';\nimport { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters';\n\nconst TODO_FILTERS = {\n  [SHOW_ALL]: () => true,\n  [SHOW_ACTIVE]: todo => !todo.completed,\n  [SHOW_COMPLETED]: todo => todo.completed\n};\n\nclass MainSection extends Component {\n  constructor(props, context) {\n    super(props, context);\n    this.state = { filter: SHOW_ALL };\n  }\n\n  handleClearCompleted() {\n    const atLeastOneCompleted = this.props.todos.some(todo => todo.completed);\n    if (atLeastOneCompleted) {\n      this.props.actions.clearCompleted();\n    }\n  }\n\n  handleShow(filter) {\n    this.setState({ filter });\n  }\n\n  renderToggleAll(completedCount) {\n    const { todos, actions } = this.props;\n    if (todos.length > 0) {\n      return (\n        <input className=\"toggle-all\"\n               type=\"checkbox\"\n               checked={completedCount === todos.length}\n               onChange={actions.completeAll} />\n      );\n    }\n  }\n\n  renderFooter(completedCount) {\n    const { todos } = this.props;\n    const { filter } = this.state;\n    const activeCount = todos.length - completedCount;\n\n    if (todos.length) {\n      return (\n        <Footer completedCount={completedCount}\n                activeCount={activeCount}\n                filter={filter}\n                onClearCompleted={this.handleClearCompleted.bind(this)}\n                onShow={this.handleShow.bind(this)} />\n      );\n    }\n  }\n\n  render() {\n    const { todos, actions } = this.props;\n    const { filter } = this.state;\n\n    const filteredTodos = todos.filter(TODO_FILTERS[filter]);\n    const completedCount = todos.reduce((count, todo) =>\n      todo.completed ? count + 1 : count,\n      0\n    );\n\n    return (\n      <section className=\"main\">\n        {this.renderToggleAll(completedCount)}\n        <ul className=\"todo-list\">\n          {filteredTodos.map(todo =>\n            <TodoItem key={todo.id} todo={todo} {...actions} />\n          )}\n        </ul>\n        {this.renderFooter(completedCount)}\n      </section>\n    );\n  }\n}\n\nMainSection.propTypes = {\n  todos: PropTypes.array.isRequired,\n  actions: PropTypes.object.isRequired\n};\n\nexport default MainSection;\n"
  },
  {
    "path": "examples/router/components/TodoItem.js",
    "content": "import React, { Component, PropTypes } from 'react';\nimport classnames from 'classnames';\nimport TodoTextInput from './TodoTextInput';\n\nclass TodoItem extends Component {\n  constructor(props, context) {\n    super(props, context);\n    this.state = {\n      editing: false\n    };\n  }\n\n  handleDoubleClick() {\n    this.setState({ editing: true });\n  }\n\n  handleSave(id, text) {\n    if (text.length === 0) {\n      this.props.deleteTodo(id);\n    } else {\n      this.props.editTodo(id, text);\n    }\n    this.setState({ editing: false });\n  }\n\n  render() {\n    const {todo, completeTodo, deleteTodo} = this.props;\n\n    let element;\n    if (this.state.editing) {\n      element = (\n        <TodoTextInput text={todo.text}\n                       editing={this.state.editing}\n                       onSave={(text) => this.handleSave(todo.id, text)} />\n      );\n    } else {\n      element = (\n        <div className=\"view\">\n          <input className=\"toggle\"\n                 type=\"checkbox\"\n                 checked={todo.completed}\n                 onChange={() => completeTodo(todo.id)} />\n          <label onDoubleClick={this.handleDoubleClick.bind(this)}>\n            {todo.text}\n          </label>\n          <button className=\"destroy\"\n                  onClick={() => deleteTodo(todo.id)} />\n        </div>\n      );\n    }\n\n    return (\n      <li className={classnames({\n        completed: todo.completed,\n        editing: this.state.editing\n      })}>\n        {element}\n      </li>\n    );\n  }\n}\n\nTodoItem.propTypes = {\n  todo: PropTypes.object.isRequired,\n  editTodo: PropTypes.func.isRequired,\n  deleteTodo: PropTypes.func.isRequired,\n  completeTodo: PropTypes.func.isRequired\n};\n\nexport default TodoItem;\n"
  },
  {
    "path": "examples/router/components/TodoTextInput.js",
    "content": "import React, { Component, PropTypes } from 'react';\nimport classnames from 'classnames';\n\nclass TodoTextInput extends Component {\n  constructor(props, context) {\n    super(props, context);\n    this.state = {\n      text: this.props.text || ''\n    };\n  }\n\n  handleSubmit(e) {\n    const text = e.target.value.trim();\n    if (e.which === 13) {\n      this.props.onSave(text);\n      if (this.props.newTodo) {\n        this.setState({ text: '' });\n      }\n    }\n  }\n\n  handleChange(e) {\n    this.setState({ text: e.target.value });\n  }\n\n  handleBlur(e) {\n    if (!this.props.newTodo) {\n      this.props.onSave(e.target.value);\n    }\n  }\n\n  render() {\n    return (\n      <input className={\n        classnames({\n          edit: this.props.editing,\n          'new-todo': this.props.newTodo\n        })}\n        type=\"text\"\n        placeholder={this.props.placeholder}\n        autoFocus=\"true\"\n        value={this.state.text}\n        onBlur={this.handleBlur.bind(this)}\n        onChange={this.handleChange.bind(this)}\n        onKeyDown={this.handleSubmit.bind(this)} />\n    );\n  }\n}\n\nTodoTextInput.propTypes = {\n  onSave: PropTypes.func.isRequired,\n  text: PropTypes.string,\n  placeholder: PropTypes.string,\n  editing: PropTypes.bool,\n  newTodo: PropTypes.bool\n};\n\nexport default TodoTextInput;\n"
  },
  {
    "path": "examples/router/constants/ActionTypes.js",
    "content": "export const ADD_TODO = 'ADD_TODO';\nexport const DELETE_TODO = 'DELETE_TODO';\nexport const EDIT_TODO = 'EDIT_TODO';\nexport const COMPLETE_TODO = 'COMPLETE_TODO';\nexport const COMPLETE_ALL = 'COMPLETE_ALL';\nexport const CLEAR_COMPLETED = 'CLEAR_COMPLETED';\n"
  },
  {
    "path": "examples/router/constants/TodoFilters.js",
    "content": "export const SHOW_ALL = 'show_all';\nexport const SHOW_COMPLETED = 'show_completed';\nexport const SHOW_ACTIVE = 'show_active';\n"
  },
  {
    "path": "examples/router/containers/App.js",
    "content": "import React, { Component, PropTypes } from 'react';\nimport { bindActionCreators } from 'redux';\nimport { connect } from 'react-redux';\nimport Header from '../components/Header';\nimport MainSection from '../components/MainSection';\nimport * as TodoActions from '../actions/todos';\n\nclass App extends Component {\n  render() {\n    const { todos, path, actions } = this.props;\n    return (\n      <div>\n        <Header addTodo={actions.addTodo} path={path} />\n        <MainSection todos={todos} actions={actions} />\n      </div>\n    );\n  }\n}\n\nApp.propTypes = {\n  todos: PropTypes.array.isRequired,\n  actions: PropTypes.object.isRequired\n};\n\nfunction mapStateToProps(state) {\n  return {\n    todos: state.todos,\n    path: state.router.location.pathname\n  };\n}\n\nfunction mapDispatchToProps(dispatch) {\n  return {\n    actions: bindActionCreators(TodoActions, dispatch)\n  };\n}\n\nexport default connect(\n  mapStateToProps,\n  mapDispatchToProps\n)(App);\n"
  },
  {
    "path": "examples/router/containers/Root.js",
    "content": "import React, { Component, PropTypes } from 'react';\nimport { Provider } from 'react-redux';\nimport { Route, Redirect } from 'react-router';\nimport { ReduxRouter } from 'redux-router';\nimport Wrapper from './Wrapper';\nimport App from './App';\n\nclass Root extends Component {\n  render() {\n    return (\n      <ReduxRouter>\n        <Redirect from=\"/\" to=\"Standard Todo\"/>\n        <Route path=\"/\" component={Wrapper}>\n          <Route path=\"/:id\" component={App}/>\n        </Route>\n      </ReduxRouter>\n    );\n  }\n}\n\nexport default Root;\n"
  },
  {
    "path": "examples/router/containers/Wrapper.js",
    "content": "import React, { Component, PropTypes } from 'react';\nimport { bindActionCreators } from 'redux';\nimport { connect } from 'react-redux';\nimport { pushState } from 'redux-router';\nimport { Route, Link } from 'react-router';\nimport * as TodoActions from '../actions/todos';\n\nfunction mapDispatchToProps(dispatch) {\n  return {\n    pushState: bindActionCreators(pushState, dispatch),\n    actions: bindActionCreators(TodoActions, dispatch)\n  };\n}\n\n@connect((state) => ({}), mapDispatchToProps)\nclass Wrapper extends Component {\n  static propTypes = {\n    children: PropTypes.node\n  };\n\n  constructor(props) {\n    super(props);\n    this.handleClick = this.handleClick.bind(this);\n  }\n\n  handleClick(event) {\n    event.preventDefault();\n    const { actions, pushState } = this.props;\n    const path = event.target.innerText;\n\n    pushState(null, path);\n    console.log('Navigate to', path);\n\n    if (this.timeout) clearInterval(this.timeout);\n    if (path === 'AutoTodo') {\n      console.log('!');\n      this.timeout = setInterval(() => {\n        actions.addTodo('Auto generated task');\n      }, 100);\n    }\n  }\n\n  render() {\n\n    return (\n      <div>\n        <div style={{ padding: 20, backgroundColor: '#eee', fontWeight: 'bold', textAlign: 'center' }}>\n          <a href=\"#\" onClick={this.handleClick}>Standard Todo</a> | <a href=\"#\" onClick={this.handleClick}>AutoTodo</a>\n        </div>\n        {this.props.children}\n      </div>\n    );\n  }\n}\n\nexport default Wrapper;\n"
  },
  {
    "path": "examples/router/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Redux TodoMVC example</title>\n  </head>\n  <body>\n    <div class=\"todoapp\" id=\"root\">\n    </div>\n    <script src=\"/static/bundle.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/router/index.js",
    "content": "import 'babel-polyfill';\nimport React from 'react';\nimport { render } from 'react-dom';\nimport { Provider } from 'react-redux';\nimport Root from './containers/Root';\nimport configureStore from './store/configureStore';\nimport 'todomvc-app-css/index.css';\n\nconst store = configureStore();\n\nrender(\n  <Provider store={store}>\n    <Root />\n  </Provider>,\n  document.getElementById('root')\n);\n"
  },
  {
    "path": "examples/router/package.json",
    "content": "{\n  \"name\": \"redux-todomvc-example\",\n  \"version\": \"0.0.0\",\n  \"description\": \"Redux TodoMVC example\",\n  \"scripts\": {\n    \"start\": \"node server.js\",\n    \"test\": \"NODE_ENV=test mocha --recursive --compilers js:babel-core/register --require ./test/setup.js\",\n    \"test:watch\": \"npm test -- --watch\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/rackt/redux.git\"\n  },\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/rackt/redux/issues\"\n  },\n  \"homepage\": \"http://rackt.github.io/redux\",\n  \"dependencies\": {\n    \"classnames\": \"^2.1.2\",\n    \"history\": \"^1.13.1\",\n    \"react\": \"^0.14.0\",\n    \"react-dom\": \"^0.14.0\",\n    \"react-redux\": \"^4.0.0\",\n    \"react-router\": \"^1.0.2\",\n    \"redux\": \"^3.0.0\",\n    \"redux-router\": \"^1.0.0-beta5\"\n  },\n  \"devDependencies\": {\n    \"babel-core\": \"^6.3.15\",\n    \"babel-loader\": \"^6.2.0\",\n    \"babel-plugin-add-module-exports\": \"^0.1.1\",\n    \"babel-plugin-react-transform\": \"^2.0.0-beta1\",\n    \"babel-plugin-transform-decorators-legacy\": \"^1.2.0\",\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    \"expect\": \"^1.8.0\",\n    \"express\": \"^4.13.3\",\n    \"jsdom\": \"^5.6.1\",\n    \"mocha\": \"^2.2.5\",\n    \"node-libs-browser\": \"^0.5.2\",\n    \"raw-loader\": \"^0.5.1\",\n    \"react-addons-test-utils\": \"^0.14.0\",\n    \"react-transform-hmr\": \"^1.0.0\",\n    \"style-loader\": \"^0.12.3\",\n    \"todomvc-app-css\": \"^2.0.1\",\n    \"webpack\": \"^1.9.11\",\n    \"webpack-dev-middleware\": \"^1.2.0\",\n    \"webpack-hot-middleware\": \"^2.2.0\"\n  }\n}\n"
  },
  {
    "path": "examples/router/reducers/index.js",
    "content": "import { combineReducers } from 'redux';\nimport { routerStateReducer } from 'redux-router';\nimport todos from './todos';\n\nconst rootReducer = combineReducers({\n  todos,\n  router: routerStateReducer\n});\n\nexport default rootReducer;\n"
  },
  {
    "path": "examples/router/reducers/todos.js",
    "content": "import { ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO, COMPLETE_ALL, CLEAR_COMPLETED } from '../constants/ActionTypes';\n\nconst initialState = [{\n  text: 'Use Redux',\n  completed: false,\n  id: 0\n}];\n\nexport default function todos(state = initialState, action) {\n  switch (action.type) {\n  case ADD_TODO:\n    return [{\n      id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,\n      completed: false,\n      text: action.text\n    }, ...state];\n\n  case DELETE_TODO:\n    return state.filter(todo =>\n      todo.id !== action.id\n    );\n\n  case EDIT_TODO:\n    return state.map(todo =>\n      todo.id === action.id ?\n        Object.assign({}, todo, { text: action.text }) :\n        todo\n    );\n\n  case COMPLETE_TODO:\n    return state.map(todo =>\n      todo.id === action.id ?\n        Object.assign({}, todo, { completed: !todo.completed }) :\n        todo\n    );\n\n  case COMPLETE_ALL:\n    const areAllMarked = state.every(todo => todo.completed);\n    return state.map(todo => Object.assign({}, todo, {\n      completed: !areAllMarked\n    }));\n\n  case CLEAR_COMPLETED:\n    return state.filter(todo => todo.completed === false);\n\n  default:\n    return state;\n  }\n}\n"
  },
  {
    "path": "examples/router/server.js",
    "content": "var webpack = require('webpack');\nvar webpackDevMiddleware = require('webpack-dev-middleware');\nvar webpackHotMiddleware = require('webpack-hot-middleware');\nvar config = require('./webpack.config');\n\nvar app = new require('express')();\nvar port = 4002;\n\nvar compiler = webpack(config);\napp.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));\napp.use(webpackHotMiddleware(compiler));\n\napp.get(\"/\", function(req, res) {\n  res.sendFile(__dirname + '/index.html');\n});\n\napp.listen(port, function(error) {\n  if (error) {\n    console.error(error);\n  } else {\n    console.info(\"==> 🌎  Listening on port %s. Open up http://localhost:%s/ in your browser.\", port, port);\n  }\n});\n"
  },
  {
    "path": "examples/router/store/configureStore.js",
    "content": "import { createStore, compose } from 'redux';\nimport { reduxReactRouter, routerStateReducer, ReduxRouter } from 'redux-router';\n//import createHistory from 'history/lib/createBrowserHistory';\nimport createHistory from 'history/lib/createHashHistory';\nimport rootReducer from '../reducers';\n\nexport default function configureStore(initialState) {\n  let finalCreateStore = compose(\n    reduxReactRouter({ createHistory }),\n    global.devToolsExtension ? global.devToolsExtension() : f => f\n  )(createStore);\n\n  const store = finalCreateStore(rootReducer, initialState);\n\n  if (module.hot) {\n    // Enable Webpack hot module replacement for reducers\n    module.hot.accept('../reducers', () => {\n      const nextReducer = require('../reducers');\n      store.replaceReducer(nextReducer);\n    });\n  }\n\n  return store;\n}\n"
  },
  {
    "path": "examples/router/test/actions/todos.spec.js",
    "content": "import expect from 'expect';\nimport * as types from '../../constants/ActionTypes';\nimport * as actions from '../../actions/todos';\n\ndescribe('todo actions', () => {\n  it('addTodo should create ADD_TODO action', () => {\n    expect(actions.addTodo('Use Redux')).toEqual({\n      type: types.ADD_TODO,\n      text: 'Use Redux'\n    });\n  });\n\n  it('deleteTodo should create DELETE_TODO action', () => {\n    expect(actions.deleteTodo(1)).toEqual({\n      type: types.DELETE_TODO,\n      id: 1\n    });\n  });\n\n  it('editTodo should create EDIT_TODO action', () => {\n    expect(actions.editTodo(1, 'Use Redux everywhere')).toEqual({\n      type: types.EDIT_TODO,\n      id: 1,\n      text: 'Use Redux everywhere'\n    });\n  });\n\n  it('completeTodo should create COMPLETE_TODO action', () => {\n    expect(actions.completeTodo(1)).toEqual({\n      type: types.COMPLETE_TODO,\n      id: 1\n    });\n  });\n\n  it('completeAll should create COMPLETE_ALL action', () => {\n    expect(actions.completeAll()).toEqual({\n      type: types.COMPLETE_ALL\n    });\n  });\n\n  it('clearCompleted should create CLEAR_COMPLETED action', () => {\n    expect(actions.clearCompleted('Use Redux')).toEqual({\n      type: types.CLEAR_COMPLETED\n    });\n  });\n});\n"
  },
  {
    "path": "examples/router/test/components/Footer.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport Footer from '../../components/Footer';\nimport { SHOW_ALL, SHOW_ACTIVE } from '../../constants/TodoFilters';\n\nfunction setup(propOverrides) {\n  const props = Object.assign({\n    completedCount: 0,\n    activeCount: 0,\n    filter: SHOW_ALL,\n    onClearCompleted: expect.createSpy(),\n    onShow: expect.createSpy()\n  }, propOverrides);\n\n  const renderer = TestUtils.createRenderer();\n  renderer.render(<Footer {...props} />);\n  const output = renderer.getRenderOutput();\n\n  return {\n    props: props,\n    output: output\n  };\n}\n\nfunction getTextContent(elem) {\n  const children = Array.isArray(elem.props.children) ?\n    elem.props.children : [elem.props.children];\n\n  return children.reduce(function concatText(out, child) {\n    // Children are either elements or text strings\n    return out + (child.props ? getTextContent(child) : child);\n  }, '');\n}\n\ndescribe('components', () => {\n  describe('Footer', () => {\n    it('should render container', () => {\n      const { output } = setup();\n      expect(output.type).toBe('footer');\n      expect(output.props.className).toBe('footer');\n    });\n\n    it('should display active count when 0', () => {\n      const { output } = setup({ activeCount: 0 });\n      const [count] = output.props.children;\n      expect(getTextContent(count)).toBe('No items left');\n    });\n\n    it('should display active count when above 0', () => {\n      const { output } = setup({ activeCount: 1 });\n      const [count] = output.props.children;\n      expect(getTextContent(count)).toBe('1 item left');\n    });\n\n    it('should render filters', () => {\n      const { output } = setup();\n      const [, filters] = output.props.children;\n      expect(filters.type).toBe('ul');\n      expect(filters.props.className).toBe('filters');\n      expect(filters.props.children.length).toBe(3);\n      filters.props.children.forEach(function checkFilter(filter, i) {\n        expect(filter.type).toBe('li');\n        const a = filter.props.children;\n        expect(a.props.className).toBe(i === 0 ? 'selected' : '');\n        expect(a.props.children).toBe({\n          0: 'All',\n          1: 'Active',\n          2: 'Completed'\n        }[i]);\n      });\n    });\n\n    it('should call onShow when a filter is clicked', () => {\n      const { output, props } = setup();\n      const [, filters] = output.props.children;\n      const filterLink = filters.props.children[1].props.children;\n      filterLink.props.onClick({});\n      expect(props.onShow).toHaveBeenCalledWith(SHOW_ACTIVE);\n    });\n\n    it('shouldnt show clear button when no completed todos', () => {\n      const { output } = setup({ completedCount: 0 });\n      const [,, clear] = output.props.children;\n      expect(clear).toBe(undefined);\n    });\n\n    it('should render clear button when completed todos', () => {\n      const { output } = setup({ completedCount: 1 });\n      const [,, clear] = output.props.children;\n      expect(clear.type).toBe('button');\n      expect(clear.props.children).toBe('Clear completed');\n    });\n\n    it('should call onClearCompleted on clear button click', () => {\n      const { output, props } = setup({ completedCount: 1 });\n      const [,, clear] = output.props.children;\n      clear.props.onClick({});\n      expect(props.onClearCompleted).toHaveBeenCalled();\n    });\n  });\n});\n"
  },
  {
    "path": "examples/router/test/components/Header.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport Header from '../../components/Header';\nimport TodoTextInput from '../../components/TodoTextInput';\n\nfunction setup() {\n  const props = {\n    addTodo: expect.createSpy()\n  };\n\n  const renderer = TestUtils.createRenderer();\n  renderer.render(<Header {...props} />);\n  const output = renderer.getRenderOutput();\n\n  return {\n    props: props,\n    output: output,\n    renderer: renderer\n  };\n}\n\ndescribe('components', () => {\n  describe('Header', () => {\n    it('should render correctly', () => {\n      const { output } = setup();\n\n      expect(output.type).toBe('header');\n      expect(output.props.className).toBe('header');\n\n      const [h1, input] = output.props.children;\n\n      expect(h1.type).toBe('h1');\n      expect(h1.props.children).toBe('todos');\n\n      expect(input.type).toBe(TodoTextInput);\n      expect(input.props.newTodo).toBe(true);\n      expect(input.props.placeholder).toBe('What needs to be done?');\n    });\n\n    it('should call call addTodo if length of text is greater than 0', () => {\n      const { output, props } = setup();\n      const input = output.props.children[1];\n      input.props.onSave('');\n      expect(props.addTodo.calls.length).toBe(0);\n      input.props.onSave('Use Redux');\n      expect(props.addTodo.calls.length).toBe(1);\n    });\n  });\n});\n"
  },
  {
    "path": "examples/router/test/components/MainSection.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport MainSection from '../../components/MainSection';\nimport TodoItem from '../../components/TodoItem';\nimport Footer from '../../components/Footer';\nimport { SHOW_ALL, SHOW_COMPLETED } from '../../constants/TodoFilters';\n\nfunction setup(propOverrides) {\n  const props = Object.assign({\n    todos: [{\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }, {\n      text: 'Run the tests',\n      completed: true,\n      id: 1\n    }],\n    actions: {\n      editTodo: expect.createSpy(),\n      deleteTodo: expect.createSpy(),\n      completeTodo: expect.createSpy(),\n      completeAll: expect.createSpy(),\n      clearCompleted: expect.createSpy()\n    }\n  }, propOverrides);\n\n  const renderer = TestUtils.createRenderer();\n  renderer.render(<MainSection {...props} />);\n  const output = renderer.getRenderOutput();\n\n  return {\n    props: props,\n    output: output,\n    renderer: renderer\n  };\n}\n\ndescribe('components', () => {\n  describe('MainSection', () => {\n    it('should render container', () => {\n      const { output } = setup();\n      expect(output.type).toBe('section');\n      expect(output.props.className).toBe('main');\n    });\n\n    describe('toggle all input', () => {\n      it('should render', () => {\n        const { output } = setup();\n        const [toggle] = output.props.children;\n        expect(toggle.type).toBe('input');\n        expect(toggle.props.type).toBe('checkbox');\n        expect(toggle.props.checked).toBe(false);\n      });\n\n      it('should be checked if all todos completed', () => {\n        const { output } = setup({ todos: [{\n          text: 'Use Redux',\n          completed: true,\n          id: 0\n        }]});\n        const [toggle] = output.props.children;\n        expect(toggle.props.checked).toBe(true);\n      });\n\n      it('should call completeAll on change', () => {\n        const { output, props } = setup();\n        const [toggle] = output.props.children;\n        toggle.props.onChange({});\n        expect(props.actions.completeAll).toHaveBeenCalled();\n      });\n    });\n\n    describe('footer', () => {\n      it('should render', () => {\n        const { output } = setup();\n        const [,, footer] = output.props.children;\n        expect(footer.type).toBe(Footer);\n        expect(footer.props.completedCount).toBe(1);\n        expect(footer.props.activeCount).toBe(1);\n        expect(footer.props.filter).toBe(SHOW_ALL);\n      });\n\n      it('onShow should set the filter', () => {\n        const { output, renderer } = setup();\n        const [,, footer] = output.props.children;\n        footer.props.onShow(SHOW_COMPLETED);\n        const updated = renderer.getRenderOutput();\n        const [,, updatedFooter] = updated.props.children;\n        expect(updatedFooter.props.filter).toBe(SHOW_COMPLETED);\n      });\n\n      it('onClearCompleted should call clearCompleted', () => {\n        const { output, props } = setup();\n        const [,, footer] = output.props.children;\n        footer.props.onClearCompleted();\n        expect(props.actions.clearCompleted).toHaveBeenCalled();\n      });\n\n      it('onClearCompleted shouldnt call clearCompleted if no todos completed', () => {\n        const { output, props } = setup({ todos: [{\n          text: 'Use Redux',\n          completed: false,\n          id: 0\n        }]});\n        const [,, footer] = output.props.children;\n        footer.props.onClearCompleted();\n        expect(props.actions.clearCompleted.calls.length).toBe(0);\n      });\n    });\n\n    describe('todo list', () => {\n      it('should render', () => {\n        const { output, props } = setup();\n        const [, list] = output.props.children;\n        expect(list.type).toBe('ul');\n        expect(list.props.children.length).toBe(2);\n        list.props.children.forEach((item, i) => {\n          expect(item.type).toBe(TodoItem);\n          expect(item.props.todo).toBe(props.todos[i]);\n        });\n      });\n\n      it('should filter items', () => {\n        const { output, renderer, props } = setup();\n        const [,, footer] = output.props.children;\n        footer.props.onShow(SHOW_COMPLETED);\n        const updated = renderer.getRenderOutput();\n        const [, updatedList] = updated.props.children;\n        expect(updatedList.props.children.length).toBe(1);\n        expect(updatedList.props.children[0].props.todo).toBe(props.todos[1]);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "examples/router/test/components/TodoItem.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport TodoItem from '../../components/TodoItem';\nimport TodoTextInput from '../../components/TodoTextInput';\n\nfunction setup( editing = false ) {\n  const props = {\n    todo: {\n      id: 0,\n      text: 'Use Redux',\n      completed: false\n    },\n    editTodo: expect.createSpy(),\n    deleteTodo: expect.createSpy(),\n    completeTodo: expect.createSpy()\n  };\n\n  const renderer = TestUtils.createRenderer();\n\n  renderer.render(\n    <TodoItem {...props} />\n  );\n\n  let output = renderer.getRenderOutput();\n\n  if (editing) {\n    const label = output.props.children.props.children[1];\n    label.props.onDoubleClick({});\n    output = renderer.getRenderOutput();\n  }\n\n  return {\n    props: props,\n    output: output,\n    renderer: renderer\n  };\n}\n\ndescribe('components', () => {\n  describe('TodoItem', () => {\n    it('initial render', () => {\n      const { output } = setup();\n\n      expect(output.type).toBe('li');\n      expect(output.props.className).toBe('');\n\n      const div = output.props.children;\n\n      expect(div.type).toBe('div');\n      expect(div.props.className).toBe('view');\n\n      const [input, label, button] = div.props.children;\n\n      expect(input.type).toBe('input');\n      expect(input.props.checked).toBe(false);\n\n      expect(label.type).toBe('label');\n      expect(label.props.children).toBe('Use Redux');\n\n      expect(button.type).toBe('button');\n      expect(button.props.className).toBe('destroy');\n    });\n\n    it('input onChange should call completeTodo', () => {\n      const { output, props } = setup();\n      const input = output.props.children.props.children[0];\n      input.props.onChange({});\n      expect(props.completeTodo).toHaveBeenCalledWith(0);\n    });\n\n    it('button onClick should call deleteTodo', () => {\n      const { output, props } = setup();\n      const button = output.props.children.props.children[2];\n      button.props.onClick({});\n      expect(props.deleteTodo).toHaveBeenCalledWith(0);\n    });\n\n    it('label onDoubleClick should put component in edit state', () => {\n      const { output, renderer } = setup();\n      const label = output.props.children.props.children[1];\n      label.props.onDoubleClick({});\n      const updated = renderer.getRenderOutput();\n      expect(updated.type).toBe('li');\n      expect(updated.props.className).toBe('editing');\n    });\n\n    it('edit state render', () => {\n      const { output } = setup(true);\n\n      expect(output.type).toBe('li');\n      expect(output.props.className).toBe('editing');\n\n      const input = output.props.children;\n      expect(input.type).toBe(TodoTextInput);\n      expect(input.props.text).toBe('Use Redux');\n      expect(input.props.editing).toBe(true);\n    });\n\n    it('TodoTextInput onSave should call editTodo', () => {\n      const { output, props } = setup(true);\n      output.props.children.props.onSave('Use Redux');\n      expect(props.editTodo).toHaveBeenCalledWith(0, 'Use Redux');\n    });\n\n    it('TodoTextInput onSave should call deleteTodo if text is empty', () => {\n      const { output, props } = setup(true);\n      output.props.children.props.onSave('');\n      expect(props.deleteTodo).toHaveBeenCalledWith(0);\n    });\n\n    it('TodoTextInput onSave should exit component from edit state', () => {\n      const { output, renderer } = setup(true);\n      output.props.children.props.onSave('Use Redux');\n      const updated = renderer.getRenderOutput();\n      expect(updated.type).toBe('li');\n      expect(updated.props.className).toBe('');\n    });\n  });\n});\n"
  },
  {
    "path": "examples/router/test/components/TodoTextInput.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport TodoTextInput from '../../components/TodoTextInput';\n\nfunction setup(propOverrides) {\n  const props = Object.assign({\n    onSave: expect.createSpy(),\n    text: 'Use Redux',\n    placeholder: 'What needs to be done?',\n    editing: false,\n    newTodo: false\n  }, propOverrides);\n\n  const renderer = TestUtils.createRenderer();\n\n  renderer.render(\n    <TodoTextInput {...props} />\n  );\n\n  let output = renderer.getRenderOutput();\n\n  output = renderer.getRenderOutput();\n\n  return {\n    props: props,\n    output: output,\n    renderer: renderer\n  };\n}\n\ndescribe('components', () => {\n  describe('TodoTextInput', () => {\n    it('should render correctly', () => {\n      const { output } = setup();\n      expect(output.props.placeholder).toEqual('What needs to be done?');\n      expect(output.props.value).toEqual('Use Redux');\n      expect(output.props.className).toEqual('');\n    });\n\n    it('should render correctly when editing=true', () => {\n      const { output } = setup({ editing: true });\n      expect(output.props.className).toEqual('edit');\n    });\n\n    it('should render correctly when newTodo=true', () => {\n      const { output } = setup({ newTodo: true });\n      expect(output.props.className).toEqual('new-todo');\n    });\n\n    it('should update value on change', () => {\n      const { output, renderer } = setup();\n      output.props.onChange({ target: { value: 'Use Radox' }});\n      const updated = renderer.getRenderOutput();\n      expect(updated.props.value).toEqual('Use Radox');\n    });\n\n    it('should call onSave on return key press', () => {\n      const { output, props } = setup();\n      output.props.onKeyDown({ which: 13, target: { value: 'Use Redux' }});\n      expect(props.onSave).toHaveBeenCalledWith('Use Redux');\n    });\n\n    it('should reset state on return key press if newTodo', () => {\n      const { output, renderer } = setup({ newTodo: true });\n      output.props.onKeyDown({ which: 13, target: { value: 'Use Redux' }});\n      const updated = renderer.getRenderOutput();\n      expect(updated.props.value).toEqual('');\n    });\n\n    it('should call onSave on blur', () => {\n      const { output, props } = setup();\n      output.props.onBlur({ target: { value: 'Use Redux' }});\n      expect(props.onSave).toHaveBeenCalledWith('Use Redux');\n    });\n\n    it('shouldnt call onSave on blur if newTodo', () => {\n      const { output, props } = setup({ newTodo: true });\n      output.props.onBlur({ target: { value: 'Use Redux' }});\n      expect(props.onSave.calls.length).toBe(0);\n    });\n  });\n});\n"
  },
  {
    "path": "examples/router/test/reducers/todos.spec.js",
    "content": "import expect from 'expect';\nimport todos from '../../reducers/todos';\nimport * as types from '../../constants/ActionTypes';\n\ndescribe('todos reducer', () => {\n  it('should handle initial state', () => {\n    expect(\n      todos(undefined, {})\n    ).toEqual([{\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle ADD_TODO', () => {\n    expect(\n      todos([], {\n        type: types.ADD_TODO,\n        text: 'Run the tests'\n      })\n    ).toEqual([{\n      text: 'Run the tests',\n      completed: false,\n      id: 0\n    }]);\n\n    expect(\n      todos([{\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.ADD_TODO,\n        text: 'Run the tests'\n      })\n    ).toEqual([{\n      text: 'Run the tests',\n      completed: false,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: false,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.ADD_TODO,\n        text: 'Fix the tests'\n      })\n    ).toEqual([{\n      text: 'Fix the tests',\n      completed: false,\n      id: 2\n    }, {\n      text: 'Run the tests',\n      completed: false,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle DELETE_TODO', () => {\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: false,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.DELETE_TODO,\n        id: 1\n      })\n    ).toEqual([{\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle EDIT_TODO', () => {\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: false,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.EDIT_TODO,\n        text: 'Fix the tests',\n        id: 1\n      })\n    ).toEqual([{\n      text: 'Fix the tests',\n      completed: false,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle COMPLETE_TODO', () => {\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: false,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.COMPLETE_TODO,\n        id: 1\n      })\n    ).toEqual([{\n      text: 'Run the tests',\n      completed: true,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle COMPLETE_ALL', () => {\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: true,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.COMPLETE_ALL\n      })\n    ).toEqual([{\n      text: 'Run the tests',\n      completed: true,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: true,\n      id: 0\n    }]);\n\n    // Unmark if all todos are currently completed\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: true,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: true,\n        id: 0\n      }], {\n        type: types.COMPLETE_ALL\n      })\n    ).toEqual([{\n      text: 'Run the tests',\n      completed: false,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle CLEAR_COMPLETED', () => {\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: true,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.CLEAR_COMPLETED\n      })\n    ).toEqual([{\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should not generate duplicate ids after CLEAR_COMPLETED', () => {\n    expect(\n      [{\n        type: types.COMPLETE_TODO,\n        id: 0\n      }, {\n        type: types.CLEAR_COMPLETED\n      }, {\n        type: types.ADD_TODO,\n        text: 'Write more tests'\n      }].reduce(todos, [{\n        id: 0,\n        completed: false,\n        text: 'Use Redux'\n      }, {\n        id: 1,\n        completed: false,\n        text: 'Write tests'\n      }])\n    ).toEqual([{\n      text: 'Write more tests',\n      completed: false,\n      id: 2\n    }, {\n      text: 'Write tests',\n      completed: false,\n      id: 1\n    }]);\n  });\n});\n"
  },
  {
    "path": "examples/router/test/setup.js",
    "content": "import { jsdom } from 'jsdom';\n\nglobal.document = jsdom('<!doctype html><html><body></body></html>');\nglobal.window = document.defaultView;\nglobal.navigator = global.window.navigator;\n"
  },
  {
    "path": "examples/router/webpack.config.js",
    "content": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n  devtool: 'cheap-module-eval-source-map',\n  entry: [\n    'webpack-hot-middleware/client',\n    './index'\n  ],\n  output: {\n    path: path.join(__dirname, 'dist'),\n    filename: 'bundle.js',\n    publicPath: '/static/'\n  },\n  plugins: [\n    new webpack.optimize.OccurenceOrderPlugin(),\n    new webpack.HotModuleReplacementPlugin(),\n    new webpack.NoErrorsPlugin()\n  ],\n  module: {\n    loaders: [{\n      test: /\\.js$/,\n      loaders: ['babel'],\n      exclude: /node_modules/,\n      include: __dirname\n    }, {\n      test: /\\.css?$/,\n      loaders: ['style', 'raw'],\n      include: __dirname\n    }]\n  }\n};\n"
  },
  {
    "path": "examples/saga-counter/.babelrc",
    "content": "{\n  \"presets\": [ \"es2015\", \"stage-0\", \"react\" ]\n}\n"
  },
  {
    "path": "examples/saga-counter/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Redux Saga Counter example</title>\n  </head>\n  <body>\n\n    <div id=\"root\"></div>\n\n   <script type=\"text/javascript\" src=\"/static/bundle.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/saga-counter/package.json",
    "content": "{\n  \"name\": \"redux-counter-example\",\n  \"version\": \"0.0.0\",\n  \"description\": \"Redux counter example\",\n  \"scripts\": {\n    \"start\": \"webpack-dev-server --progress\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/rackt/redux.git\"\n  },\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/rackt/redux/issues\"\n  },\n  \"homepage\": \"http://rackt.github.io/redux\",\n  \"dependencies\": {\n    \"prop-types\": \"^15.6.2\",\n    \"react\": \"^16.0.0\",\n    \"react-dom\": \"^16.0.0\",\n    \"react-redux\": \"^6.0.0\",\n    \"redux\": \"^4.0.0\",\n    \"redux-saga\": \"^0.10.5\"\n  },\n  \"devDependencies\": {\n    \"babel-cli\": \"^6.3.17\",\n    \"babel-core\": \"^6.3.17\",\n    \"babel-loader\": \"^7.0.0\",\n    \"babel-preset-es2015\": \"^6.0.0\",\n    \"babel-preset-react\": \"6.3.13\",\n    \"babel-preset-stage-0\": \"^6.3.13\",\n    \"webpack\": \"^4.0.0\",\n    \"webpack-cli\": \"^3.2.0\",\n    \"webpack-dev-server\": \"^3.1.0\",\n    \"webpack-hot-middleware\": \"^2.24.0\"\n  }\n}\n"
  },
  {
    "path": "examples/saga-counter/src/components/Counter.js",
    "content": "import React from 'react'\nimport PropTypes from 'prop-types'\n\nconst Counter = ({ value, onIncrement, onIncrementAsync, onDecrement, onIncrementIfOdd }) =>\n      <p>\n        Clicked: {value} times\n        {' '}\n        <button onClick={onIncrement}>\n          +\n        </button>\n        {' '}\n        <button onClick={onDecrement}>\n          -\n        </button>\n        {' '}\n        <button onClick={onIncrementIfOdd}>\n          Increment if odd\n        </button>\n        {' '}\n        <button onClick={onIncrementAsync}>\n          Increment async\n        </button>\n      </p>\n\nCounter.propTypes = {\n  value: PropTypes.number.isRequired,\n  onIncrement: PropTypes.func.isRequired,\n  onDecrement: PropTypes.func.isRequired,\n  onIncrementAsync: PropTypes.func.isRequired,\n  onIncrementIfOdd: PropTypes.func.isRequired\n}\n\nexport default Counter\n"
  },
  {
    "path": "examples/saga-counter/src/main.js",
    "content": "import \"babel-polyfill\"\n\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { createStore, applyMiddleware, compose } from 'redux'\nimport createSagaMiddleware from 'redux-saga'\n// import sagaMonitor from './sagaMonitor'\n\nimport Counter from './components/Counter'\nimport reducer from './reducers'\nimport rootSaga from './sagas'\n\n\nconst sagaMiddleware = createSagaMiddleware(/* {sagaMonitor} */)\nconst composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&\n  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true, traceLimit: 25 }) || compose;\nconst store = createStore(\n  reducer,\n  composeEnhancers(applyMiddleware(sagaMiddleware))\n)\nsagaMiddleware.run(rootSaga)\n\nconst action = type => store.dispatch({type})\n\nfunction render() {\n  ReactDOM.render(\n    <Counter\n      value={store.getState()}\n      onIncrement={() => action('INCREMENT')}\n      onDecrement={() => action('DECREMENT')}\n      onIncrementIfOdd={() => action('INCREMENT_IF_ODD')}\n      onIncrementAsync={() => action('INCREMENT_ASYNC')} />,\n    document.getElementById('root')\n  )\n}\n\nrender()\nstore.subscribe(render)\n"
  },
  {
    "path": "examples/saga-counter/src/reducers/index.js",
    "content": "export default function counter(state = 0, action) {\n  switch (action.type) {\n    case 'INCREMENT':\n      return state + 1\n    case 'INCREMENT_IF_ODD':\n      return (state % 2 !== 0) ? state + 1 : state\n    case 'DECREMENT':\n      return state - 1\n    default:\n      return state\n  }\n}\n"
  },
  {
    "path": "examples/saga-counter/src/sagas/index.js",
    "content": "/* eslint-disable no-constant-condition */\n\nimport { takeEvery } from 'redux-saga'\nimport { put, call } from 'redux-saga/effects'\nimport { delay } from 'redux-saga'\n\nexport function* incrementAsync() {\n  yield call(delay, 1000)\n  yield put({type: 'INCREMENT'})\n}\n\nexport default function* rootSaga() {\n  yield* takeEvery('INCREMENT_ASYNC', incrementAsync)\n}\n"
  },
  {
    "path": "examples/saga-counter/webpack.config.js",
    "content": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n  mode: 'development',\n  devtool: 'source-map',\n  entry: [\n    path.join(__dirname, 'src', 'main')\n  ],\n  output: {\n    path: path.join(__dirname, 'dist'),\n    filename: 'bundle.js',\n    publicPath: '/static/'\n  },\n  module: {\n    rules: [{\n      test: /\\.js$/,\n      loaders: ['babel-loader'],\n      exclude: /node_modules/\n    }]\n  },\n  devServer: {\n    port: 4003\n  }\n};\n"
  },
  {
    "path": "examples/todomvc/.babelrc",
    "content": "{\n  \"presets\": [ \"es2015\", \"stage-0\", \"react\" ]\n}"
  },
  {
    "path": "examples/todomvc/actions/index.js",
    "content": "export * from './todos';\n"
  },
  {
    "path": "examples/todomvc/actions/todos.js",
    "content": "import * as types from '../constants/ActionTypes';\n\nexport function addTodo(text) {\n  return { type: types.ADD_TODO, text };\n}\n\nexport function deleteTodo(id) {\n  return { type: types.DELETE_TODO, id };\n}\n\nexport function editTodo(id, text) {\n  return { type: types.EDIT_TODO, id, text };\n}\n\nexport function completeTodo(id) {\n  return { type: types.COMPLETE_TODO, id };\n}\n\nexport function completeAll() {\n  return { type: types.COMPLETE_ALL };\n}\n\nexport function clearCompleted() {\n  return { type: types.CLEAR_COMPLETED };\n}\n"
  },
  {
    "path": "examples/todomvc/components/Footer.js",
    "content": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport classnames from 'classnames';\nimport { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters';\n\nconst FILTER_TITLES = {\n  [SHOW_ALL]: 'All',\n  [SHOW_ACTIVE]: 'Active',\n  [SHOW_COMPLETED]: 'Completed'\n};\n\nclass Footer extends Component {\n  renderTodoCount() {\n    const { activeCount } = this.props;\n    const itemWord = activeCount === 1 ? 'item' : 'items';\n\n    return (\n      <span className=\"todo-count\">\n        <strong>{activeCount || 'No'}</strong> {itemWord} left\n      </span>\n    );\n  }\n\n  renderFilterLink(filter) {\n    const title = FILTER_TITLES[filter];\n    const { filter: selectedFilter, onShow } = this.props;\n\n    return (\n      <a className={classnames({ selected: filter === selectedFilter })}\n         style={{ cursor: 'pointer' }}\n         onClick={() => onShow(filter)}>\n        {title}\n      </a>\n    );\n  }\n\n  renderClearButton() {\n    const { completedCount, onClearCompleted } = this.props;\n    if (completedCount > 0) {\n      return (\n        <button className=\"clear-completed\"\n                onClick={onClearCompleted} >\n          Clear completed\n        </button>\n      );\n    }\n  }\n\n  render() {\n    return (\n      <footer className=\"footer\">\n        {this.renderTodoCount()}\n        <ul className=\"filters\">\n          {[SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED].map(filter =>\n            <li key={filter}>\n              {this.renderFilterLink(filter)}\n            </li>\n          )}\n        </ul>\n        {this.renderClearButton()}\n      </footer>\n    );\n  }\n}\n\nFooter.propTypes = {\n  completedCount: PropTypes.number.isRequired,\n  activeCount: PropTypes.number.isRequired,\n  filter: PropTypes.string.isRequired,\n  onClearCompleted: PropTypes.func.isRequired,\n  onShow: PropTypes.func.isRequired\n};\n\nexport default Footer;\n"
  },
  {
    "path": "examples/todomvc/components/Header.js",
    "content": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport TodoTextInput from './TodoTextInput';\n\nclass Header extends Component {\n  handleSave(text) {\n    if (text.length !== 0) {\n      this.props.addTodo(text);\n    }\n  }\n\n  render() {\n    return (\n      <header className=\"header\">\n          <h1>todos</h1>\n          <TodoTextInput newTodo\n                         onSave={this.handleSave.bind(this)}\n                         placeholder=\"What needs to be done?\" />\n      </header>\n    );\n  }\n}\n\nHeader.propTypes = {\n  addTodo: PropTypes.func.isRequired\n};\n\nexport default Header;\n"
  },
  {
    "path": "examples/todomvc/components/MainSection.js",
    "content": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport TodoItem from './TodoItem';\nimport Footer from './Footer';\nimport { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters';\n\nconst TODO_FILTERS = {\n  [SHOW_ALL]: () => true,\n  [SHOW_ACTIVE]: todo => !todo.completed,\n  [SHOW_COMPLETED]: todo => todo.completed\n};\n\nclass MainSection extends Component {\n  constructor(props, context) {\n    super(props, context);\n    this.state = { filter: SHOW_ALL };\n  }\n\n  handleClearCompleted() {\n    const atLeastOneCompleted = this.props.todos.some(todo => todo.completed);\n    if (atLeastOneCompleted) {\n      this.props.actions.clearCompleted();\n    }\n  }\n\n  handleShow(filter) {\n    this.setState({ filter });\n  }\n\n  renderToggleAll(completedCount) {\n    const { todos, actions } = this.props;\n    if (todos.length > 0) {\n      return (\n        <input className=\"toggle-all\"\n               type=\"checkbox\"\n               checked={completedCount === todos.length}\n               onChange={actions.completeAll} />\n      );\n    }\n  }\n\n  renderFooter(completedCount) {\n    const { todos } = this.props;\n    const { filter } = this.state;\n    const activeCount = todos.length - completedCount;\n\n    if (todos.length) {\n      return (\n        <Footer completedCount={completedCount}\n                activeCount={activeCount}\n                filter={filter}\n                onClearCompleted={this.handleClearCompleted.bind(this)}\n                onShow={this.handleShow.bind(this)} />\n      );\n    }\n  }\n\n  render() {\n    const { todos, actions } = this.props;\n    const { filter } = this.state;\n\n    const filteredTodos = todos.filter(TODO_FILTERS[filter]);\n    const completedCount = todos.reduce((count, todo) =>\n      todo.completed ? count + 1 : count,\n      0\n    );\n\n    return (\n      <section className=\"main\">\n        {this.renderToggleAll(completedCount)}\n        <ul className=\"todo-list\">\n          {filteredTodos.map(todo =>\n            <TodoItem key={todo.id} todo={todo} {...actions} />\n          )}\n        </ul>\n        {this.renderFooter(completedCount)}\n      </section>\n    );\n  }\n}\n\nMainSection.propTypes = {\n  todos: PropTypes.array.isRequired,\n  actions: PropTypes.object.isRequired\n};\n\nexport default MainSection;\n"
  },
  {
    "path": "examples/todomvc/components/TodoItem.js",
    "content": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport classnames from 'classnames';\nimport TodoTextInput from './TodoTextInput';\n\nclass TodoItem extends Component {\n  constructor(props, context) {\n    super(props, context);\n    this.state = {\n      editing: false\n    };\n  }\n\n  handleDoubleClick() {\n    this.setState({ editing: true });\n  }\n\n  handleSave(id, text) {\n    if (text.length === 0) {\n      this.props.deleteTodo(id);\n    } else {\n      this.props.editTodo(id, text);\n    }\n    this.setState({ editing: false });\n  }\n\n  render() {\n    const {todo, completeTodo, deleteTodo} = this.props;\n\n    let element;\n    if (this.state.editing) {\n      element = (\n        <TodoTextInput text={todo.text}\n                       editing={this.state.editing}\n                       onSave={(text) => this.handleSave(todo.id, text)} />\n      );\n    } else {\n      element = (\n        <div className=\"view\">\n          <input className=\"toggle\"\n                 type=\"checkbox\"\n                 checked={todo.completed}\n                 onChange={() => completeTodo(todo.id)} />\n          <label onDoubleClick={this.handleDoubleClick.bind(this)}>\n            {todo.text}\n          </label>\n          <button className=\"destroy\"\n                  onClick={() => deleteTodo(todo.id)} />\n        </div>\n      );\n    }\n\n    return (\n      <li className={classnames({\n        completed: todo.completed,\n        editing: this.state.editing\n      })}>\n        {element}\n      </li>\n    );\n  }\n}\n\nTodoItem.propTypes = {\n  todo: PropTypes.object.isRequired,\n  editTodo: PropTypes.func.isRequired,\n  deleteTodo: PropTypes.func.isRequired,\n  completeTodo: PropTypes.func.isRequired\n};\n\nexport default TodoItem;\n"
  },
  {
    "path": "examples/todomvc/components/TodoTextInput.js",
    "content": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport classnames from 'classnames';\n\nclass TodoTextInput extends Component {\n  constructor(props, context) {\n    super(props, context);\n    this.state = {\n      text: this.props.text || ''\n    };\n  }\n\n  handleSubmit(e) {\n    const text = e.target.value.trim();\n    if (e.which === 13) {\n      this.props.onSave(text);\n      if (this.props.newTodo) {\n        this.setState({ text: '' });\n      }\n    }\n  }\n\n  handleChange(e) {\n    this.setState({ text: e.target.value });\n  }\n\n  handleBlur(e) {\n    if (!this.props.newTodo) {\n      this.props.onSave(e.target.value);\n    }\n  }\n\n  render() {\n    return (\n      <input className={\n        classnames({\n          edit: this.props.editing,\n          'new-todo': this.props.newTodo\n        })}\n        type=\"text\"\n        placeholder={this.props.placeholder}\n        autoFocus={true}\n        value={this.state.text}\n        onBlur={this.handleBlur.bind(this)}\n        onChange={this.handleChange.bind(this)}\n        onKeyDown={this.handleSubmit.bind(this)} />\n    );\n  }\n}\n\nTodoTextInput.propTypes = {\n  onSave: PropTypes.func.isRequired,\n  text: PropTypes.string,\n  placeholder: PropTypes.string,\n  editing: PropTypes.bool,\n  newTodo: PropTypes.bool\n};\n\nexport default TodoTextInput;\n"
  },
  {
    "path": "examples/todomvc/constants/ActionTypes.js",
    "content": "export const ADD_TODO = 'ADD_TODO';\nexport const DELETE_TODO = 'DELETE_TODO';\nexport const EDIT_TODO = 'EDIT_TODO';\nexport const COMPLETE_TODO = 'COMPLETE_TODO';\nexport const COMPLETE_ALL = 'COMPLETE_ALL';\nexport const CLEAR_COMPLETED = 'CLEAR_COMPLETED';\n"
  },
  {
    "path": "examples/todomvc/constants/TodoFilters.js",
    "content": "export const SHOW_ALL = 'show_all';\nexport const SHOW_COMPLETED = 'show_completed';\nexport const SHOW_ACTIVE = 'show_active';\n"
  },
  {
    "path": "examples/todomvc/containers/App.js",
    "content": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport { bindActionCreators } from 'redux';\nimport { connect } from 'react-redux';\nimport Header from '../components/Header';\nimport MainSection from '../components/MainSection';\nimport * as TodoActions from '../actions/todos';\n\nclass App extends Component {\n  render() {\n    const { todos, actions } = this.props;\n    return (\n      <div>\n        <Header addTodo={actions.addTodo} />\n        <MainSection todos={todos} actions={actions} />\n      </div>\n    );\n  }\n}\n\nApp.propTypes = {\n  todos: PropTypes.array.isRequired,\n  actions: PropTypes.object.isRequired\n};\n\nfunction mapStateToProps(state) {\n  return {\n    todos: state.todos\n  };\n}\n\nfunction mapDispatchToProps(dispatch) {\n  return {\n    actions: bindActionCreators(TodoActions, dispatch)\n  };\n}\n\nexport default connect(\n  mapStateToProps,\n  mapDispatchToProps\n)(App);\n"
  },
  {
    "path": "examples/todomvc/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Redux TodoMVC example</title>\n  </head>\n  <body>\n    <div class=\"todoapp\" id=\"root\">\n    </div>\n    <script src=\"/static/bundle.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/todomvc/index.js",
    "content": "import 'babel-polyfill';\nimport React from 'react';\nimport { render } from 'react-dom';\nimport { Provider } from 'react-redux';\nimport App from './containers/App';\nimport configureStore from './store/configureStore';\nimport 'todomvc-app-css/index.css';\n\nconst store = configureStore();\n\nrender(\n  <Provider store={store}>\n    <App />\n  </Provider>,\n  document.getElementById('root')\n);\n"
  },
  {
    "path": "examples/todomvc/package.json",
    "content": "{\n  \"name\": \"redux-todomvc-example\",\n  \"version\": \"0.0.0\",\n  \"description\": \"Redux TodoMVC example\",\n  \"scripts\": {\n    \"start\": \"node server.js\",\n    \"test\": \"NODE_ENV=test mocha --recursive --compilers js:babel-core/register --require ./test/setup.js\",\n    \"test:watch\": \"npm test -- --watch\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/rackt/redux.git\"\n  },\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/rackt/redux/issues\"\n  },\n  \"homepage\": \"http://rackt.github.io/redux\",\n  \"dependencies\": {\n    \"classnames\": \"^2.1.2\",\n    \"prop-types\": \"^15.6.2\",\n    \"react\": \"^16.0.0\",\n    \"react-dom\": \"^16.0.0\",\n    \"react-redux\": \"^6.0.0\",\n    \"redux\": \"^4.0.0\"\n  },\n  \"devDependencies\": {\n    \"babel-cli\": \"^6.3.17\",\n    \"babel-core\": \"^6.3.17\",\n    \"babel-loader\": \"^7.0.0\",\n    \"babel-preset-es2015\": \"^6.0.0\",\n    \"babel-preset-react\": \"6.3.13\",\n    \"babel-preset-stage-0\": \"^6.3.13\",\n    \"express\": \"^4.13.3\",\n    \"raw-loader\": \"^1.0.0\",\n    \"style-loader\": \"^0.23.0\",\n    \"todomvc-app-css\": \"^2.0.1\",\n    \"webpack\": \"^4.0.0\",\n    \"webpack-dev-server\": \"^3.0.0\",\n    \"webpack-hot-middleware\": \"^2.2.0\"\n  }\n}\n"
  },
  {
    "path": "examples/todomvc/reducers/index.js",
    "content": "import { combineReducers } from 'redux';\nimport todos from './todos';\n\nconst rootReducer = combineReducers({\n  todos\n});\n\nexport default rootReducer;\n"
  },
  {
    "path": "examples/todomvc/reducers/todos.js",
    "content": "import { ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO, COMPLETE_ALL, CLEAR_COMPLETED } from '../constants/ActionTypes';\n\nconst initialState = [{\n  text: 'Use Redux',\n  completed: false,\n  modified: new Date(),\n  id: 0\n}];\n\nexport default function todos(state = initialState, action) {\n  switch (action.type) {\n  case ADD_TODO:\n    return [{\n      id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,\n      completed: false,\n      modified: new Date(),\n      text: action.text\n    }, ...state];\n\n  case DELETE_TODO:\n    return state.filter(todo =>\n      todo.id !== action.id\n    );\n\n  case EDIT_TODO:\n    return state.map(todo =>\n      todo.id === action.id ?\n        Object.assign({}, todo, { text: action.text, modified: new Date() }) :\n        todo\n    );\n\n  case COMPLETE_TODO:\n    return state.map(todo =>\n      todo.id === action.id ?\n        Object.assign({}, todo, { completed: !todo.completed, modified: new Date() }) :\n        todo\n    );\n\n  case COMPLETE_ALL:\n    const areAllMarked = state.every(todo => todo.completed);\n    return state.map(todo => Object.assign({}, todo, {\n      completed: !areAllMarked, modified: new Date()\n    }));\n\n  case CLEAR_COMPLETED:\n    return state.filter(todo => todo.completed === false);\n\n  default:\n    return state;\n  }\n}\n"
  },
  {
    "path": "examples/todomvc/server.js",
    "content": "var webpack = require('webpack');\nvar webpackDevMiddleware = require('webpack-dev-middleware');\nvar webpackHotMiddleware = require('webpack-hot-middleware');\nvar config = require('./webpack.config');\n\nvar app = new require('express')();\nvar port = 4002;\n\nvar compiler = webpack(config);\napp.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));\napp.use(webpackHotMiddleware(compiler));\n\napp.get(\"/\", function(req, res) {\n  res.sendFile(__dirname + '/index.html');\n});\n\napp.listen(port, function(error) {\n  if (error) {\n    console.error(error);\n  } else {\n    console.info(\"==> 🌎  Listening on port %s. Open up http://localhost:%s/ in your browser.\", port, port);\n  }\n});\n"
  },
  {
    "path": "examples/todomvc/store/configureStore.js",
    "content": "import { createStore } from 'redux';\nimport rootReducer from '../reducers';\nimport * as actionCreators from '../actions';\n\nexport default function configureStore(preloadedState) {\n  const enhancer = window.__REDUX_DEVTOOLS_EXTENSION__ &&\n    window.__REDUX_DEVTOOLS_EXTENSION__({ actionCreators, serialize: true, trace: true });\n  if (!enhancer) {\n    console.warn('Install Redux DevTools Extension to inspect the app state: ' +\n      'https://github.com/zalmoxisus/redux-devtools-extension#installation')\n  }\n\n  const store = createStore(rootReducer, preloadedState, enhancer);\n\n  if (module.hot) {\n    // Enable Webpack hot module replacement for reducers\n    module.hot.accept('../reducers', () => {\n      store.replaceReducer(require('../reducers').default)\n    });\n  }\n\n  return store;\n}\n"
  },
  {
    "path": "examples/todomvc/test/actions/todos.spec.js",
    "content": "import expect from 'expect';\nimport * as types from '../../constants/ActionTypes';\nimport * as actions from '../../actions/todos';\n\ndescribe('todo actions', () => {\n  it('addTodo should create ADD_TODO action', () => {\n    expect(actions.addTodo('Use Redux')).toEqual({\n      type: types.ADD_TODO,\n      text: 'Use Redux'\n    });\n  });\n\n  it('deleteTodo should create DELETE_TODO action', () => {\n    expect(actions.deleteTodo(1)).toEqual({\n      type: types.DELETE_TODO,\n      id: 1\n    });\n  });\n\n  it('editTodo should create EDIT_TODO action', () => {\n    expect(actions.editTodo(1, 'Use Redux everywhere')).toEqual({\n      type: types.EDIT_TODO,\n      id: 1,\n      text: 'Use Redux everywhere'\n    });\n  });\n\n  it('completeTodo should create COMPLETE_TODO action', () => {\n    expect(actions.completeTodo(1)).toEqual({\n      type: types.COMPLETE_TODO,\n      id: 1\n    });\n  });\n\n  it('completeAll should create COMPLETE_ALL action', () => {\n    expect(actions.completeAll()).toEqual({\n      type: types.COMPLETE_ALL\n    });\n  });\n\n  it('clearCompleted should create CLEAR_COMPLETED action', () => {\n    expect(actions.clearCompleted('Use Redux')).toEqual({\n      type: types.CLEAR_COMPLETED\n    });\n  });\n});\n"
  },
  {
    "path": "examples/todomvc/test/components/Footer.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport Footer from '../../components/Footer';\nimport { SHOW_ALL, SHOW_ACTIVE } from '../../constants/TodoFilters';\n\nfunction setup(propOverrides) {\n  const props = Object.assign({\n    completedCount: 0,\n    activeCount: 0,\n    filter: SHOW_ALL,\n    onClearCompleted: expect.createSpy(),\n    onShow: expect.createSpy()\n  }, propOverrides);\n\n  const renderer = TestUtils.createRenderer();\n  renderer.render(<Footer {...props} />);\n  const output = renderer.getRenderOutput();\n\n  return {\n    props: props,\n    output: output\n  };\n}\n\nfunction getTextContent(elem) {\n  const children = Array.isArray(elem.props.children) ?\n    elem.props.children : [elem.props.children];\n\n  return children.reduce(function concatText(out, child) {\n    // Children are either elements or text strings\n    return out + (child.props ? getTextContent(child) : child);\n  }, '');\n}\n\ndescribe('components', () => {\n  describe('Footer', () => {\n    it('should render container', () => {\n      const { output } = setup();\n      expect(output.type).toBe('footer');\n      expect(output.props.className).toBe('footer');\n    });\n\n    it('should display active count when 0', () => {\n      const { output } = setup({ activeCount: 0 });\n      const [count] = output.props.children;\n      expect(getTextContent(count)).toBe('No items left');\n    });\n\n    it('should display active count when above 0', () => {\n      const { output } = setup({ activeCount: 1 });\n      const [count] = output.props.children;\n      expect(getTextContent(count)).toBe('1 item left');\n    });\n\n    it('should render filters', () => {\n      const { output } = setup();\n      const [, filters] = output.props.children;\n      expect(filters.type).toBe('ul');\n      expect(filters.props.className).toBe('filters');\n      expect(filters.props.children.length).toBe(3);\n      filters.props.children.forEach(function checkFilter(filter, i) {\n        expect(filter.type).toBe('li');\n        const a = filter.props.children;\n        expect(a.props.className).toBe(i === 0 ? 'selected' : '');\n        expect(a.props.children).toBe({\n          0: 'All',\n          1: 'Active',\n          2: 'Completed'\n        }[i]);\n      });\n    });\n\n    it('should call onShow when a filter is clicked', () => {\n      const { output, props } = setup();\n      const [, filters] = output.props.children;\n      const filterLink = filters.props.children[1].props.children;\n      filterLink.props.onClick({});\n      expect(props.onShow).toHaveBeenCalledWith(SHOW_ACTIVE);\n    });\n\n    it('shouldnt show clear button when no completed todos', () => {\n      const { output } = setup({ completedCount: 0 });\n      const [,, clear] = output.props.children;\n      expect(clear).toBe(undefined);\n    });\n\n    it('should render clear button when completed todos', () => {\n      const { output } = setup({ completedCount: 1 });\n      const [,, clear] = output.props.children;\n      expect(clear.type).toBe('button');\n      expect(clear.props.children).toBe('Clear completed');\n    });\n\n    it('should call onClearCompleted on clear button click', () => {\n      const { output, props } = setup({ completedCount: 1 });\n      const [,, clear] = output.props.children;\n      clear.props.onClick({});\n      expect(props.onClearCompleted).toHaveBeenCalled();\n    });\n  });\n});\n"
  },
  {
    "path": "examples/todomvc/test/components/Header.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport Header from '../../components/Header';\nimport TodoTextInput from '../../components/TodoTextInput';\n\nfunction setup() {\n  const props = {\n    addTodo: expect.createSpy()\n  };\n\n  const renderer = TestUtils.createRenderer();\n  renderer.render(<Header {...props} />);\n  const output = renderer.getRenderOutput();\n\n  return {\n    props: props,\n    output: output,\n    renderer: renderer\n  };\n}\n\ndescribe('components', () => {\n  describe('Header', () => {\n    it('should render correctly', () => {\n      const { output } = setup();\n\n      expect(output.type).toBe('header');\n      expect(output.props.className).toBe('header');\n\n      const [h1, input] = output.props.children;\n\n      expect(h1.type).toBe('h1');\n      expect(h1.props.children).toBe('todos');\n\n      expect(input.type).toBe(TodoTextInput);\n      expect(input.props.newTodo).toBe(true);\n      expect(input.props.placeholder).toBe('What needs to be done?');\n    });\n\n    it('should call call addTodo if length of text is greater than 0', () => {\n      const { output, props } = setup();\n      const input = output.props.children[1];\n      input.props.onSave('');\n      expect(props.addTodo.calls.length).toBe(0);\n      input.props.onSave('Use Redux');\n      expect(props.addTodo.calls.length).toBe(1);\n    });\n  });\n});\n"
  },
  {
    "path": "examples/todomvc/test/components/MainSection.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport MainSection from '../../components/MainSection';\nimport TodoItem from '../../components/TodoItem';\nimport Footer from '../../components/Footer';\nimport { SHOW_ALL, SHOW_COMPLETED } from '../../constants/TodoFilters';\n\nfunction setup(propOverrides) {\n  const props = Object.assign({\n    todos: [{\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }, {\n      text: 'Run the tests',\n      completed: true,\n      id: 1\n    }],\n    actions: {\n      editTodo: expect.createSpy(),\n      deleteTodo: expect.createSpy(),\n      completeTodo: expect.createSpy(),\n      completeAll: expect.createSpy(),\n      clearCompleted: expect.createSpy()\n    }\n  }, propOverrides);\n\n  const renderer = TestUtils.createRenderer();\n  renderer.render(<MainSection {...props} />);\n  const output = renderer.getRenderOutput();\n\n  return {\n    props: props,\n    output: output,\n    renderer: renderer\n  };\n}\n\ndescribe('components', () => {\n  describe('MainSection', () => {\n    it('should render container', () => {\n      const { output } = setup();\n      expect(output.type).toBe('section');\n      expect(output.props.className).toBe('main');\n    });\n\n    describe('toggle all input', () => {\n      it('should render', () => {\n        const { output } = setup();\n        const [toggle] = output.props.children;\n        expect(toggle.type).toBe('input');\n        expect(toggle.props.type).toBe('checkbox');\n        expect(toggle.props.checked).toBe(false);\n      });\n\n      it('should be checked if all todos completed', () => {\n        const { output } = setup({ todos: [{\n          text: 'Use Redux',\n          completed: true,\n          id: 0\n        }]});\n        const [toggle] = output.props.children;\n        expect(toggle.props.checked).toBe(true);\n      });\n\n      it('should call completeAll on change', () => {\n        const { output, props } = setup();\n        const [toggle] = output.props.children;\n        toggle.props.onChange({});\n        expect(props.actions.completeAll).toHaveBeenCalled();\n      });\n    });\n\n    describe('footer', () => {\n      it('should render', () => {\n        const { output } = setup();\n        const [,, footer] = output.props.children;\n        expect(footer.type).toBe(Footer);\n        expect(footer.props.completedCount).toBe(1);\n        expect(footer.props.activeCount).toBe(1);\n        expect(footer.props.filter).toBe(SHOW_ALL);\n      });\n\n      it('onShow should set the filter', () => {\n        const { output, renderer } = setup();\n        const [,, footer] = output.props.children;\n        footer.props.onShow(SHOW_COMPLETED);\n        const updated = renderer.getRenderOutput();\n        const [,, updatedFooter] = updated.props.children;\n        expect(updatedFooter.props.filter).toBe(SHOW_COMPLETED);\n      });\n\n      it('onClearCompleted should call clearCompleted', () => {\n        const { output, props } = setup();\n        const [,, footer] = output.props.children;\n        footer.props.onClearCompleted();\n        expect(props.actions.clearCompleted).toHaveBeenCalled();\n      });\n\n      it('onClearCompleted shouldnt call clearCompleted if no todos completed', () => {\n        const { output, props } = setup({ todos: [{\n          text: 'Use Redux',\n          completed: false,\n          id: 0\n        }]});\n        const [,, footer] = output.props.children;\n        footer.props.onClearCompleted();\n        expect(props.actions.clearCompleted.calls.length).toBe(0);\n      });\n    });\n\n    describe('todo list', () => {\n      it('should render', () => {\n        const { output, props } = setup();\n        const [, list] = output.props.children;\n        expect(list.type).toBe('ul');\n        expect(list.props.children.length).toBe(2);\n        list.props.children.forEach((item, i) => {\n          expect(item.type).toBe(TodoItem);\n          expect(item.props.todo).toBe(props.todos[i]);\n        });\n      });\n\n      it('should filter items', () => {\n        const { output, renderer, props } = setup();\n        const [,, footer] = output.props.children;\n        footer.props.onShow(SHOW_COMPLETED);\n        const updated = renderer.getRenderOutput();\n        const [, updatedList] = updated.props.children;\n        expect(updatedList.props.children.length).toBe(1);\n        expect(updatedList.props.children[0].props.todo).toBe(props.todos[1]);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "examples/todomvc/test/components/TodoItem.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport TodoItem from '../../components/TodoItem';\nimport TodoTextInput from '../../components/TodoTextInput';\n\nfunction setup( editing = false ) {\n  const props = {\n    todo: {\n      id: 0,\n      text: 'Use Redux',\n      completed: false\n    },\n    editTodo: expect.createSpy(),\n    deleteTodo: expect.createSpy(),\n    completeTodo: expect.createSpy()\n  };\n\n  const renderer = TestUtils.createRenderer();\n\n  renderer.render(\n    <TodoItem {...props} />\n  );\n\n  let output = renderer.getRenderOutput();\n\n  if (editing) {\n    const label = output.props.children.props.children[1];\n    label.props.onDoubleClick({});\n    output = renderer.getRenderOutput();\n  }\n\n  return {\n    props: props,\n    output: output,\n    renderer: renderer\n  };\n}\n\ndescribe('components', () => {\n  describe('TodoItem', () => {\n    it('initial render', () => {\n      const { output } = setup();\n\n      expect(output.type).toBe('li');\n      expect(output.props.className).toBe('');\n\n      const div = output.props.children;\n\n      expect(div.type).toBe('div');\n      expect(div.props.className).toBe('view');\n\n      const [input, label, button] = div.props.children;\n\n      expect(input.type).toBe('input');\n      expect(input.props.checked).toBe(false);\n\n      expect(label.type).toBe('label');\n      expect(label.props.children).toBe('Use Redux');\n\n      expect(button.type).toBe('button');\n      expect(button.props.className).toBe('destroy');\n    });\n\n    it('input onChange should call completeTodo', () => {\n      const { output, props } = setup();\n      const input = output.props.children.props.children[0];\n      input.props.onChange({});\n      expect(props.completeTodo).toHaveBeenCalledWith(0);\n    });\n\n    it('button onClick should call deleteTodo', () => {\n      const { output, props } = setup();\n      const button = output.props.children.props.children[2];\n      button.props.onClick({});\n      expect(props.deleteTodo).toHaveBeenCalledWith(0);\n    });\n\n    it('label onDoubleClick should put component in edit state', () => {\n      const { output, renderer } = setup();\n      const label = output.props.children.props.children[1];\n      label.props.onDoubleClick({});\n      const updated = renderer.getRenderOutput();\n      expect(updated.type).toBe('li');\n      expect(updated.props.className).toBe('editing');\n    });\n\n    it('edit state render', () => {\n      const { output } = setup(true);\n\n      expect(output.type).toBe('li');\n      expect(output.props.className).toBe('editing');\n\n      const input = output.props.children;\n      expect(input.type).toBe(TodoTextInput);\n      expect(input.props.text).toBe('Use Redux');\n      expect(input.props.editing).toBe(true);\n    });\n\n    it('TodoTextInput onSave should call editTodo', () => {\n      const { output, props } = setup(true);\n      output.props.children.props.onSave('Use Redux');\n      expect(props.editTodo).toHaveBeenCalledWith(0, 'Use Redux');\n    });\n\n    it('TodoTextInput onSave should call deleteTodo if text is empty', () => {\n      const { output, props } = setup(true);\n      output.props.children.props.onSave('');\n      expect(props.deleteTodo).toHaveBeenCalledWith(0);\n    });\n\n    it('TodoTextInput onSave should exit component from edit state', () => {\n      const { output, renderer } = setup(true);\n      output.props.children.props.onSave('Use Redux');\n      const updated = renderer.getRenderOutput();\n      expect(updated.type).toBe('li');\n      expect(updated.props.className).toBe('');\n    });\n  });\n});\n"
  },
  {
    "path": "examples/todomvc/test/components/TodoTextInput.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport TodoTextInput from '../../components/TodoTextInput';\n\nfunction setup(propOverrides) {\n  const props = Object.assign({\n    onSave: expect.createSpy(),\n    text: 'Use Redux',\n    placeholder: 'What needs to be done?',\n    editing: false,\n    newTodo: false\n  }, propOverrides);\n\n  const renderer = TestUtils.createRenderer();\n\n  renderer.render(\n    <TodoTextInput {...props} />\n  );\n\n  let output = renderer.getRenderOutput();\n\n  output = renderer.getRenderOutput();\n\n  return {\n    props: props,\n    output: output,\n    renderer: renderer\n  };\n}\n\ndescribe('components', () => {\n  describe('TodoTextInput', () => {\n    it('should render correctly', () => {\n      const { output } = setup();\n      expect(output.props.placeholder).toEqual('What needs to be done?');\n      expect(output.props.value).toEqual('Use Redux');\n      expect(output.props.className).toEqual('');\n    });\n\n    it('should render correctly when editing=true', () => {\n      const { output } = setup({ editing: true });\n      expect(output.props.className).toEqual('edit');\n    });\n\n    it('should render correctly when newTodo=true', () => {\n      const { output } = setup({ newTodo: true });\n      expect(output.props.className).toEqual('new-todo');\n    });\n\n    it('should update value on change', () => {\n      const { output, renderer } = setup();\n      output.props.onChange({ target: { value: 'Use Radox' }});\n      const updated = renderer.getRenderOutput();\n      expect(updated.props.value).toEqual('Use Radox');\n    });\n\n    it('should call onSave on return key press', () => {\n      const { output, props } = setup();\n      output.props.onKeyDown({ which: 13, target: { value: 'Use Redux' }});\n      expect(props.onSave).toHaveBeenCalledWith('Use Redux');\n    });\n\n    it('should reset state on return key press if newTodo', () => {\n      const { output, renderer } = setup({ newTodo: true });\n      output.props.onKeyDown({ which: 13, target: { value: 'Use Redux' }});\n      const updated = renderer.getRenderOutput();\n      expect(updated.props.value).toEqual('');\n    });\n\n    it('should call onSave on blur', () => {\n      const { output, props } = setup();\n      output.props.onBlur({ target: { value: 'Use Redux' }});\n      expect(props.onSave).toHaveBeenCalledWith('Use Redux');\n    });\n\n    it('shouldnt call onSave on blur if newTodo', () => {\n      const { output, props } = setup({ newTodo: true });\n      output.props.onBlur({ target: { value: 'Use Redux' }});\n      expect(props.onSave.calls.length).toBe(0);\n    });\n  });\n});\n"
  },
  {
    "path": "examples/todomvc/test/reducers/todos.spec.js",
    "content": "import expect from 'expect';\nimport todos from '../../reducers/todos';\nimport * as types from '../../constants/ActionTypes';\n\ndescribe('todos reducer', () => {\n  it('should handle initial state', () => {\n    expect(\n      todos(undefined, {})\n    ).toEqual([{\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle ADD_TODO', () => {\n    expect(\n      todos([], {\n        type: types.ADD_TODO,\n        text: 'Run the tests'\n      })\n    ).toEqual([{\n      text: 'Run the tests',\n      completed: false,\n      id: 0\n    }]);\n\n    expect(\n      todos([{\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.ADD_TODO,\n        text: 'Run the tests'\n      })\n    ).toEqual([{\n      text: 'Run the tests',\n      completed: false,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: false,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.ADD_TODO,\n        text: 'Fix the tests'\n      })\n    ).toEqual([{\n      text: 'Fix the tests',\n      completed: false,\n      id: 2\n    }, {\n      text: 'Run the tests',\n      completed: false,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle DELETE_TODO', () => {\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: false,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.DELETE_TODO,\n        id: 1\n      })\n    ).toEqual([{\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle EDIT_TODO', () => {\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: false,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.EDIT_TODO,\n        text: 'Fix the tests',\n        id: 1\n      })\n    ).toEqual([{\n      text: 'Fix the tests',\n      completed: false,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle COMPLETE_TODO', () => {\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: false,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.COMPLETE_TODO,\n        id: 1\n      })\n    ).toEqual([{\n      text: 'Run the tests',\n      completed: true,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle COMPLETE_ALL', () => {\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: true,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.COMPLETE_ALL\n      })\n    ).toEqual([{\n      text: 'Run the tests',\n      completed: true,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: true,\n      id: 0\n    }]);\n\n    // Unmark if all todos are currently completed\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: true,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: true,\n        id: 0\n      }], {\n        type: types.COMPLETE_ALL\n      })\n    ).toEqual([{\n      text: 'Run the tests',\n      completed: false,\n      id: 1\n    }, {\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should handle CLEAR_COMPLETED', () => {\n    expect(\n      todos([{\n        text: 'Run the tests',\n        completed: true,\n        id: 1\n      }, {\n        text: 'Use Redux',\n        completed: false,\n        id: 0\n      }], {\n        type: types.CLEAR_COMPLETED\n      })\n    ).toEqual([{\n      text: 'Use Redux',\n      completed: false,\n      id: 0\n    }]);\n  });\n\n  it('should not generate duplicate ids after CLEAR_COMPLETED', () => {\n    expect(\n      [{\n        type: types.COMPLETE_TODO,\n        id: 0\n      }, {\n        type: types.CLEAR_COMPLETED\n      }, {\n        type: types.ADD_TODO,\n        text: 'Write more tests'\n      }].reduce(todos, [{\n        id: 0,\n        completed: false,\n        text: 'Use Redux'\n      }, {\n        id: 1,\n        completed: false,\n        text: 'Write tests'\n      }])\n    ).toEqual([{\n      text: 'Write more tests',\n      completed: false,\n      id: 2\n    }, {\n      text: 'Write tests',\n      completed: false,\n      id: 1\n    }]);\n  });\n});\n"
  },
  {
    "path": "examples/todomvc/test/setup.js",
    "content": "import { jsdom } from 'jsdom';\n\nglobal.document = jsdom('<!doctype html><html><body></body></html>');\nglobal.window = document.defaultView;\nglobal.navigator = global.window.navigator;\n"
  },
  {
    "path": "examples/todomvc/webpack.config.js",
    "content": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n  mode: 'development',\n  devtool: 'source-map',\n  entry: [\n    'webpack-hot-middleware/client',\n    './index'\n  ],\n  output: {\n    path: path.join(__dirname, 'dist'),\n    filename: 'bundle.js',\n    publicPath: '/static/'\n  },\n  plugins: [\n    new webpack.HotModuleReplacementPlugin()\n  ],\n  module: {\n    rules: [{\n      test: /\\.js$/,\n      loaders: ['babel-loader'],\n      exclude: /node_modules/\n    }, {\n      test: /\\.css?$/,\n      loaders: ['style-loader', 'raw-loader'],\n      include: __dirname\n    }]\n  }\n};\n"
  },
  {
    "path": "gulpfile.babel.js",
    "content": "import fs from 'fs';\nimport gulp from 'gulp';\nimport gutil from 'gulp-util';\nimport jade from 'gulp-pug';\nimport rename from 'gulp-rename';\nimport zip from 'gulp-zip';\nimport webpack from 'webpack';\nimport mocha from 'gulp-mocha';\nimport crdv from 'chromedriver';\nimport devConfig from './webpack/dev.config';\nimport prodConfig from './webpack/prod.config';\nimport wrapConfig from './webpack/wrap.config';\n\nfunction copy(dest) {\n  gulp.src('./src/assets/**/*').pipe(gulp.dest(dest));\n}\n\n/*\n * common tasks\n */\ngulp.task('replace-webpack-code', () => {\n  const replaceTasks = [{\n    from: './webpack/replace/JsonpMainTemplate.runtime.js',\n    to: './node_modules/webpack/lib/JsonpMainTemplate.runtime.js'\n  }, {\n    from: './webpack/replace/log-apply-result.js',\n    to: './node_modules/webpack/hot/log-apply-result.js'\n  }];\n  replaceTasks.forEach(task => fs.writeFileSync(task.to, fs.readFileSync(task.from)));\n});\n\n/*\n * dev tasks\n */\n\ngulp.task('webpack:dev', (callback) => {\n  webpack(devConfig, (err, stats) => {\n    if (err) {\n      throw new gutil.PluginError('webpack:dev', err);\n    }\n    gutil.log('[webpack:dev]', stats.toString({ colors: true }));\n  });\n  callback();\n});\n\ngulp.task('views:dev', () => {\n  gulp.src('./src/browser/views/*.pug')\n    .pipe(jade({\n      locals: { env: 'dev' }\n    }))\n    .pipe(gulp.dest('./dev'));\n});\n\ngulp.task('copy:dev', () => {\n  gulp.src('./src/browser/extension/manifest.json')\n    .pipe(rename('manifest.json'))\n    .pipe(gulp.dest('./dev'));\n  copy('./dev');\n});\n\n/*\n * build tasks\n */\n\ngulp.task('webpack:build:extension', (callback) => {\n  function webpackProcess(config) {\n    return new Promise((resolve, reject) =>\n      webpack(config, (err, stats) => {\n        if (err) {\n          reject(new gutil.PluginError('webpack:build', err));\n        }\n        gutil.log('[webpack:build]', stats.toString({ colors: true }));\n        resolve();\n      })\n    );\n  }\n  webpackProcess(wrapConfig)\n    .then(() => webpackProcess(prodConfig))\n    .then(() => {\n      const dest = './build/extension';\n      fs.rename(\n        `${dest}/js/redux-devtools-extension.bundle.js`,\n        `${dest}/js/redux-devtools-extension.js`,\n        callback\n      );\n    });\n});\n\ngulp.task('views:build:extension', () => {\n  gulp.src([\n    './src/browser/views/*.pug'\n  ])\n    .pipe(jade({\n      locals: { env: 'prod' }\n    }))\n    .pipe(gulp.dest('./build/extension'));\n});\n\ngulp.task('copy:build:extension', () => {\n  gulp.src('./src/browser/extension/manifest.json')\n    .pipe(rename('manifest.json'))\n    .pipe(gulp.dest('./build/extension'));\n  copy('./build/extension');\n});\n\ngulp.task('copy:build:firefox', ['build:extension'], () => {\n  gulp.src([\n    './build/extension/**', '!./build/extension/js/redux-devtools-extension.js'\n  ])\n    .pipe(gulp.dest('./build/firefox'))\n    .on('finish', function() {\n      gulp.src('./src/browser/firefox/manifest.json')\n        .pipe(gulp.dest('./build/firefox'));\n    });\n  copy('./build/firefox');\n});\n\n/*\n * compress task\n */\n\ngulp.task('compress:extension', () => {\n  gulp.src('build/extension/**')\n    .pipe(zip('extension.zip'))\n    .pipe(gulp.dest('./build'));\n});\n\ngulp.task('compress:firefox', () => {\n  gulp.src('build/firefox/**')\n    .pipe(zip('firefox.zip'))\n    .pipe(gulp.dest('./build'));\n});\n\n/*\n * watch tasks\n */\n\ngulp.task('views:watch', () => {\n  gulp.watch('./src/browser/views/*.pug', ['views:dev']);\n});\n\ngulp.task('copy:watch', () => {\n  gulp.watch(['./src/browser/extension/manifest.json', './src/assets/**/*'], ['copy:dev']);\n});\n\ngulp.task('test:chrome', () => {\n  crdv.start();\n  return gulp.src('./test/chrome/*.spec.js')\n    .pipe(mocha({ require: ['babel-polyfill', 'co-mocha'] }))\n    .on('end', () => crdv.stop());\n});\n\ngulp.task('test:electron', () => {\n  crdv.start();\n  return gulp.src('./test/electron/*.spec.js')\n    .pipe(mocha({ require: ['babel-polyfill', 'co-mocha'] }))\n    .on('end', () => crdv.stop());\n});\n\ngulp.task('default', ['replace-webpack-code', 'webpack:dev', 'views:dev', 'copy:dev', 'views:watch', 'copy:watch']);\ngulp.task('build:extension', ['replace-webpack-code', 'webpack:build:extension', 'views:build:extension', 'copy:build:extension']);\ngulp.task('build:firefox', ['copy:build:firefox']);\n"
  },
  {
    "path": "npm-package/README.md",
    "content": "# Redux DevTools Extension's helper\n\n[![Join the chat at https://gitter.im/zalmoxisus/redux-devtools-extension](https://badges.gitter.im/zalmoxisus/redux-devtools-extension.svg)](https://gitter.im/zalmoxisus/redux-devtools-extension?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n\n## Usage\n\n  Install:\n  ```\n  npm install --save redux-devtools-extension\n  ```\n  and use like that:\n  ```js\n  import { createStore, applyMiddleware } from 'redux';\n  import { composeWithDevTools } from 'redux-devtools-extension';\n\n  const store = createStore(reducer, composeWithDevTools(\n    applyMiddleware(...middleware),\n    // other store enhancers if any\n  ));\n  ```\n  or if needed to apply [extension’s options](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#windowdevtoolsextensionconfig):\n  ```js\n  import { createStore, applyMiddleware } from 'redux';\n  import { composeWithDevTools } from 'redux-devtools-extension';\n\n  const composeEnhancers = composeWithDevTools({\n    // Specify here name, actionsBlacklist, actionsCreators and other options\n  });\n  const store = createStore(reducer, composeEnhancers(\n    applyMiddleware(...middleware),\n    // other store enhancers if any\n  ));\n  ```  \n  There’re just [few lines of code](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/index.js). If you don’t want to allow the extension in production, just use ‘redux-devtools-extension/developmentOnly’ instead of ‘redux-devtools-extension’.\n\n## License\n\nMIT\n"
  },
  {
    "path": "npm-package/developmentOnly.d.ts",
    "content": "export * from \"redux-devtools-extension\";\n"
  },
  {
    "path": "npm-package/developmentOnly.js",
    "content": "\"use strict\";\n\nvar compose = require('redux').compose;\n\nexports.__esModule = true;\nexports.composeWithDevTools = (\n  process.env.NODE_ENV !== 'production' && typeof window !== 'undefined' &&\n  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?\n    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ :\n    function() {\n      if (arguments.length === 0) return undefined;\n      if (typeof arguments[0] === 'object') return compose;\n      return compose.apply(null, arguments);\n    }\n);\n\nexports.devToolsEnhancer = (\n  process.env.NODE_ENV !== 'production' && typeof window !== 'undefined' &&\n  window.__REDUX_DEVTOOLS_EXTENSION__ ?\n    window.__REDUX_DEVTOOLS_EXTENSION__ :\n    function() { return function(noop) { return noop; } }\n);\n"
  },
  {
    "path": "npm-package/index.d.ts",
    "content": "import {Action, ActionCreator, StoreEnhancer, compose} from \"redux\";\n\nexport interface EnhancerOptions {\n  /**\n   * the instance name to be showed on the monitor page. Default value is `document.title`.\n   * If not specified and there's no document title, it will consist of `tabId` and `instanceId`.\n   */\n  name?: string;\n  /**\n   * action creators functions to be available in the Dispatcher.\n   */\n  actionCreators?: ActionCreator<any>[] | {[key: string]: ActionCreator<any>};\n  /**\n   * if more than one action is dispatched in the indicated interval, all new actions will be collected and sent at once.\n   * It is the joint between performance and speed. When set to `0`, all actions will be sent instantly.\n   * Set it to a higher value when experiencing perf issues (also `maxAge` to a lower value).\n   *\n   * @default 500 ms.\n   */\n  latency?: number;\n  /**\n   * (> 1) - maximum allowed actions to be stored in the history tree. The oldest actions are removed once maxAge is reached. It's critical for performance.\n   *\n   * @default 50\n   */\n  maxAge?: number;\n  /**\n   * - `undefined` - will use regular `JSON.stringify` to send data (it's the fast mode).\n   * - `false` - will handle also circular references.\n   * - `true` - will handle also date, regex, undefined, error objects, symbols, maps, sets and functions.\n   * - object, which contains `date`, `regex`, `undefined`, `error`, `symbol`, `map`, `set` and `function` keys.\n   *   For each of them you can indicate if to include (by setting as `true`).\n   *   For `function` key you can also specify a custom function which handles serialization.\n   *   See [`jsan`](https://github.com/kolodny/jsan) for more details.\n   */\n  serialize?: boolean | {\n  date?: boolean;\n  regex?: boolean;\n  undefined?: boolean;\n  error?: boolean;\n  symbol?: boolean;\n  map?: boolean;\n  set?: boolean;\n  function?: boolean | Function;\n  };\n  /**\n   * function which takes `action` object and id number as arguments, and should return `action` object back.\n   */\n  actionSanitizer?: <A extends Action>(action: A, id: number) => A;\n  /**\n   * function which takes `state` object and index as arguments, and should return `state` object back.\n   */\n  stateSanitizer?: <S>(state: S, index: number) => S;\n  /**\n   * *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers).\n   * If `actionsWhitelist` specified, `actionsBlacklist` is ignored.\n   */\n  actionsBlacklist?: string | string[];\n  /**\n   * *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers).\n   * If `actionsWhitelist` specified, `actionsBlacklist` is ignored.\n   */\n  actionsWhitelist?: string | string[];\n  /**\n   * called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor.\n   * Use it as a more advanced version of `actionsBlacklist`/`actionsWhitelist` parameters.\n   */\n  predicate?: <S, A extends Action>(state: S, action: A) => boolean;\n  /**\n   * if specified as `false`, it will not record the changes till clicking on `Start recording` button.\n   * Available only for Redux enhancer, for others use `autoPause`.\n   *\n   * @default true\n   */\n  shouldRecordChanges?: boolean;\n  /**\n   * if specified, whenever clicking on `Pause recording` button and there are actions in the history log, will add this action type.\n   * If not specified, will commit when paused. Available only for Redux enhancer.\n   *\n   * @default \"@@PAUSED\"\"\n   */\n  pauseActionType?: string;\n  /**\n   * auto pauses when the extension’s window is not opened, and so has zero impact on your app when not in use.\n   * Not available for Redux enhancer (as it already does it but storing the data to be sent).\n   *\n   * @default false\n   */\n  autoPause?: boolean;\n  /**\n   * if specified as `true`, it will not allow any non-monitor actions to be dispatched till clicking on `Unlock changes` button.\n   * Available only for Redux enhancer.\n   *\n   * @default false\n   */\n  shouldStartLocked?: boolean;\n  /**\n   * if set to `false`, will not recompute the states on hot reloading (or on replacing the reducers). Available only for Redux enhancer.\n   *\n   * @default true\n   */\n  shouldHotReload?: boolean;\n  /**\n   * if specified as `true`, whenever there's an exception in reducers, the monitors will show the error message, and next actions will not be dispatched.\n   *\n   * @default false\n   */\n  shouldCatchErrors?: boolean;\n  /**\n   * If you want to restrict the extension, specify the features you allow.\n   * If not specified, all of the features are enabled. When set as an object, only those included as `true` will be allowed.\n   * Note that except `true`/`false`, `import` and `export` can be set as `custom` (which is by default for Redux enhancer), meaning that the importing/exporting occurs on the client side.\n   * Otherwise, you'll get/set the data right from the monitor part.\n   */\n  features?: {\n    /**\n     * start/pause recording of dispatched actions\n     */\n    pause?: boolean;\n    /**\n     * lock/unlock dispatching actions and side effects\n     */\n    lock?: boolean;\n    /**\n     * persist states on page reloading\n     */\n    persist?: boolean;\n    /**\n     * export history of actions in a file\n     */\n    export?: boolean | \"custom\";\n    /**\n     * import history of actions from a file\n     */\n    import?: boolean | \"custom\";\n    /**\n     * jump back and forth (time travelling)\n     */\n    jump?: boolean;\n    /**\n     * skip (cancel) actions\n     */\n    skip?: boolean;\n    /**\n     * drag and drop actions in the history list\n     */\n    reorder?: boolean;\n    /**\n     * dispatch custom actions or action creators\n     */\n    dispatch?: boolean;\n    /**\n     * generate tests for the selected actions\n     */\n    test?: boolean;\n  };\n  /**\n   * Set to true or a stacktrace-returning function to record call stack traces for dispatched actions.\n   * Defaults to false.\n   */\n  trace?: boolean | (<A extends Action>(action: A) => string);\n  /**\n   * The maximum number of stack trace entries to record per action. Defaults to 10.\n   */\n  traceLimit?: number;\n}\n\nexport function composeWithDevTools<StoreExt, StateExt>(...funcs: Array<StoreEnhancer<StoreExt>>): StoreEnhancer<StoreExt>;\nexport function composeWithDevTools(options: EnhancerOptions): typeof compose;\nexport function devToolsEnhancer(options: EnhancerOptions): StoreEnhancer<any>;\n"
  },
  {
    "path": "npm-package/index.js",
    "content": "\"use strict\";\n\nvar compose = require('redux').compose;\n\nexports.__esModule = true;\nexports.composeWithDevTools = (\n  typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?\n    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ :\n    function() {\n      if (arguments.length === 0) return undefined;\n      if (typeof arguments[0] === 'object') return compose;\n      return compose.apply(null, arguments);\n    }\n);\n\nexports.devToolsEnhancer = (\n  typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ?\n    window.__REDUX_DEVTOOLS_EXTENSION__ :\n    function() { return function(noop) { return noop; } }\n);\n"
  },
  {
    "path": "npm-package/logOnly.d.ts",
    "content": "export * from \"redux-devtools-extension\";\n"
  },
  {
    "path": "npm-package/logOnly.js",
    "content": "\"use strict\";\n\nvar assign = require('./utils/assign');\nvar compose = require('redux').compose;\n\nfunction enhancer() {\n  var config = arguments[0] || {};\n  config.features = { pause: true, export: true, test: true };\n  config.type = 'redux';\n  if (config.autoPause === undefined) config.autoPause = true;\n  if (config.latency === undefined) config.latency = 500;\n\n  return function(createStore) {\n    return function(reducer, preloadedState, enhancer) {\n      var store = createStore(reducer, preloadedState, enhancer);\n      var origDispatch = store.dispatch;\n\n      var devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect(config);\n      devTools.init(store.getState());\n\n      var dispatch = function(action) {\n        var r = origDispatch(action);\n        devTools.send(action, store.getState());\n        return r;\n      };\n\n      if (Object.assign) return Object.assign(store, { dispatch: dispatch });\n      return assign(store, 'dispatch', dispatch);\n    }\n  }\n}\n\nfunction composeWithEnhancer(config) {\n  return function () {\n    return compose(compose.apply(null, arguments), enhancer(config));\n  }\n}\n\nexports.__esModule = true;\nexports.composeWithDevTools = function() {\n  if (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__) {\n    if (arguments.length === 0) return enhancer();\n    if (typeof arguments[0] === 'object') return composeWithEnhancer(arguments[0]);\n    return composeWithEnhancer().apply(null, arguments);\n  }\n\n  if (arguments.length === 0) return undefined;\n  if (typeof arguments[0] === 'object') return compose;\n  return compose.apply(null, arguments);\n};\n\nexports.devToolsEnhancer = (\n  typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ?\n    enhancer :\n    function() { return function(noop) { return noop; } }\n);\n"
  },
  {
    "path": "npm-package/logOnlyInProduction.d.ts",
    "content": "export * from \"redux-devtools-extension\";\n"
  },
  {
    "path": "npm-package/logOnlyInProduction.js",
    "content": "\"use strict\";\n\nvar compose = require('redux').compose;\nvar logOnly = require('./logOnly');\n\nexports.__esModule = true;\nexports.composeWithDevTools = (\n  process.env.NODE_ENV === 'production' ? logOnly.composeWithDevTools :\n    typeof window !== 'undefined' &&\n    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?\n      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ :\n      function() {\n        if (arguments.length === 0) return undefined;\n        if (typeof arguments[0] === 'object') return compose;\n        return compose.apply(null, arguments);\n      }\n);\n\nexports.devToolsEnhancer = (\n  process.env.NODE_ENV === 'production' ? logOnly.devToolsEnhancer :\n    typeof window !== 'undefined' &&\n    window.__REDUX_DEVTOOLS_EXTENSION__ ?\n      window.__REDUX_DEVTOOLS_EXTENSION__ :\n      function() { return function(noop) { return noop; } }\n);\n"
  },
  {
    "path": "npm-package/package.json",
    "content": "{\n  \"name\": \"redux-devtools-extension\",\n  \"version\": \"2.13.8\",\n  \"description\": \"Wrappers for Redux DevTools Extension.\",\n  \"main\": \"index.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/zalmoxisus/redux-devtools-extension\"\n  },\n  \"homepage\": \"https://github.com/zalmoxisus/redux-devtools-extension\",\n  \"author\": \"Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)\",\n  \"license\": \"MIT\",\n  \"peerDependencies\": {\n    \"redux\": \"^3.1.0 || ^4.0.0\"\n  }\n}\n"
  },
  {
    "path": "npm-package/utils/assign.js",
    "content": "var objectKeys = Object.keys || function (obj) {\n    var keys = [];\n    for (var key in obj) {\n      if ({}.hasOwnProperty.call(obj, key)) keys.push(key);\n    }\n    return keys;\n  };\n\nfunction assign(obj, newKey, newValue) {\n  var keys = objectKeys(obj);\n  var copy = {};\n\n  for (var i = 0, l = keys.length; i < l; i++) {\n    var key = keys[i];\n    copy[key] = obj[key];\n  }\n\n  copy[newKey] = newValue;\n  return copy;\n}\n\nmodule.exports = assign;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"version\": \"2.17.1\",\n  \"name\": \"remotedev-redux-devtools-extension\",\n  \"description\": \"Redux Developer Tools for debugging application state changes.\",\n  \"scripts\": {\n    \"start\": \"gulp\",\n    \"build:extension\": \"rimraf build/extension && cross-env BABEL_ENV=production gulp build:extension\",\n    \"build:firefox\": \"cross-env BABEL_ENV=production gulp build:firefox\",\n    \"build:examples\": \"babel-node examples/buildAll.js\",\n    \"precompress:extension\": \"npm run lint && npm run test:app && npm run build:extension && npm run test:chrome && npm run test:electron\",\n    \"precompress:firefox\": \"npm run lint && npm run build:firefox && npm run test:app\",\n    \"compress:extension\": \"gulp compress:extension\",\n    \"compress:firefox\": \"gulp compress:firefox\",\n    \"docs:clean\": \"rimraf _book\",\n    \"docs:prepare\": \"gitbook install\",\n    \"docs:build\": \"npm run docs:prepare && gitbook build\",\n    \"docs:watch\": \"npm run docs:prepare && gitbook serve\",\n    \"docs:publish\": \"npm run docs:clean && npm run docs:build && cd _book && git init && git commit --allow-empty -m 'update book' && git checkout -b gh-pages && touch .nojekyll && git add . && git commit -am 'update book' && git push git@github.com:zalmoxisus/redux-devtools-extension gh-pages --force\",\n    \"clean\": \"rimraf build/ && rimraf dev/\",\n    \"lint\": \"eslint .\",\n    \"test:app\": \"cross-env BABEL_ENV=test mocha --require test/app/setup.js --recursive test/app\",\n    \"test:chrome\": \"gulp test:chrome\",\n    \"test:electron\": \"gulp test:electron && rimraf test/electron/tmp\",\n    \"test\": \"npm run test:app && npm run build:extension && npm run test:chrome && npm run test:electron\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/zalmoxisus/redux-devtools-extension\"\n  },\n  \"homepage\": \"https://github.com/zalmoxisus/redux-devtools-extension\",\n  \"author\": \"Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)\",\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"babel-cli\": \"^6.18.0\",\n    \"babel-core\": \"^6.21.0\",\n    \"babel-eslint\": \"^7.1.0\",\n    \"babel-loader\": \"^7.1.1\",\n    \"babel-plugin-add-module-exports\": \"^0.2.1\",\n    \"babel-plugin-react-transform\": \"^2.0.2\",\n    \"babel-plugin-transform-decorators-legacy\": \"^1.2.0\",\n    \"babel-polyfill\": \"^6.20.0\",\n    \"babel-preset-es2015\": \"^6.18.0\",\n    \"babel-preset-react\": \"^6.16.0\",\n    \"babel-preset-stage-0\": \"^6.16.0\",\n    \"babel-register\": \"^6.18.0\",\n    \"chromedriver\": \"^2.35.0\",\n    \"co-mocha\": \"^1.1.3\",\n    \"cross-env\": \"^1.0.8\",\n    \"electron\": \"^2.0.2\",\n    \"enzyme\": \"^2.3.0\",\n    \"eslint\": \"^1.7.1\",\n    \"eslint-config-airbnb\": \"^0.1.0\",\n    \"eslint-plugin-react\": \"^3.2.3\",\n    \"expect\": \"^1.20.1\",\n    \"gitbook-cli\": \"^2.3.0\",\n    \"gulp\": \"^3.9.1\",\n    \"gulp-mocha\": \"^3.0.1\",\n    \"gulp-pug\": \"^3.1.0\",\n    \"gulp-rename\": \"^1.2.2\",\n    \"gulp-util\": \"^3.0.7\",\n    \"gulp-zip\": \"^3.0.2\",\n    \"jsdom\": \"^9.8.3\",\n    \"mocha\": \"^3.1.2\",\n    \"raw-loader\": \"^0.5.1\",\n    \"react-addons-test-utils\": \"^15.4.1\",\n    \"react-transform-catch-errors\": \"^1.0.0\",\n    \"react-transform-hmr\": \"^1.0.1\",\n    \"rimraf\": \"^2.5.3\",\n    \"selenium-webdriver\": \"^3.0.1\",\n    \"sinon-chrome\": \"^1.1.2\",\n    \"style-loader\": \"^0.18.2\",\n    \"webpack\": \"^4.27.1\"\n  },\n  \"dependencies\": {\n    \"jsan\": \"^3.1.13\",\n    \"lodash\": \"^4.17.2\",\n    \"react\": \"^15.4.1\",\n    \"react-dom\": \"^15.4.1\",\n    \"react-icons\": \"^2.2.1\",\n    \"react-json-tree\": \"^0.10.9\",\n    \"react-redux\": \"^4.4.5\",\n    \"redux\": \"^3.5.2\",\n    \"redux-devtools\": \"^3.4.1\",\n    \"redux-devtools-instrument\": \"^1.9.6\",\n    \"remotedev-app\": \"^0.10.13-beta\",\n    \"remotedev-monitor-components\": \"^0.0.5\",\n    \"remotedev-serialize\": \"^0.1.8\",\n    \"remotedev-slider\": \"^1.1.1\",\n    \"remotedev-utils\": \"0.0.1\",\n    \"terser-webpack-plugin\": \"^1.1.0\"\n  }\n}\n"
  },
  {
    "path": "src/app/api/filters.js",
    "content": "import mapValues from 'lodash/mapValues';\n\nexport const FilterState = {\n  DO_NOT_FILTER: 'DO_NOT_FILTER',\n  BLACKLIST_SPECIFIC: 'BLACKLIST_SPECIFIC',\n  WHITELIST_SPECIFIC: 'WHITELIST_SPECIFIC'\n};\n\nexport function getLocalFilter(config) {\n  if (config.actionsBlacklist || config.actionsWhitelist) {\n    return {\n      whitelist: Array.isArray(config.actionsWhitelist) ? config.actionsWhitelist.join('|') : config.actionsWhitelist,\n      blacklist: Array.isArray(config.actionsBlacklist) ? config.actionsBlacklist.join('|') : config.actionsBlacklist\n    };\n  }\n  return undefined;\n}\n\nexport const noFiltersApplied = (localFilter) => (\n  // !predicate &&\n  !localFilter && (!window.devToolsOptions || !window.devToolsOptions.filter ||\n  window.devToolsOptions.filter === FilterState.DO_NOT_FILTER)\n);\n\nexport function isFiltered(action, localFilter) {\n  if (\n    noFiltersApplied(localFilter) ||\n    typeof action !== 'string' && typeof action.type.match !== 'function'\n  ) return false;\n\n  const { whitelist, blacklist } = localFilter || window.devToolsOptions || {};\n  const actionType = action.type || action;\n  return (\n    whitelist && !actionType.match(whitelist) ||\n    blacklist && actionType.match(blacklist)\n  );\n}\n\nfunction filterActions(actionsById, actionSanitizer) {\n  if (!actionSanitizer) return actionsById;\n  return mapValues(actionsById, (action, id) => (\n    { ...action, action: actionSanitizer(action.action, id) }\n  ));\n}\n\nfunction filterStates(computedStates, stateSanitizer) {\n  if (!stateSanitizer) return computedStates;\n  return computedStates.map((state, idx) => (\n    { ...state, state: stateSanitizer(state.state, idx) }\n  ));\n}\n\nexport function filterState(state, type, localFilter, stateSanitizer, actionSanitizer, nextActionId, predicate) {\n  if (type === 'ACTION') return !stateSanitizer ? state : stateSanitizer(state, nextActionId - 1);\n  else if (type !== 'STATE') return state;\n\n  if (predicate || !noFiltersApplied(localFilter)) {\n    const filteredStagedActionIds = [];\n    const filteredComputedStates = [];\n    const sanitizedActionsById = actionSanitizer && {};\n    const { actionsById } = state;\n    const { computedStates } = state;\n\n    state.stagedActionIds.forEach((id, idx) => {\n      const liftedAction = actionsById[id];\n      if (!liftedAction) return;\n      const currAction = liftedAction.action;\n      const liftedState = computedStates[idx];\n      const currState = liftedState.state;\n      if (idx) {\n        if (predicate && !predicate(currState, currAction)) return;\n        if (isFiltered(currAction, localFilter)) return;\n      }\n\n      filteredStagedActionIds.push(id);\n      filteredComputedStates.push(\n        stateSanitizer ? { ...liftedState, state: stateSanitizer(currState, idx) } : liftedState\n      );\n      if (actionSanitizer) {\n        sanitizedActionsById[id] = {\n          ...liftedAction, action: actionSanitizer(currAction, id)\n        };\n      }\n    });\n\n    return {\n      ...state,\n      actionsById: sanitizedActionsById || actionsById,\n      stagedActionIds: filteredStagedActionIds,\n      computedStates: filteredComputedStates\n    };\n  }\n\n  if (!stateSanitizer && !actionSanitizer) return state;\n  return {\n    ...state,\n    actionsById: filterActions(state.actionsById, actionSanitizer),\n    computedStates: filterStates(state.computedStates, stateSanitizer)\n  };\n}\n\nexport function startingFrom(\n  sendingActionId, state, localFilter, stateSanitizer, actionSanitizer, predicate\n) {\n  const stagedActionIds = state.stagedActionIds;\n  if (sendingActionId <= stagedActionIds[1]) return state;\n  const index = stagedActionIds.indexOf(sendingActionId);\n  if (index === -1) return state;\n\n  const shouldFilter = predicate || !noFiltersApplied(localFilter);\n  const filteredStagedActionIds = shouldFilter ? [0] : stagedActionIds;\n  const actionsById = state.actionsById;\n  const computedStates = state.computedStates;\n  const newActionsById = {};\n  const newComputedStates = [];\n  let key;\n  let currAction;\n  let currState;\n\n  for (let i = shouldFilter ? 1 : index; i < stagedActionIds.length; i++) {\n    key = stagedActionIds[i];\n    currAction = actionsById[key];\n    currState = computedStates[i];\n\n    if (shouldFilter) {\n      if (\n        predicate && !predicate(currState.state, currAction.action) ||\n        isFiltered(currAction.action, localFilter)\n      ) continue;\n      filteredStagedActionIds.push(key);\n      if (i < index) continue;\n    }\n\n    newActionsById[key] = !actionSanitizer ? currAction :\n      { ...currAction, action: actionSanitizer(currAction.action, key) };\n    newComputedStates.push(\n      !stateSanitizer ? currState : { ...currState, state: stateSanitizer(currState.state, i) }\n    );\n  }\n\n  if (newComputedStates.length === 0) return undefined;\n\n  return {\n    actionsById: newActionsById,\n    computedStates: newComputedStates,\n    stagedActionIds: filteredStagedActionIds,\n    currentStateIndex: state.currentStateIndex,\n    nextActionId: state.nextActionId\n  };\n}\n"
  },
  {
    "path": "src/app/api/generateInstanceId.js",
    "content": "let id = 0;\n\nexport default function generateId(instanceId) {\n  return instanceId || ++id;\n}\n"
  },
  {
    "path": "src/app/api/importState.js",
    "content": "import mapValues from 'lodash/mapValues';\nimport jsan from 'jsan';\nimport seralizeImmutable from 'remotedev-serialize/immutable/serialize';\n\nfunction deprecate(param) {\n  console.warn(`\\`${param}\\` parameter for Redux DevTools Extension is deprecated. Use \\`serialize\\` parameter instead: https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1`); // eslint-disable-line\n}\n\nexport default function importState(state, { deserializeState, deserializeAction, serialize }) {\n  if (!state) return undefined;\n  let parse = jsan.parse;\n  if (serialize) {\n    if (serialize.immutable) {\n      parse = v => jsan.parse(v, seralizeImmutable(\n        serialize.immutable, serialize.refs, serialize.replacer, serialize.reviver\n      ).reviver);\n    } else if (serialize.reviver) {\n      parse = v => jsan.parse(v, serialize.reviver);\n    }\n  }\n\n  let preloadedState;\n  let nextLiftedState = parse(state);\n  if (nextLiftedState.payload) {\n    if (nextLiftedState.preloadedState) preloadedState = parse(nextLiftedState.preloadedState);\n    nextLiftedState = parse(nextLiftedState.payload);\n  }\n  if (deserializeState) {\n    deprecate('deserializeState');\n    if (typeof nextLiftedState.computedStates !== 'undefined') {\n      nextLiftedState.computedStates = nextLiftedState.computedStates.map(computedState => ({\n        ...computedState,\n        state: deserializeState(computedState.state)\n      }));\n    }\n    if (typeof nextLiftedState.committedState !== 'undefined') {\n      nextLiftedState.committedState = deserializeState(nextLiftedState.committedState);\n    }\n    if (typeof preloadedState !== 'undefined') {\n      preloadedState = deserializeState(preloadedState);\n    }\n  }\n  if (deserializeAction) {\n    deprecate('deserializeAction');\n    nextLiftedState.actionsById = mapValues(nextLiftedState.actionsById, liftedAction => ({\n      ...liftedAction,\n      action: deserializeAction(liftedAction.action)\n    }));\n  }\n\n  return { nextLiftedState, preloadedState };\n}\n"
  },
  {
    "path": "src/app/api/index.js",
    "content": "import jsan from 'jsan';\nimport throttle from 'lodash/throttle';\nimport seralizeImmutable from 'remotedev-serialize/immutable/serialize';\nimport { getActionsArray } from 'remotedev-utils';\nimport { getLocalFilter, isFiltered } from './filters';\nimport importState from './importState';\nimport generateId from './generateInstanceId';\n\nconst listeners = {};\nexport const source = '@devtools-page';\n\nfunction windowReplacer(key, value) {\n  if (value && value.window === value) {\n    return '[WINDOW]';\n  }\n  return value;\n}\n\nfunction tryCatchStringify(obj) {\n  try {\n    return JSON.stringify(obj);\n  } catch (err) {\n    /* eslint-disable no-console */\n    if (process.env.NODE_ENV !== 'production') console.log('Failed to stringify', err);\n    /* eslint-enable no-console */\n    return jsan.stringify(obj, windowReplacer, null, { circular: '[CIRCULAR]', date: true });\n  }\n}\n\nlet stringifyWarned;\nfunction stringify(obj, serialize) {\n  const str = typeof serialize === 'undefined' ? tryCatchStringify(obj) :\n    jsan.stringify(obj, serialize.replacer, null, serialize.options);\n\n  if (!stringifyWarned && str && str.length > 16 * 1024 * 1024) { // 16 MB\n    /* eslint-disable no-console */\n    console.warn('Application state or actions payloads are too large making Redux DevTools serialization slow and consuming a lot of memory. See https://git.io/fpcP5 on how to configure it.');\n    /* eslint-enable no-console */\n    stringifyWarned = true;\n  }\n\n  return str;\n}\n\nexport function getSeralizeParameter(config, param) {\n  const serialize = config.serialize;\n  if (serialize) {\n    if (serialize === true) return { options: true };\n    if (serialize.immutable) {\n      const immutableSerializer = seralizeImmutable(\n        serialize.immutable, serialize.refs, serialize.replacer, serialize.reviver\n      );\n      return {\n        replacer: immutableSerializer.replacer,\n        reviver: immutableSerializer.reviver,\n        options: typeof serialize.options === 'object' ?\n          { ...immutableSerializer.options, ...serialize.options } : immutableSerializer.options\n      };\n    }\n    if (!serialize.replacer && !serialize.reviver) return { options: serialize.options };\n    return { replacer: serialize.replacer, reviver: serialize.reviver, options: serialize.options || true };\n  }\n\n  const value = config[param];\n  if (typeof value === 'undefined') return undefined;\n  console.warn(`\\`${param}\\` parameter for Redux DevTools Extension is deprecated. Use \\`serialize\\` parameter instead: https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1`); // eslint-disable-line\n\n  if (typeof serializeState === 'boolean') return { options: value };\n  if (typeof serializeState === 'function') return { replacer: value };\n  return value;\n}\n\nfunction post(message) {\n  window.postMessage(message, '*');\n}\n\nfunction getStackTrace(config, toExcludeFromTrace) {\n  if (!config.trace) return undefined;\n  if (typeof config.trace === 'function') return config.trace();\n\n  let stack;\n  let extraFrames = 0;\n  let prevStackTraceLimit;\n  const traceLimit = config.traceLimit;\n  const error = Error();\n  if (Error.captureStackTrace) {\n    if (Error.stackTraceLimit < traceLimit) {\n      prevStackTraceLimit = Error.stackTraceLimit;\n      Error.stackTraceLimit = traceLimit;\n    }\n    Error.captureStackTrace(error, toExcludeFromTrace);\n  } else {\n    extraFrames = 3;\n  }\n  stack = error.stack;\n  if (prevStackTraceLimit) Error.stackTraceLimit = prevStackTraceLimit;\n  if (extraFrames || typeof Error.stackTraceLimit !== 'number' || Error.stackTraceLimit > traceLimit) {\n    const frames = stack.split('\\n');\n    if (frames.length > traceLimit) {\n      stack = frames.slice(0, traceLimit + extraFrames + (frames[0] === 'Error' ? 1 : 0)).join('\\n');\n    }\n  }\n  return stack;\n}\n\nfunction amendActionType(action, config, toExcludeFromTrace) {\n  let timestamp = Date.now();\n  let stack = getStackTrace(config, toExcludeFromTrace);\n  if (typeof action === 'string') return { action: { type: action }, timestamp, stack };\n  if (!action.type) return { action: { type: 'update' }, timestamp, stack };\n  if (action.action) return stack ? { stack, ...action } : action;\n  return { action, timestamp, stack };\n}\n\nexport function toContentScript(message, serializeState, serializeAction) {\n  if (message.type === 'ACTION') {\n    message.action = stringify(message.action, serializeAction);\n    message.payload = stringify(message.payload, serializeState);\n  } else if (message.type === 'STATE' || message.type === 'PARTIAL_STATE') {\n    const { actionsById, computedStates, committedState, ...rest } = message.payload;\n    message.payload = rest;\n    message.actionsById = stringify(actionsById, serializeAction);\n    message.computedStates = stringify(computedStates, serializeState);\n    message.committedState = typeof committedState !== 'undefined';\n  } else if (message.type === 'EXPORT') {\n    message.payload = stringify(message.payload, serializeAction);\n    if (typeof message.committedState !== 'undefined') {\n      message.committedState = stringify(message.committedState, serializeState);\n    }\n  }\n  post(message);\n}\n\nexport function sendMessage(action, state, config, instanceId, name) {\n  let amendedAction = action;\n  if (typeof config !== 'object') {\n    // Legacy: sending actions not from connected part\n    config = {}; // eslint-disable-line no-param-reassign\n    if (action) amendedAction = amendActionType(action, config, sendMessage);\n  }\n  const message = {\n    type: action ? 'ACTION' : 'STATE',\n    action: amendedAction,\n    payload: state,\n    maxAge: config.maxAge,\n    source,\n    name: config.name || name,\n    instanceId: config.instanceId || instanceId || 1\n  };\n  toContentScript(message, config.serialize, config.serialize);\n}\n\nfunction handleMessages(event) {\n  if (process.env.BABEL_ENV !== 'test' && (!event || event.source !== window)) return;\n  const message = event.data;\n  if (!message || message.source !== '@devtools-extension') return;\n  Object.keys(listeners).forEach(id => {\n    if (message.id && id !== message.id) return;\n    if (typeof listeners[id] === 'function') listeners[id](message);\n    else listeners[id].forEach(fn => { fn(message); });\n  });\n}\n\nexport function setListener(onMessage, instanceId) {\n  listeners[instanceId] = onMessage;\n  window.addEventListener('message', handleMessages, false);\n}\n\nconst liftListener = (listener, config) => message => {\n  let data = {};\n  if (message.type === 'IMPORT') {\n    data.type = 'DISPATCH';\n    data.payload = {\n      type: 'IMPORT_STATE',\n      ...importState(message.state, config)\n    };\n  } else {\n    data = message;\n  }\n  listener(data);\n};\n\nexport function disconnect() {\n  window.removeEventListener('message', handleMessages);\n  post({ type: 'DISCONNECT', source });\n}\n\nexport function connect(preConfig) {\n  const config = preConfig || {};\n  const id = generateId(config.instanceId);\n  if (!config.instanceId) config.instanceId = id;\n  if (!config.name) config.name = document.title && id === 1 ? document.title : `Instance ${id}`;\n  if (config.serialize) config.serialize = getSeralizeParameter(config);\n  const actionCreators = config.actionCreators || {};\n  const latency = config.latency;\n  const predicate = config.predicate;\n  const localFilter = getLocalFilter(config);\n  const autoPause = config.autoPause;\n  let isPaused = autoPause;\n  let delayedActions = [];\n  let delayedStates = [];\n\n  const rootListiner = action => {\n    if (autoPause) {\n      if (action.type === 'START') isPaused = false;\n      else if (action.type === 'STOP') isPaused = true;\n    }\n    if (action.type === 'DISPATCH') {\n      const payload = action.payload;\n      if (payload.type === 'PAUSE_RECORDING') {\n        isPaused = payload.status;\n        toContentScript({\n          type: 'LIFTED',\n          liftedState: { isPaused },\n          instanceId: id,\n          source\n        });\n      }\n    }\n  };\n\n  listeners[id] = [rootListiner];\n\n  const subscribe = (listener) => {\n    if (!listener) return undefined;\n    const liftedListener = liftListener(listener, config);\n    listeners[id].push(liftedListener);\n\n    return function unsubscribe() {\n      const index = listeners[id].indexOf(liftedListener);\n      listeners[id].splice(index, 1);\n    };\n  };\n\n  const unsubscribe = () => {\n    delete listeners[id];\n  };\n\n  const sendDelayed = throttle(() => {\n    sendMessage(delayedActions, delayedStates, config);\n    delayedActions = [];\n    delayedStates = [];\n  }, latency);\n\n  const send = (action, state) => {\n    if (isPaused || isFiltered(action, localFilter) || predicate && !predicate(state, action)) {\n      return;\n    }\n\n    let amendedAction = action;\n    const amendedState = config.stateSanitizer ? config.stateSanitizer(state) : state;\n    if (action) {\n      if (config.getActionType) {\n        amendedAction = config.getActionType(action);\n        if (typeof amendedAction !== 'object') {\n          amendedAction = { action: { type: amendedAction }, timestamp: Date.now() };\n        }\n      }\n      else if (config.actionSanitizer) amendedAction = config.actionSanitizer(action);\n      amendedAction = amendActionType(amendedAction, config, send);\n      if (latency) {\n        delayedActions.push(amendedAction);\n        delayedStates.push(amendedState);\n        sendDelayed();\n        return;\n      }\n    }\n    sendMessage(amendedAction, amendedState, config);\n  };\n\n  const init = (state, liftedData) => {\n    const message = {\n      type: 'INIT',\n      payload: stringify(state, config.serialize),\n      instanceId: id,\n      source\n    };\n    if (liftedData && Array.isArray(liftedData)) { // Legacy\n      message.action = stringify(liftedData);\n      message.name = config.name;\n    } else {\n      if (liftedData) {\n        message.liftedState = liftedData;\n        if (liftedData.isPaused) isPaused = true;\n      }\n      message.libConfig = {\n        actionCreators: JSON.stringify(getActionsArray(actionCreators)),\n        name: config.name || document.title,\n        features: config.features,\n        serialize: !!config.serialize,\n        type: config.type\n      };\n    }\n    post(message);\n  };\n\n  const error = (payload) => {\n    post({ type: 'ERROR', payload, id, source });\n  };\n\n  window.addEventListener('message', handleMessages, false);\n\n  post({ type: 'INIT_INSTANCE', instanceId: id, source });\n\n  return {\n    init,\n    subscribe,\n    unsubscribe,\n    send,\n    error\n  };\n}\n\nexport function updateStore(stores) {\n  return function(newStore, instanceId) {\n    /* eslint-disable no-console */\n    console.warn('`__REDUX_DEVTOOLS_EXTENSION__.updateStore` is deprecated, remove it and just use ' +\n      '`__REDUX_DEVTOOLS_EXTENSION_COMPOSE__` instead of the extension\\'s store enhancer: ' +\n      'https://github.com/zalmoxisus/redux-devtools-extension#12-advanced-store-setup');\n    /* eslint-enable no-console */\n    const store = stores[instanceId || Object.keys(stores)[0]];\n    // Mutate the store in order to keep the reference\n    store.liftedStore = newStore.liftedStore;\n    store.getState = newStore.getState;\n    store.dispatch = newStore.dispatch;\n  };\n}\n\nexport function isInIframe() {\n  try {\n    return window.self !== window.top;\n  } catch (e) {\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/app/api/notifyErrors.js",
    "content": "let handleError;\nlet lastTime = 0;\n\nfunction createExpBackoffTimer(step) {\n  let count = 1;\n  return function(reset) {\n    // Reset call\n    if (reset) {\n      count = 1;\n      return 0;\n    }\n    // Calculate next timeout\n    let timeout = Math.pow(2, count - 1);\n    if (count < 5) count += 1;\n    return timeout * step;\n  };\n}\n\nconst nextErrorTimeout = createExpBackoffTimer(5000);\n\nfunction postError(message) {\n  if (handleError && !handleError()) return;\n  window.postMessage({\n    source: '@devtools-page',\n    type: 'ERROR',\n    message: message\n  }, '*');\n}\n\nfunction catchErrors(e) {\n  if (\n    window.devToolsOptions && !window.devToolsOptions.shouldCatchErrors\n    || e.timeStamp - lastTime < nextErrorTimeout()\n  ) return;\n  lastTime = e.timeStamp; nextErrorTimeout(true);\n  postError(e.message);\n}\n\nexport default function notifyErrors(onError) {\n  handleError = onError;\n  window.addEventListener('error', catchErrors, false);\n}\n"
  },
  {
    "path": "src/app/api/openWindow.js",
    "content": "export default function openWindow(position) {\n  window.postMessage({\n    source: '@devtools-page',\n    type: 'OPEN',\n    position: position || 'right'\n  }, '*');\n}\n"
  },
  {
    "path": "src/app/containers/App.js",
    "content": "import React, { Component, PropTypes } from 'react';\nimport { bindActionCreators } from 'redux';\nimport { connect } from 'react-redux';\nimport SliderMonitor from 'remotedev-slider/lib/Slider';\nimport { liftedDispatch, getReport } from 'remotedev-app/lib/actions';\nimport { getActiveInstance } from 'remotedev-app/lib/reducers/instances';\nimport styles from 'remotedev-app/lib/styles';\nimport enhance from 'remotedev-app/lib/hoc';\nimport DevTools from 'remotedev-app/lib/containers/DevTools';\nimport Dispatcher from 'remotedev-app/lib/containers/monitors/Dispatcher';\nimport MonitorSelector from 'remotedev-app/lib/components/MonitorSelector';\nimport Notification from 'remotedev-app/lib/components/Notification';\nimport Instances from 'remotedev-app/lib/components/Instances';\nimport Button from 'remotedev-app/lib/components/Button';\nimport RecordButton from 'remotedev-app/lib/components/buttons/RecordButton';\nimport LockButton from 'remotedev-app/lib/components/buttons/LockButton';\nimport DispatcherButton from 'remotedev-app/lib/components/buttons/DispatcherButton';\nimport SliderButton from 'remotedev-app/lib/components/buttons/SliderButton';\nimport ImportButton from 'remotedev-app/lib/components/buttons/ImportButton';\nimport ExportButton from 'remotedev-app/lib/components/buttons/ExportButton';\nimport PrintButton from 'remotedev-app/lib/components/buttons/PrintButton';\nimport SettingsIcon from 'react-icons/lib/md/settings';\nimport LeftIcon from 'react-icons/lib/md/border-left';\nimport RightIcon from 'react-icons/lib/md/border-right';\nimport BottomIcon from 'react-icons/lib/md/border-bottom';\nimport RemoteIcon from 'react-icons/lib/go/radio-tower';\nimport PersistIcon from 'react-icons/lib/go/pin';\n\n@enhance\nclass App extends Component {\n  openWindow = (position) => {\n    chrome.runtime.sendMessage({ type: 'OPEN', position });\n  };\n  openOptionsPage = () => {\n    if (navigator.userAgent.indexOf('Firefox') !== -1) {\n      chrome.runtime.sendMessage({ type: 'OPEN_OPTIONS' });\n    } else {\n      chrome.runtime.openOptionsPage();\n    }\n  };\n\n  render() {\n    const {\n      monitor, position, togglePersist,\n      dispatcherIsOpen, sliderIsOpen, options, liftedState\n    } = this.props;\n    if (!position && (!options || !options.features)) {\n      return (\n        <div style={{ padding: '20px', width: '100%', textAlign: 'center' }}>\n          No store found. Make sure to follow <a href=\"https://github.com/zalmoxisus/redux-devtools-extension#usage\" target=\"_blank\">the instructions</a>.\n        </div>\n      );\n    }\n    const features = options.features || {};\n    return (\n      <div style={styles.container}>\n        <div style={styles.buttonBar}>\n          <MonitorSelector selected={monitor}/>\n          <Instances selected={this.props.selected} />\n        </div>\n        <DevTools\n          monitor={monitor}\n          liftedState={liftedState}\n          monitorState={this.props.monitorState}\n          dispatch={this.props.liftedDispatch}\n          lib={options.lib || options.explicitLib}\n        />\n        <Notification />\n        {sliderIsOpen && options.connectionId && options.features.jump &&\n          <SliderMonitor\n            monitor=\"SliderMonitor\"\n            liftedState={liftedState}\n            dispatch={this.props.liftedDispatch}\n            getReport={this.props.getReport}\n            reports={this.props.reports}\n            showActions={monitor === 'ChartMonitor'}\n            style={{ padding: '15px 5px' }}\n            fillColor=\"rgb(120, 144, 156)\"\n          />\n        }\n        {dispatcherIsOpen && options.connectionId && options.features.dispatch &&\n          <Dispatcher options={options} />\n        }\n        <div style={styles.buttonBar}>\n          {!window.isElectron && position !== '#left' &&\n          <Button\n            Icon={LeftIcon}\n            onClick={() => { this.openWindow('left'); }}\n          />\n          }\n          {!window.isElectron && position !== '#right' &&\n          <Button\n            Icon={RightIcon}\n            onClick={() => { this.openWindow('right'); }}\n          />\n          }\n          {!window.isElectron && position !== '#bottom' &&\n          <Button\n            Icon={BottomIcon}\n            onClick={() => { this.openWindow('bottom'); }}\n          />\n          }\n          {features.pause &&\n            <RecordButton paused={liftedState.isPaused} />\n          }\n          {features.lock &&\n            <LockButton locked={liftedState.isLocked} />\n          }\n          {features.persist &&\n            <Button\n              Icon={PersistIcon}\n              onClick={togglePersist}\n            >Persist</Button>\n          }\n          {features.dispatch &&\n            <DispatcherButton dispatcherIsOpen={dispatcherIsOpen}/>\n          }\n          {features.jump &&\n            <SliderButton isOpen={sliderIsOpen}/>\n          }\n          {features.import &&\n            <ImportButton />\n          }\n          {features.export &&\n            <ExportButton />\n          }\n          {position && (position !== '#popup' || navigator.userAgent.indexOf('Firefox') !== -1) &&\n            <PrintButton />\n          }\n          {!window.isElectron &&\n          <Button\n            Icon={RemoteIcon}\n            onClick={() => { this.openWindow('remote'); }}\n          >Remote</Button>\n          }\n          {(chrome.runtime.openOptionsPage || navigator.userAgent.indexOf('Firefox') !== -1) &&\n          <Button\n            Icon={SettingsIcon}\n            onClick={this.openOptionsPage}\n          >Settings</Button>\n          }\n        </div>\n      </div>\n    );\n  }\n}\n\nApp.propTypes = {\n  bgStore: PropTypes.object,\n  liftedDispatch: PropTypes.func.isRequired,\n  getReport: PropTypes.func.isRequired,\n  togglePersist: PropTypes.func.isRequired,\n  selected: PropTypes.string,\n  liftedState: PropTypes.object.isRequired,\n  monitorState: PropTypes.object,\n  options: PropTypes.object.isRequired,\n  monitor: PropTypes.string,\n  position: PropTypes.string,\n  reports: PropTypes.array.isRequired,\n  dispatcherIsOpen: PropTypes.bool,\n  sliderIsOpen: PropTypes.bool\n};\n\nfunction mapStateToProps(state) {\n  const instances = state.instances;\n  const id = getActiveInstance(instances);\n  return {\n    selected: instances.selected,\n    liftedState: instances.states[id],\n    monitorState: state.monitor.monitorState,\n    options: instances.options[id],\n    monitor: state.monitor.selected,\n    dispatcherIsOpen: state.monitor.dispatcherIsOpen,\n    sliderIsOpen: state.monitor.sliderIsOpen,\n    reports: state.reports.data,\n    shouldSync: state.instances.sync\n  };\n}\n\nfunction mapDispatchToProps(dispatch) {\n  return {\n    liftedDispatch: bindActionCreators(liftedDispatch, dispatch),\n    getReport: bindActionCreators(getReport, dispatch),\n    togglePersist: () => { dispatch({ type: 'TOGGLE_PERSIST' }); }\n  };\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)(App);\n"
  },
  {
    "path": "src/app/middlewares/api.js",
    "content": "import stringifyJSON from 'remotedev-app/lib/utils/stringifyJSON';\nimport { UPDATE_STATE, REMOVE_INSTANCE, LIFTED_ACTION } from 'remotedev-app/lib/constants/actionTypes';\nimport { nonReduxDispatch } from 'remotedev-app/lib/utils/monitorActions';\nimport syncOptions from '../../browser/extension/options/syncOptions';\nimport openDevToolsWindow from '../../browser/extension/background/openWindow';\nimport { getReport } from '../../browser/extension/background/logging';\n\nconst CONNECTED = 'socket/CONNECTED';\nconst DISCONNECTED = 'socket/DISCONNECTED';\nconst connections = {\n  tab: {},\n  panel: {},\n  monitor: {}\n};\nconst chunks = {};\nlet monitors = 0;\nlet isMonitored = false;\n\nconst getId = (sender, name) => sender.tab ? sender.tab.id : name || sender.id;\n\nfunction toMonitors(action, tabId, verbose) {\n  Object.keys(connections.monitor).forEach(id => {\n    connections.monitor[id].postMessage(\n      verbose || action.type === 'ERROR' ? action : { type: UPDATE_STATE }\n    );\n  });\n  Object.keys(connections.panel).forEach(id => {\n    connections.panel[id].postMessage(action);\n  });\n}\n\nfunction toContentScript({ message, action, id, instanceId, state }) {\n  connections.tab[id].postMessage({\n    type: message,\n    action,\n    state: nonReduxDispatch(window.store, message, instanceId, action, state),\n    id: instanceId.toString().replace(/^[^\\/]+\\//, '')\n  });\n}\n\nfunction toAllTabs(msg) {\n  const tabs = connections.tab;\n  Object.keys(tabs).forEach(id => {\n    tabs[id].postMessage(msg);\n  });\n}\n\nfunction monitorInstances(shouldMonitor, id) {\n  if (!id && isMonitored === shouldMonitor) return;\n  const action = { type: shouldMonitor ? 'START' : 'STOP' };\n  if (id) {\n    if (connections.tab[id]) connections.tab[id].postMessage(action);\n  } else {\n    toAllTabs(action);\n  }\n  isMonitored = shouldMonitor;\n}\n\nfunction getReducerError() {\n  const instancesState = window.store.getState().instances;\n  const payload = instancesState.states[instancesState.current];\n  const computedState = payload.computedStates[payload.currentStateIndex];\n  if (!computedState) return false;\n  return computedState.error;\n}\n\nfunction togglePersist() {\n  const state = window.store.getState();\n  if (state.persistStates) {\n    Object.keys(state.instances.connections).forEach(id => {\n      if (connections.tab[id]) return;\n      window.store.dispatch({ type: REMOVE_INSTANCE, id });\n      toMonitors({ type: 'NA', id });\n    });\n  }\n}\n\n// Receive messages from content scripts\nfunction messaging(request, sender, sendResponse) {\n  let tabId = getId(sender);\n  if (!tabId) return;\n  if (sender.frameId) tabId = `${tabId}-${sender.frameId}`;\n\n  if (request.type === 'STOP') {\n    if (!Object.keys(window.store.getState().instances.connections).length) {\n      window.store.dispatch({ type: DISCONNECTED });\n    }\n    return;\n  }\n  if (request.type === 'OPEN_OPTIONS') {\n    chrome.runtime.openOptionsPage();\n    return;\n  }\n  if (request.type === 'GET_OPTIONS') {\n    window.syncOptions.get(options => {\n      sendResponse({ options });\n    });\n    return;\n  }\n  if (request.type === 'GET_REPORT') {\n    getReport(request.payload, tabId, request.instanceId);\n    return;\n  }\n  if (request.type === 'OPEN') {\n    let position = 'devtools-left';\n    if (['remote', 'panel', 'left', 'right', 'bottom'].indexOf(request.position) !== -1) {\n      position = 'devtools-' + request.position;\n    }\n    openDevToolsWindow(position);\n    return;\n  }\n  if (request.type === 'ERROR') {\n    if (request.payload) {\n      toMonitors(request, tabId);\n      return;\n    }\n    if (!request.message) return;\n    const reducerError = getReducerError();\n    chrome.notifications.create('app-error', {\n      type: 'basic',\n      title: reducerError ? 'An error occurred in the reducer' : 'An error occurred in the app',\n      message: reducerError || request.message,\n      iconUrl: 'img/logo/48x48.png',\n      isClickable: !!reducerError\n    });\n    return;\n  }\n\n  const action = { type: UPDATE_STATE, request, id: tabId };\n  const instanceId = `${tabId}/${request.instanceId}`;\n  if (request.split) {\n    if (request.split === 'start') {\n      chunks[instanceId] = request;\n      return;\n    }\n    if (request.split === 'chunk') {\n      chunks[instanceId][request.chunk[0]] = (chunks[instanceId][request.chunk[0]] || '') + request.chunk[1];\n      return;\n    }\n    action.request = chunks[instanceId];\n    delete chunks[instanceId];\n  }\n  if (request.instanceId) {\n    action.request.instanceId = instanceId;\n  }\n  window.store.dispatch(action);\n\n  if (request.type === 'EXPORT') {\n    toMonitors(action, tabId, true);\n  } else {\n    toMonitors(action, tabId);\n  }\n}\n\nfunction disconnect(type, id, listener) {\n  return function disconnectListener() {\n    const p = connections[type][id];\n    if (listener && p) p.onMessage.removeListener(listener);\n    if (p) p.onDisconnect.removeListener(disconnectListener);\n    delete connections[type][id];\n    if (type === 'tab') {\n      if (!window.store.getState().persistStates) {\n        window.store.dispatch({ type: REMOVE_INSTANCE, id });\n        toMonitors({ type: 'NA', id });\n      }\n    } else {\n      monitors--;\n      if (!monitors) monitorInstances(false);\n    }\n  };\n}\n\nfunction onConnect(port) {\n  let id;\n  let listener;\n\n  window.store.dispatch({ type: CONNECTED, port });\n\n  if (port.name === 'tab') {\n    id = getId(port.sender);\n    if (port.sender.frameId) id = `${id}-${port.sender.frameId}`;\n    connections.tab[id] = port;\n    listener = msg => {\n      if (msg.name === 'INIT_INSTANCE') {\n        if (typeof id === 'number') {\n          chrome.pageAction.show(id);\n          chrome.pageAction.setIcon({tabId: id, path: 'img/logo/38x38.png'});\n        }\n        if (isMonitored) port.postMessage({ type: 'START' });\n\n        const state = window.store.getState();\n        if (state.persistStates) {\n          const instanceId = `${id}/${msg.instanceId}`;\n          const persistedState = state.instances.states[instanceId];\n          if (!persistedState) return;\n          toContentScript({\n            message: 'IMPORT',\n            id, instanceId,\n            state: stringifyJSON(persistedState, state.instances.options[instanceId].serialize)\n          });\n        }\n        return;\n      }\n      if (msg.name === 'RELAY') {\n        messaging(msg.message, port.sender, id);\n      }\n    };\n    port.onMessage.addListener(listener);\n    port.onDisconnect.addListener(disconnect('tab', id, listener));\n  } else if (port.name && port.name.indexOf('monitor') === 0) {\n    id = getId(port.sender, port.name);\n    connections.monitor[id] = port;\n    monitorInstances(true);\n    monitors++;\n    port.onDisconnect.addListener(disconnect('monitor', id));\n  } else { // devpanel\n    id = port.name || port.sender.frameId;\n    connections.panel[id] = port;\n    monitorInstances(true, port.name);\n    monitors++;\n    listener = msg => {\n      window.store.dispatch(msg);\n    };\n    port.onMessage.addListener(listener);\n    port.onDisconnect.addListener(disconnect('panel', id, listener));\n  }\n}\n\nchrome.runtime.onConnect.addListener(onConnect);\nchrome.runtime.onConnectExternal.addListener(onConnect);\nchrome.runtime.onMessage.addListener(messaging);\nchrome.runtime.onMessageExternal.addListener(messaging);\n\nchrome.notifications.onClicked.addListener(id => {\n  chrome.notifications.clear(id);\n  openDevToolsWindow('devtools-right');\n});\n\nwindow.syncOptions = syncOptions(toAllTabs); // Expose to the options page\n\nexport default function api() {\n  return next => action => {\n    if (action.type === LIFTED_ACTION) toContentScript(action);\n    else if (action.type === 'TOGGLE_PERSIST') togglePersist();\n    return next(action);\n  };\n}\n"
  },
  {
    "path": "src/app/middlewares/instanceSelector.js",
    "content": "import { SELECT_INSTANCE, UPDATE_STATE } from 'remotedev-app/lib/constants/actionTypes';\n\nfunction selectInstance(tabId, store, next) {\n  const instances = store.getState().instances;\n  if (instances.current === 'default') return;\n  const connections = instances.connections[tabId];\n  if (connections && connections.length === 1) {\n    next({ type: SELECT_INSTANCE, selected: connections[0] });\n  }\n}\n\nfunction getCurrentTabId(next) {\n  chrome.tabs.query({\n    active: true,\n    lastFocusedWindow: true\n  }, tabs => {\n    const tab = tabs[0];\n    if (!tab) return;\n    next(tab.id);\n  });\n}\n\nexport default function popupSelector(store) {\n  return next => action => {\n    const result = next(action);\n    if (action.type === UPDATE_STATE) {\n      if (chrome.devtools && chrome.devtools.inspectedWindow) {\n        selectInstance(chrome.devtools.inspectedWindow.tabId, store, next);\n      } else {\n        getCurrentTabId(tabId => selectInstance(tabId, store, next));\n      }\n    }\n    return result;\n  };\n}\n"
  },
  {
    "path": "src/app/middlewares/panelSync.js",
    "content": "import { LIFTED_ACTION, UPDATE_STATE, SELECT_INSTANCE } from 'remotedev-app/lib/constants/actionTypes';\nimport { getActiveInstance } from 'remotedev-app/lib/reducers/instances';\n\nfunction panelDispatcher(bgConnection) {\n  let autoselected = false;\n  const tabId = chrome.devtools.inspectedWindow.tabId;\n\n  return store => next => action => {\n    const result = next(action);\n    if (!autoselected && action.type === UPDATE_STATE && tabId) {\n      autoselected = true;\n      const connections = store.getState()\n        .instances.connections[tabId];\n      if (connections && connections.length === 1) {\n        next({ type: SELECT_INSTANCE, selected: connections[0] });\n      }\n    }\n    if (action.type === LIFTED_ACTION || action.type === 'TOGGLE_PERSIST') {\n      const instances = store.getState().instances;\n      const instanceId = getActiveInstance(instances);\n      const id = instances.options[instanceId].connectionId;\n      bgConnection.postMessage({ ...action, instanceId, id });\n    }\n    return result;\n  };\n}\n\nexport default panelDispatcher;\n"
  },
  {
    "path": "src/app/middlewares/windowSync.js",
    "content": "import { UPDATE_STATE, LIFTED_ACTION } from 'remotedev-app/lib/constants/actionTypes';\nimport { getActiveInstance } from 'remotedev-app/lib/reducers/instances';\n\nconst syncStores = baseStore => store => next => action => {\n  if (action.type === UPDATE_STATE) {\n    return next({\n      ...action,\n      instances: baseStore.getState().instances\n    });\n  }\n  if (action.type === LIFTED_ACTION || action.type === 'TOGGLE_PERSIST') {\n    const instances = store.getState().instances;\n    const instanceId = getActiveInstance(instances);\n    const id = instances.options[instanceId].connectionId;\n    baseStore.dispatch({ ...action, instanceId, id });\n  }\n  return next(action);\n};\n\nexport default syncStores;\n"
  },
  {
    "path": "src/app/reducers/background/index.js",
    "content": "import { combineReducers } from 'redux';\nimport instances from 'remotedev-app/lib/reducers/instances';\nimport persistStates from './persistStates';\n\nconst rootReducer = combineReducers({\n  instances,\n  persistStates\n});\n\nexport default rootReducer;\n"
  },
  {
    "path": "src/app/reducers/background/persistStates.js",
    "content": "export default function persistStates(state = false, action) {\n  if (action.type === 'TOGGLE_PERSIST') return !state;\n  return state;\n}\n"
  },
  {
    "path": "src/app/reducers/panel/index.js",
    "content": "import { combineReducers } from 'redux';\nimport instances from 'remotedev-app/lib/reducers/instances';\nimport monitor from 'remotedev-app/lib/reducers/monitor';\nimport notification from 'remotedev-app/lib/reducers/notification';\nimport test from 'remotedev-app/lib/reducers/test';\nimport reports from 'remotedev-app/lib/reducers/reports';\n\nconst rootReducer = combineReducers({\n  instances,\n  monitor,\n  test,\n  reports,\n  notification\n});\n\nexport default rootReducer;\n"
  },
  {
    "path": "src/app/reducers/window/index.js",
    "content": "import { combineReducers } from 'redux';\nimport instances from './instances';\nimport monitor from 'remotedev-app/lib/reducers/monitor';\nimport notification from 'remotedev-app/lib/reducers/notification';\nimport socket from 'remotedev-app/lib/reducers/socket';\nimport reports from 'remotedev-app/lib/reducers/reports';\nimport test from 'remotedev-app/lib/reducers/test';\n\nconst rootReducer = combineReducers({\n  instances,\n  monitor,\n  test,\n  socket,\n  reports,\n  notification\n});\n\nexport default rootReducer;\n"
  },
  {
    "path": "src/app/reducers/window/instances.js",
    "content": "import { initialState, dispatchAction } from 'remotedev-app/lib/reducers/instances';\nimport { UPDATE_STATE, SELECT_INSTANCE, LIFTED_ACTION } from 'remotedev-app/lib/constants/actionTypes';\n\nexport default function instances(state = initialState, action) {\n  switch (action.type) {\n    case UPDATE_STATE:\n      return { ...action.instances, selected: state.selected };\n    case LIFTED_ACTION:\n      if (action.message === 'DISPATCH') return dispatchAction(state, action);\n      return state;\n    case SELECT_INSTANCE:\n      return { ...state, selected: action.selected };\n    default:\n      return state;\n  }\n}\n"
  },
  {
    "path": "src/app/service/Monitor.js",
    "content": "export default class Monitor {\n  constructor(update) {\n    this.update = update;\n  }\n  reducer = (state = {}, action) => {\n    if (!this.active) return state;\n    this.lastAction = action.type;\n    if (action.type === 'LOCK_CHANGES') {\n      window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__ = action.status;\n    } else if (action.type === 'PAUSE_RECORDING') {\n      this.paused = action.status;\n    } else if (this.isHotReloaded()) {\n      // Send new lifted state on hot-reloading\n      setTimeout(this.update, 0);\n    }\n    return state;\n  };\n  start = (skipUpdate) => {\n    this.active = true;\n    if (!skipUpdate) this.update();\n  };\n  stop = () => {\n    this.active = false;\n    clearTimeout(this.waitingTimeout);\n  };\n  isHotReloaded = () => this.lastAction && /^@@redux\\/(INIT|REPLACE)/.test(this.lastAction);\n  isMonitorAction = () => this.lastAction && this.lastAction !== 'PERFORM_ACTION';\n  isTimeTraveling = () => this.lastAction === 'JUMP_TO_STATE';\n  isPaused = () => {\n    if (this.paused) {\n      if (this.lastAction !== 'BLOCKED') {\n        if (!window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__) this.lastAction = 'BLOCKED';\n        return false;\n      }\n      return true;\n    }\n    return false;\n  };\n  isLocked = () => {\n    if (window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__) {\n      if (this.lastAction !== 'BLOCKED') {\n        this.lastAction = 'BLOCKED';\n        return false;\n      }\n      return true;\n    }\n    return false;\n  };\n}\n"
  },
  {
    "path": "src/app/stores/backgroundStore.js",
    "content": "import { createStore, applyMiddleware } from 'redux';\nimport rootReducer from '../reducers/background';\nimport api from '../middlewares/api';\n\nexport default function configureStore(preloadedState) {\n  return createStore(rootReducer, preloadedState, applyMiddleware(api));\n/*\n  let enhancer;\n  if (process.env.NODE_ENV === 'production') {\n    enhancer = applyMiddleware(api);\n  } else {\n    const logger = require('redux-logger');\n    enhancer = applyMiddleware(api, logger());\n  }\n\n  return createStore(rootReducer, preloadedState, enhancer);\n*/\n}\n"
  },
  {
    "path": "src/app/stores/createStore.js",
    "content": "import { createStore } from 'redux';\n\nexport default function configureStore(reducer, initialState, enhance) {\n  return createStore(reducer, initialState, enhance());\n}\n"
  },
  {
    "path": "src/app/stores/enhancerStore.js",
    "content": "import { compose } from 'redux';\nimport instrument from 'redux-devtools-instrument';\nimport persistState from 'redux-devtools/lib/persistState';\n\nexport function getUrlParam(key) {\n  const matches = window.location.href.match(new RegExp(`[?&]${key}=([^&#]+)\\\\b`));\n  return (matches && matches.length > 0) ? matches[1] : null;\n}\n\nexport default function configureStore(next, monitorReducer, config) {\n  return compose(\n    instrument(\n      monitorReducer,\n      {\n        maxAge: config.maxAge,\n        trace: config.trace,\n        traceLimit: config.traceLimit,\n        shouldCatchErrors: config.shouldCatchErrors || window.shouldCatchErrors,\n        shouldHotReload: config.shouldHotReload,\n        shouldRecordChanges: config.shouldRecordChanges,\n        shouldStartLocked: config.shouldStartLocked,\n        pauseActionType: config.pauseActionType || '@@PAUSED'\n      }\n    ),\n    persistState(\n      getUrlParam('debug_session'),\n      config.deserializeState,\n      config.deserializeAction\n    )\n  )(next);\n}\n"
  },
  {
    "path": "src/app/stores/panelStore.js",
    "content": "import { createStore, applyMiddleware } from 'redux';\nimport persist from 'remotedev-app/lib/middlewares/persist';\nimport exportState from 'remotedev-app/lib/middlewares/exportState';\nimport panelDispatcher from '../middlewares/panelSync';\nimport rootReducer from '../reducers/panel';\n\nexport default function configureStore(position, bgConnection, preloadedState) {\n  const enhancer = applyMiddleware(exportState, panelDispatcher(bgConnection), persist(position));\n  return createStore(rootReducer, preloadedState, enhancer);\n}\n"
  },
  {
    "path": "src/app/stores/windowStore.js",
    "content": "import { createStore, compose, applyMiddleware } from 'redux';\nimport persist from 'remotedev-app/lib/middlewares/persist';\nimport exportState from 'remotedev-app/lib/middlewares/exportState';\nimport api from 'remotedev-app/lib/middlewares/api';\nimport { CONNECT_REQUEST } from 'remotedev-app/lib/constants/socketActionTypes';\nimport syncStores from '../middlewares/windowSync';\nimport instanceSelector from '../middlewares/instanceSelector';\nimport rootReducer from '../reducers/window';\n\nexport default function configureStore(baseStore, position, preloadedState) {\n  let enhancer;\n  const middlewares = [exportState, api, syncStores(baseStore), persist(position)];\n  if (!position || position === '#popup') { // select current tab instance for devPanel and pageAction\n    middlewares.push(instanceSelector);\n  }\n  if (process.env.NODE_ENV === 'production') {\n    enhancer = applyMiddleware(...middlewares);\n  } else {\n    enhancer = compose(\n      applyMiddleware(...middlewares),\n      window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : noop => noop\n    );\n  }\n  const store = createStore(rootReducer, preloadedState, enhancer);\n\n  chrome.storage.local.get(['s:hostname', 's:port', 's:secure'], options => {\n    if (!options['s:hostname'] || !options['s:port']) return;\n    store.dispatch({\n      type: CONNECT_REQUEST,\n      options: { hostname: options['s:hostname'], port: options['s:port'], secure: options['s:secure'] }\n    });\n  });\n\n  return store;\n}\n"
  },
  {
    "path": "src/browser/extension/background/contextMenus.js",
    "content": "import openDevToolsWindow from './openWindow';\n\nexport function createMenu() {\n  const menus = [\n    { id: 'devtools-left', title: 'To left' },\n    { id: 'devtools-right', title: 'To right' },\n    { id: 'devtools-bottom', title: 'To bottom' },\n    { id: 'devtools-panel', title: 'Open in a panel (enable in browser settings)' },\n    { id: 'devtools-remote', title: 'Open Remote DevTools' }\n  ];\n\n  let shortcuts = {};\n  chrome.commands.getAll(commands => {\n    commands.forEach(({ name, shortcut }) => {\n      shortcuts[name] = shortcut;\n    });\n\n    menus.forEach(({ id, title }) => {\n      chrome.contextMenus.create({\n        id: id,\n        title: title + (shortcuts[id] ? ' (' + shortcuts[id] + ')' : ''),\n        contexts: ['all']\n      });\n    });\n  });\n}\n\nexport function removeMenu() {\n  chrome.contextMenus.removeAll();\n}\n\nchrome.contextMenus.onClicked.addListener(({ menuItemId }) => {\n  openDevToolsWindow(menuItemId);\n});\n"
  },
  {
    "path": "src/browser/extension/background/getPreloadedState.js",
    "content": "const getIfExists = (sel, template) => (\n  typeof sel === 'undefined' ||\n  typeof template === 'undefined' ||\n  typeof template[sel] === 'undefined' ?\n    0 : sel\n);\n\nexport default function getPreloadedState(position, cb) {\n  chrome.storage.local.get([\n    'monitor' + position, 'slider' + position, 'dispatcher' + position,\n    'test-templates', 'test-templates-sel'\n  ], options => {\n    cb({\n      monitor: {\n        selected: options['monitor' + position],\n        sliderIsOpen: options['slider' + position] || false,\n        dispatcherIsOpen: options['dispatcher' + position] || false,\n      },\n      test: {\n        selected: getIfExists(options['test-templates-sel'], options['test-templates']),\n        templates: options['test-templates']\n      }\n    });\n  });\n}\n"
  },
  {
    "path": "src/browser/extension/background/index.js",
    "content": "import configureStore from '../../../app/stores/backgroundStore';\nimport openDevToolsWindow from './openWindow';\nimport { createMenu, removeMenu } from './contextMenus';\nimport syncOptions from '../options/syncOptions';\n\n// Expose the extension's store globally to access it from the windows\n// via chrome.runtime.getBackgroundPage\nwindow.store = configureStore();\n\n// Listen for keyboard shortcuts\nchrome.commands.onCommand.addListener(shortcut => {\n  openDevToolsWindow(shortcut);\n});\n\n// Create the context menu when installed\nchrome.runtime.onInstalled.addListener(() => {\n  syncOptions().get(option => {\n    if (option.showContextMenus) createMenu();\n  });\n});\n\n// Create or Remove context menu when config changed\nchrome.storage.onChanged.addListener(changes => {\n  if (changes.showContextMenus) {\n    if (changes.showContextMenus.newValue) createMenu();\n    else removeMenu();\n  }\n});\n"
  },
  {
    "path": "src/browser/extension/background/logging.js",
    "content": "import { LIFTED_ACTION } from 'remotedev-app/lib/constants/actionTypes';\n\nexport function getReport(reportId, tabId, instanceId) {\n  chrome.storage.local.get(['s:hostname', 's:port', 's:secure'], options => {\n    if (!options['s:hostname'] || !options['s:port']) return;\n    const url = `${options['s:secure'] ? 'https' : 'http'}://${options['s:hostname']}:${options['s:port']}`;\n\n    fetch(url, {\n      method: 'POST',\n      headers: {\n        'content-type': 'application/json'\n      },\n      body: JSON.stringify({ op: 'get', id: reportId })\n    }).then(response => {\n      return response.json();\n    }).then(json => {\n      const { payload, preloadedState } = json;\n      if (!payload) return;\n      window.store.dispatch({\n        type: LIFTED_ACTION,\n        message: 'IMPORT',\n        state: JSON.stringify({ payload, preloadedState }),\n        id: tabId,\n        instanceId: `${tabId}/${instanceId}`\n      });\n    }).catch(function(err) {\n      /* eslint-disable no-console */\n      console.warn(err);\n      /* eslint-enable no-console */\n    });\n  });\n}\n"
  },
  {
    "path": "src/browser/extension/background/openWindow.js",
    "content": "let windows = {};\nlet lastPosition = null;\n\nexport default function openDevToolsWindow(position) {\n  function popWindow(action, url, customOptions) {\n    function focusIfExist(callback) {\n      if (!windows[position]) {\n        callback();\n        lastPosition = position;\n      } else {\n        let params = {focused: true};\n        if (lastPosition !== position && position !== 'devtools-panel') params = {...params, ...customOptions};\n        chrome.windows.update(windows[position], params, () => {\n          lastPosition = null;\n          if (chrome.runtime.lastError) callback();\n        });\n      }\n    }\n\n    focusIfExist(() => {\n      let options = {\n        type: 'popup',\n        ...customOptions\n      };\n      if (action === 'open') {\n        options.url = chrome.extension.getURL(url + '#' + position.substr(position.indexOf('-') + 1));\n        chrome.windows.create(options, (win) => {\n          windows[position] = win.id;\n          if (navigator.userAgent.indexOf('Firefox') !== -1) {\n            chrome.windows.update(win.id, { focused: true, ...customOptions });\n          }\n        });\n      }\n    });\n  }\n\n  let params = { left: 0, top: 0, width: 380, height: window.screen.availHeight };\n  let url = 'window.html';\n  switch (position) {\n    case 'devtools-right':\n      params.left = window.screen.availLeft + window.screen.availWidth - params.width;\n      break;\n    case 'devtools-bottom':\n      params.height = 420;\n      params.top = window.screen.height - params.height;\n      params.width = window.screen.availWidth;\n      break;\n    case 'devtools-panel':\n      params.type = 'panel';\n      break;\n    case 'devtools-remote':\n      params = { width: 850, height: 600 };\n      url = 'remote.html';\n      break;\n  }\n  popWindow('open', url, params);\n}\n"
  },
  {
    "path": "src/browser/extension/chromeAPIMock.js",
    "content": "// Mock not supported chrome.* API for Firefox and Electron\n\nwindow.isElectron = window.navigator &&\n  window.navigator.userAgent.indexOf('Electron') !== -1;\n\nconst isFirefox = navigator.userAgent.indexOf('Firefox') !== -1;\n\n// Background page only\nif (\n  window.isElectron &&\n  location.pathname === '/_generated_background_page.html' ||\n  isFirefox\n) {\n  chrome.runtime.onConnectExternal = {\n    addListener() {}\n  };\n  chrome.runtime.onMessageExternal = {\n    addListener() {}\n  };\n\n  if (window.isElectron) {\n    chrome.notifications = {\n      onClicked: {\n        addListener() {}\n      },\n      create() {},\n      clear() {}\n    };\n    chrome.contextMenus = {\n      onClicked: {\n        addListener() {}\n      }\n    };\n  } else {\n    chrome.storage.sync = chrome.storage.local;\n    chrome.runtime.onInstalled = {\n      addListener: cb => cb()\n    };\n  }\n}\n\nif (window.isElectron) {\n  if (!chrome.storage.local || !chrome.storage.local.remove) {\n    chrome.storage.local = {\n      set(obj, callback) {\n        Object.keys(obj).forEach(key => {\n          localStorage.setItem(key, obj[key]);\n        });\n        if (callback) {\n          callback();\n        }\n      },\n      get(obj, callback) {\n        const result = {};\n        Object.keys(obj).forEach(key => {\n          result[key] = localStorage.getItem(key) || obj[key];\n        });\n        if (callback) {\n          callback(result);\n        }\n      },\n      // Electron ~ 1.4.6\n      remove(items, callback) {\n        if (Array.isArray(items)) {\n          items.forEach(name => {\n            localStorage.removeItem(name);\n          });\n        } else {\n          localStorage.removeItem(items);\n        }\n        if (callback) {\n          callback();\n        }\n      }\n    };\n  }\n  // Avoid error: chrome.runtime.sendMessage is not supported responseCallback\n  const originSendMessage = chrome.runtime.sendMessage;\n  chrome.runtime.sendMessage = function() {\n    if (process.env.NODE_ENV === 'development') {\n      return originSendMessage(...arguments);\n    }\n    if (typeof arguments[arguments.length - 1] === 'function') {\n      Array.prototype.pop.call(arguments);\n    }\n    return originSendMessage(...arguments);\n  };\n}\n\nif (isFirefox) {\n  chrome.storage.sync = chrome.storage.local;\n}\n"
  },
  {
    "path": "src/browser/extension/devpanel/index.js",
    "content": "import 'remotedev-monitor-components/lib/presets';\nimport React from 'react';\nimport { render, unmountComponentAtNode } from 'react-dom';\nimport { Provider } from 'react-redux';\nimport { REMOVE_INSTANCE } from 'remotedev-app/lib/constants/actionTypes';\nimport App from '../../../app/containers/App';\nimport configureStore from '../../../app/stores/panelStore';\nimport getPreloadedState from '../background/getPreloadedState';\n\nconst position = location.hash;\nconst messageStyle = { padding: '20px', width: '100%', textAlign: 'center' };\n\nlet rendered;\nlet store;\nlet bgConnection;\nlet naTimeout;\nlet preloadedState;\n\nconst isChrome = navigator.userAgent.indexOf('Firefox') === -1;\n\ngetPreloadedState(position, state => { preloadedState = state; });\n\nfunction renderDevTools() {\n  const node = document.getElementById('root');\n  unmountComponentAtNode(node);\n  clearTimeout(naTimeout);\n  store = configureStore(position, bgConnection, preloadedState);\n  render(\n    <Provider store={store}>\n      <App position={position} />\n    </Provider>,\n    node\n  );\n  rendered = true;\n}\n\nfunction renderNA() {\n  if (rendered === false) return;\n  rendered = false;\n  naTimeout = setTimeout(() => {\n    let message = (\n      <div style={messageStyle}>\n        No store found. Make sure to follow <a href=\"https://github.com/zalmoxisus/redux-devtools-extension#usage\" target=\"_blank\">the instructions</a>.\n      </div>\n    );\n    if (isChrome) {\n      chrome.devtools.inspectedWindow.getResources(resources => {\n        if (resources[0].url.substr(0, 4) === 'file') {\n          message = (\n            <div style={messageStyle}>\n              No store found. Most likely you did not allow access to file URLs. <a href=\"https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/Troubleshooting.md#access-file-url-file\" target=\"_blank\">See details</a>.\n            </div>\n          );\n        }\n\n        const node = document.getElementById('root');\n        unmountComponentAtNode(node);\n        render(message, node);\n        store = undefined;\n      });\n    } else {\n      const node = document.getElementById('root');\n      unmountComponentAtNode(node);\n      render(message, node);\n      store = undefined;\n    }\n  }, 3500);\n}\n\nfunction init(id) {\n  renderNA();\n  bgConnection = chrome.runtime.connect({ name: id ? id.toString() : undefined });\n  bgConnection.onMessage.addListener(message => {\n    if (message.type === 'NA') {\n      if (message.id === id) renderNA();\n      else store.dispatch({ type: REMOVE_INSTANCE, id: message.id });\n    } else {\n      if (!rendered) renderDevTools();\n      store.dispatch(message);\n    }\n  });\n}\n\ninit(chrome.devtools.inspectedWindow.tabId);\n"
  },
  {
    "path": "src/browser/extension/devtools/index.js",
    "content": "function createPanel(url) {\n  chrome.devtools.panels.create(\n    'Redux', 'img/logo/scalable.png', url, function() {}\n  );\n}\n\nif (chrome.runtime.getBackgroundPage) {\n  // Check if the background page's object is accessible (not in incognito)\n  chrome.runtime.getBackgroundPage(background => {\n    createPanel(background ? 'window.html' : 'devpanel.html');\n  });\n} else {\n  createPanel('devpanel.html');\n}\n"
  },
  {
    "path": "src/browser/extension/inject/contentScript.js",
    "content": "import { injectOptions, getOptionsFromBg, isAllowed } from '../options/syncOptions';\nconst source = '@devtools-extension';\nconst pageSource = '@devtools-page';\n// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts\nconst maxChromeMsgSize = 32 * 1024 * 1024;\nlet connected = false;\nlet bg;\n\nfunction connect() {\n  // Connect to the background script\n  connected = true;\n  const name = 'tab';\n  if (window.devToolsExtensionID) {\n    bg = chrome.runtime.connect(window.devToolsExtensionID, { name });\n  } else {\n    bg = chrome.runtime.connect({ name });\n  }\n\n  // Relay background script messages to the page script\n  bg.onMessage.addListener((message) => {\n    if (message.action) {\n      window.postMessage({\n        type: message.type,\n        payload: message.action,\n        state: message.state,\n        id: message.id,\n        source\n      }, '*');\n    } else if (message.options) {\n      injectOptions(message.options);\n    } else {\n      window.postMessage({\n        type: message.type,\n        state: message.state,\n        id: message.id,\n        source\n      }, '*');\n    }\n  });\n\n  bg.onDisconnect.addListener(handleDisconnect);\n}\n\nfunction handleDisconnect() {\n  window.removeEventListener('message', handleMessages);\n  window.postMessage({ type: 'STOP', failed: true, source }, '*');\n  bg = undefined;\n}\n\nfunction tryCatch(fn, args) {\n  try {\n    return fn(args);\n  } catch (err) {\n    if (err.message === 'Message length exceeded maximum allowed length.') {\n      const instanceId = args.instanceId;\n      const newArgs = { split: 'start' };\n      const toSplit = [];\n      let size = 0;\n      let arg;\n      Object.keys(args).map(key => {\n        arg = args[key];\n        if (typeof arg === 'string') {\n          size += arg.length;\n          if (size > maxChromeMsgSize) {\n            toSplit.push([key, arg]);\n            return;\n          }\n        }\n        newArgs[key] = arg;\n      });\n      fn(newArgs);\n      for (let i = 0; i < toSplit.length; i++) {\n        for (let j = 0; j < toSplit[i][1].length; j += maxChromeMsgSize) {\n          fn({\n            instanceId,\n            source: pageSource,\n            split: 'chunk',\n            chunk: [toSplit[i][0], toSplit[i][1].substr(j, maxChromeMsgSize)]\n          });\n        }\n      }\n      return fn({ instanceId, source: pageSource, split: 'end' });\n    }\n    handleDisconnect();\n    /* eslint-disable no-console */\n    if (process.env.NODE_ENV !== 'production') console.error('Failed to send message', err);\n    /* eslint-enable no-console */\n  }\n}\n\nfunction send(message) {\n  if (!connected) connect();\n  if (message.type === 'INIT_INSTANCE') {\n    getOptionsFromBg();\n    bg.postMessage({ name: 'INIT_INSTANCE', instanceId: message.instanceId });\n  } else {\n    bg.postMessage({ name: 'RELAY', message });\n  }\n}\n\n// Resend messages from the page to the background script\nfunction handleMessages(event) {\n  if (!isAllowed()) return;\n  if (!event || event.source !== window || typeof event.data !== 'object') return;\n  const message = event.data;\n  if (message.source !== pageSource) return;\n  if (message.type === 'DISCONNECT') {\n    if (bg) {\n      bg.disconnect();\n      connected = false;\n    }\n    return;\n  }\n\n  tryCatch(send, message);\n}\n\nwindow.addEventListener('message', handleMessages, false);\n"
  },
  {
    "path": "src/browser/extension/inject/deprecatedWarn.js",
    "content": "// Deprecated warning for inject.bundle.js\n/* eslint-disable no-console */\nconsole.warn(\n  'Using Redux DevTools inside extensions is deprecated and won\\'t be supported in the next major version. ' +\n  'Please use https://github.com/zalmoxisus/remote-redux-devtools instead.'\n);\n/* eslint-enable no-console */\n"
  },
  {
    "path": "src/browser/extension/inject/index.js",
    "content": "// Include this script in Chrome apps and extensions for remote debugging\n// <script src=\"chrome-extension://lmhkpmbekcpmknklioeibfkpmmfibljd/js/redux-devtools-extension.js\"></script>\n\nwindow.devToolsExtensionID = 'lmhkpmbekcpmknklioeibfkpmmfibljd';\nrequire('./contentScript');\nrequire('./pageScript');\n\nchrome.runtime.sendMessage(window.devToolsExtensionID, { type: 'GET_OPTIONS' }, function(response) {\n  if (!response.options.inject) {\n    const urls = response.options.urls.split('\\n').filter(Boolean).join('|');\n    if (!location.href.match(new RegExp(urls))) return;\n  }\n\n  window.devToolsOptions = response.options;\n  window.__REDUX_DEVTOOLS_EXTENSION__.notifyErrors();\n});\n"
  },
  {
    "path": "src/browser/extension/inject/pageScript.js",
    "content": "import { getActionsArray, evalAction } from 'remotedev-utils';\nimport throttle from 'lodash/throttle';\nimport createStore from '../../../app/stores/createStore';\nimport configureStore, { getUrlParam } from '../../../app/stores/enhancerStore';\nimport { isAllowed } from '../options/syncOptions';\nimport Monitor from '../../../app/service/Monitor';\nimport {\n  noFiltersApplied, getLocalFilter, isFiltered, filterState, startingFrom\n} from '../../../app/api/filters';\nimport notifyErrors from '../../../app/api/notifyErrors';\nimport importState from '../../../app/api/importState';\nimport openWindow from '../../../app/api/openWindow';\nimport generateId from '../../../app/api/generateInstanceId';\nimport {\n  updateStore, toContentScript, sendMessage, setListener, connect, disconnect,\n  isInIframe, getSeralizeParameter\n} from '../../../app/api';\n\nconst source = '@devtools-page';\nlet stores = {};\nlet reportId;\n\nfunction deprecateParam(oldParam, newParam) {\n  /* eslint-disable no-console */\n  console.warn(`${oldParam} parameter is deprecated, use ${newParam} instead: https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md`);\n  /* eslint-enable no-console */\n}\n\nconst __REDUX_DEVTOOLS_EXTENSION__ = function(reducer, preloadedState, config) {\n  /* eslint-disable no-param-reassign */\n  if (typeof reducer === 'object') {\n    config = reducer; reducer = undefined;\n  } else if (typeof config !== 'object') config = {};\n  /* eslint-enable no-param-reassign */\n  if (!window.devToolsOptions) window.devToolsOptions = {};\n\n  let store;\n  let errorOccurred = false;\n  let maxAge;\n  let actionCreators;\n  let sendingActionId = 1;\n  const instanceId = generateId(config.instanceId);\n  const localFilter = getLocalFilter(config);\n  const serializeState = getSeralizeParameter(config, 'serializeState');\n  const serializeAction = getSeralizeParameter(config, 'serializeAction');\n  let {\n    statesFilter, actionsFilter, stateSanitizer, actionSanitizer, predicate, latency = 500\n  } = config;\n\n  // Deprecate statesFilter and actionsFilter\n  if (statesFilter) {\n    deprecateParam('statesFilter', 'stateSanitizer');\n    stateSanitizer = statesFilter; // eslint-disable-line no-param-reassign\n  }\n  if (actionsFilter) {\n    deprecateParam('actionsFilter', 'actionSanitizer');\n    actionSanitizer = actionsFilter; // eslint-disable-line no-param-reassign\n  }\n\n  const monitor = new Monitor(relayState);\n  if (config.getMonitor) {\n    /* eslint-disable no-console */\n    console.warn('Redux DevTools extension\\'s `getMonitor` parameter is deprecated and will be not ' +\n      'supported in the next version, please remove it and just use ' +\n      '`__REDUX_DEVTOOLS_EXTENSION_COMPOSE__` instead: ' +\n      'https://github.com/zalmoxisus/redux-devtools-extension#12-advanced-store-setup');\n    /* eslint-enable no-console */\n    config.getMonitor(monitor);\n  }\n\n  function exportState() {\n    const liftedState = store.liftedStore.getState();\n    const actionsById = liftedState.actionsById;\n    const payload = [];\n    liftedState.stagedActionIds.slice(1).forEach(id => {\n      // if (isFiltered(actionsById[id].action, localFilter)) return;\n      payload.push(actionsById[id].action);\n    });\n    toContentScript({\n      type: 'EXPORT', payload, committedState: liftedState.committedState, source, instanceId\n    }, serializeState, serializeAction);\n  }\n\n  function relay(type, state, action, nextActionId, libConfig) {\n    const message = {\n      type,\n      payload: filterState(\n        state, type, localFilter, stateSanitizer, actionSanitizer, nextActionId, predicate\n      ),\n      source,\n      instanceId\n    };\n\n    if (type === 'ACTION') {\n      message.action = !actionSanitizer ? action : actionSanitizer(action.action, nextActionId - 1);\n      message.maxAge = getMaxAge();\n      message.nextActionId = nextActionId;\n    } else if (libConfig) {\n      message.libConfig = libConfig;\n    }\n\n    toContentScript(message, serializeState, serializeAction);\n  }\n\n  const relayState = throttle((liftedState, libConfig) => {\n    relayAction.cancel();\n    const state = liftedState || store.liftedStore.getState();\n    sendingActionId = state.nextActionId;\n    relay('STATE', state, undefined, undefined, libConfig);\n  }, latency);\n\n  const relayAction = throttle(() => {\n    const liftedState = store.liftedStore.getState();\n    const nextActionId = liftedState.nextActionId;\n    const currentActionId = nextActionId - 1;\n    const liftedAction = liftedState.actionsById[currentActionId];\n\n    // Send a single action\n    if (sendingActionId === currentActionId) {\n      sendingActionId = nextActionId;\n      const action = liftedAction.action;\n      const computedStates = liftedState.computedStates;\n      if (\n        isFiltered(action, localFilter) ||\n        predicate && !predicate(computedStates[computedStates.length - 1].state, action)\n      ) return;\n      const state = liftedState.computedStates[liftedState.computedStates.length - 1].state;\n      relay('ACTION', state, liftedState.actionsById[nextActionId - 1], nextActionId);\n      return;\n    }\n\n    // Send multiple actions\n    const payload = startingFrom(\n      sendingActionId,\n      liftedState,\n      localFilter, stateSanitizer, actionSanitizer, predicate\n    );\n    sendingActionId = nextActionId;\n    if (typeof payload === 'undefined') return;\n    if (typeof payload.skippedActionIds !== 'undefined') {\n      relay('STATE', payload);\n      return;\n    }\n    toContentScript({\n      type: 'PARTIAL_STATE',\n      payload,\n      source,\n      instanceId,\n      maxAge: getMaxAge()\n    }, serializeState, serializeAction);\n  }, latency);\n\n  function dispatchRemotely(action) {\n    if (config.features && !config.features.dispatch) return;\n    try {\n      const result = evalAction(action, actionCreators);\n      (store.initialDispatch || store.dispatch)(result);\n    } catch (e) {\n      relay('ERROR', e.message);\n    }\n  }\n\n  function importPayloadFrom(state) {\n    if (config.features && !config.features.import) return;\n    try {\n      const nextLiftedState = importState(state, config);\n      if (!nextLiftedState) return;\n      store.liftedStore.dispatch({type: 'IMPORT_STATE', ...nextLiftedState});\n    } catch (e) {\n      relay('ERROR', e.message);\n    }\n  }\n\n  function dispatchMonitorAction(action) {\n    const type = action.type;\n    const features = config.features;\n    if (features) {\n      if (!features.jump && (type === 'JUMP_TO_STATE' || type === 'JUMP_TO_ACTION')) return;\n      if (!features.skip && type === 'TOGGLE_ACTION') return;\n      if (!features.reorder && type === 'REORDER_ACTION') return;\n      if (!features.import && type === 'IMPORT_STATE') return;\n      if (!features.lock && type === 'LOCK_CHANGES') return;\n      if (!features.pause && type === 'PAUSE_RECORDING') return;\n    }\n    if (type === 'JUMP_TO_STATE') {\n      const liftedState = store.liftedStore.getState();\n      const index = liftedState.stagedActionIds.indexOf(action.actionId);\n      if (index === -1) return;\n      store.liftedStore.dispatch({ type, index });\n      return;\n    }\n    store.liftedStore.dispatch(action);\n  }\n\n  function onMessage(message) {\n    switch (message.type) {\n      case 'DISPATCH':\n        dispatchMonitorAction(message.payload);\n        return;\n      case 'ACTION':\n        dispatchRemotely(message.payload);\n        return;\n      case 'IMPORT':\n        importPayloadFrom(message.state);\n        return;\n      case 'EXPORT':\n        exportState();\n        return;\n      case 'UPDATE':\n        relayState();\n        return;\n      case 'START':\n        monitor.start(true);\n        if (!actionCreators && config.actionCreators) {\n          actionCreators = getActionsArray(config.actionCreators);\n        }\n        relayState(undefined, {\n          name: config.name || document.title,\n          actionCreators: JSON.stringify(actionCreators),\n          features: config.features,\n          serialize: !!config.serialize,\n          type: 'redux'\n        });\n\n        if (reportId) {\n          relay('GET_REPORT', reportId);\n          reportId = null;\n        }\n        return;\n      case 'STOP':\n        monitor.stop();\n        relayAction.cancel();\n        relayState.cancel();\n        if (!message.failed) relay('STOP');\n    }\n  }\n\n  const filteredActionIds = []; // simple circular buffer of non-excluded actions with fixed maxAge-1 length\n  const getMaxAge = (liftedAction, liftedState) => {\n    let m = config && config.maxAge || window.devToolsOptions.maxAge || 50;\n    if (!liftedAction || noFiltersApplied(localFilter) || !liftedAction.action) return m;\n    if (!maxAge || maxAge < m) maxAge = m; // it can be modified in process on options page\n    if (isFiltered(liftedAction.action, localFilter)) {\n      // TODO: check also predicate && !predicate(state, action) with current state\n      maxAge++;\n    } else {\n      filteredActionIds.push(liftedState.nextActionId);\n      if (filteredActionIds.length >= m) {\n        const stagedActionIds = liftedState.stagedActionIds;\n        let i = 1;\n        while (maxAge > m && filteredActionIds.indexOf(stagedActionIds[i]) === -1) {\n          maxAge--; i++;\n        }\n        filteredActionIds.shift();\n      }\n    }\n    return maxAge;\n  };\n\n  function init() {\n    setListener(onMessage, instanceId);\n    notifyErrors(() => {\n      errorOccurred = true;\n      const state = store.liftedStore.getState();\n      if (state.computedStates[state.currentStateIndex].error) {\n        relayState(state);\n      }\n      return true;\n    });\n\n    relay('INIT_INSTANCE');\n    store.subscribe(handleChange);\n\n    if (typeof reportId === 'undefined') {\n      reportId = getUrlParam('remotedev_report');\n      if (reportId) openWindow();\n    }\n  }\n\n  function handleChange() {\n    if (!monitor.active) return;\n    if (!errorOccurred && !monitor.isMonitorAction()) {\n      relayAction();\n      return;\n    }\n    if (monitor.isPaused() || monitor.isLocked() || monitor.isTimeTraveling()) return;\n    const liftedState = store.liftedStore.getState();\n    if (errorOccurred && !liftedState.computedStates[liftedState.currentStateIndex].error) {\n      errorOccurred = false;\n    }\n    relayState(liftedState);\n  }\n\n  const enhance = () => (next) => {\n    return (reducer_, initialState_, enhancer_) => {\n      if (!isAllowed(window.devToolsOptions)) return next(reducer_, initialState_, enhancer_);\n\n      store = stores[instanceId] =\n        configureStore(next, monitor.reducer, { ...config, maxAge: getMaxAge })(reducer_, initialState_, enhancer_);\n\n      if (isInIframe()) setTimeout(init, 3000);\n      else init();\n\n      return store;\n    };\n  };\n\n  if (!reducer) return enhance();\n  /* eslint-disable no-console */\n  console.warn('Creating a Redux store directly from DevTools extension is discouraged and will not be supported in future major version. For more details see: https://git.io/fphCe');\n  /* eslint-enable no-console */\n  return createStore(reducer, preloadedState, enhance);\n};\n\n// noinspection JSAnnotator\nwindow.__REDUX_DEVTOOLS_EXTENSION__ = __REDUX_DEVTOOLS_EXTENSION__;\nwindow.__REDUX_DEVTOOLS_EXTENSION__.open = openWindow;\nwindow.__REDUX_DEVTOOLS_EXTENSION__.updateStore = updateStore(stores);\nwindow.__REDUX_DEVTOOLS_EXTENSION__.notifyErrors = notifyErrors;\nwindow.__REDUX_DEVTOOLS_EXTENSION__.send = sendMessage;\nwindow.__REDUX_DEVTOOLS_EXTENSION__.listen = setListener;\nwindow.__REDUX_DEVTOOLS_EXTENSION__.connect = connect;\nwindow.__REDUX_DEVTOOLS_EXTENSION__.disconnect = disconnect;\n\n// Deprecated\n/* eslint-disable no-console */\nlet varNameDeprecatedWarned;\nconst varNameDeprecatedWarn = () => {\n  if (varNameDeprecatedWarned) return;\n  console.warn('`window.devToolsExtension` is deprecated in favor of `window.__REDUX_DEVTOOLS_EXTENSION__`, and will be removed in next version of Redux DevTools: https://git.io/fpEJZ');\n  varNameDeprecatedWarned = true;\n};\n/* eslint-enable no-console */\nwindow.devToolsExtension = (...args) => {\n  varNameDeprecatedWarn();\n  return __REDUX_DEVTOOLS_EXTENSION__.apply(null, args);\n};\nwindow.devToolsExtension.open = (...args) => {\n  varNameDeprecatedWarn();\n  return openWindow.apply(null, args);\n};\nwindow.devToolsExtension.updateStore = (...args) => {\n  varNameDeprecatedWarn();\n  return updateStore(stores).apply(null, args);\n};\nwindow.devToolsExtension.notifyErrors = (...args) => {\n  varNameDeprecatedWarn();\n  return notifyErrors.apply(null, args);\n};\nwindow.devToolsExtension.send = (...args) => {\n  varNameDeprecatedWarn();\n  return sendMessage.apply(null, args);\n};\nwindow.devToolsExtension.listen = (...args) => {\n  varNameDeprecatedWarn();\n  return setListener.apply(null, args);\n};\nwindow.devToolsExtension.connect = (...args) => {\n  varNameDeprecatedWarn();\n  return connect.apply(null, args);\n};\nwindow.devToolsExtension.disconnect = (...args) => {\n  varNameDeprecatedWarn();\n  return disconnect.apply(null, args);\n};\n\nconst preEnhancer = instanceId => next =>\n  (reducer, preloadedState, enhancer) => {\n    const store = next(reducer, preloadedState, enhancer);\n\n    if (stores[instanceId]) {\n      stores[instanceId].initialDispatch = store.dispatch;\n    }\n\n    return {\n      ...store,\n      dispatch: (...args) => (\n        !window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__ && store.dispatch(...args)\n      )\n    };\n  };\n\nconst extensionCompose = (config) => (...funcs) => {\n  return (...args) => {\n    const instanceId = generateId(config.instanceId);\n    return [preEnhancer(instanceId), ...funcs].reduceRight(\n      (composed, f) => f(composed), __REDUX_DEVTOOLS_EXTENSION__({ ...config, instanceId })(...args)\n    );\n  };\n};\n\nwindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = (...funcs) => {\n  if (funcs.length === 0) {\n    return __REDUX_DEVTOOLS_EXTENSION__();\n  }\n  if (funcs.length === 1 && typeof funcs[0] === 'object') {\n    return extensionCompose(funcs[0]);\n  }\n  return extensionCompose({})(...funcs);\n};\n"
  },
  {
    "path": "src/browser/extension/inject/pageScriptWrap.js",
    "content": "let s = document.createElement('script');\ns.type = 'text/javascript';\n\nif (process.env.NODE_ENV === 'production') {\n  const script = require('raw-loader!tmp/page.bundle.js');\n  s.appendChild(document.createTextNode(script));\n  (document.head || document.documentElement).appendChild(s);\n  s.parentNode.removeChild(s);\n} else {\n  s.src = chrome.extension.getURL('js/page.bundle.js');\n  s.onload = function() {\n    this.parentNode.removeChild(this);\n  };\n  (document.head || document.documentElement).appendChild(s);\n}\n"
  },
  {
    "path": "src/browser/extension/manifest.json",
    "content": "{\n  \"version\": \"2.17.1\",\n  \"name\": \"Redux DevTools\",\n  \"short_name\": \"Redux DevTools\",\n  \"description\": \"Redux DevTools for debugging application's state changes.\",\n  \"homepage_url\": \"https://github.com/zalmoxisus/redux-devtools-extension\",\n  \"manifest_version\": 2,\n  \"page_action\": {\n    \"default_icon\": \"img/logo/gray.png\",\n    \"default_title\": \"Redux DevTools\",\n    \"default_popup\": \"window.html#popup\"\n  },\n  \"commands\": {\n    \"devtools-left\": {\n      \"description\": \"DevTools window to left\"\n    },\n    \"devtools-right\": {\n      \"description\": \"DevTools window to right\"\n    },\n    \"devtools-bottom\": {\n      \"description\": \"DevTools window to bottom\"\n    },\n    \"devtools-remote\": {\n      \"description\": \"Remote DevTools\"\n    },\n    \"_execute_page_action\": {\n      \"suggested_key\": {\n        \"default\": \"Ctrl+Shift+E\"\n      }\n    }\n  },\n  \"icons\": {\n    \"16\": \"img/logo/16x16.png\",\n    \"48\": \"img/logo/48x48.png\",\n    \"128\": \"img/logo/128x128.png\"\n  },\n  \"options_ui\": {\n    \"page\": \"options.html\",\n    \"chrome_style\": true\n  },\n  \"background\": {\n    \"scripts\": [\"js/background.bundle.js\"],\n    \"persistent\": false\n  },\n  \"content_scripts\": [\n    {\n      \"matches\": [\"<all_urls>\"],\n      \"exclude_globs\": [ \"https://www.google*\" ],\n      \"js\": [\"js/content.bundle.js\", \"js/pagewrap.bundle.js\"],\n      \"run_at\": \"document_start\",\n      \"all_frames\": true\n    }\n  ],\n  \"devtools_page\": \"devtools.html\",\n  \"web_accessible_resources\": [\n    \"js/page.bundle.js\",\n    \"js/inject.bundle.js\",\n    \"js/redux-devtools-extension.js\"\n  ],\n  \"externally_connectable\": {\n    \"ids\": [\"*\"]\n  },\n  \"permissions\": [ \"notifications\", \"contextMenus\", \"tabs\", \"storage\", \"<all_urls>\" ],\n  \"content_security_policy\": \"script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;\",\n  \"update_url\": \"https://clients2.google.com/service/update2/crx\",\n  \"key\": \"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdJEPwY92xUACA9CcDBDBmbdbp8Ap3cKQ0DJTUuVQvqb4FQAv8RtKY3iUjGvdwuAcSJQIZwHXcP2aNDH3TiFik/NhRK2GRW8X3OZyTdkuDueABGP2KEX8q1WQDgjX/rPIinGYztUrvoICw/UerMPwNW62jwGoVU3YhAGf+15CgX2Y6a4tppnf/+1mPedKPidh0RsM+aJY98rX+r1SPAHPcGzMjocLkqcT75DZBXer8VQN14tOOzRCd6T6oy7qm7eWru8lJwcY66qMQvhk0osqEod2G3nA7aTWpmqPFS66VEiecP9PgZlp8gQdgZ3dFhA62exydlD55JuRhiMIR63yQIDAQAB\"\n}\n"
  },
  {
    "path": "src/browser/extension/options/AllowToRunGroup.js",
    "content": "import React from 'react';\n\nexport default ({ options, saveOption }) => {\n  const AllowToRunState = {\n    EVERYWHERE: true,\n    ON_SPECIFIC_URLS: false\n  };\n\n  return (\n    <fieldset className=\"option-group\">\n      <legend className=\"option-group__title\">Allow to run</legend>\n\n      <div className=\"option option_type_radio\">\n        <input className=\"option__element\"\n               id=\"inject-always\"\n               name=\"inject\"\n               type=\"radio\"\n               checked={options.inject === AllowToRunState.EVERYWHERE}\n               onChange={() => saveOption('inject', AllowToRunState.EVERYWHERE)}/>\n        <label className=\"option__label\" htmlFor=\"inject-always\">Everywhere</label>\n      </div>\n\n      <div className=\"option option_type_radio\">\n        <input className=\"option__element\"\n               id=\"inject-specific\"\n               name=\"inject\"\n               type=\"radio\"\n               checked={options.inject === AllowToRunState.ON_SPECIFIC_URLS}\n               onChange={() => saveOption('inject', AllowToRunState.ON_SPECIFIC_URLS)}/>\n        <label className=\"option__label\" htmlFor=\"inject-specific\">Only on the following URLs:</label>\n        <br/>\n        <textarea className=\"option__textarea\"\n                  value={options.urls}\n                  disabled={options.inject !== AllowToRunState.ON_SPECIFIC_URLS}\n                  onChange={(e) => saveOption('urls', e.target.value)} />\n        <div className=\"option__hint\">Each RegExp from the new line</div>\n      </div>\n    </fieldset>\n  );\n};\n"
  },
  {
    "path": "src/browser/extension/options/ContextMenuGroup.js",
    "content": "import React from 'react';\n\nexport default ({ options, saveOption }) => {\n  return (\n    <fieldset className=\"option-group\">\n      <legend className=\"option-group__title\">Context Menu</legend>\n\n      <div className=\"option option_type_checkbox\">\n        <input className=\"option__element\"\n               id=\"showContextMenus\"\n               type=\"checkbox\"\n               checked={options.showContextMenus}\n               onChange={(e) => saveOption('showContextMenus', e.target.checked)}/>\n        <label className=\"option__label\" htmlFor=\"showContextMenus\">Add Context Menus</label>\n        <div className=\"option__hint\">\n          Add Redux DevTools to right-click context menu\n        </div>\n      </div>\n    </fieldset>\n  );\n};\n"
  },
  {
    "path": "src/browser/extension/options/EditorGroup.js",
    "content": "import React from 'react';\n\nexport default ({ options, saveOption }) => {\n  const EditorState = {\n    BROWSER: 0,\n    EXTERNAL: 1\n  };\n\n  return (\n    <fieldset className=\"option-group\">\n      <legend className=\"option-group__title\">Editor for stack traces</legend>\n\n      <div className=\"option option_type_radio\">\n        <input className=\"option__element\"\n               id=\"editor-browser\"\n               name=\"useEditor\"\n               type=\"radio\"\n               checked={options.useEditor === EditorState.BROWSER}\n               onChange={() => saveOption('useEditor', EditorState.BROWSER)}/>\n        <label className=\"option__label\" htmlFor=\"editor-browser\">{\n          navigator.userAgent.indexOf('Firefox') !== -1 ?\n            'Don\\'t open in external editor' :\n            'Use browser\\'s debugger (from Chrome devpanel only)'\n        }</label>\n      </div>\n\n      <div className=\"option option_type_radio\" style={{ display: 'flex', alignItems: 'center' }}>\n        <input className=\"option__element\"\n               id=\"editor-external\"\n               name=\"useEditor\"\n               type=\"radio\"\n               checked={options.useEditor === EditorState.EXTERNAL}\n               onChange={() => saveOption('useEditor', EditorState.EXTERNAL)}/>\n        <label className=\"option__label\" htmlFor=\"editor-external\">External editor:&nbsp;</label>\n        <input className=\"option__element\"\n               id=\"editor\"\n               type=\"text\"\n               size=\"33\"\n               maxLength={30}\n               placeholder=\"vscode, atom, webstorm, sublime...\"\n               value={options.editor}\n               disabled={options.useEditor !== EditorState.EXTERNAL}\n               onChange={(e) => saveOption('editor', e.target.value.replace(/\\W/g, ''))}/>\n      </div>\n      <div className=\"option option_type_radio\">\n        <label className=\"option__label\" htmlFor=\"editor-external\" style={{marginLeft: '20px'}}>\n          Absolute path to the project directory to open:\n        </label>\n        <br/>\n        <textarea className=\"option__textarea\"\n                  placeholder=\"/home/user/my-awesome-app\"\n                  value={options.projectPath}\n                  disabled={options.useEditor !== EditorState.EXTERNAL}\n                  onChange={(e) => saveOption('projectPath', e.target.value.replace('\\n', ''))} />\n        <div className=\"option__hint\">Run `pwd` in your project root directory to get it</div>\n      </div>\n    </fieldset>\n  );\n};\n"
  },
  {
    "path": "src/browser/extension/options/FilterGroup.js",
    "content": "import React from 'react';\nimport { FilterState } from '../../../app/api/filters';\n\nexport default ({ options, saveOption }) => {\n  return (\n    <fieldset className=\"option-group\">\n      <legend className=\"option-group__title\">Filter actions in DevTools</legend>\n\n      <div className=\"option option_type_radio\">\n        <input className=\"option__element\"\n               id=\"filter-do-not\"\n               name=\"filter\"\n               type=\"radio\"\n               checked={options.filter === FilterState.DO_NOT_FILTER}\n               onChange={() => saveOption('filter', FilterState.DO_NOT_FILTER)}/>\n        <label className=\"option__label\" htmlFor=\"filter-do-not\">Don’t filter</label>\n      </div>\n\n      <div className=\"option option_type_radio\">\n        <input className=\"option__element\"\n               id=\"filter-hide\"\n               name=\"filter\"\n               type=\"radio\"\n               checked={options.filter === FilterState.BLACKLIST_SPECIFIC}\n               onChange={() => saveOption('filter', FilterState.BLACKLIST_SPECIFIC)}/>\n        <label className=\"option__label\" htmlFor=\"filter-hide\">Hide the following:</label>\n        <br/>\n        <textarea className=\"option__textarea\"\n                  value={options.blacklist}\n                  disabled={options.filter !== FilterState.BLACKLIST_SPECIFIC}\n                  onChange={(e) => saveOption('blacklist', e.target.value)} />\n        <div className=\"option__hint\">Each action from the new line</div>\n      </div>\n\n      <div className=\"option option_type_radio\">\n        <input className=\"option__element\"\n               id=\"filter-show\"\n               name=\"filter\"\n               type=\"radio\"\n               checked={options.filter === FilterState.WHITELIST_SPECIFIC}\n               onChange={() => saveOption('filter', FilterState.WHITELIST_SPECIFIC)}/>\n        <label className=\"option__label\" htmlFor=\"filter-show\">Show the following:</label>\n        <br/>\n        <textarea className=\"option__textarea\"\n                  value={options.whitelist}\n                  disabled={options.filter !== FilterState.WHITELIST_SPECIFIC}\n                  onChange={(e) => saveOption('whitelist', e.target.value)} />\n        <div className=\"option__hint\">Each action from the new line</div>\n      </div>\n    </fieldset>\n  );\n};\n"
  },
  {
    "path": "src/browser/extension/options/MiscellaneousGroup.js",
    "content": "import React from 'react';\n\nexport default ({ options, saveOption }) => {\n  const browserName = navigator.userAgent.includes('Firefox') ? 'Firefox' : 'Chrome';\n\n  return (\n    <fieldset className=\"option-group\">\n      <legend className=\"option-group__title\">Miscellaneous</legend>\n\n      <div className=\"option option_value_max-age\">\n        <label className=\"option__label\" htmlFor=\"maxAge\">Limit the action history to</label>\n        {' '}\n        <input className=\"option__element\"\n               id=\"maxAge\"\n               type=\"number\"\n               min=\"2\"\n               value={options.maxAge}\n               onChange={(e) => saveOption('maxAge', Number(e.target.value))}/>\n        {' '}\n        <label className=\"option__label\" htmlFor=\"maxAge\">items</label>\n        <div className=\"option__hint\">\n          When the number is reached, DevTools start removing the oldest actions. Improves the DevTools performance.\n          {' '}\n          <a href=\"https://github.com/zalmoxisus/redux-devtools-extension/pull/54#issuecomment-188167725\">More info</a>\n        </div>\n      </div>\n\n      <div className=\"option option_type_checkbox\">\n        <input className=\"option__element\"\n               id=\"notifyErrors\"\n               type=\"checkbox\"\n               checked={options.shouldCatchErrors}\n               onChange={(e) => saveOption('shouldCatchErrors', e.target.checked)}/>\n        <label className=\"option__label\" htmlFor=\"notifyErrors\">Show errors</label>\n        <div className=\"option__hint\">\n          Show the {browserName} notifications when errors occur in the app\n        </div>\n      </div>\n    </fieldset>\n  );\n};\n"
  },
  {
    "path": "src/browser/extension/options/Options.js",
    "content": "import React from 'react';\nimport EditorGroup from './EditorGroup';\nimport FilterGroup from './FilterGroup';\nimport AllowToRunGroup from './AllowToRunGroup';\nimport MiscellaneousGroup from './MiscellaneousGroup';\nimport ContextMenuGroup from './ContextMenuGroup';\n\nexport default (props) => (\n  <div>\n    <EditorGroup {...props} />\n    <FilterGroup {...props} />\n    <AllowToRunGroup {...props} />\n    <MiscellaneousGroup {...props} />\n    <ContextMenuGroup {...props} />\n    <div style={{ color: 'red' }}><br /><hr />Setting options here is discouraged, and will not be possible in the next major release. Please <a href=\"https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md\" target=\"_blank\" style={{ color: 'red' }}>specify them as parameters</a>. See <a href=\"https://github.com/zalmoxisus/redux-devtools-extension/issues/296\" target=\"_blank\" style={{ color: 'red' }}>the issue</a> for more details.</div>\n  </div>\n);\n"
  },
  {
    "path": "src/browser/extension/options/index.js",
    "content": "import React from 'react';\nimport { render } from 'react-dom';\nimport Options from './Options';\n\nchrome.runtime.getBackgroundPage(background => {\n  const syncOptions = background.syncOptions;\n\n  const saveOption = (name, value) => {\n    syncOptions.save(name, value);\n  };\n\n  const renderOptions = options => {\n    render(\n      <Options options={options} saveOption={saveOption} />,\n      document.getElementById('root')\n    );\n  };\n\n  syncOptions.subscribe(renderOptions);\n  syncOptions.get(options => {\n    renderOptions(options);\n  });\n});\n"
  },
  {
    "path": "src/browser/extension/options/syncOptions.js",
    "content": "import { FilterState } from '../../../app/api/filters';\n\nlet options;\nlet subscribers = [];\n\nconst save = (toAllTabs) => (key, value) => {\n  let obj = {};\n  obj[key] = value;\n  chrome.storage.sync.set(obj);\n  options[key] = value;\n  toAllTabs({ options: options });\n  subscribers.forEach(s => s(options));\n};\n\nconst migrateOldOptions = (oldOptions) => {\n  let newOptions = Object.assign({}, oldOptions);\n\n  // Migrate the old `filter` option from 2.2.1\n  if (typeof oldOptions.filter === 'boolean') {\n    if (oldOptions.filter && oldOptions.whitelist.length > 0) {\n      newOptions.filter = FilterState.WHITELIST_SPECIFIC;\n    } else if (oldOptions.filter) {\n      newOptions.filter = FilterState.BLACKLIST_SPECIFIC;\n    } else {\n      newOptions.filter = FilterState.DO_NOT_FILTER;\n    }\n  }\n\n  return newOptions;\n};\n\nconst get = callback => {\n  if (options) callback(options);\n  else {\n    chrome.storage.sync.get({\n      useEditor: 0,\n      editor: '',\n      projectPath: '',\n      maxAge: 50,\n      filter: FilterState.DO_NOT_FILTER,\n      whitelist: '',\n      blacklist: '',\n      shouldCatchErrors: false,\n      inject: true,\n      urls: '^https?://localhost|0\\\\.0\\\\.0\\\\.0:\\\\d+\\n^https?://.+\\\\.github\\\\.io',\n      showContextMenus: true\n    }, function(items) {\n      options = migrateOldOptions(items);\n      callback(options);\n    });\n  }\n};\n\nconst subscribe = callback => {\n  subscribers = subscribers.concat(callback);\n};\n\nconst toReg = str => (\n  str !== '' ? str.split('\\n').filter(Boolean).join('|') : null\n);\n\nexport const injectOptions = newOptions => {\n  if (!newOptions) return;\n  if (newOptions.filter !== FilterState.DO_NOT_FILTER) {\n    newOptions.whitelist = toReg(newOptions.whitelist);\n    newOptions.blacklist = toReg(newOptions.blacklist);\n  }\n\n  options = newOptions;\n  let s = document.createElement('script');\n  s.type = 'text/javascript';\n  s.appendChild(document.createTextNode(\n    'window.devToolsOptions = Object.assign(window.devToolsOptions||{},' + JSON.stringify(options) + ');'\n  ));\n  (document.head || document.documentElement).appendChild(s);\n  s.parentNode.removeChild(s);\n};\n\nexport const getOptionsFromBg = () => {\n/*  chrome.runtime.sendMessage({ type: 'GET_OPTIONS' }, response => {\n    if (response && response.options) injectOptions(response.options);\n  });\n*/\n  get(newOptions => { injectOptions(newOptions); }); // Legacy\n};\n\nexport const isAllowed = (localOptions = options) => (\n  !localOptions || localOptions.inject || !localOptions.urls\n    || location.href.match(toReg(localOptions.urls))\n);\n\nexport default function syncOptions(toAllTabs) {\n  if (toAllTabs && !options) get(() => {}); // Initialize\n  return {\n    save: save(toAllTabs),\n    get: get,\n    subscribe: subscribe\n  };\n}\n"
  },
  {
    "path": "src/browser/extension/window/index.js",
    "content": "import 'remotedev-monitor-components/lib/presets';\nimport React from 'react';\nimport { render } from 'react-dom';\nimport { Provider } from 'react-redux';\nimport { UPDATE_STATE } from 'remotedev-app/lib/constants/actionTypes';\nimport App from '../../../app/containers/App';\nimport configureStore from '../../../app/stores/windowStore';\nimport getPreloadedState from '../background/getPreloadedState';\n\nconst position = location.hash;\nlet preloadedState;\ngetPreloadedState(position, state => { preloadedState = state; });\n\nchrome.runtime.getBackgroundPage(({ store }) => {\n  const localStore = configureStore(store, position, preloadedState);\n  let name = 'monitor';\n  if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) name += chrome.devtools.inspectedWindow.tabId;\n  const bg = chrome.runtime.connect({ name });\n  const update = action => { localStore.dispatch(action || { type: UPDATE_STATE }); };\n  bg.onMessage.addListener(update);\n  update();\n\n  render(\n    <Provider store={localStore}>\n      <App position={position} />\n    </Provider>,\n    document.getElementById('root')\n  );\n});\n\nif (position !== '#popup') document.body.style.minHeight = '100%';\n"
  },
  {
    "path": "src/browser/extension/window/remote.js",
    "content": "import React from 'react';\nimport { render } from 'react-dom';\nimport App from 'remotedev-app';\n\nchrome.storage.local.get({\n  'select-monitor': 'InspectorMonitor',\n  'test-templates': null,\n  'test-templates-sel': null,\n  's:hostname': null,\n  's:port': null,\n  's:secure': null\n}, options => {\n  render(\n    <App\n      selectMonitor={options['select-monitor']}\n      testTemplates={options['test-templates']}\n      selectedTemplate={options['test-templates-sel']}\n      testTemplates={options['test-templates']}\n      useCodemirror\n      socketOptions={\n        options['s:hostname'] && options['s:port'] ?\n          {\n            hostname: options['s:hostname'], port: options['s:port'], secure: options['s:secure']\n          } : undefined\n      }\n    />,\n    document.getElementById('root')\n  );\n});\n"
  },
  {
    "path": "src/browser/firefox/manifest.json",
    "content": "{\n  \"version\": \"2.17.1\",\n  \"name\": \"Redux DevTools\",\n  \"manifest_version\": 2,\n  \"description\": \"Redux Developer Tools for debugging application state changes.\",\n  \"homepage_url\": \"https://github.com/zalmoxisus/redux-devtools-extension\",\n  \"applications\": {\n    \"gecko\": {\n      \"id\": \"extension@redux.devtools\",\n      \"strict_min_version\": \"54.0\"\n    }\n  },\n  \"page_action\": {\n    \"default_icon\": \"img/logo/38x38.png\",\n    \"default_title\": \"Redux DevTools\",\n    \"default_popup\": \"window.html#popup\"\n  },\n  \"commands\": {\n    \"devtools-left\": {\n      \"description\": \"DevTools window to left\"\n    },\n    \"devtools-right\": {\n      \"description\": \"DevTools window to right\"\n    },\n    \"devtools-bottom\": {\n      \"description\": \"DevTools window to bottom\"\n    },\n    \"devtools-remote\": {\n      \"description\": \"Remote DevTools\"\n    }\n  },\n  \"icons\": {\n    \"16\": \"img/logo/16x16.png\",\n    \"48\": \"img/logo/48x48.png\",\n    \"128\": \"img/logo/128x128.png\"\n  },\n  \"options_ui\": {\n    \"page\": \"options.html\"\n  },\n  \"background\": {\n    \"scripts\": [\"js/background.bundle.js\"]\n  },\n  \"content_scripts\": [\n    {\n      \"matches\": [\"<all_urls>\"],\n      \"js\": [\"js/content.bundle.js\", \"js/pagewrap.bundle.js\"],\n      \"run_at\": \"document_start\",\n      \"all_frames\": true\n    }\n  ],\n  \"devtools_page\": \"devtools.html\",\n  \"web_accessible_resources\": [\n    \"js/page.bundle.js\",\n    \"js/inject.bundle.js\",\n    \"js/redux-devtools-extension.js\"\n  ],\n  \"permissions\": [\"notifications\", \"contextMenus\", \"tabs\", \"storage\", \"<all_urls>\"],\n  \"content_security_policy\": \"script-src 'self'; object-src 'self'; img-src 'self' data:;\"\n}\n"
  },
  {
    "path": "src/browser/views/devpanel.pug",
    "content": "doctype html\n\nhtml\n  head\n    meta(charset='UTF-8')\n  title Redux DevTools\n  include ./includes/style.pug\n  style.\n    body {\n      min-height: 100px;\n    }\n\n  body\n    #root\n    script(src='/js/devpanel.bundle.js')\n"
  },
  {
    "path": "src/browser/views/devtools.pug",
    "content": "doctype html\n\nhtml\n    head\n        meta(charset='UTF-8')\n    title Redux DevTools\n\n    body\n        #root\n        script(src='/js/devtools.bundle.js')"
  },
  {
    "path": "src/browser/views/includes/style.pug",
    "content": "style.\n  html {\n    height: 100%;\n    width: 100%;\n  }\n  body {\n    overflow: hidden;\n    height: 100%;\n    width: 100%;\n    min-width: 350px;\n    min-height: 400px;\n    margin: 0;\n    padding: 0;\n    font-family: \"Helvetica Neue\", \"Lucida Grande\", sans-serif;\n    font-size: 11px;\n    background-color: rgb(53, 59, 70);\n    color: #fff;\n  }\n  a {\n    color: #fff;\n  }\n  #root {\n    height: 100%;\n  }\n  #root > div {\n    height: 100%;\n  }\n  #root > div > div:nth-child(2) {\n    min-height: 0;\n  }\n  .ReactCodeMirror {\n    overflow: auto;\n    height: 100%;\n  }\n  button:disabled {\n    opacity: 0.5;\n    cursor: initial !important;\n  }\n\n  @media print {\n    @page {\n      size: auto;\n      margin: 0;\n    }\n\n    body {\n      position: static;\n    }\n\n    #root > div > div:not(:nth-child(2)) {\n      display: none !important;\n    }\n\n    #root > div > div:nth-child(2) {\n      overflow: visible !important;\n      position: absolute !important;\n      z-index: 2147483647;\n      page-break-after: avoid;\n    }\n\n    #root > div > div:nth-child(2) * {\n      overflow: visible !important;\n    }\n  }\n"
  },
  {
    "path": "src/browser/views/options.pug",
    "content": "doctype html\n\nhtml\n  head\n    meta(charset='UTF-8')\n  title Redux DevTools Options\n  style.\n    body {\n      padding: 2px;\n      min-width: 380px;\n    }\n\n    .option-group {\n      /* Reset the default fieldset styles */\n      margin: initial;\n      border: initial;\n      padding: initial;\n    }\n\n    .option-group + .option-group {\n      margin-top: 30px;\n    }\n\n    .option-group__title {\n      /* Reset the default legend styles */\n      margin: initial;\n      padding: initial;\n\n      margin-bottom: 8px;\n      font-weight: bold;\n      font-size: 30px;\n    }\n\n    .option + .option {\n      margin-top: 5px;\n    }\n\n    .option__textarea {\n      margin-top: 2px;\n      width: 300px;\n      min-height: 50px;\n    }\n\n    .option__hint {\n      margin-top: 2px;\n      font-size: 10px;\n    }\n\n    .option__textarea + .option__hint {\n      margin-top: -2px;\n    }\n\n    /* Checkbox and radio styling */\n    .option_type_checkbox .option__element,\n    .option_type_radio .option__element {\n      vertical-align: bottom;\n    }\n\n    .option_type_checkbox .option__label,\n    .option_type_radio .option__label {\n      margin-left: 4px;\n    }\n\n    .option_type_checkbox .option__textarea,\n    .option_type_checkbox .option__hint,\n    .option_type_radio .option__textarea,\n    .option_type_radio .option__hint {\n      margin-left: 20px;\n    }\n\n\n    /* Checkbox styling */\n    .option_type_checkbox .option__element {\n      /* Checkboxes in Chrome are 2px narrower than radio buttons.\n         These margins align them. */\n      margin-left: 1px;\n      /* ...margin-right is 2px instead of 1px\n         because both radios and checkboxes have initial margin-right of 1px */\n      margin-right: 2px;\n    }\n\n    /* Value-based styling */\n    .option_value_max-age {\n      margin-left: 20px;\n    }\n\n    .option_value_max-age .option__element {\n      width: 50px;\n    }\n\n  body\n    #root\n    script(src='/js/options.bundle.js')\n"
  },
  {
    "path": "src/browser/views/remote.pug",
    "content": "doctype html\n\nhtml\n  head\n    meta(charset='UTF-8')\n  title RemoteDev\n  include ./includes/style.pug\n\n  body\n    #root\n    script(src='/js/remote.bundle.js')"
  },
  {
    "path": "src/browser/views/window.pug",
    "content": "doctype html\n\nhtml\n  head\n    meta(charset='UTF-8')\n  title Redux DevTools\n  include ./includes/style.pug\n\n  body\n    #root\n      div(style='position: relative')\n        img(\n          src='/img/loading.svg',\n          height=300, width=350,\n          style='position: absolute; top: 50%; left: 50%; margin-top: -175px; margin-left: -175px;'\n        )\n    script(src='/js/window.bundle.js')\n"
  },
  {
    "path": "test/.eslintrc",
    "content": "{\n  \"env\": {\n    \"mocha\": true\n  },\n  \"globals\": {\n    \"UI\": true\n  },\n  \"rules\": {\n    \"no-unused-expressions\": 0\n  }\n}\n"
  },
  {
    "path": "test/app/containers/App.spec.js",
    "content": "import expect from 'expect';\nimport React from 'react';\nimport { mount } from 'enzyme';\nimport { Provider } from 'react-redux';\nimport configureStore from '../../../src/app/stores/windowStore';\nimport App from '../../../src/app/containers/App.js';\n\nconst store = configureStore(store);\nconst component = mount(<Provider store={store}><App position=\"devtools-left\" /></Provider>);\n\ndescribe('App container', () => {\n  it('should render inspector monitor\\'s component', () => {\n    expect(component.find('DevtoolsInspector').html()).toExist();\n  });\n\n  it('should contain an empty action list', () => {\n    expect(\n      component.find('ActionList').html()\n    ).toMatch(/<div class=\"actionListRows-[0-9]+\"><\\/div>/);\n  });\n});\n"
  },
  {
    "path": "test/app/inject/api.spec.js",
    "content": "import expect from 'expect';\nimport { insertScript, listenMessage } from '../../utils/inject';\nimport '../../../src/browser/extension/inject/pageScript';\n\ndescribe('API', () => {\n  it('should get window.__REDUX_DEVTOOLS_EXTENSION__ function', () => {\n    expect(window.__REDUX_DEVTOOLS_EXTENSION__).toBeA('function');\n  });\n\n  it('should notify error', () => {\n    const spy = expect.createSpy(() => {});\n    window.__REDUX_DEVTOOLS_EXTENSION__.notifyErrors(spy);\n    insertScript('hi()');\n    expect(spy).toHaveBeenCalled();\n  });\n\n  it('should open monitor', async () => {\n    let message = await listenMessage(() => {\n      window.__REDUX_DEVTOOLS_EXTENSION__.open();\n    });\n    expect(message).toEqual({ source: '@devtools-page', type: 'OPEN', position: 'right' });\n\n    message = await listenMessage(() => {\n      window.__REDUX_DEVTOOLS_EXTENSION__.open('left');\n    });\n    expect(message).toEqual({ source: '@devtools-page', type: 'OPEN', position: 'left' });\n  });\n\n  it('should send message', async () => {\n    let message = await listenMessage(() => {\n      window.__REDUX_DEVTOOLS_EXTENSION__.send('hi');\n    });\n    expect(message).toInclude({\n      type: 'ACTION',\n      payload: undefined,\n      instanceId: 1,\n      name: undefined,\n      source: '@devtools-page'\n    });\n    expect(message.action).toMatch(/{\"action\":{\"type\":\"hi\"},\"timestamp\":\\d+}/);\n\n    message = await listenMessage(() => {\n      window.__REDUX_DEVTOOLS_EXTENSION__.send({ type: 'hi' }, { counter: 1 }, 1);\n    });\n    expect(message).toInclude({\n      type: 'ACTION',\n      payload: '{\"counter\":1}',\n      instanceId: 1,\n      name: undefined,\n      source: '@devtools-page'\n    });\n    expect(message.action).toMatch(/{\"action\":{\"type\":\"hi\"},\"timestamp\":\\d+}/);\n\n    message = await listenMessage(() => {\n      window.__REDUX_DEVTOOLS_EXTENSION__.send({ type: 'hi' }, { counter: 1 }, 1);\n    });\n    expect(message).toInclude({\n      type: 'ACTION',\n      payload: '{\"counter\":1}',\n      instanceId: 1,\n      name: undefined,\n      source: '@devtools-page'\n    });\n    expect(message.action).toMatch(/{\"action\":{\"type\":\"hi\"},\"timestamp\":\\d+}/);\n\n    message = await listenMessage(() => {\n      window.__REDUX_DEVTOOLS_EXTENSION__.send(undefined, { counter: 1 }, 1);\n    });\n    expect(message).toEqual({\n      action: undefined,\n      type: 'STATE',\n      payload: { counter: 1 },\n      actionsById: undefined,\n      computedStates: undefined,\n      committedState: false,\n      instanceId: 1,\n      maxAge: undefined,\n      name: undefined,\n      source: '@devtools-page'\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/inject/enhancer.spec.js",
    "content": "import 'babel-polyfill';\nimport expect from 'expect';\nimport { createStore, compose } from 'redux';\nimport { insertScript, listenMessage } from '../../utils/inject';\nimport '../../../src/browser/extension/inject/pageScript';\n\nfunction counter(state = 0, action) {\n  switch (action.type) {\n    case 'INCREMENT': return state + 1;\n    case 'DECREMENT': return state - 1;\n    default: return state;\n  }\n}\n\ndescribe('Redux enhancer', () => {\n  it('should create the store', async () => {\n    const message = await listenMessage(() => {\n      window.store = createStore(counter, window.__REDUX_DEVTOOLS_EXTENSION__());\n      expect(window.store).toBeA('object');\n    });\n    expect(message.type).toBe('INIT_INSTANCE');\n    expect(window.store.getState()).toBe(0);\n    insertScript('window.devToolsOptions = { serialize: false }');\n  });\n\n  it('should start monitoring', async () => {\n    let message = await listenMessage(() => {\n      window.postMessage({ type: 'START', source: '@devtools-extension' }, '*');\n    });\n    expect(message.type).toBe('START');\n\n    message = await listenMessage();\n    expect(message.type).toBe('STATE');\n    expect(message.actionsById).toInclude('{\"0\":{\"type\":\"PERFORM_ACTION\",\"action\":{\"type\":\"@@INIT\"},\"');\n    expect(message.computedStates).toBe('[{\"state\":0}]');\n  });\n\n  it('should perform actions', async () => {\n    let message = await listenMessage(() => {\n      window.store.dispatch({ type: 'INCREMENT' });\n      expect(window.store.getState()).toBe(1);\n    });\n    expect(message.type).toBe('ACTION');\n    expect(message.action).toInclude('{\"type\":\"PERFORM_ACTION\",\"action\":{\"type\":\"INCREMENT\"},');\n    expect(message.payload).toBe('1');\n\n    message = await listenMessage(() => {\n      window.store.dispatch({ type: 'INCREMENT' });\n      expect(window.store.getState()).toBe(2);\n    });\n    expect(message.type).toBe('ACTION');\n    expect(message.action).toInclude('{\"type\":\"PERFORM_ACTION\",\"action\":{\"type\":\"INCREMENT\"},');\n    expect(message.payload).toBe('2');\n  });\n\n  it('should dispatch actions remotely', async () => {\n    let message = await listenMessage(() => {\n      window.postMessage({\n        type: 'ACTION',\n        payload: '{ type: \\'INCREMENT\\' }',\n        source: '@devtools-extension'\n      }, '*');\n    });\n    expect(message.type).toBe('ACTION');\n\n    message = await listenMessage();\n    expect(message.type).toBe('ACTION');\n    expect(message.action).toInclude('{\"type\":\"PERFORM_ACTION\",\"action\":{\"type\":\"INCREMENT\"},');\n    expect(message.payload).toBe('3');\n  });\n\n  it('should cancel (toggle) action', async () => {\n    let message = await listenMessage(() => {\n      window.postMessage({\n        type: 'DISPATCH',\n        payload: {type: 'TOGGLE_ACTION', id: 1},\n        source: '@devtools-extension'\n      }, '*');\n    });\n    expect(message.type).toBe('DISPATCH');\n\n    message = await listenMessage();\n    expect(message.type).toBe('STATE');\n    expect(window.store.getState()).toBe(2);\n\n    message = await listenMessage(() => {\n      window.postMessage({\n        type: 'DISPATCH',\n        payload: {type: 'TOGGLE_ACTION', id: 1},\n        source: '@devtools-extension'\n      }, '*');\n    });\n    expect(message.type).toBe('DISPATCH');\n\n    message = await listenMessage();\n    expect(message.type).toBe('STATE');\n    expect(window.store.getState()).toBe(3);\n  });\n\n  it('should move back and forward (time travel)', async () => {\n    let message = await listenMessage(() => {\n      window.postMessage({\n        type: 'DISPATCH',\n        payload: {type: 'JUMP_TO_STATE', index: 2, actionId: 2},\n        source: '@devtools-extension'\n      }, '*');\n    });\n    expect(message.type).toBe('DISPATCH');\n    expect(window.store.getState()).toBe(2);\n\n    message = await listenMessage(() => {\n      window.postMessage({\n        type: 'DISPATCH',\n        payload: {type: 'JUMP_TO_STATE', index: 3, actionId: 3},\n        source: '@devtools-extension'\n      }, '*');\n    });\n    expect(message.type).toBe('DISPATCH');\n    expect(window.store.getState()).toBe(3);\n  });\n\n  it('should import state history', async () => {\n    let message = await listenMessage(() => {\n      window.postMessage({\n        type: 'IMPORT',\n        state: JSON.stringify({\n          monitorState: {},\n          actionsById: {\n            '0': { type: 'PERFORM_ACTION', action: { type: '@@INIT' } },\n            '1': { type: 'PERFORM_ACTION', action: { type: 'INCREMENT' } },\n            '2': { type: 'PERFORM_ACTION', action: { type: 'INCREMENT' } }\n          },\n          nextActionId: 3,\n          stagedActionIds: [ 0, 1, 2 ],\n          skippedActionIds: [],\n          currentStateIndex: 2,\n          computedStates: [ { state: 0 }, { state: 1 }, { state: 2 } ]\n        }),\n        source: '@devtools-extension'\n      }, '*');\n    });\n    expect(message.type).toBe('IMPORT');\n    message = await listenMessage();\n    expect(message.type).toBe('STATE');\n    expect(window.store.getState()).toBe(2);\n  });\n\n  it('should create the store with config parameters', async () => {\n    const message = await listenMessage(() => {\n      window.store = createStore(counter, window.__REDUX_DEVTOOLS_EXTENSION__({\n        actionsBlacklist: ['SOME_ACTION'],\n        statesFilter: state => state,\n        serializeState: (key, value) => value\n      }));\n      expect(window.store).toBeA('object');\n    });\n    expect(message.type).toBe('INIT_INSTANCE');\n  });\n\n  it('should create the store using old Redux api', async () => {\n    const message = await listenMessage(() => {\n      window.store = window.__REDUX_DEVTOOLS_EXTENSION__()(createStore)(counter);\n      expect(window.store).toBeA('object');\n    });\n    expect(message.type).toBe('INIT_INSTANCE');\n  });\n\n  it('should create the store with several enhancers', async () => {\n    const testEnhancer = next =>\n      (reducer, initialState, enhancer) => next(reducer, initialState, enhancer);\n    const message = await listenMessage(() => {\n      window.store = createStore(counter, compose(\n        testEnhancer,\n        window.__REDUX_DEVTOOLS_EXTENSION__())\n      );\n      expect(window.store).toBeA('object');\n    });\n    expect(message.type).toBe('INIT_INSTANCE');\n  });\n});\n"
  },
  {
    "path": "test/app/setup.js",
    "content": "require('babel-register')();\nrequire('babel-polyfill');\nglobal.chrome = require('sinon-chrome');\nvar jsdom = require('jsdom').jsdom;\n\nvar exposedProperties = ['window', 'navigator', 'document'];\n\nglobal.document = jsdom('');\nglobal.window = document.defaultView;\nObject.keys(document.defaultView).forEach((property) => {\n  if (typeof global[property] === 'undefined') {\n    exposedProperties.push(property);\n    global[property] = document.defaultView[property];\n  }\n});\n\nglobal.navigator = {\n  userAgent: 'gecko'\n};\n\nglobal.document.createRange = function() {\n  return {\n    setEnd: function(){},\n    setStart: function(){},\n    getBoundingClientRect: function(){\n      return {right: 0};\n    }\n  }\n};\n\ndocumentRef = document;\n"
  },
  {
    "path": "test/chrome/extension.spec.js",
    "content": "import { resolve } from 'path';\nimport webdriver from 'selenium-webdriver';\nimport expect from 'expect';\nimport { switchMonitorTests, delay } from '../utils/e2e';\n\nconst port = 9515;\nconst path = resolve('build/extension');\nconst extensionId = 'lmhkpmbekcpmknklioeibfkpmmfibljd';\nconst actionsPattern = /^@@INIT(.|\\n)+@@reduxReactRouter\\/routerDidChange(.|\\n)+@@reduxReactRouter\\/initRoutes(.|\\n)+$/;\n\ndescribe('Chrome extension', function() {\n  this.timeout(20000);\n\n  before(async () => {\n    await delay(2000);\n    this.driver = new webdriver.Builder()\n      .usingServer(`http://localhost:${port}`)\n      .withCapabilities({\n        chromeOptions: {\n          args: [`load-extension=${path}`]\n        }\n      })\n      .forBrowser('chrome')\n      .build();\n  });\n  after(async () => {\n    await this.driver.quit();\n  });\n\n  it('should open extension\\'s window', async () => {\n    await this.driver.get(`chrome-extension://${extensionId}/window.html#left`);\n    const url = await this.driver.getCurrentUrl();\n    expect(url).toBe(`chrome-extension://${extensionId}/window.html#left`);\n  });\n\n  it('should match document title', async () => {\n    const title = await this.driver.getTitle();\n    expect(title).toBe('Redux DevTools');\n  });\n\n  it('should contain inspector monitor\\'s component', async () => {\n    const val = this.driver.findElement(webdriver.By.xpath('//div[contains(@class, \"inspector-\")]'))\n      .getText();\n    expect(val).toExist();\n  });\n\n  it('should contain an empty actions list', async () => {\n    const val = await this.driver.findElement(webdriver.By.xpath('//div[contains(@class, \"actionListRows-\")]'))\n      .getText();\n    expect(val).toBe('');\n  });\n\n  Object.keys(switchMonitorTests).forEach(description =>\n    it(description, switchMonitorTests[description].bind(this))\n  );\n\n  it('should get actions list', async () => {\n    const url = 'http://zalmoxisus.github.io/examples/router/';\n    await this.driver.executeScript(`window.open('${url}')`);\n    await delay(2000);\n\n    const tabs = await this.driver.getAllWindowHandles();\n\n    await this.driver.switchTo().window(tabs[1]);\n    expect(await this.driver.getCurrentUrl()).toMatch(url);\n    await this.driver.manage().timeouts().pageLoadTimeout(5000);\n\n    await this.driver.switchTo().window(tabs[0]);\n\n    const result = await this.driver.wait(this.driver\n      .findElement(webdriver.By.xpath('//div[contains(@class, \"actionListRows-\")]'))\n      .getText().then((val) => {\n        return actionsPattern.test(val);\n      }), 15000, 'it doesn\\'t match actions pattern');\n    expect(result).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "test/electron/devpanel.spec.js",
    "content": "import { join } from 'path';\nimport webdriver from 'selenium-webdriver';\nimport electronPath from 'electron';\nimport expect from 'expect';\nimport { switchMonitorTests, delay } from '../utils/e2e';\n\nconst port = 9515;\nconst devPanelPath = 'chrome-extension://redux-devtools/devpanel.html';\n\ndescribe('DevTools panel for Electron', function() {\n  this.timeout(10000);\n\n  before(async () => {\n    await delay(1000);\n    this.driver = new webdriver.Builder()\n      .usingServer(`http://localhost:${port}`)\n      .withCapabilities({\n        chromeOptions: {\n          binary: electronPath,\n          args: [`app=${join(__dirname, 'fixture')}`]\n        }\n      })\n      .forBrowser('electron')\n      .build();\n    await this.driver.manage().timeouts().setScriptTimeout(10000);\n  });\n\n  after(async () => {\n    await this.driver.quit();\n  });\n\n  it('should open Redux DevTools tab', async () => {\n    expect(await this.driver.getCurrentUrl())\n      .toMatch(/chrome-devtools:\\/\\/devtools\\/bundled\\/inspector.html/);\n\n    await this.driver.manage().timeouts().pageLoadTimeout(5000);\n\n    const id = await this.driver.executeAsyncScript(function(callback) {\n      let attempts = 5;\n      function showReduxPanel() {\n        if (attempts === 0) {\n          return callback('Redux panel not found');\n        }\n        const tabs = UI.inspectorView._tabbedPane._tabs;\n        const idList = tabs.map(tab => tab.id);\n        const reduxPanelId = 'chrome-extension://redux-devtoolsRedux';\n        if (idList.indexOf(reduxPanelId) !== -1) {\n          UI.inspectorView.showPanel(reduxPanelId);\n          return callback(reduxPanelId);\n        }\n        attempts--;\n        setTimeout(showReduxPanel, 500);\n      }\n      showReduxPanel();\n    });\n    expect(id).toBe('chrome-extension://redux-devtoolsRedux');\n\n    const className = await this.driver.findElement(webdriver.By.className(id))\n      .getAttribute('class');\n    expect(className).toNotMatch(/hidden/); // not hidden\n  });\n\n  it('should have Redux DevTools UI on current tab', async () => {\n    await this.driver.switchTo().frame(\n      this.driver.findElement(webdriver.By.xpath(`//iframe[@src='${devPanelPath}']`))\n    );\n    await delay(1000);\n  });\n\n  it('should contain INIT action', async () => {\n    const element = await this.driver.wait(\n      webdriver.until.elementLocated(webdriver.By.xpath('//div[contains(@class, \"actionListRows-\")]')),\n      5000,\n      'Element not found'\n    );\n    const val = await element.getText();\n    expect(val).toMatch(/@@INIT/);\n  });\n\n  it('should contain Inspector monitor\\'s component', async () => {\n    const val = await this.driver.findElement(webdriver.By.xpath('//div[contains(@class, \"inspector-\")]'))\n      .getText();\n    expect(val).toExist();\n  });\n\n  Object.keys(switchMonitorTests).forEach(description =>\n    it(description, switchMonitorTests[description].bind(this))\n  );\n\n/*  it('should be no logs in console of main window', async () => {\n    const handles = await this.driver.getAllWindowHandles();\n    await this.driver.switchTo().window(handles[1]); // Change to main window\n\n    expect(await this.driver.getTitle()).toBe('Electron Test');\n\n    const logs = await this.driver.manage().logs().get(webdriver.logging.Type.BROWSER);\n    expect(logs).toEqual([]);\n  });\n*/\n});\n"
  },
  {
    "path": "test/electron/fixture/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Electron Test</title>\n  </head>\n  <body>\n    <span id=\"counter\">0</span>\n    <button id=\"increment\">+</button>\n    <button id=\"decrement\">-</button>\n    <script src=\"renderer.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/electron/fixture/main.js",
    "content": "const path = require('path');\nconst { app, BrowserWindow } = require('electron');\n\napp.setPath('userData', path.join(__dirname, '../tmp'));\n\napp.on('window-all-closed', app.quit);\napp.on('ready', () => {\n  BrowserWindow.addDevToolsExtension(\n    path.join(__dirname, '../../../build/extension')\n  );\n\n  const mainWindow = new BrowserWindow({\n    width: 150,\n    height: 100\n  });\n  mainWindow.loadURL(`file://${__dirname}/index.html`);\n  mainWindow.openDevTools({ detach: true });\n});\n"
  },
  {
    "path": "test/electron/fixture/package.json",
    "content": "{\n  \"name\": \"electron-test\",\n  \"productName\": \"Electron Test\",\n  \"main\": \"main.js\",\n  \"version\": \"0.1.0\"\n}\n"
  },
  {
    "path": "test/electron/fixture/renderer.js",
    "content": "const { createStore } = require('redux');\n\nconst INCREMENT_COUNTER = 'INCREMENT_COUNTER';\nconst DECREMENT_COUNTER = 'DECREMENT_COUNTER';\n\nconst initialState = { value: 0 };\n\nconst store = createStore(\n  (state, action) => {\n    switch (action.type) {\n      case INCREMENT_COUNTER:\n        return { value: state.value + 1 };\n      case DECREMENT_COUNTER:\n        return { value: state.value - 1 };\n      default:\n        return state;\n    }\n  },\n  initialState,\n  window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : noop => noop\n);\n\nconst el = document.getElementById('counter');\n\nstore.subscribe(() => {\n  el.innerHTML = store.getState().value;\n});\n\nconst increment = document.getElementById('increment');\nconst decrement = document.getElementById('decrement');\nincrement.onclick = () => store.dispatch({ type: INCREMENT_COUNTER });\ndecrement.onclick = () => store.dispatch({ type: DECREMENT_COUNTER });\n"
  },
  {
    "path": "test/perf/data.js",
    "content": "// Source: http://beta.json-generator.com/V1omRaUJG\n/* eslint-disable */\n\nexport const bigString = Array(10000000).join('t');\n\nexport const bigArray = [\n  {\n    \"_id\": \"580ddf0b168ab8fe470be6e0\",\n    \"index\": 0,\n    \"guid\": \"2ce09bef-e6a5-4894-b720-74c9eb72098d\",\n    \"isActive\": true,\n    \"balance\": \"$3,164.44\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 30,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Kenya\",\n      \"last\": \"Hoover\"\n    },\n    \"company\": \"QUALITERN\",\n    \"email\": \"kenya.hoover@qualitern.biz\",\n    \"phone\": \"+1 (829) 556-2040\",\n    \"address\": \"605 Glenwood Road, Cherokee, Oklahoma, 8113\",\n    \"about\": \"Fugiat dolore ut esse ullamco incididunt culpa qui pariatur mollit nostrud incididunt. Quis pariatur sunt ut ipsum qui consequat sit dolore consequat esse elit consectetur do. Cillum labore cupidatat ipsum laboris. Non nisi minim adipisicing et culpa consectetur mollit incididunt ullamco. Non velit ea irure aute aliqua et incididunt officia laborum.\",\n    \"registered\": \"Monday, June 22, 2015 9:38 AM\",\n    \"latitude\": \"84.835869\",\n    \"longitude\": \"64.139911\",\n    \"tags\": [\n      \"eiusmod\",\n      \"aute\",\n      \"ipsum\",\n      \"ad\",\n      \"mollit\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Alba Bartlett\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Andrews Mcleod\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Elise Velazquez\"\n      }\n    ],\n    \"greeting\": \"Hello, Kenya! You have 8 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0bc26524220b80fa59\",\n    \"index\": 1,\n    \"guid\": \"80713a26-75b0-4a4e-914d-52f31fade72c\",\n    \"isActive\": true,\n    \"balance\": \"$1,455.30\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 28,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Jami\",\n      \"last\": \"Mcfarland\"\n    },\n    \"company\": \"LIMAGE\",\n    \"email\": \"jami.mcfarland@limage.co.uk\",\n    \"phone\": \"+1 (944) 566-3918\",\n    \"address\": \"804 Montgomery Place, Henrietta, Alaska, 9608\",\n    \"about\": \"Velit esse ut dolor Lorem laboris esse aute est pariatur tempor esse aute ut ut. In magna esse aute ea cillum qui mollit consectetur minim qui pariatur. Sit consequat fugiat ut veniam non ut deserunt culpa velit ex id laborum tempor. Duis est amet sunt id sunt. Ullamco minim officia irure quis veniam ipsum. Minim proident commodo ea ea dolore. Velit sunt magna commodo quis est dolor eiusmod aliqua occaecat tempor ut.\",\n    \"registered\": \"Saturday, October 24, 2015 9:38 AM\",\n    \"latitude\": \"-39.25774\",\n    \"longitude\": \"-118.315297\",\n    \"tags\": [\n      \"voluptate\",\n      \"dolor\",\n      \"enim\",\n      \"reprehenderit\",\n      \"et\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Goff Compton\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Sherry Randall\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Janell Mcconnell\"\n      }\n    ],\n    \"greeting\": \"Hello, Jami! You have 9 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0b8b30c17733f75cf3\",\n    \"index\": 2,\n    \"guid\": \"dca38381-500a-4f4e-823a-d95484f9e87d\",\n    \"isActive\": false,\n    \"balance\": \"$1,451.88\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 27,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Jodie\",\n      \"last\": \"Holder\"\n    },\n    \"company\": \"AVIT\",\n    \"email\": \"jodie.holder@avit.us\",\n    \"phone\": \"+1 (948) 512-2550\",\n    \"address\": \"371 Stockholm Street, Wattsville, Federated States Of Micronesia, 4778\",\n    \"about\": \"Voluptate nisi commodo duis sunt. Magna consectetur minim consectetur esse excepteur adipisicing laboris. Culpa sunt culpa sit exercitation. Duis ea culpa aliqua do exercitation adipisicing ullamco ut officia occaecat incididunt dolore nisi. Tempor est proident cillum nulla do exercitation dolor incididunt. Aute magna aliquip elit irure dolor anim voluptate.\",\n    \"registered\": \"Sunday, April 5, 2015 12:27 PM\",\n    \"latitude\": \"68.671844\",\n    \"longitude\": \"100.317594\",\n    \"tags\": [\n      \"Lorem\",\n      \"qui\",\n      \"do\",\n      \"excepteur\",\n      \"duis\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Mason Carroll\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Bonnie Ross\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Shields Mays\"\n      }\n    ],\n    \"greeting\": \"Hello, Jodie! You have 5 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0bd7a19dd3361592b6\",\n    \"index\": 3,\n    \"guid\": \"0c45af75-ee65-427a-a462-e01d5a3d882f\",\n    \"isActive\": true,\n    \"balance\": \"$1,883.45\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 25,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Britney\",\n      \"last\": \"Ryan\"\n    },\n    \"company\": \"BUNGA\",\n    \"email\": \"britney.ryan@bunga.name\",\n    \"phone\": \"+1 (996) 502-2698\",\n    \"address\": \"520 Monument Walk, Shindler, Iowa, 1721\",\n    \"about\": \"Cupidatat aute est consectetur eiusmod ut veniam qui sit adipisicing Lorem nostrud proident deserunt exercitation. Laborum ullamco minim nisi nulla ullamco fugiat laboris nisi eiusmod ullamco enim aliqua deserunt. Occaecat anim aliquip est sunt voluptate qui dolore. Minim mollit et esse quis tempor eu anim deserunt nostrud. Voluptate amet voluptate eu anim culpa quis. Non do aliqua sit cupidatat eiusmod commodo in sit esse consequat. Cupidatat elit do exercitation ex proident ex nulla ex ad ut.\",\n    \"registered\": \"Tuesday, January 19, 2016 12:05 PM\",\n    \"latitude\": \"-12.692828\",\n    \"longitude\": \"172.541591\",\n    \"tags\": [\n      \"fugiat\",\n      \"fugiat\",\n      \"anim\",\n      \"magna\",\n      \"in\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Jeanette Paul\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Jones Sherman\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Pruitt Gross\"\n      }\n    ],\n    \"greeting\": \"Hello, Britney! You have 8 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0bdb18815e253438a8\",\n    \"index\": 4,\n    \"guid\": \"35df48d8-e840-43fc-b99c-f078a0c028d5\",\n    \"isActive\": false,\n    \"balance\": \"$2,753.26\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 20,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"West\",\n      \"last\": \"Sharp\"\n    },\n    \"company\": \"GROK\",\n    \"email\": \"west.sharp@grok.info\",\n    \"phone\": \"+1 (947) 561-3088\",\n    \"address\": \"217 Clay Street, Fingerville, Puerto Rico, 382\",\n    \"about\": \"Tempor ut ex aliqua ipsum. Ipsum fugiat dolor exercitation mollit eiusmod duis nulla occaecat excepteur ea irure minim minim sit. Proident esse id deserunt tempor dolor sit consectetur proident deserunt fugiat excepteur laborum. Incididunt fugiat id fugiat id Lorem est sit aliqua sit officia excepteur nulla mollit. Aliquip nulla nisi ea qui ex non nostrud culpa et anim. Velit labore esse id exercitation ex duis aliquip est ea eu. Sint Lorem mollit deserunt adipisicing do amet laborum velit nostrud commodo enim fugiat anim pariatur.\",\n    \"registered\": \"Tuesday, May 10, 2016 2:56 PM\",\n    \"latitude\": \"6.032735\",\n    \"longitude\": \"-168.931741\",\n    \"tags\": [\n      \"non\",\n      \"aliquip\",\n      \"ullamco\",\n      \"ex\",\n      \"veniam\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Head Riley\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Meyer Oneal\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Molina Tyson\"\n      }\n    ],\n    \"greeting\": \"Hello, West! You have 6 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0ba2234b9e4a2b47b2\",\n    \"index\": 5,\n    \"guid\": \"16a1e635-f30b-49d7-b0f2-69ee74313900\",\n    \"isActive\": true,\n    \"balance\": \"$3,982.09\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 40,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Manuela\",\n      \"last\": \"Henry\"\n    },\n    \"company\": \"MEDCOM\",\n    \"email\": \"manuela.henry@medcom.io\",\n    \"phone\": \"+1 (876) 521-2923\",\n    \"address\": \"112 Clove Road, Cliffside, Kansas, 6979\",\n    \"about\": \"Exercitation ea esse aliquip sint nisi consequat dolor adipisicing do pariatur et id est voluptate. In occaecat dolor exercitation do aliquip cillum in. Deserunt nisi deserunt Lorem aliqua adipisicing. Consequat eiusmod occaecat pariatur mollit aliqua non tempor aliquip sint consequat sit enim. Elit consectetur dolor sint ipsum officia in duis laboris irure aliquip ea labore. Aliquip ullamco fugiat dolore fugiat id. Deserunt id amet eiusmod tempor do tempor ut laborum.\",\n    \"registered\": \"Wednesday, March 4, 2015 4:00 PM\",\n    \"latitude\": \"-53.534313\",\n    \"longitude\": \"101.348892\",\n    \"tags\": [\n      \"sint\",\n      \"voluptate\",\n      \"duis\",\n      \"laboris\",\n      \"nisi\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Beryl Chang\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Pierce Simpson\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Sonja Pacheco\"\n      }\n    ],\n    \"greeting\": \"Hello, Manuela! You have 6 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0b39bcca5393e6b4a5\",\n    \"index\": 6,\n    \"guid\": \"4c69c263-a646-4446-adfe-eeac5481ade3\",\n    \"isActive\": false,\n    \"balance\": \"$1,557.52\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 36,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Marcy\",\n      \"last\": \"Collier\"\n    },\n    \"company\": \"KINETICA\",\n    \"email\": \"marcy.collier@kinetica.com\",\n    \"phone\": \"+1 (812) 599-2621\",\n    \"address\": \"701 Melrose Street, Cloverdale, North Dakota, 8703\",\n    \"about\": \"Cupidatat velit cupidatat officia ad. Nulla cupidatat esse velit velit. Officia amet ea cupidatat sint consequat cupidatat. Eiusmod deserunt laborum qui proident tempor ullamco tempor officia laborum cillum ipsum commodo mollit.\",\n    \"registered\": \"Sunday, December 7, 2014 11:48 PM\",\n    \"latitude\": \"19.152716\",\n    \"longitude\": \"119.251991\",\n    \"tags\": [\n      \"minim\",\n      \"qui\",\n      \"exercitation\",\n      \"tempor\",\n      \"sunt\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Estes Porter\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Sheila Miller\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Bell Rosario\"\n      }\n    ],\n    \"greeting\": \"Hello, Marcy! You have 9 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0b0887381e90657c23\",\n    \"index\": 7,\n    \"guid\": \"80b50d03-70cd-4ce5-b6c7-c3d60b9e29ff\",\n    \"isActive\": false,\n    \"balance\": \"$3,203.72\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 34,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Esperanza\",\n      \"last\": \"Mack\"\n    },\n    \"company\": \"FURNAFIX\",\n    \"email\": \"esperanza.mack@furnafix.tv\",\n    \"phone\": \"+1 (948) 511-2731\",\n    \"address\": \"502 Bliss Terrace, Marenisco, Northern Mariana Islands, 7773\",\n    \"about\": \"Proident deserunt ipsum aute irure laboris nisi non velit officia nisi anim. Magna nisi eiusmod deserunt ad veniam sit ullamco sit fugiat officia dolor incididunt id irure. Laborum id fugiat pariatur dolor proident Lorem do ex occaecat.\",\n    \"registered\": \"Sunday, April 24, 2016 9:33 AM\",\n    \"latitude\": \"79.033601\",\n    \"longitude\": \"138.782377\",\n    \"tags\": [\n      \"velit\",\n      \"sint\",\n      \"est\",\n      \"proident\",\n      \"in\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Roy Clemons\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Baldwin Glenn\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Castaneda Combs\"\n      }\n    ],\n    \"greeting\": \"Hello, Esperanza! You have 6 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0bee6486345816b9f4\",\n    \"index\": 8,\n    \"guid\": \"629ee6bb-bbe3-4f7e-9f6c-b8b53761e7b5\",\n    \"isActive\": false,\n    \"balance\": \"$2,112.11\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 37,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Bender\",\n      \"last\": \"Sheppard\"\n    },\n    \"company\": \"PROXSOFT\",\n    \"email\": \"bender.sheppard@proxsoft.net\",\n    \"phone\": \"+1 (986) 490-3036\",\n    \"address\": \"509 Fuller Place, Dodge, Montana, 1706\",\n    \"about\": \"Ullamco dolore fugiat nulla non amet Lorem elit ullamco ipsum. Mollit anim do eiusmod esse sint esse voluptate exercitation ipsum ut nulla. Dolore pariatur eiusmod amet reprehenderit. Ea officia in ipsum laborum officia id tempor quis nostrud id ex id duis. Eiusmod incididunt voluptate duis sint laborum.\",\n    \"registered\": \"Sunday, June 19, 2016 8:45 AM\",\n    \"latitude\": \"-35.720137\",\n    \"longitude\": \"-49.926356\",\n    \"tags\": [\n      \"enim\",\n      \"cupidatat\",\n      \"cupidatat\",\n      \"tempor\",\n      \"cupidatat\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Owens Branch\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Williams Boone\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Doris Morrison\"\n      }\n    ],\n    \"greeting\": \"Hello, Bender! You have 5 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0b4b41ebbb13abd5d0\",\n    \"index\": 9,\n    \"guid\": \"54401f5b-ae5f-412c-9149-7a361a73e02f\",\n    \"isActive\": true,\n    \"balance\": \"$1,483.87\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 26,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Summers\",\n      \"last\": \"Donaldson\"\n    },\n    \"company\": \"ARTIQ\",\n    \"email\": \"summers.donaldson@artiq.org\",\n    \"phone\": \"+1 (972) 486-2456\",\n    \"address\": \"980 Pierrepont Street, Gerber, Ohio, 9892\",\n    \"about\": \"Proident veniam officia sunt esse culpa labore anim quis do. Sunt ullamco elit amet incididunt fugiat magna id do veniam ullamco et anim. Aute labore pariatur ex ea deserunt nulla Lorem adipisicing non ad. Id ea proident voluptate aliqua velit aliquip enim incididunt. Do ipsum ipsum voluptate voluptate aliquip ex officia velit in officia ex esse aute.\",\n    \"registered\": \"Tuesday, December 30, 2014 1:21 AM\",\n    \"latitude\": \"-83.907394\",\n    \"longitude\": \"10.741994\",\n    \"tags\": [\n      \"laborum\",\n      \"proident\",\n      \"occaecat\",\n      \"elit\",\n      \"occaecat\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Love Lott\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Johnson Clay\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Nelson Blair\"\n      }\n    ],\n    \"greeting\": \"Hello, Summers! You have 8 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0bc2e80831e45b4dc6\",\n    \"index\": 10,\n    \"guid\": \"0e7ba51d-202e-4e28-a6a5-a9b6ec63abd4\",\n    \"isActive\": false,\n    \"balance\": \"$3,079.46\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 38,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Shelby\",\n      \"last\": \"Conway\"\n    },\n    \"company\": \"ZINCA\",\n    \"email\": \"shelby.conway@zinca.me\",\n    \"phone\": \"+1 (985) 510-2634\",\n    \"address\": \"955 Seagate Terrace, Verdi, Oregon, 6649\",\n    \"about\": \"Incididunt aliqua veniam elit ea cupidatat dolore. Ipsum culpa ut consequat velit sint sint ad. Ex fugiat consequat et sit ex sit esse ullamco magna reprehenderit ea id reprehenderit. Adipisicing et esse commodo sit Lorem ipsum ea fugiat officia do et culpa reprehenderit sunt.\",\n    \"registered\": \"Tuesday, March 1, 2016 12:46 PM\",\n    \"latitude\": \"79.379348\",\n    \"longitude\": \"-179.029537\",\n    \"tags\": [\n      \"elit\",\n      \"dolore\",\n      \"in\",\n      \"excepteur\",\n      \"sit\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Dean Salinas\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Josie Calhoun\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Huffman Walton\"\n      }\n    ],\n    \"greeting\": \"Hello, Shelby! You have 10 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0b38c03f61248fbb18\",\n    \"index\": 11,\n    \"guid\": \"004be9ab-5520-4f85-b6e9-806c701faafe\",\n    \"isActive\": false,\n    \"balance\": \"$3,974.78\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 23,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Reynolds\",\n      \"last\": \"Hurley\"\n    },\n    \"company\": \"TELLIFLY\",\n    \"email\": \"reynolds.hurley@tellifly.ca\",\n    \"phone\": \"+1 (906) 482-2018\",\n    \"address\": \"364 Jackson Court, Edmund, Arizona, 5300\",\n    \"about\": \"Aute deserunt labore consectetur non anim culpa mollit. Cillum Lorem excepteur esse non anim. Duis aliqua id laborum ex mollit id tempor nisi non voluptate. Nostrud enim in labore consequat id laboris Lorem veniam veniam amet. Laboris nostrud dolore proident labore incididunt elit quis officia elit est exercitation veniam aute.\",\n    \"registered\": \"Friday, May 27, 2016 1:32 PM\",\n    \"latitude\": \"-5.022457\",\n    \"longitude\": \"96.958037\",\n    \"tags\": [\n      \"sit\",\n      \"laboris\",\n      \"Lorem\",\n      \"dolor\",\n      \"sint\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Heidi Flores\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Armstrong Middleton\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Figueroa Camacho\"\n      }\n    ],\n    \"greeting\": \"Hello, Reynolds! You have 9 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0b1835b3ed2579bfc0\",\n    \"index\": 12,\n    \"guid\": \"88c12f0a-23df-4161-8e3a-c6c3101292f2\",\n    \"isActive\": false,\n    \"balance\": \"$1,419.27\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 38,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Young\",\n      \"last\": \"Santiago\"\n    },\n    \"company\": \"GINKLE\",\n    \"email\": \"young.santiago@ginkle.biz\",\n    \"phone\": \"+1 (922) 566-2702\",\n    \"address\": \"278 Bartlett Place, Tetherow, Vermont, 191\",\n    \"about\": \"Est consectetur esse culpa ullamco. Dolore voluptate aute consequat voluptate. Exercitation fugiat est anim qui exercitation nostrud.\",\n    \"registered\": \"Tuesday, June 28, 2016 7:18 AM\",\n    \"latitude\": \"-43.055507\",\n    \"longitude\": \"45.085776\",\n    \"tags\": [\n      \"sit\",\n      \"Lorem\",\n      \"deserunt\",\n      \"fugiat\",\n      \"cupidatat\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Sheryl Smith\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Felicia Mcintosh\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Hoover Hardy\"\n      }\n    ],\n    \"greeting\": \"Hello, Young! You have 5 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0bf31dbaaeb3c9cfcd\",\n    \"index\": 13,\n    \"guid\": \"3a39f0ef-3659-4e01-b2d7-bb537fdd1a3a\",\n    \"isActive\": false,\n    \"balance\": \"$2,529.26\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 34,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Trina\",\n      \"last\": \"Decker\"\n    },\n    \"company\": \"SULTRAXIN\",\n    \"email\": \"trina.decker@sultraxin.co.uk\",\n    \"phone\": \"+1 (980) 524-2887\",\n    \"address\": \"874 Llama Court, Klondike, California, 293\",\n    \"about\": \"Ad duis Lorem nulla ex. Proident pariatur Lorem pariatur fugiat deserunt duis incididunt esse eiusmod officia. Eiusmod quis pariatur mollit sint exercitation aute. Mollit enim consequat aliqua quis. Fugiat Lorem do duis aute sit dolore.\",\n    \"registered\": \"Friday, February 19, 2016 1:30 PM\",\n    \"latitude\": \"15.917567\",\n    \"longitude\": \"-1.518009\",\n    \"tags\": [\n      \"ex\",\n      \"sit\",\n      \"enim\",\n      \"consequat\",\n      \"sit\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Wyatt Pickett\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Valarie Barr\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Pace Best\"\n      }\n    ],\n    \"greeting\": \"Hello, Trina! You have 6 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0b3934d0edbaeaba18\",\n    \"index\": 14,\n    \"guid\": \"d5f5a79c-de56-4aca-bcc4-1b9df58a2b1d\",\n    \"isActive\": false,\n    \"balance\": \"$3,670.31\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 35,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Tasha\",\n      \"last\": \"Merritt\"\n    },\n    \"company\": \"RODEOCEAN\",\n    \"email\": \"tasha.merritt@rodeocean.us\",\n    \"phone\": \"+1 (940) 421-2122\",\n    \"address\": \"790 Suydam Place, Keyport, Idaho, 2211\",\n    \"about\": \"Ad elit cillum laboris laborum ut minim aute non ullamco ipsum incididunt labore officia velit. Cillum sunt do laboris enim eiusmod ad elit nostrud est deserunt quis. Consectetur ea ex consectetur commodo cillum sunt mollit Lorem ea sunt sit ut magna. Quis labore fugiat aliquip non consectetur est. Ut pariatur duis veniam ut exercitation est est. Ullamco dolore nulla est pariatur.\",\n    \"registered\": \"Wednesday, October 22, 2014 11:57 PM\",\n    \"latitude\": \"12.554934\",\n    \"longitude\": \"-149.800679\",\n    \"tags\": [\n      \"reprehenderit\",\n      \"ex\",\n      \"tempor\",\n      \"sint\",\n      \"exercitation\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Dennis Mills\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Vargas Guerrero\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Hollie Chavez\"\n      }\n    ],\n    \"greeting\": \"Hello, Tasha! You have 6 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0bafee284452c9a02e\",\n    \"index\": 15,\n    \"guid\": \"b35eff37-836a-4928-9a06-701c2bc32881\",\n    \"isActive\": true,\n    \"balance\": \"$2,931.23\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 24,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Maryann\",\n      \"last\": \"Ewing\"\n    },\n    \"company\": \"ASSISTIA\",\n    \"email\": \"maryann.ewing@assistia.name\",\n    \"phone\": \"+1 (911) 512-2851\",\n    \"address\": \"450 Seeley Street, Winfred, Mississippi, 587\",\n    \"about\": \"Aliquip et Lorem aliquip aute adipisicing nisi incididunt deserunt non ipsum irure cillum voluptate. Sunt exercitation irure mollit proident anim ut veniam enim ullamco eiusmod do sint aliqua aliqua. Enim sunt quis exercitation culpa velit. Officia commodo amet enim quis ipsum non Lorem non excepteur. Quis aute elit irure elit sit ullamco cupidatat ullamco enim et cillum id.\",\n    \"registered\": \"Friday, June 17, 2016 2:12 PM\",\n    \"latitude\": \"47.215077\",\n    \"longitude\": \"81.317216\",\n    \"tags\": [\n      \"commodo\",\n      \"id\",\n      \"nulla\",\n      \"pariatur\",\n      \"exercitation\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Sue Finley\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Chandler Mccarty\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Laura Coleman\"\n      }\n    ],\n    \"greeting\": \"Hello, Maryann! You have 8 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0b5b3b1890b6b64d7b\",\n    \"index\": 16,\n    \"guid\": \"ef7682f0-23ff-479d-8a81-54d01e2641ec\",\n    \"isActive\": true,\n    \"balance\": \"$1,841.30\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 30,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Hammond\",\n      \"last\": \"Koch\"\n    },\n    \"company\": \"CYCLONICA\",\n    \"email\": \"hammond.koch@cyclonica.info\",\n    \"phone\": \"+1 (974) 481-2184\",\n    \"address\": \"141 Exeter Street, Kipp, Alabama, 643\",\n    \"about\": \"Ullamco aliqua id veniam ea do. Commodo dolore fugiat ut aliquip. Est ut qui elit amet. Aliquip mollit occaecat consequat id exercitation eu fugiat voluptate culpa labore eiusmod aliqua cillum. Eu amet consequat deserunt enim qui mollit aliqua nisi ad ut veniam elit pariatur.\",\n    \"registered\": \"Sunday, July 13, 2014 10:47 PM\",\n    \"latitude\": \"8.698118\",\n    \"longitude\": \"-68.775186\",\n    \"tags\": [\n      \"id\",\n      \"voluptate\",\n      \"labore\",\n      \"magna\",\n      \"Lorem\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Mildred Brewer\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Buck Potts\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Kline Valencia\"\n      }\n    ],\n    \"greeting\": \"Hello, Hammond! You have 5 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0be6e62a7ef65bf203\",\n    \"index\": 17,\n    \"guid\": \"2c45743d-dfe7-4621-acc2-7664fc7a39f1\",\n    \"isActive\": true,\n    \"balance\": \"$3,281.24\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 23,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Holt\",\n      \"last\": \"Pennington\"\n    },\n    \"company\": \"NORALEX\",\n    \"email\": \"holt.pennington@noralex.io\",\n    \"phone\": \"+1 (948) 557-3137\",\n    \"address\": \"483 Woodruff Avenue, Kapowsin, Illinois, 6162\",\n    \"about\": \"Nisi non consectetur est deserunt nisi aliqua. Mollit labore dolore ex ex id eu proident nostrud deserunt consequat nostrud. Ad irure duis ex enim commodo proident ullamco esse. Ex sit eiusmod occaecat minim nulla ut Lorem Lorem. Quis est in et est cupidatat fugiat dolor minim voluptate.\",\n    \"registered\": \"Saturday, September 20, 2014 3:43 AM\",\n    \"latitude\": \"-1.585287\",\n    \"longitude\": \"-32.054323\",\n    \"tags\": [\n      \"ex\",\n      \"adipisicing\",\n      \"est\",\n      \"aute\",\n      \"amet\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Irma Wynn\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Branch Todd\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Carey Dejesus\"\n      }\n    ],\n    \"greeting\": \"Hello, Holt! You have 7 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0bd38a23008f8a6714\",\n    \"index\": 18,\n    \"guid\": \"ef07b7c0-ee2e-4163-9c25-829cfcd2b142\",\n    \"isActive\": true,\n    \"balance\": \"$2,267.65\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 27,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Farley\",\n      \"last\": \"Jensen\"\n    },\n    \"company\": \"CODACT\",\n    \"email\": \"farley.jensen@codact.com\",\n    \"phone\": \"+1 (861) 581-3498\",\n    \"address\": \"535 Richmond Street, Boomer, Rhode Island, 4214\",\n    \"about\": \"Et dolore do qui velit aliquip sit laboris consequat tempor officia pariatur ex. Consectetur adipisicing non exercitation qui culpa deserunt reprehenderit magna qui laboris irure commodo ex excepteur. Do sit eiusmod amet pariatur velit reprehenderit pariatur tempor irure.\",\n    \"registered\": \"Thursday, January 21, 2016 10:53 PM\",\n    \"latitude\": \"57.38679\",\n    \"longitude\": \"-118.607665\",\n    \"tags\": [\n      \"esse\",\n      \"ipsum\",\n      \"nisi\",\n      \"tempor\",\n      \"veniam\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Roslyn Bush\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Garrison Good\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Langley Mccray\"\n      }\n    ],\n    \"greeting\": \"Hello, Farley! You have 7 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0b4d770216e6684e75\",\n    \"index\": 19,\n    \"guid\": \"7d88afe0-cc56-454c-ae62-bebcd97bf1ce\",\n    \"isActive\": false,\n    \"balance\": \"$2,132.97\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 20,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Lindsey\",\n      \"last\": \"Mccall\"\n    },\n    \"company\": \"CENTICE\",\n    \"email\": \"lindsey.mccall@centice.tv\",\n    \"phone\": \"+1 (833) 454-2919\",\n    \"address\": \"809 Seaview Court, Sexton, New Mexico, 333\",\n    \"about\": \"Id minim voluptate ullamco voluptate elit in consectetur irure commodo velit qui eiusmod exercitation. Commodo magna consectetur excepteur eiusmod est minim ipsum occaecat amet. Labore quis anim aliqua Lorem qui. Sint ad cillum cillum excepteur eu nostrud aute.\",\n    \"registered\": \"Thursday, February 20, 2014 10:05 AM\",\n    \"latitude\": \"41.095766\",\n    \"longitude\": \"-80.580867\",\n    \"tags\": [\n      \"esse\",\n      \"do\",\n      \"velit\",\n      \"deserunt\",\n      \"officia\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Allie Deleon\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Trudy Miles\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Lucy Lambert\"\n      }\n    ],\n    \"greeting\": \"Hello, Lindsey! You have 5 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0b1cd38dd10da43b88\",\n    \"index\": 20,\n    \"guid\": \"29e94d5c-de50-4bd1-93bd-ccbb98e2b0b5\",\n    \"isActive\": true,\n    \"balance\": \"$1,494.27\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 35,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Schmidt\",\n      \"last\": \"Terry\"\n    },\n    \"company\": \"ACCUSAGE\",\n    \"email\": \"schmidt.terry@accusage.net\",\n    \"phone\": \"+1 (917) 421-2816\",\n    \"address\": \"949 Beard Street, Allensworth, Marshall Islands, 5372\",\n    \"about\": \"Sint ex aute irure consequat mollit nostrud non. Non laboris ut deserunt nulla commodo id deserunt ut magna duis irure cupidatat. Deserunt eu incididunt exercitation velit ad laborum ad ipsum mollit reprehenderit laboris occaecat ullamco nulla. Consequat culpa id ullamco voluptate esse incididunt sint labore anim minim Lorem quis duis. In officia ex enim enim.\",\n    \"registered\": \"Tuesday, January 26, 2016 1:28 PM\",\n    \"latitude\": \"41.675283\",\n    \"longitude\": \"-72.234891\",\n    \"tags\": [\n      \"consequat\",\n      \"aliqua\",\n      \"nostrud\",\n      \"voluptate\",\n      \"dolor\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Vonda Byers\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Ashley Mayer\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Deborah Fuller\"\n      }\n    ],\n    \"greeting\": \"Hello, Schmidt! You have 6 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0b06efb327eb692d35\",\n    \"index\": 21,\n    \"guid\": \"ea02ef2d-6359-47c3-b324-c753511ea778\",\n    \"isActive\": false,\n    \"balance\": \"$1,009.38\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 24,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Bradley\",\n      \"last\": \"Wyatt\"\n    },\n    \"company\": \"PANZENT\",\n    \"email\": \"bradley.wyatt@panzent.org\",\n    \"phone\": \"+1 (875) 520-2168\",\n    \"address\": \"287 Utica Avenue, Hendersonville, Nebraska, 8366\",\n    \"about\": \"Do duis proident qui reprehenderit adipisicing nisi aliquip. Ut proident adipisicing quis proident sunt laboris adipisicing dolor. Non est aute Lorem cupidatat minim ea irure laborum minim eiusmod tempor nulla.\",\n    \"registered\": \"Tuesday, May 6, 2014 6:25 PM\",\n    \"latitude\": \"77.325105\",\n    \"longitude\": \"-85.665071\",\n    \"tags\": [\n      \"in\",\n      \"laborum\",\n      \"irure\",\n      \"irure\",\n      \"exercitation\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Stacey Banks\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Kelley Cox\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Gayle Ochoa\"\n      }\n    ],\n    \"greeting\": \"Hello, Bradley! You have 6 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0b39a0426fed8383c6\",\n    \"index\": 22,\n    \"guid\": \"061746b2-33e4-4f40-b19d-d829a2068d87\",\n    \"isActive\": true,\n    \"balance\": \"$2,235.00\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 32,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Stafford\",\n      \"last\": \"Sweet\"\n    },\n    \"company\": \"VIRVA\",\n    \"email\": \"stafford.sweet@virva.me\",\n    \"phone\": \"+1 (902) 447-3038\",\n    \"address\": \"688 Bond Street, Robinette, Pennsylvania, 3883\",\n    \"about\": \"Proident minim quis esse excepteur voluptate reprehenderit ea. Adipisicing esse ex aute in exercitation aute enim ut qui fugiat ex tempor eiusmod. Consectetur minim exercitation dolor aliquip pariatur cupidatat deserunt eu reprehenderit anim duis occaecat culpa. Sint aliquip ad mollit ut dolor excepteur duis nulla ipsum.\",\n    \"registered\": \"Sunday, March 30, 2014 7:54 PM\",\n    \"latitude\": \"83.670234\",\n    \"longitude\": \"-59.354751\",\n    \"tags\": [\n      \"proident\",\n      \"et\",\n      \"sit\",\n      \"id\",\n      \"reprehenderit\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Christi Willis\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Deidre Thompson\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Torres Kennedy\"\n      }\n    ],\n    \"greeting\": \"Hello, Stafford! You have 9 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0bbd300e4bf08c3141\",\n    \"index\": 23,\n    \"guid\": \"9319b49d-63ef-4a31-bf86-70b0ceb377d9\",\n    \"isActive\": false,\n    \"balance\": \"$2,151.45\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 25,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Clarke\",\n      \"last\": \"Rhodes\"\n    },\n    \"company\": \"LIQUIDOC\",\n    \"email\": \"clarke.rhodes@liquidoc.ca\",\n    \"phone\": \"+1 (861) 411-3808\",\n    \"address\": \"276 Gain Court, Downsville, Maine, 6619\",\n    \"about\": \"Ullamco eu enim fugiat duis quis eiusmod elit eiusmod aute. Adipisicing anim mollit non cillum irure velit deserunt magna voluptate incididunt ad dolore laboris non. Excepteur aute cillum dolore voluptate mollit quis ut ea non ullamco sint. Ullamco exercitation quis consequat proident ipsum aliqua dolor laboris voluptate dolore excepteur veniam. Nostrud sunt proident officia quis laborum proident elit laboris do.\",\n    \"registered\": \"Sunday, February 15, 2015 5:43 AM\",\n    \"latitude\": \"-81.487322\",\n    \"longitude\": \"-112.87443\",\n    \"tags\": [\n      \"velit\",\n      \"nostrud\",\n      \"enim\",\n      \"voluptate\",\n      \"et\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Mindy Horne\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Pamela Logan\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Peters Mccullough\"\n      }\n    ],\n    \"greeting\": \"Hello, Clarke! You have 7 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0b736919e3a60cfa05\",\n    \"index\": 24,\n    \"guid\": \"8e31e6e1-3cc6-4a9f-bfa7-9955ec571776\",\n    \"isActive\": true,\n    \"balance\": \"$3,542.80\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 25,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Simon\",\n      \"last\": \"Sellers\"\n    },\n    \"company\": \"PEARLESSA\",\n    \"email\": \"simon.sellers@pearlessa.biz\",\n    \"phone\": \"+1 (963) 586-2837\",\n    \"address\": \"691 Wyona Street, Calvary, West Virginia, 216\",\n    \"about\": \"Mollit consequat aliquip ad ut in reprehenderit. Ea aute laborum id dolor labore est adipisicing deserunt sit aliquip culpa culpa eiusmod excepteur. Mollit veniam magna ut mollit. Exercitation elit veniam laboris nostrud deserunt sint aute.\",\n    \"registered\": \"Monday, November 9, 2015 12:10 AM\",\n    \"latitude\": \"-3.493185\",\n    \"longitude\": \"-172.87793\",\n    \"tags\": [\n      \"id\",\n      \"labore\",\n      \"consequat\",\n      \"ea\",\n      \"occaecat\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Lorrie Eaton\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Puckett Harper\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Roach Powers\"\n      }\n    ],\n    \"greeting\": \"Hello, Simon! You have 10 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0b2d85bc8100e78a3c\",\n    \"index\": 25,\n    \"guid\": \"c836a6ee-d898-46a8-82f5-30408c199ed9\",\n    \"isActive\": false,\n    \"balance\": \"$1,425.17\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 36,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Tyler\",\n      \"last\": \"Rose\"\n    },\n    \"company\": \"ZOGAK\",\n    \"email\": \"tyler.rose@zogak.co.uk\",\n    \"phone\": \"+1 (959) 508-3061\",\n    \"address\": \"575 Furman Avenue, Zeba, South Dakota, 5624\",\n    \"about\": \"Lorem irure eiusmod quis nostrud anim aute consequat mollit est. Fugiat laboris tempor officia voluptate commodo dolore. Aute elit ipsum esse cupidatat laborum anim nisi in exercitation quis duis. Nisi exercitation labore pariatur quis fugiat. Consequat cillum deserunt exercitation officia mollit amet reprehenderit laborum adipisicing id ex cupidatat. Tempor in aliqua irure ad. Cupidatat qui et aliqua sunt sint laboris incididunt in.\",\n    \"registered\": \"Friday, November 27, 2015 6:39 PM\",\n    \"latitude\": \"21.932938\",\n    \"longitude\": \"-144.979719\",\n    \"tags\": [\n      \"mollit\",\n      \"excepteur\",\n      \"deserunt\",\n      \"ipsum\",\n      \"proident\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Felecia Cantrell\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Ortega Wilkinson\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Linda Jennings\"\n      }\n    ],\n    \"greeting\": \"Hello, Tyler! You have 6 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0b0a52937ecb5d0c4d\",\n    \"index\": 26,\n    \"guid\": \"a7778e1d-70c3-4f66-ac41-60035653fb5f\",\n    \"isActive\": true,\n    \"balance\": \"$1,640.96\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 21,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Greer\",\n      \"last\": \"Robbins\"\n    },\n    \"company\": \"KYAGURU\",\n    \"email\": \"greer.robbins@kyaguru.us\",\n    \"phone\": \"+1 (925) 443-3311\",\n    \"address\": \"650 Grant Avenue, Falmouth, Texas, 2573\",\n    \"about\": \"Ipsum adipisicing occaecat occaecat deserunt non cupidatat non velit deserunt velit amet velit exercitation sint. Commodo eiusmod nisi velit ea officia laborum cupidatat mollit sunt esse tempor est non. Veniam aliquip dolor est sunt et. Et laboris nostrud reprehenderit nisi dolor aliqua occaecat aute commodo aute duis do. Esse exercitation Lorem adipisicing sunt cillum nisi minim pariatur consectetur. Ipsum amet dolore id voluptate ipsum amet ut cupidatat laboris fugiat ex sunt minim.\",\n    \"registered\": \"Wednesday, January 27, 2016 1:24 AM\",\n    \"latitude\": \"43.532255\",\n    \"longitude\": \"102.388279\",\n    \"tags\": [\n      \"non\",\n      \"occaecat\",\n      \"deserunt\",\n      \"exercitation\",\n      \"velit\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Hogan Powell\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Valenzuela Chase\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Lakeisha Ball\"\n      }\n    ],\n    \"greeting\": \"Hello, Greer! You have 6 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0b10e85c3f2db6e944\",\n    \"index\": 27,\n    \"guid\": \"7f5f0c36-a744-4121-81f0-8840fb327754\",\n    \"isActive\": true,\n    \"balance\": \"$2,977.50\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 23,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Kitty\",\n      \"last\": \"Saunders\"\n    },\n    \"company\": \"SINGAVERA\",\n    \"email\": \"kitty.saunders@singavera.name\",\n    \"phone\": \"+1 (896) 427-2344\",\n    \"address\": \"107 Norfolk Street, Noxen, Missouri, 169\",\n    \"about\": \"Commodo voluptate consectetur id et occaecat consequat consectetur adipisicing eiusmod commodo. Aute nisi culpa fugiat sunt proident. Aliquip laboris elit veniam quis cillum. Nulla enim proident excepteur magna. Proident do aute tempor exercitation nostrud. Lorem officia id excepteur commodo incididunt et deserunt excepteur qui officia est incididunt mollit ut. Aute dolore cupidatat eu pariatur.\",\n    \"registered\": \"Sunday, April 27, 2014 10:20 PM\",\n    \"latitude\": \"33.543748\",\n    \"longitude\": \"10.300569\",\n    \"tags\": [\n      \"id\",\n      \"esse\",\n      \"voluptate\",\n      \"occaecat\",\n      \"incididunt\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Webb Hawkins\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Angie Cline\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Mathews Juarez\"\n      }\n    ],\n    \"greeting\": \"Hello, Kitty! You have 9 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0baf8e44be8a693264\",\n    \"index\": 28,\n    \"guid\": \"99b20c7a-567f-4810-a793-8470571ce457\",\n    \"isActive\": false,\n    \"balance\": \"$1,017.55\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 36,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Faith\",\n      \"last\": \"Mendoza\"\n    },\n    \"company\": \"XPLOR\",\n    \"email\": \"faith.mendoza@xplor.info\",\n    \"phone\": \"+1 (872) 504-2016\",\n    \"address\": \"323 Leonard Street, Deltaville, New Hampshire, 5194\",\n    \"about\": \"Tempor commodo laborum fugiat sit consequat reprehenderit sint anim voluptate. In velit do non aliquip officia est deserunt incididunt. Officia dolor id incididunt cillum minim dolore aliqua proident duis. Irure excepteur irure culpa commodo veniam culpa quis Lorem aute.\",\n    \"registered\": \"Saturday, May 30, 2015 7:01 AM\",\n    \"latitude\": \"12.428182\",\n    \"longitude\": \"-166.07543\",\n    \"tags\": [\n      \"non\",\n      \"aute\",\n      \"aute\",\n      \"sint\",\n      \"consequat\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Blair Mcknight\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Mcclain Mcguire\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Araceli Dorsey\"\n      }\n    ],\n    \"greeting\": \"Hello, Faith! You have 8 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0b52e4d918273360dd\",\n    \"index\": 29,\n    \"guid\": \"8c61aed3-6235-4e09-8ec4-ece4fe285a9c\",\n    \"isActive\": false,\n    \"balance\": \"$1,182.53\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 36,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Amie\",\n      \"last\": \"Crane\"\n    },\n    \"company\": \"RODEOLOGY\",\n    \"email\": \"amie.crane@rodeology.io\",\n    \"phone\": \"+1 (955) 536-3756\",\n    \"address\": \"578 Box Street, Shelby, Hawaii, 1306\",\n    \"about\": \"Ex esse et minim dolore sint pariatur incididunt esse. Aute dolore quis ad dolor minim laborum amet. Aliquip deserunt ad aute fugiat proident mollit adipisicing mollit sit duis.\",\n    \"registered\": \"Thursday, June 19, 2014 2:34 PM\",\n    \"latitude\": \"-19.02611\",\n    \"longitude\": \"127.57473\",\n    \"tags\": [\n      \"ad\",\n      \"nostrud\",\n      \"sit\",\n      \"duis\",\n      \"adipisicing\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Kirk Stewart\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Rice Horton\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Kathy Shaffer\"\n      }\n    ],\n    \"greeting\": \"Hello, Amie! You have 7 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c28c0b7bda4325a1d\",\n    \"index\": 30,\n    \"guid\": \"ed1385bf-c7d8-41fa-a099-734894807171\",\n    \"isActive\": false,\n    \"balance\": \"$3,062.06\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 39,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Best\",\n      \"last\": \"Buchanan\"\n    },\n    \"company\": \"CONFERIA\",\n    \"email\": \"best.buchanan@conferia.com\",\n    \"phone\": \"+1 (845) 406-3653\",\n    \"address\": \"742 Miami Court, Wanship, Georgia, 6992\",\n    \"about\": \"Officia labore velit non mollit adipisicing sit do anim ullamco ut. Incididunt labore aliquip nostrud irure adipisicing id voluptate nulla ex. Ex duis incididunt eu sit et pariatur ullamco incididunt fugiat ex incididunt ea laboris.\",\n    \"registered\": \"Tuesday, December 30, 2014 11:23 AM\",\n    \"latitude\": \"-14.672379\",\n    \"longitude\": \"123.40741\",\n    \"tags\": [\n      \"et\",\n      \"deserunt\",\n      \"cupidatat\",\n      \"pariatur\",\n      \"eu\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Hudson Mejia\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Byers Montoya\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Patty Spencer\"\n      }\n    ],\n    \"greeting\": \"Hello, Best! You have 6 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c9e948778b2db882e\",\n    \"index\": 31,\n    \"guid\": \"700b5215-132e-4524-9ca8-1224a746f09b\",\n    \"isActive\": true,\n    \"balance\": \"$3,891.22\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 29,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Francine\",\n      \"last\": \"Green\"\n    },\n    \"company\": \"NUTRALAB\",\n    \"email\": \"francine.green@nutralab.tv\",\n    \"phone\": \"+1 (841) 589-3865\",\n    \"address\": \"165 Union Street, Hebron, North Carolina, 8094\",\n    \"about\": \"Sunt eu irure aute incididunt mollit. Nisi mollit nulla aliquip cupidatat aute culpa cupidatat mollit tempor. Irure magna officia sint officia. Quis adipisicing ullamco non id aliquip. Non velit commodo quis elit nostrud adipisicing nisi incididunt non sint enim. In qui fugiat ullamco irure Lorem veniam pariatur culpa.\",\n    \"registered\": \"Tuesday, June 30, 2015 2:52 AM\",\n    \"latitude\": \"-83.386439\",\n    \"longitude\": \"-80.298927\",\n    \"tags\": [\n      \"mollit\",\n      \"aliqua\",\n      \"dolor\",\n      \"tempor\",\n      \"sit\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Page Beasley\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Keith Atkinson\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Hahn Evans\"\n      }\n    ],\n    \"greeting\": \"Hello, Francine! You have 6 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c565d848f7f4b0064\",\n    \"index\": 32,\n    \"guid\": \"88163664-2ab3-4a01-80f9-10b83ad3dece\",\n    \"isActive\": false,\n    \"balance\": \"$2,387.41\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 35,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Kelly\",\n      \"last\": \"Fowler\"\n    },\n    \"company\": \"COMVERGES\",\n    \"email\": \"kelly.fowler@comverges.net\",\n    \"phone\": \"+1 (926) 440-3886\",\n    \"address\": \"917 Kathleen Court, Outlook, Arkansas, 505\",\n    \"about\": \"Esse dolore eiusmod culpa ea velit elit culpa veniam proident culpa consequat cupidatat. Fugiat id proident pariatur est fugiat ea. Esse ex dolor minim cillum incididunt voluptate ipsum. Non dolor amet labore excepteur enim eu duis in qui. Sint dolore minim anim mollit laborum proident ea ipsum officia aliquip. Amet culpa adipisicing nisi occaecat commodo irure in ad veniam ipsum officia labore labore.\",\n    \"registered\": \"Friday, April 4, 2014 7:14 PM\",\n    \"latitude\": \"-24.694221\",\n    \"longitude\": \"43.274287\",\n    \"tags\": [\n      \"sunt\",\n      \"ex\",\n      \"est\",\n      \"nisi\",\n      \"sint\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Cash Hull\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Lorene Michael\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Rodgers Underwood\"\n      }\n    ],\n    \"greeting\": \"Hello, Kelly! You have 9 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0cab9f4bf7a91707f2\",\n    \"index\": 33,\n    \"guid\": \"7f857967-0524-44fc-a028-14bc9b78be00\",\n    \"isActive\": false,\n    \"balance\": \"$3,904.91\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 34,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Walters\",\n      \"last\": \"Bishop\"\n    },\n    \"company\": \"CEPRENE\",\n    \"email\": \"walters.bishop@ceprene.org\",\n    \"phone\": \"+1 (955) 536-2146\",\n    \"address\": \"441 Livonia Avenue, Norvelt, Indiana, 8326\",\n    \"about\": \"Enim ad culpa ut aliqua id eiusmod ad aliqua. Cupidatat voluptate enim esse amet anim officia aliquip incididunt ipsum ut. Dolore magna cillum sunt duis duis elit commodo ipsum cillum aliquip proident nisi laboris. Ad Lorem cillum commodo quis tempor consequat laborum velit aliquip et laboris deserunt officia labore. Commodo enim et est id aute nostrud id. Excepteur consequat aliquip laboris ipsum sit aliqua proident esse adipisicing sint consectetur enim aliquip veniam.\",\n    \"registered\": \"Sunday, August 2, 2015 11:14 AM\",\n    \"latitude\": \"-33.507298\",\n    \"longitude\": \"-177.843777\",\n    \"tags\": [\n      \"est\",\n      \"officia\",\n      \"ad\",\n      \"fugiat\",\n      \"fugiat\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Sloan Larson\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Lora Howe\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Cheri Cain\"\n      }\n    ],\n    \"greeting\": \"Hello, Walters! You have 8 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c41960d0332abb69f\",\n    \"index\": 34,\n    \"guid\": \"c179f976-46db-4ceb-97dd-d511c2ff2cba\",\n    \"isActive\": false,\n    \"balance\": \"$3,922.97\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 36,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Jamie\",\n      \"last\": \"Justice\"\n    },\n    \"company\": \"CEMENTION\",\n    \"email\": \"jamie.justice@cemention.me\",\n    \"phone\": \"+1 (886) 453-3323\",\n    \"address\": \"534 Downing Street, Coldiron, Louisiana, 2936\",\n    \"about\": \"Ad sint ut qui duis commodo magna sit irure duis labore reprehenderit dolor voluptate. Cillum non dolore mollit amet proident. Tempor sunt aliquip pariatur ullamco incididunt laboris. Adipisicing incididunt magna Lorem voluptate.\",\n    \"registered\": \"Wednesday, February 25, 2015 4:11 AM\",\n    \"latitude\": \"-83.79595\",\n    \"longitude\": \"71.384564\",\n    \"tags\": [\n      \"quis\",\n      \"nisi\",\n      \"dolore\",\n      \"voluptate\",\n      \"fugiat\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Harvey Ramos\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Mays Shannon\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Ball Mcbride\"\n      }\n    ],\n    \"greeting\": \"Hello, Jamie! You have 5 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c9fb032931aaf922e\",\n    \"index\": 35,\n    \"guid\": \"2d85869d-f1e0-4f2d-8544-78652740bb11\",\n    \"isActive\": true,\n    \"balance\": \"$1,468.38\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 38,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Dyer\",\n      \"last\": \"Tran\"\n    },\n    \"company\": \"COMVEYOR\",\n    \"email\": \"dyer.tran@comveyor.ca\",\n    \"phone\": \"+1 (890) 529-2771\",\n    \"address\": \"299 Hyman Court, Sims, Colorado, 8328\",\n    \"about\": \"Adipisicing dolor velit non tempor incididunt magna magna dolore. Officia pariatur mollit anim Lorem Lorem esse. Occaecat eiusmod ipsum ipsum excepteur ut duis commodo esse incididunt. Veniam non officia consequat amet duis nulla qui qui ex sint veniam.\",\n    \"registered\": \"Tuesday, October 11, 2016 7:57 AM\",\n    \"latitude\": \"-58.389129\",\n    \"longitude\": \"-86.760618\",\n    \"tags\": [\n      \"incididunt\",\n      \"laboris\",\n      \"consectetur\",\n      \"voluptate\",\n      \"aliqua\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Dominguez Woodard\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Leach Kramer\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Cruz Wilder\"\n      }\n    ],\n    \"greeting\": \"Hello, Dyer! You have 6 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0cc5bcf8eb53c8bfdf\",\n    \"index\": 36,\n    \"guid\": \"6ced7a65-2989-44d3-8d79-7f02fbaf0f4b\",\n    \"isActive\": true,\n    \"balance\": \"$1,243.22\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 32,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Maureen\",\n      \"last\": \"Ramsey\"\n    },\n    \"company\": \"PROFLEX\",\n    \"email\": \"maureen.ramsey@proflex.biz\",\n    \"phone\": \"+1 (966) 547-2724\",\n    \"address\": \"828 Louise Terrace, Groton, Michigan, 9563\",\n    \"about\": \"Enim aliqua qui velit ut dolore Lorem officia. Nisi Lorem aute consequat consectetur reprehenderit tempor. Consectetur velit laboris Lorem do anim ex exercitation. Est deserunt mollit reprehenderit eu amet aute eu reprehenderit mollit voluptate adipisicing excepteur dolor. Cillum commodo laborum in commodo do incididunt excepteur exercitation nostrud. Officia aliquip labore id aliquip sunt officia deserunt. Sunt officia nulla commodo sit velit excepteur dolor ea occaecat commodo eu.\",\n    \"registered\": \"Monday, May 18, 2015 11:36 AM\",\n    \"latitude\": \"35.536999\",\n    \"longitude\": \"-158.630389\",\n    \"tags\": [\n      \"qui\",\n      \"amet\",\n      \"eu\",\n      \"eu\",\n      \"mollit\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Good Spears\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Gina Rodriguez\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Snow Harrington\"\n      }\n    ],\n    \"greeting\": \"Hello, Maureen! You have 6 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c4f6c60bdf26873c9\",\n    \"index\": 37,\n    \"guid\": \"4883e626-07c8-47af-97d4-8ea82f07aa9d\",\n    \"isActive\": false,\n    \"balance\": \"$2,250.74\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 21,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Cannon\",\n      \"last\": \"Walters\"\n    },\n    \"company\": \"LUNCHPAD\",\n    \"email\": \"cannon.walters@lunchpad.co.uk\",\n    \"phone\": \"+1 (830) 427-3699\",\n    \"address\": \"202 Clarkson Avenue, Durham, New Jersey, 3075\",\n    \"about\": \"Ipsum sint nulla labore do magna fugiat et in irure ex laborum sunt consequat ipsum. Duis et velit nostrud et sit incididunt laborum magna laboris do ex. Reprehenderit culpa nostrud eu qui labore sint ipsum amet reprehenderit dolor incididunt. Nulla proident dolor Lorem culpa officia eu excepteur anim in anim esse id sunt qui. Sit Lorem ea irure id ipsum culpa qui deserunt nulla veniam nisi in. Eiusmod laborum Lorem esse consectetur enim quis minim culpa nostrud.\",\n    \"registered\": \"Thursday, October 6, 2016 6:33 PM\",\n    \"latitude\": \"84.142398\",\n    \"longitude\": \"15.387476\",\n    \"tags\": [\n      \"occaecat\",\n      \"officia\",\n      \"sunt\",\n      \"labore\",\n      \"consequat\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Coleman Hendrix\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Bridges Benson\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Anthony Mathews\"\n      }\n    ],\n    \"greeting\": \"Hello, Cannon! You have 7 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0cf09ee09c6eaa88cb\",\n    \"index\": 38,\n    \"guid\": \"c54b0b2a-2a0f-4168-b335-bc9a8e0460ca\",\n    \"isActive\": true,\n    \"balance\": \"$3,203.52\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 33,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Nannie\",\n      \"last\": \"Beard\"\n    },\n    \"company\": \"ANIMALIA\",\n    \"email\": \"nannie.beard@animalia.us\",\n    \"phone\": \"+1 (889) 443-3959\",\n    \"address\": \"225 Jay Street, Datil, Wyoming, 5453\",\n    \"about\": \"Esse culpa ipsum duis minim ut ex tempor proident sunt minim consequat commodo ipsum anim. Lorem exercitation cupidatat tempor Lorem quis officia minim qui proident et. Qui esse non ex mollit magna nostrud cillum sit pariatur magna fugiat qui id.\",\n    \"registered\": \"Saturday, April 25, 2015 9:25 AM\",\n    \"latitude\": \"-87.539067\",\n    \"longitude\": \"63.880414\",\n    \"tags\": [\n      \"ut\",\n      \"et\",\n      \"esse\",\n      \"cillum\",\n      \"nulla\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Francis Barron\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Pate Aguirre\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Gay Acosta\"\n      }\n    ],\n    \"greeting\": \"Hello, Nannie! You have 10 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0cca904f751c058d2e\",\n    \"index\": 39,\n    \"guid\": \"7a2ff5cf-270a-4211-a2e8-3509d8884622\",\n    \"isActive\": false,\n    \"balance\": \"$3,606.94\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 35,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Lacy\",\n      \"last\": \"Beach\"\n    },\n    \"company\": \"DATAGEN\",\n    \"email\": \"lacy.beach@datagen.name\",\n    \"phone\": \"+1 (831) 573-3879\",\n    \"address\": \"984 Adelphi Street, Herbster, Maryland, 4378\",\n    \"about\": \"Occaecat do consectetur voluptate veniam id id officia ea deserunt aute anim irure magna quis. Ipsum cupidatat ea commodo dolor ea dolor nisi non. Excepteur irure veniam laborum qui id eu nulla reprehenderit mollit ad aute consectetur elit Lorem.\",\n    \"registered\": \"Thursday, March 13, 2014 1:02 PM\",\n    \"latitude\": \"-9.391908\",\n    \"longitude\": \"159.065461\",\n    \"tags\": [\n      \"dolor\",\n      \"reprehenderit\",\n      \"labore\",\n      \"aute\",\n      \"voluptate\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Rosa Mercado\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Fuentes Valenzuela\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Dixon Singleton\"\n      }\n    ],\n    \"greeting\": \"Hello, Lacy! You have 10 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c1458894607879e9d\",\n    \"index\": 40,\n    \"guid\": \"45bbc6f7-4183-4eeb-b34a-98ecc8787e7a\",\n    \"isActive\": false,\n    \"balance\": \"$2,097.65\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 34,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Cara\",\n      \"last\": \"Lyons\"\n    },\n    \"company\": \"VICON\",\n    \"email\": \"cara.lyons@vicon.info\",\n    \"phone\": \"+1 (933) 546-2258\",\n    \"address\": \"813 Ingraham Street, Riner, Tennessee, 3069\",\n    \"about\": \"Commodo quis excepteur nulla qui. Fugiat ullamco ex excepteur tempor. Sint duis aute magna in fugiat. Laborum sit magna est duis reprehenderit elit nulla elit. Mollit Lorem ut incididunt nostrud ad voluptate aliquip et et sint ullamco.\",\n    \"registered\": \"Sunday, May 11, 2014 7:13 AM\",\n    \"latitude\": \"11.108367\",\n    \"longitude\": \"-144.351099\",\n    \"tags\": [\n      \"excepteur\",\n      \"cillum\",\n      \"sunt\",\n      \"et\",\n      \"duis\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Ayala Black\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Nichole Sutton\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Mathis Cash\"\n      }\n    ],\n    \"greeting\": \"Hello, Cara! You have 9 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0ca9d8edf2d7811aab\",\n    \"index\": 41,\n    \"guid\": \"f4aa6a49-0c71-4ad7-9514-614ecb9cc221\",\n    \"isActive\": true,\n    \"balance\": \"$2,945.42\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 33,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Jean\",\n      \"last\": \"Copeland\"\n    },\n    \"company\": \"KIGGLE\",\n    \"email\": \"jean.copeland@kiggle.io\",\n    \"phone\": \"+1 (841) 470-2173\",\n    \"address\": \"687 Prospect Place, Welda, New York, 4786\",\n    \"about\": \"Quis eiusmod enim ad velit mollit occaecat. Ea aute laborum fugiat consequat. Exercitation laboris aliquip ad tempor culpa. Ipsum aute excepteur in fugiat adipisicing incididunt exercitation non occaecat. Id labore ex occaecat esse dolore sit commodo duis quis incididunt enim reprehenderit. Tempor sit qui irure ut pariatur laborum laboris in nostrud sit excepteur.\",\n    \"registered\": \"Monday, June 22, 2015 4:57 AM\",\n    \"latitude\": \"-36.057061\",\n    \"longitude\": \"-91.348514\",\n    \"tags\": [\n      \"occaecat\",\n      \"sit\",\n      \"id\",\n      \"anim\",\n      \"elit\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Sherman Christian\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Janna Weiss\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Janette Nieves\"\n      }\n    ],\n    \"greeting\": \"Hello, Jean! You have 8 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c06cac977a4bff550\",\n    \"index\": 42,\n    \"guid\": \"e0aacdb6-61dd-44b2-afa5-d23f8b11ff2d\",\n    \"isActive\": true,\n    \"balance\": \"$2,740.65\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 29,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Alfreda\",\n      \"last\": \"Duncan\"\n    },\n    \"company\": \"BITREX\",\n    \"email\": \"alfreda.duncan@bitrex.com\",\n    \"phone\": \"+1 (903) 491-3912\",\n    \"address\": \"541 Sunnyside Court, Ryderwood, Virginia, 9259\",\n    \"about\": \"Pariatur mollit qui veniam sunt enim Lorem non. Commodo ea deserunt aute dolor id enim anim Lorem occaecat esse nostrud id irure. Aliquip dolore voluptate enim dolore velit adipisicing id est dolore consequat enim. Voluptate ex duis duis elit. Occaecat ex nulla labore ex sit. Proident officia sunt mollit irure non quis commodo sint laboris tempor ut aliquip incididunt. Aliquip ea ad consectetur incididunt.\",\n    \"registered\": \"Saturday, March 12, 2016 2:20 PM\",\n    \"latitude\": \"33.795212\",\n    \"longitude\": \"-48.161379\",\n    \"tags\": [\n      \"velit\",\n      \"dolore\",\n      \"deserunt\",\n      \"exercitation\",\n      \"nostrud\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Tracie Hendricks\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Deann Mathis\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Newman Pace\"\n      }\n    ],\n    \"greeting\": \"Hello, Alfreda! You have 10 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c547ba7b2b5b2ff68\",\n    \"index\": 43,\n    \"guid\": \"63630a5c-d6b6-4dd9-a096-ae1c81a92bc5\",\n    \"isActive\": false,\n    \"balance\": \"$2,655.36\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 31,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Estelle\",\n      \"last\": \"Carson\"\n    },\n    \"company\": \"GEEKFARM\",\n    \"email\": \"estelle.carson@geekfarm.tv\",\n    \"phone\": \"+1 (829) 551-2574\",\n    \"address\": \"854 Calder Place, Greer, Nevada, 7137\",\n    \"about\": \"Laborum velit deserunt ad magna et qui sint sint sunt elit. Exercitation eiusmod ea deserunt quis consectetur sit cillum tempor eiusmod labore ad consequat. Pariatur tempor sunt Lorem sit tempor elit sint ex exercitation. Aliquip tempor irure aute anim tempor sint exercitation exercitation. Consectetur veniam laboris tempor ipsum duis sit tempor laboris eu aute.\",\n    \"registered\": \"Wednesday, June 3, 2015 5:48 PM\",\n    \"latitude\": \"45.625966\",\n    \"longitude\": \"-113.836952\",\n    \"tags\": [\n      \"esse\",\n      \"nostrud\",\n      \"consectetur\",\n      \"est\",\n      \"qui\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Albert Conrad\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Etta Lara\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"James Joseph\"\n      }\n    ],\n    \"greeting\": \"Hello, Estelle! You have 6 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0ce1fbdc13fd921dfa\",\n    \"index\": 44,\n    \"guid\": \"bab756db-f4f0-4c20-8819-805f4de1acf1\",\n    \"isActive\": false,\n    \"balance\": \"$3,043.42\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 28,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Flowers\",\n      \"last\": \"Guthrie\"\n    },\n    \"company\": \"GLOBOIL\",\n    \"email\": \"flowers.guthrie@globoil.net\",\n    \"phone\": \"+1 (848) 547-2136\",\n    \"address\": \"757 Rodney Street, Lupton, Washington, 290\",\n    \"about\": \"Nulla sunt ut laboris id consequat id quis excepteur et eu sint esse est. Nostrud voluptate voluptate minim enim exercitation sunt labore ea cillum veniam mollit pariatur enim. Officia ipsum voluptate eu veniam dolor est do. Ullamco minim excepteur aute eu cillum reprehenderit non laborum. Do magna eiusmod veniam eu cillum magna deserunt labore. Nisi culpa nisi proident amet eu et cillum mollit reprehenderit veniam et. Ad elit sint aliquip cupidatat exercitation do ex velit esse nisi sunt nostrud.\",\n    \"registered\": \"Wednesday, May 7, 2014 11:25 PM\",\n    \"latitude\": \"-18.985921\",\n    \"longitude\": \"-170.937155\",\n    \"tags\": [\n      \"sunt\",\n      \"nostrud\",\n      \"ex\",\n      \"qui\",\n      \"proident\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Holcomb Joyce\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Dolores Guy\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Alisa Garrett\"\n      }\n    ],\n    \"greeting\": \"Hello, Flowers! You have 8 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0ce0cd552ea52c7f2c\",\n    \"index\": 45,\n    \"guid\": \"dea849a7-62f2-4a50-8215-6070082f02e7\",\n    \"isActive\": false,\n    \"balance\": \"$2,088.13\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 35,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Duran\",\n      \"last\": \"Houston\"\n    },\n    \"company\": \"ENQUILITY\",\n    \"email\": \"duran.houston@enquility.org\",\n    \"phone\": \"+1 (968) 460-3781\",\n    \"address\": \"968 Prospect Avenue, Eastmont, Minnesota, 5346\",\n    \"about\": \"Irure nisi occaecat quis ipsum exercitation esse minim nulla magna sunt nisi culpa exercitation. Nulla ea ipsum exercitation est culpa consequat nostrud duis ex do incididunt est. Dolore et sit labore ex non do ipsum. Do velit voluptate sint nisi enim sit sunt tempor voluptate aliquip ullamco reprehenderit do. Id sunt cillum ea nisi aute qui do tempor nisi aute nostrud. Ipsum cupidatat laboris elit dolore pariatur tempor mollit exercitation tempor et voluptate laboris eu. Ex Lorem sint sit tempor sunt elit.\",\n    \"registered\": \"Saturday, June 11, 2016 4:20 PM\",\n    \"latitude\": \"18.300079\",\n    \"longitude\": \"-83.967577\",\n    \"tags\": [\n      \"labore\",\n      \"sunt\",\n      \"mollit\",\n      \"voluptate\",\n      \"duis\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Lori Roach\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Benita Farley\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Sheri Hurst\"\n      }\n    ],\n    \"greeting\": \"Hello, Duran! You have 9 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0c491e46a12015041d\",\n    \"index\": 46,\n    \"guid\": \"2c10fc4c-47d7-41b0-ab6d-b27348fd3a2c\",\n    \"isActive\": false,\n    \"balance\": \"$1,874.46\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 24,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Cortez\",\n      \"last\": \"Levy\"\n    },\n    \"company\": \"OPTICON\",\n    \"email\": \"cortez.levy@opticon.me\",\n    \"phone\": \"+1 (831) 454-3969\",\n    \"address\": \"707 Lefferts Avenue, Stouchsburg, District Of Columbia, 5615\",\n    \"about\": \"Dolor labore exercitation magna eu exercitation veniam culpa ad dolore ut consequat Lorem et officia. Voluptate aliqua laborum amet voluptate consequat id. Laboris tempor dolor cillum ut ullamco voluptate incididunt amet ipsum laboris exercitation tempor. Do quis duis qui magna eu irure sunt.\",\n    \"registered\": \"Wednesday, November 4, 2015 7:46 PM\",\n    \"latitude\": \"5.931545\",\n    \"longitude\": \"58.787345\",\n    \"tags\": [\n      \"enim\",\n      \"consequat\",\n      \"irure\",\n      \"sit\",\n      \"voluptate\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Marquita Figueroa\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Justice Stephens\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Traci Blevins\"\n      }\n    ],\n    \"greeting\": \"Hello, Cortez! You have 6 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c163f106a3d9867a0\",\n    \"index\": 47,\n    \"guid\": \"2299f9f1-f9c9-4d53-b9f5-829132d1b1f6\",\n    \"isActive\": false,\n    \"balance\": \"$3,704.08\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 31,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Sampson\",\n      \"last\": \"Sloan\"\n    },\n    \"company\": \"ZAGGLES\",\n    \"email\": \"sampson.sloan@zaggles.ca\",\n    \"phone\": \"+1 (812) 536-2825\",\n    \"address\": \"921 Beadel Street, Manila, Guam, 2501\",\n    \"about\": \"Nisi irure laboris dolore Lorem nulla irure consectetur mollit commodo aliqua pariatur et. Officia magna veniam culpa excepteur ea sint reprehenderit eiusmod. Ex dolor non nisi enim eu et ad occaecat magna id ullamco in occaecat. Commodo labore Lorem aliqua quis amet irure labore. Deserunt proident cillum et do consequat mollit reprehenderit eiusmod voluptate proident sunt ex. Aute aliqua ullamco duis laborum labore et exercitation.\",\n    \"registered\": \"Sunday, October 26, 2014 2:43 AM\",\n    \"latitude\": \"83.228508\",\n    \"longitude\": \"0.459083\",\n    \"tags\": [\n      \"tempor\",\n      \"exercitation\",\n      \"proident\",\n      \"exercitation\",\n      \"et\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Hannah Schneider\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"White Williamson\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Annie Moreno\"\n      }\n    ],\n    \"greeting\": \"Hello, Sampson! You have 5 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0ccd1cc56b6ac487a6\",\n    \"index\": 48,\n    \"guid\": \"857676b3-f998-4efa-9ec8-6762b2468e43\",\n    \"isActive\": false,\n    \"balance\": \"$3,662.77\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 35,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Nona\",\n      \"last\": \"Nguyen\"\n    },\n    \"company\": \"QUARMONY\",\n    \"email\": \"nona.nguyen@quarmony.biz\",\n    \"phone\": \"+1 (921) 562-2588\",\n    \"address\": \"660 Sutton Street, Eggertsville, American Samoa, 7519\",\n    \"about\": \"Et esse sit labore pariatur eu eiusmod eu et non elit adipisicing mollit esse id. Quis dolore magna ipsum do velit cupidatat in officia do dolore voluptate anim laboris aliquip. Lorem amet sunt irure laborum ad sunt commodo cillum minim consectetur. Eiusmod id exercitation occaecat esse enim magna consectetur reprehenderit nostrud quis ea dolor sit incididunt. Ipsum eu quis consequat ad eiusmod amet velit veniam aliquip reprehenderit in ea. Duis et aliquip voluptate dolor proident incididunt eiusmod. Magna eu aliquip aliquip id.\",\n    \"registered\": \"Friday, January 22, 2016 1:24 AM\",\n    \"latitude\": \"45.499893\",\n    \"longitude\": \"71.913207\",\n    \"tags\": [\n      \"proident\",\n      \"irure\",\n      \"eu\",\n      \"laborum\",\n      \"tempor\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Ross Landry\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Chan Joyner\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Richardson Snow\"\n      }\n    ],\n    \"greeting\": \"Hello, Nona! You have 5 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c4546f881c8f7555f\",\n    \"index\": 49,\n    \"guid\": \"28bddafd-23f4-49a2-bae8-643e1e7c9fb3\",\n    \"isActive\": false,\n    \"balance\": \"$2,081.58\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 38,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Wendi\",\n      \"last\": \"Cleveland\"\n    },\n    \"company\": \"EVENTEX\",\n    \"email\": \"wendi.cleveland@eventex.co.uk\",\n    \"phone\": \"+1 (935) 410-2326\",\n    \"address\": \"737 Ebony Court, Elfrida, Delaware, 5439\",\n    \"about\": \"Enim deserunt commodo qui laboris mollit ullamco. Id qui laboris magna do consequat nostrud sint occaecat amet officia. Laboris excepteur Lorem aliqua in est exercitation ut laborum et. Officia nisi eiusmod veniam mollit laborum magna esse labore elit pariatur pariatur. Esse ipsum pariatur pariatur eiusmod est ex tempor non.\",\n    \"registered\": \"Sunday, March 9, 2014 5:48 PM\",\n    \"latitude\": \"88.457536\",\n    \"longitude\": \"-65.024239\",\n    \"tags\": [\n      \"aliquip\",\n      \"duis\",\n      \"tempor\",\n      \"ipsum\",\n      \"velit\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Gabrielle Cote\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Brandi Downs\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Rhonda Ellison\"\n      }\n    ],\n    \"greeting\": \"Hello, Wendi! You have 9 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0cec2076eb6d116dfc\",\n    \"index\": 50,\n    \"guid\": \"39489d24-b8b8-47bc-b7d0-c3481f3ad5bd\",\n    \"isActive\": false,\n    \"balance\": \"$1,075.68\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 40,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Dale\",\n      \"last\": \"Douglas\"\n    },\n    \"company\": \"DELPHIDE\",\n    \"email\": \"dale.douglas@delphide.us\",\n    \"phone\": \"+1 (977) 457-3984\",\n    \"address\": \"754 Hicks Street, Vaughn, Palau, 7264\",\n    \"about\": \"Ad incididunt proident proident ipsum nisi ipsum cillum consectetur cupidatat aliquip labore duis aliquip est. Esse nulla sit magna ipsum. Proident aliquip sint fugiat dolor sunt elit velit qui tempor officia amet nostrud.\",\n    \"registered\": \"Saturday, July 30, 2016 8:55 AM\",\n    \"latitude\": \"-8.058598\",\n    \"longitude\": \"-33.314846\",\n    \"tags\": [\n      \"irure\",\n      \"nisi\",\n      \"consequat\",\n      \"cillum\",\n      \"dolor\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Harriett Gamble\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Hester Pittman\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Lesley Washington\"\n      }\n    ],\n    \"greeting\": \"Hello, Dale! You have 10 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0cd4ecef796970cd3f\",\n    \"index\": 51,\n    \"guid\": \"52dc9e03-d6bc-4776-975e-22735a673bb9\",\n    \"isActive\": false,\n    \"balance\": \"$2,094.76\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 23,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Eula\",\n      \"last\": \"Huber\"\n    },\n    \"company\": \"COMTENT\",\n    \"email\": \"eula.huber@comtent.name\",\n    \"phone\": \"+1 (914) 552-2231\",\n    \"address\": \"389 Grove Street, Eagletown, Massachusetts, 8524\",\n    \"about\": \"Id sunt consectetur nostrud occaecat ea eu reprehenderit excepteur amet nostrud et ea. In ipsum occaecat aliquip est ex aute labore nostrud eu incididunt velit est. Aliquip velit occaecat laborum consectetur dolor.\",\n    \"registered\": \"Saturday, June 20, 2015 8:33 AM\",\n    \"latitude\": \"-27.235963\",\n    \"longitude\": \"17.691481\",\n    \"tags\": [\n      \"eu\",\n      \"ex\",\n      \"exercitation\",\n      \"reprehenderit\",\n      \"irure\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Melva Coffey\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Farmer Weaver\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Watkins Serrano\"\n      }\n    ],\n    \"greeting\": \"Hello, Eula! You have 7 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0ccb817c1dc5c145c8\",\n    \"index\": 52,\n    \"guid\": \"7ec24512-cb72-4fa5-b411-44c7b7ac0cde\",\n    \"isActive\": true,\n    \"balance\": \"$3,388.55\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 33,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Lamb\",\n      \"last\": \"Contreras\"\n    },\n    \"company\": \"COMTRAK\",\n    \"email\": \"lamb.contreras@comtrak.info\",\n    \"phone\": \"+1 (866) 451-3939\",\n    \"address\": \"403 Himrod Street, Haring, South Carolina, 7598\",\n    \"about\": \"Labore labore ipsum non eu quis eiusmod sint fugiat qui tempor sit. Ex exercitation culpa exercitation sunt ipsum nisi et adipisicing proident adipisicing do. Amet cillum amet sit ea dolor. Ex mollit ut eu ad quis consequat cillum ullamco aliquip. Minim enim ut et incididunt occaecat ea. Et dolor nisi sunt eiusmod duis. Reprehenderit aliqua eiusmod amet dolor aliqua.\",\n    \"registered\": \"Tuesday, December 23, 2014 11:42 AM\",\n    \"latitude\": \"-56.076139\",\n    \"longitude\": \"126.054413\",\n    \"tags\": [\n      \"nostrud\",\n      \"et\",\n      \"commodo\",\n      \"nisi\",\n      \"deserunt\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Kasey Andrews\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Pearl Bridges\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Tameka Chandler\"\n      }\n    ],\n    \"greeting\": \"Hello, Lamb! You have 10 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c39b5a56a264d4bcf\",\n    \"index\": 53,\n    \"guid\": \"e07bd38e-e79b-4ceb-bcbc-ea60b4b0e740\",\n    \"isActive\": false,\n    \"balance\": \"$1,011.72\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 28,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Angelique\",\n      \"last\": \"Barry\"\n    },\n    \"company\": \"INSOURCE\",\n    \"email\": \"angelique.barry@insource.io\",\n    \"phone\": \"+1 (983) 464-3474\",\n    \"address\": \"691 Heyward Street, Bodega, Florida, 4333\",\n    \"about\": \"Deserunt voluptate eiusmod sit et quis aliquip eiusmod ex consequat velit. Cillum elit excepteur ex esse officia cupidatat ut. Nostrud pariatur ex et ullamco dolor dolore fugiat excepteur irure exercitation. Id Lorem proident ea quis.\",\n    \"registered\": \"Tuesday, April 26, 2016 12:53 PM\",\n    \"latitude\": \"69.146806\",\n    \"longitude\": \"-136.72291\",\n    \"tags\": [\n      \"qui\",\n      \"anim\",\n      \"anim\",\n      \"quis\",\n      \"pariatur\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Doyle Tanner\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Gloria Stevens\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Sally Chapman\"\n      }\n    ],\n    \"greeting\": \"Hello, Angelique! You have 7 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c653a65b1bde5a9af\",\n    \"index\": 54,\n    \"guid\": \"79ad167a-209e-4ce3-8e84-cc46e9d79702\",\n    \"isActive\": true,\n    \"balance\": \"$2,652.68\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 33,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Marissa\",\n      \"last\": \"Head\"\n    },\n    \"company\": \"BOLAX\",\n    \"email\": \"marissa.head@bolax.com\",\n    \"phone\": \"+1 (919) 411-2094\",\n    \"address\": \"224 Tampa Court, Harleigh, Virgin Islands, 2507\",\n    \"about\": \"Nulla elit do ullamco nulla. Fugiat incididunt culpa deserunt deserunt dolore nisi commodo. Labore exercitation pariatur aliqua id id duis.\",\n    \"registered\": \"Saturday, April 9, 2016 11:53 PM\",\n    \"latitude\": \"12.624239\",\n    \"longitude\": \"-108.119849\",\n    \"tags\": [\n      \"labore\",\n      \"proident\",\n      \"labore\",\n      \"eiusmod\",\n      \"aute\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Chambers Lester\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Mckenzie Mcdonald\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Benson Clark\"\n      }\n    ],\n    \"greeting\": \"Hello, Marissa! You have 6 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0cbbf466925baae126\",\n    \"index\": 55,\n    \"guid\": \"44384d90-908a-44f7-ac6a-f3e4e9ce3541\",\n    \"isActive\": false,\n    \"balance\": \"$2,707.40\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 33,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Avila\",\n      \"last\": \"Pena\"\n    },\n    \"company\": \"TERASCAPE\",\n    \"email\": \"avila.pena@terascape.tv\",\n    \"phone\": \"+1 (973) 462-2068\",\n    \"address\": \"393 Story Court, Veguita, Utah, 6716\",\n    \"about\": \"Reprehenderit nostrud laborum duis excepteur adipisicing voluptate dolor. Quis aliqua et laborum officia dolor nostrud laborum est. Excepteur aliquip ipsum id eiusmod esse laboris nostrud in excepteur est. Non eiusmod esse non ut.\",\n    \"registered\": \"Tuesday, August 11, 2015 11:07 PM\",\n    \"latitude\": \"-40.827485\",\n    \"longitude\": \"159.48116\",\n    \"tags\": [\n      \"et\",\n      \"culpa\",\n      \"cupidatat\",\n      \"ut\",\n      \"sunt\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Boone Goodman\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Jenny Weeks\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Tracy Franklin\"\n      }\n    ],\n    \"greeting\": \"Hello, Avila! You have 10 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0c0224b3b46507d00d\",\n    \"index\": 56,\n    \"guid\": \"7631262c-7af8-4caf-9dd6-9882b20e9ccf\",\n    \"isActive\": true,\n    \"balance\": \"$3,127.77\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 24,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Graciela\",\n      \"last\": \"Vang\"\n    },\n    \"company\": \"EARWAX\",\n    \"email\": \"graciela.vang@earwax.net\",\n    \"phone\": \"+1 (810) 450-2884\",\n    \"address\": \"620 Tech Place, Glendale, Connecticut, 369\",\n    \"about\": \"Cillum laboris elit laboris irure ex aliquip amet aute commodo ea ea sunt nostrud irure. Ex incididunt anim nostrud excepteur in dolore aliqua laboris incididunt consequat officia dolore. Aliqua aliqua minim elit pariatur nulla occaecat aliquip consectetur mollit sit elit proident.\",\n    \"registered\": \"Thursday, September 1, 2016 6:03 PM\",\n    \"latitude\": \"-22.465372\",\n    \"longitude\": \"174.750198\",\n    \"tags\": [\n      \"irure\",\n      \"amet\",\n      \"ad\",\n      \"cupidatat\",\n      \"consequat\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Teresa Dickson\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Reyna Gonzales\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Carol Hunt\"\n      }\n    ],\n    \"greeting\": \"Hello, Graciela! You have 9 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c44df164b323518f0\",\n    \"index\": 57,\n    \"guid\": \"21cf0aae-e2fd-4987-a12a-df9845dfe256\",\n    \"isActive\": true,\n    \"balance\": \"$2,129.83\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 38,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Kaye\",\n      \"last\": \"Whitehead\"\n    },\n    \"company\": \"TALKOLA\",\n    \"email\": \"kaye.whitehead@talkola.org\",\n    \"phone\": \"+1 (998) 495-2808\",\n    \"address\": \"933 Sands Street, Frystown, Wisconsin, 9993\",\n    \"about\": \"Laborum id id esse fugiat eu consectetur non nostrud eu sint. Enim dolor aliquip ut elit et. Officia labore labore duis veniam reprehenderit. Ut id nulla minim adipisicing laboris deserunt pariatur. Laborum aute magna laboris quis.\",\n    \"registered\": \"Friday, May 2, 2014 8:39 PM\",\n    \"latitude\": \"5.18003\",\n    \"longitude\": \"-138.228885\",\n    \"tags\": [\n      \"elit\",\n      \"labore\",\n      \"id\",\n      \"nisi\",\n      \"pariatur\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Leona Soto\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Cunningham Blankenship\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Campbell Turner\"\n      }\n    ],\n    \"greeting\": \"Hello, Kaye! You have 10 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0ca827218ce430db2f\",\n    \"index\": 58,\n    \"guid\": \"f1cc09e1-4502-4b54-8aa6-272b93335375\",\n    \"isActive\": true,\n    \"balance\": \"$1,635.66\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 38,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Vickie\",\n      \"last\": \"Walsh\"\n    },\n    \"company\": \"ZORK\",\n    \"email\": \"vickie.walsh@zork.me\",\n    \"phone\": \"+1 (966) 458-2157\",\n    \"address\": \"770 Flatlands Avenue, Marion, Oklahoma, 5819\",\n    \"about\": \"Velit laboris sint velit elit esse est commodo est officia. Sit do ut eiusmod duis ex ullamco eiusmod proident esse est cupidatat aliqua pariatur veniam. Culpa incididunt Lorem ea ut. Sint nostrud quis et tempor magna voluptate. Aliqua ea irure ad sint. Irure non nostrud non in ad officia officia laboris nostrud eiusmod. Labore commodo sit consequat velit ipsum culpa tempor.\",\n    \"registered\": \"Friday, June 24, 2016 6:10 AM\",\n    \"latitude\": \"-21.293579\",\n    \"longitude\": \"-171.238054\",\n    \"tags\": [\n      \"quis\",\n      \"dolore\",\n      \"sunt\",\n      \"sint\",\n      \"ut\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Kristie Davenport\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Sandy Foreman\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Eve Patel\"\n      }\n    ],\n    \"greeting\": \"Hello, Vickie! You have 7 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0ca320d90456f0650c\",\n    \"index\": 59,\n    \"guid\": \"7bfed2cf-dbf1-461b-949a-42c300172e0f\",\n    \"isActive\": false,\n    \"balance\": \"$3,809.61\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 22,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Travis\",\n      \"last\": \"Carrillo\"\n    },\n    \"company\": \"INTERODEO\",\n    \"email\": \"travis.carrillo@interodeo.ca\",\n    \"phone\": \"+1 (820) 527-3349\",\n    \"address\": \"878 Dekalb Avenue, Jeff, Alaska, 4205\",\n    \"about\": \"Sit aliqua aliqua eiusmod aliquip ullamco amet nulla fugiat anim dolor est cillum eiusmod. Proident dolor consectetur officia voluptate do tempor aliquip qui minim. Dolore culpa magna nulla irure. Est ad laborum deserunt amet commodo esse officia incididunt. Sit aute laborum aliqua id excepteur sunt ipsum cupidatat commodo exercitation proident.\",\n    \"registered\": \"Saturday, November 7, 2015 11:49 PM\",\n    \"latitude\": \"59.311122\",\n    \"longitude\": \"38.059303\",\n    \"tags\": [\n      \"ex\",\n      \"elit\",\n      \"voluptate\",\n      \"consectetur\",\n      \"Lorem\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Velez Daniel\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Mae Burris\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Henson Francis\"\n      }\n    ],\n    \"greeting\": \"Hello, Travis! You have 10 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0c4d57f9870f521abc\",\n    \"index\": 60,\n    \"guid\": \"6de5e184-4e24-41c1-8071-7fbd2fc184f0\",\n    \"isActive\": false,\n    \"balance\": \"$3,692.96\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 33,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Elvia\",\n      \"last\": \"Haynes\"\n    },\n    \"company\": \"OPTICOM\",\n    \"email\": \"elvia.haynes@opticom.biz\",\n    \"phone\": \"+1 (918) 418-2771\",\n    \"address\": \"646 Quincy Street, Fedora, Federated States Of Micronesia, 1232\",\n    \"about\": \"Occaecat officia amet ipsum ut mollit. Eu qui tempor voluptate sint pariatur id Lorem eiusmod veniam magna sint qui esse proident. Reprehenderit ut est sit ad tempor sit magna veniam consectetur. Id quis aliquip occaecat sint excepteur in nisi voluptate laborum ad anim. Id Lorem in incididunt ut culpa culpa duis aliqua mollit adipisicing.\",\n    \"registered\": \"Monday, November 16, 2015 2:49 AM\",\n    \"latitude\": \"-82.030922\",\n    \"longitude\": \"-160.684246\",\n    \"tags\": [\n      \"esse\",\n      \"minim\",\n      \"nulla\",\n      \"eiusmod\",\n      \"est\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Mcdowell Maxwell\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Dawson Gay\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Herminia Leach\"\n      }\n    ],\n    \"greeting\": \"Hello, Elvia! You have 8 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c62a1b2744f05b1a7\",\n    \"index\": 61,\n    \"guid\": \"fe9f08a4-cfe5-4cdb-b2e4-9ecfa54d873b\",\n    \"isActive\": false,\n    \"balance\": \"$3,807.03\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 30,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Blackwell\",\n      \"last\": \"Benton\"\n    },\n    \"company\": \"TUBESYS\",\n    \"email\": \"blackwell.benton@tubesys.co.uk\",\n    \"phone\": \"+1 (897) 452-2584\",\n    \"address\": \"343 Dupont Street, Blodgett, Iowa, 509\",\n    \"about\": \"Ut anim consectetur enim officia. Mollit anim et proident pariatur exercitation consectetur exercitation incididunt. Ullamco pariatur sit enim velit minim aliquip.\",\n    \"registered\": \"Thursday, September 25, 2014 1:31 AM\",\n    \"latitude\": \"50.446336\",\n    \"longitude\": \"-17.513084\",\n    \"tags\": [\n      \"ipsum\",\n      \"quis\",\n      \"et\",\n      \"labore\",\n      \"est\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Henrietta Santana\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Carla Cobb\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Nita Durham\"\n      }\n    ],\n    \"greeting\": \"Hello, Blackwell! You have 7 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0ce6f3ef88019d28bc\",\n    \"index\": 62,\n    \"guid\": \"62666a91-4a93-404c-bbce-170ab0668ee7\",\n    \"isActive\": false,\n    \"balance\": \"$3,792.15\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 40,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Stanton\",\n      \"last\": \"Potter\"\n    },\n    \"company\": \"UTARIAN\",\n    \"email\": \"stanton.potter@utarian.us\",\n    \"phone\": \"+1 (965) 569-3822\",\n    \"address\": \"498 Holmes Lane, Belgreen, Puerto Rico, 1728\",\n    \"about\": \"Ullamco esse sunt aliquip incididunt velit mollit aute. Et est eiusmod commodo aliqua cillum sint nostrud in dolor labore fugiat deserunt aliquip nostrud. Velit non incididunt consectetur tempor veniam minim. Magna enim minim dolore labore do magna ea pariatur tempor anim.\",\n    \"registered\": \"Thursday, November 20, 2014 7:07 PM\",\n    \"latitude\": \"13.26351\",\n    \"longitude\": \"-169.826766\",\n    \"tags\": [\n      \"dolore\",\n      \"commodo\",\n      \"ex\",\n      \"anim\",\n      \"commodo\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Jillian Burnett\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Evangelina Blackburn\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Castro Nolan\"\n      }\n    ],\n    \"greeting\": \"Hello, Stanton! You have 6 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0c66bd8e3191775b81\",\n    \"index\": 63,\n    \"guid\": \"babde8c1-773d-4f96-aeb9-bcf68d233204\",\n    \"isActive\": true,\n    \"balance\": \"$2,096.56\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 23,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Kerry\",\n      \"last\": \"Barnes\"\n    },\n    \"company\": \"PYRAMIS\",\n    \"email\": \"kerry.barnes@pyramis.name\",\n    \"phone\": \"+1 (959) 455-2215\",\n    \"address\": \"676 Roder Avenue, Eden, Kansas, 7602\",\n    \"about\": \"Et non et mollit velit proident exercitation velit elit cillum voluptate. Lorem reprehenderit nulla ex sunt adipisicing laborum excepteur in occaecat culpa fugiat enim fugiat proident. Duis minim deserunt aliqua ea. Ea minim ea eiusmod qui elit. Cupidatat quis commodo irure nisi.\",\n    \"registered\": \"Wednesday, October 21, 2015 7:02 PM\",\n    \"latitude\": \"77.232279\",\n    \"longitude\": \"153.606067\",\n    \"tags\": [\n      \"in\",\n      \"cupidatat\",\n      \"Lorem\",\n      \"consequat\",\n      \"laborum\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Veronica Beck\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Hull Velez\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Meghan Wiley\"\n      }\n    ],\n    \"greeting\": \"Hello, Kerry! You have 10 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c8e8776857572353b\",\n    \"index\": 64,\n    \"guid\": \"d34861fb-ed0e-4fd1-a315-c41e04ec60f3\",\n    \"isActive\": true,\n    \"balance\": \"$1,081.03\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 33,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Cameron\",\n      \"last\": \"Barber\"\n    },\n    \"company\": \"FRANSCENE\",\n    \"email\": \"cameron.barber@franscene.info\",\n    \"phone\": \"+1 (849) 484-3425\",\n    \"address\": \"446 Macon Street, Heil, North Dakota, 4499\",\n    \"about\": \"Consectetur elit eu labore adipisicing. Enim dolore sunt sint duis exercitation do quis enim veniam enim incididunt sint nostrud. Est veniam exercitation deserunt irure consectetur non esse ut eu aliqua ad. Occaecat commodo dolore magna labore ad id ipsum. Aliquip ad consectetur incididunt laborum exercitation cillum cillum minim commodo laboris proident in. Sit aliqua consequat sunt dolor veniam.\",\n    \"registered\": \"Wednesday, March 25, 2015 12:58 AM\",\n    \"latitude\": \"-0.119378\",\n    \"longitude\": \"-52.799542\",\n    \"tags\": [\n      \"laboris\",\n      \"ut\",\n      \"nostrud\",\n      \"nisi\",\n      \"tempor\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Young Bates\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Rhea Chen\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Alissa Morin\"\n      }\n    ],\n    \"greeting\": \"Hello, Cameron! You have 5 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c3a6582461fb89886\",\n    \"index\": 65,\n    \"guid\": \"4974e20f-b8df-4cbd-92ce-fe1cfc9c3657\",\n    \"isActive\": false,\n    \"balance\": \"$1,022.79\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 40,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Ashlee\",\n      \"last\": \"Randolph\"\n    },\n    \"company\": \"SLUMBERIA\",\n    \"email\": \"ashlee.randolph@slumberia.io\",\n    \"phone\": \"+1 (850) 590-3733\",\n    \"address\": \"539 Wortman Avenue, Rose, Northern Mariana Islands, 4842\",\n    \"about\": \"Lorem non commodo ea anim proident reprehenderit id consequat velit laborum deserunt. Veniam ad excepteur anim laboris laboris quis ad in dolor. Minim irure ad mollit reprehenderit dolor voluptate duis dolor incididunt cillum irure.\",\n    \"registered\": \"Saturday, March 28, 2015 2:02 AM\",\n    \"latitude\": \"45.475368\",\n    \"longitude\": \"-140.783475\",\n    \"tags\": [\n      \"nulla\",\n      \"ea\",\n      \"eiusmod\",\n      \"adipisicing\",\n      \"ea\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Martin Abbott\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Franco Munoz\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Briggs Briggs\"\n      }\n    ],\n    \"greeting\": \"Hello, Ashlee! You have 8 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c9b9a0708efa913ae\",\n    \"index\": 66,\n    \"guid\": \"d7e3871b-05bc-4db9-8ebb-aa136e9349a1\",\n    \"isActive\": false,\n    \"balance\": \"$1,282.07\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 24,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Karin\",\n      \"last\": \"Mcintyre\"\n    },\n    \"company\": \"QIAO\",\n    \"email\": \"karin.mcintyre@qiao.com\",\n    \"phone\": \"+1 (827) 522-3528\",\n    \"address\": \"476 Kings Place, Rowe, Montana, 1003\",\n    \"about\": \"Anim Lorem irure commodo sunt culpa proident velit quis consectetur. Et qui irure et ullamco ut ipsum. In sit dolor cupidatat dolore veniam sunt aliquip elit aliquip incididunt commodo eu in. Quis labore adipisicing officia eu mollit veniam officia ipsum excepteur dolor.\",\n    \"registered\": \"Sunday, February 9, 2014 3:39 PM\",\n    \"latitude\": \"-50.934733\",\n    \"longitude\": \"80.069751\",\n    \"tags\": [\n      \"non\",\n      \"aliqua\",\n      \"deserunt\",\n      \"exercitation\",\n      \"sint\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Bradshaw Vega\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Imelda Barnett\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Margaret Cross\"\n      }\n    ],\n    \"greeting\": \"Hello, Karin! You have 5 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0c5e44b5027bec1ac0\",\n    \"index\": 67,\n    \"guid\": \"4be33215-4b35-4079-9831-c2632a0bfe28\",\n    \"isActive\": false,\n    \"balance\": \"$2,900.86\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 38,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Rowe\",\n      \"last\": \"Hogan\"\n    },\n    \"company\": \"ISOSURE\",\n    \"email\": \"rowe.hogan@isosure.tv\",\n    \"phone\": \"+1 (925) 401-2733\",\n    \"address\": \"978 Ditmas Avenue, Herald, Ohio, 4322\",\n    \"about\": \"Nostrud commodo deserunt Lorem magna esse nulla eu cillum. Aliquip aute est laborum enim ut irure enim exercitation anim ipsum minim cillum. Qui commodo labore veniam et anim proident anim. Aliquip ut veniam consectetur amet mollit reprehenderit qui.\",\n    \"registered\": \"Wednesday, April 13, 2016 6:04 AM\",\n    \"latitude\": \"38.442477\",\n    \"longitude\": \"-127.884805\",\n    \"tags\": [\n      \"et\",\n      \"occaecat\",\n      \"elit\",\n      \"tempor\",\n      \"sint\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Elnora Guzman\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Talley Townsend\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Chaney Harvey\"\n      }\n    ],\n    \"greeting\": \"Hello, Rowe! You have 8 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c9075ebf36eef4cb0\",\n    \"index\": 68,\n    \"guid\": \"6cc1ae86-7aa6-4c1f-8338-1cd7b3630ade\",\n    \"isActive\": true,\n    \"balance\": \"$3,381.58\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 27,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Ronda\",\n      \"last\": \"Parker\"\n    },\n    \"company\": \"BOILCAT\",\n    \"email\": \"ronda.parker@boilcat.net\",\n    \"phone\": \"+1 (888) 404-2571\",\n    \"address\": \"836 Java Street, Detroit, Oregon, 9319\",\n    \"about\": \"Fugiat quis est id anim. Enim nostrud tempor esse in officia enim deserunt. Nisi anim adipisicing nostrud occaecat laboris occaecat. Qui laborum ut exercitation culpa aute veniam exercitation tempor qui adipisicing minim. Dolor ex voluptate pariatur aliquip dolor cillum laborum est. Ex adipisicing mollit ea occaecat ut eu.\",\n    \"registered\": \"Friday, August 12, 2016 6:40 AM\",\n    \"latitude\": \"-67.552112\",\n    \"longitude\": \"-148.920666\",\n    \"tags\": [\n      \"enim\",\n      \"occaecat\",\n      \"ex\",\n      \"est\",\n      \"magna\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Ora Cannon\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Mcintosh Carter\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Glenn Foley\"\n      }\n    ],\n    \"greeting\": \"Hello, Ronda! You have 6 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0cd164eec1663b2d49\",\n    \"index\": 69,\n    \"guid\": \"03ba15d9-ec37-4820-9833-5191c760dabe\",\n    \"isActive\": false,\n    \"balance\": \"$3,202.56\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 33,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Gallegos\",\n      \"last\": \"Preston\"\n    },\n    \"company\": \"VETRON\",\n    \"email\": \"gallegos.preston@vetron.org\",\n    \"phone\": \"+1 (923) 570-3649\",\n    \"address\": \"327 Irvington Place, Snelling, Arizona, 5946\",\n    \"about\": \"Anim aliquip deserunt commodo id incididunt mollit cillum et fugiat ullamco magna sint fugiat. Minim qui ea sint commodo eiusmod velit cupidatat excepteur eu. Enim cillum est sint incididunt. Culpa deserunt ex sunt labore voluptate. Ad laboris in pariatur dolore laboris dolor duis quis esse do sit velit pariatur dolore.\",\n    \"registered\": \"Saturday, July 23, 2016 4:24 AM\",\n    \"latitude\": \"23.3067\",\n    \"longitude\": \"26.605291\",\n    \"tags\": [\n      \"adipisicing\",\n      \"aute\",\n      \"consequat\",\n      \"fugiat\",\n      \"elit\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Raquel Dillard\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Conway Cruz\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Hope Woods\"\n      }\n    ],\n    \"greeting\": \"Hello, Gallegos! You have 7 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c0282495ae724dd00\",\n    \"index\": 70,\n    \"guid\": \"e4e4b345-ccb8-46bd-ad7e-25905e3f887d\",\n    \"isActive\": false,\n    \"balance\": \"$2,592.79\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 28,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Scott\",\n      \"last\": \"Anderson\"\n    },\n    \"company\": \"SCENTRIC\",\n    \"email\": \"scott.anderson@scentric.me\",\n    \"phone\": \"+1 (869) 517-3912\",\n    \"address\": \"408 Caton Place, Nadine, Vermont, 538\",\n    \"about\": \"Do elit sunt eiusmod anim anim eiusmod ex. Ipsum officia velit ipsum non minim enim. Enim culpa nostrud occaecat voluptate sit minim consectetur sit ut eiusmod. Magna voluptate fugiat occaecat id et eiusmod voluptate. Dolore consequat fugiat quis ipsum non deserunt ex culpa ut aliqua officia.\",\n    \"registered\": \"Wednesday, February 25, 2015 11:02 AM\",\n    \"latitude\": \"88.07987\",\n    \"longitude\": \"157.464013\",\n    \"tags\": [\n      \"dolore\",\n      \"sint\",\n      \"amet\",\n      \"voluptate\",\n      \"nulla\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Browning Brock\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Goldie Greer\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Bright Clayton\"\n      }\n    ],\n    \"greeting\": \"Hello, Scott! You have 9 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c997d595f28bed4b2\",\n    \"index\": 71,\n    \"guid\": \"2e81e82f-097d-4ed9-816b-678f7b5f28f4\",\n    \"isActive\": true,\n    \"balance\": \"$1,316.05\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 23,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Clarice\",\n      \"last\": \"York\"\n    },\n    \"company\": \"SOPRANO\",\n    \"email\": \"clarice.york@soprano.ca\",\n    \"phone\": \"+1 (822) 485-3017\",\n    \"address\": \"428 Midwood Street, Witmer, California, 4690\",\n    \"about\": \"Exercitation adipisicing aute commodo commodo exercitation. Aliquip aliquip minim sit anim cillum. Cupidatat veniam adipisicing mollit duis exercitation eu consequat non nulla proident. Occaecat sunt sunt consectetur occaecat veniam consectetur do minim. Adipisicing reprehenderit eu laboris sunt minim fugiat enim est proident duis consequat laboris ea ut. Officia dolor incididunt exercitation dolore et ut veniam. Minim est incididunt ullamco quis proident velit esse et est Lorem.\",\n    \"registered\": \"Friday, October 24, 2014 8:31 AM\",\n    \"latitude\": \"50.680453\",\n    \"longitude\": \"-22.267809\",\n    \"tags\": [\n      \"esse\",\n      \"dolore\",\n      \"deserunt\",\n      \"labore\",\n      \"officia\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Madge Battle\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Francis Dunn\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Rosario Irwin\"\n      }\n    ],\n    \"greeting\": \"Hello, Clarice! You have 7 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0ce47eee37facdfcc8\",\n    \"index\": 72,\n    \"guid\": \"f7880df6-e31a-42ff-971a-82d7ca76bc14\",\n    \"isActive\": true,\n    \"balance\": \"$2,569.05\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 22,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Madeleine\",\n      \"last\": \"Mann\"\n    },\n    \"company\": \"PHORMULA\",\n    \"email\": \"madeleine.mann@phormula.biz\",\n    \"phone\": \"+1 (839) 443-3345\",\n    \"address\": \"948 McKibben Street, Wyoming, Idaho, 2596\",\n    \"about\": \"Amet eiusmod quis sit veniam irure eiusmod eiusmod et. Amet ex consectetur velit aliqua fugiat excepteur commodo in non do anim proident enim. Do nulla minim id cillum cillum non non proident laborum ullamco in nostrud excepteur.\",\n    \"registered\": \"Monday, September 7, 2015 9:57 AM\",\n    \"latitude\": \"1.417271\",\n    \"longitude\": \"14.344005\",\n    \"tags\": [\n      \"minim\",\n      \"aliqua\",\n      \"aliqua\",\n      \"velit\",\n      \"nisi\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Carole Bond\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Diann Vaughan\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Morgan Rojas\"\n      }\n    ],\n    \"greeting\": \"Hello, Madeleine! You have 10 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c49e7bdd7ed590e84\",\n    \"index\": 73,\n    \"guid\": \"7bf69708-15f1-4d35-b71e-17c43001af61\",\n    \"isActive\": false,\n    \"balance\": \"$3,449.63\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 29,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Riley\",\n      \"last\": \"Moses\"\n    },\n    \"company\": \"COMDOM\",\n    \"email\": \"riley.moses@comdom.co.uk\",\n    \"phone\": \"+1 (855) 426-2672\",\n    \"address\": \"556 Richardson Street, Grazierville, Mississippi, 7295\",\n    \"about\": \"Ullamco do reprehenderit duis mollit ad duis aute qui reprehenderit ipsum qui in eiusmod. Officia culpa cillum cupidatat irure ipsum id occaecat. Enim do in dolor veniam laborum dolor est reprehenderit occaecat et minim laboris. Duis sint voluptate deserunt cillum fugiat velit irure. Lorem ullamco laboris reprehenderit dolor aute tempor excepteur. In elit enim aliquip ut in. Nulla et tempor ut mollit reprehenderit dolore ullamco veniam nisi minim.\",\n    \"registered\": \"Wednesday, March 18, 2015 5:28 AM\",\n    \"latitude\": \"-17.956865\",\n    \"longitude\": \"60.984652\",\n    \"tags\": [\n      \"quis\",\n      \"elit\",\n      \"excepteur\",\n      \"incididunt\",\n      \"sint\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Jana Knowles\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Dotson Calderon\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Stacy Winters\"\n      }\n    ],\n    \"greeting\": \"Hello, Riley! You have 10 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c4e5dea19708856e5\",\n    \"index\": 74,\n    \"guid\": \"cf4a7bf9-7a0e-4810-b5f5-23dd2bea6a9a\",\n    \"isActive\": true,\n    \"balance\": \"$1,365.62\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 24,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Emilia\",\n      \"last\": \"Sanford\"\n    },\n    \"company\": \"AQUAZURE\",\n    \"email\": \"emilia.sanford@aquazure.us\",\n    \"phone\": \"+1 (812) 451-2498\",\n    \"address\": \"649 Guernsey Street, Wedgewood, Alabama, 7660\",\n    \"about\": \"Amet cillum esse exercitation enim tempor ut est amet ad anim. Cillum veniam exercitation sit dolor velit irure tempor anim amet quis aliquip. Aliqua dolor cupidatat culpa consequat dolor in qui est tempor aliqua dolor. Mollit tempor dolor ad nisi Lorem anim adipisicing deserunt tempor. Tempor elit eiusmod dolor reprehenderit sint consequat aliqua incididunt dolore aliqua proident exercitation officia. Et velit tempor minim voluptate. Veniam dolor labore laboris ad quis minim tempor est ad cillum nulla.\",\n    \"registered\": \"Thursday, May 5, 2016 10:20 PM\",\n    \"latitude\": \"66.972869\",\n    \"longitude\": \"26.31688\",\n    \"tags\": [\n      \"eiusmod\",\n      \"sit\",\n      \"occaecat\",\n      \"nostrud\",\n      \"nisi\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Desiree Norman\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Carlson Meadows\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Miranda Gutierrez\"\n      }\n    ],\n    \"greeting\": \"Hello, Emilia! You have 10 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0cbb2ae9fb9dc2c667\",\n    \"index\": 75,\n    \"guid\": \"f814f3d7-fd66-4061-81de-1097a56ddc95\",\n    \"isActive\": false,\n    \"balance\": \"$2,546.18\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 33,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Cristina\",\n      \"last\": \"Carver\"\n    },\n    \"company\": \"DOGNOSIS\",\n    \"email\": \"cristina.carver@dognosis.name\",\n    \"phone\": \"+1 (967) 552-2825\",\n    \"address\": \"293 Metrotech Courtr, Cawood, Illinois, 7748\",\n    \"about\": \"Incididunt nulla ut velit in tempor pariatur sit ipsum dolor do laborum elit nostrud ullamco. Quis voluptate minim qui eu enim consectetur. In duis excepteur aute nostrud exercitation proident.\",\n    \"registered\": \"Saturday, November 14, 2015 9:50 AM\",\n    \"latitude\": \"66.369831\",\n    \"longitude\": \"-113.48546\",\n    \"tags\": [\n      \"do\",\n      \"laborum\",\n      \"non\",\n      \"velit\",\n      \"sunt\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Dianna Armstrong\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Gena Stafford\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Clare Allison\"\n      }\n    ],\n    \"greeting\": \"Hello, Cristina! You have 10 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c6311ae4dab4a3f34\",\n    \"index\": 76,\n    \"guid\": \"13d0bb42-7310-486c-9717-a91fe51873e9\",\n    \"isActive\": false,\n    \"balance\": \"$2,712.28\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 31,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Rosa\",\n      \"last\": \"Cook\"\n    },\n    \"company\": \"MATRIXITY\",\n    \"email\": \"rosa.cook@matrixity.info\",\n    \"phone\": \"+1 (937) 505-2696\",\n    \"address\": \"119 Madison Place, Dola, Rhode Island, 7030\",\n    \"about\": \"Veniam laborum mollit dolor nostrud anim ex duis eiusmod ex. Nostrud excepteur laborum officia eu eiusmod aute ea aute ea irure adipisicing. Ex consectetur laboris ex nisi ex esse ea sit cupidatat cupidatat anim labore nostrud Lorem. Esse quis consectetur eiusmod officia sunt cupidatat esse ullamco esse eiusmod tempor enim laborum nulla. Esse occaecat Lorem amet laboris laborum reprehenderit eu eiusmod excepteur qui officia. Nisi irure reprehenderit adipisicing dolor ipsum pariatur sint pariatur.\",\n    \"registered\": \"Tuesday, September 29, 2015 7:15 PM\",\n    \"latitude\": \"47.166257\",\n    \"longitude\": \"48.962919\",\n    \"tags\": [\n      \"sint\",\n      \"reprehenderit\",\n      \"aute\",\n      \"velit\",\n      \"excepteur\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Nellie Mooney\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Bridget Rocha\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Rosie Neal\"\n      }\n    ],\n    \"greeting\": \"Hello, Rosa! You have 9 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0c3f6b67c114a34891\",\n    \"index\": 77,\n    \"guid\": \"a72c60f4-ea12-47ca-a738-dfc886a23fb3\",\n    \"isActive\": false,\n    \"balance\": \"$3,863.37\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 37,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Hallie\",\n      \"last\": \"Cherry\"\n    },\n    \"company\": \"MAXIMIND\",\n    \"email\": \"hallie.cherry@maximind.io\",\n    \"phone\": \"+1 (883) 445-2642\",\n    \"address\": \"816 Bridgewater Street, Worcester, New Mexico, 3552\",\n    \"about\": \"Ut id cillum mollit excepteur sunt adipisicing sint do nostrud velit nisi Lorem. Incididunt amet tempor aliqua ad. Consequat veniam non exercitation veniam occaecat eu reprehenderit excepteur. In dolor ex sunt deserunt cupidatat in consequat est commodo excepteur ea. Eu ex qui ea exercitation proident est cillum aliqua pariatur amet in anim deserunt.\",\n    \"registered\": \"Wednesday, December 16, 2015 1:09 AM\",\n    \"latitude\": \"-22.963385\",\n    \"longitude\": \"-18.88522\",\n    \"tags\": [\n      \"ad\",\n      \"tempor\",\n      \"adipisicing\",\n      \"ea\",\n      \"consequat\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Karla Waters\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Salinas Duran\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Juliette Bell\"\n      }\n    ],\n    \"greeting\": \"Hello, Hallie! You have 6 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0caf58b7c09ccbbfe0\",\n    \"index\": 78,\n    \"guid\": \"a2136e10-0d46-4e2a-9d45-1ad4a7fb6150\",\n    \"isActive\": false,\n    \"balance\": \"$1,715.46\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 37,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Hill\",\n      \"last\": \"Adams\"\n    },\n    \"company\": \"UNI\",\n    \"email\": \"hill.adams@uni.com\",\n    \"phone\": \"+1 (887) 533-2728\",\n    \"address\": \"341 Union Avenue, Tyro, Marshall Islands, 6939\",\n    \"about\": \"Lorem excepteur cupidatat dolor voluptate eu et reprehenderit id nisi eiusmod tempor. Occaecat et reprehenderit tempor est reprehenderit magna elit eu do pariatur laborum dolore fugiat do. Occaecat dolor mollit ea sunt ex ea sint eu culpa irure enim nostrud. Ipsum eu anim elit quis officia anim do laborum velit sunt tempor in. Quis id veniam laborum sint in dolor do.\",\n    \"registered\": \"Thursday, August 20, 2015 6:41 PM\",\n    \"latitude\": \"49.668798\",\n    \"longitude\": \"48.122152\",\n    \"tags\": [\n      \"deserunt\",\n      \"labore\",\n      \"ea\",\n      \"reprehenderit\",\n      \"dolore\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Dixie Reyes\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Randall Mason\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Morrison Church\"\n      }\n    ],\n    \"greeting\": \"Hello, Hill! You have 7 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0c120ddc3a2597b83c\",\n    \"index\": 79,\n    \"guid\": \"5d601f0c-923c-41f2-86ec-2794c25ba07d\",\n    \"isActive\": true,\n    \"balance\": \"$1,368.60\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 26,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Megan\",\n      \"last\": \"Grant\"\n    },\n    \"company\": \"PURIA\",\n    \"email\": \"megan.grant@puria.tv\",\n    \"phone\": \"+1 (867) 456-2370\",\n    \"address\": \"688 Jewel Street, Stagecoach, Nebraska, 5430\",\n    \"about\": \"Reprehenderit et aliquip sunt nisi aliquip minim ut. Nostrud veniam velit laboris sit consectetur occaecat incididunt nulla officia adipisicing labore commodo qui anim. Sunt sunt mollit Lorem et do magna magna occaecat ea esse sit deserunt deserunt non. Est aliqua cupidatat veniam laboris amet officia ex consequat ullamco fugiat tempor laborum. Irure minim veniam deserunt est mollit. Do elit ipsum ea laborum magna amet id qui ea non veniam. Laboris Lorem ullamco dolore ad dolor anim tempor sint nisi reprehenderit proident tempor.\",\n    \"registered\": \"Wednesday, September 10, 2014 7:56 PM\",\n    \"latitude\": \"62.666152\",\n    \"longitude\": \"-103.092817\",\n    \"tags\": [\n      \"officia\",\n      \"sit\",\n      \"veniam\",\n      \"consequat\",\n      \"aliqua\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Rush Klein\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Bruce Bryan\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Vicki Griffith\"\n      }\n    ],\n    \"greeting\": \"Hello, Megan! You have 6 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0c923ee8af5787fc95\",\n    \"index\": 80,\n    \"guid\": \"27f1d835-7fe3-4ace-8f1c-e9d834d58b51\",\n    \"isActive\": true,\n    \"balance\": \"$1,120.98\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 22,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Kathie\",\n      \"last\": \"Adkins\"\n    },\n    \"company\": \"ZANILLA\",\n    \"email\": \"kathie.adkins@zanilla.net\",\n    \"phone\": \"+1 (827) 443-2787\",\n    \"address\": \"225 Stillwell Avenue, Clayville, Pennsylvania, 2403\",\n    \"about\": \"Cillum proident cupidatat consectetur aliqua amet ad. Dolor magna anim id aliquip deserunt occaecat do sunt ad est laborum consequat non esse. Nisi magna voluptate sunt magna exercitation officia cupidatat exercitation minim eu Lorem et. Occaecat ex velit fugiat reprehenderit veniam nulla. Consequat laboris eu reprehenderit dolore ea ut anim culpa ipsum ullamco. Amet pariatur dolor dolor non cillum enim elit id. Aute magna dolor ea ea fugiat.\",\n    \"registered\": \"Wednesday, January 8, 2014 9:01 AM\",\n    \"latitude\": \"-57.35919\",\n    \"longitude\": \"16.081565\",\n    \"tags\": [\n      \"mollit\",\n      \"ut\",\n      \"velit\",\n      \"eu\",\n      \"ea\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Martha Webb\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Quinn Hampton\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Phelps Reilly\"\n      }\n    ],\n    \"greeting\": \"Hello, Kathie! You have 9 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c0f013070624983d3\",\n    \"index\": 81,\n    \"guid\": \"d20f5d74-7e69-4a38-8afc-f9eafd9eb9b8\",\n    \"isActive\": true,\n    \"balance\": \"$2,486.01\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 28,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Reid\",\n      \"last\": \"Moon\"\n    },\n    \"company\": \"UPLINX\",\n    \"email\": \"reid.moon@uplinx.org\",\n    \"phone\": \"+1 (974) 588-2358\",\n    \"address\": \"437 Wythe Place, Bergoo, Maine, 2688\",\n    \"about\": \"Proident sit nostrud adipisicing fugiat laborum adipisicing commodo est. Eu voluptate dolor ad consequat cillum tempor excepteur. Veniam sit ullamco velit aliquip nulla nisi magna sunt commodo officia tempor mollit. Velit occaecat occaecat culpa in duis dolor enim aute incididunt ipsum adipisicing excepteur adipisicing in. Mollit duis incididunt sit deserunt tempor ullamco sit. Nisi et aliquip aliquip nisi ad cillum deserunt amet Lorem id minim in. Nisi ullamco labore consequat deserunt voluptate sunt nulla commodo quis dolore et do sunt dolor.\",\n    \"registered\": \"Wednesday, November 5, 2014 8:41 PM\",\n    \"latitude\": \"-57.701702\",\n    \"longitude\": \"98.460257\",\n    \"tags\": [\n      \"culpa\",\n      \"dolore\",\n      \"sunt\",\n      \"duis\",\n      \"sint\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Christina Brennan\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Joan Key\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Terrie Colon\"\n      }\n    ],\n    \"greeting\": \"Hello, Reid! You have 6 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0cd7d10e075f923c32\",\n    \"index\": 82,\n    \"guid\": \"eff18055-8039-4733-8e71-0e8161a9a37f\",\n    \"isActive\": true,\n    \"balance\": \"$1,703.03\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 27,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Myra\",\n      \"last\": \"Cooper\"\n    },\n    \"company\": \"LOTRON\",\n    \"email\": \"myra.cooper@lotron.me\",\n    \"phone\": \"+1 (833) 490-3199\",\n    \"address\": \"454 Argyle Road, Tilleda, West Virginia, 4341\",\n    \"about\": \"Dolor sint commodo est excepteur sunt labore consequat cillum et minim. Id proident id officia aute. Aliquip exercitation nulla esse ad id do adipisicing occaecat ipsum veniam veniam dolor. Voluptate tempor qui dolor ullamco consequat consequat cupidatat nisi elit et non qui non adipisicing. Excepteur consequat minim laborum minim nostrud mollit anim exercitation minim duis ullamco. Minim minim fugiat qui voluptate dolor dolore voluptate non consectetur nulla commodo exercitation do non. Ad commodo sit excepteur adipisicing do ex sunt minim laborum dolore non do elit in.\",\n    \"registered\": \"Friday, September 30, 2016 1:21 AM\",\n    \"latitude\": \"12.698327\",\n    \"longitude\": \"98.957272\",\n    \"tags\": [\n      \"et\",\n      \"esse\",\n      \"sint\",\n      \"incididunt\",\n      \"esse\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Angelia Roth\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Vasquez Cole\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Tanner Hayes\"\n      }\n    ],\n    \"greeting\": \"Hello, Myra! You have 10 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0c79029337f04c4f74\",\n    \"index\": 83,\n    \"guid\": \"10fb3103-bb73-42c0-8fac-3fe42121176e\",\n    \"isActive\": false,\n    \"balance\": \"$3,028.72\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 33,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Mccarty\",\n      \"last\": \"Workman\"\n    },\n    \"company\": \"TURNABOUT\",\n    \"email\": \"mccarty.workman@turnabout.ca\",\n    \"phone\": \"+1 (839) 409-3408\",\n    \"address\": \"138 Rutledge Street, Tivoli, South Dakota, 9041\",\n    \"about\": \"Culpa laborum ea magna nulla ipsum velit excepteur incididunt occaecat sit. Ullamco laborum voluptate dolor est proident ex culpa. Ex est sunt aliquip culpa in adipisicing mollit irure magna exercitation.\",\n    \"registered\": \"Thursday, December 18, 2014 10:46 AM\",\n    \"latitude\": \"3.78797\",\n    \"longitude\": \"-146.349265\",\n    \"tags\": [\n      \"ut\",\n      \"pariatur\",\n      \"ad\",\n      \"voluptate\",\n      \"qui\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Skinner Webster\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Atkins James\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Enid Meyer\"\n      }\n    ],\n    \"greeting\": \"Hello, Mccarty! You have 9 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0c642e9641a7bfbcee\",\n    \"index\": 84,\n    \"guid\": \"a774c4b8-b1d2-42c3-837a-52bce2aa7a94\",\n    \"isActive\": false,\n    \"balance\": \"$2,539.93\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 34,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Davenport\",\n      \"last\": \"Small\"\n    },\n    \"company\": \"ISOTRACK\",\n    \"email\": \"davenport.small@isotrack.biz\",\n    \"phone\": \"+1 (909) 521-2228\",\n    \"address\": \"646 Fleet Walk, Dale, Texas, 3140\",\n    \"about\": \"Non magna officia veniam velit nostrud elit. Nulla ad cupidatat quis aliquip ut. Adipisicing labore sint nostrud eu veniam sunt dolor consequat pariatur commodo consectetur magna sunt. Tempor dolor consectetur deserunt in quis id proident occaecat sunt. Est minim anim proident occaecat culpa ex minim mollit ea. Enim deserunt tempor ea eu nulla sunt veniam ad. Laborum cillum dolor proident voluptate id sint ut voluptate cillum anim incididunt laboris.\",\n    \"registered\": \"Friday, May 1, 2015 11:13 AM\",\n    \"latitude\": \"-49.904277\",\n    \"longitude\": \"-35.151297\",\n    \"tags\": [\n      \"anim\",\n      \"labore\",\n      \"Lorem\",\n      \"anim\",\n      \"cillum\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Emerson Gilmore\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Castillo Wright\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Marva Stone\"\n      }\n    ],\n    \"greeting\": \"Hello, Davenport! You have 10 unread messages.\",\n    \"favoriteFruit\": \"strawberry\"\n  },\n  {\n    \"_id\": \"580ddf0cf681ca1388d97783\",\n    \"index\": 85,\n    \"guid\": \"b5860a9d-45ff-47ef-bd87-e43adc9dc8d6\",\n    \"isActive\": true,\n    \"balance\": \"$1,578.95\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 29,\n    \"eyeColor\": \"brown\",\n    \"name\": {\n      \"first\": \"Reyes\",\n      \"last\": \"Dotson\"\n    },\n    \"company\": \"GRACKER\",\n    \"email\": \"reyes.dotson@gracker.co.uk\",\n    \"phone\": \"+1 (909) 410-2034\",\n    \"address\": \"739 Junius Street, Nutrioso, Missouri, 6051\",\n    \"about\": \"Esse magna Lorem enim nostrud quis sit minim et do elit. Culpa proident ipsum dolore aute do exercitation tempor eu. Reprehenderit ullamco sunt ad anim aliqua proident deserunt commodo dolor ad quis cillum cupidatat. Ea sint id aute adipisicing do anim minim exercitation labore id ullamco duis do sit. Laboris do in sunt esse anim cillum anim nulla eu do consequat aliquip culpa amet. Ipsum velit reprehenderit minim velit incididunt enim duis ut aliquip.\",\n    \"registered\": \"Wednesday, February 5, 2014 11:01 AM\",\n    \"latitude\": \"-11.383582\",\n    \"longitude\": \"102.513196\",\n    \"tags\": [\n      \"occaecat\",\n      \"consequat\",\n      \"ipsum\",\n      \"elit\",\n      \"voluptate\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Baker Bennett\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Franklin Nelson\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Wilda Hill\"\n      }\n    ],\n    \"greeting\": \"Hello, Reyes! You have 7 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0cc99c7d2e5622dbb5\",\n    \"index\": 86,\n    \"guid\": \"0a1d6c96-621e-4fe5-9f0f-ccba26458cf1\",\n    \"isActive\": false,\n    \"balance\": \"$1,760.99\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 25,\n    \"eyeColor\": \"green\",\n    \"name\": {\n      \"first\": \"Ratliff\",\n      \"last\": \"Kinney\"\n    },\n    \"company\": \"YURTURE\",\n    \"email\": \"ratliff.kinney@yurture.us\",\n    \"phone\": \"+1 (962) 417-2906\",\n    \"address\": \"832 Duryea Court, Moscow, New Hampshire, 7275\",\n    \"about\": \"Sint commodo ullamco proident ullamco esse elit Lorem anim veniam sunt veniam nulla elit. Tempor nulla culpa ut occaecat tempor elit aliqua ipsum. Anim nisi reprehenderit pariatur officia aute elit fugiat amet est incididunt. Irure excepteur id cillum exercitation esse officia dolor in amet id nostrud. Incididunt consectetur culpa tempor officia veniam aute eu incididunt duis tempor incididunt ex nisi irure. Tempor proident aute aliquip in aliqua Lorem cillum consequat exercitation et sint nulla minim. Cillum magna ut aliqua eu qui tempor culpa laborum amet magna ullamco.\",\n    \"registered\": \"Tuesday, April 29, 2014 6:13 AM\",\n    \"latitude\": \"-13.301568\",\n    \"longitude\": \"-112.255058\",\n    \"tags\": [\n      \"labore\",\n      \"sit\",\n      \"excepteur\",\n      \"laboris\",\n      \"nulla\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Ingrid Rutledge\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Simpson Barlow\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Lottie Hickman\"\n      }\n    ],\n    \"greeting\": \"Hello, Ratliff! You have 6 unread messages.\",\n    \"favoriteFruit\": \"banana\"\n  },\n  {\n    \"_id\": \"580ddf0c461fe67eb5044e47\",\n    \"index\": 87,\n    \"guid\": \"4f264353-60c0-4324-af56-34d2f28e718a\",\n    \"isActive\": true,\n    \"balance\": \"$1,034.62\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 21,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Bettye\",\n      \"last\": \"Jenkins\"\n    },\n    \"company\": \"LUNCHPOD\",\n    \"email\": \"bettye.jenkins@lunchpod.name\",\n    \"phone\": \"+1 (873) 530-2152\",\n    \"address\": \"577 Oliver Street, Gallina, Hawaii, 2623\",\n    \"about\": \"Consectetur eiusmod aute ad duis nisi minim dolore ad do deserunt occaecat voluptate cupidatat. Sunt exercitation cillum deserunt veniam cillum. Lorem anim dolor occaecat elit amet. Magna reprehenderit laboris enim occaecat eiusmod Lorem duis sint nulla quis exercitation occaecat ipsum. Laborum Lorem magna ullamco enim ad id. Pariatur exercitation nisi cupidatat incididunt ipsum magna cillum laborum. Laboris exercitation excepteur adipisicing Lorem duis id ipsum fugiat nostrud labore culpa consequat ad.\",\n    \"registered\": \"Sunday, May 1, 2016 11:30 AM\",\n    \"latitude\": \"-2.873736\",\n    \"longitude\": \"108.897468\",\n    \"tags\": [\n      \"aliqua\",\n      \"sunt\",\n      \"ex\",\n      \"duis\",\n      \"commodo\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Leonard Kelley\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Penny Watson\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Roxanne French\"\n      }\n    ],\n    \"greeting\": \"Hello, Bettye! You have 5 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  },\n  {\n    \"_id\": \"580ddf0cf9dae7fe5522c648\",\n    \"index\": 88,\n    \"guid\": \"b47a7f45-3805-44d7-b455-67038c5466e8\",\n    \"isActive\": true,\n    \"balance\": \"$2,489.68\",\n    \"picture\": \"http://placehold.it/32x32\",\n    \"age\": 35,\n    \"eyeColor\": \"blue\",\n    \"name\": {\n      \"first\": \"Barr\",\n      \"last\": \"Pearson\"\n    },\n    \"company\": \"QNEKT\",\n    \"email\": \"barr.pearson@qnekt.info\",\n    \"phone\": \"+1 (914) 541-3874\",\n    \"address\": \"186 Robert Street, Ona, Georgia, 8717\",\n    \"about\": \"Anim ullamco mollit adipisicing sit tempor aute eiusmod adipisicing cupidatat laborum tempor reprehenderit pariatur. Reprehenderit ullamco aliqua laboris Lorem. Exercitation magna pariatur mollit ea ullamco et nostrud commodo laboris dolore. Esse do cupidatat pariatur esse in labore do et cillum reprehenderit incididunt. Ad mollit adipisicing proident culpa occaecat tempor et elit esse occaecat fugiat consectetur occaecat occaecat. Et adipisicing esse labore ullamco proident laboris consequat enim aliqua. Eu laboris et in voluptate.\",\n    \"registered\": \"Friday, September 5, 2014 6:22 PM\",\n    \"latitude\": \"55.604021\",\n    \"longitude\": \"-152.171415\",\n    \"tags\": [\n      \"occaecat\",\n      \"culpa\",\n      \"do\",\n      \"id\",\n      \"qui\"\n    ],\n    \"range\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9\n    ],\n    \"friends\": [\n      {\n        \"id\": 0,\n        \"name\": \"Kimberley Mckinney\"\n      },\n      {\n        \"id\": 1,\n        \"name\": \"Mosley Gardner\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Gay Garner\"\n      }\n    ],\n    \"greeting\": \"Hello, Barr! You have 6 unread messages.\",\n    \"favoriteFruit\": \"apple\"\n  }\n];\n\nexport const circularData = { bigArray };\ncircularData.c = circularData;\n"
  },
  {
    "path": "test/perf/send.spec.js",
    "content": "import 'babel-polyfill';\nimport expect from 'expect';\nimport { bigArray, bigString, circularData } from './data';\nimport { listenMessage } from '../utils/inject';\nimport '../../src/browser/extension/inject/pageScript';\n\nfunction test(title, data, maxTime = 100) {\n  it('should send ' + title, async() => {\n    const start = new Date();\n    await listenMessage(() => {\n      window.__REDUX_DEVTOOLS_EXTENSION__.send({ type: 'TEST_ACTION', data }, data);\n    });\n    const ms = new Date() - start;\n    // console.log(ms);\n    expect(ms).toBeLessThan(maxTime);\n  });\n}\n\ndescribe('Perf', () => {\n  test('a huge string', bigString);\n  test('a huge array', bigArray);\n  test('an object with circular references', circularData);\n});\n"
  },
  {
    "path": "test/utils/e2e.js",
    "content": "import webdriver from 'selenium-webdriver';\n\nexport const delay = time => new Promise(resolve => setTimeout(resolve, time));\n\nexport const switchMonitorTests = {\n  'should switch to Log Monitor': async function() {\n    await this.driver.findElement(webdriver.By.xpath('//div[text()=\"Inspector\"]')).click();\n    await delay(500); // Wait till menu is fully opened\n    await this.driver.findElement(webdriver.By.xpath('//div[text()=\"Log monitor\"]')).click();\n    await delay(500);\n    await this.driver.findElement(webdriver.By.xpath('//div[a[text()=\"Reset\"] and .//a[text()=\"Revert\"]]'));\n    await delay(500);\n  },\n\n  'should switch to Chart Monitor': async function() {\n    await this.driver.findElement(webdriver.By.xpath('//div[text()=\"Log monitor\"]')).click();\n    await delay(500); // Wait till menu is fully opened\n    await this.driver.findElement(webdriver.By.xpath('//div[text()=\"Chart\"]')).click();\n    await delay(500);\n    await this.driver.findElement(webdriver.By.xpath('//*[@class=\"nodeText\" and text()=\"state\"]'));\n    await delay(500); // Wait till menu is closed\n  },\n\n  'should switch back to Inspector Monitor': async function() {\n    await this.driver.findElement(webdriver.By.xpath('//div[text()=\"Chart\"]')).click();\n    await delay(1000); // Wait till menu is fully opened\n    await this.driver.findElement(webdriver.By.xpath('//div[text()=\"Inspector\"]')).click();\n    await delay(1500); // Wait till menu is closed\n  }\n};\n"
  },
  {
    "path": "test/utils/inject.js",
    "content": "export function insertScript(str) {\n  const s = window.document.createElement('script');\n  s.appendChild(document.createTextNode(str));\n  (document.head || document.documentElement).appendChild(s);\n}\n\nexport function listenMessage(f) {\n  return new Promise(resolve => {\n    const listener = event => {\n      const message = event.data;\n      window.removeEventListener('message', listener);\n      resolve(message);\n    };\n    window.addEventListener('message', listener);\n    if (f) f();\n  });\n}\n"
  },
  {
    "path": "webpack/base.config.js",
    "content": "import path from 'path';\nimport webpack from 'webpack';\nimport TerserPlugin from 'terser-webpack-plugin';\n\nconst extpath = path.join(__dirname, '../src/browser/extension/');\nconst mock = `${extpath}chromeAPIMock.js`;\n\nconst baseConfig = (params) => ({\n  // devtool: 'source-map',\n  mode: 'production',\n  entry: params.input || {\n    background: [ mock, `${extpath}background/index` ],\n    options: [ mock, `${extpath}options/index` ],\n    window: [ `${extpath}window/index` ],\n    remote: [ `${extpath}window/remote` ],\n    devpanel: [ mock, `${extpath}devpanel/index` ],\n    devtools: [ `${extpath}devtools/index` ],\n    content: [ mock, `${extpath}inject/contentScript` ],\n    pagewrap: [ `${extpath}inject/pageScriptWrap` ],\n    'redux-devtools-extension': [ `${extpath}inject/index`, `${extpath}inject/deprecatedWarn` ],\n    inject: [ `${extpath}inject/index`, `${extpath}inject/deprecatedWarn` ],\n    ...params.inputExtra\n  },\n  output: {\n    filename: '[name].bundle.js',\n    chunkFilename: '[id].chunk.js',\n    ...params.output\n  },\n  plugins: [\n    new webpack.DefinePlugin(params.globals),\n    ...(params.plugins ? params.plugins :\n      [\n        new webpack.optimize.ModuleConcatenationPlugin(),\n        new webpack.optimize.OccurrenceOrderPlugin()\n      ])\n  ],\n  optimization: {\n    minimizer: [\n      new TerserPlugin({\n        terserOptions: {\n          output: {\n            comments: false\n          }\n        },\n        // sourceMap: true,\n        cache: true,\n        parallel: true\n      })\n    ]\n  },\n  performance: {\n    hints: false\n  },\n  resolve: {\n    alias: {\n      app: path.join(__dirname, '../src/app'),\n      tmp: path.join(__dirname, '../build/tmp')\n    },\n    extensions: ['.js']\n  },\n  module: {\n    rules: [\n      ...(params.loaders ? params.loaders : [{\n        test: /\\.js$/,\n        use: 'babel-loader',\n        exclude: /(node_modules|tmp\\/page\\.bundle)/\n      }]),\n      {\n        test: /\\.css?$/,\n        use: ['style-loader', 'raw-loader'],\n      }\n    ]\n  }\n});\n\nexport default baseConfig;\n"
  },
  {
    "path": "webpack/dev.config.js",
    "content": "import path from 'path';\nimport webpack from 'webpack';\nimport baseConfig from './base.config';\n\nlet config = baseConfig({\n  inputExtra: { page: [ path.join(__dirname, '../src/browser/extension/inject/pageScript') ] },\n  output: { path: path.join(__dirname, '../dev/js') },\n  globals: {\n    'process.env': {\n      NODE_ENV: '\"development\"'\n    }\n  },\n  plugins: [\n    new webpack.NoEmitOnErrorsPlugin()\n  ]\n});\n\nconfig.watch = true;\n\nexport default config;\n"
  },
  {
    "path": "webpack/prod.config.js",
    "content": "import path from 'path';\nimport baseConfig from './base.config';\n\nexport default baseConfig({\n  output: { path: path.join(__dirname, '../build/extension/js') },\n  globals: {\n    'process.env': {\n      NODE_ENV: '\"production\"'\n    }\n  }\n});\n"
  },
  {
    "path": "webpack/replace/JsonpMainTemplate.runtime.js",
    "content": "/*\n\tMIT License http://www.opensource.org/licenses/mit-license.php\n\tAuthor Tobias Koppers @sokra\n*/\n/*globals hotAddUpdateChunk parentHotUpdateCallback document XMLHttpRequest $require$ $hotChunkFilename$ $hotMainFilename$ */\nmodule.exports = function() {\n\tfunction webpackHotUpdateCallback(chunkId, moreModules) { // eslint-disable-line no-unused-vars\n\t\thotAddUpdateChunk(chunkId, moreModules);\n\t\tif(parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules);\n\t}\n\n  var context = this;\n  function evalCode(code, context) {\n    return (function() { return eval(code); }).call(context);\n  }\n\n  context.hotDownloadUpdateChunk = function (chunkId) { // eslint-disable-line no-unused-vars\n    var src = __webpack_require__.p + \"\" + chunkId + \".\" + hotCurrentHash + \".hot-update.js\";\n    var request = new XMLHttpRequest();\n\n    request.onload = function() {\n\t\t\tevalCode(this.responseText, context);\n\t\t};\n    request.open(\"get\", src, true);\n    request.send();\n  }\n\n\tfunction hotDownloadManifest(callback) { // eslint-disable-line no-unused-vars\n\t\tif(typeof XMLHttpRequest === \"undefined\")\n\t\t\treturn callback(new Error(\"No browser support\"));\n\t\ttry {\n\t\t\tvar request = new XMLHttpRequest();\n\t\t\tvar requestPath = $require$.p + $hotMainFilename$;\n\t\t\trequest.open(\"GET\", requestPath, true);\n\t\t\trequest.timeout = 10000;\n\t\t\trequest.send(null);\n\t\t} catch(err) {\n\t\t\treturn callback(err);\n\t\t}\n\t\trequest.onreadystatechange = function() {\n\t\t\tif(request.readyState !== 4) return;\n\t\t\tif(request.status === 0) {\n\t\t\t\t// timeout\n\t\t\t\tcallback(new Error(\"Manifest request to \" + requestPath + \" timed out.\"));\n\t\t\t} else if(request.status === 404) {\n\t\t\t\t// no update available\n\t\t\t\tcallback();\n\t\t\t} else if(request.status !== 200 && request.status !== 304) {\n\t\t\t\t// other failure\n\t\t\t\tcallback(new Error(\"Manifest request to \" + requestPath + \" failed.\"));\n\t\t\t} else {\n\t\t\t\t// success\n\t\t\t\ttry {\n\t\t\t\t\tvar update = JSON.parse(request.responseText);\n\t\t\t\t} catch(e) {\n\t\t\t\t\tcallback(e);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcallback(null, update);\n\t\t\t}\n\t\t};\n\t}\n};\n"
  },
  {
    "path": "webpack/replace/log-apply-result.js",
    "content": "/*\n\tMIT License http://www.opensource.org/licenses/mit-license.php\n\tAuthor Tobias Koppers @sokra\n*/\nmodule.exports = function(updatedModules, renewedModules) {\n\tvar unacceptedModules = updatedModules.filter(function(moduleId) {\n\t\treturn renewedModules && renewedModules.indexOf(moduleId) < 0;\n\t});\n\n\tif(unacceptedModules.length > 0) {\n\t\tconsole.warn(\"[HMR] The following modules couldn't be hot updated: (They would need a full reload!)\");\n\t\tunacceptedModules.forEach(function(moduleId) {\n\t\t\tconsole.warn(\"[HMR]  - \" + moduleId);\n\t\t});\n\n\t\tif(chrome && chrome.runtime && chrome.runtime.reload) {\n\t\t\tconsole.warn(\"[HMR] extension reload\");\n\t\t\tchrome.runtime.reload();\n\t\t} else {\n\t\t\tconsole.warn(\"[HMR] Can't extension reload. not found chrome.runtime.reload.\");\n\t\t}\n\t}\n\n\tif(!renewedModules || renewedModules.length === 0) {\n\t\tconsole.log(\"[HMR] Nothing hot updated.\");\n\t} else {\n\t\tconsole.log(\"[HMR] Updated modules:\");\n\t\trenewedModules.forEach(function(moduleId) {\n\t\t\tconsole.log(\"[HMR]  - \" + moduleId);\n\t\t});\n\t}\n};\n"
  },
  {
    "path": "webpack/wrap.config.js",
    "content": "import path from 'path';\nimport baseConfig from './base.config';\n\nexport default baseConfig({\n  input: { page: [ path.join(__dirname, '../src/browser/extension/inject/pageScript') ] },\n  output: { path: path.join(__dirname, '../build/tmp') },\n  globals: {\n    'process.env': {\n      NODE_ENV: '\"production\"'\n    }\n  }\n});\n"
  }
]