├── .eslintignore ├── .eslintrc ├── .github └── workflows │ ├── build.yaml │ └── release.yml ├── .gitignore ├── .npmignore ├── .twosky.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── bamboo-specs ├── bamboo.yaml ├── build-beta.yaml ├── build-release.yaml ├── deploy-beta.yaml ├── deploy-release.yaml ├── increment.yaml ├── permissions-beta.yaml ├── permissions-release.yaml └── tests.yaml ├── dist ├── assistant.d.ts ├── assistant.js └── self.assistant.js ├── locales ├── ar │ ├── messages.json │ └── messages.meta.json ├── be │ ├── messages.json │ └── messages.meta.json ├── bg │ ├── messages.json │ └── messages.meta.json ├── ca │ ├── messages.json │ └── messages.meta.json ├── cs │ ├── messages.json │ └── messages.meta.json ├── da │ ├── messages.json │ └── messages.meta.json ├── de │ ├── messages.json │ └── messages.meta.json ├── el │ ├── messages.json │ └── messages.meta.json ├── en │ ├── messages.json │ └── messages.meta.json ├── es │ ├── messages.json │ └── messages.meta.json ├── fa │ ├── messages.json │ └── messages.meta.json ├── fi │ ├── messages.json │ └── messages.meta.json ├── fr │ ├── messages.json │ └── messages.meta.json ├── he │ ├── messages.json │ └── messages.meta.json ├── hi │ ├── messages.json │ └── messages.meta.json ├── hr │ ├── messages.json │ └── messages.meta.json ├── hu │ ├── messages.json │ └── messages.meta.json ├── id │ ├── messages.json │ └── messages.meta.json ├── index.js ├── it │ ├── messages.json │ └── messages.meta.json ├── ja │ ├── messages.json │ └── messages.meta.json ├── ko │ ├── messages.json │ └── messages.meta.json ├── lt │ ├── messages.json │ └── messages.meta.json ├── mk │ ├── messages.json │ └── messages.meta.json ├── nl │ ├── messages.json │ └── messages.meta.json ├── no │ ├── messages.json │ └── messages.meta.json ├── pl │ ├── messages.json │ └── messages.meta.json ├── pt-PT │ ├── messages.json │ └── messages.meta.json ├── pt │ ├── messages.json │ └── messages.meta.json ├── ro │ ├── messages.json │ └── messages.meta.json ├── ru │ ├── messages.json │ └── messages.meta.json ├── sk │ ├── messages.json │ └── messages.meta.json ├── sl │ ├── messages.json │ └── messages.meta.json ├── sr │ ├── messages.json │ └── messages.meta.json ├── sv │ ├── messages.json │ └── messages.meta.json ├── th │ ├── messages.json │ └── messages.meta.json ├── tr │ ├── messages.json │ └── messages.meta.json ├── uk │ ├── messages.json │ └── messages.meta.json ├── vi │ ├── messages.json │ └── messages.meta.json ├── zh-HK │ ├── messages.json │ └── messages.meta.json ├── zh-TW │ ├── messages.json │ └── messages.meta.json └── zh │ ├── messages.json │ └── messages.meta.json ├── package.json ├── pnpm-lock.yaml ├── scripts ├── build │ ├── constants.js │ ├── meta.settings.js │ ├── meta.template.js │ ├── metadata.plugin.js │ ├── webpack.common.config.js │ ├── webpack.self.config.js │ ├── webpack.umd.config.js │ └── webpack.user.config.js └── locales │ ├── cli-log.js │ ├── constants.js │ ├── helpers.js │ ├── locales.js │ └── validate.js ├── src ├── adguard-rules-constructor.js ├── adguard-selector.js ├── button.js ├── controllers │ ├── blockPreviewController.js │ ├── mainMenuController.js │ ├── selectorMenuController.js │ ├── settingsMenuController.js │ ├── sliderMenuController.js │ └── sliderMenuControllerMobile.js ├── embedded.js ├── event.js ├── gm-empty.js ├── gm.js ├── helpers.js ├── iframe.js ├── iframe.mobile.js ├── index.js ├── index.user.js ├── inline-resources.js ├── ioc.js ├── libs │ └── css.escape.js ├── localization.js ├── log.js ├── main.js ├── protectedApi.js ├── runSheduler.js ├── settings.js ├── slider-widget.js ├── styles │ ├── base │ │ ├── base-common.less │ │ ├── buttons.less │ │ ├── dark-mode.less │ │ ├── fonts.css │ │ ├── fonts.less │ │ ├── form-ui.less │ │ ├── forms.less │ │ ├── light-mode.less │ │ ├── normalize.less │ │ ├── palette.less │ │ ├── scaffolding.less │ │ ├── tooltip.less │ │ └── variables.less │ ├── button.less │ ├── img │ │ ├── check.svg │ │ ├── clock.svg │ │ ├── close-white.svg │ │ ├── close.svg │ │ ├── cross.svg │ │ ├── extensions.svg │ │ ├── eye-closed.svg │ │ ├── eye-opened.svg │ │ ├── landscape.svg │ │ ├── logo.svg │ │ ├── minus.svg │ │ ├── plus.svg │ │ ├── pos-arr.svg │ │ ├── report.svg │ │ ├── security.svg │ │ ├── settings.svg │ │ ├── wot-confidence-0.svg │ │ ├── wot-confidence-1.svg │ │ ├── wot-confidence-2.svg │ │ ├── wot-confidence-3.svg │ │ ├── wot-confidence-4.svg │ │ ├── wot-confidence-5.svg │ │ └── wot-logo.svg │ ├── menu.less │ ├── mixins │ │ ├── forms.less │ │ ├── mixins.less │ │ ├── pagination.less │ │ ├── responsive-visibility.less │ │ ├── tab-focus.less │ │ ├── table-row.less │ │ ├── text-emphasis.less │ │ ├── text-overflow.less │ │ └── vendor-prefixes.less │ ├── mobile-style.less │ ├── modules │ │ ├── element-rule.less │ │ ├── host.less │ │ ├── layout.less │ │ ├── menu.less │ │ ├── modules-common.less │ │ ├── settings.less │ │ ├── sprite.less │ │ ├── tooltip.less │ │ └── wot.less │ └── selector.less ├── templates │ ├── blockPreview.html │ ├── button.html │ ├── mainMenu.html │ ├── mobileMenu.html │ ├── mobilePopup.html │ ├── selectorMenu.html │ ├── settingsMenu.html │ ├── sliderMenu.html │ └── svgIcons.html ├── upgradeHelper.js ├── utils │ ├── common-utils.js │ ├── dom-utils.js │ ├── ui-utils.js │ └── ui-validation-utils.js └── wot.js ├── tests ├── assistant.test.js ├── index.html ├── index.test.js ├── puppeteer.js └── webpack.test.config.js └── types └── assistant.d.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /build/ 3 | /tests/dist/ 4 | /dist/ 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "env": { 4 | "browser": true, 5 | "node": true 6 | }, 7 | "rules": { 8 | "no-console": "error", 9 | "max-len": ["error", { 10 | "code": 120, 11 | "comments": 120, 12 | "tabWidth": 4, 13 | "ignoreUrls": true, 14 | "ignoreTrailingComments": false, 15 | "ignoreComments": false, 16 | "ignoreTemplateLiterals": true 17 | }], 18 | "indent": [ 19 | "error", 20 | 4, 21 | { "SwitchCase": 1 } 22 | ], 23 | "import/no-extraneous-dependencies": 0, 24 | "import/prefer-default-export": 0, 25 | "arrow-body-style": 0 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build AdGuard Assistant 2 | 3 | env: 4 | NODE_VERSION: 22.14.0 5 | PNPM_VERSION: 10.7.1 6 | # Forks do not have access to our vars 7 | VARS_PNPM: ${{ vars.VARS_PNPM || '--silent --ignore-scripts' }} 8 | 9 | on: push 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Setup pnpm 18 | uses: pnpm/action-setup@v4 19 | with: 20 | version: ${{ env.PNPM_VERSION }} 21 | run_install: false 22 | 23 | - name: Use Node.jobs 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: ${{ env.NODE_VERSION }} 27 | 28 | - name: Install dependencies 29 | run: pnpm install ${{ env.VARS_PNPM }} 30 | 31 | - name: Run linter 32 | run: pnpm lint 33 | 34 | - name: Build release 35 | run: pnpm release 36 | 37 | notify: 38 | needs: 39 | - build 40 | 41 | if: 42 | ${{ always() && 43 | ( 44 | github.event_name == 'push' || 45 | github.event.pull_request.head.repo.full_name == github.repository 46 | ) 47 | }} 48 | runs-on: ubuntu-latest 49 | steps: 50 | - name: Conclusion 51 | uses: technote-space/workflow-conclusion-action@v3 52 | - name: Send Slack notif 53 | uses: 8398a7/action-slack@v3 54 | with: 55 | status: ${{ env.WORKFLOW_CONCLUSION }} 56 | fields: workflow, repo, message, commit, author, eventName, ref 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 60 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | env: 4 | NODE_VERSION: 22.14.0 5 | PNPM_VERSION: 10.7.1 6 | # Forks do not have access to our vars 7 | VARS_PNPM: ${{ vars.VARS_PNPM || '--silent --ignore-scripts' }} 8 | 9 | # Workflow need write access to the repository to create a GitHub release 10 | permissions: 11 | contents: write 12 | 13 | on: 14 | push: 15 | tags: 16 | - v* 17 | # Make possible to run manually 18 | workflow_dispatch: 19 | inputs: 20 | # warn before running manually 21 | warning: 22 | description: 'Should be run only for tags like `v*`' 23 | required: false 24 | type: boolean 25 | 26 | # Make sure that only one release workflow runs at a time. 27 | concurrency: 28 | group: release 29 | 30 | jobs: 31 | release: 32 | name: Create NPM and GitHub release 33 | runs-on: ubuntu-latest 34 | # Only run this job for v* tags 35 | if: startsWith(github.ref, 'refs/tags/v') 36 | steps: 37 | - name: Check out the repository 38 | uses: actions/checkout@v4 39 | 40 | - name: Setup pnpm 41 | uses: pnpm/action-setup@v4 42 | with: 43 | version: ${{ env.PNPM_VERSION }} 44 | run_install: false 45 | 46 | - name: Set up Node.js 47 | uses: actions/setup-node@v4 48 | with: 49 | node-version: ${{ env.NODE_VERSION }} 50 | registry-url: https://registry.npmjs.org 51 | cache: pnpm 52 | 53 | - name: Install dependencies 54 | run: pnpm install ${{ env.VARS_PNPM }} 55 | 56 | - name: Run ESLint 57 | run: pnpm lint 58 | 59 | - name: Run tests 60 | run: pnpm test 61 | 62 | - name: Run Rollup build 63 | run: pnpm build 64 | 65 | - name: Validate locales 66 | run: pnpm locales:validate-required 67 | 68 | - name: Pack files 69 | run: pnpm pack && mv adguard-assistant-*.tgz assistant.tgz 70 | 71 | - name: Release on GitHub 72 | uses: softprops/action-gh-release@v1 73 | with: 74 | files: | 75 | assistant.tgz 76 | dist/assistant.js 77 | dist/self.assistant.js 78 | draft: false 79 | prerelease: false 80 | body: See [CHANGELOG.md](./CHANGELOG.md) for the list of changes. 81 | env: 82 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 83 | 84 | notify: 85 | name: Send Slack notification 86 | needs: 87 | - release 88 | 89 | if: 90 | ${{ always() && 91 | ( 92 | github.event_name == 'push' || 93 | github.event_name == 'workflow_dispatch' 94 | ) 95 | }} 96 | runs-on: ubuntu-latest 97 | steps: 98 | - name: Conclusion 99 | uses: technote-space/workflow-conclusion-action@v3 100 | 101 | - name: Send Slack notification 102 | uses: 8398a7/action-slack@v3 103 | with: 104 | status: ${{ env.WORKFLOW_CONCLUSION }} 105 | fields: workflow, repo, message, commit, author, eventName, ref 106 | env: 107 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 108 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 109 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | build/ 3 | node_modules/ 4 | tests/dist/ 5 | .DS_Store 6 | .pnpm-store/ 7 | .npmrc 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /.github 2 | /bamboo-specs 3 | /build 4 | /locales 5 | /scripts 6 | /src 7 | /tests 8 | /types 9 | /.pnpm-store 10 | .eslintignore 11 | .eslintrc 12 | .gitignore 13 | .npmignore 14 | .twosky.json 15 | .idea 16 | babel.config.js 17 | pnpm-lock.yaml 18 | -------------------------------------------------------------------------------- /.twosky.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "project_id": "adguard-assistant", 4 | "base_locale": "en", 5 | "languages": { 6 | "en": "English", 7 | "ar": "Arabic", 8 | "be": "Belarusian", 9 | "bg": "Bulgarian", 10 | "ca": "Catalan", 11 | "cs": "Czech", 12 | "da": "Danish", 13 | "de": "German", 14 | "el": "Greek", 15 | "es": "Spanish", 16 | "fa": "Persian", 17 | "fi": "Finnish", 18 | "fr": "French", 19 | "he": "Hebrew", 20 | "hi": "Hindi", 21 | "hr": "Croatian", 22 | "hu": "Hungarian", 23 | "id": "Indonesian", 24 | "it": "Italian", 25 | "ja": "Japanese", 26 | "ko": "Korean", 27 | "lt": "Lithuanian", 28 | "mk": "Macedonian", 29 | "no": "Norwegian", 30 | "nl": "Dutch", 31 | "pl": "Polish", 32 | "pt-BR": "Portuguese (Brazil)", 33 | "pt-PT": "Portuguese", 34 | "ro": "Romanian", 35 | "ru": "Russian", 36 | "sk": "Slovak", 37 | "sl": "Slovenian", 38 | "sr": "Serbian (latin)", 39 | "sv": "Swedish", 40 | "th": "Thai", 41 | "tr": "Turkish", 42 | "uk": "Ukrainian", 43 | "vi": "Vietnamese", 44 | "zh-CN": "Chinese Simplified (mainland China)", 45 | "zh-HK": "Chinese Traditional (Hong Kong)", 46 | "zh-TW": "Chinese Traditional (Taiwan)" 47 | }, 48 | "localizable_files": [ 49 | "locales/en/messages.json", 50 | "locales/en/messages.meta.json" 51 | ] 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # AdGuard Assistant Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog], and this project adheres to [Semantic Versioning]. 6 | 7 | [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ 8 | [Semantic Versioning]: https://semver.org/spec/v2.0.0.html 9 | 10 | ## [4.3.76] - 2025-09-23 11 | 12 | ### Fixed 13 | 14 | - AdGuard Assistant icon doesn't appear on YouTube and Google sites [#438]. 15 | 16 | [#438]: https://github.com/AdguardTeam/AdguardAssistant/issues/438 17 | [4.3.76]: https://github.com/AdguardTeam/AdguardAssistant/compare/v4.3.75...v4.3.76 18 | 19 | ## [4.3.75] - 2025-07-29 20 | 21 | ### Fixed 22 | 23 | - Fix broken settings screen [#433]. 24 | 25 | [#433]: https://github.com/AdguardTeam/AdguardAssistant/issues/433 26 | [4.3.75]: https://github.com/AdguardTeam/AdguardAssistant/compare/v4.3.74...v4.3.75 27 | 28 | ## [4.3.74] - 2025-06-30 29 | 30 | ### Added 31 | 32 | - Support of Bulgarian, Catalan, Macedonian and Thai locales. 33 | 34 | ### Changed 35 | 36 | - Updated UI of element selector menu to match with other projects. 37 | 38 | ### Fixed 39 | 40 | - Issue with slider dragging does not work properly on touch devices. 41 | 42 | [4.3.74]: https://github.com/AdguardTeam/AdguardAssistant/compare/v4.3.70...v4.3.74 43 | 44 | ## [4.3.70] - 2023-06-01 45 | 46 | ### Added 47 | 48 | - `self` type build [AdguardForMac#1246]. 49 | 50 | [4.3.70]: https://github.com/AdguardTeam/AdguardAssistant/compare/v4.3.68...v4.3.70 51 | [AdguardForMac#1246]: https://github.com/AdguardTeam/AdguardForMac/issues/1246 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AdGuard Assistant 2 | 3 | AdGuard Assistant is a userscript that helps you manage filtering right from the browser. With its aid you are able to manually block any element, whitelist or report the page, or see the website's security report — all without having to leave the current page. 4 | 5 | ## How to report an issue? 6 | 7 | GitHub can be used to report a bug or to submit a feature request. To do so, go to [this page](https://github.com/AdguardTeam/AdguardAssistant/issues) and click the *New issue* button. 8 | 9 | ## Our plans 10 | 11 | To see the 'big picture', to watch current progress and to get an idea of approximate dates for upcoming AdGuard Assistant releases, see this page: https://github.com/AdguardTeam/AdguardAssistant/milestones 12 | 13 | ## Releases 14 | 15 | You can find all AdGuard Assistant releases here: https://github.com/AdguardTeam/AdguardAssistant/releases 16 | 17 | ## Development 18 | 19 | > Make sure you have installed [Node.js] v22 or later 20 | > and [pnpm] v10 or later. 21 | 22 | ### Install dependencies 23 | 24 | ```bash 25 | pnpm install 26 | ``` 27 | 28 | ### Build 29 | 30 | | Command | Output Dir | 31 | |-------------------|---------------| 32 | | `pnpm dev` | build/dev | 33 | | `pnpm beta` | build/beta | 34 | | `pnpm release` | build/release | 35 | 36 | Each command builds next files inside directory: 37 | 38 | - `assistant.meta.js` — meta information for userscript; 39 | - `assistant.user.js` — userscript code; 40 | - `assistant.js` — code for embedding in another projects, it has functionality 41 | for selection of DOM nodes and rules building, 42 | - `build.txt` — bamboo environment variables. 43 | 44 | ## Usage 45 | 46 | ### Installation 47 | 48 | #### pnpm 49 | 50 | ```bash 51 | pnpm add @adguard/assistant 52 | ``` 53 | 54 | #### NPM 55 | 56 | ```bash 57 | npm install @adguard/assistant 58 | ``` 59 | 60 | ### Usage of module 61 | 62 | ```js 63 | import { adguardAssistant } from '@adguard/assistant' 64 | adguardAssistant().start(null, callback); 65 | ``` 66 | 67 | ### Usage of embedded version 68 | 69 | Embedded version can be run as: 70 | 71 | ```js 72 | adguardAssistant().start(null, callback); 73 | ``` 74 | 75 | Callback returns a rule string. 76 | 77 | ## Todo 78 | 79 | - write tests 80 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | api.cache(false); 3 | return { 4 | presets: ['@babel/preset-env'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /bamboo-specs/bamboo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | !include 'tests.yaml' 3 | 4 | --- 5 | !include 'increment.yaml' 6 | 7 | --- 8 | !include 'build-beta.yaml' 9 | 10 | --- 11 | !include 'build-release.yaml' 12 | 13 | --- 14 | !include 'deploy-beta.yaml' 15 | 16 | --- 17 | !include 'deploy-release.yaml' 18 | 19 | --- 20 | !include 'permissions-beta.yaml' 21 | 22 | --- 23 | !include 'permissions-release.yaml' 24 | -------------------------------------------------------------------------------- /bamboo-specs/build-beta.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | plan: 4 | project-key: ADGEXT 5 | key: LEGASSBETASPECS 6 | name: legacy assistant - build beta 7 | variables: 8 | dockerNode: adguard/node-ssh:22.17--0 9 | 10 | stages: 11 | - Build: 12 | manual: false 13 | final: false 14 | jobs: 15 | - Build 16 | 17 | Build: 18 | key: BUILD 19 | other: 20 | clean-working-dir: true 21 | docker: 22 | image: ${bamboo.dockerNode} 23 | volumes: 24 | ${system.PNPM_DIR}: "${bamboo.cachePnpm}" 25 | tasks: 26 | - checkout: 27 | force-clean-build: true 28 | - script: 29 | interpreter: SHELL 30 | scripts: 31 | - |- 32 | set -x 33 | set -e 34 | ls -alt 35 | 36 | pnpm install ${bamboo.varsPnpm} 37 | 38 | pnpm lint 39 | 40 | pnpm locales:validate-required 41 | 42 | pnpm beta 43 | - inject-variables: 44 | file: build/beta/build.txt 45 | scope: RESULT 46 | namespace: userscriptMeta 47 | - any-task: 48 | plugin-key: com.atlassian.bamboo.plugins.vcs:task.vcs.tagging 49 | configuration: 50 | selectedRepository: defaultRepository 51 | tagName: v${bamboo.userscriptMeta.version}-beta 52 | final-tasks: 53 | - script: 54 | interpreter: SHELL 55 | scripts: 56 | - |- 57 | set -x 58 | set -e 59 | 60 | # Fix mixed logs 61 | exec 2>&1 62 | 63 | ls -la 64 | 65 | echo "Size before cleanup:" && du -h | tail -n 1 66 | rm -rf node_modules 67 | echo "Size after cleanup:" && du -h | tail -n 1 68 | artifacts: 69 | - name: assistant.meta.js 70 | location: build/beta 71 | pattern: assistant.meta.js 72 | shared: true 73 | required: true 74 | - name: assistant.user.js 75 | location: build/beta 76 | pattern: assistant.user.js 77 | shared: true 78 | required: true 79 | requirements: 80 | - adg-docker: 'true' 81 | 82 | triggers: [] 83 | 84 | branches: 85 | create: manually 86 | delete: never 87 | link-to-jira: true 88 | 89 | notifications: 90 | - events: 91 | - plan-status-changed 92 | recipients: 93 | - webhook: 94 | name: Build webhook 95 | url: http://prod.jirahub.service.eu.consul/v1/webhook/bamboo 96 | labels: [] 97 | other: 98 | concurrent-build-plugin: system-default 99 | -------------------------------------------------------------------------------- /bamboo-specs/build-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | plan: 4 | project-key: ADGEXT 5 | key: LEGASSRELEASESPECS 6 | name: legacy assistant - build release 7 | variables: 8 | dockerNode: adguard/node-ssh:22.17--0 9 | 10 | stages: 11 | - Build: 12 | manual: false 13 | final: false 14 | jobs: 15 | - Build 16 | 17 | Build: 18 | key: BUILD 19 | other: 20 | clean-working-dir: true 21 | docker: 22 | image: ${bamboo.dockerNode} 23 | volumes: 24 | ${system.PNPM_DIR}: "${bamboo.cachePnpm}" 25 | tasks: 26 | - checkout: 27 | force-clean-build: true 28 | - checkout: 29 | repository: extensions-private 30 | path: private 31 | force-clean-build: true 32 | - script: 33 | interpreter: SHELL 34 | scripts: 35 | - |- 36 | set -x 37 | set -e 38 | ls -alt 39 | 40 | pnpm install ${bamboo.varsPnpm} 41 | 42 | pnpm lint 43 | 44 | pnpm locales:validate-required 45 | 46 | pnpm release 47 | - inject-variables: 48 | file: build/release/build.txt 49 | scope: RESULT 50 | namespace: userscriptMeta 51 | - any-task: 52 | plugin-key: com.atlassian.bamboo.plugins.vcs:task.vcs.commit 53 | configuration: 54 | # do not change commitMessage as it does not trigger increment after deploy 55 | commitMessage: 'deploy: update dist v${bamboo.userscriptMeta.version}' 56 | selectedRepository: defaultRepository 57 | - any-task: 58 | plugin-key: com.atlassian.bamboo.plugins.vcs:task.vcs.tagging 59 | configuration: 60 | selectedRepository: defaultRepository 61 | tagName: v${bamboo.userscriptMeta.version} 62 | final-tasks: 63 | - script: 64 | interpreter: SHELL 65 | scripts: 66 | - |- 67 | set -x 68 | set -e 69 | 70 | # Fix mixed logs 71 | exec 2>&1 72 | 73 | ls -la 74 | 75 | echo "Size before cleanup:" && du -h | tail -n 1 76 | rm -rf node_modules 77 | echo "Size after cleanup:" && du -h | tail -n 1 78 | artifacts: 79 | - name: assistant.meta.js 80 | location: build/release 81 | pattern: assistant.meta.js 82 | shared: true 83 | required: true 84 | - name: assistant.user.js 85 | location: build/release 86 | pattern: assistant.user.js 87 | shared: true 88 | required: true 89 | requirements: 90 | - adg-docker: 'true' 91 | 92 | triggers: [] 93 | 94 | branches: 95 | create: manually 96 | delete: never 97 | link-to-jira: true 98 | 99 | notifications: 100 | - events: 101 | - plan-status-changed 102 | recipients: 103 | - webhook: 104 | name: Build webhook 105 | url: http://prod.jirahub.service.eu.consul/v1/webhook/bamboo 106 | labels: [] 107 | other: 108 | concurrent-build-plugin: system-default 109 | -------------------------------------------------------------------------------- /bamboo-specs/deploy-beta.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | deployment: 4 | name: legacy assistant - deploy beta 5 | source-plan: ADGEXT-LEGASSBETASPECS 6 | release-naming: ${bamboo.userscriptMeta.version}-beta 7 | 8 | environments: 9 | - userscripts.adtidy.org 10 | 11 | userscripts.adtidy.org: 12 | triggers: [] 13 | tasks: 14 | - clean 15 | - checkout: 16 | repository: bamboo-deploy-publisher 17 | path: bamboo-deploy-publisher 18 | force-clean-build: false 19 | - artifact-download: 20 | artifacts: 21 | - {} 22 | - script: 23 | interpreter: SHELL 24 | scripts: 25 | - |- 26 | set -x 27 | set -e 28 | ls -la 29 | 30 | ./bamboo-deploy-publisher/deploy.sh adguard-assistant-beta 31 | final-tasks: [] 32 | variables: {} 33 | requirements: 34 | - adg-docker: true 35 | notifications: 36 | - events: 37 | - deployment-started-and-finished 38 | recipients: 39 | - webhook: 40 | name: Deploy webhook 41 | url: http://prod.jirahub.service.eu.consul/v1/webhook/bamboo?channel=adguard-qa-extensions-builds 42 | -------------------------------------------------------------------------------- /bamboo-specs/deploy-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | deployment: 4 | name: legacy assistant - deploy release 5 | source-plan: ADGEXT-LEGASSRELEASESPECS 6 | release-naming: ${bamboo.userscriptMeta.version} 7 | 8 | environments: 9 | - userscripts.adtidy.org 10 | - npmjs 11 | 12 | userscripts.adtidy.org: 13 | triggers: [] 14 | tasks: 15 | - clean 16 | - checkout: 17 | repository: bamboo-deploy-publisher 18 | path: bamboo-deploy-publisher 19 | force-clean-build: false 20 | - artifact-download: 21 | artifacts: 22 | - {} 23 | - script: 24 | interpreter: SHELL 25 | scripts: 26 | - |- 27 | set -x 28 | set -e 29 | ls -la 30 | 31 | ./bamboo-deploy-publisher/deploy.sh adguard-assistant-release 32 | final-tasks: [] 33 | variables: {} 34 | requirements: 35 | - adg-docker: true 36 | notifications: 37 | - events: 38 | - deployment-started-and-finished 39 | recipients: 40 | - webhook: 41 | name: Deploy webhook 42 | url: http://prod.jirahub.service.eu.consul/v1/webhook/bamboo?channel=adguard-qa-extensions-builds 43 | 44 | npmjs: 45 | docker: 46 | image: adguard/node-ssh:22.17--0 47 | volumes: 48 | ${system.PNPM_DIR}: "${bamboo.cachePnpm}" 49 | triggers: [] 50 | tasks: 51 | - checkout: 52 | force-clean-build: true 53 | - script: 54 | interpreter: SHELL 55 | scripts: 56 | - |- 57 | set -e 58 | set -x 59 | 60 | pnpm install ${bamboo.varsPnpm} 61 | 62 | ls -la 63 | 64 | pnpm release 65 | 66 | export NPM_TOKEN=${bamboo.npmSecretToken} 67 | echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc 68 | npm publish --access public 69 | final-tasks: 70 | - script: 71 | interpreter: SHELL 72 | scripts: 73 | - |- 74 | set -x 75 | set -e 76 | 77 | # Fix mixed logs 78 | exec 2>&1 79 | 80 | ls -la 81 | 82 | echo "Size before cleanup:" && du -h | tail -n 1 83 | rm -rf node_modules 84 | echo "Size after cleanup:" && du -h | tail -n 1 85 | requirements: 86 | - adg-docker: true 87 | notifications: 88 | - events: 89 | - deployment-started-and-finished 90 | recipients: 91 | - webhook: 92 | name: Deploy webhook 93 | url: http://prod.jirahub.service.eu.consul/v1/webhook/bamboo?channel=adguard-qa-extensions-builds 94 | -------------------------------------------------------------------------------- /bamboo-specs/increment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | plan: 4 | project-key: ADGEXT 5 | key: LEGASSINCRSPECS 6 | name: legacy assistant - increment version 7 | variables: 8 | dockerNode: adguard/node-ssh:22.17--0 9 | 10 | stages: 11 | - Increment: 12 | manual: false 13 | final: false 14 | jobs: 15 | - Increment 16 | Increment: 17 | key: INCR 18 | other: 19 | clean-working-dir: true 20 | docker: 21 | image: ${bamboo.dockerNode} 22 | volumes: 23 | ${system.PNPM_DIR}: "${bamboo.cachePnpm}" 24 | tasks: 25 | - checkout: 26 | force-clean-build: true 27 | - script: 28 | interpreter: SHELL 29 | scripts: 30 | - |- 31 | #!/bin/bash 32 | set -e -f -u -x 33 | 34 | # Fix mixed logs 35 | exec 2>&1 36 | 37 | # Explicitly checkout the revision that we need. 38 | git checkout "${bamboo.repository.revision.number}" 39 | 40 | # do not increment for dist deploy after release build 41 | if ! [[ `git log -1 --pretty=%s` =~ "deploy: update dist" ]];then 42 | git checkout master 43 | ls -alt 44 | pnpm increment 45 | fi 46 | - any-task: 47 | plugin-key: com.atlassian.bamboo.plugins.vcs:task.vcs.commit 48 | configuration: 49 | commitMessage: 'skipci: Automatic increment build number' 50 | selectedRepository: defaultRepository 51 | requirements: 52 | - adg-docker: 'true' 53 | 54 | branches: 55 | create: manually 56 | delete: never 57 | link-to-jira: true 58 | notifications: [] 59 | labels: [] 60 | other: 61 | concurrent-build-plugin: system-default 62 | -------------------------------------------------------------------------------- /bamboo-specs/permissions-beta.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | deployment: 4 | name: legacy assistant - deploy beta 5 | deployment-permissions: 6 | - groups: 7 | - extensions-developers 8 | - adguard-qa 9 | permissions: 10 | - view 11 | environment-permissions: 12 | - userscripts.adtidy.org: 13 | - groups: 14 | - extensions-developers 15 | permissions: 16 | - view 17 | - deploy 18 | -------------------------------------------------------------------------------- /bamboo-specs/permissions-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | deployment: 4 | name: legacy assistant - deploy release 5 | deployment-permissions: 6 | - groups: 7 | - extensions-developers 8 | - adguard-qa 9 | permissions: 10 | - view 11 | environment-permissions: 12 | - userscripts.adtidy.org: 13 | - groups: 14 | - extensions-developers 15 | permissions: 16 | - view 17 | - deploy 18 | - npmjs: 19 | - groups: 20 | - extensions-developers 21 | permissions: 22 | - view 23 | - deploy 24 | -------------------------------------------------------------------------------- /bamboo-specs/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | plan: 4 | project-key: ADGEXT 5 | key: LEGASSTESTSPECS 6 | name: legacy assistant - test 7 | variables: 8 | dockerPuppeteer: adguard/puppeteer-runner:22.14--24.5--0 9 | 10 | stages: 11 | - Test: 12 | manual: false 13 | final: false 14 | jobs: 15 | - Test 16 | 17 | Test: 18 | key: TEST 19 | other: 20 | clean-working-dir: true 21 | docker: 22 | image: ${bamboo.dockerPuppeteer} 23 | volumes: 24 | ${system.PNPM_DIR}: "${bamboo.cachePnpm}" 25 | tasks: 26 | - checkout: 27 | force-clean-build: true 28 | - script: 29 | interpreter: SHELL 30 | scripts: 31 | - |- 32 | set -x 33 | set -e 34 | 35 | # Fix mixed logs 36 | exec 2>&1 37 | 38 | ls -laht 39 | 40 | # Exclude '--ignore-scripts' from pnpm arguments 41 | # since it prevents postinstall scripts from running 42 | # so Chrome is not installed which is crucial for tests 43 | originalPnpmArgs="$bamboo_varsPnpm" 44 | modifiedPnpmArgs=$(echo "$originalPnpmArgs" | sed 's/--ignore-scripts//g') 45 | 46 | # Install dependencies 47 | pnpm install ${modifiedPnpmArgs} 48 | 49 | pnpm lint 50 | 51 | pnpm dev 52 | 53 | pnpm test 54 | final-tasks: 55 | - script: 56 | interpreter: SHELL 57 | scripts: 58 | - |- 59 | set -x 60 | set -e 61 | 62 | # Fix mixed logs 63 | exec 2>&1 64 | 65 | ls -la 66 | 67 | echo "Size before cleanup:" && du -h | tail -n 1 68 | rm -rf node_modules 69 | echo "Size after cleanup:" && du -h | tail -n 1 70 | artifacts: 71 | - name: assistant.meta.js 72 | location: build/dev 73 | pattern: assistant.meta.js 74 | shared: true 75 | required: true 76 | - name: assistant.js 77 | location: build/dev 78 | pattern: assistant.js 79 | shared: true 80 | required: true 81 | - name: assistant.user.js 82 | location: build/dev 83 | pattern: assistant.user.js 84 | shared: true 85 | required: true 86 | requirements: 87 | - adg-docker: 'true' 88 | 89 | branches: 90 | create: for-pull-request 91 | delete: 92 | after-deleted-days: '1' 93 | after-inactive-days: '5' 94 | link-to-jira: true 95 | 96 | notifications: [] 97 | labels: [] 98 | other: 99 | concurrent-build-plugin: system-default 100 | -------------------------------------------------------------------------------- /dist/assistant.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@adguard/assistant" { 2 | interface Callback { 3 | (ruleText: string): void 4 | } 5 | 6 | interface Assistant { 7 | start: (element: HTMLElement | null, callback: Callback) => void, 8 | close: () => void, 9 | } 10 | 11 | export function adguardAssistant(): Assistant; 12 | } 13 | -------------------------------------------------------------------------------- /locales/ar/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings_position_save_all": "كافة مواقع الويب", 3 | "settings_position_save_this": "هذا الموقع", 4 | "assistant_select_element_start": "بدء", 5 | "assistant_select_element_text": "اختر عنصرا في الصفحة للحظرقم بتحديث الصفحة لالغاء وضع حظر العنصر", 6 | "menu_filtration_status": { 7 | "message": "تفعيل الفلترةعلى هذا الموقع" 8 | }, 9 | "menu_do_not_filter_30_sec": { 10 | "message": "عدم الفلترة لمده 30 ثانيه" 11 | }, 12 | "menu_block_ad_on_site": { 13 | "message": "منع الإعلان على هذا الموقع" 14 | }, 15 | "menu_report_abuse": { 16 | "message": "الإبلاغ عن هذا الموقع" 17 | }, 18 | "menu_site_report": { 19 | "message": "تقرير أمن الموقع" 20 | }, 21 | "menu_settings": { 22 | "message": "إعدادات المساعد" 23 | }, 24 | "menu_wot_reputation_indicator": { 25 | "message": "مؤشر سمعة الموقع" 26 | }, 27 | "menu_wot_reputation_confidence_level": { 28 | "message": "مستوى الثقة في السمعة" 29 | }, 30 | "assistant_select_element": { 31 | "message": "حظر العنصر" 32 | }, 33 | "assistant_select_element_cancel": { 34 | "message": "الغاء" 35 | }, 36 | "assistant_block_element": { 37 | "message": "حظر العنصر" 38 | }, 39 | "assistant_extended_settings": { 40 | "message": "إعدادات متقدمة" 41 | }, 42 | "assistant_apply_rule_to_all_sites": { 43 | "message": "تطبيق القاعدة علي كافة مواقع الويب" 44 | }, 45 | "assistant_block_by_reference": { 46 | "message": "حظر حسب الارتباط المرجعي" 47 | }, 48 | "assistant_block_similar": { 49 | "message": "حظر عنصر مما ثل" 50 | }, 51 | "assistant_another_element": { 52 | "message": "حدد عنصرًا مختلفًا" 53 | }, 54 | "assistant_preview": { 55 | "message": "المعاينه" 56 | }, 57 | "assistant_block": { 58 | "message": "حظر" 59 | }, 60 | "assistant_settings": { 61 | "message": "إعدادات المساعد" 62 | }, 63 | "assistant_preview_end": { 64 | "message": "إنهاء المعاينة" 65 | }, 66 | "wot_unknown_description": { 67 | "message": "$1 لم يتم تعريف سمعه هذا الموقع من قبل" 68 | }, 69 | "wot_bad_description": { 70 | "message": "$1هذا الموقع لديه سمعة سيئة للغاية \nوفقاً لـ" 71 | }, 72 | "wot_poor_description": { 73 | "message": "$1 هذا الموقع له سمعة سيئة \nوفقا لـ" 74 | }, 75 | "wot_unsatisfactory_description": { 76 | "message": "$1هذا الموقع له سمعة سيئة\nوفقا لـ" 77 | }, 78 | "wot_good_description": { 79 | "message": "$1يتمتع هذا الموقع بسمعة طيبة\nوفقا لـ" 80 | }, 81 | "wot_excellent_description": { 82 | "message": "هذا الموقع يتمتع بسمعة ممتازة\nوفقاً لـ $1" 83 | }, 84 | "settings_choose_size_and_position": { 85 | "message": "وموضعه AdGuard ضبط حجم مساعد" 86 | }, 87 | "settings_icon_size": { 88 | "message": ":حجم الرمز" 89 | }, 90 | "settings_small": { 91 | "message": "صغير" 92 | }, 93 | "settings_big": { 94 | "message": "كبير" 95 | }, 96 | "settings_position": { 97 | "message": "وضع" 98 | }, 99 | "settings_left_top": { 100 | "message": "اعلي اليسار" 101 | }, 102 | "settings_right_top": { 103 | "message": "اعلي اليمين" 104 | }, 105 | "settings_left_bottom": { 106 | "message": "أسفل لليسار" 107 | }, 108 | "settings_right_bottom": { 109 | "message": "أسفل لليمين" 110 | }, 111 | "settings_cancel": { 112 | "message": "الغاء" 113 | }, 114 | "settings_save": { 115 | "message": "حفظ التغييرات" 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /locales/ar/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "مساعد AdGuard", 5 | "description": "يوفر طريقه سهله ومريحه لأداره التصفية الحقيقة من المتصفح" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/be/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings_position_save_all": "На ўсіх", 3 | "settings_position_save_this": "Толькі на гэтым", 4 | "assistant_select_element_start": "Пачаць", 5 | "assistant_select_element_text": "Абярыце на старонцы элемент для блакавання. Абнавіце старонку, каб скасаваць рэжым блакавання элементаў.", 6 | "menu_filtration_status": { 7 | "message": "Фільтрацыя на гэтым сайце" 8 | }, 9 | "menu_do_not_filter_30_sec": { 10 | "message": "Не фільтраваць 30 секундаў" 11 | }, 12 | "menu_block_ad_on_site": { 13 | "message": "Заблакаваць рэкламу на сайце" 14 | }, 15 | "menu_site_report": { 16 | "message": "Справаздача пра бяспеку сайта" 17 | }, 18 | "menu_wot_reputation_indicator": { 19 | "message": "Індыкатар рэпутацыі сайта" 20 | }, 21 | "menu_wot_reputation_confidence_level": { 22 | "message": "Узровень верагоднасці рэпутацыі" 23 | }, 24 | "assistant_select_element_cancel": { 25 | "message": "Скасаваць" 26 | }, 27 | "assistant_extended_settings": { 28 | "message": "Пашыраныя налады" 29 | }, 30 | "assistant_apply_rule_to_all_sites": { 31 | "message": "Ужыць правіла для ўсіх сайтаў" 32 | }, 33 | "assistant_block_by_reference": { 34 | "message": "Блакаваць па спасылцы" 35 | }, 36 | "assistant_another_element": { 37 | "message": "Абраць іншы элемент" 38 | }, 39 | "assistant_preview": { 40 | "message": "Перадпрагляд" 41 | }, 42 | "assistant_block": { 43 | "message": "Заблакіраваць" 44 | }, 45 | "wot_unknown_description": { 46 | "message": "Рэпутацыя не вызначана" 47 | }, 48 | "wot_bad_description": { 49 | "message": "У сайта вельмі дрэнная рэпутацыя па дадзеных $1" 50 | }, 51 | "wot_poor_description": { 52 | "message": "У сайта дрэнная рэпутацыя па дадзеных $1" 53 | }, 54 | "wot_unsatisfactory_description": { 55 | "message": "У сайта нездавальняльная рэпутацыя па дадзеных $1" 56 | }, 57 | "wot_excellent_description": { 58 | "message": "У сайта выдатная рэпутацыя па дадзеных $1" 59 | }, 60 | "settings_choose_size_and_position": { 61 | "message": "Наладзьце памер і становішча памочніка AdGuard" 62 | }, 63 | "settings_big": { 64 | "message": "Вялікая" 65 | }, 66 | "settings_left_top": { 67 | "message": "Уверсе злева" 68 | }, 69 | "settings_right_top": { 70 | "message": "Уверсе справа" 71 | }, 72 | "settings_left_bottom": { 73 | "message": "Унізе злева" 74 | }, 75 | "settings_right_bottom": { 76 | "message": "Унізе справа" 77 | }, 78 | "settings_cancel": { 79 | "message": "Скасаваць" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /locales/be/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Памочнік AdGuard", 5 | "description": "Дазваляе лёгка і хутка кіраваць фільтрацыяй непасрэдна з браўзара" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/bg/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Асистент на AdGuard", 5 | "description": "Осигурява лесен и удобен начин за управление на филтрирането директно от браузъра" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/ca/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Assistent d'AdGuard", 5 | "description": "Proporciona una manera fàcil i còmoda de gestionar el filtratge directament des del navegador" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/cs/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Asistent", 5 | "description": "Poskytuje snadný a pohodlný způsob správy filtrování přímo z prohlížeče" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/da/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Assistent", 5 | "description": "Muliggør nem og bekvem filtreringshåndtering direkte fra browseren" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/de/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard-Assistent", 5 | "description": "Bietet eine einfache und bequeme Möglichkeit, das Filtern direkt im Browser zu verwalten" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/el/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Βοηθός AdGuard", 5 | "description": "Παρέχει εύκολο και βολικό τρόπο διαχείρισης του φιλτραρίσματος απευθείας από το πρόγραμμα περιήγησης" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/en/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Assistant", 5 | "description": "Provides easy and convenient way to manage filtering right from the browser" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/es/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Asistente de Adguard", 5 | "description": "Permite fácilmente administrar el filtrado directamente desde el navegador" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/fa/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "دستیار AdGuard", 5 | "description": "روش راحت و آسان برای مدیریت فیلترینگ مستیق از داخل مرورگر." 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/fi/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Avustaja", 5 | "description": "Voit muutta hiljattain käyttämiesi sovellusten ja verkkosivujen suodatusasetuksia kätevästi suoraan selaimesta." 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/fr/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Assistant AdGuard ", 5 | "description": "Fournit un moyen simple et pratique de gérer le filtrage directement depuis le navigateur" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/he/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "העוזר האישי של אדגארד", 5 | "description": "מספק דרך קלה ונוחה לנהל סינון תקין ישירות מהדפדפן" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/hi/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "assistant_select_element_cancel": { 3 | "message": "रद्द करें" 4 | }, 5 | "assistant_extended_settings": { 6 | "message": "उन्नत सेटिंग" 7 | }, 8 | "assistant_block": { 9 | "message": "अवरुद्ध" 10 | }, 11 | "settings_icon_size": { 12 | "message": "आइकन का आकार:" 13 | }, 14 | "settings_small": { 15 | "message": "छोटा" 16 | }, 17 | "settings_big": { 18 | "message": "बड़ा" 19 | }, 20 | "settings_cancel": { 21 | "message": "रद्द करें" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /locales/hi/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard सहायक", 5 | "description": "" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/hr/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Pomoćnik", 5 | "description": "Pruža jednostavan i praktičan način upravljanja filtriranjem izravno iz preglednika" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/hu/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Asszisztens", 5 | "description": "Könnyű és kényelmes módot kínál a szűrés kezelésére közvetlenül a böngészőből" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/id/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Asisten AdGuard", 5 | "description": "Menyediakan cara mudah dan nyaman untuk mengelola penyaringan langsung dari peramban" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/index.js: -------------------------------------------------------------------------------- 1 | const { getEquivalent } = require('../scripts/locales/helpers'); 2 | 3 | const { LANGUAGES, LOCALE_DATA_FILENAME } = require('../scripts/locales/constants'); 4 | 5 | const locales = Object.keys(LANGUAGES) 6 | .reduce((acc, language) => { 7 | const resultLocale = getEquivalent(language); 8 | // eslint-disable-next-line global-require,import/no-dynamic-require 9 | const dictionary = require(`./${resultLocale}/${LOCALE_DATA_FILENAME}`); 10 | acc[resultLocale] = dictionary; 11 | return acc; 12 | }, {}); 13 | 14 | export default locales; 15 | -------------------------------------------------------------------------------- /locales/it/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Assistant", 5 | "description": "Fornisce un modo facile e comodo per gestire il filtraggio direttamente dal browser" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/ja/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings_position_save": "ウェブサイト", 3 | "settings_position_save_all": "すべてのウェブサイト", 4 | "settings_position_save_this": "このウェブサイト", 5 | "assistant_select_element_start": "開始", 6 | "assistant_select_element_text": "ブロックするページ上の要素を選択してください。 ページを更新し要素のブロックモードを解除します。", 7 | "menu_filtration_status": { 8 | "message": "このウェブサイトをフィルタリング" 9 | }, 10 | "menu_do_not_filter_30_sec": { 11 | "message": "30秒間フィルタリングしない" 12 | }, 13 | "menu_block_ad_on_site": { 14 | "message": "このサイトで広告を手動ブロックする" 15 | }, 16 | "menu_report_abuse": { 17 | "message": "このサイトの問題を報告する" 18 | }, 19 | "menu_site_report": { 20 | "message": "ウェブサイトのセキュリティレポート" 21 | }, 22 | "menu_settings": { 23 | "message": "アシスタントの設定" 24 | }, 25 | "menu_wot_reputation_indicator": { 26 | "message": "ウェブサイトの評価基準" 27 | }, 28 | "menu_wot_reputation_confidence_level": { 29 | "message": "評判の信頼度(Reputation Confidence Level)" 30 | }, 31 | "assistant_select_element": { 32 | "message": "手動で要素をブロック" 33 | }, 34 | "assistant_select_element_ext": { 35 | "message": "ブロックしたい要素をクリックしてください。AdGuardがその要素に対してユーザールールを作成します。" 36 | }, 37 | "assistant_select_element_cancel": { 38 | "message": "キャンセル" 39 | }, 40 | "assistant_block_element": { 41 | "message": "手動で要素をブロック" 42 | }, 43 | "assistant_block_element_explain": { 44 | "message": "要素のブロックルールを作成します。" 45 | }, 46 | "assistant_slider_explain": { 47 | "message": "スライダーを移動してフレームのサイズを調整してください:" 48 | }, 49 | "assistant_extended_settings": { 50 | "message": "高度な設定" 51 | }, 52 | "assistant_apply_rule_to_all_sites": { 53 | "message": "全てのウェブサイトにこのルールを適用" 54 | }, 55 | "assistant_block_by_reference": { 56 | "message": "参照リンクによるブロック" 57 | }, 58 | "assistant_block_similar": { 59 | "message": "類似項目をブロック" 60 | }, 61 | "assistant_another_element": { 62 | "message": "他の要素を選択" 63 | }, 64 | "assistant_preview": { 65 | "message": "プレビュー" 66 | }, 67 | "assistant_block": { 68 | "message": "ブロック" 69 | }, 70 | "assistant_settings": { 71 | "message": "アシスタントの設定" 72 | }, 73 | "assistant_preview_header": { 74 | "message": "要素ブロック後の画面プレビューを確認する" 75 | }, 76 | "assistant_preview_header_info": { 77 | "message": "画面プレビューで要素が思い通りにブロックされているかを確認してください。" 78 | }, 79 | "assistant_preview_end": { 80 | "message": "プレビューを終了" 81 | }, 82 | "wot_unknown_description": { 83 | "message": "評判が定義されていません" 84 | }, 85 | "wot_bad_description": { 86 | "message": "このウェブサイトは非常に評判が悪いです: " 87 | }, 88 | "wot_poor_description": { 89 | "message": "このウェブサイトは評判が悪いです: " 90 | }, 91 | "wot_unsatisfactory_description": { 92 | "message": "このウェブサイトは評判がやや悪いです: " 93 | }, 94 | "wot_good_description": { 95 | "message": "このウェブサイトは評判が良いです: " 96 | }, 97 | "wot_excellent_description": { 98 | "message": "このウェブサイトはとても評判が良いです: " 99 | }, 100 | "settings_choose_size_and_position": { 101 | "message": "AdGuardアシスタントのサイズと位置を調整する" 102 | }, 103 | "settings_icon_size": { 104 | "message": "アイコンの大きさ:" 105 | }, 106 | "settings_small": { 107 | "message": "小" 108 | }, 109 | "settings_big": { 110 | "message": "大" 111 | }, 112 | "settings_position": { 113 | "message": "位置:" 114 | }, 115 | "settings_left_top": { 116 | "message": "左上" 117 | }, 118 | "settings_right_top": { 119 | "message": "右上" 120 | }, 121 | "settings_left_bottom": { 122 | "message": "左下" 123 | }, 124 | "settings_right_bottom": { 125 | "message": "右下" 126 | }, 127 | "settings_cancel": { 128 | "message": "キャンセル" 129 | }, 130 | "settings_save": { 131 | "message": "設定を保存" 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /locales/ja/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuardアシスタント", 5 | "description": "ブラウザから簡単にフィルタリングを管理する便利な機能を提供します。" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/ko/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings_position_save": "웹사이트", 3 | "settings_position_save_all": "모든 웹사이트", 4 | "settings_position_save_this": "이 웹사이트", 5 | "assistant_select_element_start": "시작", 6 | "assistant_select_element_text": "페이지에서 차단할 요소를 선택하세요. 요소 차단 모드를 취소하려면 페이지를 새로 고침하세요.", 7 | "menu_filtration_status": { 8 | "message": "이 웹사이트에서의 보호" 9 | }, 10 | "menu_do_not_filter_30_sec": { 11 | "message": "30초 동안 차단 일시정지" 12 | }, 13 | "menu_block_ad_on_site": { 14 | "message": "이 웹사이트에서 광고 차단" 15 | }, 16 | "menu_report_abuse": { 17 | "message": "이 웹사이트에 관한 불만 사항 제출" 18 | }, 19 | "menu_site_report": { 20 | "message": "웹사이트 보안 보고" 21 | }, 22 | "menu_settings": { 23 | "message": "어시스턴트 설정" 24 | }, 25 | "menu_wot_reputation_indicator": { 26 | "message": "웹사이트 평판 표시기" 27 | }, 28 | "menu_wot_reputation_confidence_level": { 29 | "message": "평판 신뢰 수준" 30 | }, 31 | "assistant_select_element": { 32 | "message": "요소 차단" 33 | }, 34 | "assistant_select_element_ext": { 35 | "message": "차단하려는 요소를 클릭합니다. AdGuard가 해당 요소에 대한 사용자 규칙을 생성합니다." 36 | }, 37 | "assistant_select_element_cancel": { 38 | "message": "선택 모드 취소" 39 | }, 40 | "assistant_block_element": { 41 | "message": "요소 차단" 42 | }, 43 | "assistant_block_element_explain": { 44 | "message": "요소 차단 규칙을 생성하세요." 45 | }, 46 | "assistant_slider_explain": { 47 | "message": "슬라이더를 움직여 프레임 크기를 조정합니다." 48 | }, 49 | "assistant_extended_settings": { 50 | "message": "고급 설정" 51 | }, 52 | "assistant_apply_rule_to_all_sites": { 53 | "message": "모든 웹사이트에 이 규칙 적용" 54 | }, 55 | "assistant_block_by_reference": { 56 | "message": "참조 링크가 차단" 57 | }, 58 | "assistant_block_similar": { 59 | "message": "유사한 요소 차단" 60 | }, 61 | "assistant_another_element": { 62 | "message": "다른 요소 선택" 63 | }, 64 | "assistant_preview": { 65 | "message": "미리 보기" 66 | }, 67 | "assistant_block": { 68 | "message": "차단" 69 | }, 70 | "assistant_settings": { 71 | "message": "어시스턴트 설정" 72 | }, 73 | "assistant_preview_header": { 74 | "message": "차단된 요소 미리보기" 75 | }, 76 | "assistant_preview_header_info": { 77 | "message": "요소가 올바르게 차단되었는지 확인하세요." 78 | }, 79 | "assistant_preview_end": { 80 | "message": "미리 보기 종료" 81 | }, 82 | "wot_unknown_description": { 83 | "message": "평판이 등록되지 않았습니다." 84 | }, 85 | "wot_bad_description": { 86 | "message": "이 웹사이트는 아주 나쁜 평판을 갖고 있습니다.\n제공 " 87 | }, 88 | "wot_poor_description": { 89 | "message": "이 웹사이트는 나쁜 평판을 갖고 있습니다.\n제공 " 90 | }, 91 | "wot_unsatisfactory_description": { 92 | "message": "이 웹사이트는 좋지 않은 평판을 갖고 있습니다.\n제공 " 93 | }, 94 | "wot_good_description": { 95 | "message": "이 웹사이트는 좋은 평판을 갖고 있습니다.\n제공 " 96 | }, 97 | "wot_excellent_description": { 98 | "message": "이 웹사이트는 아주 좋은 평판을 갖고 있습니다.\n제공 " 99 | }, 100 | "settings_choose_size_and_position": { 101 | "message": "AdGuard 어시스턴트 크기와 위치를 조정합니다" 102 | }, 103 | "settings_icon_size": { 104 | "message": "아이콘 크기:" 105 | }, 106 | "settings_small": { 107 | "message": "작게" 108 | }, 109 | "settings_big": { 110 | "message": "크게" 111 | }, 112 | "settings_position": { 113 | "message": "위치:" 114 | }, 115 | "settings_left_top": { 116 | "message": "왼쪽 위" 117 | }, 118 | "settings_right_top": { 119 | "message": "오른쪽 위" 120 | }, 121 | "settings_left_bottom": { 122 | "message": "왼쪽 아래" 123 | }, 124 | "settings_right_bottom": { 125 | "message": "오른쪽 아래" 126 | }, 127 | "settings_cancel": { 128 | "message": "취소" 129 | }, 130 | "settings_save": { 131 | "message": "저장" 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /locales/ko/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard 어시스턴트", 5 | "description": "브라우저에서 바로 필터링 관리를 쉽고 간편하게 할 수 있도록 도와줍니다." 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/lt/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Asistentas", 5 | "description": "Suteikia lengvą ir patogų būdą valdyti filtravimą iš naršyklės" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/mk/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Помошник на AdGuard", 5 | "description": "Обезбедува лесен и удобен начин за управување со филтрирањето директно од прелистувачот" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/nl/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Assistent", 5 | "description": "Biedt een gemakkelijke en handige manier om filters rechtstreeks vanuit de browser te beheren" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/no/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard-assistent", 5 | "description": "Tilbyr en enkel og praktisk måte å administrere filtrering rett fra nettleseren" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/pl/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Asystent AdGuarda", 5 | "description": "Zapewnia łatwy i wygodny sposób na zarządzanie filtrowaniem bezpośrednio z przeglądarki" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/pt-PT/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Assistente do AdGuard", 5 | "description": "Fornece uma forma fácil e conveniente de gerir a filtragem a partir do seu navegador" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/pt/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Assistente do AdGuard", 5 | "description": "Fornece uma maneira fácil e conveniente de gerenciar a filtragem diretamente do seu navegador" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/ro/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Asistentul AdGuard", 5 | "description": "Oferă o manieră ușoară și convenabilă de a gera filtrarea chiar din browser" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/ru/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Помощник AdGuard", 5 | "description": "Позволяет легко и быстро управлять фильтрацией прямо из браузера" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/sk/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Asistent", 5 | "description": "Poskytuje jednoduchý a pohodlný spôsob manažmentu filtrov priamo z prehliadača" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/sl/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Pomočnik", 5 | "description": "Omogoča preprost in priročen način za upravljanje filtriranja neposredno iz brskalnika" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/sr/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard pomoćnik", 5 | "description": "Pruža jednostavan i zgodan način upravljanja filtriranjem direktno iz preglednika" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/sv/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard-assistent", 5 | "description": "Erbjuder ett enkelt och bekvämt sätt att hantera filtrering direkt i webbläsaren" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/th/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Assistant", 5 | "description": "มอบวิธีที่ง่ายและสะดวกในการจัดการการกรองจากเบราว์เซอร์" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/tr/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard Asistanı", 5 | "description": "Filtrelemeyi doğrudan tarayıcıdan yönetmenin kolay ve kullanışlı bir yolunu sağlar" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/uk/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings_position_save": "Сайти", 3 | "settings_position_save_all": "На всіх", 4 | "settings_position_save_this": "Тільки на даному", 5 | "assistant_select_element_start": "Почати", 6 | "assistant_select_element_text": "Виберіть елемент на сторінці, який треба заблокувати. Оновіть сторінку, щоб скасувати режим блокування.", 7 | "menu_filtration_status": { 8 | "message": "Фільтрування на цьому сайті" 9 | }, 10 | "menu_do_not_filter_30_sec": { 11 | "message": "Не фільтрувати 30 секунд" 12 | }, 13 | "menu_block_ad_on_site": { 14 | "message": "Заблокувати рекламу на даному сайті" 15 | }, 16 | "menu_report_abuse": { 17 | "message": "Повідомити про сайт" 18 | }, 19 | "menu_site_report": { 20 | "message": "Звіт про безпеку сайту" 21 | }, 22 | "menu_settings": { 23 | "message": "Налаштування помічника" 24 | }, 25 | "menu_wot_reputation_indicator": { 26 | "message": "Індикатор репутації сайту" 27 | }, 28 | "menu_wot_reputation_confidence_level": { 29 | "message": "Рівень надійності репутації" 30 | }, 31 | "assistant_select_element": { 32 | "message": "Заблокувати елемент" 33 | }, 34 | "assistant_select_element_cancel": { 35 | "message": "Скасувати" 36 | }, 37 | "assistant_block_element": { 38 | "message": "Заблокувати елемент" 39 | }, 40 | "assistant_extended_settings": { 41 | "message": "Розширені налаштування" 42 | }, 43 | "assistant_apply_rule_to_all_sites": { 44 | "message": "Застосувати правило для всіх сайтів" 45 | }, 46 | "assistant_block_by_reference": { 47 | "message": "Блокувати за посиланням" 48 | }, 49 | "assistant_block_similar": { 50 | "message": "Блокувати схожі елементи" 51 | }, 52 | "assistant_another_element": { 53 | "message": "Вибрати інший елемент" 54 | }, 55 | "assistant_preview": { 56 | "message": "Попередній перегляд" 57 | }, 58 | "assistant_block": { 59 | "message": "Заблокувати" 60 | }, 61 | "assistant_settings": { 62 | "message": "Налаштування помічника" 63 | }, 64 | "assistant_preview_end": { 65 | "message": "Закінчити попередній перегляд" 66 | }, 67 | "wot_unknown_description": { 68 | "message": "Репутація цього вебсайту не визначена" 69 | }, 70 | "wot_bad_description": { 71 | "message": "Цей сайт має дуже погану репутацію\nза версією $1" 72 | }, 73 | "wot_poor_description": { 74 | "message": "Цей сайт має погану репутацію\nза версією $1" 75 | }, 76 | "wot_unsatisfactory_description": { 77 | "message": "Цей сайт має незадовільну репутацію\nза версією $1" 78 | }, 79 | "wot_good_description": { 80 | "message": "Цей сайт має добру репутацію\nза версією $1" 81 | }, 82 | "wot_excellent_description": { 83 | "message": "Цей сайт має дуже відмінну репутацію\nза версією $1" 84 | }, 85 | "settings_choose_size_and_position": { 86 | "message": "Налаштуйте розмір і положення помічника AdGuard" 87 | }, 88 | "settings_icon_size": { 89 | "message": "Розмір іконки:" 90 | }, 91 | "settings_small": { 92 | "message": "Маленька" 93 | }, 94 | "settings_big": { 95 | "message": "Велика" 96 | }, 97 | "settings_position": { 98 | "message": "Позиція:" 99 | }, 100 | "settings_left_top": { 101 | "message": "Зверху зліва" 102 | }, 103 | "settings_right_top": { 104 | "message": "Зверху справа" 105 | }, 106 | "settings_left_bottom": { 107 | "message": "Внизу зліва" 108 | }, 109 | "settings_right_bottom": { 110 | "message": "Внизу справа" 111 | }, 112 | "settings_cancel": { 113 | "message": "Скасувати" 114 | }, 115 | "settings_save": { 116 | "message": "Зберегти зміни" 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /locales/uk/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Помічник AdGuard", 5 | "description": "Забезпечує простий та зручний спосіб керування фільтруванням безпосередньо у браузері" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/vi/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "Trợ Lý AdGuard", 5 | "description": "Cung cấp cách dễ dàng và thuận tiện để quản lý lọc ngay từ trình duyệt" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/zh-HK/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings_position_save": "網站", 3 | "settings_position_save_all": "所有網站", 4 | "settings_position_save_this": "此網站", 5 | "assistant_select_element_start": "開始", 6 | "assistant_select_element_text": "選擇網頁上的元素來阻擋。若要取消重新整理網頁即可。", 7 | "menu_filtration_status": { 8 | "message": "過濾此網頁上的廣告" 9 | }, 10 | "menu_do_not_filter_30_sec": { 11 | "message": "暫停過濾 30 秒" 12 | }, 13 | "menu_block_ad_on_site": { 14 | "message": "封鎖此網頁上的廣告" 15 | }, 16 | "menu_report_abuse": { 17 | "message": "回報此網站" 18 | }, 19 | "menu_site_report": { 20 | "message": "網站安全性報告" 21 | }, 22 | "menu_settings": { 23 | "message": "助手設定" 24 | }, 25 | "menu_wot_reputation_indicator": { 26 | "message": "網頁名譽指標" 27 | }, 28 | "menu_wot_reputation_confidence_level": { 29 | "message": "名譽可信程度" 30 | }, 31 | "assistant_select_element_cancel": { 32 | "message": "取消" 33 | }, 34 | "assistant_extended_settings": { 35 | "message": "進階設定" 36 | }, 37 | "assistant_apply_rule_to_all_sites": { 38 | "message": "套用至所有網站" 39 | }, 40 | "assistant_block_by_reference": { 41 | "message": "通過參考連結封鎖" 42 | }, 43 | "assistant_block_similar": { 44 | "message": "封鎖相關或類似的" 45 | }, 46 | "assistant_another_element": { 47 | "message": "選取其他網頁元素" 48 | }, 49 | "assistant_preview": { 50 | "message": "預覽" 51 | }, 52 | "assistant_block": { 53 | "message": "封鎖" 54 | }, 55 | "assistant_settings": { 56 | "message": "助手設定" 57 | }, 58 | "assistant_preview_end": { 59 | "message": "關閉預覽" 60 | }, 61 | "wot_unknown_description": { 62 | "message": "此網站名譽尚未被 $1 評定" 63 | }, 64 | "wot_bad_description": { 65 | "message": "根據 $1\n此網站名譽非常糟糕" 66 | }, 67 | "wot_poor_description": { 68 | "message": "根據 $1\n此網站擁有不良的名譽" 69 | }, 70 | "wot_unsatisfactory_description": { 71 | "message": "根據 $1\n此網站擁有不好的名譽" 72 | }, 73 | "wot_good_description": { 74 | "message": "根據 $1\n此網站擁有良好的名譽" 75 | }, 76 | "wot_excellent_description": { 77 | "message": "根據 $1\n此網站擁有非常優秀的名譽" 78 | }, 79 | "settings_choose_size_and_position": { 80 | "message": "調整 AdGuard 小助手大小與位置" 81 | }, 82 | "settings_icon_size": { 83 | "message": "圖示大小:" 84 | }, 85 | "settings_small": { 86 | "message": "小" 87 | }, 88 | "settings_big": { 89 | "message": "大" 90 | }, 91 | "settings_position": { 92 | "message": "位置:" 93 | }, 94 | "settings_left_top": { 95 | "message": "左上角" 96 | }, 97 | "settings_right_top": { 98 | "message": "右上角" 99 | }, 100 | "settings_left_bottom": { 101 | "message": "左下角" 102 | }, 103 | "settings_right_bottom": { 104 | "message": "右下角" 105 | }, 106 | "settings_cancel": { 107 | "message": "取消" 108 | }, 109 | "settings_save": { 110 | "message": "儲存變更" 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /locales/zh-HK/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard 小助手", 5 | "description": "在瀏覽器中提供了簡易且直覺的管理過濾方式" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/zh-TW/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings_position_save": "網站", 3 | "settings_position_save_all": "所有的網站", 4 | "settings_position_save_this": "此網站", 5 | "assistant_select_element_start": "開始", 6 | "assistant_select_element_text": "選擇於該頁面上之元件以封鎖。重新整理該頁面以取消元件封鎖模式。", 7 | "menu_filtration_status": { 8 | "message": "對此網站之過濾" 9 | }, 10 | "menu_do_not_filter_30_sec": { 11 | "message": "計 30 秒不過濾" 12 | }, 13 | "menu_block_ad_on_site": { 14 | "message": "封鎖於此網站上之廣告" 15 | }, 16 | "menu_report_abuse": { 17 | "message": "報告該網站" 18 | }, 19 | "menu_site_report": { 20 | "message": "網站安全性報告" 21 | }, 22 | "menu_settings": { 23 | "message": "助理設定" 24 | }, 25 | "menu_wot_reputation_indicator": { 26 | "message": "網站信譽指標" 27 | }, 28 | "menu_wot_reputation_confidence_level": { 29 | "message": "信譽信賴等級" 30 | }, 31 | "assistant_select_element": { 32 | "message": "封鎖元件" 33 | }, 34 | "assistant_select_element_ext": { 35 | "message": "點選想要封鎖的元件。AdGuard 將創建一條使用者規則。" 36 | }, 37 | "assistant_select_element_cancel": { 38 | "message": "取消" 39 | }, 40 | "assistant_block_element": { 41 | "message": "封鎖元件" 42 | }, 43 | "assistant_block_element_explain": { 44 | "message": "建立元件封鎖規則" 45 | }, 46 | "assistant_slider_explain": { 47 | "message": "移動滑塊以調整框架大小" 48 | }, 49 | "assistant_extended_settings": { 50 | "message": "進階設定" 51 | }, 52 | "assistant_apply_rule_to_all_sites": { 53 | "message": "對所有的網站套用該規則" 54 | }, 55 | "assistant_block_by_reference": { 56 | "message": "按照參考連結封鎖" 57 | }, 58 | "assistant_block_similar": { 59 | "message": "封鎖相似之物" 60 | }, 61 | "assistant_another_element": { 62 | "message": "選擇不同的元件" 63 | }, 64 | "assistant_preview": { 65 | "message": "預覽" 66 | }, 67 | "assistant_block": { 68 | "message": "封鎖" 69 | }, 70 | "assistant_settings": { 71 | "message": "助理設定" 72 | }, 73 | "assistant_preview_header": { 74 | "message": "預覽已封鎖元件" 75 | }, 76 | "assistant_preview_header_info": { 77 | "message": "檢查該元件是否被正確封鎖" 78 | }, 79 | "assistant_preview_end": { 80 | "message": "離開預覽" 81 | }, 82 | "wot_unknown_description": { 83 | "message": "此網站的信譽未被 $1 界定" 84 | }, 85 | "wot_bad_description": { 86 | "message": "根據 $1,\n此網站有非常壞的信譽" 87 | }, 88 | "wot_poor_description": { 89 | "message": "根據 $1,\n此網站有壞的信譽" 90 | }, 91 | "wot_unsatisfactory_description": { 92 | "message": "根據 $1,\n此網站有不好的信譽" 93 | }, 94 | "wot_good_description": { 95 | "message": "根據 $1,\n此網站有好的信譽" 96 | }, 97 | "wot_excellent_description": { 98 | "message": "根據 $1,\n此網站有極好的信譽" 99 | }, 100 | "settings_choose_size_and_position": { 101 | "message": "調整 AdGuard 助理尺寸和位置" 102 | }, 103 | "settings_icon_size": { 104 | "message": "圖示尺寸:" 105 | }, 106 | "settings_small": { 107 | "message": "小的" 108 | }, 109 | "settings_big": { 110 | "message": "大的" 111 | }, 112 | "settings_position": { 113 | "message": "位置:" 114 | }, 115 | "settings_left_top": { 116 | "message": "左上角" 117 | }, 118 | "settings_right_top": { 119 | "message": "右上角" 120 | }, 121 | "settings_left_bottom": { 122 | "message": "左下角" 123 | }, 124 | "settings_right_bottom": { 125 | "message": "右下角" 126 | }, 127 | "settings_cancel": { 128 | "message": "取消" 129 | }, 130 | "settings_save": { 131 | "message": "儲存更改" 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /locales/zh-TW/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "AdGuard 助理", 5 | "description": "提供簡單的且方便的方法以直接地從該瀏覽器管理過濾" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /locales/zh/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings_position_save": "网站", 3 | "settings_position_save_all": "所有网站", 4 | "settings_position_save_this": "此网站", 5 | "assistant_select_element_start": "开始", 6 | "assistant_select_element_text": "选择要拦截的网页元素。要取消元素拦截模式,请刷新网页。", 7 | "menu_filtration_status": { 8 | "message": "对此网站进行过滤" 9 | }, 10 | "menu_do_not_filter_30_sec": { 11 | "message": "暂停过滤 30 秒" 12 | }, 13 | "menu_block_ad_on_site": { 14 | "message": "拦截此网站上的广告" 15 | }, 16 | "menu_report_abuse": { 17 | "message": "报告此网站" 18 | }, 19 | "menu_site_report": { 20 | "message": "网站安全报告" 21 | }, 22 | "menu_settings": { 23 | "message": "助手设置" 24 | }, 25 | "menu_wot_reputation_indicator": { 26 | "message": "网站声誉指标" 27 | }, 28 | "menu_wot_reputation_confidence_level": { 29 | "message": "声望置信度" 30 | }, 31 | "assistant_select_element": { 32 | "message": "拦截元素" 33 | }, 34 | "assistant_select_element_ext": { 35 | "message": "点击想要拦截的元素。AdGuard 将创建一条用户规则。" 36 | }, 37 | "assistant_select_element_cancel": { 38 | "message": "取消" 39 | }, 40 | "assistant_block_element": { 41 | "message": "拦截元素" 42 | }, 43 | "assistant_block_element_explain": { 44 | "message": "创建元素拦截规则" 45 | }, 46 | "assistant_slider_explain": { 47 | "message": "移动滑块以调整框架大小" 48 | }, 49 | "assistant_extended_settings": { 50 | "message": "高级设置" 51 | }, 52 | "assistant_apply_rule_to_all_sites": { 53 | "message": "应用规则至所有网站" 54 | }, 55 | "assistant_block_by_reference": { 56 | "message": "通过参考链接进行拦截" 57 | }, 58 | "assistant_block_similar": { 59 | "message": "拦截类似元素" 60 | }, 61 | "assistant_another_element": { 62 | "message": "选择其它元素" 63 | }, 64 | "assistant_preview": { 65 | "message": "预览" 66 | }, 67 | "assistant_block": { 68 | "message": "拦截" 69 | }, 70 | "assistant_settings": { 71 | "message": "AdGuard 助手设置" 72 | }, 73 | "assistant_preview_header": { 74 | "message": "预览已拦截元素" 75 | }, 76 | "assistant_preview_header_info": { 77 | "message": "检查该元素是否被正确拦截" 78 | }, 79 | "assistant_preview_end": { 80 | "message": "退出预览" 81 | }, 82 | "wot_unknown_description": { 83 | "message": "声望尚未定义" 84 | }, 85 | "wot_bad_description": { 86 | "message": "此网站在以下数据库中声望极低" 87 | }, 88 | "wot_poor_description": { 89 | "message": "此网站在以下数据库中声望低下 " 90 | }, 91 | "wot_unsatisfactory_description": { 92 | "message": "此网站在以下数据库中声望不佳 " 93 | }, 94 | "wot_good_description": { 95 | "message": "此网站在以下数据库中声望良好 " 96 | }, 97 | "wot_excellent_description": { 98 | "message": "此网站在以下数据库中声望极佳 " 99 | }, 100 | "settings_choose_size_and_position": { 101 | "message": "调整 AdGuard 助手的大小与位置" 102 | }, 103 | "settings_icon_size": { 104 | "message": "图标大小:" 105 | }, 106 | "settings_small": { 107 | "message": "小" 108 | }, 109 | "settings_big": { 110 | "message": "大" 111 | }, 112 | "settings_position": { 113 | "message": "位置:" 114 | }, 115 | "settings_left_top": { 116 | "message": "左上角" 117 | }, 118 | "settings_right_top": { 119 | "message": "右上角" 120 | }, 121 | "settings_left_bottom": { 122 | "message": "左下角" 123 | }, 124 | "settings_right_bottom": { 125 | "message": "右下角" 126 | }, 127 | "settings_cancel": { 128 | "message": "取消" 129 | }, 130 | "settings_save": { 131 | "message": "保存更改" 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /locales/zh/messages.meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": { 3 | "assistant": { 4 | "name": "使用 AdGuard 助手", 5 | "description": "提供简单方便的方法来管理浏览器筛选" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@adguard/assistant", 3 | "version": "4.3.77", 4 | "description": "AdGuard Assistant helps you manage filtering right from the browser", 5 | "main": "dist/assistant.js", 6 | "types": "dist/assistant.d.ts", 7 | "scripts": { 8 | "dev": "export CHANNEL_ENV=DEV; pnpm build && pnpm build:embedded && pnpm build:self", 9 | "beta": "export CHANNEL_ENV=BETA; pnpm build && pnpm build:embedded && pnpm build:self", 10 | "release": "export CHANNEL_ENV=RELEASE; pnpm build && pnpm build:embedded && pnpm build:self", 11 | "lint": "eslint ./src ./tests", 12 | "build": "webpack --config scripts/build/webpack.user.config.js", 13 | "build:embedded": "webpack --config scripts/build/webpack.umd.config.js", 14 | "build:self": "webpack --config scripts/build/webpack.self.config.js", 15 | "test": "pnpm webpack --config tests/webpack.test.config.js && node tests/puppeteer.js", 16 | "locales:download": "LOCALES=DOWNLOAD node scripts/locales/locales.js", 17 | "locales:upload": "LOCALES=UPLOAD node scripts/locales/locales.js", 18 | "locales:validate-required": "LOCALES=VALIDATE-MIN node scripts/locales/locales.js", 19 | "increment": "pnpm version patch --no-git-tag-version", 20 | "prepublishOnly": "pnpm release" 21 | }, 22 | "author": "AdGuard", 23 | "license": "GPL-3.0", 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/AdguardTeam/AdguardAssistant.git" 27 | }, 28 | "bugs": { 29 | "url": "https://github.com/AdguardTeam/AdguardAssistant/issues" 30 | }, 31 | "homepage": "https://github.com/AdguardTeam/AdguardAssistant#adguard-assistant", 32 | "pnpm": { 33 | "neverBuiltDependencies": [] 34 | }, 35 | "devDependencies": { 36 | "@adguard/translate": "^1.0.2", 37 | "@babel/core": "^7.9.0", 38 | "@babel/preset-env": "^7.9.0", 39 | "axios": "^0.21.1", 40 | "babel-loader": "^8.1.0", 41 | "chalk": "4.1.2", 42 | "clean-webpack-plugin": "^3.0.0", 43 | "copy-webpack-plugin": "^6", 44 | "cp-file": "^9.0.0", 45 | "create-file-webpack": "^1.0.2", 46 | "css-loader": "^3.4.2", 47 | "eslint": "^7.2.0", 48 | "eslint-config-airbnb-base": "14.2.1", 49 | "eslint-plugin-import": "^2.22.1", 50 | "filemanager-webpack-plugin": "^2.0.5", 51 | "fs-extra": "^11.1.0", 52 | "html-loader": "^1.1.0", 53 | "html-webpack-plugin": "^5.5.0", 54 | "less": "^3.11.1", 55 | "less-loader": "^11.1.0", 56 | "node-qunit-puppeteer": "^2.2.1", 57 | "puppeteer": "^24.5.0", 58 | "qunit": "^2.22.0", 59 | "replace-in-file": "^5.0.2", 60 | "to-string-loader": "^1.1.6", 61 | "url-loader": "^4.0.0", 62 | "webpack": "^5.75.0", 63 | "webpack-cli": "^5.0.1", 64 | "webpack-merge": "^5.7.3" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /scripts/build/constants.js: -------------------------------------------------------------------------------- 1 | const CHANNEL_ENVS = { 2 | DEV: 'dev', 3 | BETA: 'beta', 4 | RELEASE: 'release', 5 | }; 6 | 7 | const BUILD_DIR = '../../build'; 8 | const SOURCE_DIR = '../../src'; 9 | const DIST_DIR = '../../dist'; 10 | const FILENAME = 'assistant.js'; 11 | const SELF_FILENAME = 'self.assistant.js'; 12 | const USERSCRIPT_NAME = 'assistant'; 13 | const LOCALES_DIR = '../../locales'; 14 | const TYPES_DIR = '../../types'; 15 | 16 | module.exports = { 17 | CHANNEL_ENVS, 18 | BUILD_DIR, 19 | SOURCE_DIR, 20 | DIST_DIR, 21 | FILENAME, 22 | USERSCRIPT_NAME, 23 | LOCALES_DIR, 24 | TYPES_DIR, 25 | SELF_FILENAME, 26 | }; 27 | -------------------------------------------------------------------------------- /scripts/build/meta.settings.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../../package.json'); 2 | 3 | const releaseChannels = { 4 | common: { 5 | fields: { 6 | USERSCRIPT_VERSION: pkg.version, 7 | USERSCRIPT_NAME: { 8 | messageKey: 'extension.assistant.name', 9 | metaName: 'name', 10 | usePostfix: true, 11 | }, 12 | USERSCRIPT_DESCRIPTION: { 13 | messageKey: 'extension.assistant.description', 14 | metaName: 'description', 15 | }, 16 | }, 17 | }, 18 | dev: { 19 | postfix: '(Dev)', 20 | fields: { 21 | DOWNLOAD_URL: 'https://AdguardTeam.github.io/AdguardAssistant/assistant.user.js', 22 | UPDATE_URL: 'https://AdguardTeam.github.io/AdguardAssistant/assistant.meta.js', 23 | }, 24 | }, 25 | beta: { 26 | postfix: '(Beta)', 27 | fields: { 28 | DOWNLOAD_URL: 'https://userscripts.adtidy.org/beta/assistant/4.3/assistant.user.js', 29 | UPDATE_URL: 'https://userscripts.adtidy.org/beta/assistant/4.3/assistant.meta.js', 30 | }, 31 | }, 32 | release: { 33 | fields: { 34 | DOWNLOAD_URL: 'https://userscripts.adtidy.org/release/assistant/4.3/assistant.user.js', 35 | UPDATE_URL: 'https://userscripts.adtidy.org/release/assistant/4.3/assistant.meta.js', 36 | }, 37 | }, 38 | }; 39 | 40 | module.exports = releaseChannels; 41 | -------------------------------------------------------------------------------- /scripts/build/meta.template.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // [USERSCRIPT_NAME] 3 | // @namespace adguard 4 | // [USERSCRIPT_DESCRIPTION] 5 | // @version [USERSCRIPT_VERSION] 6 | // @downloadURL [DOWNLOAD_URL] 7 | // @updateURL [UPDATE_URL] 8 | // @homepageURL https://github.com/AdguardTeam/AdguardAssistant 9 | // @include * 10 | // @exclude *://mil.ru/* 11 | // @exclude *wikipedia.org* 12 | // @exclude *icloud.com* 13 | // @exclude *hangouts.google.com* 14 | // @exclude *www.facebook.com/plugins/сomments* 15 | // @exclude *www.facebook.com/v*/plugins* 16 | // @exclude *disqus.com/embed/comments* 17 | // @exclude *vk.com/widget* 18 | // @exclude *twitter.com/intent/* 19 | // @exclude *www.youtube.com/embed/* 20 | // @exclude *player.vimeo.com* 21 | // @exclude *coub.com/embed* 22 | // @exclude *staticxx.facebook.com/connect/xd_arbiter/* 23 | // @exclude *vk.com/q_frame* 24 | // @exclude *tpc.googlesyndication.com* 25 | // @exclude *syndication.twitter.com* 26 | // @exclude *platform.twitter.com* 27 | // @exclude *tutosdeath.blogspot.com* 28 | // @exclude *notifications.google.com* 29 | // @exclude *google.com/recaptcha/* 30 | // @exclude *bizmania.ru/* 31 | // @grant GM_getValue 32 | // @grant GM_setValue 33 | // @grant GM_getResourceText 34 | // @grant GM_addStyle 35 | // @grant property:settings 36 | // @run-at document-start 37 | // ==/UserScript== 38 | -------------------------------------------------------------------------------- /scripts/build/webpack.common.config.js: -------------------------------------------------------------------------------- 1 | const { CHANNEL_ENVS } = require('./constants'); 2 | 3 | const CHANNEL = CHANNEL_ENVS[process.env.CHANNEL_ENV] || CHANNEL_ENVS.DEV; 4 | 5 | const config = { 6 | mode: CHANNEL === CHANNEL_ENVS.DEV ? 'development' : 'production', 7 | performance: { hints: false }, 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.svg$/, 12 | exclude: /node_modules/, 13 | loader: 'url-loader', 14 | }, 15 | { 16 | test: /\.html$/, 17 | exclude: /node_modules/, 18 | loader: 'html-loader', 19 | options: { 20 | minimize: true, 21 | }, 22 | }, 23 | { 24 | test: /\.less$/, 25 | exclude: /node_modules/, 26 | use: ['to-string-loader', 'css-loader', 'less-loader'], 27 | }, 28 | { 29 | test: /\.js$/, 30 | exclude: /node_modules/, 31 | loader: 'babel-loader', 32 | }, 33 | ], 34 | }, 35 | }; 36 | 37 | module.exports = config; 38 | -------------------------------------------------------------------------------- /scripts/build/webpack.self.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const FileManagerPlugin = require('filemanager-webpack-plugin'); 3 | 4 | const config = require('./webpack.umd.config'); 5 | const { 6 | BUILD_DIR, 7 | CHANNEL_ENVS, 8 | SELF_FILENAME, 9 | DIST_DIR, 10 | } = require('./constants'); 11 | 12 | const CHANNEL_ENV = CHANNEL_ENVS[process.env.CHANNEL_ENV] || CHANNEL_ENVS.DEV; 13 | 14 | /** 15 | * This config is used to build a script that would expose itself as a variable in the self-context. 16 | * After this script is injected into the page with script tag, it can be used as a regular 17 | * variable "adguardAssistant". 18 | * This module is needed because sometimes there is no way to use umd module on some sites. 19 | * Because they have their own implementation of the define function in the global scope. 20 | * see AG-22653 issue 21 | */ 22 | config.output = { 23 | path: path.resolve(__dirname, BUILD_DIR, CHANNEL_ENV), 24 | filename: SELF_FILENAME, 25 | library: { 26 | type: 'self', 27 | }, 28 | }; 29 | 30 | const fileManagerPlugin = new FileManagerPlugin({ 31 | onEnd: { 32 | copy: [ 33 | { 34 | source: path.resolve(__dirname, BUILD_DIR, CHANNEL_ENVS.RELEASE, SELF_FILENAME), 35 | destination: path.resolve(__dirname, DIST_DIR), 36 | }, 37 | ], 38 | }, 39 | }); 40 | 41 | if (CHANNEL_ENV === CHANNEL_ENVS.RELEASE) { 42 | config.plugins.unshift(fileManagerPlugin); 43 | } 44 | 45 | module.exports = config; 46 | -------------------------------------------------------------------------------- /scripts/build/webpack.umd.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const FileManagerPlugin = require('filemanager-webpack-plugin'); 4 | const { merge } = require('webpack-merge'); 5 | const CopyPlugin = require('copy-webpack-plugin'); 6 | 7 | const commonConfig = require('./webpack.common.config'); 8 | 9 | const pkg = require('../../package.json'); 10 | 11 | const { 12 | DIST_DIR, 13 | FILENAME, 14 | BUILD_DIR, 15 | SOURCE_DIR, 16 | CHANNEL_ENVS, 17 | TYPES_DIR, 18 | } = require('./constants'); 19 | 20 | const CHANNEL_ENV = CHANNEL_ENVS[process.env.CHANNEL_ENV] || CHANNEL_ENVS.DEV; 21 | 22 | const banner = `AdGuard Assistant - v${pkg.version} - ${new Date().toDateString()} 23 | ${pkg.homepage ? `${pkg.homepage}` : ''} 24 | Copyright (c) ${new Date().getFullYear()} ${pkg.author}. Licensed ${pkg.license}`; 25 | 26 | const config = { 27 | entry: path.resolve(__dirname, SOURCE_DIR, 'index.js'), 28 | devtool: CHANNEL_ENV === CHANNEL_ENVS.DEV ? 'eval-source-map' : false, 29 | output: { 30 | path: path.resolve(__dirname, BUILD_DIR, CHANNEL_ENV), 31 | filename: FILENAME, 32 | library: { 33 | type: 'umd', 34 | }, 35 | }, 36 | optimization: { 37 | minimize: false, 38 | }, 39 | plugins: [ 40 | new webpack.BannerPlugin(banner), 41 | new webpack.NormalModuleReplacementPlugin( 42 | /src\/gm\.js/, 43 | 'gm-empty.js', 44 | ), 45 | new webpack.DefinePlugin({ 46 | DEBUG: CHANNEL_ENV === CHANNEL_ENVS.DEV, 47 | }), 48 | new CopyPlugin({ 49 | patterns: [ 50 | { 51 | from: path.resolve(__dirname, TYPES_DIR), 52 | to: path.resolve(__dirname, DIST_DIR), 53 | }, 54 | ], 55 | }), 56 | ], 57 | }; 58 | 59 | const fileManagerPlugin = new FileManagerPlugin({ 60 | onEnd: { 61 | copy: [ 62 | { 63 | source: path.resolve(__dirname, BUILD_DIR, CHANNEL_ENVS.RELEASE, FILENAME), 64 | destination: path.resolve(__dirname, DIST_DIR), 65 | }, 66 | ], 67 | }, 68 | }); 69 | 70 | if (CHANNEL_ENV === CHANNEL_ENVS.RELEASE) { 71 | config.plugins.unshift(fileManagerPlugin); 72 | } 73 | 74 | module.exports = merge(commonConfig, config); 75 | -------------------------------------------------------------------------------- /scripts/build/webpack.user.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 3 | const CreateFileWebpack = require('create-file-webpack'); 4 | const webpack = require('webpack'); 5 | const { merge } = require('webpack-merge'); 6 | 7 | const commonConfig = require('./webpack.common.config'); 8 | 9 | const MetaDataPlugin = require('./metadata.plugin'); 10 | const metaSettings = require('./meta.settings'); 11 | const { 12 | SOURCE_DIR, 13 | BUILD_DIR, 14 | CHANNEL_ENVS, 15 | USERSCRIPT_NAME, 16 | LOCALES_DIR, 17 | METADATA_TEMPLATE, 18 | } = require('./constants'); 19 | const pkg = require('../../package.json'); 20 | 21 | const CHANNEL = CHANNEL_ENVS[process.env.CHANNEL_ENV] || CHANNEL_ENVS.DEV; 22 | const OUTPUT_PATH = path.resolve(__dirname, BUILD_DIR, CHANNEL); 23 | 24 | const config = { 25 | entry: { 26 | [`${USERSCRIPT_NAME}.user`]: path.resolve(__dirname, SOURCE_DIR, 'index.user.js'), 27 | }, 28 | output: { 29 | path: OUTPUT_PATH, 30 | filename: '[name].js', 31 | }, 32 | optimization: { 33 | minimize: CHANNEL === CHANNEL_ENVS.RELEASE || CHANNEL === CHANNEL_ENVS.BETA, 34 | }, 35 | plugins: [ 36 | new CleanWebpackPlugin(), 37 | new CreateFileWebpack({ 38 | path: OUTPUT_PATH, 39 | fileName: 'build.txt', 40 | content: `version=${pkg.version}`, 41 | }), 42 | new webpack.DefinePlugin({ 43 | DEBUG: CHANNEL === CHANNEL_ENVS.DEV, 44 | }), 45 | new MetaDataPlugin({ 46 | filename: USERSCRIPT_NAME, 47 | localesDir: LOCALES_DIR, 48 | metadataTemplate: METADATA_TEMPLATE, 49 | ...metaSettings.common, 50 | ...(metaSettings[CHANNEL] || {}), 51 | fields: { 52 | ...metaSettings.common.fields, 53 | ...((metaSettings[CHANNEL] && metaSettings[CHANNEL].fields) || {}), 54 | }, 55 | }), 56 | ], 57 | }; 58 | 59 | module.exports = merge(commonConfig, config); 60 | -------------------------------------------------------------------------------- /scripts/locales/cli-log.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import chalk from 'chalk'; 3 | 4 | const info = (text) => { 5 | if (text) { 6 | console.log(text); 7 | } else { 8 | console.log('Unknown info'); 9 | } 10 | }; 11 | 12 | const success = (text) => { 13 | if (text) { 14 | console.log(chalk.green.bgBlack(text)); 15 | } else { 16 | info(); 17 | } 18 | }; 19 | 20 | const warning = (text) => { 21 | if (text) { 22 | console.log(chalk.black.bgYellowBright(text)); 23 | } else { 24 | info(); 25 | } 26 | }; 27 | 28 | const warningRed = (text) => { 29 | if (text) { 30 | console.log(chalk.bold.yellow.bgRed(text)); 31 | } else { 32 | info(); 33 | } 34 | }; 35 | 36 | const error = (text) => { 37 | if (text) { 38 | console.log(chalk.bold.yellow.bgRed(text)); 39 | throw new Error(text); 40 | } else { 41 | throw new Error('Unknown error'); 42 | } 43 | }; 44 | 45 | export const cliLog = { 46 | info, 47 | success, 48 | warning, 49 | warningRed, 50 | error, 51 | }; 52 | -------------------------------------------------------------------------------- /scripts/locales/constants.js: -------------------------------------------------------------------------------- 1 | const [{ 2 | // Base locale 3 | base_locale: BASE_LOCALE, 4 | // Twosky project see mapping https://twosky.int.agrd.dev/api/v1/mapping 5 | project_id: PROJECT_ID, 6 | // Available translations list 7 | languages: LANGUAGES, 8 | // Crowdin files for downloading/uploading 9 | localizable_files: LOCALIZABLE_FILES, 10 | }] = require('../../.twosky.json'); 11 | 12 | const LOCALES_DIR = '../../locales'; 13 | 14 | /** 15 | * Users locale may be defined with only two chars (language code) 16 | * Here we provide a map of equivalent translation for such locales 17 | */ 18 | const LOCALES_EQUIVALENTS_MAP = { 19 | 'pt-BR': 'pt', 20 | 'zh-CN': 'zh', 21 | }; 22 | 23 | const LOCALES = Object.keys(LANGUAGES); 24 | 25 | const REQUIRED_LOCALES = [ 26 | 'de', 27 | 'es', 28 | 'fr', 29 | 'it', 30 | 'ja', 31 | 'ko', 32 | 'pt', 33 | 'pt-PT', 34 | 'ru', 35 | 'zh', 36 | 'zh-TW', 37 | ]; 38 | 39 | /** 40 | * Threshold percentage for validation of required locales. 41 | */ 42 | const THRESHOLD_PERCENTAGE = 100; 43 | 44 | /** 45 | * Name of the locale data file. 46 | */ 47 | const LOCALE_DATA_FILENAME = 'messages.json'; 48 | 49 | module.exports = { 50 | LOCALES_EQUIVALENTS_MAP, 51 | BASE_LOCALE, 52 | PROJECT_ID, 53 | LANGUAGES, 54 | LOCALIZABLE_FILES, 55 | LOCALES, 56 | REQUIRED_LOCALES, 57 | THRESHOLD_PERCENTAGE, 58 | LOCALES_DIR, 59 | LOCALE_DATA_FILENAME, 60 | }; 61 | -------------------------------------------------------------------------------- /scripts/locales/helpers.js: -------------------------------------------------------------------------------- 1 | const { LOCALES_EQUIVALENTS_MAP } = require('./constants'); 2 | 3 | /** 4 | * Returns equivalent of specified locale code 5 | * @param {string} locale locale 6 | */ 7 | const getEquivalent = (locale) => LOCALES_EQUIVALENTS_MAP[locale] || locale; 8 | 9 | /** 10 | * Normalizes locale code before validation. 11 | * 12 | * @param {string} rawLocale Locale code to normalize. 13 | * 14 | * @returns {string} Normalized locale code. 15 | * 16 | * @example 17 | * 'pt_BR' -> 'pt_br' 18 | * 'es_419' -> 'es' 19 | * 'sr-Latn' -> 'sr_latn' 20 | */ 21 | const normalizeLocale = (rawLocale) => { 22 | // locale should be lowercase, e.g. 'pt_br', not 'pt_BR' 23 | // and with underscore, not dash, e.g. 'sr_latn', not 'sr-latn' 24 | const locale = rawLocale.toLowerCase().replace('-', '_'); 25 | 26 | return getEquivalent(locale); 27 | }; 28 | 29 | module.exports = { 30 | getEquivalent, 31 | normalizeLocale, 32 | }; 33 | -------------------------------------------------------------------------------- /src/controllers/blockPreviewController.js: -------------------------------------------------------------------------------- 1 | import log from '../log'; 2 | import { toArray } from '../utils/dom-utils'; 3 | import selector from '../adguard-selector'; 4 | 5 | /** 6 | * Block preview controller 7 | * @param addRule 8 | * @param iframe 9 | * @returns {{init: init}} 10 | * @constructor 11 | */ 12 | export default function BlockPreviewController(addRule, iframe) { 13 | let contentDocument = null; 14 | let currentElement = null; 15 | let selectedElement = null; 16 | let selectedPath = null; 17 | let optionsState = null; 18 | const iframeCtrl = iframe; 19 | const previewStyleID = 'ag-preview-style-id'; 20 | 21 | const showElement = () => { 22 | iframeCtrl.showHiddenElements(previewStyleID); 23 | }; 24 | 25 | const close = () => { 26 | showElement(); 27 | iframeCtrl.removeIframe(); 28 | }; 29 | 30 | const selectAnotherElement = () => { 31 | showElement(); 32 | iframeCtrl.showSelectorMenu(); 33 | }; 34 | 35 | const blockElement = (e) => { 36 | e.stopPropagation(); 37 | iframeCtrl.blockElement(selectedPath, addRule); 38 | }; 39 | 40 | const showDetailedMenu = () => { 41 | showElement(); 42 | iframeCtrl.showSliderMenu(currentElement, selectedElement, selectedPath, optionsState); 43 | }; 44 | 45 | const bindEvents = () => { 46 | const menuEvents = { 47 | '.main_close': close, 48 | '#select-another-element': selectAnotherElement, 49 | '#end-preview': showDetailedMenu, 50 | '#block-element': blockElement, 51 | }; 52 | Object.keys(menuEvents).forEach((item) => { 53 | const elems = contentDocument.querySelectorAll(item); 54 | toArray(elems).forEach((elem) => elem.addEventListener('click', menuEvents[item])); 55 | }); 56 | }; 57 | 58 | const hideElement = () => { 59 | if (!selectedPath) { 60 | log.error('Can`t block element: `selector` path is empty'); 61 | return; 62 | } 63 | 64 | iframeCtrl.hideElementsByPath(selectedPath, previewStyleID); 65 | }; 66 | 67 | /* 68 | Called from IframeController.showMenuItem to initialize view 69 | */ 70 | // eslint-disable-next-line no-shadow 71 | const init = (iframe, options) => { 72 | selectedElement = options.element; 73 | selectedPath = options.path; 74 | // eslint-disable-next-line prefer-destructuring 75 | currentElement = options.currentElement; 76 | // eslint-disable-next-line prefer-destructuring 77 | contentDocument = iframe.contentDocument; 78 | optionsState = options.options; 79 | selector.reset(); 80 | bindEvents(); 81 | hideElement(); 82 | }; 83 | 84 | return { 85 | init, 86 | }; 87 | } 88 | -------------------------------------------------------------------------------- /src/controllers/selectorMenuController.js: -------------------------------------------------------------------------------- 1 | import { toArray } from '../utils/dom-utils'; 2 | import selector from '../adguard-selector'; 3 | 4 | /** 5 | * Selector menu controller 6 | * @returns {{init: init}} 7 | * @constructor 8 | */ 9 | export default function SelectorMenuController(iframe) { 10 | let contentDocument = null; 11 | const iframeCtrl = iframe; 12 | 13 | const close = () => { 14 | iframeCtrl.removeIframe(); 15 | }; 16 | 17 | const bindEvents = () => { 18 | const menuEvents = { 19 | '.main_close': close, 20 | '.btn-default': close, 21 | }; 22 | Object.keys(menuEvents).forEach((item) => { 23 | const elems = contentDocument.querySelectorAll(item); 24 | toArray(elems).forEach((elem) => elem.addEventListener('click', menuEvents[item])); 25 | }); 26 | }; 27 | 28 | const onElementSelected = (element) => { 29 | iframeCtrl.showSliderMenu(element); 30 | }; 31 | 32 | const startSelector = () => { 33 | selector.reset(); 34 | selector.init(onElementSelected); 35 | }; 36 | 37 | /* 38 | Called from IframeController._showMenuItem to initialize view 39 | */ 40 | // eslint-disable-next-line no-shadow 41 | const init = (iframe) => { 42 | // eslint-disable-next-line prefer-destructuring 43 | contentDocument = iframe.contentDocument; 44 | bindEvents(); 45 | startSelector(); 46 | }; 47 | 48 | iframeCtrl.onCloseMenu.attach(selector.close); 49 | 50 | return { 51 | init, 52 | startSelector, 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /src/controllers/settingsMenuController.js: -------------------------------------------------------------------------------- 1 | import { toArray } from '../utils/dom-utils'; 2 | import settings from '../settings'; 3 | import button from '../button'; 4 | 5 | /** 6 | * Settings menu controller 7 | * @returns {{init: init}} 8 | * @constructor 9 | */ 10 | export default function SettingsMenuController(iframe) { 11 | let contentDocument = null; 12 | const iframeCtrl = iframe; 13 | const buttonSides = { 14 | 'position-bottom-right': { top: false, left: false }, 15 | 'position-bottom-left': { top: false, left: true }, 16 | 'position-top-right': { top: true, left: false }, 17 | 'position-top-left': { top: true, left: true }, 18 | }; 19 | 20 | const close = () => { 21 | iframeCtrl.removeIframe(); 22 | }; 23 | 24 | const setIconSize = () => { 25 | const smallIcon = contentDocument.getElementById('size-small').checked; 26 | settings.setIconSize(smallIcon); 27 | }; 28 | 29 | const setPersonalParam = () => { 30 | const personalConfig = contentDocument.getElementById('this-site').checked; 31 | settings.setPersonalParam(personalConfig); 32 | }; 33 | 34 | const setButtonSide = () => { 35 | let sideItem = null; 36 | Object.keys(buttonSides).forEach((item) => { 37 | if (contentDocument.getElementById(item).checked) { 38 | sideItem = item; 39 | } 40 | }); 41 | 42 | if (sideItem) { 43 | settings.setButtonSide(buttonSides[sideItem]); 44 | } 45 | }; 46 | 47 | const setDefaultSettings = () => { 48 | if (settings.getIconSize()) { 49 | contentDocument.getElementById('size-small').checked = true; 50 | } else { 51 | contentDocument.getElementById('size-big').checked = true; 52 | } 53 | 54 | if (settings.getPersonalConfig()) { 55 | contentDocument.getElementById('this-site').checked = true; 56 | } else { 57 | contentDocument.getElementById('all-site').checked = true; 58 | } 59 | 60 | const position = settings.getUserPositionForButton(); 61 | if (position) { 62 | return; 63 | } 64 | const sideFromSettings = settings.getButtonSide(); 65 | 66 | Object.keys(buttonSides).forEach((item) => { 67 | const sideItem = buttonSides[item]; 68 | if ((sideItem.left === sideFromSettings.left) 69 | && (sideItem.top === sideFromSettings.top)) { 70 | contentDocument.getElementById(item).checked = true; 71 | } 72 | }); 73 | }; 74 | 75 | const saveSettings = () => { 76 | setPersonalParam(); 77 | setIconSize(); 78 | setButtonSide(); 79 | settings.saveSettings(); 80 | close(); 81 | button.remove(); 82 | button.show(); 83 | }; 84 | 85 | const bindEvents = () => { 86 | const menuEvents = { 87 | '.close': close, 88 | '#cancel': iframeCtrl.showDetailedMenu, 89 | '#save-settings': saveSettings, 90 | }; 91 | Object.keys(menuEvents).forEach((item) => { 92 | const elems = contentDocument.querySelectorAll(item); 93 | toArray(elems).forEach((elem) => elem.addEventListener('click', menuEvents[item])); 94 | }); 95 | }; 96 | 97 | /* 98 | Called from IframeController._showMenuItem to initialize view 99 | */ 100 | // eslint-disable-next-line no-shadow 101 | const init = (iframe) => { 102 | // eslint-disable-next-line prefer-destructuring 103 | contentDocument = iframe.contentDocument; 104 | bindEvents(); 105 | setDefaultSettings(); 106 | }; 107 | 108 | return { 109 | init, 110 | }; 111 | } 112 | -------------------------------------------------------------------------------- /src/embedded.js: -------------------------------------------------------------------------------- 1 | /* global AdguardSettings */ 2 | 3 | import ioc from './ioc'; 4 | import protectedApi from './protectedApi'; 5 | import wot from './wot'; 6 | import settings from './settings'; 7 | import IframeController from './iframe'; 8 | import IframeControllerMobile from './iframe.mobile'; 9 | 10 | /* embedded script for extensions */ 11 | /* 12 | * adguardAssistantExtended main function is for desktop browsers 13 | */ 14 | export function adguardAssistantExtended() { 15 | const adguardSettings = typeof (AdguardSettings) === 'undefined' ? null : AdguardSettings; 16 | wot.registerWotEventHandler(); 17 | settings.setAdguardSettings(adguardSettings); 18 | 19 | const iframeController = new IframeController(); 20 | ioc.register('iframeController', iframeController); 21 | 22 | return { 23 | start(element, callback) { 24 | ioc.register('addRule', protectedApi.functionBind.call(callback, this)); 25 | 26 | if (element) { 27 | iframeController.showSelectorMenu(); 28 | iframeController.showSliderMenu(element); 29 | } else { 30 | iframeController.showSelectorMenu(); 31 | } 32 | }, 33 | close() { 34 | iframeController.removeIframe(); 35 | }, 36 | }; 37 | } 38 | 39 | /* 40 | * adguardAssistantMini function is for mobile browsers 41 | */ 42 | export function adguardAssistantMini() { 43 | const iframeController = new IframeControllerMobile(); 44 | ioc.register('iframeController', iframeController); 45 | 46 | return { 47 | start(element, callback) { 48 | ioc.register('addRule', protectedApi.functionBind.call(callback, this)); 49 | 50 | if (element) { 51 | iframeController.showSelectorMenu(); 52 | iframeController.showSliderMenu(element); 53 | } else { 54 | iframeController.showSelectorMenu(); 55 | } 56 | }, 57 | close() { 58 | iframeController.removeIframe(); 59 | }, 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /src/event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom event 3 | * @returns {{attach: attach, notify: notify}} 4 | * @constructor 5 | */ 6 | export default function CustomEvent() { // jshint ignore:line 7 | const listeners = []; 8 | 9 | const attach = (listener) => { 10 | listeners.push(listener); 11 | }; 12 | 13 | const notify = (args) => { 14 | for (let i = 0; i < listeners.length; i += 1) { 15 | listeners[i](args); 16 | } 17 | }; 18 | 19 | return { 20 | attach, 21 | notify, 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/gm-empty.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /src/gm.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable object-shorthand */ 2 | /* eslint-disable no-global-assign, no-alert, no-param-reassign, camelcase */ 3 | /* global 4 | GM_getValue, 5 | GM_setValue, 6 | GM_getResourceText, 7 | GM_addStyle, 8 | ADG_addRule, 9 | ADG_temporaryDontBlock, 10 | ADG_sendAbuse, 11 | ADG_isFiltered, 12 | ADG_changeFilteringState, 13 | */ 14 | 15 | import protectedApi from './protectedApi'; 16 | 17 | /** 18 | * Gm api wrapper 19 | * @returns {{ 20 | * getValue, 21 | * setValue, 22 | * GM_getResourceText, 23 | * GM_addStyle, 24 | * ADG_addRule: *, 25 | * ADG_temporaryDontBlock: *, 26 | * ADG_sendAbuse: *, 27 | * ADG_isFiltered: *, 28 | * ADG_changeFilteringState: 29 | * *}} 30 | * @constructor 31 | */ 32 | function GM() { 33 | const addRule = typeof ADG_addRule !== 'undefined' 34 | ? ADG_addRule 35 | : (rule, callback) => { 36 | alert(`GM_api is not supported. ${rule} rule added`); 37 | if (callback) callback(); 38 | }; 39 | 40 | // TODO: fix calling this one 41 | const sendAbuse = typeof ADG_sendAbuse !== 'undefined' 42 | ? ADG_sendAbuse 43 | : (url, callback) => { 44 | alert(`GM_api is not supported. ${url} abused`); 45 | if (callback) callback(); 46 | }; 47 | 48 | const temporaryDontBlock = typeof ADG_temporaryDontBlock !== 'undefined' 49 | ? ADG_temporaryDontBlock 50 | : (timeout, callback) => { 51 | alert(`GM_api is not supported. Do not block for ${timeout} seconds`); 52 | if (callback) callback(); 53 | }; 54 | 55 | const isFiltered = typeof ADG_isFiltered !== 'undefined' 56 | ? ADG_isFiltered 57 | : (callback) => { 58 | if (callback) callback(true); 59 | return true; 60 | }; 61 | 62 | const changeFilteringState = typeof ADG_changeFilteringState !== 'undefined' 63 | ? ADG_changeFilteringState 64 | : (state, callback) => { 65 | alert('GM_api is not supported. State changed'); 66 | if (callback) callback(); 67 | }; 68 | 69 | const getValue = (value) => new Promise(((resolve) => { 70 | resolve(GM_getValue(value)); 71 | })); 72 | 73 | const setValue = (key, value) => new Promise(((resolve) => { 74 | GM_setValue(key, protectedApi.json.stringify(value)); 75 | resolve(); 76 | })); 77 | 78 | return { 79 | getValue, 80 | setValue, 81 | GM_getResourceText: GM_getResourceText, 82 | GM_addStyle: GM_addStyle, 83 | ADG_addRule: addRule, 84 | ADG_temporaryDontBlock: temporaryDontBlock, 85 | ADG_sendAbuse: sendAbuse, 86 | ADG_isFiltered: isFiltered, 87 | ADG_changeFilteringState: changeFilteringState, 88 | }; 89 | } 90 | 91 | const gm = new GM(); 92 | 93 | export default gm; 94 | -------------------------------------------------------------------------------- /src/helpers.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line max-len 2 | const mobileReg = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i; 3 | 4 | export const isMobile = (ua) => { 5 | return mobileReg.test(ua); 6 | }; 7 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { adguardAssistantMini, adguardAssistantExtended } from './embedded'; 2 | import { isMobile } from './helpers'; 3 | 4 | export const adguardAssistant = isMobile(navigator.userAgent) 5 | ? adguardAssistantMini 6 | : adguardAssistantExtended; 7 | -------------------------------------------------------------------------------- /src/index.user.js: -------------------------------------------------------------------------------- 1 | import { isMobile } from './helpers'; 2 | import { adguardAssistantMini, adguardAssistantExtended } from './main'; 3 | 4 | // eslint-disable-next-line func-names 5 | (function (base) { 6 | const ua = navigator.userAgent; 7 | if (isMobile(ua)) { 8 | // eslint-disable-next-line no-param-reassign 9 | base.adguardAssistant = adguardAssistantMini; 10 | } else if (ua.indexOf('MSIE ') > -1 || ua.indexOf('Trident/') > -1) { 11 | // https://github.com/AdguardTeam/AdguardAssistant/issues/317#issuecomment-564502254 12 | // DO NOTHING 13 | } else { 14 | adguardAssistantExtended(); 15 | } 16 | }(this || window)); 17 | -------------------------------------------------------------------------------- /src/inline-resources.js: -------------------------------------------------------------------------------- 1 | import RESOURCE_TEMPLATE_BUTTON from './templates/button.html'; 2 | import RESOURCE_TEMPLATE_DETAILEDMENU from './templates/mainMenu.html'; 3 | import RESOURCE_TEMPLATE_SELECTORMENU from './templates/selectorMenu.html'; 4 | import RESOURCE_TEMPLATE_SETTINGSMENU from './templates/settingsMenu.html'; 5 | import RESOURCE_TEMPLATE_SLIDERMENU from './templates/sliderMenu.html'; 6 | import RESOURCE_TEMPLATE_BLOCKPREVIEW from './templates/blockPreview.html'; 7 | import RESOURCE_TEMPLATE_POPUP from './templates/mobilePopup.html'; 8 | import RESOURCE_TEMPLATE_MENU from './templates/mobileMenu.html'; 9 | import RESOURCE_TEMPLATE_SVG_ICONS from './templates/svgIcons.html'; 10 | 11 | import RESOURCE_CSS_COMMON from './styles/base/base-common.less'; 12 | import RESOURCE_CSS_BUTTON from './styles/button.less'; 13 | import RESOURCE_CSS_IFRAME from './styles/menu.less'; 14 | import RESOURCE_CSS_SELECTOR from './styles/selector.less'; 15 | import RESOURCE_CSS_MOBILE from './styles/mobile-style.less'; 16 | 17 | export const CSS = { 18 | common: RESOURCE_CSS_COMMON, 19 | button: RESOURCE_CSS_BUTTON, 20 | iframe: RESOURCE_CSS_IFRAME, 21 | selector: RESOURCE_CSS_SELECTOR, 22 | mobile: RESOURCE_CSS_MOBILE, 23 | }; 24 | 25 | export const HTML = { 26 | button: RESOURCE_TEMPLATE_BUTTON, 27 | detailed_menu: RESOURCE_TEMPLATE_DETAILEDMENU, 28 | selector_menu: RESOURCE_TEMPLATE_SELECTORMENU, 29 | settings_menu: RESOURCE_TEMPLATE_SETTINGSMENU, 30 | slider_menu: RESOURCE_TEMPLATE_SLIDERMENU, 31 | preview: RESOURCE_TEMPLATE_BLOCKPREVIEW, 32 | popup: RESOURCE_TEMPLATE_POPUP, 33 | mobile_menu: RESOURCE_TEMPLATE_MENU, 34 | svg_icons: RESOURCE_TEMPLATE_SVG_ICONS, 35 | }; 36 | -------------------------------------------------------------------------------- /src/ioc.js: -------------------------------------------------------------------------------- 1 | const getArguments = (func) => { 2 | // This regex is from require.js 3 | const FN_ARGS = /^function\s*[^(]*\(\s*([^)]*)\)/m; 4 | const args = func.toString().match(FN_ARGS)[1].split(','); 5 | if (args[0] === '') { 6 | return []; 7 | } 8 | return args; 9 | }; 10 | 11 | class Ioc { 12 | constructor() { 13 | this.dependencies = {}; 14 | } 15 | 16 | resolveDependencies(func) { 17 | const args = getArguments(func); 18 | const resolved = []; 19 | for (let i = 0; i < args.length; i += 1) { 20 | const depName = args[i].trim(); 21 | const dep = this.dependencies[depName]; 22 | if (!dep) { 23 | throw new Error(`Can't find dependency: ${depName}`); 24 | } 25 | resolved.push(this.dependencies[depName]); 26 | } 27 | return resolved; 28 | } 29 | 30 | register(qualifier, obj) { 31 | this.dependencies[qualifier] = obj; 32 | } 33 | 34 | get(func) { 35 | if (typeof func === 'string') { 36 | const resolved = this.dependencies[func]; 37 | if (!resolved) { 38 | throw new Error(`Can't resolve ${func}`); 39 | } 40 | return resolved; 41 | } 42 | const resolvedDependencies = this.resolveDependencies(func); 43 | 44 | function FuncWrapper() { 45 | return func.apply(func, resolvedDependencies); 46 | } 47 | 48 | FuncWrapper.prototype = func.prototype; 49 | return new FuncWrapper(); 50 | } 51 | } 52 | 53 | const ioc = new Ioc(); 54 | 55 | export default ioc; 56 | -------------------------------------------------------------------------------- /src/libs/css.escape.js: -------------------------------------------------------------------------------- 1 | /*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */ 2 | 3 | // https://drafts.csswg.org/cssom/#serialize-an-identifier 4 | export default function cssEscape(value) { 5 | if (arguments.length === 0) { 6 | throw new TypeError('`CSS.escape` requires an argument.'); 7 | } 8 | const string = String(value); 9 | const { length } = string; 10 | let index = -1; 11 | let codeUnit; 12 | let result = ''; 13 | const firstCodeUnit = string.charCodeAt(0); 14 | // eslint-disable-next-line no-plusplus 15 | while (++index < length) { 16 | codeUnit = string.charCodeAt(index); 17 | // Note: there’s no need to special-case astral symbols, surrogate 18 | // pairs, or lone surrogates. 19 | 20 | // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER 21 | // (U+FFFD). 22 | if (codeUnit === 0x0000) { 23 | result += '\uFFFD'; 24 | // eslint-disable-next-line no-continue 25 | continue; 26 | } 27 | 28 | if ( 29 | // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is 30 | // U+007F, […] 31 | // eslint-disable-next-line eqeqeq 32 | (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F 33 | // If the character is the first character and is in the range [0-9] 34 | // (U+0030 to U+0039), […] 35 | || (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) 36 | // If the character is the second character and is in the range [0-9] 37 | // (U+0030 to U+0039) and the first character is a `-` (U+002D), […] 38 | || ( 39 | index === 1 40 | && codeUnit >= 0x0030 41 | && codeUnit <= 0x0039 42 | // eslint-disable-next-line eqeqeq 43 | && firstCodeUnit == 0x002D 44 | ) 45 | ) { 46 | // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point 47 | result += `\\${codeUnit.toString(16)} `; 48 | // eslint-disable-next-line no-continue 49 | continue; 50 | } 51 | 52 | if ( 53 | // If the character is the first character and is a `-` (U+002D), and 54 | // there is no second character, […] 55 | index === 0 56 | && length === 1 57 | // eslint-disable-next-line eqeqeq 58 | && codeUnit == 0x002D 59 | ) { 60 | result += `\\${string.charAt(index)}`; 61 | // eslint-disable-next-line no-continue 62 | continue; 63 | } 64 | 65 | // If the character is not handled by one of the above rules and is 66 | // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or 67 | // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to 68 | // U+005A), or [a-z] (U+0061 to U+007A), […] 69 | if ( 70 | codeUnit >= 0x0080 71 | // eslint-disable-next-line eqeqeq 72 | || codeUnit == 0x002D 73 | // eslint-disable-next-line eqeqeq 74 | || codeUnit == 0x005F 75 | || (codeUnit >= 0x0030 && codeUnit <= 0x0039) 76 | || (codeUnit >= 0x0041 && codeUnit <= 0x005A) 77 | || (codeUnit >= 0x0061 && codeUnit <= 0x007A) 78 | ) { 79 | // the character itself 80 | result += string.charAt(index); 81 | // eslint-disable-next-line no-continue 82 | continue; 83 | } 84 | 85 | // Otherwise, the escaped character. 86 | // https://drafts.csswg.org/cssom/#escape-a-character 87 | result += `\\${string.charAt(index)}`; 88 | } 89 | return result; 90 | } 91 | -------------------------------------------------------------------------------- /src/log.js: -------------------------------------------------------------------------------- 1 | /* global DEBUG */ 2 | /** 3 | * Simple logger with log levels 4 | * @returns {{ 5 | * warn: warn, 6 | * info: info, 7 | * debug: debug, 8 | * error: error 9 | * }} 10 | * @constructor 11 | */ 12 | function Log() { 13 | const currentLevel = DEBUG ? 'DEBUG' : 'ERROR'; 14 | 15 | const LogLevels = { 16 | ERROR: 1, 17 | WARN: 2, 18 | INFO: 3, 19 | DEBUG: 4, 20 | }; 21 | 22 | const print = (level, method, args) => { 23 | // check log level 24 | if (LogLevels[currentLevel] < LogLevels[level]) { 25 | return; 26 | } 27 | if (!args || args.length === 0 || !args[0]) { 28 | return; 29 | } 30 | let formatted; 31 | if (typeof args[0] === 'object') { 32 | // eslint-disable-next-line prefer-destructuring 33 | formatted = args[0]; 34 | } else { 35 | const str = `${args[0]}`; 36 | // eslint-disable-next-line no-param-reassign 37 | args = Array.prototype.slice.call(args, 1); 38 | formatted = str.replace( 39 | /{(\d+)}/g, 40 | (match, number) => (typeof args[number] !== 'undefined' ? args[number] : match), 41 | ); 42 | if (LogLevels[level] >= LogLevels[currentLevel]) { 43 | const now = new Date(); 44 | formatted = `${now.toISOString()}: ${formatted}`; 45 | } 46 | } 47 | // eslint-disable-next-line no-console 48 | console[method](formatted); 49 | }; 50 | 51 | const debug = (...args) => { 52 | print('DEBUG', 'log', args); 53 | }; 54 | 55 | const info = (...args) => { 56 | print('INFO', 'info', args); 57 | }; 58 | 59 | const warn = (...args) => { 60 | print('WARN', 'info', args); 61 | }; 62 | 63 | const error = (...args) => { 64 | print('ERROR', 'error', args); 65 | }; 66 | 67 | return { 68 | debug, info, warn, error, 69 | }; 70 | } 71 | 72 | const log = new Log(); 73 | 74 | export default log; 75 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* 3 | global 4 | AdguardSettings 5 | */ 6 | import ioc from './ioc'; 7 | import protectedApi from './protectedApi'; 8 | import wot from './wot'; 9 | import settings from './settings'; 10 | import IframeController from './iframe'; 11 | import runSheduler from './runSheduler'; 12 | import IframeControllerMobile from './iframe.mobile'; 13 | import button from './button'; 14 | 15 | /** 16 | * adguardAssistantExtended main function is for desktop browsers, running by onload event 17 | */ 18 | export const adguardAssistantExtended = () => { 19 | ioc.register('addRule', () => false); 20 | ioc.register('iframeController', new IframeController()); 21 | 22 | wot.registerWotEventHandler(); 23 | const adguardSettings = typeof (AdguardSettings) === 'undefined' ? null : AdguardSettings; 24 | settings.setAdguardSettings(adguardSettings); 25 | 26 | runSheduler.onDocumentEnd(settings.loadSettings, button.show); 27 | }; 28 | 29 | /** 30 | * adguardAssistantMini function is for mobile browsers 31 | * and stored in global variable `adguardAssistant` execute with callback: 32 | * 33 | * adguardAssistant().start(null, callback); 34 | */ 35 | export const adguardAssistantMini = () => ({ 36 | start(callback) { 37 | ioc.register('addRule', protectedApi.functionBind.call(callback, this)); 38 | 39 | const iframeController = new IframeControllerMobile(); 40 | ioc.register('iframeController', iframeController); 41 | 42 | runSheduler.onDocumentEnd(iframeController.showSelectorMenu); 43 | }, 44 | }); 45 | -------------------------------------------------------------------------------- /src/runSheduler.js: -------------------------------------------------------------------------------- 1 | import protectedApi from './protectedApi'; 2 | 3 | /** 4 | * Run callback with args on document-end 5 | * @returns {{}} 6 | * @constructor 7 | */ 8 | function RunSheduler() { 9 | const READY_STATE_CHANGE = 'readystatechange'; 10 | const INTERACTIVE = 'interactive'; 11 | const COMPLETE = 'complete'; 12 | 13 | // readyState become 'interactive' too early on IE10 and lower 14 | // https://github.com/AdguardTeam/AdguardForWindows/issues/2042 15 | // https://github.com/mobify/mobifyjs/issues/136 16 | // We fallback to running on `complete` only, as what jquery did. 17 | const isIE10OrLess = protectedApi.documentMode < 11; 18 | const onDocumentEnd = (callback, arg) => { 19 | let done = false; 20 | 21 | const onreadystatechange = () => { 22 | const readyState = protectedApi.getReadyState(); 23 | if ((!isIE10OrLess && readyState === INTERACTIVE) 24 | || readyState === COMPLETE) { 25 | // eslint-disable-next-line no-use-before-define 26 | _callback(); 27 | } 28 | }; 29 | 30 | // Function declarations 31 | // eslint-disable-next-line no-underscore-dangle 32 | const _callback = () => { 33 | if (done) { return; } 34 | done = true; 35 | protectedApi.removeListenerFromWindow(READY_STATE_CHANGE, onreadystatechange); 36 | callback(arg); 37 | }; 38 | 39 | // Attaches event listeners 40 | const readyState = protectedApi.getReadyState(); 41 | if ((!isIE10OrLess && readyState === INTERACTIVE) || readyState === COMPLETE) { 42 | _callback(); 43 | return; 44 | } 45 | 46 | protectedApi.addListenerToWindow(READY_STATE_CHANGE, onreadystatechange, true); 47 | }; 48 | 49 | return { 50 | onDocumentEnd, 51 | }; 52 | } 53 | const runSheduler = new RunSheduler(); 54 | 55 | export default runSheduler; 56 | -------------------------------------------------------------------------------- /src/styles/base/base-common.less: -------------------------------------------------------------------------------- 1 | // Base common 2 | 3 | @import "variables"; 4 | @import "../mixins/mixins"; 5 | @import "normalize"; 6 | @import "scaffolding"; 7 | @import "forms"; 8 | @import "fonts"; 9 | @import "form-ui"; 10 | @import "buttons"; 11 | 12 | @media (prefers-color-scheme: light) { 13 | @import "light-mode"; 14 | } 15 | 16 | @media (prefers-color-scheme: dark) { 17 | @import "dark-mode"; 18 | } 19 | -------------------------------------------------------------------------------- /src/styles/base/fonts.less: -------------------------------------------------------------------------------- 1 | // Fonts 2 | 3 | @import (inline) "fonts.css"; 4 | 5 | .weight-regular { 6 | font-family: "Roboto", Arial, sans-serif; 7 | font-weight: 400; 8 | } 9 | 10 | .weight-medium { 11 | font-family: "Roboto", Arial, sans-serif; 12 | font-weight: 600; 13 | } 14 | 15 | .weight-bold { 16 | font-family: "Roboto", Arial, sans-serif; 17 | font-weight: 700; 18 | } 19 | -------------------------------------------------------------------------------- /src/styles/base/form-ui.less: -------------------------------------------------------------------------------- 1 | // checkbox radio 2 | 3 | 4 | .form-ui { 5 | position: relative; 6 | display: inline-block; 7 | vertical-align: top; 8 | margin: 0; 9 | line-height: 20px; 10 | font-weight: 400; 11 | cursor: pointer; 12 | } 13 | 14 | .form-ui-control{ 15 | position: absolute; 16 | top: -9999px; 17 | left: -9999px; 18 | width: 0; 19 | height: 0; 20 | overflow: hidden; 21 | clip: rect(0, 0, 0, 0); 22 | outline: 0 none; 23 | } 24 | 25 | .form-ui-txt{ 26 | position: relative; 27 | display: inline-block; 28 | vertical-align: top; 29 | padding: 2px 0 0 30px; 30 | &:before{ 31 | content: ''; 32 | position: absolute; 33 | top: 0; 34 | left: 0; 35 | width: 22px; 36 | height: 22px; 37 | } 38 | &:after{ 39 | content: ''; 40 | position: absolute; 41 | } 42 | } 43 | 44 | 45 | 46 | 47 | // radio 48 | input[type="radio"] { 49 | & + .form-ui{ 50 | .form-ui-txt{ 51 | &:before{ 52 | border: 1px solid #cfcfcf; 53 | border-radius: 50%; 54 | } 55 | &:after{ 56 | top: 7px; 57 | left: 7px; 58 | width: 8px; 59 | height: 8px; 60 | border-radius: 50%; 61 | } 62 | } 63 | } 64 | 65 | &:disabled { 66 | & + .form-ui{ 67 | .form-ui-txt{ 68 | &:before{ 69 | background: #f2f2f2; 70 | opacity: .6; 71 | } 72 | &:after{ 73 | opacity: .6; 74 | } 75 | } 76 | } 77 | } 78 | } 79 | // /radio 80 | 81 | 82 | 83 | 84 | // checkbox 85 | input[type="checkbox"] { 86 | & + .form-ui { 87 | display: flex; 88 | column-gap: 12px; 89 | 90 | .form-ui-txt { 91 | padding: 3px 0; 92 | font-size: 14px; 93 | font-weight: 600; 94 | line-height: 18px; 95 | 96 | &:before, 97 | &:after { 98 | content: none; 99 | } 100 | } 101 | 102 | .form-ui-icon { 103 | width: 24px; 104 | height: 24px; 105 | 106 | &--off { 107 | display: block; 108 | } 109 | 110 | &--on { 111 | display: none; 112 | } 113 | } 114 | } 115 | 116 | &:disabled + .form-ui .form-ui-icon { 117 | opacity: .6; 118 | } 119 | 120 | &:checked + .form-ui .form-ui-icon { 121 | &--off { 122 | display: none; 123 | } 124 | 125 | &--on { 126 | display: block; 127 | } 128 | } 129 | } 130 | // /checkbox 131 | -------------------------------------------------------------------------------- /src/styles/base/palette.less: -------------------------------------------------------------------------------- 1 | // == Figma base color variables == 2 | 3 | @gray-0: #FFFFFF; 4 | @gray-10: #F6F6F6; 5 | @gray-20: #E4E4E4; 6 | @gray-30: #D2D2D2; 7 | @gray-40: #C0C0C0; 8 | @gray-50: #A4A4A4; 9 | @gray-60: #7F7F7F; 10 | @gray-70: #5B5B5B; 11 | @gray-80: #3D3D3D; 12 | 13 | @red-30: #FAA98F; 14 | @red-50: #F67247; 15 | @red-60: #DF6740; 16 | @red-70: #B55434; 17 | @red-90: #7B3923; 18 | 19 | @product-primary-50: #67B279; -------------------------------------------------------------------------------- /src/styles/base/scaffolding.less: -------------------------------------------------------------------------------- 1 | // 2 | // Scaffolding 3 | // -------------------------------------------------- 4 | // Reset the box-sizing 5 | // 6 | // Heads up! This reset may cause conflicts with some third-party widgets. 7 | // For recommendations on resolving such conflicts, see 8 | // http://getbootstrap.com/getting-started/#third-box-sizing 9 | 10 | * { 11 | box-sizing: border-box; 12 | } 13 | 14 | *:after, 15 | *:before { 16 | box-sizing: border-box; 17 | } 18 | // Body reset 19 | html { 20 | font-size: 10px; 21 | -webkit-tap-highlight-color: rgba(0,0,0,0); 22 | } 23 | 24 | body { 25 | position: relative; 26 | font-size: 1.3rem; 27 | line-height: 1.428571429; 28 | background-color: #e6e6e6; 29 | .weight-regular; 30 | overflow-y: hidden; 31 | } 32 | 33 | // Reset fonts for relevant elements 34 | button, 35 | input, 36 | select, 37 | textarea { 38 | font-family: inherit; 39 | font-size: inherit; 40 | line-height: inherit; 41 | outline: none; 42 | border-radius: 0; 43 | box-shadow: none; 44 | } 45 | // Links 46 | a { 47 | color: @link-color; 48 | text-decoration: underline; 49 | outline: 0; 50 | 51 | &:hover { 52 | color: @link-hover-color; 53 | text-decoration: underline; 54 | } 55 | } 56 | // Figures 57 | // 58 | // We reset this here because previously Normalize had no `figure` margins. This 59 | // ensures we don't break anyone's use of the element. 60 | 61 | figure { 62 | margin: 0; 63 | } 64 | // Images 65 | img { 66 | vertical-align: middle; 67 | max-width: 100%; 68 | } 69 | -------------------------------------------------------------------------------- /src/styles/base/tooltip.less: -------------------------------------------------------------------------------- 1 | // 2 | // Tooltips 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .tooltip { 8 | position: absolute; 9 | z-index: @zindex-tooltip; 10 | display: block; 11 | // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element. 12 | // So reset our font and text properties to avoid inheriting weird values. 13 | //.reset-text(); 14 | font-size: 15px; 15 | line-height: 1.3; 16 | 17 | opacity: 0; 18 | 19 | &.in { opacity: 1; }//@tooltip-opacity 20 | &.top { margin-top: -3px; padding: @tooltip-arrow-width 0; } 21 | &.right { margin-left: 3px; padding: 0 @tooltip-arrow-width; } 22 | &.bottom { margin-top: 3px; padding: @tooltip-arrow-width 0; } 23 | &.left { margin-left: -3px; padding: 0 @tooltip-arrow-width; } 24 | } 25 | 26 | // Wrapper for the tooltip content 27 | .tooltip-cont{ 28 | max-width: @tooltip-max-width; 29 | border: 1px solid #ccc; 30 | box-shadow: 0 2px 20px fade(#000, 10); 31 | } 32 | 33 | .tooltip-inner { 34 | position: relative; 35 | padding: 5px 10px; 36 | color: @text-color; 37 | text-align: center; 38 | background-color: #fff; //@tooltip-bg 39 | //border-radius: @border-radius-base; 40 | } 41 | 42 | // Arrows 43 | .tooltip-arrow { 44 | position: absolute; 45 | width: @tooltip-arrow-width * 2; 46 | height: @tooltip-arrow-width * 2; 47 | //border-color: transparent; 48 | //border-style: solid; 49 | &:before{ 50 | content: ''; 51 | position: absolute; 52 | width: 100%; 53 | height: 100%; 54 | background: #fff; 55 | border: 1px solid #ccc; 56 | transform: rotate(45deg); 57 | } 58 | } 59 | 60 | .tooltip { 61 | &.top .tooltip-arrow { 62 | bottom: 0; 63 | left: 50%; 64 | margin-left: -@tooltip-arrow-width; 65 | margin-bottom: 1px; 66 | //border-width: @tooltip-arrow-width @tooltip-arrow-width 0; 67 | //border-top-color: @tooltip-arrow-color; 68 | } 69 | &.top-left .tooltip-arrow { 70 | bottom: 0; 71 | right: @tooltip-arrow-width; 72 | margin-bottom: -@tooltip-arrow-width; 73 | //border-width: @tooltip-arrow-width @tooltip-arrow-width 0; 74 | //border-top-color: @tooltip-arrow-color; 75 | } 76 | &.top-right .tooltip-arrow { 77 | bottom: 0; 78 | left: @tooltip-arrow-width; 79 | margin-bottom: -@tooltip-arrow-width; 80 | //border-width: @tooltip-arrow-width @tooltip-arrow-width 0; 81 | //border-top-color: @tooltip-arrow-color; 82 | } 83 | &.right .tooltip-arrow { 84 | top: 50%; 85 | left: 0; 86 | margin-top: -@tooltip-arrow-width; 87 | //border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0; 88 | //border-right-color: @tooltip-arrow-color; 89 | } 90 | &.left .tooltip-arrow { 91 | top: 50%; 92 | right: 0; 93 | margin-top: -@tooltip-arrow-width; 94 | //border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width; 95 | //border-left-color: @tooltip-arrow-color; 96 | } 97 | &.bottom .tooltip-arrow { 98 | top: 0; 99 | left: 50%; 100 | margin-left: -@tooltip-arrow-width; 101 | margin-top: 1px; 102 | //border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; 103 | //border-bottom-color: @tooltip-arrow-color; 104 | } 105 | &.bottom-left .tooltip-arrow { 106 | top: 0; 107 | right: @tooltip-arrow-width; 108 | margin-top: -@tooltip-arrow-width; 109 | //border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; 110 | //border-bottom-color: @tooltip-arrow-color; 111 | } 112 | &.bottom-right .tooltip-arrow { 113 | top: 0; 114 | left: @tooltip-arrow-width; 115 | margin-top: -@tooltip-arrow-width; 116 | //border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; 117 | //border-bottom-color: @tooltip-arrow-color; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/styles/base/variables.less: -------------------------------------------------------------------------------- 1 | // 2 | // Variables 3 | // -------------------------------------------------- 4 | 5 | 6 | //== Colors 7 | 8 | @assist-green02: rgba(104, 188, 113, 0.2); 9 | @assist-greenc1: #bde5c1; 10 | @assist-green5B: #4d9c5b; 11 | @assist-green72: #68bc72; 12 | @assist-green74: #66B574; 13 | @assist-red37: #e85037; 14 | @assist-red37light: lighten(@assist-red37, 10%); 15 | @assist-grey-shadow: rgba(162, 161, 161, 0.3); 16 | 17 | @gray-darker: lighten(#000, 13.5%); // #222 18 | @gray-dark: lighten(#000, 20%); // #333 19 | @gray: lighten(#000, 33.5%); // #555 20 | @gray-light: lighten(#000, 46.7%); // #777 21 | @gray-lighter: lighten(#000, 93.5%); // #eee 22 | 23 | 24 | @base-color: #36ba53; 25 | @secondary-color: #58595b; 26 | @base-color-hover: lighten(@base-color, 40%); 27 | 28 | 29 | 30 | //== Scaffolding 31 | 32 | @body-bg: #e8ecf0; 33 | @text-color: #333; 34 | 35 | @link-color: @base-color; 36 | @link-hover-color: darken(@link-color, 15%); 37 | 38 | 39 | 40 | //== Forms 41 | 42 | @border-radius-base: 4px; 43 | @input-bg: #fff; 44 | @input-bg-disabled: @gray-lighter; 45 | 46 | @input-color: @gray; 47 | @input-border: #ccc; 48 | @input-border-radius: @border-radius-base; 49 | @input-border-focus: darken(@input-border, 25%); 50 | 51 | @input-color-placeholder: @gray-light; 52 | 53 | @input-height-base: @line-height-base; 54 | 55 | 56 | 57 | //== Typography 58 | 59 | @font-family-sans-serif: Arial, sans-serif; 60 | @font-family-serif: Georgia, "Times New Roman", Times, serif; 61 | @font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; 62 | 63 | 64 | @font-family-base: @font-family-sans-serif; 65 | @font-size-base: 14px; 66 | @line-height-base: 22px; 67 | @line-height-computed: 22/14; 68 | 69 | 70 | 71 | //== Grid system 72 | 73 | @grid-columns: 12; 74 | @grid-gutter-width: 30px; 75 | 76 | 77 | 78 | //== Media queries breakpoints 79 | 80 | @mobile: 320px; 81 | 82 | @screen-xs: 480px; 83 | @screen-xs-min: @screen-xs; 84 | @screen-phone: @screen-xs-min; 85 | 86 | @screen-sm: 768px; 87 | @screen-sm-min: @screen-sm; 88 | @screen-tablet: @screen-sm-min; 89 | 90 | @screen-md: 1010px; 91 | @screen-md-min: @screen-md; 92 | @screen-desktop: @screen-md-min; 93 | 94 | @screen-lg: 1200px; 95 | @screen-lg-min: @screen-lg; 96 | @screen-lg-desktop: @screen-lg-min; 97 | 98 | @screen-xxs-max: (@screen-xs-min - 1); 99 | @screen-xs-max: (@screen-sm-min - 1); 100 | @screen-sm-max: (@screen-md-min - 1); 101 | @screen-md-max: (@screen-lg-min - 1); 102 | 103 | 104 | 105 | //== Container sizes 106 | 107 | @container-tablet: ((720px + @grid-gutter-width)); 108 | @container-sm: @container-tablet; 109 | 110 | @container-desktop: ((980px + @grid-gutter-width)); 111 | @container-md: @container-desktop; 112 | 113 | @container-large-desktop: ((1170px + @grid-gutter-width)); 114 | @container-lg: @container-large-desktop; 115 | 116 | 117 | 118 | //== Modals 119 | 120 | @modal-inner-padding: 15px; 121 | @modal-title-padding: 15px; 122 | @modal-title-line-height: @line-height-base; 123 | 124 | @modal-content-bg: #fff; 125 | @modal-content-border-color: rgba(0,0,0,.2); 126 | @modal-content-fallback-border-color: #999; 127 | 128 | @modal-backdrop-bg: #000; 129 | @modal-backdrop-opacity: .3; 130 | @modal-header-border-color: #e5e5e5; 131 | @modal-footer-border-color: @modal-header-border-color; 132 | 133 | @modal-lg: 900px; 134 | @modal-md: 600px; 135 | @modal-sm: 300px; 136 | -------------------------------------------------------------------------------- /src/styles/button.less: -------------------------------------------------------------------------------- 1 | @import "base/variables"; 2 | @import "mixins/mixins"; 3 | @import "modules/host"; 4 | 5 | .adguard-assistant-button-main-logo { 6 | background-image: url("./img/logo.svg")!important; 7 | } 8 | 9 | .adguard-alert { 10 | cursor: pointer; 11 | left: 0; 12 | top: 0; 13 | position: fixed !important; 14 | z-index: 2147483647 !important; 15 | width: 40px !important; 16 | height: 40px !important; 17 | zoom: 1 !important; 18 | display: inline-block !important; 19 | margin: 0 !important; 20 | border: 0 !important; 21 | padding: 0 !important; 22 | will-change: transform; 23 | opacity: 1; 24 | touch-action: none; 25 | -ms-touch-action: none; 26 | visibility: visible !important; 27 | min-height: auto !important; 28 | max-height: auto !important; 29 | min-width: auto !important; 30 | max-width: auto !important; 31 | background-size: 21px!important; 32 | background-position: center center!important; 33 | background-repeat: no-repeat!important; 34 | background-color: #fff!important; 35 | border: none !important; 36 | box-shadow: 0 0 10px 3px rgba(162, 161, 161, .3) !important; 37 | border-radius: 100% !important; 38 | transition: background-color .3s ease; 39 | 40 | &.sg_hide_element { 41 | display: none!important; 42 | } 43 | 44 | &.logo-small { 45 | width: 24px !important; 46 | height: 24px !important; 47 | background-position: 50% 6px!important; 48 | background-size: 14px!important; 49 | } 50 | 51 | &:hover { 52 | background-color: @base-color-hover!important; 53 | } 54 | } 55 | 56 | @media print { 57 | .adguard-alert { 58 | display: none!important; 59 | } 60 | } 61 | 62 | .adguard-assistant-button-right { 63 | left: auto; 64 | right: 0; 65 | } 66 | 67 | .adguard-assistant-button-left { 68 | left: 0; 69 | right: auto; 70 | } 71 | 72 | .adguard-assistant-button-top { 73 | top: 0; 74 | bottom: auto; 75 | } 76 | 77 | .adguard-assistant-button-bottom { 78 | top: auto; 79 | bottom: 0; 80 | } 81 | 82 | .adguard-assistant-button-top.adguard-assistant-button-left { 83 | left: 0; 84 | right: auto; 85 | transform: translate3d(10px, 10px, 0); 86 | } 87 | 88 | .adguard-assistant-button-top.adguard-assistant-button-right { 89 | left: auto; 90 | right: 0; 91 | transform: translate3d(-10px, 10px, 0); 92 | } 93 | 94 | .adguard-assistant-button-bottom.adguard-assistant-button-left { 95 | left: 0; 96 | right: auto; 97 | transform: translate3d(10px, -10px, 0); 98 | } 99 | 100 | .adguard-assistant-button-bottom.adguard-assistant-button-right { 101 | left: auto; 102 | right: 0; 103 | transform: translate3d(-10px, -10px, 0); 104 | } 105 | 106 | .adguard-assistant-button-bottom.adguard-assistant-button-respect-vk { 107 | transform: translate3d(-70px, -5px, 0); 108 | } 109 | 110 | .adguard-assistant-button-bottom.adguard-assistant-button-respect-fb { 111 | transform: translate3d(-5px, -40px, 0); 112 | } 113 | -------------------------------------------------------------------------------- /src/styles/img/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/styles/img/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/styles/img/close-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/styles/img/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/styles/img/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/styles/img/extensions.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/styles/img/eye-closed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/styles/img/eye-opened.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/styles/img/landscape.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/styles/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/styles/img/minus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/styles/img/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/styles/img/pos-arr.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/styles/img/report.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/styles/img/security.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/styles/img/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/styles/img/wot-confidence-0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/styles/img/wot-confidence-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/styles/img/wot-confidence-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/styles/img/wot-confidence-3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/styles/img/wot-confidence-4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/styles/img/wot-confidence-5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/styles/img/wot-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/styles/menu.less: -------------------------------------------------------------------------------- 1 | @import "base/variables"; 2 | @import "mixins/mixins"; 3 | @import "modules/modules-common"; 4 | @import "modules/host"; 5 | -------------------------------------------------------------------------------- /src/styles/mixins/forms.less: -------------------------------------------------------------------------------- 1 | // Form validation states 2 | // 3 | // Used in forms.less to generate the form validation CSS for warnings, errors, 4 | // and successes. 5 | 6 | .form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) { 7 | // Color the label and help text 8 | .help-block, 9 | .control-label, 10 | .radio, 11 | .checkbox, 12 | .radio-inline, 13 | .checkbox-inline { 14 | color: @text-color; 15 | } 16 | // Set the border and box shadow on specific inputs to match 17 | .form-control { 18 | border-color: @border-color; 19 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work 20 | &:focus { 21 | border-color: darken(@border-color, 10%); 22 | @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%); 23 | .box-shadow(@shadow); 24 | } 25 | } 26 | // Set validation states also for addons 27 | .input-group-addon { 28 | color: @text-color; 29 | border-color: @border-color; 30 | background-color: @background-color; 31 | } 32 | // Optional feedback icon 33 | .form-control-feedback { 34 | color: @text-color; 35 | } 36 | } 37 | 38 | 39 | // Form control focus state 40 | // 41 | // Generate a customized focus state and for any input with the specified color, 42 | // which defaults to the `@input-border-focus` variable. 43 | // 44 | // We highly encourage you to not customize the default value, but instead use 45 | // this to tweak colors on an as-needed basis. This aesthetic change is based on 46 | // WebKit's default styles, but applicable to a wider range of browsers. Its 47 | // usability and accessibility should be taken into account with any change. 48 | // 49 | // Example usage: change the default blue border and shadow to white for better 50 | // contrast against a dark gray background. 51 | .form-control-focus(@color: @input-border-focus) { 52 | @color-rgba: rgba(red(@color), green(@color), blue(@color), .6); 53 | &:focus { 54 | border-color: @color; 55 | outline: 0; 56 | } 57 | } 58 | 59 | // Form control sizing 60 | // 61 | // Relative text size, padding, and border-radii changes for form controls. For 62 | // horizontal sizing, wrap controls in the predefined grid classes. ` 21 | 22 | 23 | 24 | 25 | 32 | 33 | -------------------------------------------------------------------------------- /src/templates/mobileMenu.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/templates/mobilePopup.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 |
5 | 9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /src/templates/selectorMenu.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |
8 |
9 |
10 |
11 |
12 | 13 |
14 |
15 | -------------------------------------------------------------------------------- /src/templates/svgIcons.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 21 | 22 | 23 | 34 | 35 | 36 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/upgradeHelper.js: -------------------------------------------------------------------------------- 1 | import protectedApi from './protectedApi'; 2 | import log from './log'; 3 | 4 | /* eslint-disable no-param-reassign */ 5 | /** 6 | * Helper for backward compatibility 7 | * @returns {{}} 8 | * @constructor 9 | */ 10 | class UpgradeHelper { 11 | constructor() { 12 | this.Constants = { 13 | BUTTON_POSITION_ITEM_NAME: '__adbpos', 14 | }; 15 | } 16 | 17 | getButtonPositionData() { 18 | try { 19 | const userPosition = localStorage.getItem(this.Constants.BUTTON_POSITION_ITEM_NAME); 20 | if (userPosition) { 21 | return protectedApi.json.parse(userPosition); 22 | } 23 | return undefined; 24 | } catch (ex) { 25 | log.error(ex); 26 | return undefined; 27 | } 28 | } 29 | 30 | removeUserPositionForButton() { 31 | try { 32 | localStorage.removeItem(this.Constants.BUTTON_POSITION_ITEM_NAME); 33 | } catch (ex) { 34 | log.error(ex); 35 | } 36 | } 37 | 38 | // eslint-disable-next-line class-methods-use-this 39 | upgradeGmStorage(settings, version) { 40 | settings.personal = {}; 41 | settings.scriptVersion = version; 42 | settings.personalConfig = true; 43 | return settings; 44 | } 45 | 46 | // Helper for assistant update from 4.1 to 4.2 47 | upgradeLocalStorage(settings, sitename) { 48 | const position = this.getButtonPositionData(); 49 | if (position) { 50 | if (!settings.personal[sitename]) { 51 | settings.personal[sitename] = {}; 52 | } 53 | settings.personal[sitename].position = position; 54 | settings.personal[sitename].largeIcon = settings.largeIcon; 55 | } 56 | this.removeUserPositionForButton(); 57 | return settings; 58 | } 59 | } 60 | 61 | const upgradeHelper = new UpgradeHelper(); 62 | 63 | export default upgradeHelper; 64 | -------------------------------------------------------------------------------- /src/utils/common-utils.js: -------------------------------------------------------------------------------- 1 | export const cropDomain = (domain) => domain.replace('www.', '').replace(/:\d+/, ''); 2 | 3 | /** 4 | * Force clear the page cache 5 | * see: https://stackoverflow.com/questions/10719505/force-a-reload-of-page-in-chrome-using-javascript-no-cache/27058362#27058362 6 | * @param callback 7 | */ 8 | export const bypassCache = (callback) => { 9 | const xhr = new XMLHttpRequest(); 10 | xhr.open('GET', window.location.href, true); 11 | 12 | xhr.setRequestHeader('Pragma', 'no-cache'); 13 | xhr.setRequestHeader('Expires', '-1'); 14 | xhr.setRequestHeader('Cache-Control', 'no-cache'); 15 | 16 | xhr.onreadystatechange = () => { 17 | if (xhr.readyState === 4 && callback) { 18 | callback(); 19 | } 20 | }; 21 | 22 | xhr.send(); 23 | }; 24 | 25 | /** 26 | * Reload page after bypassing cache 27 | */ 28 | export const reloadPageBypassCache = () => { 29 | bypassCache(() => { 30 | window.location.reload(true); 31 | }); 32 | }; 33 | 34 | /** 35 | * Multiple event handler helper. 36 | * @param {Object} elements element or nodeList. 37 | * @param {String} events multiple events divided by space. 38 | * @param {Function} eventHandler event handler. 39 | * @param {Boolean} useCapture capture. 40 | * @return {Function} add/remove. 41 | */ 42 | export const events = { 43 | add(elements, es, eventHandler, useCapture) { 44 | this.addRemoveEvents(true, elements, es, eventHandler, useCapture); 45 | }, 46 | remove(elements, es, eventHandler, useCapture) { 47 | this.addRemoveEvents(false, elements, es, eventHandler, useCapture); 48 | }, 49 | // eslint-disable-next-line consistent-return 50 | addRemoveEvents(add, elements, es, eventHandler, useCapture) { 51 | if (!elements || !es || !eventHandler) { 52 | return false; 53 | } 54 | 55 | const eventList = es.split(' '); 56 | 57 | if (!eventList || eventList.length < 1) { 58 | return false; 59 | } 60 | 61 | if (!elements.length) { 62 | // eslint-disable-next-line no-param-reassign 63 | elements = new Array(elements); 64 | } 65 | 66 | for (let el = 0; el < elements.length; el += 1) { 67 | for (let evt = 0; evt < eventList.length; evt += 1) { 68 | if (!eventList[evt] || !eventList[evt].length) { 69 | // eslint-disable-next-line no-continue 70 | continue; 71 | } 72 | 73 | if (add) { 74 | elements[el].addEventListener( 75 | eventList[evt], 76 | eventHandler, 77 | !!useCapture, 78 | ); 79 | } else { 80 | elements[el].removeEventListener( 81 | eventList[evt], 82 | eventHandler, 83 | !!useCapture, 84 | ); 85 | } 86 | } 87 | } 88 | }, 89 | }; 90 | 91 | /** 92 | * Common utils 93 | * @type {{ 94 | * cropDomain: Function, 95 | * bypassCache: Function, 96 | * reloadPageBypassCache: Function, 97 | * events: Object 98 | * }} 99 | */ 100 | const CommonUtils = { 101 | cropDomain, 102 | bypassCache, 103 | reloadPageBypassCache, 104 | events, 105 | }; 106 | 107 | export default CommonUtils; 108 | -------------------------------------------------------------------------------- /src/utils/ui-validation-utils.js: -------------------------------------------------------------------------------- 1 | import log from '../log'; 2 | import settings from '../settings'; 3 | 4 | /** 5 | * Utils that checks environment for compatibility with assistant 6 | * @returns {{ 7 | * checkVisibleAreaSize: checkVisibleAreaSize, 8 | * validateBrowser: validateBrowser, 9 | * validatePage: validatePage, 10 | * getViewPort: getViewPort 11 | * }} 12 | * @constructor 13 | */ 14 | function UIValidationUtils() { 15 | const { document } = window; 16 | 17 | const getViewPort = () => { 18 | const width = window.innerWidth; 19 | const height = window.innerHeight; 20 | 21 | return { width, height }; 22 | }; 23 | 24 | /** 25 | * Check if visible area are enough to show menu. 26 | * @returns boolean. True if area enough 27 | */ 28 | const checkVisibleAreaSize = () => { 29 | const viewPort = getViewPort(); 30 | // eslint-disable-next-line max-len 31 | const visibleAreaSize = viewPort.height > settings.Constants.MINIMUM_VISIBLE_HEIGHT_TO_SHOW_BUTTON; 32 | 33 | if (!visibleAreaSize) { 34 | log.error(`Viewport height is too small: ${viewPort.height}`); 35 | } 36 | 37 | return visibleAreaSize; 38 | }; 39 | 40 | /** 41 | * Checks if browser is valid for Adguard assistant 42 | * @returns boolean. True if browser valid 43 | */ 44 | const validateBrowser = () => { 45 | const valid = !document.documentMode 46 | || (document.documentMode > settings.Constants.MINIMUM_IE_SUPPORTED_VERSION); 47 | 48 | if (!valid) { 49 | log.error(`IE version is ${document.documentMode}`); 50 | } 51 | 52 | return valid; 53 | }; 54 | 55 | /** 56 | * Checks if page is valid for Adguard assistant to work here. 57 | */ 58 | const validatePage = () => { 59 | // Assistant do not work in iframes 60 | if (window.window !== window.top) { 61 | return false; 62 | } 63 | return true; 64 | }; 65 | 66 | return { 67 | checkVisibleAreaSize, 68 | validateBrowser, 69 | validatePage, 70 | getViewPort, 71 | }; 72 | } 73 | 74 | const uiValidationUtils = new UIValidationUtils(); 75 | 76 | export default uiValidationUtils; 77 | -------------------------------------------------------------------------------- /src/wot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Object that manages wot data 3 | * @returns {{ 4 | * registerWotEventHandler: Function, 5 | * getWotData: Function, 6 | * getWotScorecardUrl: Function, 7 | * WOT_URL: string 8 | * }} 9 | * @constructor 10 | */ 11 | function Wot() { 12 | const wotUrlScorecardTemplate = 'https://link.adtidy.org/forward.html?action=wot_scorecard&from=main_menu&app=assistant&domain='; 13 | const WOT_URL = 'https://link.adtidy.org/forward.html?action=wot&from=main_menu&app=assistant'; 14 | let wotData = null; 15 | 16 | const registerWotEventHandler = () => { 17 | const wotDataCb = (data) => { 18 | wotData = data; 19 | }; 20 | if (window.WotData) { 21 | wotData = window.WotData; 22 | } else { 23 | window.WotData = wotDataCb; 24 | } 25 | }; 26 | 27 | const getWotData = () => wotData; 28 | 29 | const getWotScorecardUrl = (url) => `${wotUrlScorecardTemplate}${url}`; 30 | 31 | return { 32 | registerWotEventHandler, 33 | getWotData, 34 | getWotScorecardUrl, 35 | WOT_URL, 36 | }; 37 | } 38 | 39 | const wot = new Wot(); 40 | 41 | export default wot; 42 | -------------------------------------------------------------------------------- /tests/assistant.test.js: -------------------------------------------------------------------------------- 1 | /* global QUnit */ 2 | import '../src/index'; // import assistant into tests 3 | 4 | const { test, module } = QUnit; 5 | 6 | module('Assistant main'); 7 | 8 | test('Should be true', (assert) => { 9 | assert.ok(true, 'assistant didn\'t cause errors on the page'); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | QUnit extension boilerplate 7 | 8 | 9 | 10 | 11 |
12 |
13 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/index.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import './assistant.test'; 3 | -------------------------------------------------------------------------------- /tests/puppeteer.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | const { runQunitPuppeteer, printFailedTests, printResultSummary } = require('node-qunit-puppeteer'); 3 | const path = require('path'); 4 | 5 | const qunitArgs = { 6 | targetUrl: `file://${path.resolve(__dirname, './dist/index.html')}`, 7 | timeout: 10000, 8 | puppeteerArgs: [ 9 | '--no-sandbox', 10 | '--allow-file-access-from-files', 11 | ], 12 | }; 13 | 14 | runQunitPuppeteer(qunitArgs) 15 | .then((result) => { 16 | printResultSummary(result, console); 17 | if (result.stats.failed > 0) { 18 | printFailedTests(result, console); 19 | } 20 | }) 21 | .then(() => { 22 | }) 23 | .catch((ex) => { 24 | console.error(ex); 25 | process.exit(1); 26 | }); 27 | -------------------------------------------------------------------------------- /tests/webpack.test.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 5 | 6 | module.exports = { 7 | mode: 'development', 8 | entry: path.resolve(__dirname, './index.test.js'), 9 | output: { 10 | path: path.resolve(__dirname, './dist'), 11 | filename: 'bundle.test.js', 12 | }, 13 | devtool: 'inline-source-map', 14 | optimization: { 15 | minimize: false, 16 | }, 17 | module: { 18 | rules: [ 19 | { 20 | test: /\.svg$/, 21 | exclude: /node_modules/, 22 | loader: 'url-loader', 23 | }, 24 | { 25 | test: /\.html$/, 26 | exclude: /node_modules/, 27 | loader: 'html-loader', 28 | options: { 29 | minimize: true, 30 | attributes: false, 31 | }, 32 | }, 33 | { 34 | test: /\.less$/, 35 | exclude: /node_modules/, 36 | use: ['to-string-loader', 'css-loader', 'less-loader'], 37 | }, 38 | { 39 | test: /\.js$/, 40 | exclude: /node_modules/, 41 | use: { 42 | loader: 'babel-loader', 43 | }, 44 | }, 45 | ], 46 | }, 47 | plugins: [ 48 | new CleanWebpackPlugin(), 49 | new CopyWebpackPlugin({ 50 | patterns: [ 51 | { 52 | from: 'node_modules/qunit/qunit/qunit.js', 53 | }, 54 | { 55 | from: 'node_modules/qunit/qunit/qunit.css', 56 | }, 57 | ], 58 | }), 59 | new HtmlWebpackPlugin({ 60 | template: path.resolve(__dirname, 'index.html'), 61 | }), 62 | ], 63 | }; 64 | -------------------------------------------------------------------------------- /types/assistant.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@adguard/assistant" { 2 | interface Callback { 3 | (ruleText: string): void 4 | } 5 | 6 | interface Assistant { 7 | start: (element: HTMLElement | null, callback: Callback) => void, 8 | close: () => void, 9 | } 10 | 11 | export function adguardAssistant(): Assistant; 12 | } 13 | --------------------------------------------------------------------------------