├── .browserslistrc ├── .eslintrc.js ├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ ├── chrome-token.yml │ ├── codeql-analysis.yml │ ├── comment-pr.yml │ └── publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public ├── _locales │ ├── be │ │ └── messages.json │ ├── ca │ │ └── messages.json │ ├── cs │ │ └── messages.json │ ├── da │ │ └── messages.json │ ├── de │ │ └── messages.json │ ├── el │ │ └── messages.json │ ├── en │ │ └── messages.json │ ├── es │ │ └── messages.json │ ├── es_419 │ │ └── messages.json │ ├── et │ │ └── messages.json │ ├── fi │ │ └── messages.json │ ├── fr │ │ └── messages.json │ ├── gl │ │ └── messages.json │ ├── he │ │ └── messages.json │ ├── hu │ │ └── messages.json │ ├── id │ │ └── messages.json │ ├── it │ │ └── messages.json │ ├── ja │ │ └── messages.json │ ├── ko │ │ └── messages.json │ ├── lzh │ │ └── messages.json │ ├── mag │ │ └── messages.json │ ├── nb_NO │ │ └── messages.json │ ├── nl │ │ └── messages.json │ ├── pl │ │ └── messages.json │ ├── pt-PT │ │ └── messages.json │ ├── pt_BR │ │ └── messages.json │ ├── pt_PT │ │ └── messages.json │ ├── ru │ │ └── messages.json │ ├── sl │ │ └── messages.json │ ├── sv │ │ └── messages.json │ ├── th │ │ └── messages.json │ ├── tr │ │ └── messages.json │ ├── uk │ │ └── messages.json │ ├── zh_CN │ │ └── messages.json │ └── zh_Hant │ │ └── messages.json ├── assets │ ├── iconfont │ │ ├── MaterialIcons-Regular.woff2 │ │ └── material-icons.css │ ├── icons │ │ ├── 24 │ │ │ └── userscript-no-icon.png │ │ ├── 48 │ │ │ ├── icon-beta.png │ │ │ └── icon.png │ │ ├── 96 │ │ │ ├── icon-beta.png │ │ │ └── icon.png │ │ ├── 128 │ │ │ ├── icon-beta.png │ │ │ └── icon.png │ │ ├── extension.svg │ │ ├── favicon.ico │ │ └── icon.svg │ ├── images │ │ ├── IITC-black-horizontally.svg │ │ └── IITC-black-horizontally.webp │ ├── noto │ │ ├── NotoSansMono-Regular.woff2 │ │ └── noto-font.css │ └── roboto │ │ ├── Roboto-Regular.woff2 │ │ └── roboto-font.css ├── browser-extension.html └── index.html ├── safari ├── IITC Button.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ ├── iOS.xcscheme │ │ └── macOS.xcscheme ├── Safari Extension │ ├── Shared │ │ └── SafariWebExtensionHandler.swift │ ├── iOS │ │ └── Info.plist │ └── macOS │ │ ├── Info.plist │ │ └── Safari Extension.entitlements ├── Scripts │ └── pre-build.sh ├── Shared │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── iOS_AppStore_1024.png │ │ │ ├── iOS_iPadApp_76.png │ │ │ ├── iOS_iPadApp_76@2x.png │ │ │ ├── iOS_iPadNotifications_20.png │ │ │ ├── iOS_iPadNotifications_20@2x.png │ │ │ ├── iOS_iPadProApp_83.5@2x.png │ │ │ ├── iOS_iPadSettings_29.png │ │ │ ├── iOS_iPadSettings_29@2x.png │ │ │ ├── iOS_iPadSpotlight_40.png │ │ │ ├── iOS_iPadSpotlight_40@2x.png │ │ │ ├── iOS_iPhoneApp_60@2x.png │ │ │ ├── iOS_iPhoneApp_60@3x.png │ │ │ ├── iOS_iPhoneNotifications_20@2x.png │ │ │ ├── iOS_iPhoneNotifications_20@3x.png │ │ │ ├── iOS_iPhoneSettings_29@2x.png │ │ │ ├── iOS_iPhoneSettings_29@3x.png │ │ │ ├── iOS_iPhoneSpotlight_40@2x.png │ │ │ ├── iOS_iPhoneSpotlight_40@3x.png │ │ │ ├── macOS_128.png │ │ │ ├── macOS_128@2x.png │ │ │ ├── macOS_16.png │ │ │ ├── macOS_16@2x.png │ │ │ ├── macOS_256.png │ │ │ ├── macOS_256@2x.png │ │ │ ├── macOS_32.png │ │ │ ├── macOS_32@2x.png │ │ │ ├── macOS_512.png │ │ │ └── macOS_512@2x.png │ │ └── Contents.json │ └── IITCButtonApp.swift ├── iOS │ ├── Info.plist │ └── Views │ │ ├── ContentView.swift │ │ └── Helper.swift └── macOS │ ├── IITCButtonCommands.swift │ ├── Info.plist │ ├── Views │ └── ContentView.swift │ └── macOS.entitlements ├── src ├── background │ ├── background.js │ ├── injector.js │ ├── intel.js │ ├── requests.js │ ├── utils.js │ └── xhr-fallback.js ├── content-scripts │ ├── bridge-manager.js │ ├── loader.js │ ├── storage-bridge.js │ ├── utils.js │ └── xhr-bridge.js ├── i18n.js ├── jsview │ ├── App.vue │ ├── Code.vue │ ├── Header.vue │ ├── highlightjs-lineToDiv.js │ └── main.js ├── manifest.json ├── popup │ ├── App.vue │ ├── components │ │ ├── Alert.vue │ │ ├── Header.vue │ │ ├── Hr.vue │ │ ├── InputCustomServer.vue │ │ ├── Message.vue │ │ ├── SectionMainMenu │ │ │ ├── PluginList │ │ │ │ ├── NoData.vue │ │ │ │ ├── Plugin.vue │ │ │ │ ├── PluginList.vue │ │ │ │ └── Title.vue │ │ │ ├── ProgressBar.vue │ │ │ ├── SearchBar.vue │ │ │ ├── SectionMainMenu.vue │ │ │ ├── Tags │ │ │ │ ├── Tag.vue │ │ │ │ └── Tags.vue │ │ │ ├── Title.vue │ │ │ └── ToggleIITC.vue │ │ ├── SectionOptions.vue │ │ ├── UpdateCheckIntervalSelector.vue │ │ └── mixins.js │ ├── data.js │ ├── main.js │ └── search.js ├── settings │ ├── App.vue │ ├── Header.vue │ ├── add │ │ ├── BlockDrop.vue │ │ ├── BlockURL.vue │ │ └── Main.vue │ ├── backup │ │ ├── Main.vue │ │ └── utils.js │ ├── debug │ │ └── Main.vue │ ├── main.js │ └── utils.js ├── strToBase64.js ├── userscripts │ ├── env.js │ ├── gm-api.js │ ├── utils.js │ └── wrapper.js ├── uuid.js └── xhr-sandbox.js └── vue.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | webextensions: true, 6 | }, 7 | extends: [ 8 | "plugin:vue/essential", 9 | "eslint:recommended", 10 | "plugin:prettier/recommended", 11 | ], 12 | parserOptions: { 13 | parser: "@babel/eslint-parser", 14 | }, 15 | rules: { 16 | "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", 17 | "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", 18 | "vue/multi-word-component-names": "off", 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ['https://t.me/iitc_news/58', 'https://iitc.app/donate.html'] 2 | -------------------------------------------------------------------------------- /.github/workflows/chrome-token.yml: -------------------------------------------------------------------------------- 1 | name: "fetch-chrome-webstore-access-token" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 3 2 * *' # At 03:00 on day-of-month 2 6 | 7 | jobs: 8 | check-secret: 9 | runs-on: ubuntu-latest 10 | outputs: 11 | secrets: ${{ steps.key-check.outputs.defined }} 12 | steps: 13 | - name: Check for Secret availability 14 | id: key-check 15 | shell: bash 16 | run: | 17 | if [ "${{ secrets.CHROME_CLIENT_ID }}" != '' ]; then 18 | echo "defined=true" >> $GITHUB_OUTPUT; 19 | else 20 | echo "defined=false" >> $GITHUB_OUTPUT; 21 | fi 22 | 23 | fetchToken: 24 | runs-on: ubuntu-latest 25 | needs: [check-secret] 26 | if: needs.check-secret.outputs.secrets == 'true' 27 | 28 | steps: 29 | - uses: cardinalby/google-api-fetch-token-action@v1 30 | with: 31 | clientId: ${{ secrets.CHROME_CLIENT_ID }} 32 | clientSecret: ${{ secrets.CHROME_CLIENT_SECRET }} 33 | refreshToken: ${{ secrets.CHROME_REFRESH_TOKEN }} 34 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '37 12 * * 4' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.github/workflows/comment-pr.yml: -------------------------------------------------------------------------------- 1 | name: Add artifact links to pull request 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Build IITC Button"] 6 | types: [completed] 7 | workflow_dispatch: 8 | 9 | jobs: 10 | comment-links: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Get artifact list from API 14 | id: get-artifacts 15 | uses: actions/github-script@v7 16 | with: 17 | github-token: ${{ secrets.GITHUB_TOKEN }} 18 | script: | 19 | const runId = ${{ github.event.workflow_run.id }}; 20 | const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ 21 | owner: context.repo.owner, 22 | repo: context.repo.repo, 23 | run_id: runId 24 | }); 25 | artifacts.data.artifacts.forEach((artifact, index) => { 26 | const nameVar = `artifact${index + 1}`; 27 | const displayNameVar = `displayArtifact${index + 1}`; 28 | const urlVar = `url${index + 1}`; 29 | let displayName = artifact.name.endsWith('-artifacts') ? `**${artifact.name}**` : artifact.name; 30 | core.exportVariable(nameVar, artifact.name); 31 | core.exportVariable(displayNameVar, displayName); 32 | core.exportVariable(urlVar, `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/artifacts/${artifact.id}`); 33 | }); 34 | core.exportVariable("firstArtifactId", artifacts.data.artifacts[0].id); 35 | 36 | - name: Download first artifact 37 | uses: actions/github-script@v7 38 | with: 39 | github-token: ${{ secrets.GITHUB_TOKEN }} 40 | script: | 41 | let download = await github.rest.actions.downloadArtifact({ 42 | owner: context.repo.owner, 43 | repo: context.repo.repo, 44 | artifact_id: ${{ env.firstArtifactId }}, 45 | archive_format: 'zip', 46 | }); 47 | let fs = require('fs'); 48 | fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/build.zip`, Buffer.from(download.data)); 49 | 50 | - name: Extract PR number from artifact 51 | run: | 52 | unzip build.zip 53 | if [ -f ".metadata/pr_number" ]; then 54 | PR_NUMBER=$(cat .metadata/pr_number) 55 | echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV 56 | fi 57 | 58 | - name: Comment with artifact links for PR 59 | uses: marocchino/sticky-pull-request-comment@v2 60 | if: ${{ env.PR_NUMBER != '' }} 61 | with: 62 | header: pr_artifacts 63 | number: ${{ env.PR_NUMBER }} 64 | message: | 65 | Build completed successfully. Below are the download links for the build artifacts: 66 | 67 | | Artifact Name | Download Link | 68 | | ------------- | ------------- | 69 | | ${{ env.displayArtifact1 }} | [Download](${{ env.url1 }}) | 70 | | ${{ env.displayArtifact2 }} | [Download](${{ env.url2 }}) | 71 | | ${{ env.displayArtifact3 }} | [Download](${{ env.url3 }}) | 72 | | ${{ env.displayArtifact4 }} | [Download](${{ env.url4 }}) | 73 | | ${{ env.displayArtifact5 }} | [Download](${{ env.url5 }}) | 74 | 75 | Artifacts will only be retained for 90 days. 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | 24 | # Vue Browser Extension Output 25 | *.pem 26 | *.pub 27 | *.zip 28 | /artifacts 29 | 30 | # Xcode 31 | ## User settings 32 | xcuserdata/ 33 | 34 | ## Info.plist Preprocessor 35 | info-plist-preprocessor.h -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"], 3 | }; 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iitc-button", 3 | "version": "3.1.0", 4 | "repository": "https://github.com/IITC-CE/IITC-Button.git", 5 | "license": "GPLv3", 6 | "private": true, 7 | "scripts": { 8 | "build_mv2": "export MANIFEST_VERSION=2 && vue-cli-service build", 9 | "build_mv3_firefox": "export MANIFEST_VERSION=3 && export BROWSER=firefox && vue-cli-service build", 10 | "build_mv3_chrome": "export MANIFEST_VERSION=3 && export BROWSER=chrome && vue-cli-service build", 11 | "build_mv3_safari": "export MANIFEST_VERSION=3 && export BROWSER=safari && vue-cli-service build", 12 | "lint": "vue-cli-service lint" 13 | }, 14 | "dependencies": { 15 | "@highlightjs/vue-plugin": "^1.0.2", 16 | "core-js": "^3.8.3", 17 | "highlight.js": "^10.7.3", 18 | "jszip": "^3.10.1", 19 | "lib-iitc-manager": "^1.10.1", 20 | "scored-fuzzysearch": "^1.0.5", 21 | "vue": "^2.6.14" 22 | }, 23 | "devDependencies": { 24 | "@babel/core": "^7.12.16", 25 | "@babel/eslint-parser": "^7.12.16", 26 | "@vue/cli-plugin-babel": "~5.0.0", 27 | "@vue/cli-plugin-eslint": "~5.0.0", 28 | "@vue/cli-service": "~5.0.0", 29 | "@vue/eslint-config-prettier": "^6.0.0", 30 | "eslint": "^7.32.0", 31 | "eslint-config-prettier": "^8.3.0", 32 | "eslint-plugin-vue": "^8.0.3", 33 | "prettier": "^2.4.1", 34 | "publish-browser-extension": "^2.1.3", 35 | "vue-cli-plugin-browser-extension": "^0.26.1", 36 | "vue-template-compiler": "^2.6.14", 37 | "webextension-polyfill": "^0.10.0" 38 | }, 39 | "readme": "README.md" 40 | } 41 | -------------------------------------------------------------------------------- /public/_locales/be/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "iitcButtonOptions": { 3 | "message": "Налады IITC-CE кнопкі" 4 | }, 5 | "updateNow": { 6 | "message": "Абнавіць зараз" 7 | }, 8 | "release": { 9 | "message": "Выпуск" 10 | }, 11 | "updateFrequency": { 12 | "message": "Праверка абнаўленняў IITC-CE" 13 | }, 14 | "every6hours": { 15 | "message": "Кожныя шэсць гадзін" 16 | }, 17 | "every12hours": { 18 | "message": "Кожныя дванаццаць гадзін" 19 | }, 20 | "everyDay": { 21 | "message": "Кожны дзень" 22 | }, 23 | "everyWeek": { 24 | "message": "Кожны тыдзень" 25 | }, 26 | "lang": { 27 | "message": "by" 28 | }, 29 | "titleDefault": { 30 | "message": "Адчыніць Intel", 31 | "description": "Title for the button at the application panel" 32 | }, 33 | "needRebootIntel": { 34 | "message": "Змены будуць ужыты пасля перазагрузкі Intel" 35 | }, 36 | "updateInProgress": { 37 | "message": "Абнаўляецца…" 38 | }, 39 | "noData": { 40 | "message": "Няма дадзеных" 41 | }, 42 | "addExternalPlugin": { 43 | "message": "Дабавіць знешнюю ўбудову" 44 | }, 45 | "iitcHomePage": { 46 | "message": "Хатняя старонка" 47 | }, 48 | "changesApplied": { 49 | "message": "Змены былі ўжытыя" 50 | }, 51 | "extDescription": { 52 | "message": "Лёгкі запуск IITC.", 53 | "description": "Description of the extension" 54 | }, 55 | "choosingUpdateChannel": { 56 | "message": "Выбар канала абнаўлення IITC-CE" 57 | }, 58 | "updateExternalFrequency": { 59 | "message": "Праверка абнаўленняў знешніх убудоў" 60 | }, 61 | "anyChannel": { 62 | "message": "Усе каналы" 63 | }, 64 | "loading": { 65 | "message": "Чакайце…" 66 | }, 67 | "install": { 68 | "message": "Усталяваць" 69 | }, 70 | "clickInstallPlugin": { 71 | "message": "Гэта плагін IITC. Націсніце \"Усталяваць\", каб пачаць выкарыстоўваць яго." 72 | }, 73 | "willBeOverwrittenByNewPlugin": { 74 | "message": "Плагін з тым самым ID будзе заменены новым плагінам." 75 | }, 76 | "dropJSHereOrClick": { 77 | "message": "Перацягніце .js файлы сюды альбо націсніце, каб загрузіць." 78 | }, 79 | "external": { 80 | "message": "Знешні" 81 | }, 82 | "serverNotAvailableRetry": { 83 | "message": "Сервер недаступны. Паўтарыце спробу пасля $NUM$ сек", 84 | "placeholders": { 85 | "num": { 86 | "content": "$1", 87 | "example": "5" 88 | } 89 | } 90 | }, 91 | "or": { 92 | "message": "ці" 93 | }, 94 | "errorReadingFile": { 95 | "message": "Адбылася памылка падчас чытання файла $PLUGIN_NAME$.", 96 | "placeholders": { 97 | "plugin_name": { 98 | "content": "$1", 99 | "example": "Awesome plugin" 100 | } 101 | } 102 | }, 103 | "addressNotAvailable": { 104 | "message": "Адрас недаступны" 105 | }, 106 | "addedUserScriptTo": { 107 | "message": "$PLUGIN_NAME$ быў дададзены ў катэгорыю \"$PLUGIN_CATEGORY$\".", 108 | "placeholders": { 109 | "plugin_name": { 110 | "content": "$1", 111 | "example": "Awesome plugin" 112 | }, 113 | "plugin_category": { 114 | "content": "$2", 115 | "example": "Draw" 116 | } 117 | } 118 | }, 119 | "notValidUserScript": { 120 | "message": "$PLUGIN_NAME$ не з'яўляецца сапраўдным сцэнарыям карыстальніка.", 121 | "placeholders": { 122 | "plugin_name": { 123 | "content": "$1", 124 | "example": "Awesome plugin" 125 | } 126 | } 127 | }, 128 | "pluginDelete": { 129 | "message": "Выдаліць" 130 | }, 131 | "pluginSave": { 132 | "message": "Захаваць" 133 | }, 134 | "openSupport": { 135 | "message": "Адкрыць старонку падтрымкі:" 136 | }, 137 | "badgeOverride": { 138 | "message": "Перавызначыць" 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /public/_locales/ca/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "lang": { 3 | "message": "ca" 4 | }, 5 | "titleDefault": { 6 | "message": "Obre l'Intel", 7 | "description": "Title for the button at the application panel" 8 | }, 9 | "serverNotAvailableRetry": { 10 | "message": "El servidor no està disponible. Torna-ho a provar després de $NUM$ segons", 11 | "placeholders": { 12 | "num": { 13 | "content": "$1", 14 | "example": "5" 15 | } 16 | } 17 | }, 18 | "updateInProgress": { 19 | "message": "S'està actualitzant…" 20 | }, 21 | "needRebootIntel": { 22 | "message": "Els canvis s'aplicaran després de reiniciar l'Intel." 23 | }, 24 | "extDescription": { 25 | "message": "Inicia IITC per Ingress Intel amb facilitat.", 26 | "description": "Description of the extension" 27 | }, 28 | "external": { 29 | "message": "Extern" 30 | }, 31 | "loading": { 32 | "message": "Carregant…" 33 | }, 34 | "install": { 35 | "message": "Instal·la" 36 | }, 37 | "clickInstallPlugin": { 38 | "message": "Això és un plugin d'IITC. Selecciona “Instal·la” per fer-lo servir." 39 | }, 40 | "willBeOverwrittenByNewPlugin": { 41 | "message": "Els plugins amb el mateix ID es sobreescriuran pel nou plugin." 42 | }, 43 | "dropJSHereOrClick": { 44 | "message": "Arrossega fitxers .js aquí o fes clic per carregar-los." 45 | }, 46 | "or": { 47 | "message": "o" 48 | }, 49 | "errorReadingFile": { 50 | "message": "S'ha trobat un error en llegir el fitxer $PLUGIN_NAME$.", 51 | "placeholders": { 52 | "plugin_name": { 53 | "content": "$1", 54 | "example": "Awesome plugin" 55 | } 56 | } 57 | }, 58 | "addressNotAvailable": { 59 | "message": "L'adreça no està disponible" 60 | }, 61 | "notValidUserScript": { 62 | "message": "$PLUGIN_NAME$ no és un UserScript vàlid.", 63 | "placeholders": { 64 | "plugin_name": { 65 | "content": "$1", 66 | "example": "Awesome plugin" 67 | } 68 | } 69 | }, 70 | "pluginDelete": { 71 | "message": "Elimina" 72 | }, 73 | "pluginSave": { 74 | "message": "Desa" 75 | }, 76 | "openSupport": { 77 | "message": "Obre la pàgina de suport:" 78 | }, 79 | "anyChannel": { 80 | "message": "Qualsevol canal" 81 | }, 82 | "everyWeek": { 83 | "message": "Cada setmana" 84 | }, 85 | "everyDay": { 86 | "message": "Cada dia" 87 | }, 88 | "every12hours": { 89 | "message": "Cada 12 hores" 90 | }, 91 | "every6hours": { 92 | "message": "Cada 6 hores" 93 | }, 94 | "updateExternalFrequency": { 95 | "message": "Comprova actualitzacions de plugins externs" 96 | }, 97 | "updateFrequency": { 98 | "message": "Comprova actualitzacions d'IITC-CE" 99 | }, 100 | "release": { 101 | "message": "Estable" 102 | }, 103 | "choosingUpdateChannel": { 104 | "message": "Tria un canal d'actualitzacions d'IITC-CE" 105 | }, 106 | "updateNow": { 107 | "message": "actualitza ara" 108 | }, 109 | "iitcButtonOptions": { 110 | "message": "Opcions del Botó IITC-CE" 111 | }, 112 | "iitcHomePage": { 113 | "message": "Pàgina d'inici" 114 | }, 115 | "addExternalPlugin": { 116 | "message": "Afegeix un plugin extern" 117 | }, 118 | "noData": { 119 | "message": "Sense dades" 120 | }, 121 | "changesApplied": { 122 | "message": "Els canvis s'han aplicat" 123 | }, 124 | "addedUserScriptTo": { 125 | "message": "$PLUGIN_NAME$ ha estat afegit a la categoria \"$PLUGIN_CATEGORY$\".", 126 | "placeholders": { 127 | "plugin_name": { 128 | "content": "$1", 129 | "example": "Awesome plugin" 130 | }, 131 | "plugin_category": { 132 | "content": "$2", 133 | "example": "Draw" 134 | } 135 | } 136 | }, 137 | "badgeOverride": { 138 | "message": "substituir" 139 | }, 140 | "beta": { 141 | "message": "Beta" 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /public/_locales/da/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "beta": { 3 | "message": "Beta" 4 | }, 5 | "updateNow": { 6 | "message": "Updater nu" 7 | }, 8 | "iitcButtonOptions": { 9 | "message": "IITC-CE Knap indstillinger" 10 | }, 11 | "addExternalPlugin": { 12 | "message": "Tilføj eksternt plugin" 13 | }, 14 | "noData": { 15 | "message": "Ingen data" 16 | }, 17 | "updateInProgress": { 18 | "message": "Opdaterer…" 19 | }, 20 | "titleDefault": { 21 | "message": "Åben Intel", 22 | "description": "Title for the button at the application panel" 23 | }, 24 | "lang": { 25 | "message": "da" 26 | }, 27 | "serverNotAvailableRetry": { 28 | "message": "Serveren er ikke tilgængelig. Prøv igen efter $NUM$ sekunder", 29 | "placeholders": { 30 | "num": { 31 | "content": "$1", 32 | "example": "5" 33 | } 34 | } 35 | }, 36 | "dropJSHereOrClick": { 37 | "message": "Træk .js filerne her, eller klik for at uploade." 38 | }, 39 | "errorReadingFile": { 40 | "message": "Der opstod en fejl under læsning af $PLUGIN_NAME$-fil.", 41 | "placeholders": { 42 | "plugin_name": { 43 | "content": "$1", 44 | "example": "Awesome plugin" 45 | } 46 | } 47 | }, 48 | "addedUserScriptTo": { 49 | "message": "$PLUGIN_NAME$ er føjet til kategorien \"$PLUGIN_CATEGORY$\".", 50 | "placeholders": { 51 | "plugin_name": { 52 | "content": "$1", 53 | "example": "Awesome plugin" 54 | }, 55 | "plugin_category": { 56 | "content": "$2", 57 | "example": "Draw" 58 | } 59 | } 60 | }, 61 | "notValidUserScript": { 62 | "message": "$PLUGIN_NAME$ er ikke et gyldigt UserScript.", 63 | "placeholders": { 64 | "plugin_name": { 65 | "content": "$1", 66 | "example": "Awesome plugin" 67 | } 68 | } 69 | }, 70 | "external": { 71 | "message": "Ekstern" 72 | }, 73 | "loading": { 74 | "message": "Indlæser…" 75 | }, 76 | "install": { 77 | "message": "Installere" 78 | }, 79 | "clickInstallPlugin": { 80 | "message": "Dette er et IITC-plugin. Klik på installer for at begynde at bruge det." 81 | }, 82 | "willBeOverwrittenByNewPlugin": { 83 | "message": "Plugin med samme ID overskrives af det nye plugin." 84 | }, 85 | "or": { 86 | "message": "Eller" 87 | }, 88 | "addressNotAvailable": { 89 | "message": "Adressen er ikke tilgængelig" 90 | }, 91 | "pluginDelete": { 92 | "message": "Slet" 93 | }, 94 | "pluginSave": { 95 | "message": "Gem" 96 | }, 97 | "openSupport": { 98 | "message": "Åbn supportsiden:" 99 | }, 100 | "badgeOverride": { 101 | "message": "tilsidesætte" 102 | }, 103 | "anyChannel": { 104 | "message": "Enhver kanal" 105 | }, 106 | "everyWeek": { 107 | "message": "Hver uge" 108 | }, 109 | "everyDay": { 110 | "message": "Hver dag" 111 | }, 112 | "every12hours": { 113 | "message": "Hver 12 time" 114 | }, 115 | "every6hours": { 116 | "message": "Hver 6 time" 117 | }, 118 | "updateExternalFrequency": { 119 | "message": "Eksterne plugins kontrollerer for opdateringer" 120 | }, 121 | "updateFrequency": { 122 | "message": "IITC-CE kontrollerer for opdateringer" 123 | }, 124 | "release": { 125 | "message": "Udgivelses dato" 126 | }, 127 | "choosingUpdateChannel": { 128 | "message": "Valg af en IITC-CE opdateringskanal" 129 | }, 130 | "iitcHomePage": { 131 | "message": "Hjemmeside" 132 | }, 133 | "changesApplied": { 134 | "message": "Ændringer blev anvendt" 135 | }, 136 | "needRebootIntel": { 137 | "message": "Ændringer vil blive aktiveret efter Intel er genstartet" 138 | }, 139 | "extDescription": { 140 | "message": "Start IITC for Ingress med lethed.", 141 | "description": "Description of the extension" 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /public/_locales/el/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": { 3 | "message": "Σταθερή έκδοση" 4 | }, 5 | "lang": { 6 | "message": "el" 7 | }, 8 | "external": { 9 | "message": "\tΕξωτερικά" 10 | }, 11 | "serverNotAvailableRetry": { 12 | "message": "Ο διακομιστής δεν είναι διαθέσιμος. Δοκιμάστε ξανά μετά από $NUM$ δευτερόλεπτα", 13 | "placeholders": { 14 | "num": { 15 | "content": "$1", 16 | "example": "5" 17 | } 18 | } 19 | }, 20 | "loading": { 21 | "message": "Φόρτωση…" 22 | }, 23 | "install": { 24 | "message": "Εγκατάσταση" 25 | }, 26 | "clickInstallPlugin": { 27 | "message": "Αυτό είναι ένα πρόσθετο IITC. Κάντε κλικ στην εγκατάσταση για να αρχίσετε να το χρησιμοποιείτε." 28 | }, 29 | "willBeOverwrittenByNewPlugin": { 30 | "message": "Πρόσθετα με την ίδια Ταυτότητα θα αντικατασταθούν από το νέο πρόσθετο." 31 | }, 32 | "dropJSHereOrClick": { 33 | "message": "Τοποθετήστε αρχεία .js εδώ ή κάντε κλικ για μεταφόρτωση." 34 | }, 35 | "or": { 36 | "message": "ή" 37 | }, 38 | "errorReadingFile": { 39 | "message": "Παρουσιάστηκε σφάλμα κατά την ανάγνωση του αρχείου $PLUGIN_NAME$.", 40 | "placeholders": { 41 | "plugin_name": { 42 | "content": "$1", 43 | "example": "Awesome plugin" 44 | } 45 | } 46 | }, 47 | "addressNotAvailable": { 48 | "message": "Η διεύθυνση δεν είναι διαθέσιμη" 49 | }, 50 | "notValidUserScript": { 51 | "message": "Το $PLUGIN_NAME$ δεν είναι έγκυρο σενάριο χρήστη.", 52 | "placeholders": { 53 | "plugin_name": { 54 | "content": "$1", 55 | "example": "Awesome plugin" 56 | } 57 | } 58 | }, 59 | "pluginDelete": { 60 | "message": "Διαγραφή" 61 | }, 62 | "pluginSave": { 63 | "message": "Αποθήκευση" 64 | }, 65 | "openSupport": { 66 | "message": "Άνοιγμα σελίδας υποστήριξης:" 67 | }, 68 | "anyChannel": { 69 | "message": "Οποιοδήποτε κανάλι" 70 | }, 71 | "everyWeek": { 72 | "message": "Κάθε Εβδομάδα" 73 | }, 74 | "everyDay": { 75 | "message": "Κάθε Ημέρα" 76 | }, 77 | "every12hours": { 78 | "message": "Κάθε 12 Ώρες" 79 | }, 80 | "every6hours": { 81 | "message": "Κάθε 6 Ώρες" 82 | }, 83 | "updateExternalFrequency": { 84 | "message": "Εξωτερικά πρόσθετα έλεγχος για ενημερώσεις" 85 | }, 86 | "updateFrequency": { 87 | "message": "IITC-CE έλεγχος για ενημερώσεις" 88 | }, 89 | "choosingUpdateChannel": { 90 | "message": "Επιλογή καναλιού ενημέρωσης IITC-CE" 91 | }, 92 | "updateNow": { 93 | "message": "ενημέρωση τώρα" 94 | }, 95 | "iitcButtonOptions": { 96 | "message": "IITC-CE επιλογές" 97 | }, 98 | "iitcHomePage": { 99 | "message": "Αρχική σελίδα" 100 | }, 101 | "addExternalPlugin": { 102 | "message": "Εισαγωγή εξωτερικού πρόσθετου" 103 | }, 104 | "noData": { 105 | "message": "Χωρίς δεδομένα" 106 | }, 107 | "changesApplied": { 108 | "message": "Οι αλλαγές εφαρμόστηκαν" 109 | }, 110 | "updateInProgress": { 111 | "message": "Αναβάθμιση σε εξέλιξη…" 112 | }, 113 | "needRebootIntel": { 114 | "message": "Οι αλλαγές θα εφαρμοστούν μετά την επανεκκίνηση του Intel" 115 | }, 116 | "titleDefault": { 117 | "message": "Άνοιγμα Χάρτη", 118 | "description": "Title for the button at the application panel" 119 | }, 120 | "extDescription": { 121 | "message": "Ξεκινήστε το IITC για τον χάρτη Ingress Intel με ευκολία.", 122 | "description": "Description of the extension" 123 | }, 124 | "addedUserScriptTo": { 125 | "message": "Το $PLUGIN_NAME$ έχει προστεθεί στην κατηγορία \"$PLUGIN_CATEGORY$\".", 126 | "placeholders": { 127 | "plugin_name": { 128 | "content": "$1", 129 | "example": "Awesome plugin" 130 | }, 131 | "plugin_category": { 132 | "content": "$2", 133 | "example": "Draw" 134 | } 135 | } 136 | }, 137 | "badgeOverride": { 138 | "message": "παρακάμψετε" 139 | }, 140 | "beta": { 141 | "message": "Beta" 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /public/_locales/es_419/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "serverNotAvailableRetry": { 3 | "message": "El servidor no está disponible. Reintentando en $NUM$ segundos", 4 | "placeholders": { 5 | "num": { 6 | "content": "$1", 7 | "example": "5" 8 | } 9 | } 10 | }, 11 | "every12hours": { 12 | "message": "Cada 12 horas" 13 | }, 14 | "everyDay": { 15 | "message": "Diariamente" 16 | }, 17 | "everyWeek": { 18 | "message": "Semanalmente" 19 | }, 20 | "anyChannel": { 21 | "message": "Cualquier medio" 22 | }, 23 | "pluginSave": { 24 | "message": "Guardar" 25 | }, 26 | "updateFrequency": { 27 | "message": "IITC-CE buscará actualizaciones" 28 | }, 29 | "updateExternalFrequency": { 30 | "message": "Buscar actualizaciones de los plugin externos" 31 | }, 32 | "every6hours": { 33 | "message": "Cada 6 horas" 34 | }, 35 | "openSupport": { 36 | "message": "Abrir página de soporte:" 37 | }, 38 | "titleDefault": { 39 | "message": "Abrir Intel", 40 | "description": "Title for the button at the application panel" 41 | }, 42 | "extDescription": { 43 | "message": "Inicie IITC para Ingress Intel con facilidad.", 44 | "description": "Description of the extension" 45 | }, 46 | "lang": { 47 | "message": "es_419" 48 | }, 49 | "needRebootIntel": { 50 | "message": "Los cambios se aplicarán después de reiniciar Intel" 51 | }, 52 | "updateInProgress": { 53 | "message": "Actualización en curso…" 54 | }, 55 | "changesApplied": { 56 | "message": "Se aplicaron los cambios" 57 | }, 58 | "noData": { 59 | "message": "Sin datos" 60 | }, 61 | "addExternalPlugin": { 62 | "message": "Adicionar plugin externo" 63 | }, 64 | "iitcHomePage": { 65 | "message": "Página de inicio" 66 | }, 67 | "iitcButtonOptions": { 68 | "message": "Opciones del botón IITC-CE" 69 | }, 70 | "updateNow": { 71 | "message": "actualizar ahora" 72 | }, 73 | "choosingUpdateChannel": { 74 | "message": "Elija un canal de actualización de IITC-CE" 75 | }, 76 | "release": { 77 | "message": "Versión definitiva" 78 | }, 79 | "pluginDelete": { 80 | "message": "Borrar" 81 | }, 82 | "notValidUserScript": { 83 | "message": "$PLUGIN_NAME$ no es un plugin válido.", 84 | "placeholders": { 85 | "plugin_name": { 86 | "content": "$1", 87 | "example": "Awesome plugin" 88 | } 89 | } 90 | }, 91 | "addressNotAvailable": { 92 | "message": "Dirección no disponible" 93 | }, 94 | "errorReadingFile": { 95 | "message": "Ocurrió un error leyendo el archivo $PLUGIN_NAME$ del plugin.", 96 | "placeholders": { 97 | "plugin_name": { 98 | "content": "$1", 99 | "example": "Awesome plugin" 100 | } 101 | } 102 | }, 103 | "or": { 104 | "message": "o" 105 | }, 106 | "clickInstallPlugin": { 107 | "message": "Este es un plugin para IITC. Haga clic en Instalar para comenzar a usarlo." 108 | }, 109 | "install": { 110 | "message": "Instalar" 111 | }, 112 | "loading": { 113 | "message": "Cargando…" 114 | }, 115 | "external": { 116 | "message": "\tExterno" 117 | }, 118 | "willBeOverwrittenByNewPlugin": { 119 | "message": "Otro plugin con el mismo ID será sobrescrito con el nuevo plugin." 120 | }, 121 | "dropJSHereOrClick": { 122 | "message": "Arrastre aquí archivos .js o haga clic para cargarlos." 123 | }, 124 | "addedUserScriptTo": { 125 | "message": "$PLUGIN_NAME$ se adicionó a la categoría de plugins \"$PLUGIN_CATEGORY$\".", 126 | "placeholders": { 127 | "plugin_name": { 128 | "content": "$1", 129 | "example": "Awesome plugin" 130 | }, 131 | "plugin_category": { 132 | "content": "$2", 133 | "example": "Draw" 134 | } 135 | } 136 | }, 137 | "badgeOverride": { 138 | "message": "anular" 139 | }, 140 | "beta": { 141 | "message": "Beta" 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /public/_locales/et/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "noData": { 3 | "message": "Andmed puuduvad" 4 | }, 5 | "changesApplied": { 6 | "message": "Muutused on jõustunud" 7 | }, 8 | "updateInProgress": { 9 | "message": "Uuendus on tegemisel …" 10 | }, 11 | "needRebootIntel": { 12 | "message": "Muutused jõustuvad peale Inteli restarti" 13 | }, 14 | "titleDefault": { 15 | "message": "Ava Intel", 16 | "description": "Title for the button at the application panel" 17 | }, 18 | "lang": { 19 | "message": "et" 20 | }, 21 | "extDescription": { 22 | "message": "Käivita IITC Ingressi jaoks lihtsalt.", 23 | "description": "Description of the extension" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /public/_locales/fi/messages.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /public/_locales/fr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "noData": { 3 | "message": "Pas de données" 4 | }, 5 | "addExternalPlugin": { 6 | "message": "Ajouter un plugin externe" 7 | }, 8 | "iitcHomePage": { 9 | "message": "Accueil" 10 | }, 11 | "pluginSave": { 12 | "message": "Enregistrer" 13 | }, 14 | "pluginDelete": { 15 | "message": "Supprimer" 16 | }, 17 | "or": { 18 | "message": "ou" 19 | }, 20 | "willBeOverwrittenByNewPlugin": { 21 | "message": "Le plugin avec le même ID sera écrasé par le nouveau plugin." 22 | }, 23 | "clickInstallPlugin": { 24 | "message": "Ceci est un plugin IITC. Cliquez sur installer pour commencer à l'utiliser." 25 | }, 26 | "install": { 27 | "message": "Installer" 28 | }, 29 | "loading": { 30 | "message": "Chargement…" 31 | }, 32 | "external": { 33 | "message": "Externe" 34 | }, 35 | "errorReadingFile": { 36 | "message": "Une erreur est survenue lors de la lecture du fichier $PLUGIN_NAME$.", 37 | "placeholders": { 38 | "plugin_name": { 39 | "content": "$1", 40 | "example": "Awesome plugin" 41 | } 42 | } 43 | }, 44 | "serverNotAvailableRetry": { 45 | "message": "Le serveur n'est pas disponible. Réessayez dans $NUM$ secondes", 46 | "placeholders": { 47 | "num": { 48 | "content": "$1", 49 | "example": "5" 50 | } 51 | } 52 | }, 53 | "updateInProgress": { 54 | "message": "Mise à jour en cours…" 55 | }, 56 | "everyWeek": { 57 | "message": "Toutes les semaines" 58 | }, 59 | "extDescription": { 60 | "message": "Lancer facilement IITC pour Ingress.", 61 | "description": "Description of the extension" 62 | }, 63 | "titleDefault": { 64 | "message": "Ouvrir l'Intel", 65 | "description": "Title for the button at the application panel" 66 | }, 67 | "lang": { 68 | "message": "fr" 69 | }, 70 | "openSupport": { 71 | "message": "Ouvrir le support :" 72 | }, 73 | "anyChannel": { 74 | "message": "Tout les canaux " 75 | }, 76 | "release": { 77 | "message": "Release" 78 | }, 79 | "choosingUpdateChannel": { 80 | "message": "Choisissez un canal de mise à jour" 81 | }, 82 | "iitcButtonOptions": { 83 | "message": "Options du bouton IITC-CE" 84 | }, 85 | "dropJSHereOrClick": { 86 | "message": "Déposez les fichiers .js ici ou cliquez pour télécharger." 87 | }, 88 | "addressNotAvailable": { 89 | "message": "L'adresse n'est pas disponible" 90 | }, 91 | "notValidUserScript": { 92 | "message": "$PLUGIN_NAME$ n'est pas un UserScript valide.", 93 | "placeholders": { 94 | "plugin_name": { 95 | "content": "$1", 96 | "example": "Awesome plugin" 97 | } 98 | } 99 | }, 100 | "everyDay": { 101 | "message": "Tout les jours" 102 | }, 103 | "every12hours": { 104 | "message": "Toutes les 12 heures" 105 | }, 106 | "every6hours": { 107 | "message": "Toutes les 6 heures" 108 | }, 109 | "updateExternalFrequency": { 110 | "message": "Vérifier les mises à jour des plugins externes" 111 | }, 112 | "updateFrequency": { 113 | "message": "Mise à jour IITC-CE" 114 | }, 115 | "updateNow": { 116 | "message": "Mettre à jour" 117 | }, 118 | "changesApplied": { 119 | "message": "Les modifications ont été appliquées" 120 | }, 121 | "needRebootIntel": { 122 | "message": "Les changements seront appliqués après le rechargement de l'Intel" 123 | }, 124 | "addedUserScriptTo": { 125 | "message": "$PLUGIN_NAME$ a été ajouté à la catégorie \"$PLUGIN_CATEGORY$\".", 126 | "placeholders": { 127 | "plugin_name": { 128 | "content": "$1", 129 | "example": "Awesome plugin" 130 | }, 131 | "plugin_category": { 132 | "content": "$2", 133 | "example": "Draw" 134 | } 135 | } 136 | }, 137 | "badgeOverride": { 138 | "message": "passer outre" 139 | }, 140 | "searchBoxPlaceholder": { 141 | "message": "Recherche…" 142 | }, 143 | "customServerHint": { 144 | "message": "Par exemple :" 145 | }, 146 | "customServerTooltipSuccess": { 147 | "message": "Connexion au serveur réussie" 148 | }, 149 | "reinstall": { 150 | "message": "Réinstaller" 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /public/_locales/gl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": { 3 | "message": "Lanzamento" 4 | }, 5 | "choosingUpdateChannel": { 6 | "message": "Seleccionando dunha canle de actualización IITC-CE" 7 | }, 8 | "updateNow": { 9 | "message": "actualizar agora" 10 | }, 11 | "iitcButtonOptions": { 12 | "message": "Opcións do botón IITC-CE" 13 | }, 14 | "iitcHomePage": { 15 | "message": "Páxina de inicio" 16 | }, 17 | "addExternalPlugin": { 18 | "message": "Engade pluggin externo" 19 | }, 20 | "noData": { 21 | "message": "Sen datos" 22 | }, 23 | "changesApplied": { 24 | "message": "Aplicáronse cambios" 25 | }, 26 | "needRebootIntel": { 27 | "message": "Os cambios aplicaranse despois de reiniciar Intel" 28 | }, 29 | "titleDefault": { 30 | "message": "Abrir Intel", 31 | "description": "Title for the button at the application panel" 32 | }, 33 | "extDescription": { 34 | "message": "Inicia IITC para Ingress Intel con facilidade.", 35 | "description": "Description of the extension" 36 | }, 37 | "updateInProgress": { 38 | "message": "Actualización en curso…" 39 | }, 40 | "lang": { 41 | "message": "gl" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /public/_locales/he/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "updateFrequency": { 3 | "message": "בדיקת עדכונים של אייאייטיסי-סיאי" 4 | }, 5 | "updateExternalFrequency": { 6 | "message": "בדיקת עדכונים של תוספים חיצוניים" 7 | }, 8 | "beta": { 9 | "message": "בטא" 10 | }, 11 | "release": { 12 | "message": "גרסא" 13 | }, 14 | "choosingUpdateChannel": { 15 | "message": "בחירת ערוץ עדכונים של אייאייטיסי-סיאי" 16 | }, 17 | "updateNow": { 18 | "message": "עדכן עכשיו" 19 | }, 20 | "iitcButtonOptions": { 21 | "message": "אפשרויות כפתור אייאייטיסי-סיאי" 22 | }, 23 | "iitcHomePage": { 24 | "message": "דף הבית" 25 | }, 26 | "addExternalPlugin": { 27 | "message": "הוסף תוסף חיצוני" 28 | }, 29 | "noData": { 30 | "message": "אין מידע" 31 | }, 32 | "changesApplied": { 33 | "message": "השינויים הוחלו" 34 | }, 35 | "updateInProgress": { 36 | "message": "עדכון בתהליך…" 37 | }, 38 | "needRebootIntel": { 39 | "message": "השינויים יוחלו לאחר אתחול מחדש של אינטל" 40 | }, 41 | "titleDefault": { 42 | "message": "פתח אינטל", 43 | "description": "Title for the button at the application panel" 44 | }, 45 | "lang": { 46 | "message": "he" 47 | }, 48 | "extDescription": { 49 | "message": "השק את אייאייטיסי לאינגרס אינטל בקלות.", 50 | "description": "Description of the extension" 51 | }, 52 | "notValidUserScript": { 53 | "message": "$PLUGIN_NAME$ אינו יוזרסקריפט חוקי.", 54 | "placeholders": { 55 | "plugin_name": { 56 | "content": "$1", 57 | "example": "Awesome plugin" 58 | } 59 | } 60 | }, 61 | "pluginDelete": { 62 | "message": "מחק" 63 | }, 64 | "pluginSave": { 65 | "message": "שמור" 66 | }, 67 | "openSupport": { 68 | "message": "פתח דף תמיכה:" 69 | }, 70 | "badgeOverride": { 71 | "message": "לעקוף" 72 | }, 73 | "anyChannel": { 74 | "message": "כול ערוץ" 75 | }, 76 | "everyWeek": { 77 | "message": "כול שבוע" 78 | }, 79 | "everyDay": { 80 | "message": "כול יום" 81 | }, 82 | "every12hours": { 83 | "message": "כול 12 שעות" 84 | }, 85 | "external": { 86 | "message": "חיצוני" 87 | }, 88 | "serverNotAvailableRetry": { 89 | "message": "השרת אינו זמין. נסה שוב בעוד $NUM$ שניות", 90 | "placeholders": { 91 | "num": { 92 | "content": "$1", 93 | "example": "5" 94 | } 95 | } 96 | }, 97 | "loading": { 98 | "message": "טוען…" 99 | }, 100 | "install": { 101 | "message": "התקן" 102 | }, 103 | "clickInstallPlugin": { 104 | "message": "זהו תוסף אייאייטיסי. לחץ על התקן כדי להתחיל להשתמש בו." 105 | }, 106 | "willBeOverwrittenByNewPlugin": { 107 | "message": "תוסף עם אותו מזהה יוחלף על ידי התוסף החדש." 108 | }, 109 | "dropJSHereOrClick": { 110 | "message": "זרוק לכאן קבצי .js או לחץ להעלאה." 111 | }, 112 | "or": { 113 | "message": "או" 114 | }, 115 | "errorReadingFile": { 116 | "message": "אירעה שגיאה בקריאת קובץ $PLUGIN_NAME$.", 117 | "placeholders": { 118 | "plugin_name": { 119 | "content": "$1", 120 | "example": "Awesome plugin" 121 | } 122 | } 123 | }, 124 | "addressNotAvailable": { 125 | "message": "כתובת אינה זמינה" 126 | }, 127 | "addedUserScriptTo": { 128 | "message": "$PLUGIN_NAME$ נוסף לקטגוריה \"$PLUGIN_CATEGORY$\".", 129 | "placeholders": { 130 | "plugin_name": { 131 | "content": "$1", 132 | "example": "Awesome plugin" 133 | }, 134 | "plugin_category": { 135 | "content": "$2", 136 | "example": "Draw" 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /public/_locales/hu/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "notValidUserScript": { 3 | "message": "$PLUGIN_NAME$ nem érvényes UserScript.", 4 | "placeholders": { 5 | "plugin_name": { 6 | "content": "$1", 7 | "example": "Awesome plugin" 8 | } 9 | } 10 | }, 11 | "external": { 12 | "message": "Külső" 13 | }, 14 | "serverNotAvailableRetry": { 15 | "message": "A szerver nem elérhető. Újrapróbálkozás $NUM$ mp. múlva", 16 | "placeholders": { 17 | "num": { 18 | "content": "$1", 19 | "example": "5" 20 | } 21 | } 22 | }, 23 | "loading": { 24 | "message": "Betöltés…" 25 | }, 26 | "install": { 27 | "message": "Telepítés" 28 | }, 29 | "or": { 30 | "message": "vagy" 31 | }, 32 | "errorReadingFile": { 33 | "message": "Hiba történt a $PLUGIN_NAME$ fájl olvasása során.", 34 | "placeholders": { 35 | "plugin_name": { 36 | "content": "$1", 37 | "example": "Awesome plugin" 38 | } 39 | } 40 | }, 41 | "addressNotAvailable": { 42 | "message": "Cím nem elérhető" 43 | }, 44 | "pluginDelete": { 45 | "message": "Törlés" 46 | }, 47 | "openSupport": { 48 | "message": "Támogatási oldal megnyitása:" 49 | }, 50 | "pluginSave": { 51 | "message": "Mentés" 52 | }, 53 | "everyWeek": { 54 | "message": "Hetente" 55 | }, 56 | "everyDay": { 57 | "message": "Naponta" 58 | }, 59 | "every12hours": { 60 | "message": "12 óránként" 61 | }, 62 | "every6hours": { 63 | "message": "6 óránként" 64 | }, 65 | "updateNow": { 66 | "message": "frissítés most" 67 | }, 68 | "iitcHomePage": { 69 | "message": "Kezdőoldal" 70 | }, 71 | "addExternalPlugin": { 72 | "message": "Bővítmény hozzáadása" 73 | }, 74 | "updateInProgress": { 75 | "message": "Frissítés folyamatban…" 76 | }, 77 | "addedUserScriptTo": { 78 | "message": "$PLUGIN_NAME$ hozzáadva a \"$PLUGIN_CATEGORY$\" kategóriába.", 79 | "placeholders": { 80 | "plugin_name": { 81 | "content": "$1", 82 | "example": "Awesome plugin" 83 | }, 84 | "plugin_category": { 85 | "content": "$2", 86 | "example": "Draw" 87 | } 88 | } 89 | }, 90 | "lang": { 91 | "message": "hu" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /public/_locales/lzh/messages.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /public/_locales/mag/messages.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /public/_locales/nb_NO/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "lang": { 3 | "message": "nb_NO" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/_locales/nl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extDescription": { 3 | "message": "Eenvoudig opstarten van IITC voor Ingress Intel.", 4 | "description": "Description of the extension" 5 | }, 6 | "lang": { 7 | "message": "nl" 8 | }, 9 | "external": { 10 | "message": "Extern" 11 | }, 12 | "errorReadingFile": { 13 | "message": "Er is een fout opgetreden bij het lezen van het bestand $PLUGIN_NAME$.", 14 | "placeholders": { 15 | "plugin_name": { 16 | "content": "$1", 17 | "example": "Awesome plugin" 18 | } 19 | } 20 | }, 21 | "addressNotAvailable": { 22 | "message": "Adres is niet beschikbaar" 23 | }, 24 | "addedUserScriptTo": { 25 | "message": "$PLUGIN_NAME$ werd aan de $PLUGIN_CATEGORY$ categorie toegevoegd.", 26 | "placeholders": { 27 | "plugin_name": { 28 | "content": "$1", 29 | "example": "Awesome plugin" 30 | }, 31 | "plugin_category": { 32 | "content": "$2", 33 | "example": "Draw" 34 | } 35 | } 36 | }, 37 | "notValidUserScript": { 38 | "message": "$PLUGIN_NAME$ is geen geldig UserScript.", 39 | "placeholders": { 40 | "plugin_name": { 41 | "content": "$1", 42 | "example": "Awesome plugin" 43 | } 44 | } 45 | }, 46 | "openSupport": { 47 | "message": "Hulppagina openen:" 48 | }, 49 | "anyChannel": { 50 | "message": "Eender welk kanaal" 51 | }, 52 | "updateExternalFrequency": { 53 | "message": "Externe plugins zoeken naar updates" 54 | }, 55 | "updateFrequency": { 56 | "message": "IITC-CE zoekt naar updates" 57 | }, 58 | "beta": { 59 | "message": "Beta" 60 | }, 61 | "release": { 62 | "message": "Release" 63 | }, 64 | "choosingUpdateChannel": { 65 | "message": "Selecteer een IITC-CE update kanaal" 66 | }, 67 | "iitcHomePage": { 68 | "message": "Homepagina" 69 | }, 70 | "addExternalPlugin": { 71 | "message": "Externe plugin toevoegen" 72 | }, 73 | "noData": { 74 | "message": "Geen data" 75 | }, 76 | "changesApplied": { 77 | "message": "Wijzigingen werden bewaard" 78 | }, 79 | "updateInProgress": { 80 | "message": "Aan het updaten…" 81 | }, 82 | "needRebootIntel": { 83 | "message": "Veranderingen worden bewaard na een herstart van Intel" 84 | }, 85 | "titleDefault": { 86 | "message": "Intel openen", 87 | "description": "Title for the button at the application panel" 88 | }, 89 | "iitcButtonOptions": { 90 | "message": "IITC-CE Knop opties" 91 | }, 92 | "badgeOverride": { 93 | "message": "opheffen" 94 | }, 95 | "dropJSHereOrClick": { 96 | "message": "Sleep hier .js bestanden of klik om te uploaden." 97 | }, 98 | "loading": { 99 | "message": "Laden…" 100 | }, 101 | "install": { 102 | "message": "Installeren" 103 | }, 104 | "clickInstallPlugin": { 105 | "message": "Dit is een IITC plugin. Installeer om te gebruiken." 106 | }, 107 | "willBeOverwrittenByNewPlugin": { 108 | "message": "Een plugin met hetzelfde ID zal overschreven worden door de nieuwe plugin." 109 | }, 110 | "serverNotAvailableRetry": { 111 | "message": "De server is niet beschikbaar. Probeer opnieuw over $NUM$ seconden", 112 | "placeholders": { 113 | "num": { 114 | "content": "$1", 115 | "example": "5" 116 | } 117 | } 118 | }, 119 | "or": { 120 | "message": "of" 121 | }, 122 | "pluginDelete": { 123 | "message": "Verwijderen" 124 | }, 125 | "pluginSave": { 126 | "message": "Bewaren" 127 | }, 128 | "everyWeek": { 129 | "message": "Elke week" 130 | }, 131 | "everyDay": { 132 | "message": "Elke dag" 133 | }, 134 | "every12hours": { 135 | "message": "Iedere 12 uur" 136 | }, 137 | "every6hours": { 138 | "message": "Iedere 6 uur" 139 | }, 140 | "updateNow": { 141 | "message": "nu updaten" 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /public/_locales/pt-PT/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "external": { 3 | "message": "Externo" 4 | }, 5 | "serverNotAvailableRetry": { 6 | "message": "O servidor não está disponível. Tente novamente depois de $NUM$ segundos", 7 | "placeholders": { 8 | "num": { 9 | "content": "$1", 10 | "example": "5" 11 | } 12 | } 13 | }, 14 | "loading": { 15 | "message": "A carregar…" 16 | }, 17 | "install": { 18 | "message": "Instalar" 19 | }, 20 | "clickInstallPlugin": { 21 | "message": "Este é um plugin IITC. Clique em \"instalar\" para o começar a usar." 22 | }, 23 | "willBeOverwrittenByNewPlugin": { 24 | "message": "O plugin com o mesmo ID será substituído pelo novo plugin." 25 | }, 26 | "dropJSHereOrClick": { 27 | "message": "Solte os arquivos .js aqui ou clique em upload." 28 | }, 29 | "or": { 30 | "message": "ou" 31 | }, 32 | "errorReadingFile": { 33 | "message": "Ocorreu um erro ao ler o arquivo $PLUGIN_NAME$.", 34 | "placeholders": { 35 | "plugin_name": { 36 | "content": "$1", 37 | "example": "Awesome plugin" 38 | } 39 | } 40 | }, 41 | "addressNotAvailable": { 42 | "message": "O endereço não está disponível" 43 | }, 44 | "addedUserScriptTo": { 45 | "message": "$PLUGIN_NAME$ foi adicionado à categoria \"$PLUGIN_CATEGORY$\".", 46 | "placeholders": { 47 | "plugin_name": { 48 | "content": "$1", 49 | "example": "Awesome plugin" 50 | }, 51 | "plugin_category": { 52 | "content": "$2", 53 | "example": "Draw" 54 | } 55 | } 56 | }, 57 | "notValidUserScript": { 58 | "message": "$PLUGIN_NAME$ Não é um plugin válido.", 59 | "placeholders": { 60 | "plugin_name": { 61 | "content": "$1", 62 | "example": "Awesome plugin" 63 | } 64 | } 65 | }, 66 | "pluginDelete": { 67 | "message": "Apagar" 68 | }, 69 | "pluginSave": { 70 | "message": "Guardar" 71 | }, 72 | "openSupport": { 73 | "message": "Abrir página de suporte:" 74 | }, 75 | "badgeOverride": { 76 | "message": "substituir" 77 | }, 78 | "anyChannel": { 79 | "message": "Qualquer canal" 80 | }, 81 | "everyWeek": { 82 | "message": "Todas as Semanas" 83 | }, 84 | "everyDay": { 85 | "message": "Todos os Dias" 86 | }, 87 | "every12hours": { 88 | "message": "A cada 12 horas" 89 | }, 90 | "every6hours": { 91 | "message": "A cada 6 horas" 92 | }, 93 | "updateExternalFrequency": { 94 | "message": "Verificar atualizações dos plugins externos" 95 | }, 96 | "updateFrequency": { 97 | "message": "Verificar atualizações do IITC-CE" 98 | }, 99 | "beta": { 100 | "message": "Beta" 101 | }, 102 | "release": { 103 | "message": "Lançamento" 104 | }, 105 | "choosingUpdateChannel": { 106 | "message": "Escolher um canal de atualização para o IITC-CE" 107 | }, 108 | "updateNow": { 109 | "message": "Atualizar agora" 110 | }, 111 | "iitcButtonOptions": { 112 | "message": "Opções do Botão IITC-CE" 113 | }, 114 | "iitcHomePage": { 115 | "message": "Página inicial" 116 | }, 117 | "addExternalPlugin": { 118 | "message": "Adicionar plugin externo" 119 | }, 120 | "noData": { 121 | "message": "Sem dados" 122 | }, 123 | "changesApplied": { 124 | "message": "As alterações foram aplicadas" 125 | }, 126 | "updateInProgress": { 127 | "message": "Atualização em progresso…" 128 | }, 129 | "needRebootIntel": { 130 | "message": "As alterações serão aplicadas depois de reiniciar o Intel" 131 | }, 132 | "titleDefault": { 133 | "message": "Abrir Intel", 134 | "description": "Title for the button at the application panel" 135 | }, 136 | "lang": { 137 | "message": "pt_PT" 138 | }, 139 | "extDescription": { 140 | "message": "Inicie o IITC para o mapa Ingress Intel com facilidade.", 141 | "description": "Description of the extension" 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /public/_locales/pt_PT/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "titleDefault": { 3 | "message": "Abrir Intel", 4 | "description": "Title for the button at the application panel" 5 | }, 6 | "needRebootIntel": { 7 | "message": "As alterações serão aplicadas depois de reiniciar o Intel" 8 | }, 9 | "updateInProgress": { 10 | "message": "Atualização em progresso…" 11 | }, 12 | "changesApplied": { 13 | "message": "As alterações foram aplicadas" 14 | }, 15 | "noData": { 16 | "message": "Sem dados" 17 | }, 18 | "iitcHomePage": { 19 | "message": "Página inicial" 20 | }, 21 | "iitcButtonOptions": { 22 | "message": "Opções do Botão IITC-CE" 23 | }, 24 | "updateNow": { 25 | "message": "Atualizar agora" 26 | }, 27 | "release": { 28 | "message": "Lançamento" 29 | }, 30 | "updateFrequency": { 31 | "message": "Verificar atualizações do IITC-CE" 32 | }, 33 | "updateExternalFrequency": { 34 | "message": "Verificar atualizações dos plugins externos" 35 | }, 36 | "every6hours": { 37 | "message": "A cada 6 horas" 38 | }, 39 | "every12hours": { 40 | "message": "A cada 12 horas" 41 | }, 42 | "everyDay": { 43 | "message": "Diariamente" 44 | }, 45 | "everyWeek": { 46 | "message": "Semanalmente" 47 | }, 48 | "lang": { 49 | "message": "pt_PT" 50 | }, 51 | "openSupport": { 52 | "message": "Abrir página de suporte:" 53 | }, 54 | "pluginSave": { 55 | "message": "Guardar" 56 | }, 57 | "pluginDelete": { 58 | "message": "Apagar" 59 | }, 60 | "or": { 61 | "message": "ou" 62 | }, 63 | "addressNotAvailable": { 64 | "message": "O endereço não está disponível" 65 | }, 66 | "dropJSHereOrClick": { 67 | "message": "Solte os arquivos .js aqui ou clique em upload." 68 | }, 69 | "clickInstallPlugin": { 70 | "message": "Este é um plugin IITC. Clique em \"instalar\" para o começar a usar." 71 | }, 72 | "install": { 73 | "message": "Instalar" 74 | }, 75 | "external": { 76 | "message": "Externo" 77 | }, 78 | "errorReadingFile": { 79 | "message": "Ocorreu um erro ao ler o arquivo $PLUGIN_NAME$.", 80 | "placeholders": { 81 | "plugin_name": { 82 | "content": "$1", 83 | "example": "Awesome plugin" 84 | } 85 | } 86 | }, 87 | "willBeOverwrittenByNewPlugin": { 88 | "message": "O plugin com o mesmo ID será substituído pelo novo plugin." 89 | }, 90 | "loading": { 91 | "message": "A carregar…" 92 | }, 93 | "serverNotAvailableRetry": { 94 | "message": "O servidor não está disponível. Tente novamente depois de $NUM$ segundos", 95 | "placeholders": { 96 | "num": { 97 | "content": "$1", 98 | "example": "5" 99 | } 100 | } 101 | }, 102 | "notValidUserScript": { 103 | "message": "$PLUGIN_NAME$ não é um plugin válido.", 104 | "placeholders": { 105 | "plugin_name": { 106 | "content": "$1", 107 | "example": "Awesome plugin" 108 | } 109 | } 110 | }, 111 | "extDescription": { 112 | "message": "Inicie o IITC para o mapa Ingress Intel com facilidade.", 113 | "description": "Description of the extension" 114 | }, 115 | "addExternalPlugin": { 116 | "message": "Adicionar plugin externo" 117 | }, 118 | "choosingUpdateChannel": { 119 | "message": "Escolher um canal de atualização para o IITC-CE" 120 | }, 121 | "anyChannel": { 122 | "message": "Qualquer canal" 123 | }, 124 | "addedUserScriptTo": { 125 | "message": "$PLUGIN_NAME$ foi adicionado à categoria \"$PLUGIN_CATEGORY$\".", 126 | "placeholders": { 127 | "plugin_name": { 128 | "content": "$1", 129 | "example": "Awesome plugin" 130 | }, 131 | "plugin_category": { 132 | "content": "$2", 133 | "example": "Draw" 134 | } 135 | } 136 | }, 137 | "badgeOverride": { 138 | "message": "substituir" 139 | }, 140 | "beta": { 141 | "message": "Beta" 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /public/_locales/sl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "updateInProgress": { 3 | "message": "Posodobitev v teku…" 4 | }, 5 | "needRebootIntel": { 6 | "message": "Spremembe bodo uveljavljene po ponovnem zagonu Intela" 7 | }, 8 | "titleDefault": { 9 | "message": "Odprite Intel", 10 | "description": "Title for the button at the application panel" 11 | }, 12 | "lang": { 13 | "message": "sl" 14 | }, 15 | "extDescription": { 16 | "message": "Zaženite IITC za Intel Ingress z lahkoto.", 17 | "description": "Description of the extension" 18 | }, 19 | "beta": { 20 | "message": "Beta" 21 | }, 22 | "release": { 23 | "message": "Izdati" 24 | }, 25 | "choosingUpdateChannel": { 26 | "message": "Izbira kanala za posodobitev IITC-CE" 27 | }, 28 | "updateNow": { 29 | "message": "posodobi zdaj" 30 | }, 31 | "iitcButtonOptions": { 32 | "message": "Možnosti gumba IITC-CE" 33 | }, 34 | "iitcHomePage": { 35 | "message": "Domača stran" 36 | }, 37 | "addExternalPlugin": { 38 | "message": "Dodajte zunanji vtičnik" 39 | }, 40 | "noData": { 41 | "message": "Ni podatkov" 42 | }, 43 | "changesApplied": { 44 | "message": "Spremembe so bile uporabljene" 45 | }, 46 | "addressNotAvailable": { 47 | "message": "Naslov ni na voljo" 48 | }, 49 | "addedUserScriptTo": { 50 | "message": "$PLUGIN_NAME$ je bil dodan v kategorijo \"$PLUGIN_CATEGORY$\".", 51 | "placeholders": { 52 | "plugin_name": { 53 | "content": "$1", 54 | "example": "Awesome plugin" 55 | }, 56 | "plugin_category": { 57 | "content": "$2", 58 | "example": "Draw" 59 | } 60 | } 61 | }, 62 | "notValidUserScript": { 63 | "message": "$PLUGIN_NAME$ ni veljaven UserScript.", 64 | "placeholders": { 65 | "plugin_name": { 66 | "content": "$1", 67 | "example": "Awesome plugin" 68 | } 69 | } 70 | }, 71 | "pluginDelete": { 72 | "message": "Izbriši" 73 | }, 74 | "pluginSave": { 75 | "message": "Shrani" 76 | }, 77 | "openSupport": { 78 | "message": "Odpri stran za podporo:" 79 | }, 80 | "badgeOverride": { 81 | "message": "zavrnitev" 82 | }, 83 | "anyChannel": { 84 | "message": "Kateri koli kanal" 85 | }, 86 | "everyWeek": { 87 | "message": "Vsak teden" 88 | }, 89 | "everyDay": { 90 | "message": "Vsak dan" 91 | }, 92 | "every12hours": { 93 | "message": "Vsakih 12 ur" 94 | }, 95 | "every6hours": { 96 | "message": "Vsakih 6 ur" 97 | }, 98 | "updateExternalFrequency": { 99 | "message": "Zunanji vtičniki preverijo posodobitve" 100 | }, 101 | "updateFrequency": { 102 | "message": "IITC-CE preverja posodobitve" 103 | }, 104 | "external": { 105 | "message": "Zunanji" 106 | }, 107 | "serverNotAvailableRetry": { 108 | "message": "Strežnik ni na voljo. Ponovno poskusi čez $NUM$ sekund", 109 | "placeholders": { 110 | "num": { 111 | "content": "$1", 112 | "example": "5" 113 | } 114 | } 115 | }, 116 | "loading": { 117 | "message": "Nalaganje…" 118 | }, 119 | "install": { 120 | "message": "Namestiti" 121 | }, 122 | "clickInstallPlugin": { 123 | "message": "To je vtičnik IITC. Klikni \"namestiti\" gumb, da ga začneš uporabljati." 124 | }, 125 | "willBeOverwrittenByNewPlugin": { 126 | "message": "Vtičnik z istim ID-jem bo novi vtičnik prepisal." 127 | }, 128 | "dropJSHereOrClick": { 129 | "message": "Prenesi .js datoteko sem ali klikni za nalaganje." 130 | }, 131 | "or": { 132 | "message": "ali" 133 | }, 134 | "errorReadingFile": { 135 | "message": "Med branjem datoteke za $PLUGIN_NAME$ je prišlo do napake.", 136 | "placeholders": { 137 | "plugin_name": { 138 | "content": "$1", 139 | "example": "Awesome plugin" 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /public/_locales/sv/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "updateFrequency": { 3 | "message": "IITC-CE söker efter uppdateringar" 4 | }, 5 | "updateNow": { 6 | "message": "uppdatera nu" 7 | }, 8 | "iitcButtonOptions": { 9 | "message": "IITC-CE Knapp inställningar" 10 | }, 11 | "noData": { 12 | "message": "Inga data" 13 | }, 14 | "changesApplied": { 15 | "message": "Ändringar tillämpades" 16 | }, 17 | "updateInProgress": { 18 | "message": "Uppdatering pågår…" 19 | }, 20 | "needRebootIntel": { 21 | "message": "Ändringar kommer att tillämpas efter omstart av Intel" 22 | }, 23 | "titleDefault": { 24 | "message": "Öppna Intel", 25 | "description": "Title for the button at the application panel" 26 | }, 27 | "lang": { 28 | "message": "sv" 29 | }, 30 | "beta": { 31 | "message": "Beta" 32 | }, 33 | "extDescription": { 34 | "message": "Starta IITC för Ingress Intel enkelt.", 35 | "description": "Description of the extension" 36 | }, 37 | "dropJSHereOrClick": { 38 | "message": "Släpp .js filer här eller klicka för att ladda upp." 39 | }, 40 | "or": { 41 | "message": "eller" 42 | }, 43 | "errorReadingFile": { 44 | "message": "Ett fel uppstod vid läsning av $PLUGIN_NAME$ filen.", 45 | "placeholders": { 46 | "plugin_name": { 47 | "content": "$1", 48 | "example": "Awesome plugin" 49 | } 50 | } 51 | }, 52 | "addressNotAvailable": { 53 | "message": "Adressen är inte tillgänglig" 54 | }, 55 | "pluginDelete": { 56 | "message": "Radera" 57 | }, 58 | "pluginSave": { 59 | "message": "Spara" 60 | }, 61 | "openSupport": { 62 | "message": "Öppna supportsida:" 63 | }, 64 | "anyChannel": { 65 | "message": "Alla kanaler" 66 | }, 67 | "everyWeek": { 68 | "message": "Varje vecka" 69 | }, 70 | "everyDay": { 71 | "message": "Varje dag" 72 | }, 73 | "updateExternalFrequency": { 74 | "message": "Externa plugins söker efter uppdateringar" 75 | }, 76 | "external": { 77 | "message": "Extern" 78 | }, 79 | "loading": { 80 | "message": "Laddar…" 81 | }, 82 | "install": { 83 | "message": "Installera" 84 | }, 85 | "notValidUserScript": { 86 | "message": "$PLUGIN_NAME$ är inte en giltig UserScript.", 87 | "placeholders": { 88 | "plugin_name": { 89 | "content": "$1", 90 | "example": "Awesome plugin" 91 | } 92 | } 93 | }, 94 | "clickInstallPlugin": { 95 | "message": "Detta är en IITC plugin. Tryck installera för att börja använda den." 96 | }, 97 | "every12hours": { 98 | "message": "Var 12:e timme" 99 | }, 100 | "every6hours": { 101 | "message": "Var 6:e timme" 102 | }, 103 | "addedUserScriptTo": { 104 | "message": "$PLUGIN_NAME$ har lagts till i kategorin \"$PLUGIN_CATEGORY$\".", 105 | "placeholders": { 106 | "plugin_name": { 107 | "content": "$1", 108 | "example": "Awesome plugin" 109 | }, 110 | "plugin_category": { 111 | "content": "$2", 112 | "example": "Draw" 113 | } 114 | } 115 | }, 116 | "serverNotAvailableRetry": { 117 | "message": "Servern är inte tillgänglig. Försök igen efter $NUM$ sekund(er)", 118 | "placeholders": { 119 | "num": { 120 | "content": "$1", 121 | "example": "5" 122 | } 123 | } 124 | }, 125 | "willBeOverwrittenByNewPlugin": { 126 | "message": "Plugin med samma ID kommer skrivas över av det nya pluginet." 127 | }, 128 | "iitcHomePage": { 129 | "message": "Startsida" 130 | }, 131 | "choosingUpdateChannel": { 132 | "message": "Välj IITC-CE kanal för uppdateringar" 133 | }, 134 | "badgeOverride": { 135 | "message": "Åsidosätt" 136 | }, 137 | "release": { 138 | "message": "Stabil" 139 | }, 140 | "addExternalPlugin": { 141 | "message": "Lägg till extern plugin" 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /public/_locales/uk/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extDescription": { 3 | "message": "Запускай Ingress Intel с IITC в один клік." 4 | }, 5 | "lang": { 6 | "message": "uk" 7 | }, 8 | "titleDefault": { 9 | "message": "Відкрити Intel" 10 | }, 11 | "install": { 12 | "message": "Встановити" 13 | }, 14 | "or": { 15 | "message": "або" 16 | }, 17 | "pluginDelete": { 18 | "message": "Видалити" 19 | }, 20 | "pluginSave": { 21 | "message": "Зберегти" 22 | }, 23 | "openSupport": { 24 | "message": "Відкрити сторінку підтримки:" 25 | }, 26 | "everyWeek": { 27 | "message": "Кожен тиждень" 28 | }, 29 | "everyDay": { 30 | "message": "Кожен день" 31 | }, 32 | "every12hours": { 33 | "message": "Кожні 12 годин" 34 | }, 35 | "every6hours": { 36 | "message": "Кожні 6 годин" 37 | }, 38 | "updateNow": { 39 | "message": "Оновити зараз" 40 | }, 41 | "iitcHomePage": { 42 | "message": "Домашня сторінка" 43 | }, 44 | "noData": { 45 | "message": "Нема даних" 46 | }, 47 | "changesApplied": { 48 | "message": "Зміни застосовані" 49 | }, 50 | "updateInProgress": { 51 | "message": "Оновлення в процесі…" 52 | }, 53 | "needRebootIntel": { 54 | "message": "Зміни буде застосовано після перезавантаження Intel" 55 | }, 56 | "iitcButtonOptions": { 57 | "message": "Налаштування IITC-CE Button" 58 | }, 59 | "notValidUserScript": { 60 | "message": "$PLUGIN_NAME$ не є дійсним UserScript.", 61 | "placeholders": { 62 | "plugin_name": { 63 | "content": "$1", 64 | "example": "Awesome plugin" 65 | } 66 | } 67 | }, 68 | "errorReadingFile": { 69 | "message": "Під час читання $PLUGIN_NAME$ сталася помилка.", 70 | "placeholders": { 71 | "plugin_name": { 72 | "content": "$1", 73 | "example": "Awesome plugin" 74 | } 75 | } 76 | }, 77 | "dropJSHereOrClick": { 78 | "message": "Перетягніть сюди .js файли або натисніть, щоб завантажити." 79 | }, 80 | "willBeOverwrittenByNewPlugin": { 81 | "message": "При збігу ID плагін буде перезаписан новим." 82 | }, 83 | "clickInstallPlugin": { 84 | "message": "Це плагін IITC. Натисни встановити щоб почати його використовувати." 85 | }, 86 | "loading": { 87 | "message": "Завантаження…" 88 | }, 89 | "addExternalPlugin": { 90 | "message": "Додати зовнішній плагін" 91 | }, 92 | "choosingUpdateChannel": { 93 | "message": "Канал оновлень IITC-CE" 94 | }, 95 | "release": { 96 | "message": "Реліз" 97 | }, 98 | "updateFrequency": { 99 | "message": "IITC-CE перевіряє наявність оновлень" 100 | }, 101 | "updateExternalFrequency": { 102 | "message": "Перевірка оновлень зовнішніх плагінів" 103 | }, 104 | "anyChannel": { 105 | "message": "Будь-який канал" 106 | }, 107 | "addressNotAvailable": { 108 | "message": "Недоступний" 109 | }, 110 | "serverNotAvailableRetry": { 111 | "message": "Сервер не доступний. Повторна спроба через $NUM$ сек", 112 | "placeholders": { 113 | "num": { 114 | "content": "$1", 115 | "example": "5" 116 | } 117 | } 118 | }, 119 | "external": { 120 | "message": "Зовнішні" 121 | }, 122 | "addedUserScriptTo": { 123 | "message": "$PLUGIN_NAME$ додано до категорії \"$PLUGIN_CATEGORY$\".", 124 | "placeholders": { 125 | "plugin_name": { 126 | "content": "$1", 127 | "example": "Awesome plugin" 128 | }, 129 | "plugin_category": { 130 | "content": "$2", 131 | "example": "Draw" 132 | } 133 | } 134 | }, 135 | "badgeOverride": { 136 | "message": "замінити" 137 | }, 138 | "beta": { 139 | "message": "Бета" 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /public/assets/iconfont/MaterialIcons-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/iconfont/MaterialIcons-Regular.woff2 -------------------------------------------------------------------------------- /public/assets/iconfont/material-icons.css: -------------------------------------------------------------------------------- 1 | /* MaterialIcons; Apache License 2.0 */ 2 | /* https://github.com/google/material-design-icons/issues/786#issuecomment-487055009 */ 3 | 4 | @font-face { 5 | font-family: 'Material Icons'; 6 | font-style: normal; 7 | font-weight: 400; 8 | src: url(MaterialIcons-Regular.woff2) format('woff2'); 9 | } 10 | 11 | .material-icons { 12 | font-family: 'Material Icons'; 13 | font-weight: normal; 14 | font-style: normal; 15 | font-size: 24px; /* Preferred icon size */ 16 | display: inline-block; 17 | line-height: 1; 18 | text-transform: none; 19 | letter-spacing: normal; 20 | word-wrap: normal; 21 | white-space: nowrap; 22 | direction: ltr; 23 | 24 | /* Support for all WebKit browsers. */ 25 | -webkit-font-smoothing: antialiased; 26 | /* Support for Safari and Chrome. */ 27 | text-rendering: optimizeLegibility; 28 | 29 | /* Support for Firefox. */ 30 | -moz-osx-font-smoothing: grayscale; 31 | 32 | /* Support for IE. */ 33 | font-feature-settings: 'liga'; 34 | } 35 | -------------------------------------------------------------------------------- /public/assets/icons/128/icon-beta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/icons/128/icon-beta.png -------------------------------------------------------------------------------- /public/assets/icons/128/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/icons/128/icon.png -------------------------------------------------------------------------------- /public/assets/icons/24/userscript-no-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/icons/24/userscript-no-icon.png -------------------------------------------------------------------------------- /public/assets/icons/48/icon-beta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/icons/48/icon-beta.png -------------------------------------------------------------------------------- /public/assets/icons/48/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/icons/48/icon.png -------------------------------------------------------------------------------- /public/assets/icons/96/icon-beta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/icons/96/icon-beta.png -------------------------------------------------------------------------------- /public/assets/icons/96/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/icons/96/icon.png -------------------------------------------------------------------------------- /public/assets/icons/extension.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/icons/favicon.ico -------------------------------------------------------------------------------- /public/assets/images/IITC-black-horizontally.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/images/IITC-black-horizontally.webp -------------------------------------------------------------------------------- /public/assets/noto/NotoSansMono-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/noto/NotoSansMono-Regular.woff2 -------------------------------------------------------------------------------- /public/assets/noto/noto-font.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Noto Sans Mono'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: local('Noto Sans Mono'), 6 | local('NotoSansMono-Regular'), 7 | url(NotoSansMono-Regular.woff2) format('woff2'); 8 | } 9 | -------------------------------------------------------------------------------- /public/assets/roboto/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/public/assets/roboto/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /public/assets/roboto/roboto-font.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Roboto'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: local('Roboto'), 6 | local('Roboto-Regular'), 7 | url(Roboto-Regular.woff2) format('woff2'); 8 | } 9 | 10 | body, input, select { 11 | font-family: "Roboto", system-ui; 12 | } 13 | -------------------------------------------------------------------------------- /public/browser-extension.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /safari/IITC Button.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /safari/IITC Button.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /safari/IITC Button.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /safari/IITC Button.xcodeproj/xcshareddata/xcschemes/macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /safari/Safari Extension/Shared/SafariWebExtensionHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SafariWebExtensionHandler.swift 3 | // Safari Extension macOS 4 | // 5 | // Created by Lucka on 3/10/2021. 6 | // 7 | 8 | import SafariServices 9 | import os.log 10 | 11 | #if os(macOS) 12 | let SFExtensionMessageKey = "message" 13 | #endif 14 | 15 | class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { 16 | 17 | func beginRequest(with context: NSExtensionContext) { 18 | let item = context.inputItems[0] as! NSExtensionItem 19 | let message = item.userInfo?[SFExtensionMessageKey] 20 | os_log(.default, "Received message from browser.runtime.sendNativeMessage: %@", message as! CVarArg) 21 | 22 | let response = NSExtensionItem() 23 | response.userInfo = [ SFExtensionMessageKey: [ "Response to": message ] ] 24 | 25 | context.completeRequest(returningItems: [response], completionHandler: nil) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /safari/Safari Extension/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSExtension 6 | 7 | NSExtensionPointIdentifier 8 | com.apple.Safari.web-extension 9 | NSExtensionPrincipalClass 10 | $(PRODUCT_MODULE_NAME).SafariWebExtensionHandler 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /safari/Safari Extension/macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSExtension 6 | 7 | NSExtensionPointIdentifier 8 | com.apple.Safari.web-extension 9 | NSExtensionPrincipalClass 10 | $(PRODUCT_MODULE_NAME).SafariWebExtensionHandler 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /safari/Safari Extension/macOS/Safari Extension.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /safari/Scripts/pre-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # pre-build.sh 4 | # IITC-Button 5 | # 6 | # Created by Lucka on 6/10/2021. 7 | # 8 | 9 | cd "${PROJECT_DIR}" 10 | 11 | basePath=$(dirname "$0") 12 | PACKAGE_VERSION=$(sed -n 's/^.*version": "\(.*\)",$/\1/p' $basePath/../../package.json) 13 | 14 | GIT_COMMIT_COUNT=$(git rev-list --count HEAD || echo '1') 15 | 16 | INFO_PLIST_PREPROCESS_FILE="Shared/info-plist-preprocessor.h" 17 | 18 | echo "//---------------------------------------------------------------" > $INFO_PLIST_PREPROCESS_FILE 19 | echo "// Automatically generated by pre-build.sh. Do NOT edit manually." >> $INFO_PLIST_PREPROCESS_FILE 20 | echo "//---------------------------------------------------------------" >> $INFO_PLIST_PREPROCESS_FILE 21 | echo "" >> $INFO_PLIST_PREPROCESS_FILE 22 | echo "#define PACKAGE_VERSION ${PACKAGE_VERSION}" >> $INFO_PLIST_PREPROCESS_FILE 23 | echo "#define GIT_COMMIT_COUNT ${GIT_COMMIT_COUNT}" >> $INFO_PLIST_PREPROCESS_FILE 24 | -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "1.000", 9 | "green" : "0.200", 10 | "red" : "0.547" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "0.000", 28 | "red" : "0.600" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "iOS_iPhoneNotifications_20@2x.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "iOS_iPhoneNotifications_20@3x.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "iOS_iPhoneSettings_29@2x.png", 17 | "idiom" : "iphone", 18 | "scale" : "2x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "iOS_iPhoneSettings_29@3x.png", 23 | "idiom" : "iphone", 24 | "scale" : "3x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "iOS_iPhoneSpotlight_40@2x.png", 29 | "idiom" : "iphone", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "iOS_iPhoneSpotlight_40@3x.png", 35 | "idiom" : "iphone", 36 | "scale" : "3x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "iOS_iPhoneApp_60@2x.png", 41 | "idiom" : "iphone", 42 | "scale" : "2x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "filename" : "iOS_iPhoneApp_60@3x.png", 47 | "idiom" : "iphone", 48 | "scale" : "3x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "iOS_iPadNotifications_20.png", 53 | "idiom" : "ipad", 54 | "scale" : "1x", 55 | "size" : "20x20" 56 | }, 57 | { 58 | "filename" : "iOS_iPadNotifications_20@2x.png", 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "iOS_iPadSettings_29.png", 65 | "idiom" : "ipad", 66 | "scale" : "1x", 67 | "size" : "29x29" 68 | }, 69 | { 70 | "filename" : "iOS_iPadSettings_29@2x.png", 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "iOS_iPadSpotlight_40.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "40x40" 80 | }, 81 | { 82 | "filename" : "iOS_iPadSpotlight_40@2x.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "iOS_iPadApp_76.png", 89 | "idiom" : "ipad", 90 | "scale" : "1x", 91 | "size" : "76x76" 92 | }, 93 | { 94 | "filename" : "iOS_iPadApp_76@2x.png", 95 | "idiom" : "ipad", 96 | "scale" : "2x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "iOS_iPadProApp_83.5@2x.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "83.5x83.5" 104 | }, 105 | { 106 | "filename" : "iOS_AppStore_1024.png", 107 | "idiom" : "ios-marketing", 108 | "scale" : "1x", 109 | "size" : "1024x1024" 110 | }, 111 | { 112 | "filename" : "macOS_16.png", 113 | "idiom" : "mac", 114 | "scale" : "1x", 115 | "size" : "16x16" 116 | }, 117 | { 118 | "filename" : "macOS_16@2x.png", 119 | "idiom" : "mac", 120 | "scale" : "2x", 121 | "size" : "16x16" 122 | }, 123 | { 124 | "filename" : "macOS_32.png", 125 | "idiom" : "mac", 126 | "scale" : "1x", 127 | "size" : "32x32" 128 | }, 129 | { 130 | "filename" : "macOS_32@2x.png", 131 | "idiom" : "mac", 132 | "scale" : "2x", 133 | "size" : "32x32" 134 | }, 135 | { 136 | "filename" : "macOS_128.png", 137 | "idiom" : "mac", 138 | "scale" : "1x", 139 | "size" : "128x128" 140 | }, 141 | { 142 | "filename" : "macOS_128@2x.png", 143 | "idiom" : "mac", 144 | "scale" : "2x", 145 | "size" : "128x128" 146 | }, 147 | { 148 | "filename" : "macOS_256.png", 149 | "idiom" : "mac", 150 | "scale" : "1x", 151 | "size" : "256x256" 152 | }, 153 | { 154 | "filename" : "macOS_256@2x.png", 155 | "idiom" : "mac", 156 | "scale" : "2x", 157 | "size" : "256x256" 158 | }, 159 | { 160 | "filename" : "macOS_512.png", 161 | "idiom" : "mac", 162 | "scale" : "1x", 163 | "size" : "512x512" 164 | }, 165 | { 166 | "filename" : "macOS_512@2x.png", 167 | "idiom" : "mac", 168 | "scale" : "2x", 169 | "size" : "512x512" 170 | } 171 | ], 172 | "info" : { 173 | "author" : "xcode", 174 | "version" : 1 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_AppStore_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_AppStore_1024.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadApp_76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadApp_76.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadApp_76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadApp_76@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadNotifications_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadNotifications_20.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadNotifications_20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadNotifications_20@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadProApp_83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadProApp_83.5@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadSettings_29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadSettings_29.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadSettings_29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadSettings_29@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadSpotlight_40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadSpotlight_40.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadSpotlight_40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPadSpotlight_40@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneApp_60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneApp_60@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneApp_60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneApp_60@3x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneNotifications_20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneNotifications_20@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneNotifications_20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneNotifications_20@3x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneSettings_29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneSettings_29@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneSettings_29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneSettings_29@3x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneSpotlight_40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneSpotlight_40@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneSpotlight_40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/iOS_iPhoneSpotlight_40@3x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_128.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_128@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_16.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_16@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_256.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_256@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_32.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_32@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_512.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITC-CE/IITC-Button/295a2bc1ffe80df6bafe68b161851906fa3e9308/safari/Shared/Assets.xcassets/AppIcon.appiconset/macOS_512@2x.png -------------------------------------------------------------------------------- /safari/Shared/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /safari/Shared/IITCButtonApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IITCButtonApp.swift 3 | // Shared 4 | // 5 | // Created by Lucka on 3/10/2021. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct IITCButtonApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | .commands { 17 | #if os(macOS) 18 | IITCButtonCommands() 19 | #endif 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /safari/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleExecutable 6 | $(EXECUTABLE_NAME) 7 | CFBundleShortVersionString 8 | PACKAGE_VERSION 9 | CFBundleDevelopmentRegion 10 | $(DEVELOPMENT_LANGUAGE) 11 | UILaunchScreen 12 | 13 | UILaunchScreen 14 | 15 | 16 | CFBundlePackageType 17 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 18 | UISupportedInterfaceOrientations 19 | 20 | UIInterfaceOrientationPortrait 21 | UIInterfaceOrientationLandscapeLeft 22 | UIInterfaceOrientationLandscapeRight 23 | UIInterfaceOrientationPortraitUpsideDown 24 | 25 | UIApplicationSceneManifest - 2 26 | 27 | UIApplicationSupportsMultipleScenes 28 | 29 | 30 | LSRequiresIPhoneOS 31 | 32 | CFBundleIdentifier 33 | $(PRODUCT_BUNDLE_IDENTIFIER) 34 | CFBundleVersion 35 | GIT_COMMIT_COUNT 36 | CFBundleInfoDictionaryVersion 37 | 6.0 38 | CFBundleName 39 | $(PRODUCT_NAME) 40 | UIApplicationSupportsIndirectInputEvents 41 | 42 | UIApplicationSceneManifest 43 | 44 | UIApplicationSupportsMultipleScenes 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /safari/iOS/Views/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // iOS 4 | // 5 | // Created by Lucka on 6/10/2021. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | 12 | @State private var presentingHelpSheet = false 13 | 14 | var body: some View { 15 | NavigationView { 16 | List { 17 | Button(action: { presentingHelpSheet.toggle() }) { 18 | Label("How to use", systemImage: "questionmark") 19 | } 20 | Link("Open Intel Maps", systemImage: "target", destination: .init(string: "https://intel.ingress.com")!) 21 | 22 | Section("About") { 23 | about 24 | } 25 | } 26 | .navigationTitle("IITC Button") 27 | } 28 | .navigationViewStyle(.stack) 29 | .sheet(isPresented: $presentingHelpSheet) { 30 | Helper() 31 | } 32 | } 33 | 34 | @ViewBuilder 35 | private var about: some View { 36 | Link("Homepage", systemImage: "house", destination: .init(string: "https://iitc.app")!) 37 | Link("Telegram Channel", systemImage: "paperplane.circle.fill", destination: .init(string: "https://t.me/iitc_news")!) 38 | Link("Source Code", systemImage: "swift", destination: .init(string: "https://github.com/IITC-CE/IITC-Button")!) 39 | Label("Version", systemImage: "info") 40 | .badge(version) 41 | } 42 | 43 | private var version: String { 44 | guard let infoDict = Bundle.main.infoDictionary else { 45 | return "Unknown" 46 | } 47 | let shortVersion = infoDict["CFBundleShortVersionString"] as? String ?? "Unknown" 48 | let build = infoDict["CFBundleVersion"] as? String ?? "Unknown" 49 | return "\(shortVersion) (\(build))" 50 | } 51 | } 52 | 53 | #if DEBUG 54 | struct ContentView_Previews: PreviewProvider { 55 | static var previews: some View { 56 | ContentView() 57 | } 58 | } 59 | #endif 60 | 61 | fileprivate extension Link where Label == SwiftUI.Label { 62 | init(_ titleKey: LocalizedStringKey, systemImage name: String, destination: URL) { 63 | self.init(destination: destination) { 64 | SwiftUI.Label(titleKey, systemImage: name) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /safari/iOS/Views/Helper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Helper.swift 3 | // iOS 4 | // 5 | // Created by Lucka on 6/10/2021. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct Helper: View { 11 | 12 | @Environment(\.dismiss) private var dismiss 13 | 14 | var body: some View { 15 | NavigationView { 16 | VStack(alignment: .leading) { 17 | step(1, content: Link("Open Settings \(Image(systemName: "chevron.forward"))", destination: .init(string: UIApplication.openSettingsURLString)!)) 18 | step(2, content: Text("Open Safari → Extensions")) 19 | step(3, content: Text("Enable the IITC Button extension")) 20 | step(4, content: Text("Open Intel Map in Safari")) 21 | HStack { 22 | Spacer() 23 | } 24 | Spacer() 25 | } 26 | .listStyle(.plain) 27 | .navigationTitle("How to use") 28 | .navigationViewStyle(.stack) 29 | .toolbar { 30 | Button("Dismiss") { dismiss() } 31 | } 32 | } 33 | } 34 | 35 | @ViewBuilder 36 | private func step(_ index: Int, content: Content) -> some View { 37 | HStack { 38 | Label("Step \(index)", systemImage: "\(index)") 39 | .labelStyle(.iconOnly) 40 | .symbolVariant(.square.fill) 41 | .font(.largeTitle) 42 | .foregroundColor(.accentColor) 43 | content 44 | } 45 | .padding() 46 | } 47 | } 48 | 49 | #if DEBUG 50 | struct Helper_Previews: PreviewProvider { 51 | static var previews: some View { 52 | ContentView() 53 | } 54 | } 55 | #endif 56 | -------------------------------------------------------------------------------- /safari/macOS/IITCButtonCommands.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IITCButtonCommands.swift 3 | // macOS 4 | // 5 | // Created by Lucka on 6/10/2021. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct IITCButtonCommands: Commands { 11 | 12 | @Environment(\.openURL) private var openURL 13 | 14 | var body: some Commands { 15 | CommandGroup(after: .help) { 16 | Divider() 17 | Button("Homepage") { openURL(.init(string: "https://iitc.app")!) } 18 | Button("Telegram Channel") { openURL(.init(string: "https://t.me/iitc_news")!) } 19 | Button("Source Code") { openURL(.init(string: "https://github.com/IITC-CE/IITC-Button")!) } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /safari/macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleName 6 | $(PRODUCT_NAME) 7 | CFBundleVersion 8 | GIT_COMMIT_COUNT 9 | NSHumanReadableCopyright 10 | 11 | CFBundlePackageType 12 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 13 | CFBundleDevelopmentRegion 14 | $(DEVELOPMENT_LANGUAGE) 15 | LSApplicationCategoryType 16 | public.app-category.utilities 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundleShortVersionString 20 | PACKAGE_VERSION 21 | CFBundleExecutable 22 | $(EXECUTABLE_NAME) 23 | CFBundleIdentifier 24 | $(PRODUCT_BUNDLE_IDENTIFIER) 25 | 26 | 27 | -------------------------------------------------------------------------------- /safari/macOS/Views/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Shared 4 | // 5 | // Created by Lucka on 3/10/2021. 6 | // 7 | 8 | import SwiftUI 9 | import SafariServices 10 | 11 | struct ContentView: View { 12 | 13 | private enum ExtensionStatus { 14 | case unknown 15 | case disabled 16 | case enabled 17 | } 18 | 19 | static private let extensionId = "org.exarhteam.iitc-button.Safari-Extension" 20 | 21 | @Environment(\.scenePhase) private var scenePhase : ScenePhase 22 | @State private var extensionStatus = ExtensionStatus.unknown 23 | 24 | var body: some View { 25 | VStack { 26 | Image(nsImage: NSApplication.shared.applicationIconImage) 27 | extensionStateIndicator 28 | Button("Open Safari Extension Preference", action: showPreferencesForExtension) 29 | } 30 | .frame(minWidth: 240) 31 | .padding() 32 | } 33 | 34 | @ViewBuilder 35 | private var extensionStateIndicator: some View { 36 | HStack { 37 | Text("Safari Extension") 38 | Spacer() 39 | Text(statusText) 40 | .foregroundColor(statusColor) 41 | } 42 | .onChange(of: scenePhase) { phase in 43 | if phase == .active { 44 | updateExtensionStatus() 45 | } 46 | } 47 | } 48 | 49 | private var statusText: LocalizedStringKey { 50 | switch extensionStatus { 51 | case .unknown: return "Unknown" 52 | case .disabled: return "Disabled" 53 | case .enabled: return "Enabled" 54 | } 55 | } 56 | 57 | private var statusColor: Color { 58 | switch extensionStatus { 59 | case .unknown: return .orange 60 | case .disabled: return .red 61 | case .enabled: return .green 62 | } 63 | } 64 | 65 | private func updateExtensionStatus() { 66 | SFSafariExtensionManager.getStateOfSafariExtension(withIdentifier: Self.extensionId) { state, error in 67 | guard let solidState = state, error == nil else { 68 | extensionStatus = .unknown 69 | return 70 | } 71 | DispatchQueue.main.async { 72 | extensionStatus = solidState.isEnabled ? .enabled : .disabled 73 | } 74 | } 75 | } 76 | 77 | private func showPreferencesForExtension() { 78 | SFSafariApplication.showPreferencesForExtension(withIdentifier: Self.extensionId) 79 | } 80 | } 81 | 82 | struct ContentView_Previews: PreviewProvider { 83 | static var previews: some View { 84 | ContentView() 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /safari/macOS/macOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/background/injector.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import browser from "webextension-polyfill"; 4 | import { gm_api_for_plugin } from "@/userscripts/wrapper"; 5 | import { getNiaTabsToInject, getPluginMatches } from "@/background/utils"; 6 | import { is_userscripts_api_available } from "@/userscripts/utils"; 7 | import { IS_LEGACY_API } from "@/userscripts/env"; 8 | 9 | export async function inject_plugin_via_content_scripts(plugin, use_gm_api) { 10 | const tabs = await getNiaTabsToInject(plugin); 11 | for (let tab of Object.values(tabs)) { 12 | const pluginTab = { ...plugin }; 13 | if (use_gm_api) { 14 | pluginTab.code = await gm_api_for_plugin(pluginTab, tab.id); 15 | } 16 | 17 | try { 18 | if (IS_LEGACY_API) { 19 | const inject = ` 20 | document.dispatchEvent(new CustomEvent('IITCButtonInitJS', { 21 | detail: ${JSON.stringify({ plugin: pluginTab })} 22 | })); 23 | `; 24 | await browser.tabs.executeScript(tab.id, { 25 | code: inject, 26 | runAt: "document_end", 27 | }); 28 | } else { 29 | await browser.scripting.executeScript({ 30 | target: { tabId: tab.id }, 31 | func: (pluginDetail) => { 32 | document.dispatchEvent( 33 | new CustomEvent("IITCButtonInitJS", { 34 | detail: pluginDetail, 35 | }) 36 | ); 37 | }, 38 | args: [{ plugin: pluginTab }], 39 | injectImmediately: true, 40 | }); 41 | } 42 | } catch (error) { 43 | console.error( 44 | `An error occurred while injecting script: ${error.message}` 45 | ); 46 | } 47 | } 48 | } 49 | 50 | export async function manage_userscripts_api(plugins_event) { 51 | if (!is_userscripts_api_available()) return; 52 | 53 | const event = plugins_event.event; 54 | const plugins = plugins_event.plugins; 55 | const use_gm_api = plugins_event.use_gm_api !== false; 56 | 57 | if (event === "remove") { 58 | const remove_ids = Object.keys(plugins); 59 | try { 60 | await browser.userScripts.unregister({ ids: remove_ids }); 61 | return; 62 | } catch (e) { 63 | console.log("an error occurred while unregistering the plugin", e); 64 | } 65 | } 66 | 67 | let plugins_obj = []; 68 | for (let plugin of Object.values(plugins)) { 69 | if (use_gm_api) { 70 | plugin.code = await gm_api_for_plugin(plugin, 0); 71 | } 72 | plugins_obj.push({ 73 | id: plugin.uid, 74 | matches: 75 | plugin.uid === "gm_api" ? ["https://*/*"] : getPluginMatches(plugin), 76 | js: [{ code: plugin.code }], 77 | runAt: plugin.uid === "gm_api" ? "document_start" : "document_end", 78 | world: "MAIN", 79 | }); 80 | } 81 | 82 | try { 83 | const registeredScripts = await browser.userScripts.getScripts(); 84 | const registeredIds = registeredScripts.map((script) => script.id); 85 | 86 | const pluginsToAdd = plugins_obj.filter( 87 | (plugin) => !registeredIds.includes(plugin.id) 88 | ); 89 | const pluginsToUpdate = plugins_obj.filter((plugin) => 90 | registeredIds.includes(plugin.id) 91 | ); 92 | 93 | try { 94 | await browser.userScripts.register(pluginsToAdd); 95 | } catch (e) { 96 | console.log("an error occurred while registering the plugin", e); 97 | } 98 | 99 | try { 100 | await browser.userScripts.update(pluginsToUpdate); 101 | } catch (e) { 102 | console.log("an error occurred while updating the plugin", e); 103 | } 104 | } catch (e) { 105 | console.log("an error occurred while handling the plugin event", e); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/background/intel.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | import browser from "webextension-polyfill"; 3 | import { getTabsToInject } from "@/background/utils"; 4 | import { is_iitc_enabled } from "@/userscripts/utils"; 5 | import { IS_USERSCRIPTS_API } from "@/userscripts/env"; 6 | 7 | const activeTabs = []; 8 | let lastIITCTab = null; 9 | 10 | export async function onRequestOpenIntel() { 11 | if (lastIITCTab) { 12 | const tabInfo = await getTabInfo(lastIITCTab); 13 | if (tabInfo && isIngressIntelUrl(tabInfo.url)) { 14 | return await setTabActive(lastIITCTab); 15 | } 16 | } 17 | 18 | try { 19 | const tab = await browser.tabs.create({ 20 | url: "https://intel.ingress.com/", 21 | pinned: true, 22 | }); 23 | lastIITCTab = tab.id; 24 | } catch (error) { 25 | console.error(`An error occurred while open tab: ${error.message}`); 26 | } 27 | } 28 | 29 | export async function onToggleIITC(status) { 30 | await browser.storage.local.set({ IITC_is_enabled: status }); 31 | 32 | if (IS_USERSCRIPTS_API) { 33 | if (status === false) { 34 | try { 35 | await browser.userScripts.unregister(); 36 | // eslint-disable-next-line no-empty 37 | } catch {} 38 | } 39 | } else { 40 | // Fetch all completly loaded Ingress Intel tabs 41 | const tabs = await getTabsToInject(); 42 | 43 | for (let tab of Object.values(tabs)) { 44 | if (isIngressIntelUrl(tab.url)) { 45 | await browser.tabs.reload(tab.id); 46 | } 47 | } 48 | } 49 | } 50 | 51 | function removeTabFromActiveTabs(tabId) { 52 | const index = activeTabs.indexOf(tabId); 53 | if (index !== -1) { 54 | activeTabs.splice(index, 1); 55 | } 56 | } 57 | 58 | // tab listeners 59 | export async function onUpdatedListener(tabId, status, tab, manager) { 60 | if (status.status !== "complete" && activeTabs.includes(tabId)) { 61 | removeTabFromActiveTabs(tabId); 62 | } 63 | // Prevent reinitialization on the same page 64 | if (status.status === "complete" && !activeTabs.includes(tabId)) { 65 | activeTabs.push(tabId); 66 | await initialize(manager); 67 | if (isIngressIntelUrl(tab.url)) { 68 | lastIITCTab = tabId; 69 | } 70 | } 71 | } 72 | 73 | export function onRemovedListener(tabId) { 74 | removeTabFromActiveTabs(tabId); 75 | if (lastIITCTab === tabId) { 76 | lastIITCTab = null; 77 | } 78 | } 79 | 80 | async function initialize(manager) { 81 | const status = await is_iitc_enabled(); 82 | if (status) { 83 | await manager.inject(); 84 | } 85 | } 86 | 87 | async function setTabActive(tabId) { 88 | const tab = await browser.tabs.update(tabId, { 89 | active: true, 90 | }); 91 | 92 | try { 93 | await browser.windows.update(tab.windowId, { focused: true }); 94 | } catch (error) { 95 | lastIITCTab = null; 96 | await onRequestOpenIntel(); 97 | } 98 | } 99 | 100 | async function getTabInfo(tabId) { 101 | try { 102 | return await browser.tabs.get(tabId); 103 | } catch { 104 | return null; 105 | } 106 | } 107 | 108 | function isIngressIntelUrl(url) { 109 | if (url) { 110 | return /^https:\/\/intel\.ingress\.com/.test(url); 111 | } 112 | return false; 113 | } 114 | -------------------------------------------------------------------------------- /src/background/utils.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import browser from "webextension-polyfill"; 4 | import { check_matching } from "lib-iitc-manager"; 5 | 6 | const is_ingress_tab = (url) => { 7 | return /https:\/\/(intel|missions).ingress.com\/*/.test(url); 8 | }; 9 | 10 | // Fetch all completly loaded tabs 11 | export async function getTabsToInject() { 12 | let allTabs = await browser.tabs.query({ status: "complete" }); 13 | return allTabs.filter(function (tab) { 14 | return tab.status === "complete" && tab.url; 15 | }); 16 | } 17 | 18 | // Filter all completly loaded Ingress Intel tabs 19 | export async function getNiaTabsToInject(plugin) { 20 | const tabs = await getTabsToInject(); 21 | return Object.values(tabs).filter( 22 | (tab) => 23 | (is_ingress_tab(tab.url) && check_matching(plugin, "")) || 24 | check_matching(plugin, tab.url) 25 | ); 26 | } 27 | 28 | export function getPluginMatches(plugin) { 29 | let matches = []; 30 | if (check_matching(plugin, "")) { 31 | matches.push("https://intel.ingress.com/*"); 32 | matches.push("https://missions.ingress.com/*"); 33 | } 34 | if (plugin.match) { 35 | matches = matches.concat(plugin.match); 36 | } else if (plugin.include) { 37 | matches = matches.concat(plugin.include); 38 | } 39 | return matches; 40 | } 41 | -------------------------------------------------------------------------------- /src/background/xhr-fallback.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import { strToBase64 } from "@/strToBase64"; 4 | import { IS_USERSCRIPTS_API } from "@/userscripts/env"; 5 | 6 | // Execution in the context of an extension, to bypass CORS policy. 7 | export async function xmlHttpRequestFallbackHandler(data) { 8 | async function xmlResponse(tab_id, callback, response) { 9 | const detail_stringify = JSON.stringify({ 10 | task_uuid: data.task_uuid, 11 | task_type: data.task_type, 12 | response: JSON.stringify(response), 13 | }); 14 | 15 | const bridge_data = strToBase64(String(detail_stringify)); 16 | 17 | let allTabs = [ 18 | { 19 | id: data.tab_id, 20 | }, 21 | ]; 22 | if (IS_USERSCRIPTS_API) { 23 | allTabs = await browser.tabs.query({ active: true }); 24 | } 25 | 26 | for (const tab of allTabs) { 27 | await browser.tabs.sendMessage(tab.id, { 28 | type: "XHRFallbackResponse", 29 | value: bridge_data, 30 | }); 31 | } 32 | } 33 | 34 | try { 35 | const response = await fetch(data.url, { 36 | mode: "no-cors", 37 | method: data.method, 38 | headers: data.headers, 39 | body: data.method !== "GET" ? data.data : undefined, 40 | credentials: data.user && data.password ? "include" : "same-origin", 41 | }); 42 | 43 | const text = await response.text(); 44 | 45 | // Create a response object similar to the one in XMLHttpRequest 46 | const responseObject = { 47 | readyState: 4, 48 | responseHeaders: "Not directly accessible with fetch", 49 | responseText: text, 50 | status: response.status, 51 | statusText: response.statusText, 52 | }; 53 | 54 | await xmlResponse(data.tab_id, data.onload, responseObject); 55 | } catch (error) { 56 | console.error("Fetch error:", error); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/content-scripts/bridge-manager.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import { sendXhrRequest } from "./xhr-bridge"; 4 | import { handleStorageRequest } from "./storage-bridge"; 5 | import { isRunContentScript } from "@/content-scripts/utils"; 6 | 7 | // Flag to prevent multiple initializations 8 | let isInitialized = false; 9 | 10 | /** 11 | * Initialize all communication bridges 12 | */ 13 | export function setupBridges() { 14 | if (isInitialized) return; 15 | isInitialized = true; 16 | 17 | // Set up main event listener for bridge requests 18 | document.addEventListener("bridgeRequest", bridgeAction); 19 | } 20 | 21 | /** 22 | * Process incoming bridge requests from page context 23 | */ 24 | export function bridgeAction(e) { 25 | const task = e.detail; 26 | 27 | switch (task.task_type) { 28 | case "xmlHttpRequest": 29 | sendXhrRequest(task); 30 | break; 31 | case "getStorage": 32 | case "setValue": 33 | case "delValue": 34 | handleStorageRequest(task).then(); 35 | break; 36 | default: 37 | console.warn("IITC Button: Unknown bridge request type", task.task_type); 38 | break; 39 | } 40 | } 41 | 42 | // Fallback XHR 43 | if (isRunContentScript) { 44 | browser.runtime.onMessage.addListener(async (request) => { 45 | switch (request.type) { 46 | case "XHRFallbackResponse": 47 | bridgeResponse(request.value); 48 | break; 49 | } 50 | }); 51 | } 52 | 53 | /** 54 | * Send response back to page context 55 | */ 56 | export function bridgeResponse(data) { 57 | dispatchEvent( 58 | new CustomEvent("bridgeResponse", { 59 | detail: data, 60 | }) 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /src/content-scripts/loader.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import { IITCButtonInitJS, isRunContentScript } from "./utils"; 4 | import { setupBridges } from "@/content-scripts/bridge-manager"; 5 | import { inject_gm_api } from "@/userscripts/wrapper"; 6 | import { IS_USERSCRIPTS_API } from "@/userscripts/env"; 7 | import { is_iitc_enabled } from "@/userscripts/utils"; 8 | 9 | function preparePage() { 10 | // Initialize all communication bridges 11 | setupBridges(); 12 | 13 | if (IS_USERSCRIPTS_API) return; 14 | 15 | inject_gm_api(); 16 | document.addEventListener("IITCButtonInitJS", IITCButtonInitJS); 17 | } 18 | 19 | if (isRunContentScript) { 20 | window.iitcbutton = true; 21 | is_iitc_enabled().then((status) => { 22 | if (status) { 23 | preparePage(); 24 | } 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /src/content-scripts/storage-bridge.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import browser from "webextension-polyfill"; 4 | import { strToBase64 } from "@/strToBase64"; 5 | import { bridgeResponse } from "./bridge-manager"; 6 | 7 | /** 8 | * Handle storage-related requests 9 | */ 10 | export async function handleStorageRequest(task) { 11 | switch (task.task_type) { 12 | case "getStorage": 13 | await getStorageBridge(task); 14 | break; 15 | case "setValue": 16 | await setValueBridge(task); 17 | break; 18 | case "delValue": 19 | await deleteValueBridge(task); 20 | break; 21 | } 22 | } 23 | 24 | /** 25 | * Get all plugin storage data and send it to page context 26 | */ 27 | async function getStorageBridge(req) { 28 | try { 29 | const all_storage = await browser.storage.local.get(null); 30 | const plugins_storage = {}; 31 | 32 | // Filter for plugin-related storage keys 33 | for (const key in all_storage) { 34 | if (key.startsWith("VMin")) { 35 | plugins_storage[key] = all_storage[key]; 36 | } 37 | } 38 | 39 | const detail_stringify = JSON.stringify({ 40 | task_type: req.task_type, 41 | response: JSON.stringify(plugins_storage), 42 | }); 43 | 44 | const bridge_base64_data = strToBase64(String(detail_stringify)); 45 | bridgeResponse(bridge_base64_data); 46 | } catch (error) { 47 | console.error("IITC Button Storage Bridge: Error getting storage", error); 48 | } 49 | } 50 | 51 | /** 52 | * Save value to extension storage 53 | */ 54 | export async function setValueBridge(req) { 55 | try { 56 | const set_data = {}; 57 | set_data[req.key] = req.value; 58 | await browser.storage.local.set(set_data); 59 | } catch (error) { 60 | console.error("IITC Button Storage Bridge: Error setting value", error); 61 | } 62 | } 63 | 64 | /** 65 | * Delete value from extension storage 66 | */ 67 | export async function deleteValueBridge(req) { 68 | try { 69 | await browser.storage.local.remove(req.key); 70 | } catch (error) { 71 | console.error("IITC Button Storage Bridge: Error removing value", error); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/content-scripts/utils.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import { getUID } from "lib-iitc-manager"; 4 | 5 | const LOADED_PLUGINS = []; 6 | 7 | export const isRunContentScript = !window.iitcbutton; 8 | 9 | export function inject(code) { 10 | const script = document.createElement("script"); 11 | script.appendChild(document.createTextNode(code)); 12 | (document.body || document.head || document.documentElement).appendChild( 13 | script 14 | ); 15 | script.parentElement.removeChild(script); 16 | } 17 | 18 | export async function IITCButtonInitJS(e) { 19 | const plugin = e.detail.plugin; 20 | 21 | const uid = plugin.uid ? plugin.uid : getUID(plugin); 22 | if (LOADED_PLUGINS.includes(uid)) { 23 | console.debug(`Plugin ${uid} is already loaded. Skip`); 24 | } else { 25 | LOADED_PLUGINS.push(uid); 26 | console.debug(`Plugin ${uid} loaded`); 27 | inject(plugin.code); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/content-scripts/xhr-bridge.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import browser from "webextension-polyfill"; 4 | import { strToBase64 } from "@/strToBase64"; 5 | import { IS_SAFARI } from "@/userscripts/env"; 6 | 7 | let xhrIframe = null; 8 | let pendingRequests = []; 9 | let messageListener = null; 10 | 11 | // Retry tracking 12 | let retryCount = 0; 13 | const MAX_RETRIES = 3; 14 | 15 | // Create sandbox iframe 16 | function createIframe() { 17 | try { 18 | // Don't create if already exists 19 | if (xhrIframe) return; 20 | 21 | // Add message listener before creating iframe 22 | if (messageListener) { 23 | window.removeEventListener("message", messageListener); 24 | } 25 | 26 | messageListener = function (event) { 27 | // Process response messages from iframe 28 | if (event.data && event.data.type === "xhr_response") { 29 | handleXhrResponseData(event.data); 30 | } 31 | }; 32 | 33 | window.addEventListener("message", messageListener); 34 | 35 | // Create hidden iframe with sandbox for XHR 36 | xhrIframe = document.createElement("iframe"); 37 | xhrIframe.sandbox = "allow-scripts allow-same-origin"; 38 | xhrIframe.style.cssText = 39 | "display:none; position:absolute; width:0; height:0; border:0;"; 40 | xhrIframe.src = browser.runtime.getURL("xhr-sandbox.html"); 41 | 42 | // Add to DOM 43 | document.body.appendChild(xhrIframe); 44 | 45 | // Process pending requests when iframe loads 46 | xhrIframe.onload = function () { 47 | if (pendingRequests.length > 0) { 48 | pendingRequests.forEach((data) => sendXhrRequest(data)); 49 | pendingRequests = []; 50 | } 51 | }; 52 | } catch (error) { 53 | console.error("IITC Button XHR Bridge: failed to create iframe", error); 54 | } 55 | } 56 | 57 | // Process response data from iframe 58 | function handleXhrResponseData(data) { 59 | try { 60 | const detail_stringify = JSON.stringify({ 61 | task_uuid: data.uuid, 62 | task_type: "xmlHttpRequest", 63 | response: JSON.stringify(data.response), 64 | }); 65 | 66 | const bridge_data = strToBase64(String(detail_stringify)); 67 | 68 | // Send result in the same format as background would do 69 | window.dispatchEvent( 70 | new CustomEvent("bridgeResponse", { 71 | detail: bridge_data, 72 | }) 73 | ); 74 | } catch (error) { 75 | console.error("IITC Button XHR Bridge: error handling response", error); 76 | } 77 | } 78 | 79 | // Send XHR request 80 | export function sendXhrRequest(data) { 81 | // For Safari, use fetch fallback 82 | if (IS_SAFARI) { 83 | browser.runtime 84 | .sendMessage({ 85 | type: "XHRFallbackRequest", 86 | value: data, 87 | }) 88 | .then(); 89 | return; 90 | } 91 | 92 | // For other browsers, use iframe 93 | // Queue request if iframe not ready 94 | if (!xhrIframe || !xhrIframe.contentWindow) { 95 | pendingRequests.push(data); 96 | 97 | // Create iframe if it doesn't exist 98 | if (!xhrIframe) { 99 | createIframe(); 100 | 101 | // Add retry logic for iframe problems 102 | if (retryCount < MAX_RETRIES) { 103 | retryCount++; 104 | setTimeout(() => { 105 | if (pendingRequests.includes(data)) { 106 | sendXhrRequest(data); 107 | } 108 | }, 500 * retryCount); 109 | } 110 | } 111 | return; 112 | } 113 | 114 | retryCount = 0; 115 | 116 | try { 117 | // Send request to iframe 118 | xhrIframe.contentWindow.postMessage( 119 | { 120 | type: "xhr_request", 121 | uuid: data.task_uuid, 122 | method: data.method, 123 | url: data.url, 124 | headers: data.headers, 125 | data: data.data, 126 | user: data.user, 127 | password: data.password, 128 | overrideMimeType: data.overrideMimeType, 129 | timeout: data.timeout, 130 | withCredentials: data.withCredentials, 131 | }, 132 | "*" 133 | ); 134 | } catch (error) { 135 | console.error("IITC Button XHR Bridge: error sending request", error); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/i18n.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | import browser from "webextension-polyfill"; 3 | 4 | export function _(msg, arg) { 5 | return browser.i18n.getMessage(msg, arg); 6 | } 7 | -------------------------------------------------------------------------------- /src/jsview/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 79 | 80 | 86 | 87 | 99 | -------------------------------------------------------------------------------- /src/jsview/Code.vue: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 30 | 31 | 32 | 33 | 72 | -------------------------------------------------------------------------------- /src/jsview/highlightjs-lineToDiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | * highlightjs-lineToDiv 3 | * Plugin for highlightJS that transforms a line into a div element. 4 | * 5 | * https://github.com/deleterium/highlightjs-lineToDiv 6 | */ 7 | export const plugin = (result) => { 8 | let className, idName; 9 | /* Configuration */ 10 | className = "line"; 11 | idName = "code%line%"; 12 | /* end of configuration */ 13 | const htmlLines = result.value.split("\n"); 14 | let spanStack = []; 15 | result.value = htmlLines 16 | .map((content, index) => { 17 | let startSpanIndex, endSpanIndex; 18 | let needle = 0; 19 | content = spanStack.join("") + content; 20 | spanStack = []; 21 | do { 22 | const remainingContent = content.slice(needle); 23 | startSpanIndex = remainingContent.indexOf("/.exec(remainingContent); 33 | if (nextSpan === null) { 34 | // never: but ensure no exception is raised if it happens some day. 35 | break; 36 | } 37 | spanStack.push(nextSpan[0]); 38 | needle += startSpanIndex + nextSpan[0].length; 39 | } else { 40 | spanStack.pop(); 41 | needle += endSpanIndex + 1; 42 | } 43 | // eslint-disable-next-line no-constant-condition 44 | } while (true); 45 | if (spanStack.length > 0) { 46 | content += Array(spanStack.length).fill("").join(""); 47 | } 48 | let retString = "
${content}
`; 57 | return retString; 58 | }) 59 | .join(""); 60 | }; 61 | -------------------------------------------------------------------------------- /src/jsview/main.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | import Vue from "vue"; 3 | import App from "./App.vue"; 4 | 5 | /* eslint-disable no-new */ 6 | new Vue({ 7 | el: "#app", 8 | render: (h) => h(App), 9 | }); 10 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IITC Button", 3 | "description": "__MSG_extDescription__", 4 | "minimum_chrome_version": "61", 5 | "browser_specific_settings": { 6 | "gecko": { 7 | "id": "iitc@modos189.ru", 8 | "strict_min_version": "57.0" 9 | }, 10 | "gecko_android": { 11 | "strict_min_version": "113.0" 12 | } 13 | }, 14 | "permissions": [ 15 | "tabs", 16 | "storage", 17 | "unlimitedStorage" 18 | ], 19 | "default_locale": "en", 20 | "background": {}, 21 | "browser_action": { 22 | "default_popup": "popup.html", 23 | "default_title": "__MSG_titleDefault__", 24 | "default_icon": { 25 | "48": "assets/icons/48/icon.png", 26 | "96": "assets/icons/96/icon.png" 27 | } 28 | }, 29 | "icons": { 30 | "48": "assets/icons/48/icon.png", 31 | "128": "assets/icons/128/icon.png" 32 | }, 33 | "web_accessible_resources": [ 34 | { 35 | "resources": [ 36 | "xhr-sandbox.html" 37 | ], 38 | "matches": [ 39 | "" 40 | ] 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /src/popup/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 68 | 69 | 70 | 71 | 72 | 174 | -------------------------------------------------------------------------------- /src/popup/components/Header.vue: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 31 | 32 | 55 | -------------------------------------------------------------------------------- /src/popup/components/Hr.vue: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 16 | -------------------------------------------------------------------------------- /src/popup/components/InputCustomServer.vue: -------------------------------------------------------------------------------- 1 | 2 | 38 | 39 | 123 | 124 | 165 | -------------------------------------------------------------------------------- /src/popup/components/Message.vue: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 32 | 33 | 71 | -------------------------------------------------------------------------------- /src/popup/components/SectionMainMenu/PluginList/NoData.vue: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 34 | -------------------------------------------------------------------------------- /src/popup/components/SectionMainMenu/PluginList/PluginList.vue: -------------------------------------------------------------------------------- 1 | 2 | 29 | 30 | 76 | 77 | 82 | -------------------------------------------------------------------------------- /src/popup/components/SectionMainMenu/PluginList/Title.vue: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 25 | 26 | 48 | -------------------------------------------------------------------------------- /src/popup/components/SectionMainMenu/ProgressBar.vue: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 25 | 26 | 95 | -------------------------------------------------------------------------------- /src/popup/components/SectionMainMenu/SearchBar.vue: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 41 | 42 | 79 | -------------------------------------------------------------------------------- /src/popup/components/SectionMainMenu/SectionMainMenu.vue: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 105 | 106 | 113 | -------------------------------------------------------------------------------- /src/popup/components/SectionMainMenu/Tags/Tag.vue: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 46 | 47 | 84 | -------------------------------------------------------------------------------- /src/popup/components/SectionMainMenu/Tags/Tags.vue: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 103 | 104 | 111 | -------------------------------------------------------------------------------- /src/popup/components/SectionMainMenu/Title.vue: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 49 | 50 | 82 | -------------------------------------------------------------------------------- /src/popup/components/SectionMainMenu/ToggleIITC.vue: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 49 | 50 | 90 | -------------------------------------------------------------------------------- /src/popup/components/UpdateCheckIntervalSelector.vue: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 64 | 65 | 89 | -------------------------------------------------------------------------------- /src/popup/components/mixins.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | import browser from "webextension-polyfill"; 3 | import { _ } from "@/i18n"; 4 | 5 | export const mixin = { 6 | data() { 7 | return { 8 | updateChannels: { 9 | release: { name: _("release") }, 10 | beta: { name: _("beta") }, 11 | custom: { name: _("custom") }, 12 | }, 13 | updateChannelsIntervals: { 14 | release: { name: _("release") }, 15 | beta: { name: _("beta") }, 16 | custom: { name: _("custom") }, 17 | external: { name: _("anyChannel") }, 18 | }, 19 | updateIntervals: [ 20 | { name: _("every5seconds"), value: "5" }, 21 | { name: _("every6hours"), value: "21600" }, 22 | { name: _("every12hours"), value: "43200" }, 23 | { name: _("everyDay"), value: "86400" }, 24 | { name: _("everyWeek"), value: "604800" }, 25 | ], 26 | }; 27 | }, 28 | methods: { 29 | _: _, 30 | __: function (key, item) { 31 | if (item === undefined || !(key in item)) return ""; 32 | const lang = browser.i18n.getUILanguage(); 33 | return `${key}:${lang}` in item ? item[`${key}:${lang}`] : item[key]; 34 | }, 35 | objIsEmpty: function (obj) { 36 | return typeof obj !== "object" || Object.keys(obj).length === 0; 37 | }, 38 | openLink: async function (url) { 39 | await browser.tabs.create({ url: url }); 40 | window.close(); 41 | }, 42 | back: function () { 43 | document.body.id = "main-menu"; 44 | }, 45 | sortIITCObj: function (obj) { 46 | if (obj === undefined) { 47 | return {}; 48 | } 49 | 50 | const arr = Object.entries(obj); 51 | arr.sort((a, b) => { 52 | const nameA = this.__("name", a[1]).toLowerCase(); 53 | const nameB = this.__("name", b[1]).toLowerCase(); 54 | return nameA > nameB ? 1 : -1; 55 | }); 56 | 57 | return Object.fromEntries(arr); 58 | }, 59 | showMessage: function (msg) { 60 | this.$root.$emit("message", msg); 61 | }, 62 | }, 63 | }; 64 | -------------------------------------------------------------------------------- /src/popup/data.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import browser from "webextension-polyfill"; 4 | 5 | export async function init(self) { 6 | const appData = self.$data; 7 | const data = await browser.storage.local.get([ 8 | "channel", 9 | "release_categories", 10 | "beta_categories", 11 | "custom_categories", 12 | "release_plugins_flat", 13 | "beta_plugins_flat", 14 | "custom_plugins_flat", 15 | "release_iitc_core", 16 | "beta_iitc_core", 17 | "custom_iitc_core", 18 | "release_iitc_core_user", 19 | "beta_iitc_core_user", 20 | "custom_iitc_core_user", 21 | ]); 22 | const channel = data.channel ? data.channel : "release"; 23 | // initialize categories 24 | appData.categories = data[channel + "_categories"]; 25 | // initialize all plugins 26 | appData.plugins_flat = data[channel + "_plugins_flat"]; 27 | // initialize iitc core 28 | setIitcCore( 29 | appData, 30 | data[channel + "_iitc_core"], 31 | data[channel + "_iitc_core_user"] 32 | ); 33 | } 34 | 35 | function setCategories(appData, categories) { 36 | appData.categories = {}; 37 | appData.categories = categories; 38 | } 39 | 40 | function setPlugins(appData, plugins_flat) { 41 | appData.plugins_flat = plugins_flat; 42 | const category_name = appData.category_name; 43 | if (category_name !== "") { 44 | if (appData.categories[category_name]) { 45 | appData.plugins = Object.entries(plugins_flat).reduce( 46 | (category_plugins, plugin_pair) => { 47 | const [plugin_uid, plugin_obj] = plugin_pair; 48 | if (plugin_obj.category === category_name) { 49 | category_plugins[plugin_uid] = plugin_obj; 50 | } 51 | return category_plugins; 52 | }, 53 | {} 54 | ); 55 | } else { 56 | appData.plugins = {}; 57 | } 58 | } 59 | } 60 | 61 | function setIitcCore(appData, iitc_core, iitc_core_user) { 62 | let core = iitc_core; 63 | if (iitc_core_user && iitc_core_user.code) { 64 | core = iitc_core_user; 65 | core.override = true; 66 | core.user = true; 67 | } 68 | appData.iitc_core = core; 69 | } 70 | 71 | export async function onChangedListener(self) { 72 | const appData = self.$data; 73 | browser.storage.onChanged.addListener(async function (changes) { 74 | const data = await browser.storage.local.get("channel"); 75 | const channel = data.channel ? data.channel : "release"; 76 | 77 | for (let key in changes) { 78 | const new_value = changes[key].newValue; 79 | 80 | if (key === "channel") { 81 | const storage = await browser.storage.local.get([ 82 | channel + "_categories", 83 | channel + "_plugins_flat", 84 | channel + "_iitc_core", 85 | channel + "_iitc_core_user", 86 | ]); 87 | setCategories(appData, storage[channel + "_categories"]); 88 | setPlugins(appData, storage[channel + "_plugins_flat"]); 89 | setIitcCore( 90 | appData, 91 | storage[channel + "_iitc_core"], 92 | storage[channel + "_iitc_core_user"] 93 | ); 94 | } 95 | 96 | if (key === channel + "_categories") { 97 | setCategories(appData, new_value); 98 | } 99 | 100 | if (key === channel + "_plugins_flat") { 101 | setPlugins(appData, new_value); 102 | } 103 | 104 | if (key === channel + "_iitc_core_user") { 105 | const storage = await browser.storage.local.get([ 106 | channel + "_iitc_core", 107 | ]); 108 | setIitcCore(appData, storage[channel + "_iitc_core"], new_value); 109 | } 110 | } 111 | }); 112 | } 113 | 114 | export async function onMessageListener(self) { 115 | browser.runtime.onMessage.addListener(function (request) { 116 | switch (request.type) { 117 | case "showProgressbar": 118 | self.$root.$emit("showProgressbar", request.value); 119 | break; 120 | case "showMessage": 121 | self.showMessage(request.message); 122 | break; 123 | } 124 | return new Promise((resolve) => { 125 | setTimeout(() => { 126 | resolve(); 127 | }, 1); 128 | }); 129 | }); 130 | } 131 | -------------------------------------------------------------------------------- /src/popup/main.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | import Vue from "vue"; 3 | import App from "./App.vue"; 4 | 5 | new Vue({ 6 | el: "#app", 7 | render: (h) => h(App), 8 | }); 9 | -------------------------------------------------------------------------------- /src/popup/search.js: -------------------------------------------------------------------------------- 1 | import browser from "webextension-polyfill"; 2 | 3 | import { fuzzysearch } from "scored-fuzzysearch"; 4 | 5 | const score = (query, text) => { 6 | if (text === undefined) return 0; 7 | return fuzzysearch(query, text); 8 | }; 9 | 10 | const setPluginScore = (p, q) => { 11 | const lang = browser.i18n.getUILanguage(); 12 | p._search_score = Math.max( 13 | score(q, p.name), 14 | score(q, p["name:" + lang]), 15 | score(q, p.description), 16 | score(q, p["description:" + lang]), 17 | score(q, p.category) 18 | ); 19 | return p; 20 | }; 21 | 22 | const sortScoredPlugins = (arr) => { 23 | for (let i = 0; i < arr.length; i++) { 24 | for (let j = i + 1; j < arr.length; j++) { 25 | if (arr[i]._search_score < arr[j]._search_score) { 26 | let swap = arr[i]; 27 | arr[i] = arr[j]; 28 | arr[j] = swap; 29 | } 30 | } 31 | } 32 | return arr; 33 | }; 34 | 35 | export function searchPlugins(query, plugins_obj) { 36 | const plugins = Object.keys(plugins_obj).map((key) => plugins_obj[key]); 37 | const scored_plugins = plugins.map((plugin) => setPluginScore(plugin, query)); 38 | const sorted_plugins = sortScoredPlugins(scored_plugins); 39 | const result_plugins = sorted_plugins.filter((p) => p._search_score); 40 | return result_plugins; 41 | } 42 | -------------------------------------------------------------------------------- /src/settings/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 50 | 51 | 57 | 58 | 89 | -------------------------------------------------------------------------------- /src/settings/Header.vue: -------------------------------------------------------------------------------- 1 | 2 | 34 | 35 | 48 | 49 | 50 | 51 | 118 | -------------------------------------------------------------------------------- /src/settings/add/BlockDrop.vue: -------------------------------------------------------------------------------- 1 | 2 | 33 | 34 | 125 | 126 | 161 | -------------------------------------------------------------------------------- /src/settings/add/BlockURL.vue: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 83 | 84 | 133 | -------------------------------------------------------------------------------- /src/settings/add/Main.vue: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 27 | 28 | 29 | 30 | 36 | -------------------------------------------------------------------------------- /src/settings/backup/utils.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | import JSZip from "jszip"; 3 | 4 | export const backup_json_name = "iitc.json"; 5 | 6 | export function formatDate(date) { 7 | const year = date.getFullYear(); 8 | const month = padZero(date.getMonth() + 1); 9 | const day = padZero(date.getDate()); 10 | const hours = padZero(date.getHours()); 11 | const minutes = padZero(date.getMinutes()); 12 | const seconds = padZero(date.getSeconds()); 13 | 14 | return ( 15 | year + "-" + month + "-" + day + "_" + hours + "." + minutes + "." + seconds 16 | ); 17 | } 18 | 19 | function padZero(number) { 20 | if (number < 10) { 21 | return "0" + number; 22 | } 23 | return number; 24 | } 25 | 26 | export const saveAs = (blob, fileName) => { 27 | const link = document.createElement("a"); 28 | // create a blobURI pointing to our Blob 29 | link.href = URL.createObjectURL(blob); 30 | link.download = fileName; 31 | // some browser needs the anchor to be in the doc 32 | document.body.append(link); 33 | link.click(); 34 | link.remove(); 35 | // in case the Blob uses a lot of memory 36 | setTimeout(() => URL.revokeObjectURL(link.href), 7000); 37 | }; 38 | 39 | export const getBackupDataFromZip = async (file) => { 40 | const backup = { 41 | external_plugins: {}, 42 | data: {}, 43 | }; 44 | const zip = await JSZip.loadAsync(file); 45 | 46 | for (const zipName in zip.files) { 47 | const zipEntry = zip.files[zipName]; 48 | if (!zipEntry.dir) { 49 | const filename = zipEntry.name; 50 | const filename_split = filename.split("/"); 51 | 52 | if (filename === backup_json_name) { 53 | // import iitc.json 54 | backup.data = JSON.parse(await zip.file(filename).async("string")); 55 | } else if ( 56 | filename_split.length > 1 && 57 | ["release", "beta", "custom"].includes(filename_split[0]) 58 | ) { 59 | // import external plugins 60 | const channel = filename_split[0]; 61 | const plugin_filename = filename_split[1]; 62 | if (!Object.hasOwn(backup.external_plugins, channel)) { 63 | backup.external_plugins[channel] = {}; 64 | } 65 | backup.external_plugins[channel][plugin_filename] = await zip 66 | .file(filename) 67 | .async("string"); 68 | } 69 | } 70 | } 71 | return backup; 72 | }; 73 | 74 | export const createBackupZip = async (backup) => { 75 | const zip = new JSZip(); 76 | 77 | zip.file(backup_json_name, JSON.stringify(backup.data)); 78 | 79 | for (const channel in backup.external_plugins) { 80 | for (const external_plugin_name in backup.external_plugins[channel]) { 81 | zip.file( 82 | `${channel}/${external_plugin_name}`, 83 | backup.external_plugins[channel][external_plugin_name] 84 | ); 85 | } 86 | } 87 | 88 | const filename = "iitc-backup_" + formatDate(new Date()) + ".zip"; 89 | zip.generateAsync({ type: "blob" }).then(function (content) { 90 | saveAs(content, filename); 91 | }); 92 | }; 93 | -------------------------------------------------------------------------------- /src/settings/debug/Main.vue: -------------------------------------------------------------------------------- 1 | 2 | 38 | 39 | 87 | 88 | 89 | 90 | 132 | -------------------------------------------------------------------------------- /src/settings/main.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | import Vue from "vue"; 3 | import App from "./App.vue"; 4 | 5 | /* eslint-disable no-new */ 6 | new Vue({ 7 | el: "#app", 8 | render: (h) => h(App), 9 | }); 10 | -------------------------------------------------------------------------------- /src/settings/utils.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | export const readUploadedFileAsText = (inputFile) => { 4 | const temporaryFileReader = new FileReader(); 5 | 6 | return new Promise((resolve, reject) => { 7 | temporaryFileReader.onerror = () => { 8 | temporaryFileReader.abort(); 9 | reject(new DOMException("Problem parsing input file.")); 10 | }; 11 | 12 | temporaryFileReader.onload = () => { 13 | resolve(temporaryFileReader.result); 14 | }; 15 | temporaryFileReader.readAsText(inputFile); 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /src/strToBase64.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | export function strToBase64(str) { 4 | const bytes = new TextEncoder().encode(str); 5 | const binString = String.fromCodePoint(...bytes); 6 | return btoa(binString); 7 | } 8 | -------------------------------------------------------------------------------- /src/userscripts/env.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import browser from "webextension-polyfill"; 4 | 5 | export const IS_CHROME = !!browser.runtime.OnInstalledReason?.CHROME_UPDATE; 6 | export const IS_SAFARI = 7 | /^((?!chrome|android).)*safari/i.test(navigator.userAgent) || 8 | (navigator.vendor && navigator.vendor.indexOf("Apple") > -1); 9 | export const MANIFEST = browser.runtime.getManifest(); 10 | 11 | export const IS_USERSCRIPTS_API = IS_CHROME && MANIFEST.manifest_version === 3; 12 | export const IS_SCRIPTING_API = !!browser.scripting; 13 | export const IS_LEGACY_API = !IS_USERSCRIPTS_API && !IS_SCRIPTING_API; 14 | -------------------------------------------------------------------------------- /src/userscripts/utils.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import browser from "webextension-polyfill"; 4 | import { inject_gm_api } from "@/userscripts/wrapper"; 5 | 6 | export async function is_iitc_enabled() { 7 | const status = await browser.storage.local 8 | .get(["IITC_is_enabled"]) 9 | .then((data) => data["IITC_is_enabled"]); 10 | return status !== false; 11 | } 12 | 13 | export function is_userscripts_api_available() { 14 | try { 15 | // Property access which throws if developer mode is not enabled. 16 | return browser.userScripts !== undefined; 17 | } catch { 18 | // Not available. 19 | return false; 20 | } 21 | } 22 | 23 | export const init_userscripts_api = () => { 24 | if (!is_userscripts_api_available()) return; 25 | try { 26 | browser.userScripts.configureWorld({ 27 | csp: "script-src 'self' 'unsafe-inline'", 28 | messaging: true, 29 | }); 30 | inject_gm_api(); 31 | // eslint-disable-next-line no-empty 32 | } catch {} 33 | }; 34 | -------------------------------------------------------------------------------- /src/userscripts/wrapper.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | import browser from "webextension-polyfill"; 4 | import { IS_USERSCRIPTS_API } from "@/userscripts/env"; 5 | import { manage_userscripts_api } from "@/background/injector"; 6 | import { strToBase64 } from "@/strToBase64"; 7 | import { getUID } from "lib-iitc-manager"; 8 | import { GM } from "@/userscripts/gm-api"; 9 | 10 | function getPluginHash(uid) { 11 | return "VMin" + strToBase64(uid); 12 | } 13 | 14 | function inject(code) { 15 | const script = document.createElement("script"); 16 | script.appendChild(document.createTextNode(code)); 17 | (document.body || document.head || document.documentElement).appendChild( 18 | script 19 | ); 20 | script.parentElement.removeChild(script); 21 | } 22 | 23 | export function inject_gm_api() { 24 | const plugin = { 25 | uid: "gm_api", 26 | code: `((${GM.toString()}))()\n//# sourceURL=${browser.runtime.getURL( 27 | "js/GM_api.js" 28 | )}`, 29 | }; 30 | 31 | if (IS_USERSCRIPTS_API) { 32 | const plugins_event = { 33 | event: "add", 34 | use_gm_api: false, 35 | plugins: [plugin], 36 | }; 37 | manage_userscripts_api(plugins_event).then(); 38 | } else { 39 | inject(plugin.code); 40 | } 41 | } 42 | 43 | export async function gm_api_for_plugin(plugin, tab_id) { 44 | const uid = plugin.uid ? plugin.uid : getUID(plugin); 45 | let data_key = getPluginHash(uid); 46 | const name = encodeURIComponent(plugin.name); 47 | 48 | const meta = { ...plugin }; 49 | delete meta.code; 50 | 51 | return [ 52 | "((GM)=>{", 53 | // an implementation of GM API v3 based on GM API v4 54 | "const GM_info = GM.info; const unsafeWindow = window;", 55 | "const exportFunction = GM.exportFunction; const createObjectIn = GM.createObjectIn; const cloneInto = GM.cloneInto;", 56 | "const GM_getValue = (key, value) => GM._getValueSync(key, value);", 57 | "const GM_setValue = (key, value) => GM._setValueSync(key, value);", 58 | "const GM_xmlhttpRequest = (details) => GM.xmlHttpRequest(details);", 59 | 60 | plugin.code, 61 | // adding a new line in case the code ends with a line comment 62 | plugin.code.endsWith("\n") ? "" : "\n", 63 | `})(GM("${data_key}", ${tab_id}, ${JSON.stringify(meta)}))`, 64 | 65 | // Firefox lists .user.js among our own content scripts so a space at start will group them 66 | `\n//# sourceURL=${browser.runtime.getURL( 67 | "plugins/%20" + name + ".user.js" 68 | )}`, 69 | ].join(""); 70 | } 71 | -------------------------------------------------------------------------------- /src/uuid.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | export function uuidv4() { 4 | return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => 5 | ( 6 | c ^ 7 | (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4))) 8 | ).toString(16) 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/xhr-sandbox.js: -------------------------------------------------------------------------------- 1 | //@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3 2 | 3 | // XHR Handler for sandbox iframe 4 | window.addEventListener("message", (event) => { 5 | if (event.data && event.data.type === "xhr_request") { 6 | const data = event.data; 7 | const req = new XMLHttpRequest(); 8 | 9 | // Function to send response 10 | const sendResponse = (response) => { 11 | try { 12 | window.parent.postMessage( 13 | { 14 | type: "xhr_response", 15 | uuid: data.uuid, 16 | response: response, 17 | }, 18 | "*" 19 | ); 20 | } catch (error) { 21 | console.error("IITC Button XHR Sandbox: error sending response", error); 22 | } 23 | }; 24 | 25 | // Event handlers 26 | req.onload = function () { 27 | sendResponse({ 28 | readyState: this.readyState, 29 | responseHeaders: this.getAllResponseHeaders(), 30 | responseText: this.responseText, 31 | status: this.status, 32 | statusText: this.statusText, 33 | }); 34 | }; 35 | 36 | req.onerror = function () { 37 | sendResponse({ 38 | readyState: this.readyState, 39 | status: 0, 40 | statusText: "Error", 41 | responseText: "", 42 | responseHeaders: "", 43 | }); 44 | }; 45 | 46 | req.ontimeout = function () { 47 | sendResponse({ 48 | readyState: this.readyState, 49 | status: 0, 50 | statusText: "Timeout", 51 | responseText: "", 52 | responseHeaders: "", 53 | }); 54 | }; 55 | 56 | try { 57 | // Open connection 58 | req.open(data.method, data.url, true, data.user, data.password); 59 | 60 | // Set headers 61 | if (data.headers) { 62 | for (const [name, value] of Object.entries(data.headers)) { 63 | req.setRequestHeader(name, value); 64 | } 65 | } 66 | 67 | // Additional settings 68 | if (data.overrideMimeType) req.overrideMimeType(data.overrideMimeType); 69 | if (data.timeout) req.timeout = data.timeout; 70 | if (data.withCredentials) req.withCredentials = true; 71 | 72 | // Send request 73 | req.send(data.data); 74 | } catch (error) { 75 | sendResponse({ 76 | readyState: 0, 77 | status: 0, 78 | statusText: "Error: " + error.message, 79 | responseText: "", 80 | responseHeaders: "", 81 | }); 82 | } 83 | } 84 | }); 85 | --------------------------------------------------------------------------------