├── .babelrc ├── .circleci └── config.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmrc ├── .travis.yml ├── CHANGELOG.md ├── Icons.sketch ├── LICENSE ├── README.md ├── integration_test ├── .npmrc ├── jest.config.js ├── launch.js ├── package.json ├── setup.js ├── test │ └── extension.test.ts ├── tsconfig.json ├── typings.d.ts ├── util.ts └── yarn.lock ├── package.json ├── privacy_policy.md ├── renovate.json ├── screenshots ├── Screen Shot 2021-04-03 at 9.07.52 PM.png ├── Screen Shot 2021-04-03 at 9.08.57 PM.png └── Screen Shot 2021-04-03 at 9.09.09 PM.png ├── src ├── css │ └── style.css ├── font │ ├── LICENSE.txt │ └── customjs.svg ├── img │ ├── icon-128.png │ ├── icon-16.png │ ├── icon-19.png │ ├── icon-32.png │ ├── icon-38.png │ └── icon-48.png ├── js │ ├── background.tsx │ ├── base.tsx │ ├── components │ │ ├── App.tsx │ │ ├── AutoSave.tsx │ │ ├── DonateLink.tsx │ │ ├── Editor.tsx │ │ ├── Error.tsx │ │ ├── Format.tsx │ │ ├── Goto.tsx │ │ ├── HostTable.tsx │ │ ├── HostTableLink.tsx │ │ ├── Hosts.tsx │ │ ├── Include │ │ │ ├── Extra.tsx │ │ │ └── index.tsx │ │ ├── LoadError.tsx │ │ ├── Loading.tsx │ │ ├── ModeSelect.tsx │ │ ├── NewPattern.tsx │ │ ├── NewTabLink.tsx │ │ ├── Page.tsx │ │ ├── RemoveDraft.tsx │ │ ├── Reset.tsx │ │ ├── Save.tsx │ │ ├── Size.tsx │ │ ├── StoreContext.tsx │ │ └── Toggle.tsx │ ├── libs │ │ └── index.tsx │ ├── popup.tsx │ ├── run.tsx │ └── stores │ │ ├── AppStore.tsx │ │ ├── IncludeStore.tsx │ │ ├── NewPatternStore.tsx │ │ └── index.tsx ├── manifest.json └── popup.html ├── tsconfig.json ├── utils ├── analyze.js ├── build.js ├── env.js └── webserver.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "targets": { "chrome": 52 } 7 | } 8 | ], 9 | "react", 10 | "stage-2" 11 | ], 12 | "plugins": [ 13 | "transform-decorators-legacy", 14 | ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }] 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | references: 3 | yarn_key: &yarn_key 4 | v1-dependencies-{{ checksum "yarn.lock" }} 5 | 6 | save_yarn_cache: &save_yarn_cache 7 | save_cache: 8 | key: *yarn_key 9 | paths: 10 | - node_modules 11 | 12 | restore_yarn_cache: &restore_yarn_cache 13 | restore_cache: 14 | key: *yarn_key 15 | 16 | jobs: 17 | build-test: 18 | docker: 19 | - image: circleci/node:17.2.0 20 | steps: 21 | - checkout 22 | - *restore_yarn_cache 23 | - run: yarn --ignore-engines 24 | - *save_yarn_cache 25 | - run: yarn lint:ts 26 | - run: yarn test 27 | - run: yarn deploy 28 | - run: ls -l build/* build.zip 29 | - run: sha256sum build.zip | tee SHA256SUMS.txt 30 | - run: find build -type f -exec ls {} \; | sort -f | xargs sha256sum | tee -a SHA256SUMS.txt 31 | - run: find build -type f -exec ls {} \; | sort -f | xargs cat | sha256sum | tee -a SHA256SUMS.txt 32 | - run: mkdir dist && mv build.zip SHA256SUMS.txt dist 33 | - store_artifacts: 34 | path: dist 35 | destination: dist 36 | 37 | integration-test: 38 | docker: 39 | - image: circleci/node:17.2.0-browsers 40 | steps: 41 | - checkout 42 | - *restore_yarn_cache 43 | - run: yarn --ignore-engines 44 | - *save_yarn_cache 45 | - run: yarn deploy 46 | - run: cd integration_test && yarn install && yarn test 47 | 48 | publish: 49 | docker: 50 | - image: circleci/node:17.2.0 51 | steps: 52 | - checkout 53 | - *restore_yarn_cache 54 | - run: yarn install 55 | - *save_yarn_cache 56 | - run: yarn deploy 57 | - run: yarn upload:chrome 58 | - run: yarn publish-extension 59 | 60 | workflows: 61 | version: 2 62 | build: 63 | jobs: 64 | - build-test: 65 | filters: 66 | tags: 67 | only: /^v.*/ 68 | - integration-test: 69 | filters: 70 | tags: 71 | only: /^v.*/ 72 | - publish: 73 | requires: 74 | - build-test 75 | - integration-test 76 | filters: 77 | branches: 78 | ignore: /.*/ 79 | tags: 80 | only: /^v.*/ 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | build/ 4 | extension.zip 5 | build.zip 6 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname $0)/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.yarnpkg.com 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: yarn 3 | before_install: 4 | - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.1.0 5 | - export PATH="$HOME/.yarn/bin:$PATH" 6 | node_js: 7 | - "15" 8 | script: 9 | - yarn test 10 | - yarn deploy 11 | - ls -l build/* build.zip 12 | - sha256sum build.zip | tee SHA256SUMS.txt 13 | - find build -type f -exec ls {} \; | sort -f | xargs sha256sum | tee -a SHA256SUMS.txt 14 | - find build -type f -exec ls {} \; | sort -f | xargs cat | sha256sum | tee -a SHA256SUMS.txt 15 | deploy: 16 | provider: releases 17 | api_key: $GITHUB_OAUTH_TOKEN 18 | file: 19 | - build.zip 20 | - SHA256SUMS.txt 21 | skip_cleanup: true 22 | on: 23 | tags: true 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [3.4.12](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.11...v3.4.12) (2022-05-18) 6 | 7 | ### [3.4.11](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.10...v3.4.11) (2022-05-18) 8 | 9 | ### Bug Fixes 10 | 11 | - **deps:** update dependency antd to v4.16.11 ([#546](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/546)) ([316d081](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/316d081cd20b71a78e46f0b5f0bd26a3c2f1ff4f)) 12 | - **deps:** update dependency antd to v4.16.12 ([#553](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/553)) ([f975561](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/f9755617169de5ef66c44b863067b50764c0b9ba)) 13 | - **deps:** update dependency antd to v4.16.13 ([#559](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/559)) ([229f227](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/229f22762c795af27632ea50a343aebd26e92e46)) 14 | - **deps:** update dependency antd to v4.17.1 ([#627](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/627)) ([39f11ee](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/39f11ee82091e62d3f414b4d267a4a027b1bfdcf)) 15 | - **deps:** update dependency antd to v4.17.2 ([#637](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/637)) ([1dfbe3d](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/1dfbe3d19e45ad8fddd7e302c3366ef9f346d608)) 16 | - **deps:** update dependency antd to v4.17.3 ([#656](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/656)) ([1b922c8](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/1b922c86191b816b7ea2018e93dbf32c674e25ea)) 17 | - **deps:** update dependency antd to v4.17.4 ([#673](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/673)) ([7356771](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/73567717d049ecce332f0ecdd903008dd727c5a9)) 18 | - **deps:** update dependency antd to v4.18.0 ([#682](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/682)) ([4ba4696](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/4ba4696077301101baeb45b59c5abfd3ccae60c9)) 19 | - **deps:** update dependency antd to v4.18.1 ([#684](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/684)) ([e8444f5](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/e8444f53502f42bcfceccc5a27d50a98d2847ee1)) 20 | - **deps:** update dependency antd to v4.18.2 ([#687](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/687)) ([652bd1d](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/652bd1d951ec3b8ff274a1848480d82c1fa7797d)) 21 | - **deps:** update dependency antd to v4.18.3 ([#704](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/704)) ([dd87799](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/dd87799109921404f29890c928db08cf27d54b68)) 22 | - **deps:** update dependency antd to v4.18.4 ([#717](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/717)) ([455bfc3](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/455bfc3d39e674213b8fa6764feab994aee6b2cf)) 23 | - **deps:** update dependency antd to v4.18.5 ([#726](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/726)) ([84b0feb](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/84b0febe6473618b678957ce3c8d69606914ea35)) 24 | - **deps:** update dependency antd to v4.18.6 ([#748](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/748)) ([f98715a](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/f98715a96ff415c111e3129f5f146ae80ce3bf82)) 25 | - **deps:** update dependency antd to v4.18.7 ([#754](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/754)) ([3265043](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/326504311efa8ff3f58458cd2bbf055f46a3d2f3)) 26 | - **deps:** update dependency antd to v4.18.8 ([#763](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/763)) ([148e23f](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/148e23fa9fd04acaa24f0e57c0641be7429fafdd)) 27 | - **deps:** update dependency antd to v4.18.9 ([#769](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/769)) ([38891a2](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/38891a2030e6fdf5985f799bdf10cf8a09a22220)) 28 | - **deps:** update dependency antd to v4.19.0 ([#781](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/781)) ([a9cd0bf](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/a9cd0bff91099eb556a354f5b4daa8cdca4425bd)) 29 | - **deps:** update dependency antd to v4.19.1 ([#782](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/782)) ([ba5a135](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/ba5a1358d61540a1ce20d943a55c7572de676850)) 30 | - **deps:** update dependency antd to v4.19.2 ([#786](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/786)) ([240aa20](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/240aa206db7d5833d808f86e7ed3b7b28da0a251)) 31 | - **deps:** update dependency antd to v4.19.3 ([#798](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/798)) ([3703004](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/3703004a976da7b9fda192d24a8063d329c2a72f)) 32 | - **deps:** update dependency antd to v4.19.4 ([#807](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/807)) ([dcb0a52](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/dcb0a525d2efb57f862af84ff44297ac14c23102)) 33 | - **deps:** update dependency antd to v4.19.5 ([#816](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/816)) ([640813e](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/640813e58496003bc67f40298837f8cfc99adc17)) 34 | - **deps:** update dependency antd to v4.20.2 ([#849](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/849)) ([f1090b7](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/f1090b75c504ae89cdeb4b312a5e1833ff6a8d06)) 35 | - **deps:** update dependency clean-webpack-plugin to v4.0.0 ([#569](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/569)) ([adfcf2a](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/adfcf2a6c6e9e68cee0086e278280c017d838b4d)) 36 | - **deps:** update dependency js-beautify to v1.14.1 ([#808](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/808)) ([40240bf](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/40240bfadcfa65cc93d29808c670d03149255b8d)) 37 | - **deps:** update dependency js-beautify to v1.14.2 ([#810](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/810)) ([b447b09](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/b447b09d8cb8d05b960c7ba77d52a9db14f50950)) 38 | - **deps:** update dependency js-beautify to v1.14.3 ([#829](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/829)) ([6050c9b](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/6050c9b5d3886d4fc299f62edd6aed2af3eecfc9)) 39 | - **deps:** update dependency mobx to v6.3.10 ([#677](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/677)) ([12c1747](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/12c1747381709236de267670ded857b81f5a2f62)) 40 | - **deps:** update dependency mobx to v6.3.11 ([#695](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/695)) ([fd163c6](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/fd163c62f6dda96743f5db8dd3673449ae69c2df)) 41 | - **deps:** update dependency mobx to v6.3.12 ([#696](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/696)) ([1e655b3](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/1e655b3bbfd4ed1e62dfa8081584521cc7c48b9b)) 42 | - **deps:** update dependency mobx to v6.3.13 ([#724](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/724)) ([1b44eac](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/1b44eacab6b892004464997fd476027927662afc)) 43 | - **deps:** update dependency mobx to v6.3.6 ([#567](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/567)) ([4a9104f](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/4a9104f754559892c428e94024c6119c6442d240)) 44 | - **deps:** update dependency mobx to v6.3.7 ([#607](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/607)) ([592d351](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/592d3516939b1d398b3bb7e78da5f233e7298421)) 45 | - **deps:** update dependency mobx to v6.3.8 ([#639](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/639)) ([64fd216](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/64fd216ced1e0816bbe713ea82ac196b7b88fad9)) 46 | - **deps:** update dependency mobx to v6.3.9 ([#668](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/668)) ([d229356](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/d2293568d345c312000cff7cdea1034bfa8b880e)) 47 | - **deps:** update dependency mobx to v6.4.0 ([#760](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/760)) ([f94f33c](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/f94f33c6934b64d02545c9bc8e587294b407336c)) 48 | - **deps:** update dependency mobx to v6.4.1 ([#762](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/762)) ([eebee16](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/eebee163b62305890fa89a6415036c8144b8ba9c)) 49 | - **deps:** update dependency mobx to v6.4.2 ([#768](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/768)) ([a89e18f](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/a89e18f2753bf9250e4ae150b072c116562e4246)) 50 | - **deps:** update dependency mobx to v6.5.0 ([#794](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/794)) ([74a3ada](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/74a3ada6b99120d8a3395a0a3cc52547d9cf6e92)) 51 | - **deps:** update dependency mobx-react to v7.2.1 ([#584](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/584)) ([15602d4](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/15602d400f218c3e6744558be5fe931a748e9891)) 52 | - **deps:** update dependency mobx-react to v7.3.0 ([#761](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/761)) ([547c4d5](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/547c4d5266c23baa54913e7ccab68f350ea68c9b)) 53 | - **deps:** update dependency mobx-react to v7.4.0 ([#850](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/850)) ([a08521c](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/a08521c74abee8f8eaa12d60f8742ede2ba8fb11)) 54 | - **deps:** update dependency object-sizeof to v1.6.2 ([#752](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/752)) ([3b20710](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/3b20710ee6c66d9356dad9236be3066604d29861)) 55 | - **deps:** update dependency object-sizeof to v1.6.3 ([#757](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/757)) ([739f210](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/739f2102f34ee73e4259a290869f4308b4819917)) 56 | - **deps:** update dependency query-string to v7.1.0 ([#698](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/698)) ([48e886b](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/48e886b5aab76d632bad784d83165fd912c06423)) 57 | - **deps:** update dependency query-string to v7.1.1 ([#746](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/746)) ([6eb6ed9](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/6eb6ed9353056b8942b6ade137455a748e9ce513)) 58 | - **deps:** update dependency react-ace to v9.5.0 ([#577](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/577)) ([1739ebd](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/1739ebd7d21c4d36063912342a7f6d2615bf066a)) 59 | - **deps:** update dependency react-router-dom to v6.0.2 ([#565](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/565)) ([08b6dfb](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/08b6dfbbff7a95b60be82b444f60d9ccc08c3f13)) 60 | - **deps:** update dependency react-router-dom to v6.1.0 ([#660](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/660)) ([1c477ec](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/1c477ec9a9df4099b9e7874108dbb7fc39a5b81b)) 61 | - **deps:** update dependency react-router-dom to v6.1.1 ([#661](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/661)) ([ce7714b](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/ce7714bf8b8ac2106748ab87bb6c9711e2200fa5)) 62 | - **deps:** update dependency react-router-dom to v6.2.1 ([#671](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/671)) ([77a0361](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/77a03612485a27259d2b717cbe1c6d518d328c11)) 63 | - **deps:** update dependency react-router-dom to v6.2.2 ([#771](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/771)) ([c506191](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/c5061915829320ef0a3659ad3bf9cef2d62dfca9)) 64 | - **deps:** update dependency react-router-dom to v6.3.0 ([#812](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/812)) ([ddadf58](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/ddadf58cc69010ebe3e2962f18ad22f4d00d148c)) 65 | - **deps:** update material-ui monorepo ([#818](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/818)) ([9cc606b](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/9cc606b49523972d2ddee466b10806bf9d2544e2)) 66 | 67 | ### [3.4.10](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.9...v3.4.10) (2021-08-02) 68 | 69 | ### Features 70 | 71 | - Remove the tabs permission ([d98eaa2](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/d98eaa217eb00a8ee3121a3e724efc2856c72e08)) 72 | 73 | ### Bug Fixes 74 | 75 | - Upgrade dependencies ([#537](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/537)) ([87190fa](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/87190faa265a09e601b1e02633726bc3cec66c1b)) 76 | - **deps:** update dependency @material-ui/core to v4.12.0 ([#488](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/488)) ([ebb118f](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/ebb118fa1069282995c6835bdeda69b2a564549f)) 77 | - **deps:** update dependency @material-ui/core to v4.12.1 ([#493](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/493)) ([463b5a8](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/463b5a8f44577dbbb07d5e09a2fe8e9f0ee0c712)) 78 | - **deps:** update dependency @material-ui/core to v4.12.2 ([#510](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/510)) ([6d5d804](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/6d5d804a02571fe7c39b8aa2b135519d69443880)) 79 | - **deps:** update dependency @material-ui/core to v4.12.3 ([#527](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/527)) ([56a8ce4](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/56a8ce4c95731d50da733aa211bd7d7443c1737f)) 80 | - **deps:** update dependency antd to v4.12.3 ([#216](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/216)) ([06bc61b](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/06bc61bfff0641165cc6598f1923eb6361fe937b)) 81 | - **deps:** update dependency antd to v4.13.0 ([#249](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/249)) ([9562df8](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/9562df8803385e3de44ccfb41e63bc7fa95e0749)) 82 | - **deps:** update dependency antd to v4.13.1 ([#257](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/257)) ([bdaa91b](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/bdaa91b2ece20b92b16dd89567bf6460e17a2105)) 83 | - **deps:** update dependency antd to v4.14.1 ([#268](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/268)) ([94cce36](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/94cce36ff384bc6d7a71ef8a86dc4ef64a4ff505)) 84 | - **deps:** update dependency antd to v4.15.0 ([#293](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/293)) ([2a394e7](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/2a394e79b416c059d97beade82f9069f9a48eced)) 85 | - **deps:** update dependency antd to v4.15.1 ([#310](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/310)) ([b861383](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/b861383a33d2334cd5e862dd474def8cd02d6fd8)) 86 | - **deps:** update dependency antd to v4.15.2 ([#323](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/323)) ([a293808](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/a2938080e1930e43d1007dc934f1a0ffc894993c)) 87 | - **deps:** update dependency antd to v4.15.3 ([#338](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/338)) ([2ae38c3](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/2ae38c3af25a3d45be6c319991d4216474dafd77)) 88 | - **deps:** update dependency antd to v4.15.4 ([#350](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/350)) ([c6f3aa9](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/c6f3aa93b58bee95bcedcb70cefe5e527ef47979)) 89 | - **deps:** update dependency antd to v4.15.5 ([#374](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/374)) ([02ab6ce](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/02ab6cefe262aca244e6b7424e662afd8b29604c)) 90 | - **deps:** update dependency antd to v4.15.6 ([#394](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/394)) ([0173c4c](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/0173c4cb8c205d880e0738f46596575211ce40e6)) 91 | - **deps:** update dependency antd to v4.16.0 ([#411](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/411)) ([b9edbd5](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/b9edbd55353d429a42cd0497952f8e3f1c2efbcb)) 92 | - **deps:** update dependency antd to v4.16.1 ([#427](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/427)) ([ecdb7f4](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/ecdb7f4cb8bc49c637d1dc3ea479b2133df0e512)) 93 | - **deps:** update dependency antd to v4.16.10 ([#533](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/533)) ([d88ff4b](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/d88ff4b0419e1f917c9fcc679de03620bbcce8bf)) 94 | - **deps:** update dependency antd to v4.16.2 ([#440](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/440)) ([22ed74a](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/22ed74aefb6f8e65372b153346013b4af0e22454)) 95 | - **deps:** update dependency antd to v4.16.3 ([#451](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/451)) ([19d62d6](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/19d62d679b2fb084e2afc2d24ba75741f682e066)) 96 | - **deps:** update dependency antd to v4.16.5 ([#467](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/467)) ([b799039](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/b7990399cf92909e510f0250dd9f857e73f622ec)) 97 | - **deps:** update dependency antd to v4.16.6 ([#477](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/477)) ([5cbcba8](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/5cbcba8648e1b13d6b7f88a3f678e247bd7e73a9)) 98 | - **deps:** update dependency antd to v4.16.7 ([#499](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/499)) ([880f62e](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/880f62e7cceffd2a7865d085e4166530ca566db1)) 99 | - **deps:** update dependency antd to v4.16.8 ([#508](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/508)) ([3a97025](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/3a970252931c96d43c593ad72016f1a860afb27a)) 100 | - **deps:** update dependency antd to v4.16.9 ([#522](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/522)) ([4bed7b2](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/4bed7b27386183722fcbe768e588769d77190227)) 101 | - **deps:** update dependency js-beautify to v1.13.11 ([#315](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/315)) ([6ea105e](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/6ea105e5101da914bcc743dcea1cc1c0dde704fa)) 102 | - **deps:** update dependency js-beautify to v1.13.13 ([#320](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/320)) ([a7ebae8](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/a7ebae8547d289d3d5402f6ba4271e5f74f929b9)) 103 | - **deps:** update dependency js-beautify to v1.14.0 ([#447](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/447)) ([f047fbe](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/f047fbe87d03bbef340f36c1a5e955e9fd052a45)) 104 | - **deps:** update dependency mobx to v6.1.7 ([#217](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/217)) ([d8dec05](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/d8dec058312845dcfd98c86e83ef38bcb50410a9)) 105 | - **deps:** update dependency mobx to v6.1.8 ([#250](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/250)) ([e734527](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/e73452702ad7e2af61d32ad081f43ccd12599551)) 106 | - **deps:** update dependency mobx to v6.2.0 ([#311](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/311)) ([47d11d0](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/47d11d0d3b260443c78bbb1bcb8d064c91fe217a)) 107 | - **deps:** update dependency mobx to v6.3.0 ([#332](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/332)) ([2fb497f](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/2fb497f781038836997ac91f5028369f12f5ab2b)) 108 | - **deps:** update dependency mobx to v6.3.1 ([#395](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/395)) ([3e602c6](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/3e602c6929bb3390872240942dc6ac9feda20a10)) 109 | - **deps:** update dependency mobx to v6.3.2 ([#407](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/407)) ([1eef316](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/1eef316013a6913a61877fe442fec55db9ce260b)) 110 | - **deps:** update dependency mobx-react to v7.2.0 ([#396](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/396)) ([0f29aa1](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/0f29aa179dbb229ea68ba4ae0a743b2ab5ce05dc)) 111 | - **deps:** update dependency query-string to v6.14.0 ([#218](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/218)) ([f285db6](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/f285db642d341701a85e17691457bcdab465bbc8)) 112 | - **deps:** update dependency query-string to v6.14.1 ([#246](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/246)) ([8c1d4f0](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/8c1d4f0f92d9cbc816932cbbe01f7c97cbc4be18)) 113 | - **deps:** update dependency query-string to v7.0.1 ([#461](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/461)) ([2338eca](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/2338ecac9d6d2228800cf59c18bf4a863d732907)) 114 | - **deps:** update dependency react-ace to v9.3.0 ([#219](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/219)) ([13507ae](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/13507ae3559e9d6cb63db686026ddf59969b7f73)) 115 | - **deps:** update dependency react-ace to v9.4.0 ([#291](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/291)) ([4821811](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/48218110a3a65ec0dd3bc26719cb57877a142a08)) 116 | - **deps:** update dependency react-ace to v9.4.1 ([#438](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/438)) ([348d255](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/348d255bd05dd2d9baa4ca6431349c09f5657756)) 117 | - **deps:** update dependency react-ace to v9.4.3 ([#529](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/529)) ([c53743f](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/c53743f1464fab7841f28f4483fb85bd5c21aea8)) 118 | - **deps:** update mui monorepo to v4.11.4 ([#343](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/343)) ([ba63b95](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/ba63b95d2286bbfa731f7d4cdc18c56593ac3212)) 119 | - **deps:** update react monorepo to v17.0.2 ([#280](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/280)) ([a948f93](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/a948f93bbef0c707909795ff3fb4ddd9c658d668)) 120 | - Update the link of oversize warning ([d7cb70c](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/d7cb70c24cd6cd7df2d59ec3fcbaff99dd96f3c6)) 121 | 122 | ### [3.4.9](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.8...v3.4.9) (2020-12-02) 123 | 124 | ### [3.4.8](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.7...v3.4.8) (2020-11-10) 125 | 126 | ### Bug Fixes 127 | 128 | - Crash issue because invalid custom external script injection ([a77d137](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/a77d137cd15b3088bef3321eae5624d3555e185e)) 129 | 130 | ### [3.4.7](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.6...v3.4.7) (2020-11-10) 131 | 132 | ### Bug Fixes 133 | 134 | - Remove the start with // check for 'your own external scripts' ([21e91a1](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/21e91a1a2dc9b8a54330781b836101cbfc5be3fd)) 135 | 136 | ### [3.4.6](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.5...v3.4.6) (2020-08-23) 137 | 138 | ### Bug Fixes 139 | 140 | - Add multiple selection & delete in Hosts Table ([c7e6e35](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/c7e6e353203402c944a05dddcf73d41f428ea451)) 141 | - Fix the hosts table navigation ([34babeb](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/34babeb2769bcb68118e5360c0dec91055e2891b)) 142 | - Host Tables page is not scrollable ([cfb8c96](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/cfb8c962413f96744680de8f7f490c93da7e6e13)) 143 | - Update the rest popup to include Cancel, Reset, Reset & Refresh ([5252267](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/52522670ce10c91d879ce0b87f0dbd59ad8c71e0)) 144 | - **deps:** pin dependencies ([#96](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/96)) ([c004649](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/c004649f4b46fce0de75707d2efa39598299fc8d)) 145 | 146 | ### [3.4.5](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.0...v3.4.5) (2020-04-29) 147 | 148 | ### Bug Fixes 149 | 150 | - Add https://cdnjs.cloudflare.com and https://gitcdn.link in the CSP ([ca3b295](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/ca3b295a582c1817167551ea494ab9edf123dc7a)) 151 | - External scripts do not inject in strict time order ([dd86352](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/dd86352623397ca0ec7778e0c4fd26bee8255f00)) 152 | - Show log message only if the website is enabled ([775c856](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/775c856008d644eb3730b1b49458dec3bb685841)) 153 | - Upgrade dependencies ([8e1c37e](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/8e1c37ebdadb3ca14bc2e64d85e52f135ffd52f5)) 154 | - Use Ant Design 4.0 ([3e2fdee](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/3e2fdeeaca476efe9fadf548a33f289ca78cc582)) 155 | 156 | ### [3.4.4](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.3...v3.4.4) (2020-01-11) 157 | 158 | ### Bug Fixes 159 | 160 | - Upgrade dependencies ([8e1c37e](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/8e1c37ebdadb3ca14bc2e64d85e52f135ffd52f5)) 161 | 162 | ### [3.4.3](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.2...v3.4.3) (2020-01-08) 163 | 164 | ### [3.4.2](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.1...v3.4.2) (2019-10-26) 165 | 166 | ### Bug Fixes 167 | 168 | - External scripts do not inject in strict time order ([dd86352](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/dd86352)) 169 | 170 | ### [3.4.1](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.4.0...v3.4.1) (2019-10-23) 171 | 172 | ### Bug Fixes 173 | 174 | - Show log message only if the website is enabled ([775c856](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/775c856)) 175 | 176 | ## [3.4.0](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.3.0...v3.4.0) (2019-09-25) 177 | 178 | ### Bug Fixes 179 | 180 | - Add chrome-webstore-upload-cli to automate Chrome webstore publish ([571a480](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/571a480)) 181 | - Add greeting message in run.js to inform user the extension is enabled ([2d45b3f](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/2d45b3f)) 182 | - Add release script ([080dabe](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/080dabe)) 183 | 184 | ### Features 185 | 186 | - Add a button to format the code ([10e469a](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/10e469a)) 187 | 188 | ### [3.3.5](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.3.4...v3.3.5) (2019-08-18) 189 | 190 | ### Bug Fixes 191 | 192 | - Add chrome-webstore-upload-cli to automate Chrome webstore publish ([571a480](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/571a480)) 193 | 194 | ### [3.3.4](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/compare/v3.3.3...v3.3.4) (2019-08-18) 195 | 196 | ### Bug Fixes 197 | 198 | - Add release script ([080dabe](https://github.com/xcv58/Custom-JavaScript-for-Websites-2/commit/080dabe)) 199 | -------------------------------------------------------------------------------- /Icons.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcv58/Custom-JavaScript-for-Websites-2/9b6c2946e621bcdc7c6df18f359b60b30437f30c/Icons.sketch -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 hromadadan 4 | Copyright (c) 2015 xcv58 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | **There is no way to provide the same experience in latest Chrome. The extension won't be updated to manifest v3. More details in https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/868** 4 | 5 | # Custom JavaScript for websites 6 | 7 | [![CircleCI](https://circleci.com/gh/xcv58/Custom-JavaScript-for-Websites-2.svg?style=svg)](https://circleci.com/gh/xcv58/Custom-JavaScript-for-Websites-2) 8 | [![Build Status](https://travis-ci.org/xcv58/Custom-JavaScript-for-Websites-2.svg?branch=master)](https://travis-ci.org/xcv58/Custom-JavaScript-for-Websites-2) 9 | [![dependencies Status](https://david-dm.org/xcv58/Custom-JavaScript-for-Websites-2/status.svg)](https://david-dm.org/xcv58/Custom-JavaScript-for-Websites-2) 10 | [![devDependencies Status](https://david-dm.org/xcv58/Custom-JavaScript-for-Websites-2/dev-status.svg)](https://david-dm.org/xcv58/Custom-JavaScript-for-Websites-2?type=dev) 11 | 12 | [![Maintainability](https://api.codeclimate.com/v1/badges/92a8617dc60beef87408/maintainability)](https://codeclimate.com/github/xcv58/Custom-JavaScript-for-Websites-2/maintainability) 13 | [![DeepScan Grade](https://deepscan.io/api/projects/737/branches/1388/badge/grade.svg)](https://deepscan.io/dashboard/#view=project&pid=737&bid=1388) 14 | 15 | [![JavaScript Style Guide](https://cdn.rawgit.com/standard/standard/master/badge.svg)](https://github.com/standard/standard) 16 | 17 | 18 | [Custom JavaScript for Websites 2 - Chrome Extension](https://xcv58.xyz/inject-js) 19 | 20 | Run custom JavaScript on any website. 21 | Use this tool to inject custom javascript in any website. 22 | 23 | Your scripts are kept in the local storage and applied across domain URLs. 24 | 25 | You can use jQuery 1.11.x or 2.1.x or your own external scripts. 26 | 27 | Use cases: 28 | - site debugging (wrong list sort, etc.) 29 | - hiding annoyng popups and Ads 30 | - custom UI 31 | - anything you can think of :) 32 | 33 | New features: 34 | - Ace Editor (formating, highlight, undo/redo by hotkeys) 35 | - Draft auto save (so doesn't matter when you close the window without saving) 36 | - Hosts (websites) switch (you can browse customjs of other websites) 37 | - Include external script (eq. Underscore.js is cool) 38 | 39 | Thanks to: 40 | - Ace - http://ace.c9.io/ 41 | - Pure - http://purecss.io/ 42 | 43 | # base.js 44 | You can find `base.js` at `extension/lib/base.js`. 45 | It provides useful functions for you. You can directly use all functions in your 46 | JavaScript code. To avoid name conflict, all functions start with `customjs`. 47 | 48 | Now there're only one function: 49 | 50 | ## `customjsReady` 51 | ```javascript 52 | customjsReady('.nav', function(element) { 53 | // do something 54 | }); 55 | ``` 56 | 57 | The `customjsReady` wiil be called when an element matching the selector 58 | is added to the DOM. You can find more details from: 59 | http://ryanmorr.com/using-mutation-observers-to-watch-for-element-availability/ 60 | 61 | Special thanks to [Ryan Morr](http://ryanmorr.com/) 62 | 63 | ## How do I inject large JavaScript 64 | 65 | You can host the JS code in public accessible url and dynamically load and eval it. A sample implementation like this: 66 | 67 | ``` 68 | customjsReady('body', function(element) { 69 | fetch('https://gist.githubusercontent.com/xcv58/5aaeda690ace2f468d51dbf9c65a3980/raw/a8b1c59223892fb2be08490b00c84fa4a029bb8e/test.js') 70 | .then((res) => res.text()) 71 | .then((js) => { 72 | console.log('works in fetch', js) 73 | eval(js); 74 | }) 75 | }); 76 | ``` 77 | 78 | # Why Custom JavaScript for Websites 2 79 | Since the author haven't update original extension for almost one year. 80 | Its website http://hromadadan.com is also unavailable. 81 | I can not find the author. 82 | 83 | But the sync feature is urgent. So this repos is here. 84 | 85 | You can download older versions from: https://crx.dam.io/ext/ddbjnfjiigjmcpcpkmhogomapikjbjdk.html 86 | -------------------------------------------------------------------------------- /integration_test/.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.yarnpkg.com 2 | -------------------------------------------------------------------------------- /integration_test/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'jest-playwright-preset', 3 | testRegex: './*\\.test\\.ts$', 4 | setupFilesAfterEnv: ['./setup.js'], 5 | transform: { 6 | '^.+\\.(ts)$': 'ts-jest' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /integration_test/launch.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer') 2 | const { launch } = require('./puppeteer.config') 3 | const { getExtensionURL } = require('./util') 4 | 5 | const init = async () => { 6 | const browser = await puppeteer.launch(launch) 7 | const page = await browser.newPage() 8 | await page.setViewport({ width: 1920, height: 1080 }) 9 | const extensionURL = await getExtensionURL(browser) 10 | await page.goto(extensionURL) 11 | } 12 | 13 | init() 14 | -------------------------------------------------------------------------------- /integration_test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "integration-test", 3 | "version": "0.0.1", 4 | "description": "Integration test via Playwright", 5 | "scripts": { 6 | "test": "jest --maxWorkers=1", 7 | "test-watch": "yarn test --watch" 8 | }, 9 | "devDependencies": { 10 | "@types/jest": "27.5.2", 11 | "@types/jest-image-snapshot": "4.3.2", 12 | "jest": "27.5.1", 13 | "jest-image-snapshot": "4.5.1", 14 | "jest-playwright-preset": "1.7.2", 15 | "jest-puppeteer": "6.2.0", 16 | "playwright": "1.22.1", 17 | "ts-jest": "27.1.5", 18 | "typescript": "4.9.5" 19 | }, 20 | "private": true 21 | } 22 | -------------------------------------------------------------------------------- /integration_test/setup.js: -------------------------------------------------------------------------------- 1 | jest.setTimeout(30000) 2 | -------------------------------------------------------------------------------- /integration_test/test/extension.test.ts: -------------------------------------------------------------------------------- 1 | import { Page, ChromiumBrowserContext } from 'playwright' 2 | import { toMatchImageSnapshot, } from 'jest-image-snapshot' 3 | import { CLOSE_PAGES, initBrowserWithExtension, } from '../util' 4 | 5 | expect.extend({ toMatchImageSnapshot }) 6 | 7 | let page: Page 8 | let browserContext: ChromiumBrowserContext 9 | let extensionURL: string 10 | 11 | describe('The Extension page should', () => { 12 | beforeAll(async () => { 13 | const init = await initBrowserWithExtension() 14 | browserContext = init.browserContext 15 | extensionURL = init.extensionURL 16 | page = browserContext.pages()[0] 17 | }) 18 | 19 | afterAll(async () => { 20 | await browserContext?.close() 21 | browserContext = null 22 | page = null 23 | extensionURL = '' 24 | }) 25 | 26 | beforeEach(async () => { 27 | if (!extensionURL) { 28 | console.error('Invalid extensionURL', { extensionURL }) 29 | } 30 | await page.bringToFront() 31 | await page.goto(extensionURL) 32 | await page.waitForTimeout(1000) 33 | await CLOSE_PAGES(browserContext) 34 | }) 35 | 36 | afterEach(async () => { 37 | await CLOSE_PAGES(browserContext) 38 | }) 39 | 40 | it('have title ends with the extension name', async () => { 41 | await page.goto(extensionURL) 42 | await expect(page.title()).resolves.toMatch('Custom JavaScript') 43 | }) 44 | 45 | it('render correct layout', async () => { 46 | const buttons = await page.$$('button') 47 | expect(buttons).toHaveLength(7) 48 | const checkbox = await page.$$('input[type="checkbox"]') 49 | expect(checkbox).toHaveLength(1) 50 | expect(await page.$$('a[href="https://paypal.me/xcv58"]')).toHaveLength(1) 51 | const aceEditor = await page.$('#ace-editor') 52 | expect(aceEditor).toBeTruthy() 53 | expect(await page.$eval('#ace-editor', (node) => node.textContent)).toMatch( 54 | '// Here You can type your custom JavaScript...' 55 | ) 56 | }) 57 | 58 | it('execute the injected script after page loaded', async () => { 59 | await page.goto(extensionURL) 60 | await page.waitForTimeout(1000) 61 | await page.evaluate(() => { 62 | const editor = window.ace.edit('ace-editor') 63 | editor.session.setValue(` 64 | console.log('abc') 65 | document.querySelector('body').style.background = 'red'`) 66 | }) 67 | await page.evaluate(() => { 68 | document.querySelectorAll('button')[6].click() 69 | }) 70 | await page.waitForTimeout(500) 71 | const input = await page.$$('textarea') 72 | await input[1].evaluate((node) => (node.value = '')) 73 | await input[1].type( 74 | 'https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.11.0/underscore-min.js', 75 | { delay: 2 } 76 | ) 77 | await page.waitForTimeout(500) 78 | const allButtons = await page.$$('button') 79 | await allButtons[allButtons.length - 1].click() 80 | await page.waitForTimeout(500) 81 | await page.$eval('button', (x) => x.click()) 82 | 83 | const newPage = await browserContext.newPage() 84 | await newPage.goto('https://google.com/') 85 | await newPage.bringToFront() 86 | await page.waitForTimeout(500) 87 | const background = await newPage.$eval( 88 | 'body', 89 | (node) => node.style.background 90 | ) 91 | expect(background).toBe('red') 92 | expect(await newPage.$eval('body', (node) => window._.VERSION)).toBe( 93 | '1.11.0' 94 | ) 95 | 96 | page.bringToFront() 97 | const buttons = await page.$$('button') 98 | await buttons[1].click() 99 | await page.waitForTimeout(500) 100 | const dialogButtons = await page.$$('.MuiDialogActions-root > button') 101 | expect(dialogButtons).toHaveLength(3) 102 | await dialogButtons[1].click() 103 | }) 104 | }) 105 | -------------------------------------------------------------------------------- /integration_test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "resolveJsonModule": true, 4 | "lib": ["es2018", "dom"], 5 | "types": ["jasmine", "jest"], 6 | "esModuleInterop": true, 7 | "sourceMap": true, 8 | "noImplicitAny": true, 9 | "module": "es6", 10 | "moduleResolution": "node", 11 | "target": "es6", 12 | "allowJs": true 13 | }, 14 | "exclude": ["node_modules"] 15 | } 16 | -------------------------------------------------------------------------------- /integration_test/typings.d.ts: -------------------------------------------------------------------------------- 1 | import 'jest' 2 | 3 | declare global { 4 | namespace jest { 5 | interface Matchers { 6 | toMatchImageSnapshot(): R 7 | } 8 | } 9 | interface Window { 10 | _: any; 11 | ace: any; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /integration_test/util.ts: -------------------------------------------------------------------------------- 1 | import { chromium, ChromiumBrowserContext, Page } from 'playwright' 2 | import { join } from 'path' 3 | 4 | export const EXTENSION_PATH = join(__dirname, '../build') 5 | 6 | export const isExtensionURL = (url: string) => 7 | url.startsWith('chrome-extension://') 8 | 9 | export const CLOSE_PAGES = async (browserContext: ChromiumBrowserContext) => { 10 | const pages = (await browserContext?.pages()) || [] 11 | for (const page of pages) { 12 | const url = await page.url() 13 | if (!isExtensionURL(url)) { 14 | await page.close() 15 | } 16 | } 17 | } 18 | 19 | export const initBrowserWithExtension = async () => { 20 | const userDataDir = `/tmp/test-user-data-${Math.random()}` 21 | let extensionURL: string 22 | const browserContext = (await chromium.launchPersistentContext(userDataDir, { 23 | headless: false, 24 | args: [ 25 | // Follow suggestions on https://playwright.dev/docs/ci#docker 26 | '--disable-dev-shm-usage', 27 | '--ipc=host', 28 | `--disable-extensions-except=${EXTENSION_PATH}`, 29 | `--load-extension=${EXTENSION_PATH}`, 30 | ], 31 | })) as ChromiumBrowserContext 32 | 33 | /** 34 | * The background page is useful to retrieve the extension id so that we 35 | * could programatically open the extension page. 36 | * 37 | * There is uncertain timing of backgroundPages. Sometimes the 38 | * `browserContext.backgroundPages()` will return empty at the beginning, 39 | * so we have to rely on the `browserContext.on('backgroundpage')` to get 40 | * the background page. But sometimes the 'backgroundpage' would never be 41 | * triggered and the `browserContext.backgroundPages()` would give an array 42 | * with the existing background page. 43 | */ 44 | const setExtensionURL = (backgroundPage: Page) => { 45 | const url = backgroundPage.url() 46 | const [, , extensionId] = url.split('/') 47 | // extensionURL = `chrome-extension://${extensionId}/popup.html?not_popup=1` 48 | extensionURL = `chrome-extension://${extensionId}/popup.html#/?domain=https%3A%2F%2Fwww.google.com` 49 | } 50 | 51 | const page = await browserContext.newPage() 52 | await page.bringToFront() 53 | await page.goto('chrome://inspect/#extensions') 54 | 55 | browserContext.on('backgroundpage', setExtensionURL) 56 | const backgroundPages = browserContext.backgroundPages() 57 | if (backgroundPages.length) { 58 | setExtensionURL(backgroundPages[0]) 59 | } 60 | while (!extensionURL) { 61 | await page.waitForTimeout(1000) 62 | } 63 | return { browserContext, extensionURL } 64 | } 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-javaScript-for-websites-2", 3 | "version": "3.4.12", 4 | "description": "Run custom JavaScript on any website.", 5 | "scripts": { 6 | "analyze": "NODE_ENV=production node utils/analyze.js", 7 | "build": "NODE_ENV=production node utils/build.js", 8 | "deploy": "run-s build zip", 9 | "zip": "zip -X --compression-method deflate -r build.zip build", 10 | "prettier": "prettier --no-semi --single-quote --write", 11 | "lint:ts": "standard --parser @typescript-eslint/parser --plugin typescript ./**/*.tsx", 12 | "lint": "standard", 13 | "lintfix": "yarn prettier 'src/**/*.{js,ts,tsx}' && yarn lint --fix && yarn lint:ts --fix", 14 | "test:chrome-upload": "chrome-webstore-upload --help", 15 | "start": "node utils/webserver.js", 16 | "upload:chrome": "node -r dotenv/config ./node_modules/.bin/chrome-webstore-upload upload --source build.zip", 17 | "publish:chrome": "node -r dotenv/config ./node_modules/.bin/chrome-webstore-upload publish", 18 | "publish-extension": "yarn publish:chrome", 19 | "release": "standard-version", 20 | "test": "run-s lint build test:*" 21 | }, 22 | "dependencies": { 23 | "@material-ui/core": "4.12.4", 24 | "@material-ui/styles": "4.11.5", 25 | "antd": "4.20.5", 26 | "chrome-extension-async": "3.4.1", 27 | "clean-webpack-plugin": "4.0.0", 28 | "js-beautify": "1.15.4", 29 | "lodash.orderby": "4.6.0", 30 | "lodash.uniqby": "4.7.0", 31 | "mobx": "6.13.6", 32 | "mobx-react": "7.6.0", 33 | "object-sizeof": "1.6.3", 34 | "query-string": "7.1.3", 35 | "react": "17.0.2", 36 | "react-ace": "9.5.0", 37 | "react-dom": "17.0.2", 38 | "react-router-dom": "6.30.0" 39 | }, 40 | "devDependencies": { 41 | "@types/chrome": "0.0.307", 42 | "@types/jasmine": "3.10.18", 43 | "@types/jest": "27.5.2", 44 | "@types/react": "17.0.83", 45 | "@types/react-dom": "17.0.26", 46 | "@typescript-eslint/eslint-plugin": "5.62.0", 47 | "@typescript-eslint/parser": "5.62.0", 48 | "babel-eslint": "10.1.0", 49 | "babel-plugin-import": "1.13.8", 50 | "chrome-webstore-upload-cli": "2.2.2", 51 | "codecov": "3.8.3", 52 | "copy-webpack-plugin": "9.1.0", 53 | "coveralls": "3.1.1", 54 | "css-loader": "6.11.0", 55 | "dotenv": "14.3.2", 56 | "enzyme": "3.11.0", 57 | "enzyme-adapter-react-16": "1.15.8", 58 | "eslint": "7.32.0", 59 | "eslint-plugin-typescript": "0.14.0", 60 | "file-loader": "6.2.0", 61 | "fork-ts-checker-webpack-plugin": "6.5.3", 62 | "hard-source-webpack-plugin": "0.13.1", 63 | "html-loader": "3.1.2", 64 | "html-webpack-plugin": "5.6.3", 65 | "husky": "7.0.4", 66 | "jest": "27.5.1", 67 | "lint-staged": "12.5.0", 68 | "mini-css-extract-plugin": "2.9.2", 69 | "npm-run-all": "4.1.5", 70 | "optimize-css-assets-webpack-plugin": "6.0.1", 71 | "prettier": "2.8.8", 72 | "progress-bar-webpack-plugin": "2.1.0", 73 | "react-axe": "3.5.4", 74 | "react-test-renderer": "17.0.2", 75 | "regenerator-runtime": "0.14.1", 76 | "samsam": "1.3.0", 77 | "sinon": "12.0.1", 78 | "sinon-chrome": "3.0.1", 79 | "standard": "16.0.4", 80 | "standard-version": "9.5.0", 81 | "style-loader": "3.3.4", 82 | "terser-webpack-plugin": "5.3.12", 83 | "ts-jest": "27.1.5", 84 | "ts-loader": "9.5.2", 85 | "typescript": "4.9.5", 86 | "web-ext": "6.8.0", 87 | "webpack": "5.98.0", 88 | "webpack-bundle-analyzer": "4.10.2", 89 | "webpack-dev-server": "4.15.2", 90 | "write-file-webpack-plugin": "4.5.1" 91 | }, 92 | "lint-staged": { 93 | "*.js": [ 94 | "yarn prettier", 95 | "standard --fix" 96 | ], 97 | "*.{ts,tsx}": [ 98 | "yarn prettier", 99 | "standard --parser @typescript-eslint/parser --plugin typescript --fix" 100 | ], 101 | "*.{json,css,md}": [ 102 | "yarn prettier" 103 | ] 104 | }, 105 | "standard": { 106 | "env": [ 107 | "jest" 108 | ], 109 | "parser": "babel-eslint", 110 | "ignore": [ 111 | "src/js/chrome-extension-async.js", 112 | "src/lib" 113 | ], 114 | "globals": [ 115 | "screen", 116 | "Event", 117 | "HTMLElement", 118 | "HTMLInputElement", 119 | "HTMLDivElement", 120 | "page", 121 | "browser", 122 | "ace", 123 | "chrome" 124 | ] 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /privacy_policy.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | Your privacy is important to us. It is Tab Manager v2's policy to respect your privacy regarding any information we may collect from you across this extension. 4 | 5 | We only ask for personal information when we truly need it to provide a service to you. We collect it by fair and lawful means, with your knowledge and consent. We also let you know why we’re collecting it and how it will be used. 6 | 7 | We only retain collected information for as long as necessary to provide you with your requested service. What data we store, we’ll protect within commercially acceptable means to prevent loss and theft, as well as unauthorized access, disclosure, copying, use or modification. 8 | 9 | We don’t share any personally identifying information publicly or with third-parties, except when required to by law. 10 | 11 | The extension may link to external sites that are not operated by us. Please be aware that we have no control over the content and practices of these sites, and cannot accept responsibility or liability for their respective privacy policies. 12 | 13 | You are free to refuse our request for your personal information, with the understanding that we may be unable to provide you with some of your desired services. 14 | 15 | Your continued use of the extension will be regarded as acceptance of our practices around privacy and personal information. If you have any questions about how we handle user data and personal information, feel free to contact us. 16 | 17 | This policy is effective as of 1 January 2020. 18 | 19 | Privacy Policy created with GetTerms. 20 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageRules": [ 3 | { 4 | "updateTypes": ["minor", "patch", "pin", "digest"], 5 | "automerge": true 6 | } 7 | ], 8 | "extends": ["config:base"] 9 | } 10 | -------------------------------------------------------------------------------- /screenshots/Screen Shot 2021-04-03 at 9.07.52 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcv58/Custom-JavaScript-for-Websites-2/9b6c2946e621bcdc7c6df18f359b60b30437f30c/screenshots/Screen Shot 2021-04-03 at 9.07.52 PM.png -------------------------------------------------------------------------------- /screenshots/Screen Shot 2021-04-03 at 9.08.57 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcv58/Custom-JavaScript-for-Websites-2/9b6c2946e621bcdc7c6df18f359b60b30437f30c/screenshots/Screen Shot 2021-04-03 at 9.08.57 PM.png -------------------------------------------------------------------------------- /screenshots/Screen Shot 2021-04-03 at 9.09.09 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcv58/Custom-JavaScript-for-Websites-2/9b6c2946e621bcdc7c6df18f359b60b30437f30c/screenshots/Screen Shot 2021-04-03 at 9.09.09 PM.png -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | min-width: 800px; 3 | min-height: 600px; 4 | height: 100%; 5 | font-size: 14px; 6 | font-family: Arial, Helvetica, sans-serif; 7 | overflow: hidden; 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | #app { 13 | height: 100%; 14 | max-width: 1024px; 15 | margin: auto; 16 | padding: 0 0.5rem; 17 | } 18 | 19 | .customjs.customjs--error { 20 | width: 400px; 21 | height: 75px; 22 | } 23 | 24 | .customjs.customjs--error form { 25 | display: none; 26 | } 27 | 28 | a, 29 | a:hover, 30 | .blue-text, 31 | .blue-text:hover { 32 | color: #0F14F3; 33 | } 34 | 35 | .red-text, 36 | .red-text:hover { 37 | color: #f10000; 38 | } 39 | 40 | a { 41 | text-decoration: none; 42 | } 43 | 44 | a:hover { 45 | text-decoration: underline; 46 | } 47 | 48 | .is-hidden { 49 | display: none; 50 | } 51 | 52 | .pure-g { 53 | padding: 10px 0; 54 | } 55 | 56 | .host { 57 | } 58 | 59 | .host__name select { 60 | min-width: 220px; 61 | } 62 | 63 | .host__name a { 64 | margin-left: 5px; 65 | font-size: 12px; 66 | } 67 | 68 | .host__enable { 69 | text-align: right; 70 | line-height: 34px; 71 | } 72 | 73 | .host__enable input { 74 | position: relative; 75 | bottom: -2px; 76 | margin-left: 2px; 77 | } 78 | 79 | 80 | .include { 81 | text-align: right; 82 | } 83 | 84 | .include__body { 85 | color: #999; 86 | font-size: 12px; 87 | line-height: 35px; 88 | } 89 | 90 | .include__body select { 91 | min-width: 110px; 92 | } 93 | 94 | .include__popbox { 95 | } 96 | 97 | .include__popbox__body { 98 | position: absolute; 99 | top: 50%; 100 | left: 50%; 101 | z-index: 10; 102 | margin: -150px 0 0 -250px; 103 | width: 500px; 104 | height: 300px; 105 | } 106 | 107 | .include__popbox__body textarea { 108 | width: 100%; 109 | height: 100%; 110 | color: #777; 111 | font-size: 12px; 112 | line-height: 180%; 113 | resize: none; 114 | } 115 | 116 | .include__popbox__screenmask { 117 | position: absolute; 118 | top: 0; 119 | left: 0; 120 | z-index: 5; 121 | width: 100%; 122 | height: 100%; 123 | background-color: #ddd; 124 | opacity: 0.4; 125 | } 126 | 127 | #ace-editor { 128 | height: 380px; 129 | width: 100%; 130 | } 131 | 132 | .controls { } 133 | 134 | .controls__save, 135 | .controls__reset, 136 | .controls__new-tab, 137 | .controls__remove-draft { 138 | margin-right: 10px; 139 | font-size: 18px; 140 | } 141 | 142 | .controls__remove-draft { 143 | color: red !important; 144 | } 145 | 146 | .donate { 147 | text-align: right; 148 | } 149 | 150 | .donate img { 151 | position: absolute; 152 | } 153 | 154 | .donate__button { 155 | background: rgb(28, 184, 65) !important; /* this is a green */ 156 | color: #fff !important; 157 | font-size: 18px !important; 158 | } 159 | 160 | .donate-form { 161 | width: 0; 162 | height: 0; 163 | overflow: hidden; 164 | } 165 | 166 | 167 | /** 168 | * Font icons 169 | */ 170 | 171 | @font-face { 172 | font-weight: normal; 173 | font-style: normal; 174 | font-family: 'customjs'; 175 | src: url('../font/customjs.svg?96127482#customjs') format('svg'); 176 | } 177 | 178 | [class^="icon-"]:before, [class*=" icon-"]:before { 179 | display: inline-block; 180 | font-variant: normal; 181 | font-family: "customjs"; 182 | } 183 | 184 | .icon-left-open:before { content: '\e800'; } /* '' */ 185 | .icon-right-open:before { content: '\e801'; } /* '' */ 186 | .icon-share:before { content: '\e802'; } /* '' */ 187 | -------------------------------------------------------------------------------- /src/font/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Font license info 2 | 3 | 4 | ## Font Awesome 5 | 6 | Copyright (C) 2012 by Dave Gandy 7 | 8 | Author: Dave Gandy 9 | License: SIL () 10 | Homepage: http://fortawesome.github.com/Font-Awesome/ 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/font/customjs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2014 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/img/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcv58/Custom-JavaScript-for-Websites-2/9b6c2946e621bcdc7c6df18f359b60b30437f30c/src/img/icon-128.png -------------------------------------------------------------------------------- /src/img/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcv58/Custom-JavaScript-for-Websites-2/9b6c2946e621bcdc7c6df18f359b60b30437f30c/src/img/icon-16.png -------------------------------------------------------------------------------- /src/img/icon-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcv58/Custom-JavaScript-for-Websites-2/9b6c2946e621bcdc7c6df18f359b60b30437f30c/src/img/icon-19.png -------------------------------------------------------------------------------- /src/img/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcv58/Custom-JavaScript-for-Websites-2/9b6c2946e621bcdc7c6df18f359b60b30437f30c/src/img/icon-32.png -------------------------------------------------------------------------------- /src/img/icon-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcv58/Custom-JavaScript-for-Websites-2/9b6c2946e621bcdc7c6df18f359b60b30437f30c/src/img/icon-38.png -------------------------------------------------------------------------------- /src/img/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcv58/Custom-JavaScript-for-Websites-2/9b6c2946e621bcdc7c6df18f359b60b30437f30c/src/img/icon-48.png -------------------------------------------------------------------------------- /src/js/background.tsx: -------------------------------------------------------------------------------- 1 | import 'chrome-extension-async' 2 | import { 3 | findMatchedHosts, 4 | getHostKey, 5 | getActiveTab, 6 | getHosts, 7 | setLastFocusedWindowId 8 | } from 'libs' 9 | 10 | const getURL = ({ url }) => new window.URL(url) 11 | 12 | const reloadTab = (tab) => chrome.tabs.reload(tab.id) 13 | 14 | const methodMap = { 15 | getData: async (message, { tab, url }, sendResponse) => { 16 | const { host, protocol } = url 17 | const hosts = await getHosts() 18 | const matchedHosts = findMatchedHosts(hosts, url, message) 19 | if (matchedHosts.length === 0) { 20 | sendResponse({ host, protocol, tab }) 21 | } else { 22 | const matchedHost = matchedHosts[0] 23 | const hostKey = getHostKey(matchedHost) 24 | const data = await chrome.storage.sync.get(hostKey) 25 | const customjs = data[hostKey] 26 | sendResponse({ customjs, host, protocol, tab, matchedHost }) 27 | } 28 | }, 29 | setData: async (message, _, sendResponse) => { 30 | const { matchedHost, customjs } = message 31 | const hostKey = getHostKey(matchedHost) 32 | try { 33 | await chrome.storage.sync.set({ [hostKey]: customjs }) 34 | sendResponse() 35 | } catch (err) { 36 | sendResponse(err) 37 | } 38 | }, 39 | removeData: (message, { url }) => { 40 | const { isRegex, pattern } = message 41 | if (isRegex) { 42 | chrome.storage.sync.remove(pattern) 43 | } else { 44 | chrome.storage.sync.remove(url.origin) 45 | } 46 | }, 47 | goTo: (message, { tab }) => chrome.tabs.update(tab.id, { url: message.link }) 48 | } 49 | 50 | const onMessage = async (message, sender, sendResponse) => { 51 | const { domain } = message 52 | try { 53 | const tab = await getActiveTab() 54 | const url = domain ? getURL({ url: domain }) : getURL(tab) 55 | const { method, reload } = message 56 | 57 | const func = methodMap[method] 58 | if (func && typeof func === 'function') { 59 | func(message, { tab, url }, sendResponse) 60 | } else { 61 | console.error(`Unknown method: ${method}`) 62 | sendResponse({ source: '', config: {} }) 63 | } 64 | 65 | if (reload) { 66 | reloadTab(tab) 67 | } 68 | } catch (e) { 69 | sendResponse({ error: e.message }) 70 | } 71 | } 72 | 73 | const onFocusChanged = (windowId) => { 74 | if (windowId < 0) { 75 | return 76 | } 77 | setLastFocusedWindowId(windowId) 78 | } 79 | 80 | chrome.runtime.onMessage.addListener((...args) => { 81 | onMessage(...args) 82 | return true 83 | }) 84 | 85 | chrome.windows.onFocusChanged.addListener(onFocusChanged) 86 | 87 | chrome.runtime.onInstalled.addListener((details) => { 88 | chrome.tabs.query({ currentWindow: true, active: true }, (tabs) => { 89 | if (tabs.length <= 0) { 90 | return 91 | } 92 | const { windowId } = tabs[0] 93 | setLastFocusedWindowId(windowId) 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /src/js/base.tsx: -------------------------------------------------------------------------------- 1 | // inspired by http://ryanmorr.com/using-mutation-observers-to-watch-for-element-availability/ 2 | const listeners = [] 3 | const doc = window.document 4 | const MutationObserver = 5 | window.MutationObserver || window.WebKitMutationObserver 6 | let observer = null 7 | 8 | const check = () => { 9 | // Check the DOM for elements matching a stored selector 10 | listeners.forEach((listener) => { 11 | const elements = doc.querySelectorAll(listener.selector) 12 | elements.forEach((ele) => { 13 | if (!ele.ready) { 14 | ele.ready = true 15 | listener.fn.call(ele, ele) 16 | } 17 | }) 18 | }) 19 | } 20 | 21 | const ready = (selector, fn) => { 22 | // Store the selector and callback to be monitored 23 | listeners.push({ 24 | selector: selector, 25 | fn: fn 26 | }) 27 | if (!observer) { 28 | // Watch for changes in the document 29 | observer = new MutationObserver(check) 30 | observer.observe(doc.documentElement, { 31 | childList: true, 32 | subtree: true 33 | }) 34 | } 35 | // Check if the element is currently in the DOM 36 | check() 37 | } 38 | 39 | // Expose `ready` 40 | window.customjsReady = ready 41 | -------------------------------------------------------------------------------- /src/js/components/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Page from './Page' 3 | import { HashRouter, Route, Routes } from 'react-router-dom' 4 | import HostTable from './HostTable' 5 | 6 | export default () => ( 7 | 8 | 9 | } /> 10 | } /> 11 | 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /src/js/components/AutoSave.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { CircularProgress, InputLabel } from '@material-ui/core' 3 | import { useStore } from './StoreContext' 4 | import { observer } from 'mobx-react' 5 | 6 | export default observer(() => { 7 | const { saved, autoSaveHandle } = useStore().AppStore 8 | const content = 9 | (autoSaveHandle && ) || 10 | (saved && ( 11 | Draft saved. Click "Save" to apply the script. 12 | )) 13 | return {content} 14 | }) 15 | -------------------------------------------------------------------------------- /src/js/components/DonateLink.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Button from '@material-ui/core/Button' 3 | 4 | export default () => { 5 | const href = 'https://paypal.me/xcv58' 6 | return ( 7 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /src/js/components/Editor.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import AceEditor from 'react-ace' 3 | import 'ace-builds/src-noconflict/theme-tomorrow' 4 | import 'ace-builds/src-noconflict/mode-javascript' 5 | import 'ace-builds/src-noconflict/mode-css' 6 | import 'ace-builds/src-noconflict/snippets/css' 7 | import 'ace-builds/src-noconflict/snippets/javascript' 8 | import 'ace-builds/src-noconflict/ext-language_tools' 9 | import 'ace-builds/src-noconflict/ext-searchbox' 10 | import { useStore } from './StoreContext' 11 | import { observer } from 'mobx-react' 12 | 13 | const style = { 14 | border: '1px solid #EBEBEB', 15 | margin: 0, 16 | height: '100%', 17 | width: '100%', 18 | lineHeight: '150%' 19 | } 20 | 21 | const setOptions = { 22 | enableBasicAutocompletion: true, 23 | enableLiveAutocompletion: true, 24 | enableSnippets: true, 25 | showLineNumbers: true, 26 | tabSize: 2 27 | } 28 | 29 | export default observer(() => { 30 | const { source, onChangeSource, mode } = useStore().AppStore 31 | return ( 32 | 44 | ) 45 | }) 46 | -------------------------------------------------------------------------------- /src/js/components/Error.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | Dialog, 4 | DialogContent, 5 | DialogContentText, 6 | DialogTitle 7 | } from '@material-ui/core' 8 | import Hosts from 'components/Hosts' 9 | 10 | export default ({ error }) => ( 11 | 12 | Invalid Pattern 13 | 14 | 15 | The pattern in the URL is invalid, details: {error}. 16 | Please choose a valid host from below or click the extension icon in the 17 | webpage you want to inject: 18 | 19 | 20 | 21 | 22 | ) 23 | -------------------------------------------------------------------------------- /src/js/components/Format.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Button from '@material-ui/core/Button' 3 | import { useStore } from './StoreContext' 4 | import { observer } from 'mobx-react' 5 | 6 | export default observer(() => { 7 | const { beautify } = useStore().AppStore 8 | return ( 9 | 12 | ) 13 | }) 14 | -------------------------------------------------------------------------------- /src/js/components/Goto.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Button from '@material-ui/core/Button' 3 | import { useStore } from './StoreContext' 4 | import { observer } from 'mobx-react' 5 | 6 | export default observer(() => { 7 | const { differentURL, goTo } = useStore().AppStore 8 | if (!differentURL) { 9 | return null 10 | } 11 | return ( 12 | 15 | ) 16 | }) 17 | -------------------------------------------------------------------------------- /src/js/components/HostTable.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { getHostKey, getHostName } from 'libs' 3 | import Loading from 'components/Loading' 4 | import queryString from 'query-string' 5 | import { useStore } from './StoreContext' 6 | import { observer } from 'mobx-react' 7 | import { Affix, Button, Table, message, PageHeader, Popconfirm } from 'antd' 8 | import { Link, useNavigate } from 'react-router-dom' 9 | 10 | const Host = (props) => { 11 | const key = getHostKey(props) 12 | const { isRegex, pattern } = props 13 | const query = isRegex ? { isRegex, pattern } : { domain: key } 14 | const to = { 15 | pathname: '/', 16 | search: queryString.stringify(query) 17 | } 18 | return {getHostName(props)} 19 | } 20 | 21 | const Regex = (isRegex) => { 22 | if (isRegex) { 23 | return 'Yes' 24 | } 25 | return 'No' 26 | } 27 | 28 | export default observer((props) => { 29 | useEffect(() => { 30 | AppStore.init({}) 31 | }, []) 32 | const navigate = useNavigate() 33 | const [selectedRowKeys, setSelectedRowKeys] = useState([]) 34 | const rowSelection = { 35 | selectedRowKeys, 36 | onChange: setSelectedRowKeys, 37 | selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT] 38 | } 39 | 40 | const { AppStore } = useStore() 41 | const { hosts, loading } = AppStore 42 | if (loading) { 43 | return 44 | } 45 | 46 | const data = hosts.map((host) => { 47 | return { 48 | key: getHostKey(host), 49 | host, 50 | isRegex: Boolean(host.isRegex) 51 | } 52 | }) 53 | const columns = [ 54 | { 55 | title: 'Host', 56 | dataIndex: 'host', 57 | key: 'host', 58 | render: Host, 59 | sorter: (a, b) => { 60 | const aKey = getHostKey(a.host) 61 | const bKey = getHostKey(b.host) 62 | return aKey.localeCompare(bKey) 63 | } 64 | }, 65 | { 66 | title: 'isRegex', 67 | dataIndex: 'isRegex', 68 | key: 'isRegex', 69 | render: Regex, 70 | sorter: (a, b) => a.isRegex - b.isRegex 71 | }, 72 | { 73 | title: 'Action', 74 | key: 'action', 75 | render: ({ host }) => { 76 | const name = getHostName(host) 77 | return ( 78 | { 81 | AppStore.removeHost({ host }) 82 | message.success(`Successfully remove host: ${name}`) 83 | }} 84 | okText='Yes' 85 | cancelText='No' 86 | > 87 | 88 | 89 | ) 90 | } 91 | } 92 | ] 93 | const hasSelected = selectedRowKeys.length > 0 94 | return ( 95 |
96 | 97 | navigate(-1)} 100 | title='All Hosts & Patterns' 101 | extra={ 102 | <> 103 | 104 | {hasSelected 105 | ? `Selected ${selectedRowKeys.length} host${ 106 | selectedRowKeys.length > 1 ? 's' : '' 107 | }` 108 | : ''} 109 | 110 | 1 ? 's' : '' 114 | }`} 115 | onConfirm={() => { 116 | const toDelete = new Set(selectedRowKeys) 117 | data 118 | .filter((x) => toDelete.has(x.key)) 119 | .map((x) => x.host) 120 | .forEach((host) => AppStore.removeHost({ host })) 121 | message.success( 122 | `Successfully remove all selected host${ 123 | selectedRowKeys.length > 1 ? 's' : '' 124 | }` 125 | ) 126 | }} 127 | okText='Yes' 128 | cancelText='No' 129 | > 130 | 133 | 134 | 135 | } 136 | /> 137 | 138 | 144 | 145 | ) 146 | }) 147 | -------------------------------------------------------------------------------- /src/js/components/HostTableLink.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Button from '@material-ui/core/Button' 3 | import { Link } from 'react-router-dom' 4 | 5 | export default () => { 6 | return ( 7 | 8 | 9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /src/js/components/Hosts.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { getHostName } from 'libs' 3 | import { useNavigate } from 'react-router-dom' 4 | import queryString from 'query-string' 5 | import { useStore } from './StoreContext' 6 | import { observer } from 'mobx-react' 7 | import { MenuItem, FormControl, Select } from '@material-ui/core' 8 | 9 | export default observer((props) => { 10 | const navigate = useNavigate() 11 | const { hosts, matchedHost = '' } = useStore().AppStore 12 | const options = hosts.map((host) => { 13 | const name = getHostName(host) 14 | return ( 15 | 16 | {name} 17 | 18 | ) 19 | }) 20 | return ( 21 | 22 | 37 | 38 | ) 39 | }) 40 | -------------------------------------------------------------------------------- /src/js/components/Include/Extra.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import TextField from '@material-ui/core/TextField' 3 | import Button from '@material-ui/core/Button' 4 | import Dialog from '@material-ui/core/Dialog' 5 | import DialogActions from '@material-ui/core/DialogActions' 6 | import DialogContent from '@material-ui/core/DialogContent' 7 | import DialogTitle from '@material-ui/core/DialogTitle' 8 | import { useStore } from 'components/StoreContext' 9 | import { observer } from 'mobx-react' 10 | 11 | export default observer(() => { 12 | const { IncludeStore } = useStore() 13 | const { extraOpen, extraValue, placeholder, toggleExtraOpen } = IncludeStore 14 | return ( 15 | 16 | External Scripts 17 | 18 | IncludeStore.onUpdateExtra(e.target.value)} 26 | margin='none' 27 | type='text' 28 | value={extraValue} 29 | /> 30 | 31 | 32 | 35 | 36 | 37 | ) 38 | }) 39 | -------------------------------------------------------------------------------- /src/js/components/Include/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import InputLabel from '@material-ui/core/InputLabel' 3 | import MenuItem from '@material-ui/core/MenuItem' 4 | import FormControl from '@material-ui/core/FormControl' 5 | import Button from '@material-ui/core/Button' 6 | import Select from '@material-ui/core/Select' 7 | import Extra from './Extra' 8 | import { useStore } from '../StoreContext' 9 | import { observer } from 'mobx-react' 10 | 11 | export default observer(() => { 12 | const { include, includes, toggleExtraOpen, onSelect } = 13 | useStore().IncludeStore 14 | const options = includes.map(({ name, path }) => ( 15 | 16 | {name} 17 | 18 | )) 19 | return ( 20 | <> 21 |
27 | You can inject 28 | 35 | or predefined one: 36 | 37 | 48 | 49 |
50 | 51 | 52 | ) 53 | }) 54 | -------------------------------------------------------------------------------- /src/js/components/LoadError.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | Button, 4 | Dialog, 5 | DialogActions, 6 | DialogContent, 7 | DialogContentText, 8 | DialogTitle 9 | } from '@material-ui/core' 10 | 11 | export default () => { 12 | return ( 13 | 14 | Can not load data 15 | 16 | 17 | Can not load data, please try to open a new window and try again. 18 | 19 | 20 | 21 | 31 | 32 | 33 | 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /src/js/components/Loading.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { CircularProgress } from '@material-ui/core' 3 | 4 | export default class Loading extends Component { 5 | state = { tooLong: false } 6 | 7 | componentDidMount () { 8 | this.timer = setTimeout(() => this.setState({ tooLong: true }), 100) 9 | } 10 | 11 | componentWillUnmount () { 12 | if (this.timer) { 13 | clearTimeout(this.timer) 14 | } 15 | } 16 | 17 | render () { 18 | if (!this.state.tooLong) { 19 | return 'Loading...' 20 | } 21 | return ( 22 |
30 | 31 |
32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/js/components/ModeSelect.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { FormControl, MenuItem, Select } from '@material-ui/core' 3 | import { useStore } from './StoreContext' 4 | import { observer } from 'mobx-react' 5 | 6 | export default observer(() => { 7 | const { mode, setMode } = useStore().AppStore 8 | const options = ['javascript', 'css'].map((option) => { 9 | return ( 10 | 11 | {option} 12 | 13 | ) 14 | }) 15 | return ( 16 | 17 | 26 | 27 | ) 28 | }) 29 | -------------------------------------------------------------------------------- /src/js/components/NewPattern.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useNavigate } from 'react-router-dom' 3 | import queryString from 'query-string' 4 | import { useStore } from './StoreContext' 5 | import { observer } from 'mobx-react' 6 | import { 7 | DialogContentText, 8 | Dialog, 9 | DialogTitle, 10 | DialogContent, 11 | FormControl, 12 | TextField, 13 | FormHelperText, 14 | DialogActions, 15 | Button 16 | } from '@material-ui/core' 17 | 18 | const Content = () => ( 19 | 20 | To use regular expression match websites, please enter a valid regular 21 | expression here. You can use{' '} 22 | 23 | Regex101 24 | {' '} 25 | to validate your regular expression. 26 | 27 | ) 28 | 29 | const NewPatternDialog = observer(() => { 30 | const navigate = useNavigate() 31 | const { NewPatternStore } = useStore() 32 | const { closeDialog, error, validPattern, pattern } = NewPatternStore 33 | return ( 34 | 35 | New RegExp Pattern 36 | 37 | 38 | 39 | { 48 | NewPatternStore.setPattern(e.target.value) 49 | }} 50 | /> 51 | {error} 52 | 53 | 54 | 55 | 56 | 67 | 68 | 69 | ) 70 | }) 71 | 72 | export default observer(() => { 73 | // TODO: support to use current domain 74 | const { NewPatternStore } = useStore() 75 | const { openDialog, open } = NewPatternStore 76 | return ( 77 | 78 | 81 | {open && } 82 | 83 | ) 84 | }) 85 | -------------------------------------------------------------------------------- /src/js/components/NewTabLink.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Button from '@material-ui/core/Button' 3 | import queryString from 'query-string' 4 | import { useStore } from './StoreContext' 5 | import { observer } from 'mobx-react' 6 | import { Link } from 'react-router-dom' 7 | 8 | export default observer(() => { 9 | const { tabMode, domain, matchedHost } = useStore().AppStore 10 | if (tabMode) { 11 | return null 12 | } 13 | const query = typeof matchedHost === 'string' ? { domain } : matchedHost 14 | const to = { 15 | pathname: '/', 16 | search: queryString.stringify(query) 17 | } 18 | return ( 19 | 20 | 21 | 22 | ) 23 | }) 24 | -------------------------------------------------------------------------------- /src/js/components/Page.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import AutoSave from 'components/AutoSave' 3 | import Loading from 'components/Loading' 4 | import Editor from 'components/Editor' 5 | import Error from 'components/Error' 6 | import LoadError from 'components/LoadError' 7 | import RemoveDraft from 'components/RemoveDraft' 8 | import Goto from 'components/Goto' 9 | import Hosts from 'components/Hosts' 10 | import DonateLink from 'components/DonateLink' 11 | import Toggle from 'components/Toggle' 12 | import Include from 'components/Include' 13 | import Reset from 'components/Reset' 14 | import ModeSelect from 'components/ModeSelect' 15 | import Save from 'components/Save' 16 | import Size from 'components/Size' 17 | import NewPattern from 'components/NewPattern' 18 | import queryString from 'query-string' 19 | import NewTabLink from './NewTabLink' 20 | import { useStore } from './StoreContext' 21 | import { observer } from 'mobx-react' 22 | import Format from './Format' 23 | import HostTableLink from './HostTableLink' 24 | import { useLocation } from 'react-router-dom' 25 | 26 | const toolbarStyle = { 27 | display: 'flex', 28 | justifyContent: 'space-between', 29 | alignItems: 'center' 30 | } 31 | 32 | export default observer((props) => { 33 | const { AppStore } = useStore() 34 | const location = useLocation() 35 | useEffect(() => { 36 | const query = queryString.parse(location.search) 37 | AppStore.init(query) 38 | }, [location.search]) 39 | 40 | const { loading, error, loadError } = AppStore 41 | 42 | const closePopup = () => { 43 | if (!AppStore.tabMode) { 44 | window.close() 45 | } 46 | } 47 | 48 | if (loadError) { 49 | return 50 | } 51 | if (error) { 52 | return 53 | } 54 | if (loading) { 55 | return 56 | } 57 | return ( 58 |
65 |
66 |
67 | AppStore.save()} /> 68 | 69 | 70 | 71 | 72 | 73 |
74 |
75 | 76 |
77 |
78 |
79 |
80 | 81 | 82 | { 84 | AppStore.goTo() 85 | closePopup() 86 | }} 87 | /> 88 |
89 | 90 |
91 | 92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 |
100 |
101 | Custom JavaScript says: 102 |

103 | It seems that this page cannot be modified with me.. 104 |

105 | tip: Try refresh page 106 |
107 |
108 | ) 109 | }) 110 | -------------------------------------------------------------------------------- /src/js/components/RemoveDraft.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Button from '@material-ui/core/Button' 3 | import { useStore } from './StoreContext' 4 | import { observer } from 'mobx-react' 5 | 6 | export default observer(() => { 7 | const { draft, onRemoveDraft } = useStore().AppStore 8 | if (!draft) { 9 | return null 10 | } 11 | return ( 12 | 15 | ) 16 | }) 17 | -------------------------------------------------------------------------------- /src/js/components/Reset.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | Button, 4 | Dialog, 5 | DialogActions, 6 | DialogContent, 7 | DialogContentText, 8 | DialogTitle 9 | } from '@material-ui/core' 10 | import { useStore } from './StoreContext' 11 | import { observer } from 'mobx-react' 12 | 13 | export default observer(({ closePopup }) => { 14 | const [open, setOpen] = useState(false) 15 | const { AppStore } = useStore() 16 | const { target } = AppStore 17 | const closeDialog = () => setOpen(false) 18 | 19 | return [ 20 | , 23 | 24 | Remove all codes and external scripts? 25 | 26 | 27 | Reset will remove your codes and external scripts, and delete current 28 | domain/pattern: "{target}". 29 | 30 | 31 | 32 | 33 | 43 | 53 | 54 | 55 | ] 56 | }) 57 | -------------------------------------------------------------------------------- /src/js/components/Save.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Button from '@material-ui/core/Button' 3 | import { useStore } from './StoreContext' 4 | import { observer } from 'mobx-react' 5 | 6 | export default observer(({ onSave }) => { 7 | const { differentURL, tabMode, saved, draft } = useStore().AppStore 8 | return ( 9 | 16 | ) 17 | }) 18 | -------------------------------------------------------------------------------- /src/js/components/Size.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | Button, 4 | Dialog, 5 | DialogActions, 6 | DialogContent, 7 | DialogContentText, 8 | DialogTitle 9 | } from '@material-ui/core' 10 | import { useStore } from './StoreContext' 11 | import { observer } from 'mobx-react' 12 | 13 | const issueLink = 14 | 'https://github.com/xcv58/Custom-JavaScript-for-Websites-2/issues/32' 15 | const storageSyncDocLink = 16 | 'https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync' 17 | 18 | export default observer(() => { 19 | const { clearSaveError, saveError, size } = useStore().AppStore 20 | const alert = saveError && ( 21 | 22 | Script Save Failure 23 | 24 | 25 | Failed to save your script, it's usually because your script is too 26 | large to store in{' '} 27 | 32 | storage.sync 33 | 34 | . Please reduce your script size and try again! 35 | 36 | 37 | We know this problem and try to fix it, please follow{' '} 38 | 39 | this issue 40 | {' '} 41 | if you want. 42 | 43 | 44 | 45 | 48 | 49 | 50 | ) 51 | return ( 52 | 53 | {size} bytes{alert} 54 | 55 | ) 56 | }) 57 | -------------------------------------------------------------------------------- /src/js/components/StoreContext.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Store from '../stores' 3 | 4 | export const store = new Store() 5 | 6 | export const StoreContext = React.createContext(store) 7 | 8 | export const useStore = () => { 9 | return React.useContext(StoreContext) 10 | } 11 | -------------------------------------------------------------------------------- /src/js/components/Toggle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useStore } from './StoreContext' 3 | import { observer } from 'mobx-react' 4 | import { FormGroup, FormControlLabel, Switch } from '@material-ui/core' 5 | 6 | export default observer(() => { 7 | const { enable, toggleEnable } = useStore().AppStore 8 | return ( 9 | 10 | 13 | Enable cjs for this host 14 | 15 | } 16 | control={ 17 | 18 | } 19 | /> 20 | 21 | ) 22 | }) 23 | -------------------------------------------------------------------------------- /src/js/libs/index.tsx: -------------------------------------------------------------------------------- 1 | import orderBy from 'lodash.orderby' 2 | import uniqBy from 'lodash.uniqby' 3 | 4 | export const setLastFocusedWindowId = (lastFocusedWindowId) => { 5 | chrome.storage.local.set({ lastFocusedWindowId }) 6 | } 7 | 8 | export const getLastFocusedWindowId = async () => { 9 | const { lastFocusedWindowId } = await chrome.storage.local.get({ 10 | lastFocusedWindowId: null 11 | }) 12 | return lastFocusedWindowId 13 | } 14 | 15 | const getQueryInfo = (windowId) => { 16 | if (windowId) { 17 | return { windowId } 18 | } 19 | return { currentWindow: true } 20 | } 21 | 22 | export const getActiveTab = async () => { 23 | const windowId = await getLastFocusedWindowId() 24 | const queryInfo = getQueryInfo(windowId) 25 | const tabs = await chrome.tabs.query({ ...queryInfo, active: true }) 26 | if (tabs.length <= 0) { 27 | throw new Error('No active tab! This is impossible') 28 | } 29 | return tabs[0] 30 | } 31 | 32 | const SOURCE_PREFIX = 'data:text/javascript' 33 | const BASE64_PREFIX = SOURCE_PREFIX + ';base64,' 34 | const UTF8_PREFIX = SOURCE_PREFIX + ';charset=utf-8,' 35 | 36 | export const encodeSource = (script) => { 37 | // base64 may be smaller, but does not handle unicode characters 38 | // attempt base64 first, fall back to escaped text 39 | try { 40 | return BASE64_PREFIX + window.btoa(script) 41 | } catch (e) { 42 | return UTF8_PREFIX + encodeURIComponent(script) 43 | } 44 | } 45 | 46 | export const decodeSource = (source) => { 47 | if (source.startsWith(BASE64_PREFIX)) { 48 | return window.atob(source.replace(BASE64_PREFIX, '')) 49 | } 50 | if (source.startsWith(UTF8_PREFIX)) { 51 | return decodeURIComponent(source.replace(UTF8_PREFIX, '')) 52 | } 53 | if (!source) { 54 | return '' 55 | } 56 | return source 57 | } 58 | 59 | export const getHosts = async (key = '') => { 60 | const result = await chrome.storage.sync.get({ hosts: [] }) 61 | if (Array.isArray(result.hosts) && result.hosts.length > 0) { 62 | return result.hosts 63 | } 64 | if (!key) { 65 | return [] 66 | } 67 | const { hosts = [] } = JSON.parse(window.localStorage.getItem(key) || '{}') 68 | return hosts 69 | } 70 | 71 | const validHost = (host) => { 72 | if (typeof host === 'string') { 73 | return !!host 74 | } 75 | return !!host.pattern 76 | } 77 | 78 | export const setHosts = async (hosts = []) => { 79 | chrome.storage.sync.set({ 80 | hosts: uniqBy(hosts.filter(validHost), JSON.stringify) 81 | }) 82 | } 83 | 84 | export const clearHosts = () => { 85 | chrome.storage.sync.remove('hosts') 86 | } 87 | 88 | export const findMatchedHosts = (hosts = [], url = {}, message = {}) => { 89 | const { isRegex, pattern } = message 90 | if (isRegex && pattern) { 91 | return hosts.filter((host) => host.isRegex && host.pattern === pattern) 92 | } 93 | const matchedHosts = hosts.filter((host) => { 94 | if (typeof host === 'string') { 95 | return host === url.origin 96 | } else if (typeof host === 'object') { 97 | return new RegExp(host.pattern).test(url.href) 98 | } 99 | return false 100 | }) 101 | return orderBy(matchedHosts, ['pattern'], ['desc']) 102 | } 103 | 104 | export const getHostKey = (host) => { 105 | if (!host) { 106 | throw new Error(`getHostKey get falsy host: ${host}!`) 107 | } 108 | if (typeof host === 'string') { 109 | return host 110 | } else { 111 | return host.pattern 112 | } 113 | } 114 | 115 | export const getHostName = (host) => { 116 | const { isRegex, pattern } = host 117 | if (isRegex) { 118 | return `RegExp: ${pattern}` 119 | } 120 | return host 121 | } 122 | -------------------------------------------------------------------------------- /src/js/popup.tsx: -------------------------------------------------------------------------------- 1 | import 'chrome-extension-async' 2 | import React from 'react' 3 | import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles' 4 | import { render } from 'react-dom' 5 | import App from 'components/App' 6 | import { StoreContext, store } from 'components/StoreContext' 7 | import '../css/style.css' 8 | import 'antd/dist/antd.css' 9 | 10 | const theme = createMuiTheme({ 11 | typography: { 12 | button: { 13 | textTransform: 'none' 14 | } 15 | } 16 | }) 17 | 18 | const initApp = () => { 19 | render( 20 | 21 | 22 | 23 | 24 | , 25 | document.getElementById('app') 26 | ) 27 | } 28 | 29 | initApp() 30 | -------------------------------------------------------------------------------- /src/js/run.tsx: -------------------------------------------------------------------------------- 1 | import 'chrome-extension-async' 2 | import { getHosts, getHostKey, findMatchedHosts } from 'libs' 3 | 4 | const baseURL = chrome.runtime.getURL('base.js') 5 | 6 | const isValidURL = (url) => { 7 | return ( 8 | url && 9 | (url.startsWith('//') || 10 | url.startsWith('https://') || 11 | url.startsWith('http://')) 12 | ) 13 | } 14 | 15 | const catchErr = (e) => { 16 | console.error('Failed to inject scripts:', e) 17 | } 18 | 19 | const injectScriptPromise = (src, where = 'head') => { 20 | return new Promise((resolve, reject) => { 21 | const elm = document.createElement('script') 22 | const targetElement = document[where] ? document[where] : document.head 23 | targetElement.appendChild(elm) 24 | elm.onload = () => { 25 | resolve(`Inject ${src} complete!`) 26 | } 27 | elm.src = src 28 | }) 29 | } 30 | 31 | const extractScripts = (customjs, injections) => { 32 | if (!customjs) { 33 | return 34 | } 35 | const { 36 | config: { enable, include, extra }, 37 | source 38 | } = customjs 39 | if (!enable) { 40 | return 41 | } 42 | 43 | // base.js to provide useful functions 44 | injections.add(baseURL) 45 | 46 | // Predefined include 47 | if (include) { 48 | injections.add('https://ajax.googleapis.com/ajax/libs' + include) 49 | } 50 | 51 | // Extra include 52 | ;(extra || '') 53 | .split(';') 54 | .map((x) => x.trim()) 55 | .forEach((line) => { 56 | if (isValidURL(line)) { 57 | injections.add(line) 58 | } 59 | }) 60 | 61 | return source 62 | } 63 | 64 | const loadScripts = async (location) => { 65 | const hosts = await getHosts() 66 | const matchedHosts = findMatchedHosts(hosts, location) 67 | const injections = new Set() 68 | Promise.all( 69 | matchedHosts.map(async (host) => { 70 | const hostKey = getHostKey(host) 71 | const obj = await chrome.storage.sync.get(hostKey) 72 | return extractScripts(obj[hostKey], injections) 73 | }) 74 | ) 75 | .then((values) => values.filter((x) => x)) 76 | .then((values) => { 77 | if (values.length) { 78 | console.info( 79 | 'Custom JavaScript for websites enabled.\nPlease visit https://xcv58.xyz/inject-js if you have any issue.' 80 | ) 81 | } 82 | return Promise.all( 83 | [...injections].map((src) => src && injectScriptPromise(src)) 84 | ) 85 | .then(() => values) 86 | .catch(catchErr) 87 | }) 88 | .then((values) => values.map((src) => injectScriptPromise(src, 'body'))) 89 | .catch(catchErr) 90 | } 91 | 92 | loadScripts(window.location) 93 | -------------------------------------------------------------------------------- /src/js/stores/AppStore.tsx: -------------------------------------------------------------------------------- 1 | import { action, computed, observable, makeObservable } from 'mobx' 2 | import { encodeSource, decodeSource, getHosts, setHosts } from 'libs' 3 | import isEqual from 'lodash.isequal' 4 | import sizeof from 'object-sizeof' 5 | import { js } from 'js-beautify' 6 | import Store from 'stores' 7 | 8 | type Host = { isRegex: boolean; pattern: string } | string 9 | 10 | const key = 'popup' 11 | const defaultSource = '// Here You can type your custom JavaScript...' 12 | 13 | const getDomainKey = (host: Host) => { 14 | if (typeof host === 'object' && host.isRegex) { 15 | return `${key}-${host.pattern}` 16 | } 17 | return `${key}-${host}` 18 | } 19 | 20 | export default class AppStore { 21 | store: Store 22 | 23 | constructor (store) { 24 | makeObservable(this, { 25 | mode: observable, 26 | loading: observable, 27 | autoSaveHandle: observable, 28 | saved: observable, 29 | hosts: observable, 30 | enable: observable, 31 | source: observable, 32 | draft: observable, 33 | truth: observable, 34 | tab: observable, 35 | host: observable, 36 | protocol: observable, 37 | matchedHost: observable, 38 | loadError: observable, 39 | error: observable, 40 | saveError: observable, 41 | include: computed, 42 | extra: computed, 43 | domain: computed, 44 | target: computed, 45 | differentURL: computed, 46 | tabMode: computed, 47 | customjs: computed, 48 | size: computed, 49 | domainKey: computed, 50 | init: action, 51 | saveDraft: action, 52 | removeDraft: action, 53 | beautify: action, 54 | onChangeSource: action, 55 | onRemoveDraft: action, 56 | toggleEnable: action, 57 | save: action, 58 | removeHost: action, 59 | goTo: action, 60 | clearSaveError: action, 61 | setMode: action 62 | }) 63 | 64 | this.store = store 65 | } 66 | 67 | mode = 'javascript' 68 | 69 | loading = true 70 | 71 | autoSaveHandle = null 72 | 73 | saved = false 74 | 75 | hosts: Host[] = [] 76 | 77 | enable = false 78 | 79 | source = defaultSource 80 | 81 | draft = null 82 | 83 | truth = null 84 | 85 | tab = { url: '' } 86 | 87 | host = '' 88 | 89 | protocol = '' 90 | 91 | matchedHost: Host = '' 92 | 93 | loadError = null 94 | 95 | error = null 96 | 97 | saveError = null 98 | 99 | get include () { 100 | return this.store.IncludeStore.include 101 | } 102 | 103 | get extra () { 104 | return this.store.IncludeStore.extra 105 | } 106 | 107 | get domain () { 108 | return `${this.protocol}//${this.host}` 109 | } 110 | 111 | get target () { 112 | if (typeof this.matchedHost === 'object' && this.matchedHost.isRegex) { 113 | return this.matchedHost.pattern 114 | } 115 | return this.domain 116 | } 117 | 118 | get differentURL () { 119 | return !this.tab.url.startsWith(this.domain) 120 | } 121 | 122 | get tabMode () { 123 | return this.tab.url === window.location.href 124 | } 125 | 126 | get customjs () { 127 | return { 128 | config: { 129 | enable: this.enable, 130 | include: this.include, 131 | extra: this.extra 132 | }, 133 | source: encodeSource(this.source) 134 | } 135 | } 136 | 137 | get size () { 138 | return sizeof(this.source) 139 | } 140 | 141 | get domainKey () { 142 | return getDomainKey(this.matchedHost) 143 | } 144 | 145 | init = ({ domain, isRegex, pattern }) => { 146 | chrome.runtime.sendMessage( 147 | { method: 'getData', domain, isRegex, pattern }, 148 | async (response) => { 149 | if (!response || typeof response.host !== 'string') { 150 | if (response.error) { 151 | this.loadError = response.error 152 | return 153 | } 154 | throw new Error('Get no data for active tab!') 155 | } 156 | 157 | const { customjs, host, matchedHost, protocol, tab } = response 158 | Object.assign(this, { 159 | truth: customjs, 160 | host, 161 | matchedHost, 162 | protocol, 163 | tab 164 | }) 165 | 166 | this.hosts = await getHosts(key) 167 | this.loadDraft() 168 | 169 | if (!matchedHost) { 170 | if (isRegex) { 171 | this.error = `There is no pattern of "${pattern}"` 172 | return 173 | } else { 174 | this.hosts.push(this.domain) 175 | this.saveHosts() 176 | return this.init({ domain, isRegex, pattern }) 177 | } 178 | } 179 | 180 | if (isEqual(this.draft, this.truth)) { 181 | this.draft = null 182 | } 183 | this.loadCustomjs(this.draft || this.truth) 184 | } 185 | ) 186 | } 187 | 188 | loadCustomjs = (customjs: any = { config: {} }) => { 189 | const { 190 | source = defaultSource, 191 | config: { 192 | enable = false, 193 | include = '', 194 | extra = this.store.IncludeStore.placeholder 195 | } = {} 196 | } = customjs 197 | Object.assign(this, { enable, source: decodeSource(source) }) 198 | Object.assign(this.store.IncludeStore, { include, extra }) 199 | this.error = '' 200 | this.loading = false 201 | } 202 | 203 | saveHosts = async (hosts = this.hosts) => { 204 | setHosts(hosts.slice()) 205 | } 206 | 207 | loadDraft = () => { 208 | const { draft } = JSON.parse( 209 | window.localStorage.getItem(this.domainKey) || '{}' 210 | ) 211 | this.draft = draft 212 | } 213 | 214 | saveDraft = () => { 215 | this.draft = this.customjs 216 | window.localStorage.setItem( 217 | this.domainKey, 218 | JSON.stringify({ draft: this.draft }) 219 | ) 220 | this.saved = true 221 | this.autoSaveHandle = null 222 | } 223 | 224 | removeDraft = (domainKey = '') => { 225 | if (domainKey) { 226 | window.localStorage.removeItem(domainKey) 227 | } else { 228 | this.draft = null 229 | this.saved = false 230 | window.localStorage.removeItem(this.domainKey) 231 | } 232 | } 233 | 234 | beautify = () => { 235 | const value = js(this.source, { indent_size: 2 }) 236 | this.onChangeSource(value) 237 | } 238 | 239 | onChangeSource = (value) => { 240 | this.source = value 241 | if (!this.enable) { 242 | this.enable = true 243 | } 244 | this.autoSave() 245 | } 246 | 247 | onRemoveDraft = () => { 248 | this.removeDraft() 249 | this.loadCustomjs(this.truth) 250 | } 251 | 252 | toggleEnable = () => { 253 | this.enable = !this.enable 254 | this.autoSave() 255 | } 256 | 257 | save = () => { 258 | const { domain, customjs, matchedHost } = this 259 | chrome.runtime.sendMessage( 260 | { 261 | method: 'setData', 262 | domain, 263 | matchedHost, 264 | customjs, 265 | reload: !this.tabMode 266 | }, 267 | (err) => { 268 | if (err) { 269 | this.saveError = err 270 | } else { 271 | this.truth = this.customjs 272 | this.removeDraft() 273 | } 274 | } 275 | ) 276 | } 277 | 278 | removeHost = ({ 279 | host, 280 | reload = false 281 | }: { host?: Host; reload?: boolean } = {}) => { 282 | if (!host) { 283 | host = this.matchedHost 284 | } 285 | this.loadCustomjs() 286 | const message = { method: 'removeData', reload } 287 | let newHosts 288 | if (typeof host === 'string') { 289 | Object.assign(message, { domain: host }) 290 | newHosts = this.hosts.filter((x) => x !== host) 291 | } else { 292 | Object.assign(message, host, { domain: this.domain }) 293 | const { pattern } = host 294 | newHosts = this.hosts.filter( 295 | (x) => typeof x === 'string' || !x.isRegex || x.pattern !== pattern 296 | ) 297 | } 298 | this.saveHosts(newHosts) 299 | chrome.runtime.sendMessage(message) 300 | this.hosts = newHosts 301 | this.removeDraft(getDomainKey(host)) 302 | } 303 | 304 | goTo = () => chrome.runtime.sendMessage({ method: 'goTo', link: this.domain }) 305 | 306 | clearSaveError = () => { 307 | this.saveError = null 308 | } 309 | 310 | autoSave = () => { 311 | if (this.autoSaveHandle) { 312 | clearTimeout(this.autoSaveHandle) 313 | } 314 | this.autoSaveHandle = setTimeout(this.saveDraft, 500) 315 | } 316 | 317 | setMode = (mode) => { 318 | this.mode = mode 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/js/stores/IncludeStore.tsx: -------------------------------------------------------------------------------- 1 | import { action, computed, observable, makeObservable } from 'mobx' 2 | import Store from 'stores' 3 | 4 | const hint = 5 | '# Uncomment address of script below or type your own (one per line)' 6 | const underscore = 7 | '# //cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js;' 8 | 9 | export default class IncludeStore { 10 | store: Store 11 | 12 | constructor (store) { 13 | makeObservable(this, { 14 | extraOpen: observable, 15 | extra: observable, 16 | include: observable, 17 | includes: observable, 18 | extraValue: computed, 19 | placeholder: computed, 20 | onSelect: action, 21 | toggleExtraOpen: action, 22 | onUpdateExtra: action 23 | }) 24 | 25 | this.store = store 26 | } 27 | 28 | extraOpen = false 29 | 30 | extra = '' 31 | 32 | include = '' 33 | 34 | includes = [ 35 | { 36 | name: 'jQuery 1.12.4', 37 | path: '/jquery/1.12.4/jquery.min.js' 38 | }, 39 | { 40 | name: 'jQuery 2.2.4', 41 | path: '/jquery/2.2.4/jquery.min.js' 42 | }, 43 | { 44 | name: 'jQuery 3.2.1', 45 | path: '/jquery/3.2.1/jquery.min.js' 46 | }, 47 | { 48 | name: 'Indefinite Observable 1.0.1', 49 | path: '/indefinite-observable/1.0.1/indefinite-observable.js' 50 | }, 51 | { 52 | name: 'MooTools 1.6.0', 53 | path: '/mootools/1.6.0/mootools.min.js' 54 | } 55 | ] 56 | 57 | get extraValue () { 58 | return (this.extra || '').replace(';', '\n') 59 | } 60 | 61 | get placeholder () { 62 | return hint + '\n' + underscore 63 | } 64 | 65 | onSelect = (include) => { 66 | this.include = include 67 | this.store.AppStore.autoSave() 68 | } 69 | 70 | toggleExtraOpen = () => { 71 | this.extraOpen = !this.extraOpen 72 | } 73 | 74 | onUpdateExtra = (value = '') => { 75 | this.extra = value.replace('\n', ';') 76 | this.store.AppStore.autoSave() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/js/stores/NewPatternStore.tsx: -------------------------------------------------------------------------------- 1 | import { action, computed, observable, makeObservable } from 'mobx' 2 | import Store from 'stores' 3 | 4 | export default class NewPatternStore { 5 | store: Store 6 | re: RegExp 7 | 8 | constructor (store) { 9 | makeObservable(this, { 10 | open: observable, 11 | pattern: observable, 12 | validPattern: computed, 13 | host: computed, 14 | error: computed, 15 | setPattern: action, 16 | addToHosts: action, 17 | closeDialog: action, 18 | openDialog: action 19 | }) 20 | 21 | this.store = store 22 | } 23 | 24 | open = false 25 | 26 | pattern = '' 27 | 28 | get validPattern () { 29 | return !this.error 30 | } 31 | 32 | get host () { 33 | return { 34 | isRegex: true, 35 | pattern: this.pattern 36 | } 37 | } 38 | 39 | get error () { 40 | if (!this.pattern) { 41 | return 'Empty Pattern' 42 | } 43 | try { 44 | this.re = new RegExp(this.pattern) 45 | } catch (err) { 46 | return err.message 47 | } 48 | const { hosts } = this.store.AppStore 49 | if ( 50 | hosts.find( 51 | (x) => typeof x === 'object' && x.isRegex && x.pattern === this.pattern 52 | ) 53 | ) { 54 | return `Pattern ${this.pattern} already exists` 55 | } 56 | } 57 | 58 | setPattern = (value) => { 59 | this.pattern = value 60 | } 61 | 62 | addToHosts = () => { 63 | if (this.error) { 64 | throw new Error(`addToHost failed, pattern "${this.pattern}" is invalid!`) 65 | } 66 | const { hosts, saveHosts } = this.store.AppStore 67 | hosts.push(this.host) 68 | saveHosts() 69 | this.closeDialog() 70 | } 71 | 72 | closeDialog = () => { 73 | this.open = false 74 | this.clear() 75 | } 76 | 77 | openDialog = () => { 78 | this.clear() 79 | this.open = true 80 | } 81 | 82 | clear = () => { 83 | this.pattern = '' 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/js/stores/index.tsx: -------------------------------------------------------------------------------- 1 | import AppStore from './AppStore' 2 | import IncludeStore from './IncludeStore' 3 | import NewPatternStore from './NewPatternStore' 4 | 5 | export default class Store { 6 | AppStore: AppStore 7 | 8 | IncludeStore: IncludeStore 9 | 10 | NewPatternStore: NewPatternStore 11 | 12 | constructor () { 13 | this.AppStore = new AppStore(this) 14 | this.IncludeStore = new IncludeStore(this) 15 | this.NewPatternStore = new NewPatternStore(this) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Custom JavaScript for Websites 2", 4 | "short_name": "customjs", 5 | "author": "xcv58", 6 | "minimum_chrome_version": "52.0.2743", 7 | "icons": { 8 | "128": "icon-128.png", 9 | "48": "icon-48.png", 10 | "38": "icon-38.png", 11 | "32": "icon-32.png", 12 | "19": "icon-19.png", 13 | "16": "icon-16.png" 14 | }, 15 | "background": { 16 | "scripts": ["background.js"], 17 | "persistent": false 18 | }, 19 | "content_scripts": [ 20 | { 21 | "all_frames": true, 22 | "js": ["run.js"], 23 | "matches": ["\u003Call_urls>"] 24 | } 25 | ], 26 | "web_accessible_resources": ["*.css", "base.js"], 27 | "browser_action": { 28 | "default_icon": "icon-48.png", 29 | "default_popup": "popup.html" 30 | }, 31 | "content_security_policy": "script-src 'self' 'unsafe-eval' https://*.googleapis.com https://gitcdn.link https://cdnjs.cloudflare.com; object-src 'self'", 32 | "permissions": ["storage", "http://*/", "https://*/", "tabs"] 33 | } 34 | -------------------------------------------------------------------------------- /src/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Custom JavaScript 5 | 6 | 7 |
Loading...
8 | 9 | 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2018", "dom"], 4 | "types": ["jasmine"], 5 | "outDir": "./build/", 6 | "baseUrl": "./src/js", 7 | "paths": { 8 | "*": ["*"], 9 | "@material-ui/core": ["./material-ui/src"], 10 | "@material-ui/core/*": ["./material-ui/src/*"], 11 | "@material-ui/lab": ["./material-ui-lab/src"], 12 | "@material-ui/lab/*": ["./material-ui-lab/src/*"] 13 | }, 14 | "esModuleInterop": true, 15 | "experimentalDecorators": true, 16 | "emitDecoratorMetadata": true, 17 | "sourceMap": true, 18 | "noImplicitAny": false, 19 | "module": "es6", 20 | "moduleResolution": "node", 21 | "target": "es6", 22 | "jsx": "react", 23 | "allowJs": true 24 | }, 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /utils/analyze.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const config = require('../webpack.config') 3 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer') 4 | .BundleAnalyzerPlugin 5 | 6 | webpack( 7 | config([ 8 | new BundleAnalyzerPlugin({ 9 | analyzerMode: 'server', 10 | analyzerHost: '127.0.0.1', 11 | analyzerPort: 8888, 12 | defaultSizes: 'parsed', 13 | openAnalyzer: true, 14 | generateStatsFile: false, 15 | logLevel: 'info' 16 | }) 17 | ]), 18 | function (err) { 19 | if (err) throw err 20 | } 21 | ) 22 | -------------------------------------------------------------------------------- /utils/build.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const config = require('../webpack.config')() 3 | const TerserJSPlugin = require('terser-webpack-plugin') 4 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') 5 | 6 | delete config.chromeExtensionBoilerplate 7 | 8 | webpack( 9 | { 10 | ...config, 11 | mode: 'production', 12 | optimization: { 13 | minimizer: [ 14 | new TerserJSPlugin({ 15 | test: /\.js(\?.*)?$/i, 16 | parallel: true 17 | }), 18 | new OptimizeCSSAssetsPlugin({}) 19 | ] 20 | } 21 | }, 22 | function (err) { 23 | if (err) throw err 24 | } 25 | ) 26 | -------------------------------------------------------------------------------- /utils/env.js: -------------------------------------------------------------------------------- 1 | // tiny wrapper with default env vars 2 | module.exports = { 3 | NODE_ENV: process.env.NODE_ENV || 'development', 4 | PORT: process.env.PORT || 3000 5 | } 6 | -------------------------------------------------------------------------------- /utils/webserver.js: -------------------------------------------------------------------------------- 1 | const WebpackDevServer = require('webpack-dev-server') 2 | const webpack = require('webpack') 3 | const config = require('../webpack.config')() 4 | const env = require('./env') 5 | 6 | const options = config.chromeExtensionBoilerplate || {} 7 | const excludeEntriesToHotReload = options.notHotReload || [] 8 | 9 | for (const entryName in config.entry) { 10 | if (excludeEntriesToHotReload.indexOf(entryName) === -1) { 11 | config.entry[entryName] = [ 12 | 'webpack-dev-server/client?http://localhost:' + env.PORT, 13 | 'webpack/hot/dev-server' 14 | ].concat(config.entry[entryName]) 15 | } 16 | } 17 | 18 | config.plugins = [new webpack.HotModuleReplacementPlugin()].concat( 19 | config.plugins || [] 20 | ) 21 | 22 | delete config.chromeExtensionBoilerplate 23 | 24 | const compiler = webpack({ ...config, mode: 'development' }) 25 | 26 | const server = new WebpackDevServer(compiler, { 27 | devMiddleware: { 28 | writeToDisk: true 29 | }, 30 | hot: true, 31 | headers: { 'Access-Control-Allow-Origin': '*' } 32 | }) 33 | 34 | server.listen(env.PORT) 35 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const path = require('path') 3 | const fileSystem = require('fs') 4 | const env = require('./utils/env') 5 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 6 | const CopyWebpackPlugin = require('copy-webpack-plugin') 7 | const HtmlWebpackPlugin = require('html-webpack-plugin') 8 | const ProgressBarPlugin = require('progress-bar-webpack-plugin') 9 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 10 | 11 | const srcPath = (subdir) => { 12 | return path.join(__dirname, 'src/js', subdir) 13 | } 14 | 15 | const alias = { 16 | background: srcPath('background'), 17 | components: srcPath('components'), 18 | libs: srcPath('libs'), 19 | stores: srcPath('stores'), 20 | svgIcons: srcPath('svgIcons'), 21 | img: path.join(__dirname, 'src/img') 22 | } 23 | 24 | const secretsPath = path.join(__dirname, 'secrets.' + env.NODE_ENV + '.js') 25 | 26 | const fileExtensions = [ 27 | 'jpg', 28 | 'jpeg', 29 | 'png', 30 | 'gif', 31 | 'eot', 32 | 'otf', 33 | 'svg', 34 | 'ttf', 35 | 'woff', 36 | 'woff2' 37 | ] 38 | 39 | if (fileSystem.existsSync(secretsPath)) { 40 | alias.secrets = secretsPath 41 | } 42 | 43 | const imgDir = path.join(__dirname, 'src/img') 44 | const images = fileSystem 45 | .readdirSync(imgDir) 46 | .filter((x) => x.endsWith('.png')) 47 | .map((x) => path.join(imgDir, x)) 48 | 49 | const HtmlFiles = ['popup'].map( 50 | (name) => 51 | new HtmlWebpackPlugin({ 52 | template: path.join(__dirname, 'src', `${name}.html`), 53 | filename: `${name}.html`, 54 | chunks: [name], 55 | minify: { 56 | collapseWhitespace: true 57 | } 58 | }) 59 | ) 60 | 61 | const entry = Object.assign( 62 | ...['popup', 'background', 'run', 'base'].map((name) => ({ 63 | [name]: path.join(__dirname, 'src', 'js', `${name}.tsx`) 64 | })) 65 | ) 66 | 67 | const options = { 68 | entry, 69 | output: { 70 | path: path.join(__dirname, 'build'), 71 | filename: '[name].js' 72 | }, 73 | module: { 74 | rules: [ 75 | { 76 | test: /\.m?js/, 77 | type: 'javascript/auto', 78 | resolve: { 79 | fullySpecified: false 80 | } 81 | }, 82 | { 83 | test: /\.css$/, 84 | use: ['style-loader', 'css-loader'] 85 | }, 86 | { 87 | test: new RegExp(`\\.(${fileExtensions.join('|')})$`), 88 | loader: 'file-loader', 89 | options: { 90 | name: '[name].[ext]' 91 | }, 92 | include: path.resolve(__dirname, 'src'), 93 | exclude: /node_modules/ 94 | }, 95 | { 96 | test: /\.html$/, 97 | loader: 'html-loader', 98 | include: path.resolve(__dirname, 'src'), 99 | exclude: /node_modules/ 100 | }, 101 | { 102 | test: /\.(js|ts)x?$/, 103 | use: [ 104 | { 105 | loader: 'ts-loader', 106 | options: { 107 | transpileOnly: true, 108 | experimentalWatchApi: true 109 | } 110 | } 111 | ], 112 | include: path.resolve(__dirname, 'src') 113 | } 114 | ] 115 | }, 116 | resolve: { 117 | alias, 118 | extensions: fileExtensions 119 | .map((extension) => '.' + extension) 120 | .concat(['.css', '.jsx', '.js', '.tsx', 'ts']) 121 | }, 122 | plugins: [ 123 | // expose and write the allowed env vars on the compiled bundle 124 | new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }), 125 | new MiniCssExtractPlugin({ 126 | filename: '[name].css', 127 | chunkFilename: '[id].css' 128 | }), 129 | new webpack.DefinePlugin({ 130 | 'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV) 131 | }), 132 | new CopyWebpackPlugin({ 133 | patterns: [ 134 | ...images, 135 | { 136 | from: 'src/manifest.json', 137 | transform: function (content, path) { 138 | return Buffer.from( 139 | JSON.stringify({ 140 | description: process.env.npm_package_description, 141 | version: process.env.npm_package_version, 142 | ...JSON.parse(content.toString()) 143 | }) 144 | ) 145 | } 146 | } 147 | ] 148 | }), 149 | ...HtmlFiles, 150 | new ProgressBarPlugin() 151 | ] 152 | } 153 | 154 | if (env.NODE_ENV === 'development') { 155 | options.devtool = 'eval-cheap-source-map' 156 | } 157 | 158 | module.exports = (plugins = []) => ({ 159 | ...options, 160 | plugins: [...options.plugins, ...plugins] 161 | }) 162 | --------------------------------------------------------------------------------