├── .babelrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── README.md ├── app ├── _locales │ └── en │ │ └── messages.json ├── images │ ├── icon-128.png │ ├── icon-16.png │ ├── icon-19.png │ └── icon-38.png ├── manifest.json ├── options.html ├── popup.html ├── scripts │ ├── background.js │ ├── background_bundle.js │ ├── chromereload.js │ ├── content.js │ └── content_bundle.js └── styles │ └── main.css ├── devServer.js ├── gulpfile.js ├── package.json ├── src └── scripts │ ├── background.js │ ├── background │ └── index.js │ ├── content.js │ ├── content │ └── index.js │ ├── options.js │ ├── options │ ├── components │ │ └── Options.js │ └── index.js │ ├── popup.js │ ├── popup │ ├── components │ │ └── Popup.js │ └── index.js │ └── shared │ ├── actions │ └── chromeExtension.js │ ├── containers │ └── createContainer.js │ ├── helpers │ ├── createInitState.js │ └── getState.js │ ├── initStorage.js │ ├── reducers │ └── chromeExtension.js │ └── store │ └── configureStore.js ├── webpack.config.dev.js └── webpack.config.prod.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react"], 3 | "plugins": ["react-hot-loader/babel"] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.json] 15 | indent_size = 2 16 | 17 | # We recommend you to keep these unchanged 18 | end_of_line = lf 19 | charset = utf-8 20 | trim_trailing_whitespace = true 21 | insert_final_newline = true 22 | 23 | [*.md] 24 | trim_trailing_whitespace = false 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | temp 3 | .tmp 4 | dist 5 | .sass-cache 6 | app/bower_components 7 | test/bower_components 8 | package 9 | 10 | npm-debug.log 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Redux Chrome Extension 2 | 3 | **work in progress** 4 | 5 | 6 | A starter boilerplate for a Chrome Extension using Redux and React.js. 7 | It handles store syncing throughout the whole Extension. 8 | Uses React for Popup window UI. Console.log in every part of extensions for better debugging. 9 | Implemets Redux Counter example (https://github.com/rackt/redux/tree/master/examples/counter). 10 | 11 | 12 | https://developer.chrome.com/extensions/getstarted#unpacked 13 | 14 | ## Installation 15 | 16 | `npm i` 17 | 18 | ## Development 19 | 20 | 21 | `npm start` 22 | 23 | 24 | Browserify and Watchify are used for building scripts and it's blazing fast. For more details see: (https://github.com/gulpjs/gulp/blob/master/docs/recipes/fast-browserify-builds-with-watchify.md) 25 | - after running, please Reload just once manually to establish the connection for livereload 26 | 27 | 28 | 1. In Chrome open chrome://extensions/ 29 | 2. Select Developer mode 30 | 3. Click on Load unpacked extension 31 | 4. Add /dist folder 32 | 33 | 34 | - You can gen more info by reading comments /src/files 35 | - There is also distinction between which code belong to example a which code is React/Redux itself 36 | 37 | ##Schema 38 | 39 | Uses (https://developer.chrome.com/extensions/messaging) 40 | 41 | **Background Page** 42 | - gets persistent data for initial state from localStorage (options, user) 43 | - passes state to Popup Window and Content Scripts and Options 44 | - receives state updates from Popup Window, Content Scripts and Options 45 | - saves changes in persistent property to localStorage 46 | 47 | **Popup Window** 48 | - gets initial state from Background Page 49 | - dispatches state updates to Background Page (and optionally to Content Scripts) 50 | 51 | **Content Script** 52 | - gets initial state from Background Page 53 | - receives state updates from Popup window 54 | - dispatches state updates to Background Page (and optionally to the rest of Content Scripts) 55 | 56 | 57 | **Options Page** 58 | - gets initial state from Background Page 59 | - dispatches state updates to Background Page 60 | 61 | *** code for functionality that is in parenthesis was commented out, see src/content.index.js and src/popup/index.js *** 62 | 63 | 64 | ## Example App 65 | - There is an example counter application 66 | - Extension's code is located in /app folder 67 | - **Do not edit files in app/scripts/* directly, it will be overwritten** 68 | 69 | ## Releasing 70 | 71 | ```bash 72 | gulp release 73 | ``` 74 | 75 | You can find .zip packages in the /dist folder. 76 | 77 | ##Data passing 78 | If you understand the Schema above, you see that not every part of extension talk to each other. 79 | That's because of in certain cases it doesn't make sense to notify the part, that would otherwise load the whole state from Background Page again. 80 | 81 | 82 | It's not possible to have Content Script and Popup Window active both in the same time, since Popup Window is autoclosing when it loses focus, and after each invoking it's fetching the state from Background Page. 83 | So it doesn't make sense to send any changes from Content Script to Popup Window. Same with Popup Window and Options. 84 | 85 | 86 | On the other hand Content Script is living behind every tab all the time, so we need to propagate store changes in Popop Window to Content Scripts immediately. 87 | 88 | 89 | ##Storage 90 | All the data we need to keep stored in extension after closing Chrome or disabling Extension are being saved to localStorage. 91 | 92 | 93 | You can modify localStorage indirectly by changing `state.persistent` property, like Options page in example. 94 | 95 | 96 | 97 | ##TODO 98 | 99 | - Hot loading of Reducers 100 | 101 | - Testing 102 | 103 | - Issues and pull requests are always welcome!! 104 | 105 | 106 | Thanks to 107 | 108 | https://github.com/schovi/webpack-chrome-extension 109 | -------------------------------------------------------------------------------- /app/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Redux-chrome-extension", 4 | "description": "The name of the application" 5 | }, 6 | "appDescription": { 7 | "message": "A starter boilerplate for a Chrome Extension using Redux and React.js.", 8 | "description": "The description of the application" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/images/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladas-larry/redux-chrome-extension/b2035ed97eff27b08bd428421c78da56d24d445b/app/images/icon-128.png -------------------------------------------------------------------------------- /app/images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladas-larry/redux-chrome-extension/b2035ed97eff27b08bd428421c78da56d24d445b/app/images/icon-16.png -------------------------------------------------------------------------------- /app/images/icon-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladas-larry/redux-chrome-extension/b2035ed97eff27b08bd428421c78da56d24d445b/app/images/icon-19.png -------------------------------------------------------------------------------- /app/images/icon-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladas-larry/redux-chrome-extension/b2035ed97eff27b08bd428421c78da56d24d445b/app/images/icon-38.png -------------------------------------------------------------------------------- /app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_appName__", 3 | "version": "1.0.1", 4 | "manifest_version": 2, 5 | "description": "__MSG_appDescription__", 6 | "icons": { 7 | "16": "images/icon-16.png", 8 | "128": "images/icon-128.png" 9 | }, 10 | "default_locale": "en", 11 | "background": { 12 | "scripts": [ 13 | "scripts/chromereload.js", 14 | "scripts/background_bundle.js" 15 | ] 16 | }, 17 | "browser_action": { 18 | "default_icon": { 19 | "19": "images/icon-19.png", 20 | "38": "images/icon-38.png" 21 | }, 22 | "default_title": "Redux Chrome Extension", 23 | "default_popup": "popup.html" 24 | }, 25 | "options_ui": { 26 | "page": "options.html", 27 | "chrome_style": true 28 | }, 29 | "content_scripts": [ 30 | { 31 | "matches": [ 32 | "http://*/*", 33 | "https://*/*" 34 | ], 35 | "js": [ 36 | "/scripts/content_bundle.js" 37 | ], 38 | "run_at": "document_end", 39 | "all_frames": false 40 | } 41 | ], 42 | "permissions": [ 43 | "tabs", 44 | "http://*/*", 45 | "https://*/*" 46 | ], 47 | "content_security_policy": "script-src 'self' 'unsafe-eval' http://localhost:3000; object-src 'self'; " 48 | } 49 | -------------------------------------------------------------------------------- /app/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Redux Chrome Extension Options 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

Redux Chrome Extension

10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/scripts/background.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { 49 | for (var i = 1; i < arguments.length; i++) { 50 | args[i - 1] = arguments[i]; 51 | } 52 | } 53 | queue.push(new Item(fun, args)); 54 | if (queue.length === 1 && !draining) { 55 | setTimeout(drainQueue, 0); 56 | } 57 | }; 58 | 59 | // v8 likes predictible objects 60 | function Item(fun, array) { 61 | this.fun = fun; 62 | this.array = array; 63 | } 64 | Item.prototype.run = function () { 65 | this.fun.apply(null, this.array); 66 | }; 67 | process.title = 'browser'; 68 | process.browser = true; 69 | process.env = {}; 70 | process.argv = []; 71 | process.version = ''; // empty string to avoid regexp issues 72 | process.versions = {}; 73 | 74 | function noop() {} 75 | 76 | process.on = noop; 77 | process.addListener = noop; 78 | process.once = noop; 79 | process.off = noop; 80 | process.removeListener = noop; 81 | process.removeAllListeners = noop; 82 | process.emit = noop; 83 | 84 | process.binding = function (name) { 85 | throw new Error('process.binding is not supported'); 86 | }; 87 | 88 | process.cwd = function () { return '/' }; 89 | process.chdir = function (dir) { 90 | throw new Error('process.chdir is not supported'); 91 | }; 92 | process.umask = function() { return 0; }; 93 | 94 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux-thunk/lib/index.js":[function(require,module,exports){ 95 | 'use strict'; 96 | 97 | exports.__esModule = true; 98 | exports['default'] = thunkMiddleware; 99 | 100 | function thunkMiddleware(_ref) { 101 | var dispatch = _ref.dispatch; 102 | var getState = _ref.getState; 103 | 104 | return function (next) { 105 | return function (action) { 106 | return typeof action === 'function' ? action(dispatch, getState) : next(action); 107 | }; 108 | }; 109 | } 110 | 111 | module.exports = exports['default']; 112 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/createStore.js":[function(require,module,exports){ 113 | 'use strict'; 114 | 115 | exports.__esModule = true; 116 | exports['default'] = createStore; 117 | 118 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 119 | 120 | var _utilsIsPlainObject = require('./utils/isPlainObject'); 121 | 122 | var _utilsIsPlainObject2 = _interopRequireDefault(_utilsIsPlainObject); 123 | 124 | /** 125 | * These are private action types reserved by Redux. 126 | * For any unknown actions, you must return the current state. 127 | * If the current state is undefined, you must return the initial state. 128 | * Do not reference these action types directly in your code. 129 | */ 130 | var ActionTypes = { 131 | INIT: '@@redux/INIT' 132 | }; 133 | 134 | exports.ActionTypes = ActionTypes; 135 | /** 136 | * Creates a Redux store that holds the state tree. 137 | * The only way to change the data in the store is to call `dispatch()` on it. 138 | * 139 | * There should only be a single store in your app. To specify how different 140 | * parts of the state tree respond to actions, you may combine several reducers 141 | * into a single reducer function by using `combineReducers`. 142 | * 143 | * @param {Function} reducer A function that returns the next state tree, given 144 | * the current state tree and the action to handle. 145 | * 146 | * @param {any} [initialState] The initial state. You may optionally specify it 147 | * to hydrate the state from the server in universal apps, or to restore a 148 | * previously serialized user session. 149 | * If you use `combineReducers` to produce the root reducer function, this must be 150 | * an object with the same shape as `combineReducers` keys. 151 | * 152 | * @returns {Store} A Redux store that lets you read the state, dispatch actions 153 | * and subscribe to changes. 154 | */ 155 | 156 | function createStore(reducer, initialState) { 157 | if (typeof reducer !== 'function') { 158 | throw new Error('Expected the reducer to be a function.'); 159 | } 160 | 161 | var currentReducer = reducer; 162 | var currentState = initialState; 163 | var listeners = []; 164 | var isDispatching = false; 165 | 166 | /** 167 | * Reads the state tree managed by the store. 168 | * 169 | * @returns {any} The current state tree of your application. 170 | */ 171 | function getState() { 172 | return currentState; 173 | } 174 | 175 | /** 176 | * Adds a change listener. It will be called any time an action is dispatched, 177 | * and some part of the state tree may potentially have changed. You may then 178 | * call `getState()` to read the current state tree inside the callback. 179 | * 180 | * @param {Function} listener A callback to be invoked on every dispatch. 181 | * @returns {Function} A function to remove this change listener. 182 | */ 183 | function subscribe(listener) { 184 | listeners.push(listener); 185 | 186 | return function unsubscribe() { 187 | var index = listeners.indexOf(listener); 188 | listeners.splice(index, 1); 189 | }; 190 | } 191 | 192 | /** 193 | * Dispatches an action. It is the only way to trigger a state change. 194 | * 195 | * The `reducer` function, used to create the store, will be called with the 196 | * current state tree and the given `action`. Its return value will 197 | * be considered the **next** state of the tree, and the change listeners 198 | * will be notified. 199 | * 200 | * The base implementation only supports plain object actions. If you want to 201 | * dispatch a Promise, an Observable, a thunk, or something else, you need to 202 | * wrap your store creating function into the corresponding middleware. For 203 | * example, see the documentation for the `redux-thunk` package. Even the 204 | * middleware will eventually dispatch plain object actions using this method. 205 | * 206 | * @param {Object} action A plain object representing “what changed”. It is 207 | * a good idea to keep actions serializable so you can record and replay user 208 | * sessions, or use the time travelling `redux-devtools`. An action must have 209 | * a `type` property which may not be `undefined`. It is a good idea to use 210 | * string constants for action types. 211 | * 212 | * @returns {Object} For convenience, the same action object you dispatched. 213 | * 214 | * Note that, if you use a custom middleware, it may wrap `dispatch()` to 215 | * return something else (for example, a Promise you can await). 216 | */ 217 | function dispatch(action) { 218 | if (!_utilsIsPlainObject2['default'](action)) { 219 | throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.'); 220 | } 221 | 222 | if (typeof action.type === 'undefined') { 223 | throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?'); 224 | } 225 | 226 | if (isDispatching) { 227 | throw new Error('Reducers may not dispatch actions.'); 228 | } 229 | 230 | try { 231 | isDispatching = true; 232 | currentState = currentReducer(currentState, action); 233 | } finally { 234 | isDispatching = false; 235 | } 236 | 237 | listeners.slice().forEach(function (listener) { 238 | return listener(); 239 | }); 240 | return action; 241 | } 242 | 243 | /** 244 | * Replaces the reducer currently used by the store to calculate the state. 245 | * 246 | * You might need this if your app implements code splitting and you want to 247 | * load some of the reducers dynamically. You might also need this if you 248 | * implement a hot reloading mechanism for Redux. 249 | * 250 | * @param {Function} nextReducer The reducer for the store to use instead. 251 | * @returns {void} 252 | */ 253 | function replaceReducer(nextReducer) { 254 | currentReducer = nextReducer; 255 | dispatch({ type: ActionTypes.INIT }); 256 | } 257 | 258 | // When a store is created, an "INIT" action is dispatched so that every 259 | // reducer returns their initial state. This effectively populates 260 | // the initial state tree. 261 | dispatch({ type: ActionTypes.INIT }); 262 | 263 | return { 264 | dispatch: dispatch, 265 | subscribe: subscribe, 266 | getState: getState, 267 | replaceReducer: replaceReducer 268 | }; 269 | } 270 | },{"./utils/isPlainObject":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/isPlainObject.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/index.js":[function(require,module,exports){ 271 | 'use strict'; 272 | 273 | exports.__esModule = true; 274 | 275 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 276 | 277 | var _createStore = require('./createStore'); 278 | 279 | var _createStore2 = _interopRequireDefault(_createStore); 280 | 281 | var _utilsCombineReducers = require('./utils/combineReducers'); 282 | 283 | var _utilsCombineReducers2 = _interopRequireDefault(_utilsCombineReducers); 284 | 285 | var _utilsBindActionCreators = require('./utils/bindActionCreators'); 286 | 287 | var _utilsBindActionCreators2 = _interopRequireDefault(_utilsBindActionCreators); 288 | 289 | var _utilsApplyMiddleware = require('./utils/applyMiddleware'); 290 | 291 | var _utilsApplyMiddleware2 = _interopRequireDefault(_utilsApplyMiddleware); 292 | 293 | var _utilsCompose = require('./utils/compose'); 294 | 295 | var _utilsCompose2 = _interopRequireDefault(_utilsCompose); 296 | 297 | exports.createStore = _createStore2['default']; 298 | exports.combineReducers = _utilsCombineReducers2['default']; 299 | exports.bindActionCreators = _utilsBindActionCreators2['default']; 300 | exports.applyMiddleware = _utilsApplyMiddleware2['default']; 301 | exports.compose = _utilsCompose2['default']; 302 | },{"./createStore":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/createStore.js","./utils/applyMiddleware":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/applyMiddleware.js","./utils/bindActionCreators":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/bindActionCreators.js","./utils/combineReducers":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/combineReducers.js","./utils/compose":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/compose.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/applyMiddleware.js":[function(require,module,exports){ 303 | 'use strict'; 304 | 305 | exports.__esModule = true; 306 | 307 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 308 | 309 | exports['default'] = applyMiddleware; 310 | 311 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 312 | 313 | var _compose = require('./compose'); 314 | 315 | var _compose2 = _interopRequireDefault(_compose); 316 | 317 | /** 318 | * Creates a store enhancer that applies middleware to the dispatch method 319 | * of the Redux store. This is handy for a variety of tasks, such as expressing 320 | * asynchronous actions in a concise manner, or logging every action payload. 321 | * 322 | * See `redux-thunk` package as an example of the Redux middleware. 323 | * 324 | * Because middleware is potentially asynchronous, this should be the first 325 | * store enhancer in the composition chain. 326 | * 327 | * Note that each middleware will be given the `dispatch` and `getState` functions 328 | * as named arguments. 329 | * 330 | * @param {...Function} middlewares The middleware chain to be applied. 331 | * @returns {Function} A store enhancer applying the middleware. 332 | */ 333 | 334 | function applyMiddleware() { 335 | for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) { 336 | middlewares[_key] = arguments[_key]; 337 | } 338 | 339 | return function (next) { 340 | return function (reducer, initialState) { 341 | var store = next(reducer, initialState); 342 | var _dispatch = store.dispatch; 343 | var chain = []; 344 | 345 | var middlewareAPI = { 346 | getState: store.getState, 347 | dispatch: function dispatch(action) { 348 | return _dispatch(action); 349 | } 350 | }; 351 | chain = middlewares.map(function (middleware) { 352 | return middleware(middlewareAPI); 353 | }); 354 | _dispatch = _compose2['default'].apply(undefined, chain)(store.dispatch); 355 | 356 | return _extends({}, store, { 357 | dispatch: _dispatch 358 | }); 359 | }; 360 | }; 361 | } 362 | 363 | module.exports = exports['default']; 364 | },{"./compose":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/compose.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/bindActionCreators.js":[function(require,module,exports){ 365 | 'use strict'; 366 | 367 | exports.__esModule = true; 368 | exports['default'] = bindActionCreators; 369 | 370 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 371 | 372 | var _utilsMapValues = require('../utils/mapValues'); 373 | 374 | var _utilsMapValues2 = _interopRequireDefault(_utilsMapValues); 375 | 376 | function bindActionCreator(actionCreator, dispatch) { 377 | return function () { 378 | return dispatch(actionCreator.apply(undefined, arguments)); 379 | }; 380 | } 381 | 382 | /** 383 | * Turns an object whose values are action creators, into an object with the 384 | * same keys, but with every function wrapped into a `dispatch` call so they 385 | * may be invoked directly. This is just a convenience method, as you can call 386 | * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. 387 | * 388 | * For convenience, you can also pass a single function as the first argument, 389 | * and get a function in return. 390 | * 391 | * @param {Function|Object} actionCreators An object whose values are action 392 | * creator functions. One handy way to obtain it is to use ES6 `import * as` 393 | * syntax. You may also pass a single function. 394 | * 395 | * @param {Function} dispatch The `dispatch` function available on your Redux 396 | * store. 397 | * 398 | * @returns {Function|Object} The object mimicking the original object, but with 399 | * every action creator wrapped into the `dispatch` call. If you passed a 400 | * function as `actionCreators`, the return value will also be a single 401 | * function. 402 | */ 403 | 404 | function bindActionCreators(actionCreators, dispatch) { 405 | if (typeof actionCreators === 'function') { 406 | return bindActionCreator(actionCreators, dispatch); 407 | } 408 | 409 | if (typeof actionCreators !== 'object' || actionCreators === null || actionCreators === undefined) { 410 | // eslint-disable-line no-eq-null 411 | throw new Error('bindActionCreators expected an object or a function, instead received ' + (actionCreators === null ? 'null' : typeof actionCreators) + '. ' + 'Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?'); 412 | } 413 | 414 | return _utilsMapValues2['default'](actionCreators, function (actionCreator) { 415 | return bindActionCreator(actionCreator, dispatch); 416 | }); 417 | } 418 | 419 | module.exports = exports['default']; 420 | },{"../utils/mapValues":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/mapValues.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/combineReducers.js":[function(require,module,exports){ 421 | (function (process){ 422 | 'use strict'; 423 | 424 | exports.__esModule = true; 425 | exports['default'] = combineReducers; 426 | 427 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 428 | 429 | var _createStore = require('../createStore'); 430 | 431 | var _utilsIsPlainObject = require('../utils/isPlainObject'); 432 | 433 | var _utilsIsPlainObject2 = _interopRequireDefault(_utilsIsPlainObject); 434 | 435 | var _utilsMapValues = require('../utils/mapValues'); 436 | 437 | var _utilsMapValues2 = _interopRequireDefault(_utilsMapValues); 438 | 439 | var _utilsPick = require('../utils/pick'); 440 | 441 | var _utilsPick2 = _interopRequireDefault(_utilsPick); 442 | 443 | /* eslint-disable no-console */ 444 | 445 | function getUndefinedStateErrorMessage(key, action) { 446 | var actionType = action && action.type; 447 | var actionName = actionType && '"' + actionType.toString() + '"' || 'an action'; 448 | 449 | return 'Reducer "' + key + '" returned undefined handling ' + actionName + '. ' + 'To ignore an action, you must explicitly return the previous state.'; 450 | } 451 | 452 | function getUnexpectedStateKeyWarningMessage(inputState, outputState, action) { 453 | var reducerKeys = Object.keys(outputState); 454 | var argumentName = action && action.type === _createStore.ActionTypes.INIT ? 'initialState argument passed to createStore' : 'previous state received by the reducer'; 455 | 456 | if (reducerKeys.length === 0) { 457 | return 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.'; 458 | } 459 | 460 | if (!_utilsIsPlainObject2['default'](inputState)) { 461 | return 'The ' + argumentName + ' has unexpected type of "' + ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + '". Expected argument to be an object with the following ' + ('keys: "' + reducerKeys.join('", "') + '"'); 462 | } 463 | 464 | var unexpectedKeys = Object.keys(inputState).filter(function (key) { 465 | return reducerKeys.indexOf(key) < 0; 466 | }); 467 | 468 | if (unexpectedKeys.length > 0) { 469 | return 'Unexpected ' + (unexpectedKeys.length > 1 ? 'keys' : 'key') + ' ' + ('"' + unexpectedKeys.join('", "') + '" found in ' + argumentName + '. ') + 'Expected to find one of the known reducer keys instead: ' + ('"' + reducerKeys.join('", "') + '". Unexpected keys will be ignored.'); 470 | } 471 | } 472 | 473 | function assertReducerSanity(reducers) { 474 | Object.keys(reducers).forEach(function (key) { 475 | var reducer = reducers[key]; 476 | var initialState = reducer(undefined, { type: _createStore.ActionTypes.INIT }); 477 | 478 | if (typeof initialState === 'undefined') { 479 | throw new Error('Reducer "' + key + '" returned undefined during initialization. ' + 'If the state passed to the reducer is undefined, you must ' + 'explicitly return the initial state. The initial state may ' + 'not be undefined.'); 480 | } 481 | 482 | var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.'); 483 | if (typeof reducer(undefined, { type: type }) === 'undefined') { 484 | throw new Error('Reducer "' + key + '" returned undefined when probed with a random type. ' + ('Don\'t try to handle ' + _createStore.ActionTypes.INIT + ' or other actions in "redux/*" ') + 'namespace. They are considered private. Instead, you must return the ' + 'current state for any unknown actions, unless it is undefined, ' + 'in which case you must return the initial state, regardless of the ' + 'action type. The initial state may not be undefined.'); 485 | } 486 | }); 487 | } 488 | 489 | /** 490 | * Turns an object whose values are different reducer functions, into a single 491 | * reducer function. It will call every child reducer, and gather their results 492 | * into a single state object, whose keys correspond to the keys of the passed 493 | * reducer functions. 494 | * 495 | * @param {Object} reducers An object whose values correspond to different 496 | * reducer functions that need to be combined into one. One handy way to obtain 497 | * it is to use ES6 `import * as reducers` syntax. The reducers may never return 498 | * undefined for any action. Instead, they should return their initial state 499 | * if the state passed to them was undefined, and the current state for any 500 | * unrecognized action. 501 | * 502 | * @returns {Function} A reducer function that invokes every reducer inside the 503 | * passed object, and builds a state object with the same shape. 504 | */ 505 | 506 | function combineReducers(reducers) { 507 | var finalReducers = _utilsPick2['default'](reducers, function (val) { 508 | return typeof val === 'function'; 509 | }); 510 | var sanityError; 511 | 512 | try { 513 | assertReducerSanity(finalReducers); 514 | } catch (e) { 515 | sanityError = e; 516 | } 517 | 518 | var defaultState = _utilsMapValues2['default'](finalReducers, function () { 519 | return undefined; 520 | }); 521 | 522 | return function combination(state, action) { 523 | if (state === undefined) state = defaultState; 524 | 525 | if (sanityError) { 526 | throw sanityError; 527 | } 528 | 529 | var finalState = _utilsMapValues2['default'](finalReducers, function (reducer, key) { 530 | var newState = reducer(state[key], action); 531 | if (typeof newState === 'undefined') { 532 | var errorMessage = getUndefinedStateErrorMessage(key, action); 533 | throw new Error(errorMessage); 534 | } 535 | return newState; 536 | }); 537 | 538 | if (process.env.NODE_ENV !== 'production') { 539 | var warningMessage = getUnexpectedStateKeyWarningMessage(state, finalState, action); 540 | if (warningMessage) { 541 | console.error(warningMessage); 542 | } 543 | } 544 | 545 | return finalState; 546 | }; 547 | } 548 | 549 | module.exports = exports['default']; 550 | }).call(this,require('_process')) 551 | },{"../createStore":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/createStore.js","../utils/isPlainObject":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/isPlainObject.js","../utils/mapValues":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/mapValues.js","../utils/pick":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/pick.js","_process":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/browserify/node_modules/process/browser.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/compose.js":[function(require,module,exports){ 552 | /** 553 | * Composes single-argument functions from right to left. 554 | * 555 | * @param {...Function} funcs The functions to compose. 556 | * @returns {Function} A function obtained by composing functions from right to 557 | * left. For example, compose(f, g, h) is identical to arg => f(g(h(arg))). 558 | */ 559 | "use strict"; 560 | 561 | exports.__esModule = true; 562 | exports["default"] = compose; 563 | 564 | function compose() { 565 | for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) { 566 | funcs[_key] = arguments[_key]; 567 | } 568 | 569 | return function (arg) { 570 | return funcs.reduceRight(function (composed, f) { 571 | return f(composed); 572 | }, arg); 573 | }; 574 | } 575 | 576 | module.exports = exports["default"]; 577 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/isPlainObject.js":[function(require,module,exports){ 578 | 'use strict'; 579 | 580 | exports.__esModule = true; 581 | exports['default'] = isPlainObject; 582 | var fnToString = function fnToString(fn) { 583 | return Function.prototype.toString.call(fn); 584 | }; 585 | 586 | /** 587 | * @param {any} obj The object to inspect. 588 | * @returns {boolean} True if the argument appears to be a plain object. 589 | */ 590 | 591 | function isPlainObject(obj) { 592 | if (!obj || typeof obj !== 'object') { 593 | return false; 594 | } 595 | 596 | var proto = typeof obj.constructor === 'function' ? Object.getPrototypeOf(obj) : Object.prototype; 597 | 598 | if (proto === null) { 599 | return true; 600 | } 601 | 602 | var constructor = proto.constructor; 603 | 604 | return typeof constructor === 'function' && constructor instanceof constructor && fnToString(constructor) === fnToString(Object); 605 | } 606 | 607 | module.exports = exports['default']; 608 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/mapValues.js":[function(require,module,exports){ 609 | /** 610 | * Applies a function to every key-value pair inside an object. 611 | * 612 | * @param {Object} obj The source object. 613 | * @param {Function} fn The mapper function that receives the value and the key. 614 | * @returns {Object} A new object that contains the mapped values for the keys. 615 | */ 616 | "use strict"; 617 | 618 | exports.__esModule = true; 619 | exports["default"] = mapValues; 620 | 621 | function mapValues(obj, fn) { 622 | return Object.keys(obj).reduce(function (result, key) { 623 | result[key] = fn(obj[key], key); 624 | return result; 625 | }, {}); 626 | } 627 | 628 | module.exports = exports["default"]; 629 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/pick.js":[function(require,module,exports){ 630 | /** 631 | * Picks key-value pairs from an object where values satisfy a predicate. 632 | * 633 | * @param {Object} obj The object to pick from. 634 | * @param {Function} fn The predicate the values must satisfy to be copied. 635 | * @returns {Object} The object with the values that satisfied the predicate. 636 | */ 637 | "use strict"; 638 | 639 | exports.__esModule = true; 640 | exports["default"] = pick; 641 | 642 | function pick(obj, fn) { 643 | return Object.keys(obj).reduce(function (result, key) { 644 | if (fn(obj[key])) { 645 | result[key] = obj[key]; 646 | } 647 | return result; 648 | }, {}); 649 | } 650 | 651 | module.exports = exports["default"]; 652 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/background.js":[function(require,module,exports){ 653 | 'use strict'; 654 | 655 | require('./background/index'); 656 | 657 | console.log('Greetings from Backgroud Page!'); 658 | 659 | },{"./background/index":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/background/index.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/background/index.js":[function(require,module,exports){ 660 | 'use strict'; 661 | 662 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 663 | 664 | var _sharedStoreConfigureStore = require('../shared/store/configureStore'); 665 | 666 | var _sharedStoreConfigureStore2 = _interopRequireDefault(_sharedStoreConfigureStore); 667 | 668 | var _sharedInitStorage = require('../shared/initStorage'); 669 | 670 | var _sharedInitStorage2 = _interopRequireDefault(_sharedInitStorage); 671 | 672 | var _sharedHelpersCreateInitState = require('../shared/helpers/createInitState'); 673 | 674 | var _sharedHelpersCreateInitState2 = _interopRequireDefault(_sharedHelpersCreateInitState); 675 | 676 | var storage = JSON.parse(localStorage.getItem('persistent')) || _sharedInitStorage2['default']; 677 | var initialState = (0, _sharedHelpersCreateInitState2['default'])(storage); 678 | 679 | var store = (0, _sharedStoreConfigureStore2['default'])(initialState); 680 | 681 | chrome.runtime.onMessage.addListener(function (req, sender, sendResponse) { 682 | console.log(req); 683 | // Receiving updates from Popup Window and Content Scripts 684 | if (req.action === 'updateState') { 685 | store.dispatch({ 686 | type: 'UPDATE_STATE', 687 | state: req.state 688 | }); 689 | } 690 | // Passing initial state to Popup Window and Content Scripts 691 | if (req.action === 'getState') { 692 | sendResponse(store.getState()); 693 | } 694 | }); 695 | 696 | },{"../shared/helpers/createInitState":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/helpers/createInitState.js","../shared/initStorage":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/initStorage.js","../shared/store/configureStore":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/store/configureStore.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/actions/chromeExtension.js":[function(require,module,exports){ 697 | 'use strict'; 698 | 699 | Object.defineProperty(exports, '__esModule', { 700 | value: true 701 | }); 702 | exports.updateState = updateState; 703 | exports.setOptions = setOptions; 704 | exports.increment = increment; 705 | exports.decrement = decrement; 706 | exports.incrementIfOdd = incrementIfOdd; 707 | exports.incrementAsync = incrementAsync; 708 | var UPDATE_STATE = 'UPDATE_STATE'; 709 | exports.UPDATE_STATE = UPDATE_STATE; 710 | var SET_OPTIONS = 'SET_OPTIONS'; 711 | 712 | exports.SET_OPTIONS = SET_OPTIONS; 713 | 714 | function updateState() { 715 | return { 716 | type: UPDATE_STATE 717 | }; 718 | } 719 | 720 | function setOptions(options) { 721 | return { 722 | type: SET_OPTIONS, 723 | options: options 724 | }; 725 | } 726 | 727 | //Counter example 728 | 729 | var INCREMENT_COUNTER = 'INCREMENT_COUNTER'; 730 | exports.INCREMENT_COUNTER = INCREMENT_COUNTER; 731 | var DECREMENT_COUNTER = 'DECREMENT_COUNTER'; 732 | 733 | exports.DECREMENT_COUNTER = DECREMENT_COUNTER; 734 | 735 | function increment() { 736 | return { 737 | type: INCREMENT_COUNTER 738 | }; 739 | } 740 | 741 | function decrement() { 742 | return { 743 | type: DECREMENT_COUNTER 744 | }; 745 | } 746 | 747 | function incrementIfOdd() { 748 | return function (dispatch, getState) { 749 | var _getState = getState(); 750 | 751 | var counter = _getState.counter; 752 | 753 | if (counter % 2 === 0) { 754 | return; 755 | } 756 | 757 | dispatch(increment()); 758 | }; 759 | } 760 | 761 | function incrementAsync() { 762 | var delay = arguments.length <= 0 || arguments[0] === undefined ? 1000 : arguments[0]; 763 | 764 | return function (dispatch) { 765 | setTimeout(function () { 766 | dispatch(increment()); 767 | }, delay); 768 | }; 769 | } 770 | 771 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/helpers/createInitState.js":[function(require,module,exports){ 772 | "use strict"; 773 | 774 | Object.defineProperty(exports, "__esModule", { 775 | value: true 776 | }); 777 | exports["default"] = createInitState; 778 | 779 | function createInitState(storage) { 780 | return { 781 | persistent: storage, 782 | counter: storage.options.initCount 783 | }; 784 | } 785 | 786 | module.exports = exports["default"]; 787 | 788 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/initStorage.js":[function(require,module,exports){ 789 | "use strict"; 790 | 791 | Object.defineProperty(exports, "__esModule", { 792 | value: true 793 | }); 794 | exports["default"] = { 795 | options: { initCount: 1 } 796 | }; 797 | module.exports = exports["default"]; 798 | 799 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/reducers/chromeExtension.js":[function(require,module,exports){ 800 | 'use strict'; 801 | 802 | Object.defineProperty(exports, '__esModule', { 803 | value: true 804 | }); 805 | exports['default'] = chromeExtension; 806 | 807 | var _actionsChromeExtensionJs = require('../actions/chromeExtension.js'); 808 | 809 | function chromeExtension(state, action) { 810 | if (state === undefined) state = { counter: 0, persistent: { options: { initCount: 1 } } }; 811 | 812 | switch (action.type) { 813 | case _actionsChromeExtensionJs.UPDATE_STATE: 814 | console.log('UPDATE_STATE', action.state); 815 | var newState = Object.assign({}, state, action.state); 816 | //conditions to update localStorage only inside backround page 817 | if (location.protocol == 'chrome-extension:' && chrome.extension.getBackgroundPage() === window) { 818 | localStorage.setItem('persistent', JSON.stringify(newState.persistent)); 819 | } 820 | return newState; 821 | case _actionsChromeExtensionJs.SET_OPTIONS: 822 | return Object.assign({}, state, { persistent: { options: action.options } }); 823 | //Counter example 824 | case _actionsChromeExtensionJs.INCREMENT_COUNTER: 825 | return Object.assign({}, state, { counter: state.counter + 1 }); 826 | case _actionsChromeExtensionJs.DECREMENT_COUNTER: 827 | return Object.assign({}, state, { counter: state.counter - 1 }); 828 | default: 829 | return state; 830 | } 831 | } 832 | 833 | module.exports = exports['default']; 834 | 835 | },{"../actions/chromeExtension.js":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/actions/chromeExtension.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/store/configureStore.js":[function(require,module,exports){ 836 | 'use strict'; 837 | 838 | Object.defineProperty(exports, '__esModule', { 839 | value: true 840 | }); 841 | exports['default'] = configureStore; 842 | 843 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 844 | 845 | var _redux = require('redux'); 846 | 847 | var _reduxThunk = require('redux-thunk'); 848 | 849 | var _reduxThunk2 = _interopRequireDefault(_reduxThunk); 850 | 851 | var _reducersChromeExtension = require('../reducers/chromeExtension'); 852 | 853 | var _reducersChromeExtension2 = _interopRequireDefault(_reducersChromeExtension); 854 | 855 | var createStoreWithMiddleware = (0, _redux.applyMiddleware)(_reduxThunk2['default'])(_redux.createStore); 856 | 857 | function configureStore(initialState) { 858 | return createStoreWithMiddleware(_reducersChromeExtension2['default'], initialState); 859 | } 860 | 861 | module.exports = exports['default']; 862 | 863 | },{"../reducers/chromeExtension":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/reducers/chromeExtension.js","redux":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/index.js","redux-thunk":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux-thunk/lib/index.js"}]},{},["/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/background.js"]); 864 | -------------------------------------------------------------------------------- /app/scripts/background_bundle.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { 49 | for (var i = 1; i < arguments.length; i++) { 50 | args[i - 1] = arguments[i]; 51 | } 52 | } 53 | queue.push(new Item(fun, args)); 54 | if (queue.length === 1 && !draining) { 55 | setTimeout(drainQueue, 0); 56 | } 57 | }; 58 | 59 | // v8 likes predictible objects 60 | function Item(fun, array) { 61 | this.fun = fun; 62 | this.array = array; 63 | } 64 | Item.prototype.run = function () { 65 | this.fun.apply(null, this.array); 66 | }; 67 | process.title = 'browser'; 68 | process.browser = true; 69 | process.env = {}; 70 | process.argv = []; 71 | process.version = ''; // empty string to avoid regexp issues 72 | process.versions = {}; 73 | 74 | function noop() {} 75 | 76 | process.on = noop; 77 | process.addListener = noop; 78 | process.once = noop; 79 | process.off = noop; 80 | process.removeListener = noop; 81 | process.removeAllListeners = noop; 82 | process.emit = noop; 83 | 84 | process.binding = function (name) { 85 | throw new Error('process.binding is not supported'); 86 | }; 87 | 88 | process.cwd = function () { return '/' }; 89 | process.chdir = function (dir) { 90 | throw new Error('process.chdir is not supported'); 91 | }; 92 | process.umask = function() { return 0; }; 93 | 94 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux-thunk/lib/index.js":[function(require,module,exports){ 95 | 'use strict'; 96 | 97 | function thunkMiddleware(_ref) { 98 | var dispatch = _ref.dispatch; 99 | var getState = _ref.getState; 100 | 101 | return function (next) { 102 | return function (action) { 103 | return typeof action === 'function' ? action(dispatch, getState) : next(action); 104 | }; 105 | }; 106 | } 107 | 108 | module.exports = thunkMiddleware; 109 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/createStore.js":[function(require,module,exports){ 110 | 'use strict'; 111 | 112 | exports.__esModule = true; 113 | exports['default'] = createStore; 114 | 115 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 116 | 117 | var _utilsIsPlainObject = require('./utils/isPlainObject'); 118 | 119 | var _utilsIsPlainObject2 = _interopRequireDefault(_utilsIsPlainObject); 120 | 121 | /** 122 | * These are private action types reserved by Redux. 123 | * For any unknown actions, you must return the current state. 124 | * If the current state is undefined, you must return the initial state. 125 | * Do not reference these action types directly in your code. 126 | */ 127 | var ActionTypes = { 128 | INIT: '@@redux/INIT' 129 | }; 130 | 131 | exports.ActionTypes = ActionTypes; 132 | /** 133 | * Creates a Redux store that holds the state tree. 134 | * The only way to change the data in the store is to call `dispatch()` on it. 135 | * 136 | * There should only be a single store in your app. To specify how different 137 | * parts of the state tree respond to actions, you may combine several reducers 138 | * into a single reducer function by using `combineReducers`. 139 | * 140 | * @param {Function} reducer A function that returns the next state tree, given 141 | * the current state tree and the action to handle. 142 | * 143 | * @param {any} [initialState] The initial state. You may optionally specify it 144 | * to hydrate the state from the server in universal apps, or to restore a 145 | * previously serialized user session. 146 | * If you use `combineReducers` to produce the root reducer function, this must be 147 | * an object with the same shape as `combineReducers` keys. 148 | * 149 | * @returns {Store} A Redux store that lets you read the state, dispatch actions 150 | * and subscribe to changes. 151 | */ 152 | 153 | function createStore(reducer, initialState) { 154 | if (typeof reducer !== 'function') { 155 | throw new Error('Expected the reducer to be a function.'); 156 | } 157 | 158 | var currentReducer = reducer; 159 | var currentState = initialState; 160 | var listeners = []; 161 | var isDispatching = false; 162 | 163 | /** 164 | * Reads the state tree managed by the store. 165 | * 166 | * @returns {any} The current state tree of your application. 167 | */ 168 | function getState() { 169 | return currentState; 170 | } 171 | 172 | /** 173 | * Adds a change listener. It will be called any time an action is dispatched, 174 | * and some part of the state tree may potentially have changed. You may then 175 | * call `getState()` to read the current state tree inside the callback. 176 | * 177 | * @param {Function} listener A callback to be invoked on every dispatch. 178 | * @returns {Function} A function to remove this change listener. 179 | */ 180 | function subscribe(listener) { 181 | listeners.push(listener); 182 | var isSubscribed = true; 183 | 184 | return function unsubscribe() { 185 | if (!isSubscribed) { 186 | return; 187 | } 188 | 189 | isSubscribed = false; 190 | var index = listeners.indexOf(listener); 191 | listeners.splice(index, 1); 192 | }; 193 | } 194 | 195 | /** 196 | * Dispatches an action. It is the only way to trigger a state change. 197 | * 198 | * The `reducer` function, used to create the store, will be called with the 199 | * current state tree and the given `action`. Its return value will 200 | * be considered the **next** state of the tree, and the change listeners 201 | * will be notified. 202 | * 203 | * The base implementation only supports plain object actions. If you want to 204 | * dispatch a Promise, an Observable, a thunk, or something else, you need to 205 | * wrap your store creating function into the corresponding middleware. For 206 | * example, see the documentation for the `redux-thunk` package. Even the 207 | * middleware will eventually dispatch plain object actions using this method. 208 | * 209 | * @param {Object} action A plain object representing “what changed”. It is 210 | * a good idea to keep actions serializable so you can record and replay user 211 | * sessions, or use the time travelling `redux-devtools`. An action must have 212 | * a `type` property which may not be `undefined`. It is a good idea to use 213 | * string constants for action types. 214 | * 215 | * @returns {Object} For convenience, the same action object you dispatched. 216 | * 217 | * Note that, if you use a custom middleware, it may wrap `dispatch()` to 218 | * return something else (for example, a Promise you can await). 219 | */ 220 | function dispatch(action) { 221 | if (!_utilsIsPlainObject2['default'](action)) { 222 | throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.'); 223 | } 224 | 225 | if (typeof action.type === 'undefined') { 226 | throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?'); 227 | } 228 | 229 | if (isDispatching) { 230 | throw new Error('Reducers may not dispatch actions.'); 231 | } 232 | 233 | try { 234 | isDispatching = true; 235 | currentState = currentReducer(currentState, action); 236 | } finally { 237 | isDispatching = false; 238 | } 239 | 240 | listeners.slice().forEach(function (listener) { 241 | return listener(); 242 | }); 243 | return action; 244 | } 245 | 246 | /** 247 | * Replaces the reducer currently used by the store to calculate the state. 248 | * 249 | * You might need this if your app implements code splitting and you want to 250 | * load some of the reducers dynamically. You might also need this if you 251 | * implement a hot reloading mechanism for Redux. 252 | * 253 | * @param {Function} nextReducer The reducer for the store to use instead. 254 | * @returns {void} 255 | */ 256 | function replaceReducer(nextReducer) { 257 | currentReducer = nextReducer; 258 | dispatch({ type: ActionTypes.INIT }); 259 | } 260 | 261 | // When a store is created, an "INIT" action is dispatched so that every 262 | // reducer returns their initial state. This effectively populates 263 | // the initial state tree. 264 | dispatch({ type: ActionTypes.INIT }); 265 | 266 | return { 267 | dispatch: dispatch, 268 | subscribe: subscribe, 269 | getState: getState, 270 | replaceReducer: replaceReducer 271 | }; 272 | } 273 | },{"./utils/isPlainObject":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/isPlainObject.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/index.js":[function(require,module,exports){ 274 | 'use strict'; 275 | 276 | exports.__esModule = true; 277 | 278 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 279 | 280 | var _createStore = require('./createStore'); 281 | 282 | var _createStore2 = _interopRequireDefault(_createStore); 283 | 284 | var _utilsCombineReducers = require('./utils/combineReducers'); 285 | 286 | var _utilsCombineReducers2 = _interopRequireDefault(_utilsCombineReducers); 287 | 288 | var _utilsBindActionCreators = require('./utils/bindActionCreators'); 289 | 290 | var _utilsBindActionCreators2 = _interopRequireDefault(_utilsBindActionCreators); 291 | 292 | var _utilsApplyMiddleware = require('./utils/applyMiddleware'); 293 | 294 | var _utilsApplyMiddleware2 = _interopRequireDefault(_utilsApplyMiddleware); 295 | 296 | var _utilsCompose = require('./utils/compose'); 297 | 298 | var _utilsCompose2 = _interopRequireDefault(_utilsCompose); 299 | 300 | exports.createStore = _createStore2['default']; 301 | exports.combineReducers = _utilsCombineReducers2['default']; 302 | exports.bindActionCreators = _utilsBindActionCreators2['default']; 303 | exports.applyMiddleware = _utilsApplyMiddleware2['default']; 304 | exports.compose = _utilsCompose2['default']; 305 | },{"./createStore":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/createStore.js","./utils/applyMiddleware":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/applyMiddleware.js","./utils/bindActionCreators":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/bindActionCreators.js","./utils/combineReducers":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/combineReducers.js","./utils/compose":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/compose.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/applyMiddleware.js":[function(require,module,exports){ 306 | 'use strict'; 307 | 308 | exports.__esModule = true; 309 | 310 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 311 | 312 | exports['default'] = applyMiddleware; 313 | 314 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 315 | 316 | var _compose = require('./compose'); 317 | 318 | var _compose2 = _interopRequireDefault(_compose); 319 | 320 | /** 321 | * Creates a store enhancer that applies middleware to the dispatch method 322 | * of the Redux store. This is handy for a variety of tasks, such as expressing 323 | * asynchronous actions in a concise manner, or logging every action payload. 324 | * 325 | * See `redux-thunk` package as an example of the Redux middleware. 326 | * 327 | * Because middleware is potentially asynchronous, this should be the first 328 | * store enhancer in the composition chain. 329 | * 330 | * Note that each middleware will be given the `dispatch` and `getState` functions 331 | * as named arguments. 332 | * 333 | * @param {...Function} middlewares The middleware chain to be applied. 334 | * @returns {Function} A store enhancer applying the middleware. 335 | */ 336 | 337 | function applyMiddleware() { 338 | for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) { 339 | middlewares[_key] = arguments[_key]; 340 | } 341 | 342 | return function (next) { 343 | return function (reducer, initialState) { 344 | var store = next(reducer, initialState); 345 | var _dispatch = store.dispatch; 346 | var chain = []; 347 | 348 | var middlewareAPI = { 349 | getState: store.getState, 350 | dispatch: function dispatch(action) { 351 | return _dispatch(action); 352 | } 353 | }; 354 | chain = middlewares.map(function (middleware) { 355 | return middleware(middlewareAPI); 356 | }); 357 | _dispatch = _compose2['default'].apply(undefined, chain)(store.dispatch); 358 | 359 | return _extends({}, store, { 360 | dispatch: _dispatch 361 | }); 362 | }; 363 | }; 364 | } 365 | 366 | module.exports = exports['default']; 367 | },{"./compose":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/compose.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/bindActionCreators.js":[function(require,module,exports){ 368 | 'use strict'; 369 | 370 | exports.__esModule = true; 371 | exports['default'] = bindActionCreators; 372 | 373 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 374 | 375 | var _mapValues = require('./mapValues'); 376 | 377 | var _mapValues2 = _interopRequireDefault(_mapValues); 378 | 379 | function bindActionCreator(actionCreator, dispatch) { 380 | return function () { 381 | return dispatch(actionCreator.apply(undefined, arguments)); 382 | }; 383 | } 384 | 385 | /** 386 | * Turns an object whose values are action creators, into an object with the 387 | * same keys, but with every function wrapped into a `dispatch` call so they 388 | * may be invoked directly. This is just a convenience method, as you can call 389 | * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. 390 | * 391 | * For convenience, you can also pass a single function as the first argument, 392 | * and get a function in return. 393 | * 394 | * @param {Function|Object} actionCreators An object whose values are action 395 | * creator functions. One handy way to obtain it is to use ES6 `import * as` 396 | * syntax. You may also pass a single function. 397 | * 398 | * @param {Function} dispatch The `dispatch` function available on your Redux 399 | * store. 400 | * 401 | * @returns {Function|Object} The object mimicking the original object, but with 402 | * every action creator wrapped into the `dispatch` call. If you passed a 403 | * function as `actionCreators`, the return value will also be a single 404 | * function. 405 | */ 406 | 407 | function bindActionCreators(actionCreators, dispatch) { 408 | if (typeof actionCreators === 'function') { 409 | return bindActionCreator(actionCreators, dispatch); 410 | } 411 | 412 | if (typeof actionCreators !== 'object' || actionCreators === null || actionCreators === undefined) { 413 | throw new Error('bindActionCreators expected an object or a function, instead received ' + (actionCreators === null ? 'null' : typeof actionCreators) + '. ' + 'Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?'); 414 | } 415 | 416 | return _mapValues2['default'](actionCreators, function (actionCreator) { 417 | return bindActionCreator(actionCreator, dispatch); 418 | }); 419 | } 420 | 421 | module.exports = exports['default']; 422 | },{"./mapValues":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/mapValues.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/combineReducers.js":[function(require,module,exports){ 423 | (function (process){ 424 | 'use strict'; 425 | 426 | exports.__esModule = true; 427 | exports['default'] = combineReducers; 428 | 429 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 430 | 431 | var _createStore = require('../createStore'); 432 | 433 | var _isPlainObject = require('./isPlainObject'); 434 | 435 | var _isPlainObject2 = _interopRequireDefault(_isPlainObject); 436 | 437 | var _mapValues = require('./mapValues'); 438 | 439 | var _mapValues2 = _interopRequireDefault(_mapValues); 440 | 441 | var _pick = require('./pick'); 442 | 443 | var _pick2 = _interopRequireDefault(_pick); 444 | 445 | /* eslint-disable no-console */ 446 | 447 | function getUndefinedStateErrorMessage(key, action) { 448 | var actionType = action && action.type; 449 | var actionName = actionType && '"' + actionType.toString() + '"' || 'an action'; 450 | 451 | return 'Reducer "' + key + '" returned undefined handling ' + actionName + '. ' + 'To ignore an action, you must explicitly return the previous state.'; 452 | } 453 | 454 | function getUnexpectedStateKeyWarningMessage(inputState, outputState, action) { 455 | var reducerKeys = Object.keys(outputState); 456 | var argumentName = action && action.type === _createStore.ActionTypes.INIT ? 'initialState argument passed to createStore' : 'previous state received by the reducer'; 457 | 458 | if (reducerKeys.length === 0) { 459 | return 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.'; 460 | } 461 | 462 | if (!_isPlainObject2['default'](inputState)) { 463 | return 'The ' + argumentName + ' has unexpected type of "' + ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + '". Expected argument to be an object with the following ' + ('keys: "' + reducerKeys.join('", "') + '"'); 464 | } 465 | 466 | var unexpectedKeys = Object.keys(inputState).filter(function (key) { 467 | return reducerKeys.indexOf(key) < 0; 468 | }); 469 | 470 | if (unexpectedKeys.length > 0) { 471 | return 'Unexpected ' + (unexpectedKeys.length > 1 ? 'keys' : 'key') + ' ' + ('"' + unexpectedKeys.join('", "') + '" found in ' + argumentName + '. ') + 'Expected to find one of the known reducer keys instead: ' + ('"' + reducerKeys.join('", "') + '". Unexpected keys will be ignored.'); 472 | } 473 | } 474 | 475 | function assertReducerSanity(reducers) { 476 | Object.keys(reducers).forEach(function (key) { 477 | var reducer = reducers[key]; 478 | var initialState = reducer(undefined, { type: _createStore.ActionTypes.INIT }); 479 | 480 | if (typeof initialState === 'undefined') { 481 | throw new Error('Reducer "' + key + '" returned undefined during initialization. ' + 'If the state passed to the reducer is undefined, you must ' + 'explicitly return the initial state. The initial state may ' + 'not be undefined.'); 482 | } 483 | 484 | var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.'); 485 | if (typeof reducer(undefined, { type: type }) === 'undefined') { 486 | throw new Error('Reducer "' + key + '" returned undefined when probed with a random type. ' + ('Don\'t try to handle ' + _createStore.ActionTypes.INIT + ' or other actions in "redux/*" ') + 'namespace. They are considered private. Instead, you must return the ' + 'current state for any unknown actions, unless it is undefined, ' + 'in which case you must return the initial state, regardless of the ' + 'action type. The initial state may not be undefined.'); 487 | } 488 | }); 489 | } 490 | 491 | /** 492 | * Turns an object whose values are different reducer functions, into a single 493 | * reducer function. It will call every child reducer, and gather their results 494 | * into a single state object, whose keys correspond to the keys of the passed 495 | * reducer functions. 496 | * 497 | * @param {Object} reducers An object whose values correspond to different 498 | * reducer functions that need to be combined into one. One handy way to obtain 499 | * it is to use ES6 `import * as reducers` syntax. The reducers may never return 500 | * undefined for any action. Instead, they should return their initial state 501 | * if the state passed to them was undefined, and the current state for any 502 | * unrecognized action. 503 | * 504 | * @returns {Function} A reducer function that invokes every reducer inside the 505 | * passed object, and builds a state object with the same shape. 506 | */ 507 | 508 | function combineReducers(reducers) { 509 | var finalReducers = _pick2['default'](reducers, function (val) { 510 | return typeof val === 'function'; 511 | }); 512 | var sanityError; 513 | 514 | try { 515 | assertReducerSanity(finalReducers); 516 | } catch (e) { 517 | sanityError = e; 518 | } 519 | 520 | var defaultState = _mapValues2['default'](finalReducers, function () { 521 | return undefined; 522 | }); 523 | 524 | return function combination(state, action) { 525 | if (state === undefined) state = defaultState; 526 | 527 | if (sanityError) { 528 | throw sanityError; 529 | } 530 | 531 | var hasChanged = false; 532 | var finalState = _mapValues2['default'](finalReducers, function (reducer, key) { 533 | var previousStateForKey = state[key]; 534 | var nextStateForKey = reducer(previousStateForKey, action); 535 | if (typeof nextStateForKey === 'undefined') { 536 | var errorMessage = getUndefinedStateErrorMessage(key, action); 537 | throw new Error(errorMessage); 538 | } 539 | hasChanged = hasChanged || nextStateForKey !== previousStateForKey; 540 | return nextStateForKey; 541 | }); 542 | 543 | if (process.env.NODE_ENV !== 'production') { 544 | var warningMessage = getUnexpectedStateKeyWarningMessage(state, finalState, action); 545 | if (warningMessage) { 546 | console.error(warningMessage); 547 | } 548 | } 549 | 550 | return hasChanged ? finalState : state; 551 | }; 552 | } 553 | 554 | module.exports = exports['default']; 555 | }).call(this,require('_process')) 556 | },{"../createStore":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/createStore.js","./isPlainObject":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/isPlainObject.js","./mapValues":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/mapValues.js","./pick":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/pick.js","_process":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/browserify/node_modules/process/browser.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/compose.js":[function(require,module,exports){ 557 | /** 558 | * Composes single-argument functions from right to left. 559 | * 560 | * @param {...Function} funcs The functions to compose. 561 | * @returns {Function} A function obtained by composing functions from right to 562 | * left. For example, compose(f, g, h) is identical to arg => f(g(h(arg))). 563 | */ 564 | "use strict"; 565 | 566 | exports.__esModule = true; 567 | exports["default"] = compose; 568 | 569 | function compose() { 570 | for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) { 571 | funcs[_key] = arguments[_key]; 572 | } 573 | 574 | return function (arg) { 575 | return funcs.reduceRight(function (composed, f) { 576 | return f(composed); 577 | }, arg); 578 | }; 579 | } 580 | 581 | module.exports = exports["default"]; 582 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/isPlainObject.js":[function(require,module,exports){ 583 | 'use strict'; 584 | 585 | exports.__esModule = true; 586 | exports['default'] = isPlainObject; 587 | var fnToString = function fnToString(fn) { 588 | return Function.prototype.toString.call(fn); 589 | }; 590 | var objStringValue = fnToString(Object); 591 | 592 | /** 593 | * @param {any} obj The object to inspect. 594 | * @returns {boolean} True if the argument appears to be a plain object. 595 | */ 596 | 597 | function isPlainObject(obj) { 598 | if (!obj || typeof obj !== 'object') { 599 | return false; 600 | } 601 | 602 | var proto = typeof obj.constructor === 'function' ? Object.getPrototypeOf(obj) : Object.prototype; 603 | 604 | if (proto === null) { 605 | return true; 606 | } 607 | 608 | var constructor = proto.constructor; 609 | 610 | return typeof constructor === 'function' && constructor instanceof constructor && fnToString(constructor) === objStringValue; 611 | } 612 | 613 | module.exports = exports['default']; 614 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/mapValues.js":[function(require,module,exports){ 615 | /** 616 | * Applies a function to every key-value pair inside an object. 617 | * 618 | * @param {Object} obj The source object. 619 | * @param {Function} fn The mapper function that receives the value and the key. 620 | * @returns {Object} A new object that contains the mapped values for the keys. 621 | */ 622 | "use strict"; 623 | 624 | exports.__esModule = true; 625 | exports["default"] = mapValues; 626 | 627 | function mapValues(obj, fn) { 628 | return Object.keys(obj).reduce(function (result, key) { 629 | result[key] = fn(obj[key], key); 630 | return result; 631 | }, {}); 632 | } 633 | 634 | module.exports = exports["default"]; 635 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/utils/pick.js":[function(require,module,exports){ 636 | /** 637 | * Picks key-value pairs from an object where values satisfy a predicate. 638 | * 639 | * @param {Object} obj The object to pick from. 640 | * @param {Function} fn The predicate the values must satisfy to be copied. 641 | * @returns {Object} The object with the values that satisfied the predicate. 642 | */ 643 | "use strict"; 644 | 645 | exports.__esModule = true; 646 | exports["default"] = pick; 647 | 648 | function pick(obj, fn) { 649 | return Object.keys(obj).reduce(function (result, key) { 650 | if (fn(obj[key])) { 651 | result[key] = obj[key]; 652 | } 653 | return result; 654 | }, {}); 655 | } 656 | 657 | module.exports = exports["default"]; 658 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/background.js":[function(require,module,exports){ 659 | 'use strict'; 660 | 661 | require('./background/index'); 662 | 663 | console.log('Greetings from Backgroud Page'); 664 | 665 | },{"./background/index":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/background/index.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/background/index.js":[function(require,module,exports){ 666 | 'use strict'; 667 | 668 | var _configureStore = require('../shared/store/configureStore'); 669 | 670 | var _configureStore2 = _interopRequireDefault(_configureStore); 671 | 672 | var _initStorage = require('../shared/initStorage'); 673 | 674 | var _initStorage2 = _interopRequireDefault(_initStorage); 675 | 676 | var _createInitState = require('../shared/helpers/createInitState'); 677 | 678 | var _createInitState2 = _interopRequireDefault(_createInitState); 679 | 680 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 681 | 682 | var storage = JSON.parse(localStorage.getItem('persistent')) || _initStorage2.default; 683 | var initialState = (0, _createInitState2.default)(storage); 684 | 685 | var store = (0, _configureStore2.default)(initialState); 686 | 687 | chrome.runtime.onMessage.addListener(function (req, sender, sendResponse) { 688 | console.log(req); 689 | // Receiving updates from Popup Window and Content Scripts 690 | if (req.action === 'updateState') { 691 | store.dispatch({ 692 | type: 'UPDATE_STATE', 693 | state: req.state 694 | }); 695 | } 696 | // Passing initial state to Popup Window and Content Scripts 697 | if (req.action === 'getState') { 698 | sendResponse(store.getState()); 699 | } 700 | }); 701 | 702 | },{"../shared/helpers/createInitState":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/helpers/createInitState.js","../shared/initStorage":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/initStorage.js","../shared/store/configureStore":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/store/configureStore.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/actions/chromeExtension.js":[function(require,module,exports){ 703 | 'use strict'; 704 | 705 | Object.defineProperty(exports, "__esModule", { 706 | value: true 707 | }); 708 | exports.updateState = updateState; 709 | exports.setOptions = setOptions; 710 | exports.increment = increment; 711 | exports.decrement = decrement; 712 | exports.incrementIfOdd = incrementIfOdd; 713 | exports.incrementAsync = incrementAsync; 714 | var UPDATE_STATE = exports.UPDATE_STATE = 'UPDATE_STATE'; 715 | var SET_OPTIONS = exports.SET_OPTIONS = 'SET_OPTIONS'; 716 | 717 | function updateState() { 718 | return { 719 | type: UPDATE_STATE 720 | }; 721 | } 722 | 723 | function setOptions(options) { 724 | return { 725 | type: SET_OPTIONS, 726 | options: options 727 | }; 728 | } 729 | 730 | //Counter example 731 | 732 | var INCREMENT_COUNTER = exports.INCREMENT_COUNTER = 'INCREMENT_COUNTER'; 733 | var DECREMENT_COUNTER = exports.DECREMENT_COUNTER = 'DECREMENT_COUNTER'; 734 | 735 | function increment() { 736 | return { 737 | type: INCREMENT_COUNTER 738 | }; 739 | } 740 | 741 | function decrement() { 742 | return { 743 | type: DECREMENT_COUNTER 744 | }; 745 | } 746 | 747 | function incrementIfOdd() { 748 | return function (dispatch, getState) { 749 | var _getState = getState(); 750 | 751 | var counter = _getState.counter; 752 | 753 | if (counter % 2 === 0) { 754 | return; 755 | } 756 | 757 | dispatch(increment()); 758 | }; 759 | } 760 | 761 | function incrementAsync() { 762 | var delay = arguments.length <= 0 || arguments[0] === undefined ? 1000 : arguments[0]; 763 | 764 | return function (dispatch) { 765 | setTimeout(function () { 766 | dispatch(increment()); 767 | }, delay); 768 | }; 769 | } 770 | 771 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/helpers/createInitState.js":[function(require,module,exports){ 772 | "use strict"; 773 | 774 | Object.defineProperty(exports, "__esModule", { 775 | value: true 776 | }); 777 | exports.default = createInitState; 778 | function createInitState(storage) { 779 | return { 780 | persistent: storage, 781 | counter: storage.options.initCount 782 | }; 783 | } 784 | 785 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/initStorage.js":[function(require,module,exports){ 786 | "use strict"; 787 | 788 | Object.defineProperty(exports, "__esModule", { 789 | value: true 790 | }); 791 | exports.default = { 792 | options: { initCount: 1 } 793 | }; 794 | 795 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/reducers/chromeExtension.js":[function(require,module,exports){ 796 | 'use strict'; 797 | 798 | Object.defineProperty(exports, "__esModule", { 799 | value: true 800 | }); 801 | exports.default = chromeExtension; 802 | 803 | var _chromeExtension = require('../actions/chromeExtension.js'); 804 | 805 | function chromeExtension() { 806 | var state = arguments.length <= 0 || arguments[0] === undefined ? { counter: 0, persistent: { options: { initCount: 1 } } } : arguments[0]; 807 | var action = arguments[1]; 808 | 809 | switch (action.type) { 810 | case _chromeExtension.UPDATE_STATE: 811 | console.log('UPDATE_STATE', action.state); 812 | var newState = Object.assign({}, state, action.state); 813 | //conditions to update localStorage only inside backround page 814 | if (location.protocol == 'chrome-extension:' && chrome.extension.getBackgroundPage() === window) { 815 | localStorage.setItem('persistent', JSON.stringify(newState.persistent)); 816 | } 817 | return newState; 818 | case _chromeExtension.SET_OPTIONS: 819 | return Object.assign({}, state, { persistent: { options: action.options } }); 820 | //Counter example 821 | case _chromeExtension.INCREMENT_COUNTER: 822 | return Object.assign({}, state, { counter: state.counter + 1 }); 823 | case _chromeExtension.DECREMENT_COUNTER: 824 | return Object.assign({}, state, { counter: state.counter - 1 }); 825 | default: 826 | return state; 827 | } 828 | } 829 | 830 | },{"../actions/chromeExtension.js":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/actions/chromeExtension.js"}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/store/configureStore.js":[function(require,module,exports){ 831 | 'use strict'; 832 | 833 | Object.defineProperty(exports, "__esModule", { 834 | value: true 835 | }); 836 | exports.default = configureStore; 837 | 838 | var _redux = require('redux'); 839 | 840 | var _reduxThunk = require('redux-thunk'); 841 | 842 | var _reduxThunk2 = _interopRequireDefault(_reduxThunk); 843 | 844 | var _chromeExtension = require('../reducers/chromeExtension'); 845 | 846 | var _chromeExtension2 = _interopRequireDefault(_chromeExtension); 847 | 848 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 849 | 850 | var createStoreWithMiddleware = (0, _redux.applyMiddleware)(_reduxThunk2.default)(_redux.createStore); 851 | 852 | function configureStore(initialState) { 853 | return createStoreWithMiddleware(_chromeExtension2.default, initialState); 854 | } 855 | 856 | },{"../reducers/chromeExtension":"/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/shared/reducers/chromeExtension.js","redux":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux/lib/index.js","redux-thunk":"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/redux-thunk/lib/index.js"}]},{},["/Volumes/Workspace/Github/repos/redux-chrome-extension/src/scripts/background.js"]); 857 | -------------------------------------------------------------------------------- /app/scripts/chromereload.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Reload client for Chrome Apps & Extensions. 4 | // The reload client has a compatibility with livereload. 5 | // WARNING: only supports reload command. 6 | 7 | var LIVERELOAD_HOST = 'localhost:'; 8 | var LIVERELOAD_PORT = 35729; 9 | var connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload'); 10 | 11 | connection.onerror = function (error) { 12 | console.log('reload connection got error:', error); 13 | }; 14 | 15 | connection.onmessage = function (e) { 16 | console.log('RELOAD!'); 17 | console.dir(e); 18 | chrome.runtime.reload(); 19 | /*if (e.data) { 20 | var data = JSON.parse(e.data); 21 | if (data && data.command === 'reload') { 22 | chrome.runtime.reload(); 23 | } 24 | }*/ 25 | }; 26 | -------------------------------------------------------------------------------- /app/scripts/content_bundle.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { 49 | for (var i = 1; i < arguments.length; i++) { 50 | args[i - 1] = arguments[i]; 51 | } 52 | } 53 | queue.push(new Item(fun, args)); 54 | if (queue.length === 1 && !draining) { 55 | setTimeout(drainQueue, 0); 56 | } 57 | }; 58 | 59 | // v8 likes predictible objects 60 | function Item(fun, array) { 61 | this.fun = fun; 62 | this.array = array; 63 | } 64 | Item.prototype.run = function () { 65 | this.fun.apply(null, this.array); 66 | }; 67 | process.title = 'browser'; 68 | process.browser = true; 69 | process.env = {}; 70 | process.argv = []; 71 | process.version = ''; // empty string to avoid regexp issues 72 | process.versions = {}; 73 | 74 | function noop() {} 75 | 76 | process.on = noop; 77 | process.addListener = noop; 78 | process.once = noop; 79 | process.off = noop; 80 | process.removeListener = noop; 81 | process.removeAllListeners = noop; 82 | process.emit = noop; 83 | 84 | process.binding = function (name) { 85 | throw new Error('process.binding is not supported'); 86 | }; 87 | 88 | process.cwd = function () { return '/' }; 89 | process.chdir = function (dir) { 90 | throw new Error('process.chdir is not supported'); 91 | }; 92 | process.umask = function() { return 0; }; 93 | 94 | },{}],"/Volumes/Workspace/Github/repos/redux-chrome-extension/node_modules/q/q.js":[function(require,module,exports){ 95 | (function (process){ 96 | // vim:ts=4:sts=4:sw=4: 97 | /*! 98 | * 99 | * Copyright 2009-2012 Kris Kowal under the terms of the MIT 100 | * license found at http://github.com/kriskowal/q/raw/master/LICENSE 101 | * 102 | * With parts by Tyler Close 103 | * Copyright 2007-2009 Tyler Close under the terms of the MIT X license found 104 | * at http://www.opensource.org/licenses/mit-license.html 105 | * Forked at ref_send.js version: 2009-05-11 106 | * 107 | * With parts by Mark Miller 108 | * Copyright (C) 2011 Google Inc. 109 | * 110 | * Licensed under the Apache License, Version 2.0 (the "License"); 111 | * you may not use this file except in compliance with the License. 112 | * You may obtain a copy of the License at 113 | * 114 | * http://www.apache.org/licenses/LICENSE-2.0 115 | * 116 | * Unless required by applicable law or agreed to in writing, software 117 | * distributed under the License is distributed on an "AS IS" BASIS, 118 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 119 | * See the License for the specific language governing permissions and 120 | * limitations under the License. 121 | * 122 | */ 123 | 124 | (function (definition) { 125 | "use strict"; 126 | 127 | // This file will function properly as a