diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5eead0b..e2d893c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,7 +13,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Release Version Check" if: ${{ github.event_name == 'release' }} @@ -25,9 +25,9 @@ jobs: fi - name: "Setup Node" - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 - name: "Build All" run: | @@ -35,7 +35,7 @@ jobs: npm run build - name: "Upload to Actions" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: artifacts path: web-ext-artifacts/ diff --git a/.prettierrc.json b/.prettierrc.json index f85af66..cf655f2 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -5,13 +5,13 @@ "singleQuote": true, "overrides": [ { - "files": ["**/*.html"], + "files": ["**/*.html", "**/*.yaml"], "options": { "singleQuote": false } }, { - "files": ["**/*.json"], + "files": ["**/*.json", "**/*.yaml"], "options": { "tabWidth": 2 } diff --git a/README.md b/README.md index e9d7e48..e63d979 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ [![Chrome Web Store Users](https://img.shields.io/chrome-web-store/users/efahmjakjnnmleokcaomicgfhobabdkc?logo=google&logoColor=white&label=google%20users)](https://chromewebstore.google.com/detail/open-links-in-new-tab/efahmjakjnnmleokcaomicgfhobabdkc) [![Mozilla Add-on Users](https://img.shields.io/amo/users/open-links-in-new-tab?logo=mozilla&label=mozilla%20users)](https://addons.mozilla.org/addon/open-links-in-new-tab) +[![Chrome Web Store Rating](https://img.shields.io/chrome-web-store/rating/efahmjakjnnmleokcaomicgfhobabdkc?logo=google&logoColor=white)](https://chromewebstore.google.com/detail/open-links-in-new-tab/efahmjakjnnmleokcaomicgfhobabdkc) +[![Mozilla Add-on Rating](https://img.shields.io/amo/rating/open-links-in-new-tab?logo=mozilla&logoColor=white)](https://addons.mozilla.org/addon/open-links-in-new-tab) [![Chrome Web Store Version](https://img.shields.io/chrome-web-store/v/efahmjakjnnmleokcaomicgfhobabdkc?label=chrome&logo=googlechrome)](https://chromewebstore.google.com/detail/open-links-in-new-tab/efahmjakjnnmleokcaomicgfhobabdkc) [![Mozilla Add-on Version](https://img.shields.io/amo/v/open-links-in-new-tab?label=firefox&logo=firefox)](https://addons.mozilla.org/addon/open-links-in-new-tab) [![GitHub Release Version](https://img.shields.io/github/v/release/cssnr/open-links-in-new-tab?logo=github)](https://github.com/cssnr/open-links-in-new-tab/releases/latest) @@ -29,13 +31,9 @@ Modern Chrome Web Extension and Firefox Browser Addon to Open Links in New Tabs [![Vivaldi](https://raw.githubusercontent.com/alrra/browser-logos/main/src/vivaldi/vivaldi_48x48.png)](https://chromewebstore.google.com/detail/open-links-in-new-tab/efahmjakjnnmleokcaomicgfhobabdkc) [![Opera](https://raw.githubusercontent.com/alrra/browser-logos/main/src/opera/opera_48x48.png)](https://chromewebstore.google.com/detail/open-links-in-new-tab/efahmjakjnnmleokcaomicgfhobabdkc) -All Chromium Based Browsers can install the extension from the +All **Chromium** Based Browsers can install the extension from the [Chrome Web Store](https://chromewebstore.google.com/detail/open-links-in-new-tab/efahmjakjnnmleokcaomicgfhobabdkc). -Advanced Users can: -* Download the latest [Chrome Release](https://github.com/cssnr/open-links-in-new-tab/releases/latest/download/open_links_in_new_tab-chrome.crx) from GitHub -* Download the latest [Firefox Release](https://github.com/cssnr/open-links-in-new-tab/releases/latest/download/open_links_in_new_tab-firefox.xpi) from GitHub - # Features Please submit a [Feature Request](https://github.com/cssnr/open-links-in-new-tab/discussions/new?category=feature-requests) for new features. @@ -71,19 +69,28 @@ To open the options, click on the icon (from above) then click `Open Options`. **Quick Start** -To run chrome or firefox with web-ext. +First, clone (or download) this repository and change into the directory. + +Second, install the dependencies: +```shell +npm install +``` + +Finally, to run Chrome or Firefox with web-ext, run one of the following: ```shell -npm isntall npm run chrome npm run firefox ``` -To Load Unpacked/Temporary Add-on make a `manifest.json` and run from the [src](src) folder. +Additionally, to Load Unpacked/Temporary Add-on make a `manifest.json` and run from the [src](src) folder, run one of the following: ```shell npm run manifest:chrome npm run manifest:firefox ``` +Chrome: [https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world#load-unpacked](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world#load-unpacked) +Firefox: [https://extensionworkshop.com/documentation/develop/temporary-installation-in-firefox/](https://extensionworkshop.com/documentation/develop/temporary-installation-in-firefox/) + For more information on web-ext, [read this documentation](https://extensionworkshop.com/documentation/develop/web-ext-command-reference/). To pass additional arguments to an `npm run` command, use `--`. Example: `npm run chrome -- --chromium-binary=...` @@ -96,20 +103,14 @@ See [gulpfile.js](gulpfile.js) for more information on `postinstall`. npm install ``` -To load unpacked or temporary addon from the [src](src) folder, you must generate the `src/manifest.json` for the desired browser. -```shell -npm run manifest:chrome -npm run manifest:firefox -``` - -If you would like to create a `.zip` archive of the [src](src) directory for the desired browser. +To create a `.zip` archive of the [src](src) directory for the desired browser run one of the following: ```shell npm run build npm run build:chrome npm run build:firefox ``` -For more information on building, see the scripts in the [package.json](package.json) file. +For more information on building, see the scripts section in the [package.json](package.json) file. ## Chrome Setup @@ -121,17 +122,14 @@ For more information on building, see the scripts in the [package.json](package. ## Firefox Setup -Note: Firefox Temporary addon's will **not** remain after restarting Firefox, therefore; -it is very useful to keep addon storage after uninstall/restart with `keepStorageOnUninstall`. - 1. Build or Download a [Release](https://github.com/cssnr/open-links-in-new-tab/releases). 1. Unzip the archive, place the folder where it must remain and note its location for later. 1. Go to `about:debugging#/runtime/this-firefox` and click `Load Temporary Add-on...` 1. Navigate to the folder you extracted earlier, select `manifest.json` then click `Select File`. 1. Open `about:config` search for `extensions.webextensions.keepStorageOnUninstall` and set to `true`. -If you need to test a build or browser restart, you must pack the addon. -This only works in ESR, Development, or Nightly. +If you need to test a restart, you must pack the addon. This only works in ESR, Development, or Nightly. +You may also use an Unbranded Build: [https://wiki.mozilla.org/Add-ons/Extension_Signing#Unbranded_Builds](https://wiki.mozilla.org/Add-ons/Extension_Signing#Unbranded_Builds) 1. Run `npm run build:firefox` then use `web-ext-artifacts/{name}-firefox-{version}.zip`. 1. Open `about:config` search for `xpinstall.signatures.required` and set to `false`. diff --git a/manifest.json b/manifest.json index 76f44f0..ad7b378 100644 --- a/manifest.json +++ b/manifest.json @@ -1,9 +1,10 @@ { "manifest_version": 3, - "version": "0.1.8", + "version": "0.2.0", "name": "Open Links in New Tab", + "short_name": "Open in Tab", "description": "Modern Web Extension to Open Links in New Tabs for Specified Domains or Temporarily on Any Tab.", - "homepage_url": "https://github.com/cssnr/open-links-in-new-tab", + "homepage_url": "https://open-links-in-new-tab.cssnr.com/", "author": "Shane", "commands": { "_execute_action": { @@ -26,7 +27,7 @@ } }, "permissions": ["activeTab", "contextMenus", "scripting", "storage"], - "host_permissions": ["https://*/*", "http://*/*"], + "host_permissions": [""], "content_scripts": [ { "matches": [""], diff --git a/package-lock.json b/package-lock.json index 583ab88..54ff697 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,20 +11,21 @@ "jquery": "^3.7.1" }, "devDependencies": { + "@types/chrome": "^0.0.268", "eslint": "^8.57.0", "gulp": "^4.0.2", "json-merger": "^1.1.10", "prettier": "^3.2.5", - "web-ext": "^7.11.0" + "web-ext": "^7.12.0" } }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", + "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.24.2", + "@babel/highlight": "^7.24.6", "picocolors": "^1.0.0" }, "engines": { @@ -32,21 +33,21 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", - "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.6", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -330,9 +331,9 @@ } }, "node_modules/@mdn/browser-compat-data": { - "version": "5.5.7", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.5.7.tgz", - "integrity": "sha512-DoHTZ/TjtNfUu9eiqJd+x3IcCQrhS+yOYU436TKUnlE36jZwNbK535D1CmTsSYdi/UcdCWNm5KRQZ9g1tlZCPw==", + "version": "5.5.29", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.5.29.tgz", + "integrity": "sha512-NHdG3QOiAsxh8ygBSKMa/WaNJwpNt87uVqW+S2RlnSqgeRdk+L3foNWTX6qd0I3NHSlCFb47rgopeNCJtRDY5A==", "dev": true }, "node_modules/@nodelib/fs.scandir": { @@ -455,6 +456,37 @@ "node": ">=14.16" } }, + "node_modules/@types/chrome": { + "version": "0.0.268", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.268.tgz", + "integrity": "sha512-7N1QH9buudSJ7sI8Pe4mBHJr5oZ48s0hcanI9w3wgijAlv1OZNUZve9JR4x42dn5lJ5Sm87V1JNfnoh10EnQlA==", + "dev": true, + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, + "node_modules/@types/filesystem": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", + "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", + "dev": true, + "dependencies": { + "@types/filewriter": "*" + } + }, + "node_modules/@types/filewriter": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", + "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==", + "dev": true + }, + "node_modules/@types/har-format": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.15.tgz", + "integrity": "sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==", + "dev": true + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -468,9 +500,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -534,37 +566,36 @@ } }, "node_modules/addons-linter": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-6.21.0.tgz", - "integrity": "sha512-4GBn14BR16FZE7dog6uz+1HO6V3B+mAVxmbwxRhed2y5eyrwIW832TmEpku+5A5bbovBZ4gilXEtBsl6A1AMmg==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-6.28.0.tgz", + "integrity": "sha512-fCTjXL/yG4hwq74JG8tQdrvEu0OvGrEN9yU+Df0020RDtHl3g/tTCyMeC4G1uyk8IuyMzp4myCBNnOGC7MWSQQ==", "dev": true, "dependencies": { "@fluent/syntax": "0.19.0", - "@mdn/browser-compat-data": "5.5.7", + "@mdn/browser-compat-data": "5.5.29", "addons-moz-compare": "1.3.0", - "addons-scanner-utils": "9.9.0", - "ajv": "8.12.0", + "addons-scanner-utils": "9.10.1", + "ajv": "8.13.0", "chalk": "4.1.2", "cheerio": "1.0.0-rc.12", "columnify": "1.6.0", "common-tags": "1.8.2", "deepmerge": "4.3.1", - "eslint": "8.56.0", + "eslint": "8.57.0", "eslint-plugin-no-unsanitized": "4.0.2", - "eslint-visitor-keys": "3.4.3", - "espree": "9.6.1", + "eslint-visitor-keys": "4.0.0", + "espree": "10.0.1", "esprima": "4.0.1", "fast-json-patch": "3.1.1", - "glob": "10.3.10", + "glob": "10.4.1", "image-size": "1.1.1", "is-mergeable-object": "1.1.1", "jed": "1.1.1", "json-merge-patch": "1.0.2", "os-locale": "5.0.0", - "pino": "8.17.2", - "postcss": "8.4.33", + "pino": "8.20.0", "relaxed-json": "1.0.3", - "semver": "7.5.4", + "semver": "7.6.2", "sha.js": "2.4.11", "source-map-support": "0.5.21", "tosource": "1.0.0", @@ -579,19 +610,10 @@ "node": ">=16.0.0" } }, - "node_modules/addons-linter/node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/addons-linter/node_modules/addons-scanner-utils": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-9.9.0.tgz", - "integrity": "sha512-YDP10U3sEZMuIgnjXMiAYgUU64jTbxmhpUXMlhi1nKO4Etz+ctGWoTUst7IQRoLWaY9y2r1KZDG3jALxLA1n7Q==", + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-9.10.1.tgz", + "integrity": "sha512-Tz9OUQx9Ja0TyQ+H2GakB9KlJ50myI6ESBGRlA8N80nHBzMjjPRFGm0APADSaCd5NP74SrFtEvL4TRpDwZXETA==", "dev": true, "dependencies": { "@types/yauzl": "2.10.3", @@ -603,7 +625,7 @@ }, "peerDependencies": { "body-parser": "1.20.2", - "express": "4.18.2", + "express": "4.18.3", "node-fetch": "2.6.11", "safe-compare": "1.1.4" }, @@ -623,15 +645,15 @@ } }, "node_modules/addons-linter/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", @@ -667,83 +689,35 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/addons-linter/node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "node_modules/addons-linter/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/addons-linter/node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/addons-linter/node_modules/espree": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "acorn": "^8.11.3", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://opencollective.com/eslint" } }, - "node_modules/addons-linter/node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "node_modules/addons-linter/node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -767,28 +741,34 @@ } }, "node_modules/addons-linter/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/addons-linter/node_modules/glob/node_modules/minimatch": { + "node_modules/addons-linter/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/addons-linter/node_modules/minimatch": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", @@ -803,12 +783,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/addons-linter/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/addons-linter/node_modules/node-fetch": { "version": "2.6.11", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", @@ -1354,9 +1328,9 @@ } }, "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.0.tgz", + "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==", "dev": true }, "node_modules/bach": { @@ -3949,6 +3923,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -4571,6 +4546,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -4971,9 +4947,9 @@ "dev": true }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -5449,15 +5425,12 @@ } }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": "14 || >=16.14" } }, "node_modules/make-error": { @@ -5722,9 +5695,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -5841,6 +5814,7 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "optional": true, "dependencies": { @@ -5871,6 +5845,7 @@ "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "optional": true, "dependencies": { @@ -5898,24 +5873,6 @@ "dev": true, "optional": true }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -6654,30 +6611,21 @@ } }, "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/path-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", @@ -6705,9 +6653,9 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, "node_modules/pify": { @@ -6741,15 +6689,15 @@ } }, "node_modules/pino": { - "version": "8.17.2", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz", - "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.20.0.tgz", + "integrity": "sha512-uhIfMj5TVp+WynVASaVEJFTncTUe4dHBq6CWplu/vBgvGHhvBvQfxz+vcOrnnBQdORH3izaGEurLfNlq3YxdFQ==", "dev": true, "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.1.0", + "pino-abstract-transport": "^1.1.0", "pino-std-serializers": "^6.0.0", "process-warning": "^3.0.0", "quick-format-unescaped": "^4.0.3", @@ -6763,9 +6711,9 @@ } }, "node_modules/pino-abstract-transport": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", - "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", "dev": true, "dependencies": { "readable-stream": "^4.0.0", @@ -6832,34 +6780,6 @@ "node": ">=0.10.0" } }, - "node_modules/postcss": { - "version": "8.4.33", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", - "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7583,6 +7503,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { "glob": "^7.1.3" @@ -7655,19 +7576,16 @@ "dev": true }, "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -7956,15 +7874,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -8052,9 +7961,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", "dev": true }, "node_modules/split": { @@ -9143,14 +9052,14 @@ } }, "node_modules/web-ext": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.11.0.tgz", - "integrity": "sha512-EG6YXHITNDJB/h6Rc5FF08eMoN45sZPBBIIlEraBzxJ0RdJZ8Z3xvUUawbDwt+mowfv9X0XRWlLSwdWbRKgojg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.12.0.tgz", + "integrity": "sha512-h+uWOYBlHlPKy5CqxuZKocgOdL8J7I4ctMw/rAGbQl7jq7tr+NmY/Lhh2FPMSlJ1Y0T2VeUqwBVighK0MM1+zA==", "dev": true, "dependencies": { "@babel/runtime": "7.21.0", "@devicefarmer/adbkit": "3.2.3", - "addons-linter": "6.21.0", + "addons-linter": "6.28.0", "bunyan": "1.8.15", "camelcase": "7.0.1", "chrome-launcher": "0.15.1", @@ -9579,12 +9488,6 @@ "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", "dev": true }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yargs": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", diff --git a/package.json b/package.json index 7682a36..b3f009c 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,11 @@ "jquery": "^3.7.1" }, "devDependencies": { + "@types/chrome": "^0.0.268", "eslint": "^8.57.0", "gulp": "^4.0.2", "json-merger": "^1.1.10", "prettier": "^3.2.5", - "web-ext": "^7.11.0" + "web-ext": "^7.12.0" } } diff --git a/src/html/oninstall.html b/src/html/oninstall.html index 94625b2..76b8ed7 100644 --- a/src/html/oninstall.html +++ b/src/html/oninstall.html @@ -22,16 +22,17 @@

Welcome

To always open links in new tabs for sites you toggle on, permissions to run on all hosts are needed.

- - Grant Host Permissions +

This web extension injects the tab.js - script into every site. This script checks to see if the hostname of the site matches an enabled hostname. - If so, it sets all links to open in a new tab. + script into every site. This script then checks to see if the hostname matches an enabled site and sets those links to open in a new tab.

- + Open Options
@@ -40,8 +41,8 @@

Welcome

View Source - Get Support + href="https://open-links-in-new-tab.cssnr.com/docs/"> + Read Documentation
@@ -52,7 +53,7 @@

Welcome

- + diff --git a/src/html/options.html b/src/html/options.html index 37244d5..dee251a 100644 --- a/src/html/options.html +++ b/src/html/options.html @@ -24,40 +24,57 @@

Open Links in New Tab

-

v

- +

v

+
- - - - - - - - - - - - - - + + + + + + +
Keyboard Shortcuts
DescriptionShortcut
Show Popup Action Unknown
Toggle Current Site Unknown
Temporarily Enable Site Unknown
Unknown
+
+

+ For more information on options, + read the documentation. +

+
+ +

+ More Information on Permissions

+
+
+
- -
- -
+
+ + @@ -71,6 +88,15 @@

Open Links in New Tab

+
+ + +
- -
- + @@ -129,13 +148,14 @@

Open Links in New Tab

@@ -145,7 +165,7 @@

Open Links in New Tab

-
+
+
diff --git a/src/html/popup.html b/src/html/popup.html index d0b7334..ef0944c 100644 --- a/src/html/popup.html +++ b/src/html/popup.html @@ -14,9 +14,11 @@ - + Enable Temporarily
- -
- -
+
+ + @@ -61,6 +72,15 @@
+
+ + +

- + diff --git a/src/js/export.js b/src/js/export.js index 90682e3..5bc8f81 100644 --- a/src/js/export.js +++ b/src/js/export.js @@ -19,6 +19,10 @@ export async function toggleSite(tab) { } else { console.log(`Disabling Site: ${url.hostname}`) sites.splice(sites.indexOf(url.hostname), 1) + await chrome.action.setBadgeBackgroundColor({ + tabId: tab.id, + color: 'red', + }) } console.debug('sites:', sites) await chrome.storage.sync.set({ sites }) @@ -35,29 +39,6 @@ export async function enableSite(tab, color) { }) } -/** - * Grant Permissions Click Callback - * Shared with Options and Home - * @function grantPerms - * @param {MouseEvent} event - */ -export async function grantPerms(event) { - console.debug('grantPerms:', event) - await requestPerms() - await checkPerms() -} - -/** - * Request Host Permissions - * @function requestPerms - * @return {chrome.permissions.request} - */ -export async function requestPerms() { - return await chrome.permissions.request({ - origins: ['https://*/*', 'http://*/*'], - }) -} - /** * Check Host Permissions * @function checkPerms @@ -65,7 +46,7 @@ export async function requestPerms() { */ export async function checkPerms() { const hasPerms = await chrome.permissions.contains({ - origins: ['https://*/*', 'http://*/*'], + origins: [''], }) console.debug('checkPerms:', hasPerms) // Firefox still uses DOM Based Background Scripts @@ -84,6 +65,72 @@ export async function checkPerms() { return hasPerms } +/** + * Grant Permissions Click Callback + * Promise from requestPerms is ignored so we can close the popup immediately + * @function grantPerms + * @param {MouseEvent} event + * @param {Boolean} [close] + */ +export async function grantPerms(event, close = false) { + console.debug('grantPerms:', event) + requestPerms() + if (close) { + window.close() + } +} + +/** + * Request Host Permissions + * @function requestPerms + * @return {chrome.permissions.request} + */ +export async function requestPerms() { + return await chrome.permissions.request({ + origins: [''], + }) +} + +// /** +// * Revoke Permissions Click Callback +// * NOTE: For many reasons Chrome will determine host_perms are required and +// * will ask for them at install time and not allow them to be revoked +// * @function revokePerms +// * @param {MouseEvent} event +// */ +// export async function revokePerms(event) { +// console.debug('revokePerms:', event) +// const permissions = await chrome.permissions.getAll() +// console.debug('permissions:', permissions) +// try { +// await chrome.permissions.remove({ +// origins: permissions.origins, +// }) +// await checkPerms() +// } catch (e) { +// console.log(e) +// showToast(e.toString(), 'danger') +// } +// } + +/** + * Permissions On Added Callback + * @param {chrome.permissions} permissions + */ +export async function onAdded(permissions) { + console.debug('onAdded', permissions) + await checkPerms() +} + +/** + * Permissions On Removed Callback + * @param {chrome.permissions} permissions + */ +export async function onRemoved(permissions) { + console.debug('onRemoved', permissions) + await checkPerms() +} + /** * Save Options Callback * @function saveOptions @@ -124,6 +171,20 @@ export function updateOptions(options) { } } +/** + * Update DOM with Manifest Details + * @function updateManifest + */ +export function updateManifest() { + const manifest = chrome.runtime.getManifest() + document + .querySelectorAll('.version') + .forEach((el) => (el.textContent = manifest.version)) + document + .querySelectorAll('[href="homepage_url"]') + .forEach((el) => (el.href = manifest.homepage_url)) +} + /** * Show Bootstrap Toast * @function showToast @@ -131,12 +192,17 @@ export function updateOptions(options) { * @param {String} type */ export function showToast(message, type = 'success') { - console.log(`showToast: ${type}:`, message) - const element = document.querySelector('.d-none > .toast').cloneNode(true) - element.addEventListener('mousemove', () => toast.hide()) - element.classList.add(`text-bg-${type}`) + console.debug(`showToast: ${type}: ${message}`) + const clone = document.querySelector('.d-none > .toast') + const container = document.getElementById('toast-container') + if (!clone || !container) { + return console.warn('Missing clone or container:', clone, container) + } + const element = clone.cloneNode(true) element.querySelector('.toast-body').innerHTML = message - document.getElementById('toast-container').appendChild(element) + element.classList.add(`text-bg-${type}`) + container.appendChild(element) const toast = new bootstrap.Toast(element) + element.addEventListener('mousemove', () => toast.hide()) toast.show() } diff --git a/src/js/oninstall.js b/src/js/oninstall.js index f1c99a1..890aa81 100644 --- a/src/js/oninstall.js +++ b/src/js/oninstall.js @@ -1,12 +1,20 @@ // JS for oninstall.html -import { checkPerms, requestPerms } from './export.js' +import { checkPerms, onRemoved, grantPerms } from './export.js' chrome.permissions.onAdded.addListener(onAdded) +chrome.permissions.onRemoved.addListener(onRemoved) document.addEventListener('DOMContentLoaded', domContentLoaded) -document.getElementById('grant-perms').addEventListener('click', grantPerms) -document.getElementById('open-options').addEventListener('click', openOptions) +document + .querySelectorAll('.open-options') + .forEach((el) => el.addEventListener('click', openOptions)) +document + .querySelectorAll('.grant-permissions') + .forEach((el) => el.addEventListener('click', grantPerms)) +document + .querySelectorAll('[data-bs-toggle="tooltip"]') + .forEach((el) => new bootstrap.Tooltip(el)) /** * DOMContentLoaded @@ -16,27 +24,12 @@ async function domContentLoaded() { await checkPerms() } -/** - * Grant Permissions Click Callback - * @function grantPerms - * @param {MouseEvent} event - */ -async function grantPerms(event) { - console.debug('grantPerms:', event) - await requestPerms() - const hasPerms = await checkPerms() - if (hasPerms) { - chrome.runtime.openOptionsPage() - window.close() - } -} - /** * Permissions On Added Callback * @param permissions */ async function onAdded(permissions) { - console.info('onAdded', permissions) + console.debug('onAdded', permissions) const hasPerms = await checkPerms() if (hasPerms) { chrome.runtime.openOptionsPage() diff --git a/src/js/options.js b/src/js/options.js index 7edc396..373dfe3 100644 --- a/src/js/options.js +++ b/src/js/options.js @@ -1,15 +1,27 @@ // JS for options.html -import { checkPerms, saveOptions, showToast, updateOptions } from './export.js' +import { + checkPerms, + grantPerms, + onAdded, + onRemoved, + saveOptions, + showToast, + updateManifest, + updateOptions, +} from './export.js' chrome.storage.onChanged.addListener(onChanged) chrome.permissions.onAdded.addListener(onAdded) +chrome.permissions.onRemoved.addListener(onRemoved) document.addEventListener('DOMContentLoaded', initOptions) -document.getElementById('grant-perms').addEventListener('click', grantPerms) document.getElementById('add-host').addEventListener('submit', addHost) document.getElementById('export-hosts').addEventListener('click', exportHosts) document.getElementById('import-hosts').addEventListener('click', importHosts) +document + .querySelectorAll('.grant-permissions') + .forEach((el) => el.addEventListener('click', grantPerms)) document .querySelectorAll('#options-form input') .forEach((el) => el.addEventListener('change', saveOptions)) @@ -32,14 +44,9 @@ hostsInput.addEventListener('change', hostsInputChange) */ async function initOptions() { console.debug('initOptions') - document.getElementById('version').textContent = - chrome.runtime.getManifest().version - - await setShortcuts({ - mainKey: '_execute_action', - toggleSite: 'toggle-site', - enableTemp: 'enable-temp', - }) + updateManifest() + await setShortcuts() + await checkPerms() const { options, sites } = await chrome.storage.sync.get([ 'options', @@ -48,7 +55,6 @@ async function initOptions() { console.debug('options, sites:', options, sites) updateOptions(options) updateTable(sites) - await checkPerms() } /** @@ -69,28 +75,6 @@ function onChanged(changes, namespace) { } } -/** - * Grant Permissions Click Callback - * @function grantPerms - * @param {MouseEvent} event - */ -async function grantPerms(event) { - console.debug('grantPermsBtn:', event) - await chrome.permissions.request({ - origins: ['https://*/*', 'http://*/*'], - }) - await checkPerms() -} - -/** - * Permissions On Added Callback - * @param permissions - */ -async function onAdded(permissions) { - console.debug('onAdded', permissions) - await checkPerms() -} - /** * Open OnInstall Page Click Callback * @function openOnInstall @@ -98,7 +82,7 @@ async function onAdded(permissions) { */ async function openOnInstall(event) { console.debug('openOnInstall', event) - const url = chrome.runtime.getURL('../html/oninstall.html') + const url = chrome.runtime.getURL('/html/oninstall.html') await chrome.tabs.create({ active: true, url }) window.close() } @@ -281,19 +265,23 @@ function textFileDownload(filename, text) { /** * Set Keyboard Shortcuts * @function setShortcuts - * @param {Object} mapping { elementID: name } + * @param {String} selector */ -async function setShortcuts(mapping) { +async function setShortcuts(selector = '#keyboard-shortcuts') { + const table = document.querySelector(selector) + const tbody = table.querySelector('tbody') + const source = table.querySelector('tfoot > tr').cloneNode(true) const commands = await chrome.commands.getAll() - for (const [elementID, name] of Object.entries(mapping)) { - // console.debug(`${elementID}: ${name}`) - const command = commands.find((x) => x.name === name) - if (command?.shortcut) { - // console.debug(`${elementID}: ${command.shortcut}`) - const el = document.getElementById(elementID) - if (el) { - el.textContent = command.shortcut - } + for (const command of commands) { + // console.debug('command:', command) + const row = source.cloneNode(true) + // TODO: Chrome does not parse the description for _execute_action in manifest.json + let description = command.description + if (!description && command.name === '_execute_action') { + description = 'Show Popup' } + row.querySelector('.description').textContent = description + row.querySelector('kbd').textContent = command.shortcut || 'Not Set' + tbody.appendChild(row) } } diff --git a/src/js/popup.js b/src/js/popup.js index 325f8c5..186b304 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -3,16 +3,20 @@ import { checkPerms, enableSite, + grantPerms, saveOptions, showToast, toggleSite, + updateManifest, updateOptions, } from './export.js' document.addEventListener('DOMContentLoaded', initPopup) -document.getElementById('grant-perms').onclick = grantPerms document.getElementById('toggle-site').onclick = toggleSiteClick document.getElementById('enable-temp').onclick = enableTempClick +document + .querySelectorAll('.grant-permissions') + .forEach((el) => el.addEventListener('click', (e) => grantPerms(e, true))) document .querySelectorAll('a[href]') .forEach((el) => el.addEventListener('click', popupLinks)) @@ -30,10 +34,7 @@ document */ async function initPopup() { console.debug('initPopup') - const manifest = chrome.runtime.getManifest() - document.getElementById('version').textContent = manifest.version - document.getElementById('homepage_url').href = manifest.homepage_url - + updateManifest() await checkPerms() const { options, sites } = await chrome.storage.sync.get([ @@ -90,37 +91,22 @@ async function popupLinks(event) { console.debug('popupLinks:', event) event.preventDefault() const anchor = event.target.closest('a') - console.debug(`anchor.href: ${anchor.href}`, anchor) + const href = anchor.getAttribute('href').replace(/^\.+/g, '') + console.debug('href:', href) let url - if (anchor.href.endsWith('html/options.html')) { + if (href.endsWith('html/options.html')) { chrome.runtime.openOptionsPage() return window.close() - } else if ( - anchor.href.startsWith('http') || - anchor.href.startsWith('chrome-extension') - ) { - url = anchor.href + } else if (href.startsWith('http')) { + url = href } else { - url = chrome.runtime.getURL(anchor.href) + url = chrome.runtime.getURL(href) } - console.debug('url:', url) + console.log('url:', url) await chrome.tabs.create({ active: true, url }) return window.close() } -/** - * Grant Permissions Button Click Callback - * @function grantPerms - * @param {MouseEvent} event - */ -function grantPerms(event) { - console.debug('grantPerms:', event) - chrome.permissions.request({ - origins: ['https://*/*', 'http://*/*'], - }) - window.close() -} - /** * Enable/Disable Site Button Click Callback * @function toggleSiteClick diff --git a/src/js/service-worker.js b/src/js/service-worker.js index e7be12b..28d374e 100644 --- a/src/js/service-worker.js +++ b/src/js/service-worker.js @@ -33,11 +33,16 @@ async function onStartup() { async function onInstalled(details) { console.log('onInstalled:', details) const githubURL = 'https://github.com/cssnr/open-links-in-new-tab' + const uninstallURL = new URL( + 'https://open-links-in-new-tab.cssnr.com/uninstall/' + ) const options = await Promise.resolve( setDefaultOptions({ - autoReload: true, onScroll: false, + onAttributes: false, + autoReload: true, updateAll: true, + noOpener: true, contextMenu: true, showUpdate: false, }) @@ -45,6 +50,7 @@ async function onInstalled(details) { if (options.contextMenu) { createContextMenus() } + const manifest = chrome.runtime.getManifest() if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) { const hasPerms = await checkPerms() if (hasPerms) { @@ -55,7 +61,6 @@ async function onInstalled(details) { } } else if (details.reason === chrome.runtime.OnInstalledReason.UPDATE) { if (options.showUpdate) { - const manifest = chrome.runtime.getManifest() if (manifest.version !== details.previousVersion) { const url = `${githubURL}/releases/tag/${manifest.version}` console.log(`Update url: ${url}`) @@ -63,7 +68,9 @@ async function onInstalled(details) { } } } - chrome.runtime.setUninstallURL(`${githubURL}/issues`) + uninstallURL.searchParams.append('version', manifest.version) + console.log('uninstallURL:', uninstallURL.href) + await chrome.runtime.setUninstallURL(uninstallURL.href) } /** diff --git a/src/js/tab.js b/src/js/tab.js index 63f0f2f..cf05fc1 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -1,23 +1,24 @@ // JS Content Script tab.js +let tabEnabled = false +let noOpener = false + ;(async () => { const { options, sites } = await chrome.storage.sync.get([ 'options', 'sites', ]) - // console.debug(`sites: ${window.location.host}`, sites) + noOpener = options.noOpener if (sites?.includes(window.location.host)) { console.log(`Enabled Host: ${window.location.host}`) await activateTab('green') } + // TODO: Always enable onChanged and refactor to work with options.updateAll if (options.updateAll && !chrome.storage.onChanged.hasListener(onChanged)) { - // console.debug('Adding onChanged Listener') chrome.storage.onChanged.addListener(onChanged) } })() -let tabEnabled = false - /** * Activate Tab * @function activateTab @@ -36,18 +37,17 @@ async function activateTab(color) { console.info('Activating Tab...') tabEnabled = true updateLinks() - const observer = new MutationObserver(function () { - updateLinks() - }) - observer.observe(document.body, { - attributes: true, - childList: true, - subTree: true, - }) + const observer = new MutationObserver(updateLinks) const { options } = await chrome.storage.sync.get(['options']) + const mutationObserverInit = { + attributes: options.onAttributes, + childList: true, + subtree: true, + } + observer.observe(document.body, mutationObserverInit) if (options.onScroll) { console.debug('Enabling onScroll...') - const processChange = debounce(() => updateLinks()) + const processChange = debounce(updateLinks) document.addEventListener('scroll', processChange) } } @@ -61,8 +61,12 @@ function updateLinks() { const elements = document.getElementsByTagName('a') for (const element of elements) { if (element.href !== '#') { - element.target = '_blank' - element.setAttribute('rel', 'nofollow') + if (element.target !== '_blank') { + element.target = '_blank' + if (noOpener) { + element.setAttribute('rel', 'noopener') + } + } } } } @@ -75,7 +79,7 @@ function updateLinks() { */ async function onChanged(changes, namespace) { // console.debug('onChanged:', changes, namespace) - for (let [key, { newValue }] of Object.entries(changes)) { + for (let [key, { oldValue, newValue }] of Object.entries(changes)) { if (namespace === 'sync' && key === 'sites') { // console.debug('newValue:', newValue) if (newValue?.includes(window.location.host)) { @@ -97,6 +101,10 @@ async function onChanged(changes, namespace) { tabEnabled = false } } + } else if (namespace === 'sync' && key === 'options') { + if (oldValue.noOpener !== newValue.noOpener) { + noOpener = newValue.noOpener + } } } }