diff options
| -rw-r--r-- | .eslintrc | 9 | ||||
| -rw-r--r-- | package-lock.json | 370 | ||||
| -rw-r--r-- | package.json | 7 | ||||
| -rw-r--r-- | src/background/actions/settings.js | 0 | ||||
| -rw-r--r-- | src/background/components/background.js | 12 | ||||
| -rw-r--r-- | src/content/actions/index.js | 5 | ||||
| -rw-r--r-- | src/content/actions/input.js | 9 | ||||
| -rw-r--r-- | src/content/components/keymapper.js | 16 | ||||
| -rw-r--r-- | src/content/index.js | 4 | ||||
| -rw-r--r-- | src/content/reducers/index.js | 3 | ||||
| -rw-r--r-- | src/content/reducers/input.js | 5 | ||||
| -rw-r--r-- | src/settings/actions/setting.js | 16 | ||||
| -rw-r--r-- | src/settings/components/index.jsx | 91 | ||||
| -rw-r--r-- | src/settings/components/setting.js | 45 | ||||
| -rw-r--r-- | src/settings/components/site.scss (renamed from src/settings/site.scss) | 2 | ||||
| -rw-r--r-- | src/settings/index.html | 10 | ||||
| -rw-r--r-- | src/settings/index.js | 15 | ||||
| -rw-r--r-- | src/settings/index.jsx | 18 | ||||
| -rw-r--r-- | src/settings/reducers/setting.js | 12 | ||||
| -rw-r--r-- | src/shared/default-settings.js | 1 | ||||
| -rw-r--r-- | src/shared/store/provider.jsx | 18 | ||||
| -rw-r--r-- | test/settings/reducers/setting.test.js | 12 | ||||
| -rw-r--r-- | webpack.config.js | 6 | 
23 files changed, 551 insertions, 135 deletions
| @@ -5,13 +5,14 @@      "browser" : true,      "webextensions": true    }, +  "plugins": ["react"],    "parser": "babel-eslint",    "parserOptions": {      "ecmaFeatures": {        "jsx": true      }    }, -  "extends": [ "eslint:all" ], +  "extends": [ "eslint:all", "plugin:react/recommended" ],    "rules": {      "array-bracket-newline": ["error", { "multiline": true }],      "array-element-newline": "off", @@ -27,6 +28,7 @@      "function-paren-newline": "off",      "id-length": "off",      "indent": ["error", 2], +    "jsx-quotes": ["error", "prefer-single"],      "max-statements": ["error", 15],      "multiline-ternary": "off",      "newline-after-var": "off", @@ -56,6 +58,9 @@      "sort-imports": "off",      "sort-keys": "off",      "sort-vars": "off", -    "space-before-function-paren": ["error", "never"] +    "space-before-function-paren": ["error", "never"], + +    "react/jsx-indent": ["error", 2], +    "react/prop-types": "off",    }  } diff --git a/package-lock.json b/package-lock.json index 7fbdd22..d7a09f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -180,6 +180,16 @@        "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",        "dev": true      }, +    "array-includes": { +      "version": "3.0.3", +      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", +      "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", +      "dev": true, +      "requires": { +        "define-properties": "1.1.2", +        "es-abstract": "1.9.0" +      } +    },      "array-slice": {        "version": "0.2.3",        "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", @@ -219,6 +229,12 @@        "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",        "dev": true      }, +    "asap": { +      "version": "2.0.6", +      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", +      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", +      "dev": true +    },      "asn1": {        "version": "0.2.3",        "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", @@ -399,6 +415,47 @@          "trim-right": "1.0.1"        }      }, +    "babel-helper-builder-react-jsx": { +      "version": "6.26.0", +      "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", +      "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", +      "dev": true, +      "requires": { +        "babel-runtime": "6.26.0", +        "babel-types": "6.26.0", +        "esutils": "2.0.2" +      }, +      "dependencies": { +        "babel-runtime": { +          "version": "6.26.0", +          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", +          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", +          "dev": true, +          "requires": { +            "core-js": "2.5.0", +            "regenerator-runtime": "0.11.0" +          } +        }, +        "babel-types": { +          "version": "6.26.0", +          "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", +          "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", +          "dev": true, +          "requires": { +            "babel-runtime": "6.26.0", +            "esutils": "2.0.2", +            "lodash": "4.17.4", +            "to-fast-properties": "1.0.3" +          } +        }, +        "regenerator-runtime": { +          "version": "0.11.0", +          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", +          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", +          "dev": true +        } +      } +    },      "babel-helper-call-delegate": {        "version": "6.24.1",        "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", @@ -530,6 +587,18 @@          "babel-runtime": "6.25.0"        }      }, +    "babel-plugin-syntax-flow": { +      "version": "6.18.0", +      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", +      "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", +      "dev": true +    }, +    "babel-plugin-syntax-jsx": { +      "version": "6.18.0", +      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", +      "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", +      "dev": true +    },      "babel-plugin-transform-es2015-arrow-functions": {        "version": "6.22.0",        "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", @@ -764,6 +833,56 @@          "regexpu-core": "2.0.0"        }      }, +    "babel-plugin-transform-flow-strip-types": { +      "version": "6.22.0", +      "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", +      "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", +      "dev": true, +      "requires": { +        "babel-plugin-syntax-flow": "6.18.0", +        "babel-runtime": "6.25.0" +      } +    }, +    "babel-plugin-transform-react-display-name": { +      "version": "6.25.0", +      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", +      "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", +      "dev": true, +      "requires": { +        "babel-runtime": "6.25.0" +      } +    }, +    "babel-plugin-transform-react-jsx": { +      "version": "6.24.1", +      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", +      "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", +      "dev": true, +      "requires": { +        "babel-helper-builder-react-jsx": "6.26.0", +        "babel-plugin-syntax-jsx": "6.18.0", +        "babel-runtime": "6.25.0" +      } +    }, +    "babel-plugin-transform-react-jsx-self": { +      "version": "6.22.0", +      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz", +      "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=", +      "dev": true, +      "requires": { +        "babel-plugin-syntax-jsx": "6.18.0", +        "babel-runtime": "6.25.0" +      } +    }, +    "babel-plugin-transform-react-jsx-source": { +      "version": "6.22.0", +      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", +      "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", +      "dev": true, +      "requires": { +        "babel-plugin-syntax-jsx": "6.18.0", +        "babel-runtime": "6.25.0" +      } +    },      "babel-plugin-transform-regenerator": {        "version": "6.24.1",        "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz", @@ -826,6 +945,29 @@          "babel-plugin-transform-regenerator": "6.24.1"        }      }, +    "babel-preset-flow": { +      "version": "6.23.0", +      "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", +      "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", +      "dev": true, +      "requires": { +        "babel-plugin-transform-flow-strip-types": "6.22.0" +      } +    }, +    "babel-preset-react": { +      "version": "6.24.1", +      "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", +      "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=", +      "dev": true, +      "requires": { +        "babel-plugin-syntax-jsx": "6.18.0", +        "babel-plugin-transform-react-display-name": "6.25.0", +        "babel-plugin-transform-react-jsx": "6.24.1", +        "babel-plugin-transform-react-jsx-self": "6.22.0", +        "babel-plugin-transform-react-jsx-source": "6.22.0", +        "babel-preset-flow": "6.23.0" +      } +    },      "babel-register": {        "version": "6.24.1",        "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz", @@ -1905,6 +2047,16 @@        "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",        "dev": true      }, +    "define-properties": { +      "version": "1.1.2", +      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", +      "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", +      "dev": true, +      "requires": { +        "foreach": "2.0.5", +        "object-keys": "1.0.11" +      } +    },      "defined": {        "version": "1.0.0",        "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", @@ -2129,6 +2281,15 @@        "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=",        "dev": true      }, +    "encoding": { +      "version": "0.1.12", +      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", +      "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", +      "dev": true, +      "requires": { +        "iconv-lite": "0.4.19" +      } +    },      "engine.io": {        "version": "1.8.3",        "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", @@ -2259,6 +2420,38 @@          "is-arrayish": "0.2.1"        }      }, +    "es-abstract": { +      "version": "1.9.0", +      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.9.0.tgz", +      "integrity": "sha512-kk3IJoKo7A3pWJc0OV8yZ/VEX2oSUytfekrJiqoxBlKJMFAJVJVpGdHClCCTdv+Fn2zHfpDHHIelMFhZVfef3Q==", +      "dev": true, +      "requires": { +        "es-to-primitive": "1.1.1", +        "function-bind": "1.1.1", +        "has": "1.0.1", +        "is-callable": "1.1.3", +        "is-regex": "1.0.4" +      }, +      "dependencies": { +        "function-bind": { +          "version": "1.1.1", +          "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", +          "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", +          "dev": true +        } +      } +    }, +    "es-to-primitive": { +      "version": "1.1.1", +      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", +      "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", +      "dev": true, +      "requires": { +        "is-callable": "1.1.3", +        "is-date-object": "1.0.1", +        "is-symbol": "1.0.1" +      } +    },      "es5-ext": {        "version": "0.10.27",        "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.27.tgz", @@ -2453,6 +2646,18 @@          }        }      }, +    "eslint-plugin-react": { +      "version": "7.4.0", +      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.4.0.tgz", +      "integrity": "sha512-tvjU9u3VqmW2vVuYnE8Qptq+6ji4JltjOjJ9u7VAOxVYkUkyBZWRvNYKbDv5fN+L6wiA+4we9+qQahZ0m63XEA==", +      "dev": true, +      "requires": { +        "doctrine": "2.0.0", +        "has": "1.0.1", +        "jsx-ast-utils": "2.0.1", +        "prop-types": "15.6.0" +      } +    },      "eslint-scope": {        "version": "3.7.1",        "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", @@ -2668,6 +2873,29 @@        "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=",        "dev": true      }, +    "fbjs": { +      "version": "0.8.16", +      "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", +      "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", +      "dev": true, +      "requires": { +        "core-js": "1.2.7", +        "isomorphic-fetch": "2.2.1", +        "loose-envify": "1.3.1", +        "object-assign": "4.1.1", +        "promise": "7.3.1", +        "setimmediate": "1.0.5", +        "ua-parser-js": "0.7.14" +      }, +      "dependencies": { +        "core-js": { +          "version": "1.2.7", +          "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", +          "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", +          "dev": true +        } +      } +    },      "figures": {        "version": "2.0.0",        "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -2774,6 +3002,12 @@          "for-in": "1.0.2"        }      }, +    "foreach": { +      "version": "2.0.5", +      "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", +      "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", +      "dev": true +    },      "forever-agent": {        "version": "0.6.1",        "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -3554,6 +3788,18 @@          "builtin-modules": "1.1.1"        }      }, +    "is-callable": { +      "version": "1.1.3", +      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", +      "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", +      "dev": true +    }, +    "is-date-object": { +      "version": "1.0.1", +      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", +      "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", +      "dev": true +    },      "is-dotfile": {        "version": "1.0.3",        "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", @@ -3682,6 +3928,15 @@        "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",        "dev": true      }, +    "is-regex": { +      "version": "1.0.4", +      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", +      "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", +      "dev": true, +      "requires": { +        "has": "1.0.1" +      } +    },      "is-resolvable": {        "version": "1.0.0",        "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", @@ -3706,6 +3961,12 @@          "html-comment-regex": "1.1.1"        }      }, +    "is-symbol": { +      "version": "1.0.1", +      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", +      "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", +      "dev": true +    },      "is-typedarray": {        "version": "1.0.0",        "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -3745,6 +4006,16 @@          "isarray": "1.0.0"        }      }, +    "isomorphic-fetch": { +      "version": "2.2.1", +      "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", +      "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", +      "dev": true, +      "requires": { +        "node-fetch": "1.7.3", +        "whatwg-fetch": "2.0.3" +      } +    },      "isstream": {        "version": "0.1.2",        "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -3863,6 +4134,15 @@          }        }      }, +    "jsx-ast-utils": { +      "version": "2.0.1", +      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", +      "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", +      "dev": true, +      "requires": { +        "array-includes": "3.0.3" +      } +    },      "karma": {        "version": "1.7.0",        "resolved": "https://registry.npmjs.org/karma/-/karma-1.7.0.tgz", @@ -4652,6 +4932,16 @@          "lower-case": "1.1.4"        }      }, +    "node-fetch": { +      "version": "1.7.3", +      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", +      "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", +      "dev": true, +      "requires": { +        "encoding": "0.1.12", +        "is-stream": "1.1.0" +      } +    },      "node-gyp": {        "version": "3.6.2",        "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz", @@ -4866,6 +5156,12 @@        "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=",        "dev": true      }, +    "object-keys": { +      "version": "1.0.11", +      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", +      "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", +      "dev": true +    },      "object.omit": {        "version": "2.0.1",        "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", @@ -5766,6 +6062,26 @@        "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",        "dev": true      }, +    "promise": { +      "version": "7.3.1", +      "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", +      "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", +      "dev": true, +      "requires": { +        "asap": "2.0.6" +      } +    }, +    "prop-types": { +      "version": "15.6.0", +      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz", +      "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", +      "dev": true, +      "requires": { +        "fbjs": "0.8.16", +        "loose-envify": "1.3.1", +        "object-assign": "4.1.1" +      } +    },      "prr": {        "version": "0.0.0",        "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", @@ -5912,6 +6228,30 @@          }        }      }, +    "react": { +      "version": "16.0.0", +      "resolved": "https://registry.npmjs.org/react/-/react-16.0.0.tgz", +      "integrity": "sha1-zn348ZQbA28Cssyp29DLHw6FXi0=", +      "dev": true, +      "requires": { +        "fbjs": "0.8.16", +        "loose-envify": "1.3.1", +        "object-assign": "4.1.1", +        "prop-types": "15.6.0" +      } +    }, +    "react-dom": { +      "version": "16.0.0", +      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.0.0.tgz", +      "integrity": "sha1-nMMHnD3NcNTG4BuEqrKn40wwP1g=", +      "dev": true, +      "requires": { +        "fbjs": "0.8.16", +        "loose-envify": "1.3.1", +        "object-assign": "4.1.1", +        "prop-types": "15.6.0" +      } +    },      "read-pkg": {        "version": "2.0.0",        "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -6853,15 +7193,6 @@        "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",        "dev": true      }, -    "string_decoder": { -      "version": "1.0.3", -      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", -      "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", -      "dev": true, -      "requires": { -        "safe-buffer": "5.1.1" -      } -    },      "string-width": {        "version": "2.1.1",        "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -6895,6 +7226,15 @@          }        }      }, +    "string_decoder": { +      "version": "1.0.3", +      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", +      "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", +      "dev": true, +      "requires": { +        "safe-buffer": "5.1.1" +      } +    },      "stringstream": {        "version": "0.0.5",        "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", @@ -7176,6 +7516,12 @@        "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",        "dev": true      }, +    "ua-parser-js": { +      "version": "0.7.14", +      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz", +      "integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o=", +      "dev": true +    },      "uglify-js": {        "version": "2.8.29",        "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", @@ -7475,6 +7821,12 @@          "source-map": "0.5.6"        }      }, +    "whatwg-fetch": { +      "version": "2.0.3", +      "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", +      "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=", +      "dev": true +    },      "whet.extend": {        "version": "0.9.9",        "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", diff --git a/package.json b/package.json index 36ac096..42da3a7 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@    "description": "Vim vixen",    "scripts": {      "start": "webpack -w --debug", -    "lint": "eslint src", +    "lint": "eslint --ext .jsx,.js src",      "test": "karma start"    },    "repository": { @@ -20,10 +20,13 @@      "babel-cli": "^6.24.1",      "babel-eslint": "^7.2.3",      "babel-loader": "^7.1.1", +    "babel-plugin-transform-react-jsx": "^6.24.1",      "babel-preset-es2015": "^6.24.1", +    "babel-preset-react": "^6.24.1",      "chai": "^4.1.1",      "css-loader": "^0.28.4",      "eslint": "^4.7.0", +    "eslint-plugin-react": "^7.4.0",      "html-webpack-plugin": "^2.30.1",      "karma": "^1.7.0",      "karma-firefox-launcher": "^1.0.1", @@ -34,6 +37,8 @@      "karma-webpack": "^2.0.4",      "mocha": "^3.5.0",      "node-sass": "^4.5.3", +    "react": "^16.0.0", +    "react-dom": "^16.0.0",      "sass-loader": "^6.0.6",      "style-loader": "^0.18.2",      "webpack": "^3.5.3" diff --git a/src/background/actions/settings.js b/src/background/actions/settings.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/background/actions/settings.js diff --git a/src/background/components/background.js b/src/background/components/background.js index 0570a5a..06b6900 100644 --- a/src/background/components/background.js +++ b/src/background/components/background.js @@ -22,15 +22,7 @@ export default class BackgroundComponent {    }    update() { -    let state = this.store.getState(); -    this.updateSettings(state); -  } - -  updateSettings(setting) { -    if (!setting.settings.json) { -      return; -    } -    this.settings = JSON.parse(setting.settings.json); +    this.settings = this.store.getState();    }    onMessage(message, sender) { @@ -58,7 +50,7 @@ export default class BackgroundComponent {          });        });      case messages.SETTINGS_QUERY: -      return Promise.resolve(this.store.getState().settings); +      return Promise.resolve(this.store.getState().value);      case messages.CONSOLE_QUERY_COMPLETIONS:        return commands.complete(message.text, this.settings);      case messages.SETTINGS_RELOAD: diff --git a/src/content/actions/index.js b/src/content/actions/index.js index 0b3749d..f8db948 100644 --- a/src/content/actions/index.js +++ b/src/content/actions/index.js @@ -2,16 +2,13 @@ export default {    // User input    INPUT_KEY_PRESS: 'input.key,press',    INPUT_CLEAR_KEYS: 'input.clear.keys', -  INPUT_SET_KEYMAPS: 'input.set,keymaps', +  INPUT_SET_KEYMAPS: 'input.set.keymaps',    // Completion    COMPLETION_SET_ITEMS: 'completion.set.items',    COMPLETION_SELECT_NEXT: 'completions.select.next',    COMPLETION_SELECT_PREV: 'completions.select.prev', -  // Settings -  SETTING_SET_SETTINGS: 'setting.set.settings', -    // Follow    FOLLOW_ENABLE: 'follow.enable',    FOLLOW_DISABLE: 'follow.disable', diff --git a/src/content/actions/input.js b/src/content/actions/input.js index cc4efac..10ff835 100644 --- a/src/content/actions/input.js +++ b/src/content/actions/input.js @@ -20,4 +20,11 @@ const clearKeys = () => {    };  }; -export { keyPress, clearKeys }; +const setKeymaps = (keymaps) => { +  return { +    type: actions.INPUT_SET_KEYMAPS, +    keymaps, +  }; +}; + +export { keyPress, clearKeys, setKeymaps }; diff --git a/src/content/components/keymapper.js b/src/content/components/keymapper.js index 8f2cead..655c3f2 100644 --- a/src/content/components/keymapper.js +++ b/src/content/components/keymapper.js @@ -10,14 +10,10 @@ export default class KeymapperComponent {    }    key(key, ctrl) { -    let keymaps = this.keymaps(); -    if (!keymaps) { -      return; -    }      this.store.dispatch(inputActions.keyPress(key, ctrl));      let input = this.store.getState().input; -    let matched = Object.keys(keymaps).filter((keyStr) => { +    let matched = Object.keys(input.keymaps).filter((keyStr) => {        return keyStr.startsWith(input.keys);      });      if (matched.length === 0) { @@ -27,17 +23,9 @@ export default class KeymapperComponent {        matched.length === 1 && input.keys !== matched[0]) {        return true;      } -    let operation = keymaps[matched]; +    let operation = input.keymaps[matched];      this.store.dispatch(operationActions.exec(operation));      this.store.dispatch(inputActions.clearKeys());      return true;    } - -  keymaps() { -    let settings = this.store.getState().setting.settings; -    if (!settings || !settings.json) { -      return null; -    } -    return JSON.parse(settings.json).keymaps; -  }  } diff --git a/src/content/index.js b/src/content/index.js index 63bbf77..64d86bb 100644 --- a/src/content/index.js +++ b/src/content/index.js @@ -1,6 +1,6 @@  import './console-frame.scss';  import * as consoleFrames from './console-frames'; -import * as settingActions from 'settings/actions/setting'; +import * as inputActions from './actions/input';  import { createStore } from 'shared/store';  import ContentInputComponent from 'content/components/content-input';  import KeymapperComponent from 'content/components/keymapper'; @@ -34,7 +34,7 @@ const reloadSettings = () => {    return browser.runtime.sendMessage({      type: messages.SETTINGS_QUERY,    }).then((settings) => { -    store.dispatch(settingActions.set(settings)); +    store.dispatch(inputActions.setKeymaps(settings.keymaps));    });  }; diff --git a/src/content/reducers/index.js b/src/content/reducers/index.js index a62217f..c026a19 100644 --- a/src/content/reducers/index.js +++ b/src/content/reducers/index.js @@ -1,18 +1,15 @@ -import settingReducer from 'settings/reducers/setting';  import inputReducer from './input';  import followReducer from './follow';  // Make setting reducer instead of re-use  const defaultState = {    input: inputReducer(undefined, {}), -  setting: settingReducer(undefined, {}),    follow: followReducer(undefined, {}),  };  export default function reducer(state = defaultState, action = {}) {    return Object.assign({}, state, {      input: inputReducer(state.input, action), -    setting: settingReducer(state.setting, action),      follow: followReducer(state.follow, action),    });  } diff --git a/src/content/reducers/input.js b/src/content/reducers/input.js index 802020f..c79b206 100644 --- a/src/content/reducers/input.js +++ b/src/content/reducers/input.js @@ -2,6 +2,7 @@ import actions from 'content/actions';  const defaultState = {    keys: '', +  keymaps: {},  };  export default function reducer(state = defaultState, action = {}) { @@ -14,6 +15,10 @@ export default function reducer(state = defaultState, action = {}) {      return Object.assign({}, state, {        keys: '',      }); +  case actions.INPUT_SET_KEYMAPS: +    return Object.assign({}, state, { +      keymaps: action.keymaps, +    });    default:      return state;    } diff --git a/src/settings/actions/setting.js b/src/settings/actions/setting.js index 697bcf0..c1b27c8 100644 --- a/src/settings/actions/setting.js +++ b/src/settings/actions/setting.js @@ -3,9 +3,9 @@ import messages from 'shared/messages';  import DefaultSettings from 'shared/default-settings';  const load = () => { -  return browser.storage.local.get('settings').then((value) => { -    if (value.settings) { -      return set(value.settings); +  return browser.storage.local.get('settings').then(({ settings }) => { +    if (settings) { +      return set(settings);      }      return set(DefaultSettings);    }, console.error); @@ -13,10 +13,12 @@ const load = () => {  const save = (settings) => {    return browser.storage.local.set({ -    settings +    settings,    }).then(() => {      return browser.runtime.sendMessage({        type: messages.SETTINGS_RELOAD +    }).then(() => { +      return set(settings);      });    });  }; @@ -24,8 +26,10 @@ const save = (settings) => {  const set = (settings) => {    return {      type: actions.SETTING_SET_SETTINGS, -    settings, +    source: settings.source, +    json: settings.json, +    value: JSON.parse(settings.json),    };  }; -export { load, save, set }; +export { load, save }; diff --git a/src/settings/components/index.jsx b/src/settings/components/index.jsx new file mode 100644 index 0000000..4418942 --- /dev/null +++ b/src/settings/components/index.jsx @@ -0,0 +1,91 @@ +import './site.scss'; +import React from 'react'; +import PropTypes from 'prop-types'; +import * as settingActions from 'settings/actions/setting'; +import * as validator from 'shared/validators/setting'; + +class SettingsComponent extends React.Component { +  constructor(props, context) { +    super(props, context); + +    this.state = { +      settings: { +        json: '', +      } +    }; +    this.context.store.subscribe(this.stateChanged.bind(this)); +  } + +  componentDidMount() { +    this.context.store.dispatch(settingActions.load()); +  } + +  stateChanged() { +    let settings = this.context.store.getState(); +    this.setState({ +      settings: { +        source: settings.source, +        json: settings.json, +      } +    }); +  } + +  render() { +    return ( +      <div> +        <h1>Configure Vim-Vixen</h1> +        <form className='vimvixen-settings-form'> + +          <p>Load settings from:</p> +          <input type='radio' id='setting-source-json' +            name='source' +            value='json' +            onChange={this.bindAndSave.bind(this)} +            checked={this.state.settings.source === 'json'} /> +          <label htmlFor='settings-source-json'>JSON</label> + +          <textarea name='json' spellCheck='false' +            onInput={this.validate.bind(this)} +            onChange={this.bindValue.bind(this)} +            onBlur={this.bindAndSave.bind(this)} +            value={this.state.settings.json} /> +        </form> +      </div> +    ); +  } + +  validate(e) { +    try { +      let settings = JSON.parse(e.target.value); +      validator.validate(settings); +      e.target.setCustomValidity(''); +    } catch (err) { +      e.target.setCustomValidity(err.message); +    } +  } + +  bindValue(e) { +    let nextSettings = Object.assign({}, this.state.settings); +    nextSettings[e.target.name] = e.target.value; + +    this.setState({ settings: nextSettings }); +  } + +  bindAndSave(e) { +    this.bindValue(e); + +    try { +      let json = this.state.settings.json; +      validator.validate(JSON.parse(json)); +      this.context.store.dispatch(settingActions.save(this.state.settings)); +    } catch (err) { +      // error already shown +    } +  } +} + +SettingsComponent.contextTypes = { +  store: PropTypes.any, +}; + +export default SettingsComponent; diff --git a/src/settings/components/setting.js b/src/settings/components/setting.js deleted file mode 100644 index 14482a3..0000000 --- a/src/settings/components/setting.js +++ /dev/null @@ -1,45 +0,0 @@ -import * as settingActions from 'settings/actions/setting'; -import { validate } from 'shared/validators/setting'; - -export default class SettingComponent { -  constructor(wrapper, store) { -    this.wrapper = wrapper; -    this.store = store; - -    let doc = wrapper.ownerDocument; -    let form = doc.getElementById('vimvixen-settings-form'); -    form.addEventListener('submit', this.onSubmit.bind(this)); - -    let plainJson = form.elements['plain-json']; -    plainJson.addEventListener('input', this.onPlainJsonChanged.bind(this)); - -    store.dispatch(settingActions.load()); -  } - -  onSubmit(e) { -    let settings = { -      json: e.target.elements['plain-json'].value, -    }; -    this.store.dispatch(settingActions.save(settings)); -    e.preventDefault(); -  } - -  onPlainJsonChanged(e) { -    try { -      let settings = JSON.parse(e.target.value); -      validate(settings); -      e.target.setCustomValidity(''); -    } catch (err) { -      e.target.setCustomValidity(err.message); -    } -  } - -  update() { -    let { settings } = this.store.getState(); - -    let doc = this.wrapper.ownerDocument; -    let form = doc.getElementById('vimvixen-settings-form'); -    let plainJsonInput = form.elements['plain-json']; -    plainJsonInput.value = settings.json; -  } -} diff --git a/src/settings/site.scss b/src/settings/components/site.scss index 5707c8a..fae9c39 100644 --- a/src/settings/site.scss +++ b/src/settings/components/site.scss @@ -1,5 +1,5 @@  .vimvixen-settings-form { -  textarea[name=plain-json] { +  textarea[name=json] {      font-family: monospace;      width: 100%;      min-height: 64ex; diff --git a/src/settings/index.html b/src/settings/index.html index 99d6c6b..6fe00df 100644 --- a/src/settings/index.html +++ b/src/settings/index.html @@ -4,15 +4,7 @@      <meta charset='utf-8'>    </head>    <body> -    <h1>Configure</h1> - -    <h2>Home page</h2> -    <form id='vimvixen-settings-form' class='vimvixen-settings-form'> -      <label for='load-from-json'>Load from JSON:</label> -      <textarea name='plain-json' spellcheck='false'></textarea> - -      <button type='submit'>Save</button> -    </form> +    <div id='vimvixen-settings'></div>      <script src='settings.js'></script>    </body>  </html> diff --git a/src/settings/index.js b/src/settings/index.js deleted file mode 100644 index c8d6cc4..0000000 --- a/src/settings/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import './site.scss'; -import SettingComponent from 'settings/components/setting'; -import settingReducer from 'settings/reducers/setting'; -import { createStore } from 'shared/store'; - -const store = createStore(settingReducer); -let settingComponent = null; - -store.subscribe(() => { -  settingComponent.update(); -}); - -document.addEventListener('DOMContentLoaded', () => { -  settingComponent = new SettingComponent(document.body, store); -}); diff --git a/src/settings/index.jsx b/src/settings/index.jsx new file mode 100644 index 0000000..7516fb7 --- /dev/null +++ b/src/settings/index.jsx @@ -0,0 +1,18 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import SettingsComponent from './components'; +import reducer from 'settings/reducers/setting'; +import Provider from 'shared/store/provider'; +import { createStore } from 'shared/store'; + +const store = createStore(reducer); + +document.addEventListener('DOMContentLoaded', () => { +  let wrapper = document.getElementById('vimvixen-settings'); +  ReactDOM.render( +    <Provider store={store}> +      <SettingsComponent /> +    </Provider>, +    wrapper +  ); +}); diff --git a/src/settings/reducers/setting.js b/src/settings/reducers/setting.js index f7d9242..a61c09f 100644 --- a/src/settings/reducers/setting.js +++ b/src/settings/reducers/setting.js @@ -1,15 +1,19 @@  import actions from 'settings/actions';  const defaultState = { -  settings: {} +  source: '', +  json: '', +  value: {}  };  export default function reducer(state = defaultState, action = {}) {    switch (action.type) {    case actions.SETTING_SET_SETTINGS: -    return Object.assign({}, state, { -      settings: action.settings, -    }); +    return { +      source: action.source, +      json: action.json, +      value: action.value, +    };    default:      return state;    } diff --git a/src/shared/default-settings.js b/src/shared/default-settings.js index 24ac536..f287b7a 100644 --- a/src/shared/default-settings.js +++ b/src/shared/default-settings.js @@ -1,4 +1,5 @@  export default { +  source: 'json',    json: `{    "keymaps": {      "0": { "type": "scroll.home" }, diff --git a/src/shared/store/provider.jsx b/src/shared/store/provider.jsx new file mode 100644 index 0000000..743f656 --- /dev/null +++ b/src/shared/store/provider.jsx @@ -0,0 +1,18 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +class Provider extends React.PureComponent { +  getChildContext() { +    return { store: this.props.store }; +  } + +  render() { +    return React.Children.only(this.props.children); +  } +} + +Provider.childContextTypes = { +  store: PropTypes.any, +}; + +export default Provider; diff --git a/test/settings/reducers/setting.test.js b/test/settings/reducers/setting.test.js index 0e84247..3468d4b 100644 --- a/test/settings/reducers/setting.test.js +++ b/test/settings/reducers/setting.test.js @@ -5,18 +5,18 @@ import settingReducer from 'settings/reducers/setting';  describe("setting reducer", () => {    it('return the initial state', () => {      let state = settingReducer(undefined, {}); -    expect(state).to.have.deep.property('settings', {}); +    expect(state).to.have.deep.property('json', ''); +    expect(state).to.have.deep.property('value', {});    });    it('return next state for SETTING_SET_SETTINGS', () => {      let action = {        type: actions.SETTING_SET_SETTINGS, -      settings: { value1: 'hello', value2: 'world' }, +      json: '{ "key": "value" }', +      value: { key: 123 },      };      let state = settingReducer(undefined, action); -    expect(state).to.have.deep.property('settings', { -      value1: 'hello', -      value2: 'world', -    }); +    expect(state).to.have.deep.property('json', '{ "key": "value" }'); +    expect(state).to.have.deep.property('value', { key: 123 });    });  }); diff --git a/webpack.config.js b/webpack.config.js index fff49d1..977ea75 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -20,11 +20,11 @@ module.exports = {    module: {      loaders: [        { -        test: /\.js$/, +        test: [ /\.js$/,  /\.jsx$/ ],          exclude: /node_modules/,          loader: 'babel-loader',          query: { -          presets: [ 'es2015' ] +          presets: ['es2015', 'react']          }        },        { @@ -39,7 +39,7 @@ module.exports = {    },    resolve: { -    extensions: [ '.js' ], +    extensions: [ '.js', '.jsx' ],      modules: [path.join(__dirname, 'src'), 'node_modules']    }, | 
