diff --git a/.editorconfig b/.editorconfig index 5f50c69..51bd461 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,12 @@ +# top-most EditorConfig file root = true +# Unix-style newlines with a newline ending every file [*] -charset = utf-8 +end_of_line = lf +insert_final_newline = true indent_style = space indent_size = 2 -end_of_line = crlf -insert_final_newline = true -trim_trailing_whitespace = true \ No newline at end of file + +[*.{js,json,ts,tsx}] +charset = utf-8 \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5da5008 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +JS_ENV="development" +JS_ROOT="./" +JS_i18n="../../../src/_locales" \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..8bcae8b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: magicoflolis diff --git a/.github/ISSUE_TEMPLATE/01_bug_report.yml b/.github/ISSUE_TEMPLATE/01_bug_report.yml index 43b0b0f..ebbcfd9 100644 --- a/.github/ISSUE_TEMPLATE/01_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/01_bug_report.yml @@ -34,6 +34,13 @@ body: description: What browser are you seeing this bug on? validations: required: true + - type: input + id: userjs-manager + attributes: + label: User Script Manager + description: What user script manager are you using? - not required when using Web Extension + validations: + required: false - type: input id: url attributes: @@ -60,4 +67,4 @@ body: 1. Console error '...' 1. etc. validations: - required: false \ No newline at end of file + required: false diff --git a/.github/ISSUE_TEMPLATE/02_feature_request.yml b/.github/ISSUE_TEMPLATE/02_feature_request.yml index 389a6b1..990c982 100644 --- a/.github/ISSUE_TEMPLATE/02_feature_request.yml +++ b/.github/ISSUE_TEMPLATE/02_feature_request.yml @@ -11,4 +11,4 @@ body: label: Feature description description: A clear and concise description, please include if your feature request is related to a problem. validations: - required: true \ No newline at end of file + required: true diff --git a/.gitignore b/.gitignore index a548d45..c61cb6a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,140 +2,27 @@ haters/ build +node_modules + Notes .history -web-ext-artifacts -tests web-ext +web-ext-artifacts web-server +*.ini -# Logs -logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache *.tsbuildinfo -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 0c3700d..5e05bdd 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,6 +4,7 @@ "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "mrmlnc.vscode-scss", - "adpyke.vscode-userscript" + "adpyke.vscode-userscript", + "xyz.local-history" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index b524aac..b54e9f0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -23,14 +23,8 @@ "keepProfileChanges": false, "url": "https://www.google.com/", "addonPath": "${workspaceFolder}/build/firefox", - "reloadOnChange": { - "watch": [ - "${workspaceFolder}/src/**" - ], - "ignore": [ - "${workspaceFolder}/node_modules/**" - ] - }, + "reloadOnChange": "${workspaceFolder}/src/**", + "firefoxArgs": ["--devtools"], "pathMappings": [ { "url": "webpack:///js", diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 4f716e0..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "files.exclude": { - "**/build/**": true, - "**/dist/**": true, - "**/node_modules/**": true, - "**/Notes/**": true, - "tests/*": true - }, - "explorer.excludeGitIgnore": true, - "search.exclude": { - "**/build/**": true, - "**/dist/**": true, - "**/node_modules/**": true, - "**/Notes/**": true, - "tests/*": true - } -} diff --git a/CHANGELOG.user.md b/CHANGELOG.user.md index be5372b..c67f69b 100644 --- a/CHANGELOG.user.md +++ b/CHANGELOG.user.md @@ -1,4 +1,272 @@ -# What's New +# Changelog + +**Known Issues:** + +* SVG elements don't load on every webpage: + * _can be blocked by host, external scripts, web extensions, etc._ +* Outlook Website Loading Problem [#70](https://github.com/magicoflolis/Userscript-Plus/issues/70) + * Recommend excluding this userscript on `*.office.com` domains within your userscript manager or switch to the webextension version as it does not injected itself into the page. + * Caused by `Duplicated Default Trusted Types policy`. + +**TODO:** + +* Finish translations. +* Finish blacklist section. +* GitHub engine - filter out UserScript and UserStyles that don't match the host +* Rework TrustedTypePolicy code + +## v7.6.8 + +* Bug fixes: + * ~~Fixed [#70](https://github.com/magicoflolis/Userscript-Plus/issues/70)~~ + +--- + +## v7.6.7 + +* Bug fixes: + * Fixed [#69](https://github.com/magicoflolis/Userscript-Plus/issues/69) + +--- + +## v7.6.6 + +* Config changes: + * `autofetch` is now OFF by default +* Internal changes: + * refactored some code, better readability + * added info icon along with help links +* General changes: + * while `autofetch` is off, clicking the counter for the first time will auto fetch results + * minor bug fixes + +**TODO:** + +* Finish translations. +* Finish blacklist section. +* GitHub engine - filter out UserScript and UserStyles that don't match the host + +--- + +## v7.6.5 + +* fixed [#67](https://github.com/magicoflolis/Userscript-Plus/issues/67) + +--- + +## v7.6.4 + +* fixed `this.hostCache` for real this time +* changed `safeSelf` into a class + +--- + +## v7.6.3 + +* fixed [#66](https://github.com/magicoflolis/Userscript-Plus/issues/66) + * `this.cache` should have been `this.hostCache`, my bad + +--- + +## v7.6.2 + +* Improvements: + * improved multiple UserScript/UserStyle detection + * improved cache system + * _improved_ hostname recognition - somewhat follows web extension version + * improved readablity of "Theme Colors" section + * improved import/export of config and theme + * improved download process of UserScripts and UserStyles +* Config changes: + * GitHub engine: + * if your a UserScript or UserStyle developer and it's hosted on GitHub see [GitHub Detection](https://github.com/magicoflolis/Userscript-Plus/tree/master/wiki#github-detection) + * will now list all UserScripts and UserStyles within a repository - _even if they don't match the host_ + * multiple urls will now be fetched: + * `https://api.github.com/search/repositories?q=topic:{domain}+topic:userstyle` - cannot be changed in the settings menu + * `https://api.github.com/search/repositories?q=topic:{domain}+topic:userscript` - reset your config or manually set this address for changes to take effect + * the repository is removed from the list if no valid UserScripts or UserStyles are found +* Bug fixes: + * fixed "Enabling a engine requires the page to be reloaded, while disabling a engine does not." + * fixed "If an item changes after the list is sorted, the sorting won't be updated" + * fixed menu not timing out when "Fetch on load" was disabled + * fixed tab when searching for all-sites / `*` + * fixed searching settings +* Internal changes: + * removed `Memorize` class + * removed unused \@grants `GM_addValueChangeListener`, `GM_removeValueChangeListener`, and there `GM.*` counter parts + * removed some unused functions, types, css + * shortened some functions, types, css +* i18n additions (still not localized): + * new keys: `code`, `metadata`, `preview_metadata`, `recommend_author`, `recommend_other`, `default_sort` + +**TODO:** + +* Finish translations. +* Finish blacklist section. +* GitHub engine - filter out UserScript and UserStyles that don't match the host + +--- + +## v7.6.1 + +* Fixed sorting + +--- + +## v7.6.0 + +* New feature, `Preview Metadata` - same as `Preview Code` but loads userscripts metadata head +* New feature, `Page` - loads additonal information from userscripts homepage +* Config changes, moved `codePreview` value to `preview: code`, recommended resetting your config to clear any old/unused settings. + * If you had `Preview Code` enabled, you will need to re-enable it +* When `Preview Metadata` and `Preview Code` are both enabled, it will display the entire userscript +* Icon changes, svgs are now from [fontawesome.com](https://fontawesome.com) +* Search engines have been moved to the `Search Engines` section +* Improved mobile UI +* Improved userscript parsing +* Improved load times - added custom event to handle async item changes +* Improved GitHub engine + * Number of stars for a repo = daily installs + * Number of watchers for a repo = good rating +* Cleanup old css +* Minor bug fixes + +**Known Issues:** + +* Enabling a engine requires the page to be reloaded, while disabling a engine does not. + * This is caused by the cache system. +* If an item changes after the list is sorted, the sorting won't be updated + +**TODO:** + +* Finish translations. +* Finish blacklist section. + +--- + +## v7.5.0 + +* Merged [#64](https://github.com/magicoflolis/Userscript-Plus/pull/64) +* Cleaned up old comments + +**Known Issues:** + +* Enabling a engine requires the page to be reloaded, while disabling a engine does not. + * This is caused by the cache system. +* "Filter out other languages" may not filter _every_ userscript that is not in your language. + +**TODO:** + +* Finish translations. +* Finish blacklist section. +* Finish config section. + +--- + +## v7.4.0 + +* Merged [#59](https://github.com/magicoflolis/Userscript-Plus/pull/59) +* Added `githubusercontent.com` to `@connect`. +* Added `autoSort` to config. +* Added sorting to the following: "Total installs", "Ratings", "Created date". + * Still in the early stages, but functional +* Reworked counter system. +* Tweaked cache system. + * Reduced how often the script will cache something. +* Cleaned up code. +* Fixed minor issues. + +**Known Issues:** + +* Enabling a engine requires the page to be reloaded, while disabling a engine does not. + * This is caused by the cache system. +* "Filter out other languages" may not filter _every_ userscript that is not in your language. + +**TODO:** + +* Finish translations. +* Finish blacklist section. +* Finish config section. +* Merge [#64](https://github.com/magicoflolis/Userscript-Plus/pull/64) + +--- + +## v7.3.0 + +* Re-organized config list. +* Added [GreasyFork Bullshit Filter](https://greasyfork.org/scripts/12179) into list. + * You can enable it under "List Filters" in the config. + * You can also do `filter:` in the search bar to quickly filter the list. + * Full list here: [Wiki Page](https://github.com/magicoflolis/Userscript-Plus/tree/master/wiki/README.md) + * Example: `filter:ascii` => filters out non-ASCII scripts + +**Known Issues:** + +* "Filter out other languages" may not filter _every_ userscript that is not in your language. + +**TODO:** + +* Finish translations. +* Finish blacklist section. +* Merge [#59](https://github.com/magicoflolis/Userscript-Plus/pull/59) +* Add sorting to the following: "Total installs", "Ratings", "Created date" + +--- + +## v7.2.0 + +* New config "Inject on load", when disabled prevents this script from injecting the menu automatically. + * You can still inject the menu through the context menu in your UserScript manager. + * This will reduce overall distraction the script can have on certain pages. +* New config "Fetch on load", when disabled prevents UserScripts from being fetched automatically. + * When the menu is re-injected through the context menu in your UserScript manager UserScripts will always be fetched automatically. + * This will reduce network traffic for the engines. +* Improved design of theme section. +* Improved design of blacklist section, adding new pages to the list is still incomplete. +* Changed "Sync with GM" to "Sync with UserScript Manager". +* Made it so translated UserScripts are a different color in the list. +* CSS adjustments. + +**Known Issues:** + +* "Filter out other languages" may not filter _every_ userscript that is not in your language. + +**TODO:** + +* Add [GreasyFork Bullshit Filter](https://greasyfork.org/scripts/12179) into list +* Merge [#59](https://github.com/magicoflolis/Userscript-Plus/pull/59) +* Add sorting to the following: "Total installs", "Ratings", "Created date" + +--- + +## v7.1.1 + +* Fixed "Error When Opening YouTube" [#63](https://github.com/magicoflolis/Userscript-Plus/issues/63) + +--- + +## v7.1.0 + +* Implemented [Task Scheduling API](https://developer.mozilla.org/en-US/docs/Web/API/Prioritized_Task_Scheduling_API) + * Limited availability, see [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Scheduler#browser_compatibility) + * I added basic polyfills so hopefully there won't be issues. +* Improved error handling +* _Be sure to look in your console if the UserScript stops working for some reason!_ + +**Known Issues:** + +* "Filter out other languages" may not filter _every_ userscript that is not in your language. + +**TODO:** + +* Add [GreasyFork Bullshit Filter](https://greasyfork.org/scripts/12179) into list +* Merge [#59](https://github.com/magicoflolis/Userscript-Plus/pull/59) +* Add sorting to the following: "Total installs", "Ratings", "Created date" +* Improve design of blacklist section + +--- + +## v7.0.0 * Ability to change the URL of each engine * **Please reset your config if updating!** @@ -16,12 +284,17 @@ * `search_engine:` or `engine:` * UI ajustments -## Known Issues +**Known Issues:** * "Filter out other languages" may not filter _every_ userscript that is not in your language. -* SVG elements don't load on every webpage, _can be blocked by the page._ -## TODO +**TODO:** * Add sorting to the following: "Total installs", "Ratings", "Created date" * Improve design of blacklist section + +--- + +## Previous Changes + +[https://github.com/magicoflolis/Userscript-Plus/releases](https://github.com/magicoflolis/Userscript-Plus/releases) diff --git a/README.md b/README.md index ef1741a..80923b7 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ [![Badge Issues](https://img.shields.io/github/issues/magicoflolis/Userscript-Plus?style=flat-square)](https://github.com/magicoflolis/Userscript-Plus/issues) [![Badge Greasy Fork](https://img.shields.io/greasyfork/dt/421603?style=flat-square)](https://greasyfork.org/scripts/421603) [![Badge Stars](https://img.shields.io/github/stars/magicoflolis/Userscript-Plus?style=flat-square)](https://github.com/magicoflolis/Userscript-Plus/stargazers) -[![UserScript](https://img.shields.io/badge/Userscript-Download-brightgreen.svg?style=flat-square&label=Greasy+Fork&logo=javascript&logoColor=white)](https://greasyfork.org/scripts/421603) -[![UserScript](https://img.shields.io/badge/Userscript-Download-brightgreen.svg?style=flat-square&label=GitHub+Repo&logo=javascript&logoColor=white)](https://github.com/magicoflolis/Userscript-Plus/blob/master/dist/magic-userjs.user.js?raw=1) --- @@ -16,10 +14,12 @@ Magic Userscript+ *A complete rewrite of [Userscript+ : Show Site All UserJS](https://github.com/jae-jae/Userscript-Plus)* -Finds available UserScripts for the current webpage, the power of [Greasy Fork](https://greasyfork.org) on the go! +Finds available UserScripts and UserStyles for the current webpage, the power of [Greasy Fork](https://greasyfork.org) on the go! [UserScript Changelog](https://github.com/magicoflolis/Userscript-Plus/blob/master/CHANGELOG.user.md) +[Web Extension Changelog](https://github.com/magicoflolis/Userscript-Plus/releases) + [List of known issues by Web Browser](https://github.com/magicoflolis/Userscript-Plus/blob/master/browser-issues.md) ## **Download** @@ -27,69 +27,77 @@ Finds available UserScripts for the current webpage, the power of [Greasy Fork]( **UserScript:** > [!IMPORTANT] -> If the current webpage is set to block resources through CSP -> -> It will cause the UserScript not work for that webpage! -> -> [https://developer.mozilla.org/docs/Web/HTTP/CSP](https://developer.mozilla.org/docs/Web/HTTP/CSP) +> The UserScript only works on `HTTPS` sites! ([https://example.com](https://example.com)) * [Greasy Fork](https://greasyfork.org/scripts/421603) -* [Open UserJS](https://openuserjs.org/scripts/Magic/Magic_Userscript+_Show_Site_All_UserJS) - outdated * [GitHub Repo](https://github.com/magicoflolis/Userscript-Plus/blob/master/dist/magic-userjs.user.js?raw=1) +* [Open UserJS](https://openuserjs.org/scripts/Magic/Magic_Userscript+_Show_Site_All_UserJS) - Outdated **Web Extension:** -*Firefox:* - +* [GitHub Repo](https://github.com/magicoflolis/Userscript-Plus/releases) * [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/userscript-plus/) -* [GitHub Repo](https://github.com/magicoflolis/Userscript-Plus/releases/tag/webext/latest) - -Available for desktop and Android versions. - -*Chromium:* +* [Chrome Web Store](https://chromewebstore.google.com/detail/kbelpalpbddhjhoakbjkfookjeiennbo) +* [Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/golkolijobdaldjgefapmcknlmkjlhdh) +* ~~[Opera Add-ons](https://github.com/magicoflolis/Userscript-Plus/releases)~~ - Under review -* ~~[Chrome Web Store](https://github.com/magicoflolis/Userscript-Plus/releases/tag/webext/latest)~~ -* [GitHub Repo](https://github.com/magicoflolis/Userscript-Plus/releases/tag/webext/latest) - -Userscript+ should be compatible with any Chromium-based browser - -**(Not Recommended) Bookmarklet:** +**Bookmarklet (not recommended):** Save this URL as a bookmark, clicking it will cause the **UserScript version** to inject itself into the current webpage. -```JS +```js filename="src/UserJS/main.js" javascript:(function(){['https://cdn.jsdelivr.net/gh/magicoflolis/Userscript-Plus@master/userscript/dist/magic-userjs.user.js'].map(s=>document.body.appendChild(document.createElement('script')).src=s)})(); ``` ## Features -* Designed for mobile and desktop devices -* Support multiple languages - date formats will match your current language -* Query UserScripts from [Greasy Fork](https://greasyfork.org), [Sleazy Fork](https://sleazyfork.org), [Open UserJS](https://openuserjs.org), [GitHub](https://github.com/search?l=JavaScript&o=desc&q="==UserScript==") ( requires a [Personal Access Token](https://github.com/settings/tokens), the token does not require any permissions ) -* Customize the theme -* Import / export config and theme -* Search for UserScripts -* Filter UserScripts which do not match your current language -* Sort UserScripts, default sorting "Daily Installs" -* Preview UserScript code before install -* Built-in UserScript [Greasyfork Search with Sleazyfork Results include](https://greasyfork.org/scripts/23840) -* Save UserScript as a local file -* Highlight UserScripts created by the [author](https://greasyfork.org/users/166061) -* Highlight recommended UserScripts from the [author](https://greasyfork.org/users/166061) +* General: + * UI designed for mobile and desktop devices + * Multiple language support - date formats are based on your current language + * Import / export config and theme + * Customize theme UI +* Query UserScripts and UserStyles from: + * [Greasy Fork](https://greasyfork.org) - enabled by default + * [Sleazy Fork](https://sleazyfork.org) - disabled by default + * [GitHub](https://github.com/search?l=JavaScript&o=desc&q="==UserScript==") ( requires [Personal Access Token](https://github.com/settings/tokens), no permissions are required ) - disabled by default +* Built-in UserScripts: + * [GreasyFork Bullshit Filter](https://greasyfork.org/scripts/12179) - disabled by default + * [Greasyfork Search with Sleazyfork Results include](https://greasyfork.org/scripts/23840) - disabled by default +* Automation: + * Fetch on load - query on page load +* Blacklist: + * Attempts to exclude certain hosts from being queried - localhost, bank, government, etc. +* Menu: + * Search for UserScripts - for shortcuts see [Wiki](https://github.com/magicoflolis/Userscript-Plus/blob/master/wiki/README.md#shortcuts) + * Filter UserScripts which do not match your current language + * Sort UserScripts, default sorting "Daily Installs" + * Preview UserScripts code before install + * Save UserScript as a local file + * UserScript highlights: + * UserScripts created by the [author](https://greasyfork.org/users/166061) - enabled by default + * UserScript recommendations - enabled by default **UserScript Features:** > Tested and compatible with [TamperMonkey](https://www.tampermonkey.net/) or [ViolentMonkey](https://violentmonkey.github.io/) -* Maximize, minimize, or exit the UserScript list -* Change the position of floating counter -* Sync config with UserScript manager or per host +* General: + * Maximize, minimize, or close menu + * Sync config with UserScript manager or per host + * Customize timeout window - can be re-injected using your managers User Script Commands menu +* Query UserScripts and UserStyles from: + * [Open UserJS](https://openuserjs.org) ( limited availability, will read `Too many requests...` if limit is reached ) - disabled by default +* Automation: + * Inject on load - injects menu on page load + * Automatic fullscreen - maximizes menu when opened +* UserScript Commands via `GM_registerMenuCommand`: + * Inject Userscript+ - injects menu into the page + * Close Userscript+ - removes menu from the page -**Bookmarklet Features:** +**Web Extension Features:** -* Maximize, minimize, or exit the UserScript list -* Change the position of floating counter -* Separate config for every host +* General: + * You can fullscreen the list and it will open it to a new tab ## Previews @@ -110,7 +118,7 @@ javascript:(function(){['https://cdn.jsdelivr.net/gh/magicoflolis/Userscript-Plu * Try again on another webpage [[Test Page](https://youtube.com)] * Default timeout is 10000ms before the count disappears -* If issue persists, see [View UserScript Logs](#view-userscript-logs) or submit a [New Issue](https://github.com/magicoflolis/Userscript-Plus/issues/new/choose) +* If issue persists, see [View Console Logs](#view-console-logs) or submit a [New Issue](https://github.com/magicoflolis/Userscript-Plus/issues/new/choose) **(UserScript) Error occurred while injecting Container:** @@ -136,52 +144,16 @@ javascript:(function(){['https://cdn.jsdelivr.net/gh/magicoflolis/Userscript-Plu * **If nothing appears, this means the script is not executing at all!** * For any additional help, submit a [New Issue](https://github.com/magicoflolis/Userscript-Plus/issues/new/choose) -## Build +## Want to Contribute? -Install [Node.js](https://nodejs.org/) and [pnpm](https://pnpm.io/). *The version of Node.js should match or be greater than the `"node"` key in `package.json`.* - -``` sh -# Install dependencies -$ pnpm i -``` +Fork this repo and open a [pull request](https://github.com/magicoflolis/Userscript-Plus/pulls) - [Rules](https://github.com/magicoflolis/Userscript-Plus/blob/master/wiki/Build.md#contribution-rules) - [Guide](https://github.com/magicoflolis/Userscript-Plus/blob/master/wiki/Build.md#contribution-guide) -### UserScript Version - -**Desktop** - Developed in [ViolentMonkey](https://violentmonkey.github.io/), see [How to edit scripts with your favorite editor?](https://violentmonkey.github.io/posts/how-to-edit-scripts-with-your-favorite-editor/) - -**Mobile** - Developed in [FireFox Developer Edition](https://www.mozilla.org/firefox/developer/) "Responsive Design Mode" on desktop + [cromite](https://github.com/uazo/cromite) app on Android - -``` sh -# Watch and compile to local HTTP server -$ pnpm run dev:UserJS - -# Compile UserScript to "./dist" -$ pnpm run pub:UserJS -``` - -### WebExtension Version - -``` sh -# Compile WebExtension (developer version) for "Chromium" to "./build/chrome" -$ pnpm run dev:Cr -# Compile WebExtension (developer version) for "Firefox" to "./build/firefox" -$ pnpm run dev:FF -# Compile WebExtension (public version) for "Chromium" to "./build/chrome" -$ pnpm run webpack:Cr -# Compile WebExtension (public version) for "Firefox" to "./build/firefox" -$ pnpm run webpack:FF - -# [ Testing ] -# Browsers can be launched via "Run and Debug" in VSCode. -# Edit runtimeExecutable in ".vscode/launch.json" to your Chrome executable. -# Recommended to create an additional profile "about:profiles" for Firefox. -``` +How to add or edit translations? How to build? - [Build](https://github.com/magicoflolis/Userscript-Plus/blob/master/wiki/Build.md) ### Roadmap -* Finish WebExtension -* Add sorting to the following: "Total installs", "Ratings", "Created date" -* Improve design of blacklist section. +* See TODO section in [UserScript Changelog](https://github.com/magicoflolis/Userscript-Plus/blob/master/CHANGELOG.user.md) +* See TODO section in [Web Extension Changelog](https://github.com/magicoflolis/Userscript-Plus/releases) ### Source Code diff --git a/UserJS.json b/UserJS.json new file mode 100644 index 0000000..a814551 --- /dev/null +++ b/UserJS.json @@ -0,0 +1,69 @@ +{ + "version": "7.6.8", + "name": "Magic Userscript+ : Show Site All UserJS", + "description": "Finds available userscripts for the current webpage.", + "author": "Magic ", + "bugs": "https://github.com/magicoflolis/Userscript-Plus/issues", + "homepage": "https://github.com/magicoflolis/Userscript-Plus", + "icon": "./src/img/icon_64.png", + "downloadURL": "https://github.com/magicoflolis/Userscript-Plus/raw/master/dist/magic-userjs.user.js", + "updateURL": "https://github.com/magicoflolis/Userscript-Plus/raw/master/dist/magic-userjs.meta.js", + "license": "MIT", + "build": { + "source": { + "metadata": "./src/UserJS/header.js", + "code": "./src/UserJS/main.js", + "mainCSS": "./src/sass/magicuserjs.scss" + }, + "watch": { + "files": ["main.js", "header.js", "_main.scss"], + "dirs": ["src/UserJS", "src/sass"] + }, + "paths": { + "fileName": "magic-userjs", + "dir": "./dist", + "i18n": { + "default": "en", + "dir": "../../../src/_locales" + }, + "dev": { + "fileName": "magic-userjs.dev", + "dir": "./web-server" + } + } + }, + "metadata": { + "compatible": ["chrome", "firefox", "edge", "opera", "safari"], + "connect": [ + "greasyfork.org", + "sleazyfork.org", + "github.com", + "githubusercontent.com", + "openuserjs.org" + ], + "grant": [ + "GM_addElement", + "GM_info", + "GM_getValue", + "GM_openInTab", + "GM_setValue", + "GM_registerMenuCommand", + "GM_xmlhttpRequest", + "GM.addElement", + "GM.info", + "GM.getValue", + "GM.openInTab", + "GM.setValue", + "GM.registerMenuCommand", + "GM.xmlHttpRequest" + ], + "exclude": [], + "include": [], + "exclude-match": [], + "match": ["https://*/*"], + "noframes": true, + "resource": {}, + "require": [], + "run-at": "document-start" + } +} diff --git a/Userscript-Plus.code-workspace b/Userscript-Plus.code-workspace index 35a299a..a90eb82 100644 --- a/Userscript-Plus.code-workspace +++ b/Userscript-Plus.code-workspace @@ -6,38 +6,36 @@ ], "settings": { "search.exclude": { + "**/.vscode/**": true, "**/build/**": true, "**/dist/**": true, "**/node_modules/**": true, "**/Notes/**": true }, - "editor.codeActionsOnSave": { - "source.fixAll": "never", - "source.fixAll.eslint": "explicit" - }, - "editor.formatOnSaveMode": "modifications", "explorer.excludeGitIgnore": true, "files.exclude": { + "**/.vscode/**": true, "**/build/**": true, "**/dist/**": true, "**/node_modules/**": true, "**/Notes/**": true }, - "files.trimTrailingWhitespace": true, "local-history.daysLimit": 3, "local-history.maxDisplay": 10, "local-history.saveDelay": 0, "local-history.dateLocale": "en-US", "local-history.exclude": [ "**/.history/**", - "**/.vscode/**", - "**/node_modules/**", - "**/dist/**", - "**/chrome_dist/**", - "**/build/**", - "**/*.code-workspace", - "**/src/sass/**", + "**/.vscode/**", + "**/node_modules/**", + "**/typings/**", + "**/out/**", + "**/Code/User/**", + "**/*.code-workspace", + "**/build/**", + "**/dist/**", + "**/Notes/**" ], - "local-history.path": "${workspaceFolder}/.vscode", + "local-history.path": "${workspaceFolder}/.vscode" } } \ No newline at end of file diff --git a/dist/.env.example b/dist/.env.example deleted file mode 100644 index ca948fe..0000000 --- a/dist/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -# Please do not change this .env variable -JS_ENV="production" diff --git a/dist/magic-userjs.meta.js b/dist/magic-userjs.meta.js index 5852785..5924b2f 100644 --- a/dist/magic-userjs.meta.js +++ b/dist/magic-userjs.meta.js @@ -1,5 +1,5 @@ // ==UserScript== -// @version 7.0.0 +// @version 7.6.8 // @name Magic Userscript+ : Show Site All UserJS // @name:ar Magic Userscript+: عرض جميع ملفات UserJS // @name:de Magic Userscript+ : Website anzeigen Alle UserJS @@ -24,13 +24,13 @@ // @description:zh 为当前网页查找可用的用户脚本。 // @description:zh-CN 为当前网页查找可用的用户脚本。 // @description:zh-TW 为当前网页查找可用的用户脚本。 +// @author Magic // @supportURL https://github.com/magicoflolis/Userscript-Plus/issues // @namespace https://github.com/magicoflolis/Userscript-Plus // @homepageURL https://github.com/magicoflolis/Userscript-Plus // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV8TRZFKBzuIOGSoTnZREcGlVLEIFkpboVUHk0u/oElDkuLiKLgWHPxYrDq4OOvq4CoIgh8gzg5Oii5S4v+SQosYD4778e7e4+4dIDSrTLN6YoCm22Y6EZdy+VWp7xUigghhDqLMLCOZWczCd3zdI8DXuyjP8j/35xhUCxYDAhJxjBmmTbxBPLNpG5z3icOsLKvE58QTJl2Q+JHrisdvnEsuCzwzbGbT88RhYqnUxUoXs7KpEU8TR1RNp3wh57HKeYuzVq2z9j35C4MFfSXDdZqjSGAJSaQgQUEdFVRhI0qrToqFNO3Hffwjrj9FLoVcFTByLKAGDbLrB/+D391axalJLykYB3pfHOdjDOjbBVoNx/k+dpzWCSA+A1d6x19rArOfpDc6WuQICG0DF9cdTdkDLneA4SdDNmVXEmkKxSLwfkbflAeGboGBNa+39j5OH4AsdbV8AxwcAuMlyl73eXd/d2//nmn39wOjunK6jS33SAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+gDDBAAJWyXgRAAABRPSURBVHjazVt9VNRl9v9854VhhjdBiHcUaFQEDTVCdk1IstTV0jb1LLZkJZrl4knLTnZaKjdqdXdLXcvfyoHUErPUBBEMLREJ6egJFBUUBF9WMd6R4WW+M/P5/eHMdxkYEJS0e8498MzzzPN87537PM+9n3u/An49kgFwA+AJIACAP4AxAEIA+AFwBzDEPLYJQB2AqwDOATgN4AqAywBubNu2rTk+Pl4EAJIyABAEwTQYDyn8CoIrAEQD+BOAMLPgLgAcAGDs2LGQyWSora1FYGAg6urqUFZWBgCIiYnBqVOn0NDQAAA6T0/Pm/X19ZflcvkZo9G4w2AwHAFgwG+Q5ACCg4KClixZsuT82rVrTTt27CCAHpybm8vq6moWFRXRZDKxpqaGkyZN4q0fl3zyySelsaIoMiYmhg899BD/9re/mQAUAogHEGxe8/5TaGiob0RERMrSpUuvZ2Zmmo4cOcIjR44wLy/PpgIOHjzI2tpaTp06lVqtlhUVFUxLS5MU8MQTT0hjLQqZPHkySRIAFy1aZAwNDa0C8C/z9rpvNBbAPwBctyVob3zw4EEWFBRQJpNJFvH999/3WwE5OTncs2ePZUwVgDjzWXPHB9Wd0GsACuRy+UoXFxev5cuXo7OzEyStWBBsHzGiKMJkunWGmUwmyGQy6X+lUgkAcHBw6HXxoUOHWv4dDiAFwNcAJtzpgTUQUgNYIgjCP2bNmiX7+9//jo0bN2Lz5s3IyspCUFCQJIz5xB7Q5FVVVZg9ezby8vKwYsWKgTzTFACZAFYC+BZA+6ArQKlUOouiuAZAgiAIMq1WixEjRuCjjz6Cq6srPvzwQ1RUVNze5GQyKyV1bW/atAnJycmIj49Henr6/66qLpbUdbzRaMS0adOQm5sLAN4A/g/ARADvAGgZvCNeLvdUq9VbzVcQAXDo0KHcs2cPDQYD29ramJiYSKVSedszICIigpGRkTbb9vb2nDRpEmNiYujk5MQZM2bQ3d2dbm5unDFjhtX4qKgonj59mt7e3t3XEAH82+xnDAp5AThjSxgPDw9++eWXNJlMbGpq4htvvCEdbr82r1ixgr/73e966zeaz4W7VoIzgAyZTMaoqChOnTqVarW6hxIyMzNpMBio0+m4ZMmSflnCPWCj2RKc71R4NYD1AIwODg4sKipiU1MTV69e3UNAT09Pfv311yTJxsZGLl++/J5Zwm1YNMugvhMFxAFoA0CFQsH33nuPHR0dbGtr49KlS6lSqawW8/LyYnZ2Ng0GA2/evMkXXniBCoXit6CEFrNbPiB6SalUtncVwNnZmcnJySTJlpYWvv7665TL5VaL+fj4cN++fSTJ+vp6vvzyy31agrOzc1/7eMAcFRXFIUOG2Oq7NhA/YaRcLr85bNgwuri4WE2kUqn48ccfs7OzkzqdjgkJCT0swdfXl7m5uTQajWxubuaCBQskRYWGhjIrK4uCIBAAY2JiSFJq3y2T5PTp03vrP9wfj1EA8PkjjzzC8+fPMysriz4+PlYTubi4cN26dSTJ5uZmm/vdz8+PBw4cIEnW1tbyxRdfpCAIDA4OpsFgsBo7WML3Y64287buk7x9fX07oqOj2dLSQpPJxMzMzB5KUKvV3LRpE/V6PW/evMmFCxfSzs7Oaoy/vz9/+OEHGo1GNjY2cu7cudRoNExNTeWePXuYkZHBsLAw7t+/n4IgUBAEPv/889y+fTu/+OILvvDCC5JAGRkZjIiI4OrVq/nll19aRYxdOSMjg+PHjycAJiUl8cMPP+TMmTO5Y8cOzpo1yxI7ePYV0v5rzpw51Gg0/Mtf/kKdTkeDwcCcnBx6enpaLebq6sr169dLJ7+t/a7Vann+/HmS5I0bN7hgwQK6urpy1apVLCwslLaASqViSkoKW1tbWVJSwuLiYt68eZNpaWlUKpUkyR9//JE///wzL126xOrqagYFBdncAhblZGdns66ujoWFhTx16hQ7Ozvp7+9PcxRpM5QOBlBu2cdOTk5877332NraSpPJxN27d9PLy8tqQQcHB27ZsoWiKLKlpYULFiyQrkhHR0e+++671Ov1tFB9fT1nz54tKSo6OpokOWbMGDY0NPC1116jWq2mvb09ly9fzsbGRoaHh5MkP/nkE9rb23PMmDESTnA7BVy4cIHBwcF0cXHhTz/9xH/+8580yxhsSwHx5nuT0dHRTEpKokql4sqVK9ne3k5RFLl//356eHhYLerm5sbPPvtMEvDFF1+kvb09165dy/b2dnan69ev86mnnqIgCJJwlnC366Hr7OxMk8nE2NhYkpRcYVvASW8KyM/Pl/qys7OZk5Nj8Q3ibQVFhZbBjz32GHU6HQFQqVQyOTmZOp2ORqOR6enpPZTg5OTErVu30mAwsKmpiSkpKezs7GRvdO7cOXp7e9PHx4cGg4GRkZHs7Ozkww8/LM05fvx4dnR0cNKkST0E7q8Cjh49aqUAC0gjCEJh90Aw1uw6SgqwABAWc37rrbfY0dFBURS5d+9eurm5WS3u7u7OlJQU9of0ej2fffZZOjk5UafT0dPTkydOnGBpaSmfeeYZzpkzh6dOnWJxcTG9vLwGTQGWtqOjo9EsM2Rm/lNXcKQ7kOHs7IwNGzZg48aNEEURTz31FD7++OOuwATa2tpw/vx5GAy3xyyVSiV8fX1hMplgMplw48YNvPTSS7h48SJ27NiB9PR0XL58GQsXLkRNTY3t+7oXsMXyeV9h9/z5861kdgdwvPt92tULtFxTarWaSUlJ1Ov11Ov13LVrF4cMGUKlUsl33nmHOp2uXxYgiiLnz59Pi5ttWUcmk1GhUFChUFjdKAqFwuqO79629blcLrfyVLu2zX+PW6LFUAD/7a+zoVKp+Mknn7C9vZ0Gg4Fbtmzh66+/zo6ODvaXzp07R19f3/sdI/zXLDumA2gdyJcdHByYnJxMURSp1+v7PPC6k8lk4ubNm38LIXMrgOlyM572zEAiJVEUUVBQAHd3d4wbNw4qlar/mRhBwKhRo3DlyhWcPXtWAkfvA9kBOCoHsBDAIwP9tsFgwNGjR6HRaBAZGWl14Nx2ZTs7PProo6irq0NxcfGAwdNBpEsAcOhOzSgoKIhqtZr/+c9/BnQGWKilpYVxcXE2cYO3336b77777q+9DQ4BQNmdfDk0NJS5ubm0s7Ojs7Mz169fT6PRaHPP90W//PIL4+Pje8QRmzZtYkpKyqAIGh0dzRMnTrC5uZm7du2in5+fpa8M5qzsgCaUyWRMT0+3+NbS7fDBBx+wvLyc9fX1rK2t5cmTJ/nSSy/xq6++oiiKvSqhqamJc+fO/VUQpMDAQNbW1jI9PZ0JCQksLy/nd999R3t7ewKow7fffmuqq6tjWlqapBlRFBkfH8+srCzJJe4e6lZUVEggaVlZGbVaLRUKBYOCgjhp0iRGRkbS29ubgiDQ3d2d27dv79MSampqOH/+fOke379/Pw8fPmxz24WHh9vkBx98sMf41atXs7KykgEBAQTAWbNmsba2lsHBwQRgwLx584wrVqxgdXU18/PzOXToUJJkVVUVk5KSGBcX12PShx9+WHJhAXDt2rVWgYeDgwMfe+wxenh48NVXX6W/vz/d3Ny4Z8+ePi2hsbGRs2fPplwuZ0JCAg8dOtRj7cOHD/f6/cLCwh7j09PTWVhYKDlBTk5OFEWR4eHhtKTa6yzwVFtbGydOnEiSfOutt3pFWCzRm+UunzBhAltaWjh69GgC4LJly5iXl0dBEOjm5sbg4GA6OTnxgQce4M6dO/u0hGvXrtGCSSQkJPRYWy6XS95id+6OUQJgZmam1Y8jCAJJWhRQpzArYOjJkyehVquhVt9CkH/++ederyeDwQCDwQCVSgVRFFFWVobS0lIkJCRg5cqVWLFiBdasWQMPDw9s2bIFTzzxBGpraxEXF4dly5ZBo9FgxowZkMt74hLe3t5ISUnB888/j23btvXof+ONNzB27Fibz1VWVob333/f6rOGhgZ4enrCzs4Oer0e7u7u0Ov1aGtrA4A6mUKh+K9SqcTTTz+NlpYWNDc3S0L2Ro2NjWhoaEBQUBAAQKfTYdeuXZgyZQoWL14MOzs7HDt2DFqtFjNnzsTcuXMRFhYGPz8/hIaGYvHixdi3b1+v87u5uWHz5s2IjY3t0Tds2DCEhoba5MDAwB7ji4qK4OPjg5CQEMhkMsyZMwcNDQ1obGwEgKtITEzcn5mZyaamJn7zzTfUaDQkyccff7zXk9XR0ZGFhYVctmyZ9NmQIUN45coV3rhxg/v27aNcLmd4eDibm5uZm5vLvXv3sqCggBcvXpSSKdnZ2TavTgvV1tbyySefvKski6urK8vLy1lSUsLdu3ezvr6eqampljk3YsGCBe/n5+fzzTfflCDu0tJSTpw4sc+Jly5dypycHKt9l5SUxNLSUgmalsvlXLp0KY8dO8ZPP/2U0dHRPHPmDAEwODiYPj4+zMjI6PNMuHTpUp8/Rn94+PDh/OKLL1hUVMQ1a9ZIcsrl8iV3FAxZMrm7d+9mWFjYXT2ct7e3lEfo64qMjY0d7HRbq6+v79MDDod7i7/vhv39/Zmdnd2nJVRVVXHy5MmDGg4LghBqExC5H+zv788jR4706Tpfu3aN0dHRg2UJEiAiM9fZ3PdE5vDhw5mbm9unJVRUVDAqKmow1kvpCgNagaL3WwnHjh3r0xKuXLnC3//+93ez/SRQ1AoWDwwM5PDhwwc84eTJkymKIgEwICCAw4YNs+rXaDSSlwiAo0ePpoODQ6/toKAgHjlypE9LKC8vt4LRB8g9YHFoNJqF5eXlpr/+9a8DntCS4rL46l1dz64wu+UX6w/MrdVqefz48T4tobq6mpGRkQO1BKvEiKwLrJ2/bt26ms8++wwAEBAQgGHDhsHNzQ0jRoyAp+etnKK7uztGjBgBb29vm16c0Wjs4UVaYC+S8Pf3BwD4+fkhJCQEo0aNAgD4+/sjJCQEDg4OCAwMRGVlJf785z/jxx9/7NVjDAgIQFpamuQay2Qy+Pj4YOTIkdBqtXB1dZVgOA8PD/j6+kIQhIsACmwmR11cXDZYNPX999+zsrJSMsWysjJOmTKFRUVF0rVkMeuuFvDdd99JlZ8WtmR3ANzWtKdNm0aSfPPNNxkUFMSQkBCeOHHitgfjhAkTqFarmZKSQpPJRKPRyIKCAoaEhDA0NJR5eXkcNWpUn8lRmFPHVZYS1oaGBs6ZM4djx45ldXU1q6urrdobN260qQCj0cj29naJLaixIAi0s7MjSc6cOVNKhHZty2QykuSZM2c4cuRIqlQqjho1isePH+8TaT59+jRHjx5NjUZDZ2dnhoSEMD8/n3v37uVXX33FxMREm+nx7oWSNwC8bb4i1GfPnkVGRgaMRiMqKythb29v1X7wwQdtavHChQv46KOPJKB05MiRWLVqFQBAr9dLf9vb/1fQ2b29bt06lJeXAwCuXr2KxYsXY9u2bXjooYdsIs1hYWHYu3cv5s2bh6qqKjz99NPo6OjA1KlTcfHiRSQkJLSbZbtxu1rhHACFDQ0N0Ov1MBqN0j7u2jYajbC3t7epgOvXr+Pzzz9HamoqUlNTceDAgQHDtR0dHdL/EyZMQGtrK5577jmcPHmy1+9otVps2bIFSqUSxcXF8PLywoULF/D++++jpaWl0CzbbYulGwCsysrKaureMVjwdUdHBwICAnptA8DLL78MX19feHt7IykpCYmJiaioqMCSJUtQWlraa84hIiICr732GnJychAbG4tFixbhm2++uQ5glVm2flWLn9y5c+erhw8f7uwtIdlbctJWX/f2gQMHsGHDBnR0dCA2NrZHGwBu3ryJkpISVFZWIiwsDPv378fIkSNRX1+P5557DiUlJb2uHx0dDZlMhk2bNuHSpUut5iLqkwMulHR0dPy3pWhixowZ/MMf/iCd7NOnT5fa3t7eXLx4cY/Pu0Z8ln4AfOCBBzhv3jwmJCTQz8/Pqu3r6yv5BY8//jgXLlzIRx55hDKZjF5eXlKhxMSJE3nu3Dmbh+LWrVspCAI1Gs1dFUpaSmX/fa/d5O4vTvTG48ePZ3FxsZXwlZWVluvOBODTuymVtZC7ufDYeC8VMG3atH6NHTFiBNPS0piXl8ddu3Z19QwPDGbFuLvZEsR7oQCNRmMT4e2NlUolhwwZYkl2mABsHUzhu26H9ebaW/5GWXe3FeL9qSD/k7n29p4K5+Pjc7uiimvmZ1PjHtAEc+1t271SwKFDh1hQUGAVLU6dOpX+/v7t5me5Jy9NSX4CgLkApgH4ALfe3vpVyWAwQBRFAMCUKVNgNBrxww8/VBsMhrfNHl7DHb0OdBfP1I5b7/juBCAPDg72GjdunLOXl5csMTERoaGhOH36NERRhEajQVxcHOLj4zF58mSIoojLly9Dq9UiOTkZJ06cwMqVK/Hoo4/i7NmzaGtrg4ODA1555RU8++yz0Ov1GDduHI4ePYqSkhJDfX191blz57aaTKYXzNheO+4zyZVK5YMajWahq6vrT4sWLTLl5eUxNTWVLi4uzMnJYW1tLauqqnj58mXW1dVx1qxZUqlsVlYWq6qq2Nrays2bN1OlUnH79u2sqqriwYMH+corr9DLy8uoUCiOC4Lw23p1tpdtFatSqVLHjx9fEhUV9curr77aYYHaVCoVk5KSWFRUxJiYGOr1es6bN49yuZxxcXFcv349PTw8+Mc//rHd09PzuvkXTjFjeIrBflihl/2mkMlkTgAUwi1HXiDZaPbpXVtbW39xcnKiOUByNcf6wi23X2gAgPj4eHl4ePiI4uJij5qaGv+Wlhbftra2EL1erzWZTN4k3a5everk4+ODixcvNpGs02g01wIDA6uvXr16IiIioik/P/9UZ2dnjZ+fX8OVK1dczOuZBEGQ63S6RkdHR5vRGUlPk8nUJJfLOy1tAI2CIOi7j/1/l0eTL0xHMHkAAAAASUVORK5CYII= // @downloadURL https://github.com/magicoflolis/Userscript-Plus/raw/master/dist/magic-userjs.user.js // @updateURL https://github.com/magicoflolis/Userscript-Plus/raw/master/dist/magic-userjs.meta.js -// @author Magic // @license MIT // @compatible chrome // @compatible firefox @@ -40,6 +40,7 @@ // @connect greasyfork.org // @connect sleazyfork.org // @connect github.com +// @connect githubusercontent.com // @connect openuserjs.org // @grant GM_addElement // @grant GM_info diff --git a/dist/magic-userjs.user.js b/dist/magic-userjs.user.js index 604696e..ba6c1a6 100644 --- a/dist/magic-userjs.user.js +++ b/dist/magic-userjs.user.js @@ -1,5 +1,5 @@ // ==UserScript== -// @version 7.0.0 +// @version 7.6.8 // @name Magic Userscript+ : Show Site All UserJS // @name:ar Magic Userscript+: عرض جميع ملفات UserJS // @name:de Magic Userscript+ : Website anzeigen Alle UserJS @@ -24,13 +24,13 @@ // @description:zh 为当前网页查找可用的用户脚本。 // @description:zh-CN 为当前网页查找可用的用户脚本。 // @description:zh-TW 为当前网页查找可用的用户脚本。 +// @author Magic // @supportURL https://github.com/magicoflolis/Userscript-Plus/issues // @namespace https://github.com/magicoflolis/Userscript-Plus // @homepageURL https://github.com/magicoflolis/Userscript-Plus // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV8TRZFKBzuIOGSoTnZREcGlVLEIFkpboVUHk0u/oElDkuLiKLgWHPxYrDq4OOvq4CoIgh8gzg5Oii5S4v+SQosYD4778e7e4+4dIDSrTLN6YoCm22Y6EZdy+VWp7xUigghhDqLMLCOZWczCd3zdI8DXuyjP8j/35xhUCxYDAhJxjBmmTbxBPLNpG5z3icOsLKvE58QTJl2Q+JHrisdvnEsuCzwzbGbT88RhYqnUxUoXs7KpEU8TR1RNp3wh57HKeYuzVq2z9j35C4MFfSXDdZqjSGAJSaQgQUEdFVRhI0qrToqFNO3Hffwjrj9FLoVcFTByLKAGDbLrB/+D391axalJLykYB3pfHOdjDOjbBVoNx/k+dpzWCSA+A1d6x19rArOfpDc6WuQICG0DF9cdTdkDLneA4SdDNmVXEmkKxSLwfkbflAeGboGBNa+39j5OH4AsdbV8AxwcAuMlyl73eXd/d2//nmn39wOjunK6jS33SAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+gDDBAAJWyXgRAAABRPSURBVHjazVt9VNRl9v9854VhhjdBiHcUaFQEDTVCdk1IstTV0jb1LLZkJZrl4knLTnZaKjdqdXdLXcvfyoHUErPUBBEMLREJ6egJFBUUBF9WMd6R4WW+M/P5/eHMdxkYEJS0e8498MzzzPN87537PM+9n3u/An49kgFwA+AJIACAP4AxAEIA+AFwBzDEPLYJQB2AqwDOATgN4AqAywBubNu2rTk+Pl4EAJIyABAEwTQYDyn8CoIrAEQD+BOAMLPgLgAcAGDs2LGQyWSora1FYGAg6urqUFZWBgCIiYnBqVOn0NDQAAA6T0/Pm/X19ZflcvkZo9G4w2AwHAFgwG+Q5ACCg4KClixZsuT82rVrTTt27CCAHpybm8vq6moWFRXRZDKxpqaGkyZN4q0fl3zyySelsaIoMiYmhg899BD/9re/mQAUAogHEGxe8/5TaGiob0RERMrSpUuvZ2Zmmo4cOcIjR44wLy/PpgIOHjzI2tpaTp06lVqtlhUVFUxLS5MU8MQTT0hjLQqZPHkySRIAFy1aZAwNDa0C8C/z9rpvNBbAPwBctyVob3zw4EEWFBRQJpNJFvH999/3WwE5OTncs2ePZUwVgDjzWXPHB9Wd0GsACuRy+UoXFxev5cuXo7OzEyStWBBsHzGiKMJkunWGmUwmyGQy6X+lUgkAcHBw6HXxoUOHWv4dDiAFwNcAJtzpgTUQUgNYIgjCP2bNmiX7+9//jo0bN2Lz5s3IyspCUFCQJIz5xB7Q5FVVVZg9ezby8vKwYsWKgTzTFACZAFYC+BZA+6ArQKlUOouiuAZAgiAIMq1WixEjRuCjjz6Cq6srPvzwQ1RUVNze5GQyKyV1bW/atAnJycmIj49Henr6/66qLpbUdbzRaMS0adOQm5sLAN4A/g/ARADvAGgZvCNeLvdUq9VbzVcQAXDo0KHcs2cPDQYD29ramJiYSKVSedszICIigpGRkTbb9vb2nDRpEmNiYujk5MQZM2bQ3d2dbm5unDFjhtX4qKgonj59mt7e3t3XEAH82+xnDAp5AThjSxgPDw9++eWXNJlMbGpq4htvvCEdbr82r1ixgr/73e966zeaz4W7VoIzgAyZTMaoqChOnTqVarW6hxIyMzNpMBio0+m4ZMmSflnCPWCj2RKc71R4NYD1AIwODg4sKipiU1MTV69e3UNAT09Pfv311yTJxsZGLl++/J5Zwm1YNMugvhMFxAFoA0CFQsH33nuPHR0dbGtr49KlS6lSqawW8/LyYnZ2Ng0GA2/evMkXXniBCoXit6CEFrNbPiB6SalUtncVwNnZmcnJySTJlpYWvv7665TL5VaL+fj4cN++fSTJ+vp6vvzyy31agrOzc1/7eMAcFRXFIUOG2Oq7NhA/YaRcLr85bNgwuri4WE2kUqn48ccfs7OzkzqdjgkJCT0swdfXl7m5uTQajWxubuaCBQskRYWGhjIrK4uCIBAAY2JiSFJq3y2T5PTp03vrP9wfj1EA8PkjjzzC8+fPMysriz4+PlYTubi4cN26dSTJ5uZmm/vdz8+PBw4cIEnW1tbyxRdfpCAIDA4OpsFgsBo7WML3Y64287buk7x9fX07oqOj2dLSQpPJxMzMzB5KUKvV3LRpE/V6PW/evMmFCxfSzs7Oaoy/vz9/+OEHGo1GNjY2cu7cudRoNExNTeWePXuYkZHBsLAw7t+/n4IgUBAEPv/889y+fTu/+OILvvDCC5JAGRkZjIiI4OrVq/nll19aRYxdOSMjg+PHjycAJiUl8cMPP+TMmTO5Y8cOzpo1yxI7ePYV0v5rzpw51Gg0/Mtf/kKdTkeDwcCcnBx6enpaLebq6sr169dLJ7+t/a7Vann+/HmS5I0bN7hgwQK6urpy1apVLCwslLaASqViSkoKW1tbWVJSwuLiYt68eZNpaWlUKpUkyR9//JE///wzL126xOrqagYFBdncAhblZGdns66ujoWFhTx16hQ7Ozvp7+9PcxRpM5QOBlBu2cdOTk5877332NraSpPJxN27d9PLy8tqQQcHB27ZsoWiKLKlpYULFiyQrkhHR0e+++671Ov1tFB9fT1nz54tKSo6OpokOWbMGDY0NPC1116jWq2mvb09ly9fzsbGRoaHh5MkP/nkE9rb23PMmDESTnA7BVy4cIHBwcF0cXHhTz/9xH/+8580yxhsSwHx5nuT0dHRTEpKokql4sqVK9ne3k5RFLl//356eHhYLerm5sbPPvtMEvDFF1+kvb09165dy/b2dnan69ev86mnnqIgCJJwlnC366Hr7OxMk8nE2NhYkpRcYVvASW8KyM/Pl/qys7OZk5Nj8Q3ibQVFhZbBjz32GHU6HQFQqVQyOTmZOp2ORqOR6enpPZTg5OTErVu30mAwsKmpiSkpKezs7GRvdO7cOXp7e9PHx4cGg4GRkZHs7Ozkww8/LM05fvx4dnR0cNKkST0E7q8Cjh49aqUAC0gjCEJh90Aw1uw6SgqwABAWc37rrbfY0dFBURS5d+9eurm5WS3u7u7OlJQU9of0ej2fffZZOjk5UafT0dPTkydOnGBpaSmfeeYZzpkzh6dOnWJxcTG9vLwGTQGWtqOjo9EsM2Rm/lNXcKQ7kOHs7IwNGzZg48aNEEURTz31FD7++OOuwATa2tpw/vx5GAy3xyyVSiV8fX1hMplgMplw48YNvPTSS7h48SJ27NiB9PR0XL58GQsXLkRNTY3t+7oXsMXyeV9h9/z5861kdgdwvPt92tULtFxTarWaSUlJ1Ov11Ov13LVrF4cMGUKlUsl33nmHOp2uXxYgiiLnz59Pi5ttWUcmk1GhUFChUFjdKAqFwuqO79629blcLrfyVLu2zX+PW6LFUAD/7a+zoVKp+Mknn7C9vZ0Gg4Fbtmzh66+/zo6ODvaXzp07R19f3/sdI/zXLDumA2gdyJcdHByYnJxMURSp1+v7PPC6k8lk4ubNm38LIXMrgOlyM572zEAiJVEUUVBQAHd3d4wbNw4qlar/mRhBwKhRo3DlyhWcPXtWAkfvA9kBOCoHsBDAIwP9tsFgwNGjR6HRaBAZGWl14Nx2ZTs7PProo6irq0NxcfGAwdNBpEsAcOhOzSgoKIhqtZr/+c9/BnQGWKilpYVxcXE2cYO3336b77777q+9DQ4BQNmdfDk0NJS5ubm0s7Ojs7Mz169fT6PRaHPP90W//PIL4+Pje8QRmzZtYkpKyqAIGh0dzRMnTrC5uZm7du2in5+fpa8M5qzsgCaUyWRMT0+3+NbS7fDBBx+wvLyc9fX1rK2t5cmTJ/nSSy/xq6++oiiKvSqhqamJc+fO/VUQpMDAQNbW1jI9PZ0JCQksLy/nd999R3t7ewKow7fffmuqq6tjWlqapBlRFBkfH8+srCzJJe4e6lZUVEggaVlZGbVaLRUKBYOCgjhp0iRGRkbS29ubgiDQ3d2d27dv79MSampqOH/+fOke379/Pw8fPmxz24WHh9vkBx98sMf41atXs7KykgEBAQTAWbNmsba2lsHBwQRgwLx584wrVqxgdXU18/PzOXToUJJkVVUVk5KSGBcX12PShx9+WHJhAXDt2rVWgYeDgwMfe+wxenh48NVXX6W/vz/d3Ny4Z8+ePi2hsbGRs2fPplwuZ0JCAg8dOtRj7cOHD/f6/cLCwh7j09PTWVhYKDlBTk5OFEWR4eHhtKTa6yzwVFtbGydOnEiSfOutt3pFWCzRm+UunzBhAltaWjh69GgC4LJly5iXl0dBEOjm5sbg4GA6OTnxgQce4M6dO/u0hGvXrtGCSSQkJPRYWy6XS95id+6OUQJgZmam1Y8jCAJJWhRQpzArYOjJkyehVquhVt9CkH/++ederyeDwQCDwQCVSgVRFFFWVobS0lIkJCRg5cqVWLFiBdasWQMPDw9s2bIFTzzxBGpraxEXF4dly5ZBo9FgxowZkMt74hLe3t5ISUnB888/j23btvXof+ONNzB27Fibz1VWVob333/f6rOGhgZ4enrCzs4Oer0e7u7u0Ov1aGtrA4A6mUKh+K9SqcTTTz+NlpYWNDc3S0L2Ro2NjWhoaEBQUBAAQKfTYdeuXZgyZQoWL14MOzs7HDt2DFqtFjNnzsTcuXMRFhYGPz8/hIaGYvHixdi3b1+v87u5uWHz5s2IjY3t0Tds2DCEhoba5MDAwB7ji4qK4OPjg5CQEMhkMsyZMwcNDQ1obGwEgKtITEzcn5mZyaamJn7zzTfUaDQkyccff7zXk9XR0ZGFhYVctmyZ9NmQIUN45coV3rhxg/v27aNcLmd4eDibm5uZm5vLvXv3sqCggBcvXpSSKdnZ2TavTgvV1tbyySefvKski6urK8vLy1lSUsLdu3ezvr6eqampljk3YsGCBe/n5+fzzTfflCDu0tJSTpw4sc+Jly5dypycHKt9l5SUxNLSUgmalsvlXLp0KY8dO8ZPP/2U0dHRPHPmDAEwODiYPj4+zMjI6PNMuHTpUp8/Rn94+PDh/OKLL1hUVMQ1a9ZIcsrl8iV3FAxZMrm7d+9mWFjYXT2ct7e3lEfo64qMjY0d7HRbq6+v79MDDod7i7/vhv39/Zmdnd2nJVRVVXHy5MmDGg4LghBqExC5H+zv788jR4706Tpfu3aN0dHRg2UJEiAiM9fZ3PdE5vDhw5mbm9unJVRUVDAqKmow1kvpCgNagaL3WwnHjh3r0xKuXLnC3//+93ez/SRQ1AoWDwwM5PDhwwc84eTJkymKIgEwICCAw4YNs+rXaDSSlwiAo0ePpoODQ6/toKAgHjlypE9LKC8vt4LRB8g9YHFoNJqF5eXlpr/+9a8DntCS4rL46l1dz64wu+UX6w/MrdVqefz48T4tobq6mpGRkQO1BKvEiKwLrJ2/bt26ms8++wwAEBAQgGHDhsHNzQ0jRoyAp+etnKK7uztGjBgBb29vm16c0Wjs4UVaYC+S8Pf3BwD4+fkhJCQEo0aNAgD4+/sjJCQEDg4OCAwMRGVlJf785z/jxx9/7NVjDAgIQFpamuQay2Qy+Pj4YOTIkdBqtXB1dZVgOA8PD/j6+kIQhIsACmwmR11cXDZYNPX999+zsrJSMsWysjJOmTKFRUVF0rVkMeuuFvDdd99JlZ8WtmR3ANzWtKdNm0aSfPPNNxkUFMSQkBCeOHHitgfjhAkTqFarmZKSQpPJRKPRyIKCAoaEhDA0NJR5eXkcNWpUn8lRmFPHVZYS1oaGBs6ZM4djx45ldXU1q6urrdobN260qQCj0cj29naJLaixIAi0s7MjSc6cOVNKhHZty2QykuSZM2c4cuRIqlQqjho1isePH+8TaT59+jRHjx5NjUZDZ2dnhoSEMD8/n3v37uVXX33FxMREm+nx7oWSNwC8bb4i1GfPnkVGRgaMRiMqKythb29v1X7wwQdtavHChQv46KOPJKB05MiRWLVqFQBAr9dLf9vb/1fQ2b29bt06lJeXAwCuXr2KxYsXY9u2bXjooYdsIs1hYWHYu3cv5s2bh6qqKjz99NPo6OjA1KlTcfHiRSQkJLSbZbtxu1rhHACFDQ0N0Ov1MBqN0j7u2jYajbC3t7epgOvXr+Pzzz9HamoqUlNTceDAgQHDtR0dHdL/EyZMQGtrK5577jmcPHmy1+9otVps2bIFSqUSxcXF8PLywoULF/D++++jpaWl0CzbbYulGwCsysrKaureMVjwdUdHBwICAnptA8DLL78MX19feHt7IykpCYmJiaioqMCSJUtQWlraa84hIiICr732GnJychAbG4tFixbhm2++uQ5glVm2flWLn9y5c+erhw8f7uwtIdlbctJWX/f2gQMHsGHDBnR0dCA2NrZHGwBu3ryJkpISVFZWIiwsDPv378fIkSNRX1+P5557DiUlJb2uHx0dDZlMhk2bNuHSpUut5iLqkwMulHR0dPy3pWhixowZ/MMf/iCd7NOnT5fa3t7eXLx4cY/Pu0Z8ln4AfOCBBzhv3jwmJCTQz8/Pqu3r6yv5BY8//jgXLlzIRx55hDKZjF5eXlKhxMSJE3nu3Dmbh+LWrVspCAI1Gs1dFUpaSmX/fa/d5O4vTvTG48ePZ3FxsZXwlZWVluvOBODTuymVtZC7ufDYeC8VMG3atH6NHTFiBNPS0piXl8ddu3Z19QwPDGbFuLvZEsR7oQCNRmMT4e2NlUolhwwZYkl2mABsHUzhu26H9ebaW/5GWXe3FeL9qSD/k7n29p4K5+Pjc7uiimvmZ1PjHtAEc+1t271SwKFDh1hQUGAVLU6dOpX+/v7t5me5Jy9NSX4CgLkApgH4ALfe3vpVyWAwQBRFAMCUKVNgNBrxww8/VBsMhrfNHl7DHb0OdBfP1I5b7/juBCAPDg72GjdunLOXl5csMTERoaGhOH36NERRhEajQVxcHOLj4zF58mSIoojLly9Dq9UiOTkZJ06cwMqVK/Hoo4/i7NmzaGtrg4ODA1555RU8++yz0Ov1GDduHI4ePYqSkhJDfX191blz57aaTKYXzNheO+4zyZVK5YMajWahq6vrT4sWLTLl5eUxNTWVLi4uzMnJYW1tLauqqnj58mXW1dVx1qxZUqlsVlYWq6qq2Nrays2bN1OlUnH79u2sqqriwYMH+corr9DLy8uoUCiOC4Lw23p1tpdtFatSqVLHjx9fEhUV9curr77aYYHaVCoVk5KSWFRUxJiYGOr1es6bN49yuZxxcXFcv349PTw8+Mc//rHd09PzuvkXTjFjeIrBflihl/2mkMlkTgAUwi1HXiDZaPbpXVtbW39xcnKiOUByNcf6wi23X2gAgPj4eHl4ePiI4uJij5qaGv+Wlhbftra2EL1erzWZTN4k3a5everk4+ODixcvNpGs02g01wIDA6uvXr16IiIioik/P/9UZ2dnjZ+fX8OVK1dczOuZBEGQ63S6RkdHR5vRGUlPk8nUJJfLOy1tAI2CIOi7j/1/l0eTL0xHMHkAAAAASUVORK5CYII= // @downloadURL https://github.com/magicoflolis/Userscript-Plus/raw/master/dist/magic-userjs.user.js // @updateURL https://github.com/magicoflolis/Userscript-Plus/raw/master/dist/magic-userjs.meta.js -// @author Magic // @license MIT // @compatible chrome // @compatible firefox @@ -40,6 +40,7 @@ // @connect greasyfork.org // @connect sleazyfork.org // @connect github.com +// @connect githubusercontent.com // @connect openuserjs.org // @grant GM_addElement // @grant GM_info @@ -62,606 +63,719 @@ (() => { 'use strict'; /******************************************************************************/ -const inIframe = () => { +const inIframe = (() => { try { return window.self !== window.top; - } catch (e) { + } catch { return true; } -} -if (inIframe()) { - return; -} +})(); +if (inIframe) return; let userjs = self.userjs; /** * Skip text/plain documents, based on uBlock Origin `vapi.js` file * - * [Source Code](https://github.com/gorhill/uBlock/blob/master/platform/common/vapi.js) + * [source code](https://github.com/gorhill/uBlock/blob/68962453ff6eec7ff109615a738beb8699b9844a/platform/common/vapi.js#L35) */ if ( (document instanceof Document || (document instanceof XMLDocument && document.createElement('div') instanceof HTMLDivElement)) && - /^image\/|^text\/plain/.test(document.contentType || '') === false && + /^text\/html|^application\/(xhtml|xml)/.test(document.contentType || '') === true && (self.userjs instanceof Object === false || userjs.UserJS !== true) ) { userjs = self.userjs = { UserJS: true }; -} -if (!(typeof userjs === 'object' && userjs.UserJS)) { - return; -} -/** - * Uncompressed locales + compiler - * - * [_locales](https://github.com/magicoflolis/Userscript-Plus/tree/master/src/_locales) - * - * [languageLoader.js](https://github.com/magicoflolis/Userscript-Plus/blob/master/tools/languageLoader.js) - */ +} else { + console.error('[%cMagic Userscript+%c] %cERROR','color: rgb(29, 155, 240);','','color: rgb(249, 24, 128);', `MIME type is not a document, got "${document.contentType || ''}"`); +} +if (!(typeof userjs === 'object' && userjs.UserJS)) return; +{ + /** Native implementation exists */ + const excludePolicy = [ + 'outlook.office.com' + ]; + const hostname = location?.hostname || ''; + if (window.trustedTypes && window.trustedTypes.createPolicy && !hostname.includes(excludePolicy)) window.trustedTypes.createPolicy('default', { createHTML: (string) => string, createScript: (string) => string, createScriptURL: (string) => string }); +} +/** [i18n directory](https://github.com/magicoflolis/Userscript-Plus/tree/master/src/_locales) */ const translations = { - "ar": { - "createdby": "انشأ من قبل", - "name": "اسم", - "daily_installs": "التثبيت اليومي", - "close": "يغلق", - "filterA": "منقي", - "max": "تحقيق أقصى قدر", - "min": "تصغير", - "search": "يبحث", - "search_placeholder": "بحث في البرامج النصية", - "install": "تثبيت", - "issue": "إصدار جديد", - "version_number": "الإصدار", - "updated": "آخر تحديث", - "total_installs": "إجمالي التثبيت", - "ratings": "التقييمات", - "good": "جيد", - "ok": "جيد", - "bad": "سيء", - "created_date": "تم إنشاؤه", - "redirect": "شوكة دهنية للكبار", - "filter": "تصفية اللغات الأخرى", - "dtime": "عرض المهلة", - "save": "حفظ", - "reset": "إعادة تعيين", - "preview_code": "كود المعاينة", - "saveFile": "احفظ الملف", - "newTab": "علامة تبويب جديدة", - "applies_to": "ينطبق على", - "license": "الترخيص", - "no_license": "لا يوجد", - "antifeatures": "إعلانات", - "userjs_fullscreen": "ملء الشاشة الكاملة التلقائي", - "listing_none": "(لا يوجد)", - "export_config": "تهيئة التصدير", - "export_theme": "تصدير السمة", - "import_config": "استيراد تهيئة الاستيراد", - "import_theme": "استيراد النسق", - "code_size": "حجم الرمز", - "prmpt_css": "التثبيت كأسلوب المستخدم؟", - "userjs_inject": "حقن Userscript+", - "userjs_close": "إغلاق Userscript+" + 'ar': { + 'createdby': 'انشأ من قبل', + 'name': 'اسم', + 'daily_installs': 'التثبيت اليومي', + 'close': 'يغلق', + 'filterA': 'منقي', + 'max': 'تحقيق أقصى قدر', + 'min': 'تصغير', + 'search': 'يبحث', + 'search_placeholder': 'بحث في البرامج النصية', + 'install': 'تثبيت', + 'issue': 'إصدار جديد', + 'version_number': 'الإصدار', + 'updated': 'آخر تحديث', + 'total_installs': 'إجمالي التثبيت', + 'ratings': 'التقييمات', + 'good': 'جيد', + 'ok': 'جيد', + 'bad': 'سيء', + 'created_date': 'تم إنشاؤه', + 'redirect': 'شوكة دهنية للكبار', + 'filter': 'تصفية اللغات الأخرى', + 'dtime': 'عرض المهلة', + 'save': 'حفظ', + 'reset': 'إعادة تعيين', + 'preview_code': 'كود المعاينة', + 'saveFile': 'احفظ الملف', + 'newTab': 'علامة تبويب جديدة', + 'applies_to': 'ينطبق على', + 'license': 'الترخيص', + 'no_license': 'لا يوجد', + 'antifeatures': 'إعلانات', + 'userjs_fullscreen': 'ملء الشاشة الكاملة التلقائي', + 'listing_none': '(لا يوجد)', + 'export_config': 'تهيئة التصدير', + 'export_theme': 'تصدير السمة', + 'import_config': 'استيراد تهيئة الاستيراد', + 'import_theme': 'استيراد النسق', + 'code_size': 'حجم الرمز', + 'prmpt_css': 'التثبيت كأسلوب المستخدم؟', + 'userjs_inject': 'حقن Userscript+', + 'userjs_close': 'إغلاق Userscript+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': 'Code', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "de": { - "createdby": "Erstellt von", - "name": "Name", - "daily_installs": "Tägliche Installationen", - "close": "Schließen Sie", - "filterA": "Filter", - "max": "Maximieren Sie", - "min": "minimieren", - "search": "Suche", - "search_placeholder": "Suche nach Userscripts", - "install": "Installieren Sie", - "issue": "Neue Ausgabe", - "version_number": "Version", - "updated": "Zuletzt aktualisiert", - "total_installs": "Installationen insgesamt", - "ratings": "Bewertungen", - "good": "Gut", - "ok": "Okay", - "bad": "Schlecht", - "created_date": "Erstellt", - "redirect": "Greasy Fork für Erwachsene", - "filter": "Andere Sprachen herausfiltern", - "dtime": "Zeitüberschreitung anzeigen", - "save": "Speichern Sie", - "reset": "Zurücksetzen", - "preview_code": "Vorschau Code", - "saveFile": "Datei speichern", - "newTab": "Neue Registerkarte", - "applies_to": "Gilt für", - "license": "Lizenz", - "no_license": "N/A", - "antifeatures": "Antifeatures", - "userjs_fullscreen": "Automatischer Vollbildmodus", - "listing_none": "(Keine)", - "export_config": "Konfig exportieren", - "export_theme": "Thema exportieren", - "import_config": "Konfig importieren", - "import_theme": "Thema importieren", - "code_size": "Code Größe", - "prmpt_css": "Als UserStyle installieren?", - "userjs_inject": "Userscript+ einfügen", - "userjs_close": "Userscript+ schließen" + 'de': { + 'createdby': 'Erstellt von', + 'name': 'Name', + 'daily_installs': 'Tägliche Installationen', + 'close': 'Schließen Sie', + 'filterA': 'Filter', + 'max': 'Maximieren Sie', + 'min': 'minimieren', + 'search': 'Suche', + 'search_placeholder': 'Suche nach Userscripts', + 'install': 'Installieren Sie', + 'issue': 'Neue Ausgabe', + 'version_number': 'Version', + 'updated': 'Zuletzt aktualisiert', + 'total_installs': 'Installationen insgesamt', + 'ratings': 'Bewertungen', + 'good': 'Gut', + 'ok': 'Okay', + 'bad': 'Schlecht', + 'created_date': 'Erstellt', + 'redirect': 'Greasy Fork für Erwachsene', + 'filter': 'Andere Sprachen herausfiltern', + 'dtime': 'Zeitüberschreitung anzeigen', + 'save': 'Speichern Sie', + 'reset': 'Zurücksetzen', + 'preview_code': 'Vorschau Code', + 'saveFile': 'Datei speichern', + 'newTab': 'Neue Registerkarte', + 'applies_to': 'Gilt für', + 'license': 'Lizenz', + 'no_license': 'N/A', + 'antifeatures': 'Antifeatures', + 'userjs_fullscreen': 'Automatischer Vollbildmodus', + 'listing_none': '(Keine)', + 'export_config': 'Konfig exportieren', + 'export_theme': 'Thema exportieren', + 'import_config': 'Konfig importieren', + 'import_theme': 'Thema importieren', + 'code_size': 'Code Größe', + 'prmpt_css': 'Als UserStyle installieren?', + 'userjs_inject': 'Userscript+ einfügen', + 'userjs_close': 'Userscript+ schließen', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': 'Quelltext', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "en": { - "createdby": "Created by", - "name": "Name", - "daily_installs": "Daily Installs", - "close": "Close", - "filterA": "Filter", - "max": "Maximize", - "min": "Minimize", - "search": "Search", - "search_placeholder": "Search for userscripts", - "install": "Install", - "issue": "New Issue", - "version_number": "Version", - "updated": "Last Updated", - "total_installs": "Total Installs", - "ratings": "Ratings", - "good": "Good", - "ok": "Okay", - "bad": "Bad", - "created_date": "Created", - "redirect": "Greasy Fork for adults", - "filter": "Filter out other languages", - "dtime": "Display Timeout", - "save": "Save", - "reset": "Reset", - "preview_code": "Preview Code", - "saveFile": "Save File", - "newTab": "New Tab", - "applies_to": "Applies to", - "license": "License", - "no_license": "N/A", - "antifeatures": "Antifeatures", - "userjs_fullscreen": "Automatic Fullscreen", - "listing_none": "(None)", - "export_config": "Export Config", - "export_theme": "Export Theme", - "import_config": "Import Config", - "import_theme": "Import Theme", - "code_size": "Code Size", - "prmpt_css": "Install as UserStyle?", - "userjs_inject": "Inject Userscript+", - "userjs_close": "Close Userscript+" + 'en': { + 'createdby': 'Created by', + 'name': 'Name', + 'daily_installs': 'Daily Installs', + 'close': 'Close', + 'filterA': 'Filter', + 'max': 'Maximize', + 'min': 'Minimize', + 'search': 'Search', + 'search_placeholder': 'Search for userscripts', + 'install': 'Install', + 'issue': 'New Issue', + 'version_number': 'Version', + 'updated': 'Last Updated', + 'total_installs': 'Total Installs', + 'ratings': 'Ratings', + 'good': 'Good', + 'ok': 'Okay', + 'bad': 'Bad', + 'created_date': 'Created', + 'redirect': 'Greasy Fork for adults', + 'filter': 'Filter out other languages', + 'dtime': 'Display Timeout', + 'save': 'Save', + 'reset': 'Reset', + 'preview_code': 'Preview Code', + 'saveFile': 'Download', + 'newTab': 'New Tab', + 'applies_to': 'Applies to', + 'license': 'License', + 'no_license': 'N/A', + 'antifeatures': 'Antifeatures', + 'userjs_fullscreen': 'Automatic Fullscreen', + 'listing_none': '(None)', + 'export_config': 'Export Config', + 'export_theme': 'Export Theme', + 'import_config': 'Import Config', + 'import_theme': 'Import Theme', + 'code_size': 'Code Size', + 'prmpt_css': 'Install as UserStyle?', + 'userjs_inject': 'Inject Userscript+', + 'userjs_close': 'Close Userscript+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': 'Code', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "en_GB": { - "createdby": "Created by", - "name": "Name", - "daily_installs": "Daily Installs", - "close": "Close", - "filterA": "Filter", - "max": "Maximize", - "min": "Minimize", - "search": "Search", - "search_placeholder": "Search scripts", - "install": "Install", - "issue": "New Issue", - "version_number": "Version", - "updated": "Last Updated", - "total_installs": "Total Installs", - "ratings": "Ratings", - "good": "Good", - "ok": "Ok", - "bad": "Bad", - "created_date": "Created", - "redirect": "Greasy Fork for adults", - "filter": "Filter out other languages", - "dtime": "Display Timeout", - "save": "Save", - "reset": "Reset", - "preview_code": "Preview Code", - "saveFile": "Save File", - "newTab": "New Tab", - "applies_to": "Applies to", - "license": "License", - "no_license": "N/A", - "antifeatures": "Antifeatures", - "userjs_fullscreen": "Automatic Fullscreen", - "listing_none": "(None)", - "export_config": "Export Config", - "export_theme": "Export Theme", - "import_config": "Import Config", - "import_theme": "Import Theme", - "code_size": "Code Size", - "prmpt_css": "Install as UserStyle?", - "userjs_inject": "Inject Userscript+", - "userjs_close": "Close Userscript+" + 'en_GB': { + 'createdby': 'Created by', + 'name': 'Name', + 'daily_installs': 'Daily Installs', + 'close': 'Close', + 'filterA': 'Filter', + 'max': 'Maximize', + 'min': 'Minimize', + 'search': 'Search', + 'search_placeholder': 'Search for userscripts', + 'install': 'Install', + 'issue': 'New Issue', + 'version_number': 'Version', + 'updated': 'Last Updated', + 'total_installs': 'Total Installs', + 'ratings': 'Ratings', + 'good': 'Good', + 'ok': 'Okay', + 'bad': 'Bad', + 'created_date': 'Created', + 'redirect': 'Greasy Fork for adults', + 'filter': 'Filter out other languages', + 'dtime': 'Display Timeout', + 'save': 'Save', + 'reset': 'Reset', + 'preview_code': 'Preview Code', + 'saveFile': 'Download', + 'newTab': 'New Tab', + 'applies_to': 'Applies to', + 'license': 'License', + 'no_license': 'N/A', + 'antifeatures': 'Antifeatures', + 'userjs_fullscreen': 'Automatic Fullscreen', + 'listing_none': '(None)', + 'export_config': 'Export Config', + 'export_theme': 'Export Theme', + 'import_config': 'Import Config', + 'import_theme': 'Import Theme', + 'code_size': 'Code Size', + 'prmpt_css': 'Install as UserStyle?', + 'userjs_inject': 'Inject Userscript+', + 'userjs_close': 'Close Userscript+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': 'Code', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "es": { - "createdby": "Creado por", - "name": "Nombre", - "daily_installs": "Instalaciones diarias", - "close": "Ya no se muestra", - "filterA": "Filtro", - "max": "Maximizar", - "min": "Minimizar", - "search": "Busque en", - "search_placeholder": "Buscar userscripts", - "install": "Instalar", - "issue": "Nueva edición", - "version_number": "Versión", - "updated": "Última actualización", - "total_installs": "Total de instalaciones", - "ratings": "Clasificaciones", - "good": "Bueno", - "ok": "Ok", - "bad": "Malo", - "created_date": "Creado", - "redirect": "Greasy Fork para adultos", - "filter": "Filtrar otros idiomas", - "dtime": "Mostrar el tiempo de espera", - "save": "Guardar", - "reset": "Reiniciar", - "preview_code": "Vista previa del código", - "saveFile": "Guardar archivo", - "newTab": "Guardar archivo", - "applies_to": "Se aplica a", - "license": "Licencia", - "no_license": "Desconocida", - "antifeatures": "Características indeseables", - "userjs_fullscreen": "Pantalla completa automática", - "listing_none": "(Ninguno)", - "export_config": "Exportar configuración", - "export_theme": "Exportar tema", - "import_config": "Importar configuración", - "import_theme": "Importar tema", - "code_size": "Código Tamaño", - "prmpt_css": "¿Instalar como UserStyle?", - "userjs_inject": "Inyectar Userscript+", - "userjs_close": "Cerrar Userscript+" + 'es': { + 'createdby': 'Creado por', + 'name': 'Nombre', + 'daily_installs': 'Instalaciones diarias', + 'close': 'Ya no se muestra', + 'filterA': 'Filtro', + 'max': 'Maximizar', + 'min': 'Minimizar', + 'search': 'Busque en', + 'search_placeholder': 'Buscar userscripts', + 'install': 'Instalar', + 'issue': 'Nueva edición', + 'version_number': 'Versión', + 'updated': 'Última actualización', + 'total_installs': 'Total de instalaciones', + 'ratings': 'Clasificaciones', + 'good': 'Bueno', + 'ok': 'Ok', + 'bad': 'Malo', + 'created_date': 'Creado', + 'redirect': 'Greasy Fork para adultos', + 'filter': 'Filtrar otros idiomas', + 'dtime': 'Mostrar el tiempo de espera', + 'save': 'Guardar', + 'reset': 'Reiniciar', + 'preview_code': 'Vista previa del código', + 'saveFile': 'Guardar archivo', + 'newTab': 'Guardar archivo', + 'applies_to': 'Se aplica a', + 'license': 'Licencia', + 'no_license': 'Desconocida', + 'antifeatures': 'Características indeseables', + 'userjs_fullscreen': 'Pantalla completa automática', + 'listing_none': '(Ninguno)', + 'export_config': 'Exportar configuración', + 'export_theme': 'Exportar tema', + 'import_config': 'Importar configuración', + 'import_theme': 'Importar tema', + 'code_size': 'Código Tamaño', + 'prmpt_css': '¿Instalar como UserStyle?', + 'userjs_inject': 'Inyectar Userscript+', + 'userjs_close': 'Cerrar Userscript+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': 'Código', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "fr": { - "createdby": "Créé par", - "name": "Nom", - "daily_installs": "Installations quotidiennes", - "close": "Ne plus montrer", - "filterA": "Filtre", - "max": "Maximiser", - "min": "Minimiser", - "search": "Recherche", - "search_placeholder": "Rechercher des userscripts", - "install": "Installer", - "issue": "Nouveau numéro", - "version_number": "Version", - "updated": "Dernière mise à jour", - "total_installs": "Total des installations", - "ratings": "Notations", - "good": "Bon", - "ok": "Ok", - "bad": "Mauvais", - "created_date": "Créé", - "redirect": "Greasy Fork pour les adultes", - "filter": "Filtrer les autres langues", - "dtime": "Délai d'affichage", - "save": "Sauvez", - "reset": "Réinitialiser", - "preview_code": "Prévisualiser le code", - "saveFile": "Enregistrer le fichier", - "newTab": "Nouvel onglet", - "applies_to": "S'applique à", - "license": "Licence", - "no_license": "N/A", - "antifeatures": "Antifeatures", - "userjs_fullscreen": "Plein écran automatique", - "listing_none": "(Aucun)", - "export_config": "Export Config", - "export_theme": "Exporter le thème", - "import_config": "Importer la configuration", - "import_theme": "Importer le thème", - "code_size": "Code Taille", - "prmpt_css": "Installer comme UserStyle ?", - "userjs_inject": "Injecter Userscript+", - "userjs_close": "Fermer Userscript+" + 'fr': { + 'createdby': 'Créé par', + 'name': 'Nom', + 'daily_installs': 'Installations quotidiennes', + 'close': 'Ne plus montrer', + 'filterA': 'Filtre', + 'max': 'Maximiser', + 'min': 'Minimiser', + 'search': 'Recherche', + 'search_placeholder': 'Rechercher des userscripts', + 'install': 'Installer', + 'issue': 'Nouveau numéro', + 'version_number': 'Version', + 'updated': 'Dernière mise à jour', + 'total_installs': 'Total des installations', + 'ratings': 'Notations', + 'good': 'Bon', + 'ok': 'Ok', + 'bad': 'Mauvais', + 'created_date': 'Créé', + 'redirect': 'Greasy Fork pour les adultes', + 'filter': 'Filtrer les autres langues', + 'dtime': "Délai d'affichage", + 'save': 'Sauvez', + 'reset': 'Réinitialiser', + 'preview_code': 'Prévisualiser le code', + 'saveFile': 'Enregistrer le fichier', + 'newTab': 'Nouvel onglet', + 'applies_to': "S'applique à", + 'license': 'Licence', + 'no_license': 'N/A', + 'antifeatures': 'Antifeatures', + 'userjs_fullscreen': 'Plein écran automatique', + 'listing_none': '(Aucun)', + 'export_config': 'Export Config', + 'export_theme': 'Exporter le thème', + 'import_config': 'Importer la configuration', + 'import_theme': 'Importer le thème', + 'code_size': 'Code Taille', + 'prmpt_css': 'Installer comme UserStyle ?', + 'userjs_inject': 'Injecter Userscript+', + 'userjs_close': 'Fermer Userscript+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': 'Code', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "ja": { - "createdby": "によって作成された", - "name": "名前", - "daily_installs": "デイリーインストール", - "close": "表示されなくなりました", - "filterA": "フィルター", - "max": "最大化", - "min": "ミニマム", - "search": "検索", - "search_placeholder": "ユーザースクリプトの検索", - "install": "インストール", - "issue": "新刊のご案内", - "version_number": "バージョン", - "updated": "最終更新日", - "total_installs": "総インストール数", - "ratings": "レーティング", - "good": "グッド", - "ok": "良い", - "bad": "悪い", - "created_date": "作成", - "redirect": "大人のGreasyfork", - "filter": "他の言語をフィルタリングする", - "dtime": "表示タイムアウト", - "save": "拯救", - "reset": "リセット", - "preview_code": "コードのプレビュー", - "saveFile": "ファイルを保存", - "newTab": "新しいタブ", - "applies_to": "適用対象", - "license": "ライセンス", - "no_license": "不明", - "antifeatures": "アンチ機能", - "userjs_fullscreen": "自動フルスクリーン", - "listing_none": "(なし)", - "export_config": "エクスポート設定", - "export_theme": "テーマのエクスポート", - "import_config": "設定のインポート", - "import_theme": "テーマのインポート", - "code_size": "コード・サイズ", - "prmpt_css": "UserStyleとしてインストールしますか?", - "userjs_inject": "Userscript+ を挿入", - "userjs_close": "Userscript+ を閉じる" + 'ja': { + 'createdby': 'によって作成された', + 'name': '名前', + 'daily_installs': 'デイリーインストール', + 'close': '表示されなくなりました', + 'filterA': 'フィルター', + 'max': '最大化', + 'min': 'ミニマム', + 'search': '検索', + 'search_placeholder': 'ユーザースクリプトの検索', + 'install': 'インストール', + 'issue': '新刊のご案内', + 'version_number': 'バージョン', + 'updated': '最終更新日', + 'total_installs': '総インストール数', + 'ratings': 'レーティング', + 'good': 'グッド', + 'ok': '良い', + 'bad': '悪い', + 'created_date': '作成', + 'redirect': '大人のGreasyfork', + 'filter': '他の言語をフィルタリングする', + 'dtime': '表示タイムアウト', + 'save': '拯救', + 'reset': 'リセット', + 'preview_code': 'コードのプレビュー', + 'saveFile': 'ファイルを保存', + 'newTab': '新しいタブ', + 'applies_to': '適用対象', + 'license': 'ライセンス', + 'no_license': '不明', + 'antifeatures': 'アンチ機能', + 'userjs_fullscreen': '自動フルスクリーン', + 'listing_none': '(なし)', + 'export_config': 'エクスポート設定', + 'export_theme': 'テーマのエクスポート', + 'import_config': '設定のインポート', + 'import_theme': 'テーマのインポート', + 'code_size': 'コード・サイズ', + 'prmpt_css': 'UserStyleとしてインストールしますか?', + 'userjs_inject': 'Userscript+ を挿入', + 'userjs_close': 'Userscript+ を閉じる', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': 'コード', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "nl": { - "createdby": "Gemaakt door", - "name": "Naam", - "daily_installs": "Dagelijkse Installaties", - "close": "Sluit", - "filterA": "Filter", - "max": "Maximaliseer", - "min": "Minimaliseer", - "search": "Zoek", - "search_placeholder": "Zoeken naar gebruikersscripts", - "install": "Installeer", - "issue": "Nieuw Issue", - "version_number": "Versie", - "updated": "Laatste Update", - "total_installs": "Totale Installaties", - "ratings": "Beoordeling", - "good": "Goed", - "ok": "Ok", - "bad": "Slecht", - "created_date": "Aangemaakt", - "redirect": "Greasy Fork voor volwassenen", - "filter": "Filter andere talen", - "dtime": "Weergave timeout", - "save": "Opslaan", - "reset": "Opnieuw instellen", - "preview_code": "Voorbeeldcode", - "saveFile": "Bestand opslaan", - "newTab": "Nieuw tabblad", - "applies_to": "Geldt voor", - "license": "Licentie", - "no_license": "N.v.t.", - "antifeatures": "Functies voor eigen gewin", - "userjs_fullscreen": "Automatisch volledig scherm", - "listing_none": "(Geen)", - "export_config": "Configuratie exporteren", - "export_theme": "Thema exporteren", - "import_config": "Configuratie importeren", - "import_theme": "Thema importeren", - "code_size": "Code Grootte", - "prmpt_css": "Installeren als UserStyle?", - "userjs_inject": "Injecteer Userscript+", - "userjs_close": "Sluit Userscript+" + 'nl': { + 'createdby': 'Gemaakt door', + 'name': 'Naam', + 'daily_installs': 'Dagelijkse Installaties', + 'close': 'Sluit', + 'filterA': 'Filter', + 'max': 'Maximaliseer', + 'min': 'Minimaliseer', + 'search': 'Zoek', + 'search_placeholder': 'Zoeken naar gebruikersscripts', + 'install': 'Installeer', + 'issue': 'Nieuw Issue', + 'version_number': 'Versie', + 'updated': 'Laatste Update', + 'total_installs': 'Totale Installaties', + 'ratings': 'Beoordeling', + 'good': 'Goed', + 'ok': 'Ok', + 'bad': 'Slecht', + 'created_date': 'Aangemaakt', + 'redirect': 'Greasy Fork voor volwassenen', + 'filter': 'Filter andere talen', + 'dtime': 'Weergave timeout', + 'save': 'Opslaan', + 'reset': 'Opnieuw instellen', + 'preview_code': 'Voorbeeldcode', + 'saveFile': 'Bestand opslaan', + 'newTab': 'Nieuw tabblad', + 'applies_to': 'Geldt voor', + 'license': 'Licentie', + 'no_license': 'N.v.t.', + 'antifeatures': 'Functies voor eigen gewin', + 'userjs_fullscreen': 'Automatisch volledig scherm', + 'listing_none': '(Geen)', + 'export_config': 'Configuratie exporteren', + 'export_theme': 'Thema exporteren', + 'import_config': 'Configuratie importeren', + 'import_theme': 'Thema importeren', + 'code_size': 'Code Grootte', + 'prmpt_css': 'Installeren als UserStyle?', + 'userjs_inject': 'Injecteer Userscript+', + 'userjs_close': 'Sluit Userscript+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': 'Code', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "pl": { - "createdby": "Stworzony przez", - "name": "Nazwa", - "daily_installs": "Codzienne instalacje", - "close": "Zamknij", - "filterA": "Filtr", - "max": "Maksymalizuj", - "min": "Minimalizuj", - "search": "Wyszukiwanie", - "search_placeholder": "Wyszukiwanie skryptów użytkownika", - "install": "Instalacja", - "issue": "Nowy numer", - "version_number": "Wersja", - "updated": "Ostatnia aktualizacja", - "total_installs": "Łączna liczba instalacji", - "ratings": "Oceny", - "good": "Dobry", - "ok": "Ok", - "bad": "Zły", - "created_date": "Utworzony", - "redirect": "Greasy Fork dla dorosłych", - "filter": "Odfiltruj inne języki", - "dtime": "Limit czasu wyświetlania", - "save": "Zapisz", - "reset": "Reset", - "preview_code": "Kod podglądu", - "saveFile": "Zapisz plik", - "newTab": "Nowa karta", - "applies_to": "Dotyczy", - "license": "Licencja", - "no_license": "N/A", - "antifeatures": "Antywzorce", - "userjs_fullscreen": "Automatyczny pełny ekran", - "listing_none": "(Brak)", - "export_config": "Konfiguracja eksportu", - "export_theme": "Motyw eksportu", - "import_config": "Importuj konfigurację", - "import_theme": "Importuj motyw", - "code_size": "Kod Rozmiar", - "prmpt_css": "Zainstalować jako UserStyle?", - "userjs_inject": "Wstrzyknij Userscript+", - "userjs_close": "Zamknij Userscript+" + 'pl': { + 'createdby': 'Stworzony przez', + 'name': 'Nazwa', + 'daily_installs': 'Codzienne instalacje', + 'close': 'Zamknij', + 'filterA': 'Filtr', + 'max': 'Maksymalizuj', + 'min': 'Minimalizuj', + 'search': 'Wyszukiwanie', + 'search_placeholder': 'Wyszukiwanie skryptów użytkownika', + 'install': 'Instalacja', + 'issue': 'Nowy numer', + 'version_number': 'Wersja', + 'updated': 'Ostatnia aktualizacja', + 'total_installs': 'Łączna liczba instalacji', + 'ratings': 'Oceny', + 'good': 'Dobry', + 'ok': 'Ok', + 'bad': 'Zły', + 'created_date': 'Utworzony', + 'redirect': 'Greasy Fork dla dorosłych', + 'filter': 'Odfiltruj inne języki', + 'dtime': 'Limit czasu wyświetlania', + 'save': 'Zapisz', + 'reset': 'Reset', + 'preview_code': 'Kod podglądu', + 'saveFile': 'Zapisz plik', + 'newTab': 'Nowa karta', + 'applies_to': 'Dotyczy', + 'license': 'Licencja', + 'no_license': 'N/A', + 'antifeatures': 'Antywzorce', + 'userjs_fullscreen': 'Automatyczny pełny ekran', + 'listing_none': '(Brak)', + 'export_config': 'Konfiguracja eksportu', + 'export_theme': 'Motyw eksportu', + 'import_config': 'Importuj konfigurację', + 'import_theme': 'Importuj motyw', + 'code_size': 'Kod Rozmiar', + 'prmpt_css': 'Zainstalować jako UserStyle?', + 'userjs_inject': 'Wstrzyknij Userscript+', + 'userjs_close': 'Zamknij Userscript+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': 'Kod', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "ru": { - "createdby": "Сделано", - "name": "Имя", - "daily_installs": "Ежедневные установки", - "close": "Больше не показывать", - "filterA": "Фильтр", - "max": "Максимизировать", - "min": "Минимизировать", - "search": "Поиск", - "search_placeholder": "Поиск юзерскриптов", - "install": "Установите", - "issue": "Новый выпуск", - "version_number": "Версия", - "updated": "Последнее обновление", - "total_installs": "Всего установок", - "ratings": "Рейтинги", - "good": "Хорошо", - "ok": "Хорошо", - "bad": "Плохо", - "created_date": "Создано", - "redirect": "Greasy Fork для взрослых", - "filter": "Отфильтровать другие языки", - "dtime": "Тайм-аут отображения", - "save": "Сохранить", - "reset": "Перезагрузить", - "preview_code": "Предварительный просмотр кода", - "saveFile": "Сохранить файл", - "newTab": "Новая вкладка", - "applies_to": "Применяется к", - "license": "Лицензия", - "no_license": "Недоступно", - "antifeatures": "Нежелательная функциональность", - "userjs_fullscreen": "Автоматический полноэкранный режим", - "listing_none": "(нет)", - "export_config": "Экспорт конфигурации", - "export_theme": "Экспорт темы", - "import_config": "Импорт конфигурации", - "import_theme": "Импортировать тему", - "code_size": "Код Размер", - "prmpt_css": "Установить как UserStyle?", - "userjs_inject": "Вставить Userscript+", - "userjs_close": "Закрыть Userscript+" + 'ru': { + 'createdby': 'Сделано', + 'name': 'Имя', + 'daily_installs': 'Ежедневные установки', + 'close': 'Больше не показывать', + 'filterA': 'Фильтр', + 'max': 'Максимизировать', + 'min': 'Минимизировать', + 'search': 'Поиск', + 'search_placeholder': 'Поиск юзерскриптов', + 'install': 'Установите', + 'issue': 'Новый выпуск', + 'version_number': 'Версия', + 'updated': 'Последнее обновление', + 'total_installs': 'Всего установок', + 'ratings': 'Рейтинги', + 'good': 'Хорошо', + 'ok': 'Хорошо', + 'bad': 'Плохо', + 'created_date': 'Создано', + 'redirect': 'Greasy Fork для взрослых', + 'filter': 'Отфильтровать другие языки', + 'dtime': 'Тайм-аут отображения', + 'save': 'Сохранить', + 'reset': 'Перезагрузить', + 'preview_code': 'Предварительный просмотр кода', + 'saveFile': 'Сохранить файл', + 'newTab': 'Новая вкладка', + 'applies_to': 'Применяется к', + 'license': 'Лицензия', + 'no_license': 'Недоступно', + 'antifeatures': 'Нежелательная функциональность', + 'userjs_fullscreen': 'Автоматический полноэкранный режим', + 'listing_none': '(нет)', + 'export_config': 'Экспорт конфигурации', + 'export_theme': 'Экспорт темы', + 'import_config': 'Импорт конфигурации', + 'import_theme': 'Импортировать тему', + 'code_size': 'Код Размер', + 'prmpt_css': 'Установить как UserStyle?', + 'userjs_inject': 'Вставить Userscript+', + 'userjs_close': 'Закрыть Userscript+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': 'Исходный код', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "zh": { - "createdby": "由...制作", - "name": "姓名", - "daily_installs": "日常安装", - "close": "不再显示", - "filterA": "过滤器", - "max": "最大化", - "min": "最小化", - "search": "搜索", - "search_placeholder": "搜索用户脚本", - "install": "安装", - "issue": "新问题", - "version_number": "版本", - "updated": "最后更新", - "total_installs": "总安装量", - "ratings": "评级", - "good": "好的", - "ok": "好的", - "bad": "不好", - "created_date": "创建", - "redirect": "大人的Greasyfork", - "filter": "过滤掉其他语言", - "dtime": "显示超时", - "save": "拯救", - "reset": "重置", - "preview_code": "预览代码", - "saveFile": "保存存档", - "newTab": "新标签", - "applies_to": "适用于", - "license": "许可证", - "no_license": "暂无", - "antifeatures": "可能不受欢迎的功能", - "userjs_fullscreen": "自动全屏", - "listing_none": "(无)", - "export_config": "导出配置", - "export_theme": "导出主题", - "import_config": "导入配置", - "import_theme": "导入主题", - "code_size": "代码 尺寸", - "prmpt_css": "安装为用户风格?", - "userjs_inject": "注入 Userscript+", - "userjs_close": "关闭 Userscript+" + 'zh': { + 'createdby': '由...制作', + 'name': '姓名', + 'daily_installs': '日常安装', + 'close': '不再显示', + 'filterA': '过滤器', + 'max': '最大化', + 'min': '最小化', + 'search': '搜索', + 'search_placeholder': '搜索用户脚本', + 'install': '安装', + 'issue': '新问题', + 'version_number': '版本', + 'updated': '最后更新', + 'total_installs': '总安装量', + 'ratings': '评级', + 'good': '好的', + 'ok': '好的', + 'bad': '不好', + 'created_date': '创建', + 'redirect': '大人的Greasyfork', + 'filter': '过滤掉其他语言', + 'dtime': '显示超时', + 'save': '拯救', + 'reset': '重置', + 'preview_code': '预览代码', + 'saveFile': '保存存档', + 'newTab': '新标签', + 'applies_to': '适用于', + 'license': '许可证', + 'no_license': '暂无', + 'antifeatures': '可能不受欢迎的功能', + 'userjs_fullscreen': '自动全屏', + 'listing_none': '(无)', + 'export_config': '导出配置', + 'export_theme': '导出主题', + 'import_config': '导入配置', + 'import_theme': '导入主题', + 'code_size': '代码 尺寸', + 'prmpt_css': '安装为用户风格?', + 'userjs_inject': '注入 Userscript+', + 'userjs_close': '关闭 Userscript+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': '代码', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "zh_CN": { - "createdby": "由...制作", - "name": "姓名", - "daily_installs": "日常安装", - "close": "不再显示", - "filterA": "过滤器", - "max": "最大化", - "min": "最小化", - "search": "搜索", - "search_placeholder": "搜索用户脚本", - "install": "安装", - "issue": "新问题", - "version_number": "版本", - "updated": "最后更新", - "total_installs": "总安装量", - "ratings": "评级", - "good": "好的", - "ok": "好的", - "bad": "不好", - "created_date": "创建", - "redirect": "大人的Greasyfork", - "filter": "过滤掉其他语言", - "dtime": "显示超时", - "save": "拯救", - "reset": "重置", - "preview_code": "预览代码", - "saveFile": "保存存档", - "newTab": "新标签", - "applies_to": "适用于", - "license": "许可证", - "no_license": "暂无", - "antifeatures": "可能不受欢迎的功能", - "userjs_fullscreen": "自动全屏", - "listing_none": "(无)", - "export_config": "导出配置", - "export_theme": "导出主题", - "import_config": "导入配置", - "import_theme": "导入主题", - "code_size": "代码 尺寸", - "prmpt_css": "安装为用户风格?", - "userjs_inject": "注入 Userscript+", - "userjs_close": "关闭 Userscript+" + 'zh_CN': { + 'createdby': '由...制作', + 'name': '姓名', + 'daily_installs': '日常安装', + 'close': '不再显示', + 'filterA': '过滤器', + 'max': '最大化', + 'min': '最小化', + 'search': '搜索', + 'search_placeholder': '搜索用户脚本', + 'install': '安装', + 'issue': '新问题', + 'version_number': '版本', + 'updated': '最后更新', + 'total_installs': '总安装量', + 'ratings': '评级', + 'good': '好的', + 'ok': '好的', + 'bad': '不好', + 'created_date': '创建', + 'redirect': '大人的Greasyfork', + 'filter': '过滤掉其他语言', + 'dtime': '显示超时', + 'save': '拯救', + 'reset': '重置', + 'preview_code': '预览代码', + 'saveFile': '保存存档', + 'newTab': '新标签', + 'applies_to': '适用于', + 'license': '许可证', + 'no_license': '暂无', + 'antifeatures': '可能不受欢迎的功能', + 'userjs_fullscreen': '自动全屏', + 'listing_none': '(无)', + 'export_config': '导出配置', + 'export_theme': '导出主题', + 'import_config': '导入配置', + 'import_theme': '导入主题', + 'code_size': '代码 尺寸', + 'prmpt_css': '安装为用户风格?', + 'userjs_inject': '注入 Userscript+', + 'userjs_close': '关闭 Userscript+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': '代码', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' }, - "zh_TW": { - "createdby": "由...制作", - "name": "姓名", - "daily_installs": "日常安装", - "close": "不再显示", - "filterA": "过滤器", - "max": "最大化", - "min": "最小化", - "search": "搜索", - "search_placeholder": "搜索用户脚本", - "install": "安装", - "issue": "新问题", - "version_number": "版本", - "updated": "最后更新", - "total_installs": "总安装量", - "ratings": "评级", - "good": "好的", - "ok": "好的", - "bad": "不好", - "created_date": "创建", - "redirect": "大人的Greasyfork", - "filter": "过滤掉其他语言", - "dtime": "显示超时", - "save": "拯救", - "reset": "重置", - "preview_code": "预览代码", - "saveFile": "保存存档", - "newTab": "新标签", - "applies_to": "适用于", - "license": "许可证", - "no_license": "暂无", - "antifeatures": "可能不受欢迎的功能", - "userjs_fullscreen": "自动全屏", - "listing_none": "(无)", - "export_config": "导出配置", - "export_theme": "导出主题", - "import_config": "导入配置", - "import_theme": "导入主题", - "code_size": "代码 尺寸", - "prmpt_css": "作為使用者樣式安裝?", - "userjs_inject": "注入用戶腳本+", - "userjs_close": "關閉用戶腳本+" + 'zh_TW': { + 'createdby': '由...制作', + 'name': '姓名', + 'daily_installs': '日常安装', + 'close': '不再显示', + 'filterA': '过滤器', + 'max': '最大化', + 'min': '最小化', + 'search': '搜索', + 'search_placeholder': '搜索用户脚本', + 'install': '安装', + 'issue': '新问题', + 'version_number': '版本', + 'updated': '最后更新', + 'total_installs': '总安装量', + 'ratings': '评级', + 'good': '好的', + 'ok': '好的', + 'bad': '不好', + 'created_date': '创建', + 'redirect': '大人的Greasyfork', + 'filter': '过滤掉其他语言', + 'dtime': '显示超时', + 'save': '拯救', + 'reset': '重置', + 'preview_code': '预览代码', + 'saveFile': '保存存档', + 'newTab': '新标签', + 'applies_to': '适用于', + 'license': '许可证', + 'no_license': '暂无', + 'antifeatures': '可能不受欢迎的功能', + 'userjs_fullscreen': '自动全屏', + 'listing_none': '(无)', + 'export_config': '导出配置', + 'export_theme': '导出主题', + 'import_config': '导入配置', + 'import_theme': '导入主题', + 'code_size': '代码 尺寸', + 'prmpt_css': '作為使用者樣式安裝?', + 'userjs_inject': '注入用戶腳本+', + 'userjs_close': '關閉用戶腳本+', + 'userjs_sync': 'Sync', + 'userjs_autoinject': 'Inject on load', + 'auto_fetch': 'Fetch on load', + 'code': '代碼', + 'metadata': 'Metadata', + 'preview_metadata': 'Preview Metadata', + 'recommend_author': 'Recommend Author', + 'recommend_other': 'Recommend Others', + 'default_sort': 'Default Sort' } }; -/** - * To compile this CSS `pnpm run build:Sass` - * - * [Uncompiled Cascading Style Sheet](https://github.com/magicoflolis/Userscript-Plus/tree/master/src/sass) - */ +/** [source code](https://github.com/magicoflolis/Userscript-Plus/blob/master/src/sass/_main.scss) */ const main_css = `mujs-root { --mujs-even-row: hsl(222, 14%, 22%); --mujs-odd-row: hsl(222, 14%, 11%); @@ -693,6 +807,9 @@ const main_css = `mujs-root { } mujs-root * { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; scrollbar-color: var(--mujs-txt-color, hsl(0, 0%, 100%)) hsl(224, 14%, 21%); scrollbar-width: thin; } @@ -739,10 +856,6 @@ td.mujs-list { font-weight: 500; } -.mujs-sty-flex > mujs-btn { - margin: auto; -} - .mujs-invalid { border-radius: 8px !important; border-width: 2px !important; @@ -762,11 +875,6 @@ mujs-row { gap: 0.5em; } -@media screen and (max-width: 800px) { - mujs-column { - flex-flow: row wrap; - } -} mujs-column count-frame[data-counter=greasyfork] { background: var(--mujs-gf-color, hsl(204, 100%, 40%)); } @@ -779,6 +887,11 @@ mujs-column count-frame[data-counter=github] { mujs-column count-frame[data-counter=openuserjs] { background: hsla(12, 86%, 50%, 0.568); } +@media screen and (max-width: 800px) { + mujs-column { + flex-flow: row wrap; + } +} mujs-row { flex-flow: column wrap; @@ -876,6 +989,11 @@ mujs-main mujs-tabs mujs-tab { border-radius: 4px; background: transparent; } +@media screen and (max-width: 800px) { + mujs-main mujs-tabs mujs-tab { + min-width: 6em !important; + } +} mujs-main mujs-tabs mujs-tab.active { background: var(--mujs-even-row, hsl(222, 14%, 18%)); } @@ -918,17 +1036,25 @@ mujs-main input:not([type=checkbox]) { border: transparent; outline: none !important; } +mujs-main mujs-page, mujs-main textarea { background: inherit; overflow-y: auto; - color: var(--mujs-placeholder, hsl(81, 56%, 54%)); border: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); - border-radius: 10px; - resize: vertical; + border-radius: 5px; outline: none; font-family: monospace; font-size: 14px; } +mujs-main mujs-page { + padding: 0.5em; + margin: 0.5em; +} +mujs-main textarea { + overflow-y: auto; + color: var(--mujs-placeholder, hsl(81, 56%, 54%)); + resize: vertical; +} mujs-main textarea:focus { outline: none; } @@ -958,8 +1084,7 @@ mujs-main .mujs-prompt { align-items: center; justify-content: center; } -mujs-main .mujs-prompt svg, -mujs-main .mujs-prompt img { +mujs-main .mujs-prompt svg { width: 14px; height: 14px; background: transparent; @@ -982,19 +1107,19 @@ mujs-main .mujs-prompt > .prompt .prompt-body { grid-gap: 0.5em; padding-top: 0.5em; } -mujs-main .mujs-prompt > .prompt mujs-btn[data-command=prompt-deny] { +mujs-main .mujs-prompt > .prompt mujs-btn.prompt-deny { background: var(--mujs-sf-btn-color, hsl(12, 86%, 50%)); border-color: var(--mujs-sf-btn-color, hsl(12, 86%, 50%)); } -mujs-main .mujs-prompt > .prompt mujs-btn[data-command=prompt-deny]:hover { +mujs-main .mujs-prompt > .prompt mujs-btn.prompt-deny:hover { background: var(--mujs-sf-txt-color, hsl(12, 79%, 55%)); border-color: var(--mujs-sf-txt-color, hsl(12, 79%, 55%)); } -mujs-main .mujs-prompt > .prompt mujs-btn[data-command=prompt-confirm] { +mujs-main .mujs-prompt > .prompt mujs-btn.prompt-confirm { background: var(--mujs-gf-color, hsl(204, 100%, 40%)); border-color: var(--mujs-gf-color, hsl(204, 100%, 40%)); } -mujs-main .mujs-prompt > .prompt mujs-btn[data-command=prompt-confirm]:hover { +mujs-main .mujs-prompt > .prompt mujs-btn.prompt-confirm:hover { background: var(--mujs-gf-btn-color, hsl(211, 87%, 56%)); border-color: var(--mujs-gf-btn-color, hsl(211, 87%, 56%)); } @@ -1075,9 +1200,6 @@ mujs-header mujs-url > input { mujs-header .rate-container { order: 1; } -mujs-header .mujs-sty-flex { - order: 2; -} mujs-header .btn-frame { order: 999999999999; } @@ -1124,13 +1246,11 @@ mujs-body table { display: flex; flex-flow: row wrap; align-items: center; - border-bottom: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); padding-top: 0.5em; padding-bottom: 0.5em; } mujs-body table .frame:not(.webext-page) td { margin: auto; - border-bottom: 1px solid transparent; } mujs-body table .frame:not(.webext-page) td > mujs-a, mujs-body table .frame:not(.webext-page) td > mu-js, @@ -1138,9 +1258,32 @@ mujs-body table { text-align: center; justify-content: center; } + mujs-body table .frame:not(.webext-page) td > mujs-a { + width: 100%; + } +} +@media screen and (max-width: 1180px) and (max-width: 800px) { + mujs-body table .frame:not(.webext-page) td > mujs-column { + flex-flow: column wrap; + } + mujs-body table .frame:not(.webext-page) td > mujs-column > mujs-row { + align-content: center; + } + mujs-body table .frame:not(.webext-page) td > mujs-column mujs-column { + justify-content: center; + } +} +@media screen and (max-width: 1180px) { mujs-body table .frame:not(.webext-page) td:not(.mujs-name, .install-btn) { width: 25%; } +} +@media screen and (max-width: 1180px) and (max-width: 800px) { + mujs-body table .frame:not(.webext-page) td.install-btn { + width: 100%; + } +} +@media screen and (max-width: 1180px) { mujs-body table .frame:not(.webext-page) .mujs-name { width: 100%; } @@ -1153,15 +1296,12 @@ mujs-body table { width: auto !important; } } -mujs-body table th, -mujs-body table td { - border-bottom: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); -} mujs-body table th { position: -webkit-sticky; position: sticky; top: 0; background: hsla(222, 14%, 33%, 0.75); + border-bottom: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); } mujs-body table th.mujs-header-name { width: 50%; @@ -1174,9 +1314,15 @@ mujs-body table th.mujs-header-name { mujs-body table .frame:nth-child(even) { background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; } +mujs-body table .frame:nth-child(even) textarea { + background: var(--mujs-odd-row, hsl(222, 14%, 33%)) !important; +} mujs-body table .frame:nth-child(odd) { background: var(--mujs-odd-row, hsl(222, 14%, 33%)) !important; } +mujs-body table .frame:nth-child(odd) textarea { + background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; +} mujs-body table .frame:not([data-engine=sleazyfork], [data-engine=greasyfork]) mujs-a { color: var(--mujs-sf-txt-color, hsl(12, 79%, 55%)); } @@ -1220,6 +1366,21 @@ mujs-body table .frame[data-good] mu-jsbtn:hover, mujs-body table .frame[data-au background: hsl(81, 56%, 65%); border-color: hsl(81, 56%, 65%); } +mujs-body table .frame.translated:not([data-good], [data-author]) mujs-a { + color: hsl(249, 56%, 65%); +} +mujs-body table .frame.translated:not([data-good], [data-author]) mujs-a:hover { + color: hsl(249, 56%, 85%); +} +mujs-body table .frame.translated:not([data-good], [data-author]) mu-jsbtn { + color: hsl(215, 47%, 85%); + background: hsl(249, 56%, 65%); + border-color: hsl(249, 56%, 65%); +} +mujs-body table .frame.translated:not([data-good], [data-author]) mu-jsbtn:hover { + background: hsl(249, 56%, 65%); + border-color: hsl(249, 56%, 65%); +} mujs-body table .frame .mujs-ratings[data-el=good] { border-color: hsl(120, 50%, 40%); background-color: hsla(120, 50%, 40%, 0.102); @@ -1236,12 +1397,9 @@ mujs-body table .frame .mujs-ratings[data-el=bad] { color: hsl(0, 100%, 50%); } mujs-body table .frame svg { + width: 12px; + height: 12px; fill: currentColor; -} -mujs-body table .frame svg, -mujs-body table .frame img { - width: 14px; - height: 14px; background: transparent; } mujs-body table .frame > td:not(.mujs-name) { @@ -1264,6 +1422,11 @@ mujs-body table .frame > .mujs-name > mu-jsbtn { mujs-body table .frame > .mujs-name > mujs-column > mu-jsbtn { padding: 0px 7px; } +@media screen and (max-width: 800px) { + mujs-body table .frame > .mujs-name > mujs-column > mu-jsbtn { + width: 100%; + } +} mujs-body table .frame > .mujs-uframe > mujs-a { font-size: 16px; font-weight: 500; @@ -1304,6 +1467,27 @@ mujs-body table .frame [data-el=matches] .mujs-grants > mujs-a::after { mujs-body table .frame [data-el=matches] .mujs-grants > mujs-a:last-child::after { content: ""; } +@media screen and (max-width: 800px) { + mujs-body table .frame [data-el=matches] { + align-self: center; + width: 30em !important; + } +} +mujs-body table .frame [data-name=license] { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; +} +@media screen and (max-width: 800px) { + mujs-body table .frame [data-name=license] { + width: 100% !important; + width: -moz-available !important; + width: -webkit-fill-available !important; + } +} @media screen and (max-width: 1150px) { .mujs-cfg { @@ -1315,18 +1499,6 @@ mujs-body table .frame [data-el=matches] .mujs-grants > mujs-a:last-child::after height: -moz-fit-content; height: -webkit-fit-content; } -@media screen and (max-height: 720px) { - .mujs-cfg:not(.webext-page) { - height: 100%; - height: -moz-available; - height: -webkit-fill-available; - width: 100%; - width: -moz-available; - width: -webkit-fill-available; - overflow-x: auto; - padding: 0.5em; - } -} .mujs-cfg mujs-section { border-radius: 16px; padding: 0.5em; @@ -1334,9 +1506,68 @@ mujs-body table .frame [data-el=matches] .mujs-grants > mujs-a:last-child::after .mujs-cfg mujs-section:nth-child(even) { background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; } +.mujs-cfg mujs-section:nth-child(even) input, +.mujs-cfg mujs-section:nth-child(even) select { + background: var(--mujs-odd-row, hsl(222, 14%, 33%)); +} +.mujs-cfg mujs-section:nth-child(even) select option { + background: var(--mujs-odd-row, hsl(222, 14%, 33%)); +} +.mujs-cfg mujs-section:nth-child(even) select option:hover { + background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; +} .mujs-cfg mujs-section:nth-child(odd) { background: var(--mujs-odd-row, hsl(222, 14%, 33%)) !important; } +.mujs-cfg mujs-section:nth-child(odd) input, +.mujs-cfg mujs-section:nth-child(odd) select { + background: var(--mujs-even-row, hsl(222, 14%, 18%)); +} +.mujs-cfg mujs-section:nth-child(odd) select option { + background: var(--mujs-even-row, hsl(222, 14%, 18%)); +} +.mujs-cfg mujs-section:nth-child(odd) select option:hover { + background: var(--mujs-odd-row, hsl(222, 14%, 33%)) !important; +} +.mujs-cfg mujs-section[data-name=theme] .sub-section { + border-radius: 4px; +} +.mujs-cfg mujs-section[data-name=theme] .sub-section:nth-child(even) { + background: var(--mujs-even-row, hsl(222, 14%, 18%)); +} +.mujs-cfg mujs-section[data-name=theme] .sub-section:nth-child(odd) { + background: var(--mujs-odd-row, hsl(222, 14%, 33%)); +} +.mujs-cfg mujs-section[data-name=theme] input, +.mujs-cfg mujs-section[data-name=theme] select { + background: inherit; +} +.mujs-cfg mujs-section[data-name=theme] select option { + background: inherit; +} +.mujs-cfg mujs-section[data-name=theme] select option:hover { + background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; +} +.mujs-cfg mujs-section svg { + width: 14px; + height: 14px; + fill: currentColor; + background: transparent; +} +.mujs-cfg mujs-section[data-name=exp], .mujs-cfg mujs-section[data-name=blacklist] { + display: flex; + justify-content: space-between; + flex-direction: column; + gap: 0.25em; +} +.mujs-cfg mujs-section[data-name=exp] > mujs-btn, .mujs-cfg mujs-section[data-name=blacklist] > mujs-btn { + width: 100%; + width: -moz-available; + width: -webkit-fill-available; +} +.mujs-cfg mujs-section[data-name=exp] > mujs-btn:hover, .mujs-cfg mujs-section[data-name=blacklist] > mujs-btn:hover { + background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; +} .mujs-cfg mujs-section input[type=text]::-webkit-input-placeholder { color: var(--mujs-placeholder, hsl(81, 56%, 54%)); } @@ -1352,23 +1583,54 @@ mujs-body table .frame [data-el=matches] .mujs-grants > mujs-a:last-child::after .mujs-cfg mujs-section input[type=text]::placeholder { color: var(--mujs-placeholder, hsl(81, 56%, 54%)); } -.mujs-cfg mujs-section > label { +.mujs-cfg mujs-section > label:not([data-blacklist]) { display: flex; justify-content: space-between; } +.mujs-cfg mujs-section > label[data-blacklist] { + display: grid; + grid-auto-flow: column; +} +.mujs-cfg mujs-section > label[data-blacklist]:not(.new-list) { + grid-template-columns: repeat(2, 1fr); +} +.mujs-cfg mujs-section > label.new-list { + order: 999999999999; +} +.mujs-cfg mujs-section > label.new-list mujs-add { + font-size: 20px; +} .mujs-cfg mujs-section > label input:not([type=checkbox]) { font-size: 14px; position: relative; border-radius: 4px; border: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); } -.mujs-cfg mujs-section > input { +.mujs-cfg mujs-section select, +.mujs-cfg mujs-section select option { + color: var(--mujs-txt-color, hsl(0, 0%, 100%)); + border: 1px solid transparent; + list-style: none; + outline-style: none; + pointer-events: auto; +} +.mujs-cfg mujs-section select { + text-align: center; + border-radius: 4px; +} +.mujs-cfg mujs-section > *.sub-section { + padding: 0.2em; +} +.mujs-cfg mujs-section > *.sub-section[data-engine] { + flex-wrap: wrap; +} +.mujs-cfg mujs-section > *.sub-section[data-engine] input { width: 100%; width: -moz-available; width: -webkit-fill-available; - margin-top: 5px; - border-radius: 4px; - border: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); +} +.mujs-cfg mujs-section > *.sub-section input[type=text] { + margin: 0.2em 0px; } .mujs-cfg .mujs-inlab { position: relative; @@ -1384,8 +1646,11 @@ mujs-body table .frame [data-el=matches] .mujs-grants > mujs-a:last-child::after .mujs-cfg .mujs-inlab input[type=checkbox]:checked + label:before { right: 0px; } -.mujs-cfg .mujs-inlab input[type=checkbox][data-name=greasyfork]:checked + label, .mujs-cfg .mujs-inlab input[type=checkbox][data-name=sleazyfork]:checked + label { - background: var(--mujs-chck-gf, hsla(197, 100%, 50%, 0.568)); +.mujs-cfg .mujs-inlab input[type=checkbox][data-name=greasyfork]:checked + label { + background: var(--mujs-gf-color, hsl(204, 100%, 40%)); +} +.mujs-cfg .mujs-inlab input[type=checkbox][data-name=sleazyfork]:checked + label { + background: var(--mujs-sf-color, hsl(12, 86%, 50%)); } .mujs-cfg .mujs-inlab input[type=checkbox][data-name=openuserjs]:checked + label { background: var(--mujs-chck-open, hsla(12, 86%, 50%, 0.568)); @@ -1413,6 +1678,9 @@ mujs-body table .frame [data-el=matches] .mujs-grants > mujs-a:last-child::after right: 20px; border-radius: 20px; } +.mujs-cfg .mujs-sty-flex mujs-btn { + margin: auto; +} .mujs-cfg .mujs-sty-flex mujs-btn[data-command=reset] { background: var(--mujs-sf-btn-color, hsl(12, 86%, 50%)); border-color: var(--mujs-sf-btn-color, hsl(12, 86%, 50%)); @@ -1432,6 +1700,18 @@ mujs-body table .frame [data-el=matches] .mujs-grants > mujs-a:last-child::after .mujs-cfg:not(.webext-page) { margin: 1rem 25rem; } +@media screen and (max-height: 720px) { + .mujs-cfg:not(.webext-page) { + height: 100%; + height: -moz-available; + height: -webkit-fill-available; + width: 100%; + width: -moz-available; + width: -webkit-fill-available; + overflow-x: auto; + padding: 0.5em; + } +} mujs-a { display: inline-block; @@ -1460,12 +1740,9 @@ mujs-btn { padding: 6px 15px; } mujs-btn svg { - fill: var(--mujs-txt-color, hsl(0, 0%, 100%)); -} -mujs-btn svg, -mujs-btn img { width: 14px; height: 14px; + fill: var(--mujs-txt-color, hsl(0, 0%, 100%)); } mu-jsbtn { @@ -1483,170 +1760,340 @@ mu-jsbtn { mujs-a, mu-jsbtn, .mujs-pointer, -.mujs-cfg mujs-section *:not(input[type=password], input[type=text], input[type=number]), +.mujs-cfg mujs-section *:not(input[type=text], input[type=number], [data-theme], [data-blacklist]), .mainbtn, .mainframe, mujs-btn { cursor: pointer !important; -} -`; +}`; /******************************************************************************/ // #region Console -const dbg = (...msg) => { - const dt = new Date(); - console.debug( - '[%cMagic Userscript+%c] %cDBG', - 'color: rgb(29, 155, 240);', - '', - 'color: rgb(255, 212, 0);', - `[${dt.getHours()}:${('0' + dt.getMinutes()).slice(-2)}:${('0' + dt.getSeconds()).slice(-2)}]`, - ...msg - ); -}; -const err = (...msg) => { - console.error( - '[%cMagic Userscript+%c] %cERROR', - 'color: rgb(29, 155, 240);', - '', - 'color: rgb(249, 24, 128);', - ...msg - ); - for (const ex of msg) { - if (typeof ex === 'object' && 'cause' in ex && typeof alert !== 'undefined') { - alert(`[Magic Userscript+] (${ex.cause}) ${ex.message}`); +const con = { + title: '[%cMagic Userscript+%c]', + color: 'color: rgb(29, 155, 240);', + dbg(...msg) { + const dt = new Date(); + console.debug( + `${con.title} %cDBG`, + con.color, + '', + 'color: rgb(255, 212, 0);', + `[${dt.getHours()}:${('0' + dt.getMinutes()).slice(-2)}:${('0' + dt.getSeconds()).slice(-2)}]`, + ...msg + ); + }, + err(...msg) { + console.error(`${con.title} %cERROR`, con.color, '', 'color: rgb(249, 24, 128);', ...msg); + const a = typeof alert !== 'undefined' && alert; + const t = con.title.replace(/%c/g, ''); + for (const ex of msg) { + if (typeof ex === 'object' && 'cause' in ex && a) { + a(`${t} (${ex.cause}) ${ex.message}`); + } } + }, + info(...msg) { + console.info(`${con.title} %cINF`, con.color, '', 'color: rgb(0, 186, 124);', ...msg); + }, + log(...msg) { + console.log(`${con.title} %cLOG`, con.color, '', 'color: rgb(219, 160, 73);', ...msg); } }; -const info = (...msg) => { - console.info( - '[%cMagic Userscript+%c] %cINF', - 'color: rgb(29, 155, 240);', - '', - 'color: rgb(0, 186, 124);', - ...msg - ); -}; -const log = (...msg) => { - console.log( - '[%cMagic Userscript+%c] %cLOG', - 'color: rgb(29, 155, 240);', - '', - 'color: rgb(219, 160, 73);', - ...msg - ); -}; +const { err, info } = con; // #endregion - /** * @type { import("../typings/types.d.ts").config } */ -let cfg = {}; -// let ghMsg = null; - -/** @type { import("../typings/UserJS.d.ts").safeSelf } */ -function safeSelf() { - if (userjs.safeSelf) { - return userjs.safeSelf; - } - const g = globalThis; - const safe = { - XMLHttpRequest: g.XMLHttpRequest, - createElement: g.document.createElement.bind(g.document), - createElementNS: g.document.createElementNS.bind(g.document), - createTextNode: g.document.createTextNode.bind(g.document), - setTimeout: g.setTimeout, - clearTimeout: g.clearTimeout, - trustedTypes: g.trustedTypes - }; - userjs.safeSelf = safe; - return safe; -} - +let cfg; +/** + * @type {URL | undefined} + */ +let url; +try { + // for some reason `window.location.href` isn't always the same as `location.href` + url = new URL(window.location.href ?? BLANK_PAGE); +} catch { + /* empty */ +} +//#region Placeholders +const BLANK_FN = function () {}; +const BLANK_ASYNC_FN = async function () {}; const BLANK_PAGE = 'about:blank'; -// Lets highlight me :) -const authorID = 166061; -// Some UserJS I personally enjoy -const goodUserJS = [ - 423001, - 376510, - 23840, - 40525, - 6456, - 'https://github.com/TagoDR/MangaOnlineViewer/raw/master/Manga_OnlineViewer.user.js', - 'https://github.com/jesus2099/konami-command/raw/master/INSTALL-USER-SCRIPT.user.js', - 'https://github.com/TagoDR/MangaOnlineViewer/raw/master/dist/Manga_OnlineViewer_Adult.user.js' -]; -// Remove UserJS from banned accounts -const badUserJS = [ - 478597, // Authors have been banned from GreasyFork -]; -// Unsupport webpages for each engine -const engineUnsupported = { - greasyfork: ['pornhub.com'], - sleazyfork: ['pornhub.com'], - openuserjs: [], - github: [] -}; -const getUAData = () => { - if (typeof userjs.isMobile !== 'undefined') { - return userjs.isMobile; - } - try { - const { platform, mobile } = navigator.userAgentData ?? {}; - userjs.isMobile = - /Mobile|Tablet/.test(navigator.userAgent ?? '') || - mobile || - /Android|Apple/.test(platform ?? ''); - } catch (ex) { - err({ cause: 'getUAData', message: ex.message }); - userjs.isMobile = false; - } - return userjs.isMobile; -}; -const isMobile = getUAData(); -const isGM = typeof GM !== 'undefined'; -const builtinList = { - local: /localhost|router|gov|(\d+\.){3}\d+/, - finance: - /school|pay|bank|money|cart|checkout|authorize|bill|wallet|venmo|zalo|skrill|bluesnap|coin|crypto|currancy|insurance|finance/, - social: /login|join|signin|signup|sign-up|password|reset|password_reset/, - unsupported: { - host: 'fakku.net', - pathname: '/hentai/.+/read/page/.+' - } -}; +//#endregion /** - * @type { import("../typings/types.d.ts").config } + * @template {string} S + * @param {S} hn */ -const DEFAULT_CONFIG = { - cache: true, - codePreview: false, - autoexpand: false, - filterlang: false, - sleazyredirect: false, - time: 10000, - blacklist: ['userjs-local', 'userjs-finance', 'userjs-social', 'userjs-unsupported'], - engines: [ - { - enabled: true, - name: 'greasyfork', - query: encodeURIComponent('https://greasyfork.org/scripts/by-site/{host}.json?language=all'), - }, - { - enabled: false, - name: 'sleazyfork', - query: encodeURIComponent('https://sleazyfork.org/scripts/by-site/{host}.json?language=all'), - }, - { - enabled: false, - name: 'openuserjs', - query: encodeURIComponent('https://openuserjs.org/?q={host}'), - }, - { - enabled: false, - name: 'github', +const normalizedHostname = (hn) => hn.replace(/^www\./, ''); +/** + * @template {string} S + * @param {S} txt + */ +const formatURL = (txt) => + txt + .split('.') + .splice(-2) + .join('.') + .replace(/\/|https:/g, ''); +/** + * @template {string} S + * @param {S} str + */ +const getHostname = (str) => formatURL(normalizedHostname(str)); +// #region Validators +/** + * @type { import("../typings/types.d.ts").objToStr } + */ +const objToStr = (obj) => Object.prototype.toString.call(obj).match(/\[object (.*)\]/)[1]; +/** + * @type { import("../typings/types.d.ts").isRegExp } + */ +const isRegExp = (obj) => /RegExp/.test(objToStr(obj)); +/** + * @type { import("../typings/types.d.ts").isElem } + */ +const isElem = (obj) => /Element/.test(objToStr(obj)); +/** + * @type { import("../typings/types.d.ts").isHTML } + */ +const isHTML = (obj) => /HTML/.test(objToStr(obj)); +/** + * @type { import("../typings/types.d.ts").isObj } + */ +const isObj = (obj) => /Object/.test(objToStr(obj)); +/** + * @type { import("../typings/types.d.ts").isFN } + */ +const isFN = (obj) => /Function/.test(objToStr(obj)); +const isUserCSS = (str) => /\.user\.css$/.test(str); +const isUserJS = (str) => /\.user\.js$/.test(str); +/** + * @type { import("../typings/types.d.ts").isNull } + */ +const isNull = (obj) => { + return Object.is(obj, null) || Object.is(obj, undefined); +}; +/** + * @type { import("../typings/types.d.ts").isBlank } + */ +const isBlank = (obj) => { + return ( + (typeof obj === 'string' && Object.is(obj.trim(), '')) || + ((obj instanceof Set || obj instanceof Map) && Object.is(obj.size, 0)) || + (Array.isArray(obj) && Object.is(obj.length, 0)) || + (isObj(obj) && Object.is(Object.keys(obj).length, 0)) + ); +}; +/** + * @type { import("../typings/types.d.ts").isEmpty } + */ +const isEmpty = (obj) => { + return isNull(obj) || isBlank(obj); +}; +// #endregion +// #region Globals +/** @type { import("../typings/UserJS.d.ts").safeHandles } */ +let _self; +{ + /** + * https://github.com/zloirock/core-js/blob/master/packages/core-js/internals/global-this.js + * @returns {typeof globalThis} + */ + const globalWin = () => { + const check = (it) => it && it.Math === Math && it; + return ( + check(typeof globalThis == 'object' && globalThis) || + check(typeof window == 'object' && window) || + check(typeof self == 'object' && self) + ); + }; + const g = globalWin(); + try { + /** @type { import("../typings/UserJS.d.ts").safeHandles } */ + const safe = { + XMLHttpRequest: g.XMLHttpRequest, + CustomEvent: g.CustomEvent, + createElement: g.document.createElement.bind(g.document), + createElementNS: g.document.createElementNS.bind(g.document), + createTextNode: g.document.createTextNode.bind(g.document), + setTimeout: g.setTimeout, + clearTimeout: g.clearTimeout, + navigator: g.navigator, + scheduler: { + postTask(callback, options) { + if ('scheduler' in g && 'postTask' in g.scheduler) { + return g.scheduler.postTask(callback, options); + } + options = Object.assign({}, options); + if (options.delay === undefined) options.delay = 0; + options.delay = Number(options.delay); + if (options.delay < 0) { + return Promise.reject(new TypeError('"delay" must be a positive number.')); + } + return new Promise((resolve) => { + g.setTimeout(() => { + resolve(callback()); + }, options.delay); + }); + }, + yield() { + if ('scheduler' in g && 'yield' in g.scheduler) { + return g.scheduler.yield(); + } + return new Promise((resolve) => { + g.setTimeout(resolve, 0); + }); + } + }, + groupBy(items, keySelector) { + if ('groupBy' in Object) { + return Object.groupBy(items, keySelector); + } + /** [Object.groupBy polyfill](https://gist.github.com/gtrabanco/7c97bd41aa74af974fa935bfb5044b6e) */ + return items.reduce((acc = {}, ...args) => { + const key = keySelector(...args); + acc[key] ??= []; + acc[key].push(args[0]); + return acc; + }, {}); + } + }; + for (const [k, v] of Object.entries(safe)) { + if (/scheduler|navigator/.test(k) || isFN(v)) continue; + throw new Error(`Safe "${k}" returned "${v}"`, { cause: '_self' }); + } + _self = safe; + } catch (e) { + err(e); + _self = null; + } +} +// #endregion +// #region Constants +/** Lets highlight me :) */ +const authorID = 166061; +/** + * Some UserJS I personally enjoy - `https://greasyfork.org/scripts/{id}` + */ +const goodUserJS = [ + 33005, + 394820, + 438684, + 4870, + 394420, + 25068, + 483444, + 1682, + 22587, + 789, + 28497, + 386908, + 24204, + 404443, + 4336, + 368183, + 393396, + 473830, + 12179, + 423001, + 376510, + 23840, + 40525, + 6456, + 'https://openuserjs.org/install/Patabugen/Always_Remember_Me.user.js', + 'https://openuserjs.org/install/nokeya/Direct_links_out.user.js', + 'https://github.com/jijirae/y2monkey/raw/main/y2monkey.user.js', + 'https://github.com/jijirae/r2monkey/raw/main/r2monkey.user.js', + 'https://github.com/TagoDR/MangaOnlineViewer/raw/master/Manga_OnlineViewer.user.js', + 'https://github.com/jesus2099/konami-command/raw/master/INSTALL-USER-SCRIPT.user.js', + 'https://github.com/TagoDR/MangaOnlineViewer/raw/master/dist/Manga_OnlineViewer_Adult.user.js' +]; +/** Remove UserJS from banned accounts */ +const badUserJS = [478597]; +/** Unsupport host for search engines */ +const engineUnsupported = { + greasyfork: ['pornhub.com'], + sleazyfork: ['pornhub.com'], + openuserjs: [], + github: [] +}; +const isMobile = (() => { + if (userjs.isMobile !== undefined) { + return userjs.isMobile; + } + try { + const { navigator } = _self; + if (navigator) { + const { userAgent = '', userAgentData = {} } = navigator; + const { platform = '', mobile = false } = Object(userAgentData); + userjs.isMobile = + /Mobile|Tablet/.test(String(userAgent)) || + Boolean(mobile) || + /Android|Apple/.test(String(platform)); + } else { + userjs.isMobile = false; + } + } catch (ex) { + userjs.isMobile = false; + ex.cause = 'getUAData'; + err(ex); + } + return userjs.isMobile; +})(); +const isGM = typeof GM !== 'undefined' || typeof GM_xmlhttpRequest !== 'undefined'; +const builtinList = { + local: /localhost|router|gov|(\d+\.){3}\d+/, + finance: + /school|pay|bank|money|cart|checkout|authorize|bill|wallet|venmo|zalo|skrill|bluesnap|coin|crypto|currancy|insurance|finance/, + social: /login|join|signin|signup|sign-up|password|reset|password_reset/, + unsupported: { + host: 'fakku.net', + pathname: '/hentai/.+/read/page/.+' + } +}; +// #endregion +// #region DEFAULT_CONFIG +/** + * @type { import("../typings/types.d.ts").config } + */ +const DEFAULT_CONFIG = { + autofetch: false, + autoinject: true, + autoSort: 'daily_installs', + clearTabCache: true, + cache: true, + autoexpand: false, + filterlang: false, + sleazyredirect: false, + time: 10000, + blacklist: ['userjs-local', 'userjs-finance', 'userjs-social', 'userjs-unsupported'], + preview: { + code: false, + metadata: false + }, + engines: [ + { + enabled: true, + name: 'greasyfork', + query: encodeURIComponent('https://greasyfork.org/scripts/by-site/{host}.json?language=all') + }, + { + enabled: false, + name: 'sleazyfork', + query: encodeURIComponent('https://sleazyfork.org/scripts/by-site/{host}.json?language=all') + }, + { + enabled: false, + name: 'openuserjs', + query: encodeURIComponent('https://openuserjs.org/?q={host}') + }, + { + enabled: false, + name: 'github', token: '', - query: encodeURIComponent('https://api.github.com/search/code?q="// ==UserScript=="+{host}+ "// ==/UserScript=="+in:file+language:js&per_page=30') + query: encodeURIComponent( + 'https://api.github.com/search/repositories?q=topic:{domain}+topic:userscript' + ) } ], theme: { @@ -1676,149 +2123,98 @@ const DEFAULT_CONFIG = { recommend: { author: true, others: true - } -}; -//#region i18n -class i18nHandler { - constructor() { - /** - * @type { string[] } - */ - this.cache = []; - - const languages = navigator.languages ?? []; - for (const nlang of languages) { - const lg = nlang.split('-')[0]; - if (this.cache.indexOf(lg) === -1) { - this.cache.push(lg); - } - } - - const current = navigator.language.split('-')[0] ?? 'en'; - if (!this.cache.includes(current)) { - this.cache.push(current); + }, + filters: { + ASCII: { + enabled: false, + name: 'Non-ASCII', + regExp: '[^\\x00-\\x7F\\s]+' + }, + Latin: { + enabled: false, + name: 'Non-Latin', + regExp: '[^\\u0000-\\u024F\\u2000-\\u214F\\s]+' + }, + Games: { + enabled: false, + name: 'Games', + flag: 'iu', + regExp: + 'Aimbot|AntiGame|Agar|agar\\.io|alis\\.io|angel\\.io|ExtencionRipXChetoMalo|AposBot|DFxLite|ZTx-Lite|AposFeedingBot|AposLoader|Balz|Blah Blah|Orc Clan Script|Astro\\s*Empires|^\\s*Attack|^\\s*Battle|BiteFight|Blood\\s*Wars|Bloble|Bonk|Bots|Bots4|Brawler|\\bBvS\\b|Business\\s*Tycoon|Castle\\s*Age|City\\s*Ville|chopcoin\\.io|Comunio|Conquer\\s*Club|CosmoPulse|cursors\\.io|Dark\\s*Orbit|Dead\\s*Frontier|Diep\\.io|\\bDOA\\b|doblons\\.io|DotD|Dossergame|Dragons\\s*of\\s*Atlantis|driftin\\.io|Dugout|\\bDS[a-z]+\\n|elites\\.io|Empire\\s*Board|eRep(ublik)?|Epicmafia|Epic.*War|ExoPlanet|Falcon Tools|Feuerwache|Farming|FarmVille|Fightinfo|Frontier\\s*Ville|Ghost\\s*Trapper|Gladiatus|Goalline|Gondal|gota\\.io|Grepolis|Hobopolis|\\bhwm(\\b|_)|Ikariam|\\bIT2\\b|Jellyneo|Kapi\\s*Hospital|Kings\\s*Age|Kingdoms?\\s*of|knastv(o|oe)gel|Knight\\s*Fight|\\b(Power)?KoC(Atta?ck)?\\b|\\bKOL\\b|Kongregate|Krunker|Last\\s*Emperor|Legends?\\s*of|Light\\s*Rising|lite\\.ext\\.io|Lockerz|\\bLoU\\b|Mafia\\s*(Wars|Mofo)|Menelgame|Mob\\s*Wars|Mouse\\s*Hunt|Molehill\\s*Empire|MooMoo|MyFreeFarm|narwhale\\.io|Neopets|NeoQuest|Nemexia|\\bOGame\\b|Ogar(io)?|Pardus|Pennergame|Pigskin\\s*Empire|PlayerScripts|pokeradar\\.io|Popmundo|Po?we?r\\s*(Bot|Tools)|PsicoTSI|Ravenwood|Schulterglatze|Skribbl|slither\\.io|slitherplus\\.io|slitheriogameplay|SpaceWars|splix\\.io|Survivio|\\bSW_[a-z]+\\n|\\bSnP\\b|The\\s*Crims|The\\s*West|torto\\.io|Travian|Treasure\\s*Isl(and|e)|Tribal\\s*Wars|TW.?PRO|Vampire\\s*Wars|vertix\\.io|War\\s*of\\s*Ninja|World\\s*of\\s*Tanks|West\\s*Wars|wings\\.io|\\bWoD\\b|World\\s*of\\s*Dungeons|wtf\\s*battles|Wurzelimperium|Yohoho|Zombs' + }, + SocialNetworks: { + enabled: false, + name: 'Social Networks', + flag: 'iu', + regExp: + 'Face\\s*book|Google(\\+| Plus)|\\bHabbo|Kaskus|\\bLepra|Leprosorium|MySpace|meinVZ|odnoklassniki|Одноклассники|Orkut|sch(ue|ü)ler(VZ|\\.cc)?|studiVZ|Unfriend|Valenth|VK|vkontakte|ВКонтакте|Qzone|Twitter|TweetDeck' + }, + Clutter: { + enabled: false, + name: 'Clutter', + flag: 'iu', + regExp: + "^\\s*(.{1,3})\\1+\\n|^\\s*(.+?)\\n+\\2\\n*$|^\\s*.{1,5}\\n|do\\s*n('|o)?t (install|download)|nicht installieren|(just )?(\\ban? |\\b)test(ing|s|\\d|\\b)|^\\s*.{0,4}test.{0,4}\\n|\\ntest(ing)?\\s*|^\\s*(\\{@|Smolka|Hacks)|\\[\\d{4,5}\\]|free\\s*download|theme|(night|dark) ?(mode)?" } } - toDate(str = '') { - return new Intl.DateTimeFormat(navigator.language).format(new Date(str)); +}; +// #endregion +// #region i18n +const Language = class { + static i18nMap = new Map(Object.entries(translations)); + /** + * @param {string | Date | number} str + */ + static toDate(str = '') { + const { + navigator: { language } + } = _self; + return new Intl.DateTimeFormat(language).format(typeof str === 'string' ? new Date(str) : str); } - toNumber(number) { - return new Intl.NumberFormat(navigator.language).format(number); + /** + * @param {number | bigint} number + */ + static toNumber(number) { + const { + navigator: { language } + } = _self; + return new Intl.NumberFormat(language).format(number); } - i18n$(...keys) { - const current = navigator.language.split('-')[0] ?? 'en'; - const list = translations[cfg.language] ?? translations[current]; - const arr = []; - for (const key of keys) { - arr.push(list[key]); + /** + * @type { import("../typings/UserJS.d.ts").i18n$ } + */ + static i18n$(key) { + try { + return Language.i18nMap.get(Language.current)?.[key] ?? 'Invalid Key'; + } catch (e) { + err(e); + return 'error'; } - return arr.length !== 1 ? arr : arr[0]; } -} -const language = new i18nHandler(); -const { i18n$ } = language; -//#endregion -// #region Utilities -/** - * @type { import("../typings/types.d.ts").qs } - */ -const qs = (selector, root) => { - try { - return (root || document).querySelector(selector); - } catch (ex) { - err(ex); + static get current() { + const { + navigator: { language } + } = _self; + const [current = 'en'] = language.split('-'); + return current; } - return null; }; +const { i18n$ } = Language; +// #endregion +// #region Utilities +const union = (...arr) => [...new Set(arr.flat())]; /** - * @type { import("../typings/types.d.ts").qsA } + * @param {string} str */ -const qsA = (selectors, root) => { +const decode = (str) => { try { - return (root || document).querySelectorAll(selectors); + if (decodeURI(str) !== decodeURIComponent(str)) { + return decode(decodeURIComponent(str)); + } } catch (ex) { err(ex); } - return []; -}; -/** - * @type { import("../typings/types.d.ts").hasOwn } - */ -const hasOwn = (o, v) => { - if (typeof Object.hasOwn !== 'undefined') { - return Object.hasOwn(o, v); - } - return Object.prototype.hasOwnProperty.call(o, v); -}; -/** - * @type { import("../typings/types.d.ts").objToStr } - */ -const objToStr = (obj) => { - return Object.prototype.toString.call(obj); -}; -/** - * @type { import("../typings/types.d.ts").strToURL } - */ -const strToURL = (str) => { - const WIN_LOCATION = window.location ?? BLANK_PAGE; - try { - str = str ?? WIN_LOCATION; - return objToStr(str).includes('URL') ? str : new URL(str); - } catch (ex) { - err({ cause: 'strToURL', message: ex.message }); - } - return WIN_LOCATION; -}; -/** - * @type { import("../typings/types.d.ts").isRegExp } - */ -const isRegExp = (obj) => { - const s = objToStr(obj); - return s.includes('RegExp'); -}; -/** - * @type { import("../typings/types.d.ts").isElem } - */ -const isElem = (obj) => { - const s = objToStr(obj); - return s.includes('Element'); -}; -/** - * @type { import("../typings/types.d.ts").isObj } - */ -const isObj = (obj) => { - const s = objToStr(obj); - return s.includes('Object'); -}; -/** - * @type { import("../typings/types.d.ts").isFN } - */ -const isFN = (obj) => { - const s = objToStr(obj); - return s.includes('Function'); -}; -/** - * @type { import("../typings/types.d.ts").isNull } - */ -const isNull = (obj) => { - return Object.is(obj, null) || Object.is(obj, undefined); -}; -/** - * @type { import("../typings/types.d.ts").isBlank } - */ -const isBlank = (obj) => { - return ( - (typeof obj === 'string' && Object.is(obj.trim(), '')) || - ((obj instanceof Set || obj instanceof Map) && Object.is(obj.size, 0)) || - (Array.isArray(obj) && Object.is(obj.length, 0)) || - (isObj(obj) && Object.is(Object.keys(obj).length, 0)) - ); -}; -/** - * @type { import("../typings/types.d.ts").isEmpty } - */ -const isEmpty = (obj) => { - return isNull(obj) || isBlank(obj); + return str; }; /** * @type { import("../typings/types.d.ts").normalizeTarget } @@ -1831,52 +2227,67 @@ const normalizeTarget = (target, toQuery = true, root) => { return target; } if (typeof target === 'string') { - return toQuery ? Array.from((root || document).querySelectorAll(target)) : [target]; + return toQuery ? Array.from((root || document).querySelectorAll(target)) : Array.of(target); } - if (isElem(target)) { - return [target]; + if (/object HTML/.test(Object.prototype.toString.call(target))) { + return Array.of(target); } return Array.from(target); }; /** - * @type { import("../typings/types.d.ts").ael } + * @type { import("../typings/types.d.ts").qs } */ -const ael = (el, type, listener, options = {}) => { +const qs = (selector, root) => { try { - for (const elem of normalizeTarget(el)) { - if (!elem) { - continue; - } - if (isMobile && type === 'click') { - elem.addEventListener('touchstart', listener, options); - continue; - } - elem.addEventListener(type, listener, options); - } + return (root || document).querySelector(selector); + } catch (ex) { + err(ex); + } + return null; +}; +/** + * @type { import("../typings/types.d.ts").qsA } + */ +const qsA = (selectors, root) => { + try { + return (root || document).querySelectorAll(selectors); } catch (ex) { err(ex); } + return []; +}; +/** + * @type { import("../typings/types.d.ts").ael } + */ +const ael = (el, type, listener, options = {}) => { + for (const elem of normalizeTarget(el).filter(isHTML)) { + if (isMobile && type === 'click') { + elem.addEventListener('touchstart', listener, options); + continue; + } + elem.addEventListener(type, listener, options); + } }; /** * @type { import("../typings/types.d.ts").formAttrs } */ const formAttrs = (elem, attr = {}) => { - if (!elem) { - return elem; - } - for (const key in attr) { - if (typeof attr[key] === 'object') { - formAttrs(elem[key], attr[key]); - } else if (isFN(attr[key])) { + if (!elem) return elem; + for (const [key, value] of Object.entries(attr)) { + if (/^_mujs/i.test(key)) { + elem[key] = value; + } else if (typeof value === 'object') { + formAttrs(elem[key], value); + } else if (isFN(value)) { if (/^on/.test(key)) { - elem[key] = attr[key]; + elem[key] = value; continue; } - ael(elem, key, attr[key]); - } else if (key === 'class') { - elem.className = attr[key]; + ael(elem, key, value); + } else if (/^class/i.test(key)) { + dom.cl.add(elem, value); } else { - elem[key] = attr[key]; + elem[key] = value; } } return elem; @@ -1887,32 +2298,19 @@ const formAttrs = (elem, attr = {}) => { const make = (tagName, cname, attrs) => { let el; try { - const safe = safeSelf(); - el = safe.createElement(tagName); - if (!isEmpty(cname)) { - if (typeof cname === 'string') { - el.className = cname; - } else if (isObj(cname)) { - formAttrs(el, cname); - } - } - if (!isEmpty(attrs)) { - if (typeof attrs === 'string') { - el.textContent = attrs; - } else if (isObj(attrs)) { - formAttrs(el, attrs); - } - } + el = _self.createElement(tagName.toLowerCase()); + if ((typeof cname === 'string' || Array.isArray(cname)) && !isEmpty(cname)) + dom.cl.add(el, cname); + if (typeof attrs === 'string' && !isEmpty(attrs)) el.textContent = attrs; + formAttrs(el, (isObj(cname) && cname) || (isObj(attrs) && attrs) || {}); } catch (ex) { + if (ex instanceof DOMException) throw new Error(`${ex.name}: ${ex.message}`, { cause: 'make' }); + ex.cause = 'make'; err(ex); } return el; }; - -/** - * @type { import("../typings/UserJS.d.ts").getGMInfo } - */ -const getGMInfo = () => { +const $info = (() => { if (isGM) { if (isObj(GM.info)) { return GM.info; @@ -1925,20 +2323,19 @@ const getGMInfo = () => { icon: '', name: 'Magic Userscript+', namespace: 'https://github.com/magicoflolis/Userscript-Plus', - updateURL: 'https://github.com/magicoflolis/Userscript-Plus/releases', + updateURL: 'https://github.com/magicoflolis/Userscript-Plus/raw/master/dist/magic-userjs.js', version: 'Bookmarklet', bugs: 'https://github.com/magicoflolis/Userscript-Plus/issues' } }; -}; -const $info = getGMInfo(); +})(); // #endregion /** * @type { import("../typings/types.d.ts").dom } */ const dom = { attr(target, attr, value = undefined) { - for (const elem of normalizeTarget(target)) { + for (const elem of normalizeTarget(target).filter(isHTML)) { if (value === undefined) { return elem.getAttribute(attr); } @@ -1950,7 +2347,7 @@ const dom = { } }, prop(target, prop, value = undefined) { - for (const elem of normalizeTarget(target)) { + for (const elem of normalizeTarget(target).filter(isHTML)) { if (value === undefined) { return elem[prop]; } @@ -1958,7 +2355,7 @@ const dom = { } }, text(target, text) { - const targets = normalizeTarget(target); + const targets = normalizeTarget(target).filter(isHTML); if (text === undefined) { return targets.length !== 0 ? targets[0].textContent : undefined; } @@ -1966,135 +2363,95 @@ const dom = { elem.textContent = text; } }, + remove(target) { + normalizeTarget(target) + .filter(isHTML) + .some((elem) => elem.remove()); + }, cl: { add(target, token) { - if (Array.isArray(token)) { - for (const elem of normalizeTarget(target)) { - elem.classList.add(...token); - } - } else { - for (const elem of normalizeTarget(target)) { - elem.classList.add(token); - } - } + token = normalizeTarget((typeof token === 'string' && token.split(' ')) || token, false); + return normalizeTarget(target) + .filter(isHTML) + .some((elem) => elem.classList.add(...token)); }, remove(target, token) { - if (Array.isArray(token)) { - for (const elem of normalizeTarget(target)) { - elem.classList.remove(...token); - } - } else { - for (const elem of normalizeTarget(target)) { - elem.classList.remove(token); - } - } + token = normalizeTarget(token, false); + return normalizeTarget(target) + .filter(isHTML) + .some((elem) => elem.classList.remove(...token)); }, toggle(target, token, force) { let r; - for (const elem of normalizeTarget(target)) { + for (const elem of normalizeTarget(target).filter(isHTML)) { r = elem.classList.toggle(token, force); } return r; }, has(target, token) { - for (const elem of normalizeTarget(target)) { - if (elem.classList.contains(token)) { - return true; - } - } - return false; + return normalizeTarget(target) + .filter(isHTML) + .some((elem) => elem.classList.contains(token)); } } }; -class Memorize { - constructor() { - /** - * @type {Map>} - */ - this.store = new Map(); - /** - * @type { { [key: string]: Map; userjs: Map } } - */ - this.maps = {}; - this.create('cfg', 'container', 'userjs'); - } - /** - * @template { string } S - * @param { ...S } maps - * @returns { S | S[] } - */ - create(...maps) { - const resp = []; - for (const key of maps) { - if (this.store.has(key)) { - return this.store.get(key); - } - const m = new Map(); - this.store.set(key, m); - this.maps[key] = m; - resp.push(this.store.get(key)); - } - return resp.length >= 2 ? resp : resp[0]; - } -} -const memory = new Memorize(); //#region Icon SVGs const iconSVG = { - cfg: { - viewBox: '0 0 24 24', - html: '' - }, close: { - viewBox: '0 0 24 24', - html: '' + viewBox: '0 0 384 512', + html: '' + }, + code: { + viewBox: '0 0 640 512', + html: '' }, - filter: { - viewBox: '0 0 24 24', - html: '' + collapse: { + viewBox: '0 0 448 512', + html: '' }, - fsClose: { - viewBox: '0 0 24 24', - html: ' ' + download: { + viewBox: '0 0 384 512', + html: '' }, - fsOpen: { - viewBox: '0 0 24 24', - html: ' ' + expand: { + viewBox: '0 0 448 512', + html: '' }, - fullscreen: { - viewBox: '0 0 96 96', - html: '' + gear: { + viewBox: '0 0 512 512', + html: '' }, - gf: { - viewBox: '0 0 510.4 510.4', - html: '' + github: { + viewBox: '0 0 496 512', + html: '' }, - gh: { - viewBox: '0 0 16 16', - html: '' + globe: { + viewBox: '0 0 512 512', + html: '' }, - hide: { - viewBox: '0 0 24 24', - html: ' ' + info: { + viewBox: '0 0 512 512', + html: '' }, install: { - viewBox: '0 0 16 16', - html: '' + viewBox: '0 0 512 512', + html: '' }, issue: { - viewBox: '0 0 24 24', - html: '' + viewBox: '0 0 512 512', + html: '' }, - nav: { - viewBox: '0 0 24 24', - html: ' ' + minus: { + viewBox: '0 0 448 512', + html: '' }, - plus: { - viewBox: '0 0 24 24', - html: '' + nav: { + viewBox: '0 0 448 512', + html: '' }, - search: { - viewBox: '0 0 24 24', - html: '' + pager: { + viewBox: '0 0 512 512', + html: '' }, verified: { viewBox: '0 0 56 56', @@ -2103,30 +2460,29 @@ const iconSVG = { html: '' }, refresh: { - viewBox: '0 0 1024 1024', + viewBox: '0 0 512 512', fill: 'currentColor', - html: '' + html: '' }, load(type, container) { - const safe = safeSelf(); - const svgElem = safe.createElementNS('http://www.w3.org/2000/svg', 'svg'); + const svgElem = _self.createElementNS('http://www.w3.org/2000/svg', 'svg'); for (const [k, v] of Object.entries(iconSVG[type])) { - if (k === 'html') { - continue; - } + if (k === 'html') continue; svgElem.setAttributeNS(null, k, v); } try { if (typeof iconSVG[type].html === 'string') { svgElem.innerHTML = iconSVG[type].html; + svgElem.setAttribute('id', `mujs_${type ?? 'Unknown'}`); } - // eslint-disable-next-line no-unused-vars - } catch (ex) { + } catch { /* empty */ } - if (container) { + if (isElem(container)) { container.appendChild(svgElem); return svgElem; + } else if (isObj(container)) { + formAttrs(svgElem, container); } return svgElem.outerHTML; } @@ -2137,11 +2493,12 @@ const iconSVG = { */ const StorageSystem = { prefix: 'MUJS', + events: new Set(), getItem(key) { return window.localStorage.getItem(key); }, has(key) { - return !isNull(this.getItem(key)); + return !this.getItem(key); }, setItem(key, value) { window.localStorage.setItem(key, value); @@ -2150,7 +2507,10 @@ const StorageSystem = { window.localStorage.removeItem(key); }, async setValue(key, v) { - v = typeof v === 'string' ? v : JSON.stringify(v ?? {}); + if (!v) { + return; + } + v = typeof v === 'string' ? v : JSON.stringify(v); if (isGM) { if (isFN(GM.setValue)) { await GM.setValue(key, v); @@ -2178,7 +2538,9 @@ const StorageSystem = { ? JSON.parse(this.getItem(`${this.prefix}-${key}`)) : def; } catch (ex) { + ex.cause = 'getValue'; err(ex); + return def; } } }; @@ -2207,19 +2569,17 @@ const Command = { * @type { import("../typings/UserJS.d.ts").Network } */ const Network = { - async req(url, method = 'GET', responseType = 'json', data = {}, useFetch = false) { + async req(url, method = 'GET', responseType = 'json', data, useFetch = false) { if (isEmpty(url)) { throw new Error('"url" parameter is empty'); } + data = Object.assign({}, data); method = this.bscStr(method, false); - responseType = this.bscStr(responseType); + responseType = this.bscStr(responseType, true); const params = { method, ...data }; - if (params.hermes) { - delete params.hermes; - } if (isGM && !useFetch) { if (params.credentials) { Object.assign(params, { @@ -2235,13 +2595,15 @@ const Network = { } else if (params.onprogress) { delete params.onprogress; } - return await new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { if (isGM && !useFetch) { Network.xmlRequest({ url, responseType, ...params, - onerror: reject, + onerror: (r_1) => { + reject(new Error(`${r_1.status} ${url}`)); + }, onload: (r_1) => { if (r_1.status !== 200) reject(new Error(`${r_1.status} ${url}`)); if (responseType.match(/basic/)) resolve(r_1); @@ -2268,8 +2630,8 @@ const Network = { } else if (responseType.match(/clone/)) { resolve(check('clone')); } else if (responseType.match(/document/)) { - const domParser = new DOMParser(); const respTxt = check('text'); + const domParser = new DOMParser(); if (respTxt instanceof Promise) { respTxt.then((txt) => { const doc = domParser.parseFromString(txt, 'text/html'); @@ -2304,8 +2666,7 @@ const Network = { } } return await new Promise((resolve, reject) => { - const safe = safeSelf(); - const req = new safe.XMLHttpRequest(); + const req = new _self.XMLHttpRequest(); let method = 'GET'; let url = BLANK_PAGE; let body; @@ -2365,140 +2726,127 @@ const Network = { return txt.replaceAll(/\W/g, ''); } }; -const sleazyRedirect = () => { - const locObj = window.top.location; - const { hostname } = locObj; - const gfSite = /greasyfork\.org/.test(hostname); - if (!gfSite && cfg.sleazyredirect) { - return; - } - const otherSite = gfSite ? 'sleazyfork' : 'greasyfork'; - if (!qs('span.sign-in-link')) { - return; - } - if (!/scripts\/\d+/.test(locObj.href)) { - return; - } - if ( - !qs('#script-info') && - (otherSite == 'greasyfork' || qs('div.width-constraint>section>p>a')) - ) { - const str = locObj.href.replace( - /\/\/([^.]+\.)?(greasyfork|sleazyfork)\.org/, - '//$1' + otherSite + '.org' - ); - info(`Redirecting to "${str}"`); - if (isFN(locObj.assign)) { - locObj.assign(str); - } else { - locObj.href = str; +const Counter = { + cnt: { + total: { + count: 0 + } + }, + set(engine) { + if (!this.cnt[engine.name]) { + const counter = make('count-frame', engine.enabled ? '' : 'hidden', { + dataset: { + counter: engine.name + }, + title: decode(engine.query ?? engine.url), + textContent: '0' + }); + this.cnt[engine.name] = { + root: counter, + count: 0 + }; + return counter; + } + return this.cnt[engine.name].root; + }, + update(count, engine) { + this.cnt[engine.name].count += count; + this.cnt.total.count += count; + this.updateAll(); + }, + updateAll() { + for (const v of Object.values(this.cnt)) dom.text(v.root, v.count); + }, + reset() { + for (const [k, v] of Object.entries(this.cnt)) { + dom.text(v.root, 0); + v.count = 0; + const engine = cfg.engines.find((engine) => k === engine.name); + if (engine) { + dom.cl[engine.enabled ? 'remove' : 'add'](v.root, 'hidden'); + } } } }; // #region Container /** - * @type { import("../typings/UserJS.d.ts").Container } + * @type { typeof import("../typings/UserJS.d.ts").Container } */ -class Container { +const Container = class { + static prompts = []; webpage; host; - domain; - ready; injected; shadowRoot; - supported; + shadowSupport; frame; - cache; userjsCache; root; unsaved; isBlacklisted; rebuild; - counters; opacityMin; opacityMax; - constructor(url) { + constructor() { this.remove = this.remove.bind(this); this.refresh = this.refresh.bind(this); - this.updateCounter = this.updateCounter.bind(this); - this.updateCounters = this.updateCounters.bind(this); this.showError = this.showError.bind(this); - this.webpage = strToURL(url); - this.host = this.getHost(this.webpage.host); - this.domain = this.getDomain(this.webpage.host); - this.ready = false; + this.toElem = this.toElem.bind(this); + + this.webpage = url; + this.host = getHostname(url?.hostname ?? BLANK_PAGE); this.injected = false; this.shadowRoot = undefined; - this.supported = isFN(make('main-userjs').attachShadow); - this.frame = this.supported - ? make('main-userjs', '', { - dataset: { - insertedBy: $info.script.name, - role: 'primary-container' - } - }) - : make('iframe', 'mujs-iframe', { - dataset: { - insertedBy: $info.script.name, - role: 'primary-iframe' - }, - loading: 'lazy', - src: BLANK_PAGE, - style: - 'position: fixed;bottom: 1rem;right: 1rem;height: 525px;width: 90%;margin: 0px 1rem;z-index: 100000000000000020 !important;', - onload: (iFrame) => { - /** - * @type { HTMLIFrameElement } - */ - const target = iFrame.target; - if (!target.contentDocument) { - return; - } - this.shadowRoot = target.contentDocument.documentElement; - this.ready = true; - dom.cl.add([this.shadowRoot, target.contentDocument.body], 'mujs-iframe'); - } - }); - if (this.supported) { - this.shadowRoot = this.frame.attachShadow({ - mode: 'open', - clonable: false, - delegatesfocus: false - }); - this.ready = true; - } - this.cache = memory.maps.container; - this.userjsCache = memory.maps.userjs; + this.shadowSupport = isFN(make('main-userjs').attachShadow); + + if (!this.shadowSupport) + throw new Error('Failed to initialize: "attachShadow not supported"', { cause: 'Container' }); + this.frame = make('main-userjs', { + style: 'visibility: visible;', + dataset: { + insertedBy: $info.script.name, + role: 'primary-container' + } + }); + this.shadowRoot = this.frame.attachShadow({ mode: 'closed' }); + + this.userjsCache = new Map(); this.root = make('mujs-root'); this.unsaved = false; this.isBlacklisted = false; this.rebuild = false; - this.counters = { - total: 0 - }; this.opacityMin = '0.15'; this.opacityMax = '1'; this.elementsReady = this.init(); const Timeout = class { constructor() { + /** + * @type {number[]} + */ this.ids = []; } - + /** + * @template R + * @param {number} delay + * @param {R} [reason] + */ set(delay, reason) { return new Promise((resolve, reject) => { - const id = setTimeout(() => { + const id = _self.setTimeout(() => { Object.is(reason, null) || Object.is(reason, undefined) ? resolve() : reject(reason); this.clear(id); }, delay); this.ids.push(id); }); } - + /** + * @param {...number} ids + */ clear(...ids) { this.ids = this.ids.filter((id) => { if (ids.includes(id)) { - clearTimeout(id); + _self.clearTimeout(id); return false; } return true; @@ -2510,84 +2858,58 @@ class Container { mouse: new Timeout() }; + this.injFN = BLANK_FN; + window.addEventListener('beforeunload', this.remove); } - /** - * @param { Function } callback - * @param { Document } doc - */ - async inject(callback, doc) { + inject(callback, doc) { if (this.checkBlacklist(this.host)) { err(`Blacklisted "${this.host}"`); this.remove(); return; } - if (!this.shadowRoot) { - return; - } - if (doc === null) { - return; - } - while (this.ready === false) { - await new Promise((resolve) => requestAnimationFrame(resolve)); - } + if (!this.shadowRoot || isNull(doc)) return; try { doc.documentElement.appendChild(this.frame); if (this.injected) { + if (isFN(this.injFN.build)) { + this.injFN.build(); + } return; } this.shadowRoot.append(this.root); - if (isNull(this.loadCSS(main_css, 'primary-stylesheet'))) { + if (isNull(this.loadCSS(main_css, 'primary-stylesheet'))) throw new Error('Failed to initialize script!', { cause: 'loadCSS' }); - } this.injected = true; this.initFn(); - if (this.elementsReady && isFN(callback)) { - callback.call(this, this.shadowRoot); - } + if (isFN(callback) && this.elementsReady) this.injFN = callback.call(this, this.shadowRoot); } catch (ex) { err(ex); this.remove(); } } initFn() { - this.renderTheme(cfg.theme); + this.setTheme(); - for (const engine of cfg.engines) { - if (engine.enabled) { - const counter = make('count-frame', '', { - dataset: { - counter: engine.name - }, - title: engine.query ? decodeURIComponent(engine.query) : engine.url, - textContent: '0' - }); - this.countframe.append(counter); - } - this.counters[engine.name] = 0; - } - - const cfgpage = this.cfgpage; - const table = this.table; - const exBtn = this.exBtn; - const supported = this.supported; - const frame = this.frame; - const refresh = this.refresh; - const cache = this.cache; - const cHost = this.host; - const urlBar = this.urlBar; + Counter.cnt.total.root = this.mainbtn; + if (this.countframe) + for (const engine of cfg.engines) this.countframe.append(Counter.set(engine)); + const { refresh, urlBar, host, userjsCache, cfgpage, table } = this; class Tabs { /** * @param { HTMLElement } root */ constructor(root) { - this.Tab = new Map(); + /** + * @type { Set } + */ + this.pool = new Set(); this.blank = BLANK_PAGE; this.protocal = 'mujs:'; - this.protoReg = new RegExp(`${this.protocal}(.+)`); + this.protoReg = new RegExp(`${this.protocal}(.+)`, 'i'); this.el = { - add: make('mujs-addtab', '', { + add: make('mujs-addtab', { textContent: '+', dataset: { command: 'new-tab' @@ -2598,115 +2920,93 @@ class Container { }; this.el.head.append(this.el.add); this.el.root.append(this.el.head); - this.custom = () => {}; + this.custom = BLANK_FN; } - hasTab(...params) { - for (const p of params) { - if (!this.Tab.has(p)) { - return false; - } - const content = normalizeTarget(this.Tab.get(p)).filter((t) => p === t.dataset.host); - if (isBlank(content)) { - return false; - } - } - return true; - } - storeTab(host) { - const h = host ?? this.blank; - if (!this.Tab.has(h)) { - this.Tab.set(h, new Set()); - } - return this.Tab.get(h); + /** + * @param {string} hostname + */ + getTab(hostname) { + return [...this.pool].find(({ dataset }) => hostname === dataset.host); } - cache(host, ...tabs) { - const h = host ?? this.blank; - const tabCache = this.storeTab(h); - for (const t of normalizeTarget(tabs)) { - if (tabCache.has(t)) { - continue; - } - tabCache.add(t); - } - this.Tab.set(h, tabCache); - return tabCache; + getActive() { + return [...this.pool].find((tab) => tab.classList.contains('active')); } - intFN(host) { - if (!host.startsWith(this.protocal)) { - return; - } - const type = host.match(this.protoReg)[1]; - if (type === 'settings') { - dom.cl.remove([cfgpage, exBtn], 'hidden'); + /** + * @param {string} hostname + */ + intFN(hostname) { + const [, host] = this.protoReg.exec(hostname) ?? []; + if (host === 'settings') { + dom.cl.remove(cfgpage, 'hidden'); dom.cl.add(table, 'hidden'); - if (!supported) { - dom.attr(frame, 'style', 'height: 100%;'); - } + urlBar.placeholder = 'Search settings'; } } + /** + * @param {HTMLElement} tab + * @param {boolean} [build] + */ active(tab, build = true) { - for (const t of normalizeTarget(tab, false)) { - dom.cl.add([cfgpage, exBtn], 'hidden'); + if (!this.pool.has(tab)) this.pool.add(tab); + dom.cl.add([table, cfgpage], 'hidden'); + dom.cl.remove([...this.pool], 'active'); + dom.cl.add(tab, 'active'); + if (!build) { dom.cl.remove(table, 'hidden'); - dom.cl.remove(qsA('mujs-tab', this.el.head), 'active'); - dom.cl.add(t, 'active'); - if (!build) { - continue; - } - const host = t.dataset.host ?? this.blank; - if (host === this.blank) { - refresh(); - } else if (host.startsWith(this.protocal)) { - this.intFN(host); - } else { - this.custom(host); - // MUList.host = host; - // MUList.build(); - } + return; + } + const host = tab.dataset.host ?? this.blank; + if (host === this.blank) { + dom.cl.add(cfgpage, 'hidden'); + dom.cl.remove(table, 'hidden'); + refresh(); + } else if (host.startsWith(this.protocal)) { + this.intFN(host); + } else { + dom.cl.add(cfgpage, 'hidden'); + dom.cl.remove(table, 'hidden'); + this.custom(host); } } /** @param { HTMLElement } tab */ close(tab) { - for (const t of normalizeTarget(tab, false)) { - const host = t.dataset.host; - if (cache.has(host)) { - cache.delete(host); - } - if (dom.cl.has(t, 'active')) { - refresh(); - } - const sibling = t.previousElementSibling ?? t.nextElementSibling; - if (sibling) { - if (sibling.dataset.command !== 'new-tab') { - this.active(sibling); - } + if (this.pool.has(tab)) this.pool.delete(tab); + if (cfg.clearTabCache) { + const { host } = tab.dataset; + const arr = Array.from(userjsCache.values()).filter(({ _mujs }) => { + return !isEmpty(_mujs) && _mujs.info.host === host; + }); + for (const a of arr) { + arr.splice(arr.indexOf(a), 1); } - if (this.Tab.has(host)) { - this.Tab.delete(host); + } + if (tab.classList.contains('active')) refresh(); + const sibling = tab.nextElementSibling ?? tab.previousElementSibling; + if (sibling) { + if (sibling.dataset.command !== 'new-tab') { + this.active(sibling); } - t.remove(); } + tab.remove(); } - create(host = undefined) { - if (typeof host === 'string') { - if (host.startsWith(this.protocal) && this.hasTab(host)) { - this.active(this.Tab.get(host)); - return; - } - const content = normalizeTarget(this.storeTab(host)).filter( - (t) => host === t.dataset.host - ); - if (!isEmpty(content)) { + /** + * @param {string} [hostname] + */ + create(hostname = undefined) { + if (typeof hostname === 'string') { + const createdTab = this.getTab(hostname); + if (this.protoReg.test(hostname) && createdTab) { + this.active(createdTab); return; } } - const tab = make('mujs-tab', '', { + const tab = make('mujs-tab', { dataset: { command: 'switch-tab' }, style: `order: ${this.el.head.childElementCount};` }); - const tabClose = make('mu-js', '', { + const tabClose = make('mu-js', { dataset: { command: 'close-tab' }, @@ -2714,35 +3014,34 @@ class Container { textContent: 'X' }); const tabHost = make('mujs-host'); + const [, host] = this.protoReg.exec(hostname) ?? []; tab.append(tabHost, tabClose); this.el.head.append(tab); this.active(tab, false); - this.cache(host, tab); - if (isNull(host)) { + if (isNull(hostname)) { refresh(); - urlBar.placeholder = i18n$('newTab'); tab.dataset.host = this.blank; tabHost.title = i18n$('newTab'); tabHost.textContent = i18n$('newTab'); - } else if (host.startsWith(this.protocal)) { - const type = host.match(this.protoReg)[1]; - tab.dataset.host = host || cHost; - tabHost.title = type || tab.dataset.host; + } else if (host) { + tab.dataset.host = hostname || host; + tabHost.title = host || tab.dataset.host; tabHost.textContent = tabHost.title; - this.intFN(host); + this.intFN(hostname); } else { - tab.dataset.host = host || cHost; - tabHost.title = host || cHost; + tab.dataset.host = hostname || host; + tabHost.title = hostname || host; tabHost.textContent = tabHost.title; } return tab; } } - this.tab = new Tabs(this.toolbar); - this.tab.create(this.host); + this.Tabs = new Tabs(this.toolbar); + this.Tabs.create(host); const tabbody = this.tabbody; - const getCellValue = (tr, idx) => tr.children[idx].innerText || tr.children[idx].textContent; + const getCellValue = (tr, idx) => + tr.children[idx].dataset.value || tr.children[idx].textContent; const comparer = (idx, asc) => (a, b) => ((v1, v2) => v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) @@ -2787,51 +3086,30 @@ class Container { this.tabhead = make('thead'); this.header = make('mujs-header'); this.tbody = make('mujs-body'); - this.cfgpage = make('mujs-row', 'mujs-cfg hidden'); - this.btnframe = make('mujs-column', 'btn-frame'); - this.exBtn = make('mujs-column', 'mujs-sty-flex hidden'); - this.fsearch = make('mujs-btn', 'hidden'); - this.exportCFG = make('mujs-btn', 'mujs-export', { - textContent: i18n$('export_config'), - dataset: { - command: 'export-cfg' - } - }); - this.importCFG = make('mujs-btn', 'mujs-import', { - textContent: i18n$('import_config'), - dataset: { - command: 'import-cfg' - } - }); - this.exportTheme = make('mujs-btn', 'mujs-export', { - textContent: i18n$('export_theme'), - dataset: { - command: 'export-theme' - } - }); - this.importTheme = make('mujs-btn', 'mujs-import', { - textContent: i18n$('import_theme'), - dataset: { - command: 'import-theme' + this.cfgpage = make('mujs-row', 'mujs-cfg hidden', { + _mujs: { + base: [], + sections: new Set() } }); + this.main = make('mujs-main', 'hidden'); + this.urlContainer = make('mujs-url'); + this.btnframe = make('mujs-column', 'btn-frame'); this.btnHandles = make('mujs-column', 'btn-handles'); this.btnHide = make('mujs-btn', 'hide-list', { title: i18n$('min'), - innerHTML: iconSVG.load('hide'), + innerHTML: iconSVG.load('minus'), dataset: { command: 'hide-list' } }); this.btnfullscreen = make('mujs-btn', 'fullscreen', { title: i18n$('max'), - innerHTML: iconSVG.load('fullscreen'), + innerHTML: iconSVG.load('expand'), dataset: { command: 'fullscreen' } }); - this.main = make('mujs-main', 'hidden'); - this.urlContainer = make('mujs-url'); this.closebtn = make('mujs-btn', 'close', { title: i18n$('close'), innerHTML: iconSVG.load('close'), @@ -2841,18 +3119,18 @@ class Container { }); this.btncfg = make('mujs-btn', 'settings hidden', { title: 'Settings', - innerHTML: iconSVG.load('cfg'), + innerHTML: iconSVG.load('gear'), dataset: { command: 'settings' } }); this.btnhome = make('mujs-btn', 'github hidden', { title: `GitHub (v${ - $info.script.version.includes('.') || $info.script.version.includes('Book') + /\d+\.\d+\.\d+|Book/.test($info.script.version) ? $info.script.version : $info.script.version.slice(0, 5) })`, - innerHTML: iconSVG.load('gh'), + innerHTML: iconSVG.load('github'), dataset: { command: 'open-tab', webpage: $info.script.namespace @@ -2868,7 +3146,7 @@ class Container { }); this.btngreasy = make('mujs-btn', 'greasy hidden', { title: 'Greasy Fork', - innerHTML: iconSVG.load('gf'), + innerHTML: iconSVG.load('globe'), dataset: { command: 'open-tab', webpage: 'https://greasyfork.org/scripts/421603' @@ -2881,9 +3159,9 @@ class Container { command: 'navigation' } }); - const makeTHead = (rows) => { + const makeTHead = (rows = []) => { const tr = make('tr'); - for (const r of normalizeTarget(rows)) { + for (const r of rows) { const tparent = make('th', r.class ?? '', r); tr.append(tparent); } @@ -2909,17 +3187,28 @@ class Container { } ]); // #endregion - - this.btnHandles.append(this.btnHide, this.btnfullscreen, this.closebtn); - this.btnframe.append(this.btnhome, this.btngreasy, this.btnissue, this.btncfg, this.btnnav); + if (isMobile) { + dom.cl.add([this.btnHide, this.btnfullscreen, this.closebtn], 'hidden'); + this.btnframe.append( + this.btnHide, + this.btnfullscreen, + this.closebtn, + this.btnhome, + this.btngreasy, + this.btnissue, + this.btncfg, + this.btnnav + ); + } else { + this.btnHandles.append(this.btnHide, this.btnfullscreen, this.closebtn); + this.btnframe.append(this.btnhome, this.btngreasy, this.btnissue, this.btncfg, this.btnnav); + } this.toolbar.append(this.btnHandles); this.urlContainer.append(this.urlBar); this.header.append(this.urlContainer, this.rateContainer, this.countframe, this.btnframe); this.tbody.append(this.table, this.cfgpage); this.main.append(this.toolbar, this.header, this.tbody, this.footer, this.promptElem); this.mainframe.append(this.mainbtn); - this.exBtn.append(this.importCFG, this.importTheme, this.exportCFG, this.exportTheme); - this.header.append(this.exBtn); this.root.append(this.mainframe, this.main); return true; @@ -2929,55 +3218,43 @@ class Container { return false; } remove() { - memory.store.clear(); - if (this.frame) { - this.frame.remove(); - } + this.userjsCache.clear(); + dom.remove(this.frame); } async save() { this.unsaved = false; await StorageSystem.setValue('Config', cfg); - info('Saved:', cfg); + info('Saved config:', cfg); + this.redirect(); return cfg; } /** * @param { string } css - CSS to inject * @param { string } name - Name of stylesheet - * @return { HTMLStyleElement } Style element + * @return { HTMLStyleElement | undefined } Style element */ - loadCSS(css, name = 'CSS') { + loadCSS(css, name = 'CSS', useGM = true) { try { - if (typeof name !== 'string') { + if (this.stylesheet) return this.stylesheet; + if (typeof name !== 'string') throw new Error('"name" must be a typeof "string"', { cause: 'loadCSS' }); - } - if (qs(`style[data-role="${name}"]`, this.root)) { - return qs(`style[data-role="${name}"]`, this.root); - } - if (typeof css !== 'string') { + if (typeof css !== 'string') throw new Error('"css" must be a typeof "string"', { cause: 'loadCSS' }); - } - if (isBlank(css)) { + if (isBlank(css)) throw new Error(`"${name}" contains empty CSS string`, { cause: 'loadCSS' }); - } const parent = isEmpty(this.root.shadowRoot) ? this.root : this.root.shadowRoot; - if (isGM) { - let sty; - if (isFN(GM.addElement)) { - sty = GM.addElement(parent, 'style', { - textContent: css - }); - } else if (isFN(GM_addElement)) { - sty = GM_addElement(parent, 'style', { - textContent: css - }); - } + if (useGM && isGM) { + const fn = (isFN(GM.addElement) && GM.addElement) || (isFN(GM_addElement) && GM_addElement); + if (!isFN(fn)) return this.loadCSS(css, name, false); + const sty = fn(parent, 'style', { textContent: css }); if (isElem(sty)) { sty.dataset.insertedBy = $info.script.name; sty.dataset.role = name; - return sty; + this.stylesheet = sty; + return this.stylesheet; } } - const sty = make('style', '', { + const sty = make('style', { textContent: css, dataset: { insertedBy: $info.script.name, @@ -2985,103 +3262,64 @@ class Container { } }); parent.appendChild(sty); - return sty; + this.stylesheet = sty; + return this.stylesheet; } catch (ex) { err(ex); } - return null; } checkBlacklist(str) { str = str || this.host; - let blacklisted = false; if (/accounts*\.google\./.test(this.webpage.host)) { - blacklisted = true; + this.isBlacklisted = true; + return this.isBlacklisted; } + let blacklisted = false; for (const b of normalizeTarget(cfg.blacklist)) { if (typeof b === 'string') { if (b.startsWith('userjs-')) { - const r = /userjs-(\w+)/.exec(b)[1]; + const [, r] = /userjs-(\w+)/.exec(b) ?? []; const biList = builtinList[r]; if (isRegExp(biList)) { - if (!biList.test(str)) continue; - blacklisted = true; + if (biList.test(str)) blacklisted = true; } else if (isObj(biList) && biList.host === this.host) { blacklisted = true; } } } else if (isObj(b)) { - if (!b.enabled) { - continue; - } + if (!b.enabled) continue; if (b.regex === true) { const reg = new RegExp(b.url, b.flags); - if (!reg.test(str)) continue; - blacklisted = true; + if (reg.test(str)) blacklisted = true; } if (Array.isArray(b.url)) { for (const c of b.url) { - if (!str.includes(c)) continue; - blacklisted = true; + if (str.includes(c)) blacklisted = true; } } - if (!str.includes(b.url)) continue; - blacklisted = true; + if (str.includes(b.url)) blacklisted = true; } } this.isBlacklisted = blacklisted; - return blacklisted; - } - getInfo(url) { - const webpage = strToURL(url || this.webpage); - const host = this.getHost(webpage.host); - const domain = this.getDomain(webpage.host); - return { - domain, - host, - webpage - }; - } - /** - * @template { string } S - * @param { S } str - */ - getHost(str = '') { - return str.split('.').splice(-2).join('.'); + return this.isBlacklisted; } - /** - * @template { string } S - * @param { S } str - */ - getDomain(str = '') { - return str.split('.').at(-2) ?? BLANK_PAGE; - } - renderTheme(theme) { - theme = theme || cfg.theme; + setTheme() { + const theme = cfg.theme ?? DEFAULT_CONFIG.theme; if (theme === DEFAULT_CONFIG.theme) { return; } - const sty = this.root.style; + const { style } = this.root; for (const [k, v] of Object.entries(theme)) { const str = `--mujs-${k}`; - const prop = sty.getPropertyValue(str); - if (isEmpty(v)) { - theme[k] = prop; - } - if (prop === v) { - continue; - } - sty.removeProperty(str); - sty.setProperty(str, v); + const prop = style.getPropertyValue(str); + if (isEmpty(v)) theme[k] = prop; + if (prop === v) continue; + style.removeProperty(str); + style.setProperty(str, v); } } makePrompt(txt, dataset = {}, usePrompt = true) { - if (qs('.prompt', this.promptElem)) { - for (const elem of qsA('.prompt', this.promptElem)) { - if (elem.dataset.prompt) { - elem.remove(); - } - } - } + dom.remove(Container.prompts); const el = make('mu-js', 'prompt', { dataset: { prompt: txt @@ -3107,110 +3345,157 @@ class Container { }); elPrompt.append(elYes, elNo); el.append(elPrompt); + } else { + const elPrompt = make('mu-js', 'prompt-body'); + const elNo = make('mujs-btn', 'prompt-deny', { + textContent: i18n$('close') + }); + ael(elNo, isMobile ? 'touchend' : 'click', () => { + el.remove(); + }); + elPrompt.append(elNo); + el.append(elPrompt); } + Container.prompts.push(el); this.promptElem.append(el); + return el; } - makeError(...ex) { - const safe = safeSelf(); + /** + * @template {string | Error} E + * @param {...E} ex + */ + showError(...ex) { + err(...ex); const error = make('mu-js', 'error'); let str = ''; for (const e of ex) { - str += `${typeof e === 'string' ? e : `${e.cause ? `[${e.cause}] ` : ''}${e.message} ${e.stack ?? ''}`}\n`; - if (isObj(e)) { - if (e.notify) { - dom.cl.add(this.mainframe, 'error'); - } - } + str += `${typeof e === 'string' ? e : `${e.cause ? `[${e.cause}] ` : ''}${e.message}${e.stack ? ` ${e.stack}` : ''}`}\n`; } - error.appendChild(safe.createTextNode(str)); + error.appendChild(_self.createTextNode(str)); this.footer.append(error); } - showError(...ex) { - err(...ex); - this.makeError(...ex); - } - updateCounter(count, engine) { - this.counters[engine.name] += count; - this.counters.total += count; - this.updateCounters(); + refresh() { + this.urlBar.placeholder = i18n$('newTab'); + Counter.reset(); + dom.cl.remove(this.toElem(), 'hidden'); + dom.cl.remove(this.cfgpage._mujs.sections, 'hidden'); + dom.prop([this.tabbody, this.rateContainer, this.footer], 'innerHTML', ''); } - updateCounters() { - for (const [k, v] of Object.entries(this.counters)) { - if (k === 'total') { - continue; - } - if (qs(`count-frame[data-counter="${k}"]`, this.countframe)) { - dom.text(qs(`count-frame[data-counter="${k}"]`, this.countframe), v); + /** + * Redirects sleazyfork userscripts from greasyfork.org to sleazyfork.org + * + * Taken from: https://greasyfork.org/scripts/23840 + */ + redirect() { + const locObj = window.top.location; + const { hostname } = locObj; + const gfSite = /greasyfork\.org/.test(hostname); + if (!gfSite && cfg.sleazyredirect) return; + const otherSite = gfSite ? 'sleazyfork' : 'greasyfork'; + if (!qs('span.sign-in-link')) return; + if (!/scripts\/\d+/.test(locObj.href)) return; + if ( + !qs('#script-info') && + (otherSite == 'greasyfork' || qs('div.width-constraint>section>p>a')) + ) { + const str = locObj.href.replace( + /\/\/([^.]+\.)?(greasyfork|sleazyfork)\.org/, + '//$1' + otherSite + '.org' + ); + info(`Redirecting to "${str}"`); + if (isFN(locObj.assign)) { + locObj.assign(str); + } else { + locObj.href = str; } } - dom.text(this.mainbtn, this.counters.total); } - refresh() { - this.urlBar.placeholder = i18n$('newTab'); - this.counters.total = 0; - for (const engine of cfg.engines) { - this.counters[engine.name] = 0; + /** + * @param {number} [time] + */ + async timeoutFrame(time) { + const frameTimeout = this.timeouts.frame; + frameTimeout.clear(...frameTimeout.ids); + if (dom.cl.has(this.mainframe, 'hidden')) { + return; } - this.updateCounters(); - dom.cl.remove(qsA('tr[data-engine]', this.tabbody), 'hidden'); - dom.cl.remove(qsA('mujs-section[data-name]', this.cfgpage), 'hidden'); - dom.prop([this.tabbody, this.rateContainer, this.footer], 'innerHTML', ''); + time = time ?? cfg.time ?? DEFAULT_CONFIG.time; + let n = 10000; + if (typeof time === 'number' && !Number.isNaN(time)) { + n = this.isBlacklisted ? time / 2 : time; + } + await frameTimeout.set(n); + this.remove(); + frameTimeout.clear(...frameTimeout.ids); } -} + toElem() { + return Array.from(this).map(({ _mujs }) => { + return _mujs.root; + }); + } + *[Symbol.iterator]() { + const arr = Array.from(this.userjsCache.values()).filter(({ _mujs }) => { + return !isEmpty(_mujs) && _mujs.info.engine.enabled; + }); + for (const userjs of arr) { + yield userjs; + } + } +}; const container = new Container(); // #endregion // #region Primary Function function primaryFN() { + const respHandles = { + build: BLANK_ASYNC_FN + }; try { + const { scheduler } = _self; const { - mainframe, - urlBar, - rateContainer, - footer, - tabbody, - toolbar, - tabhead, - cfgpage, - fsearch, btnfullscreen, + mainframe, main, - btncfg, - btnhome, - btnissue, - btngreasy, - tab, - userjsCache, - updateCounter, - showError + Tabs, + showError, + cfgpage: { + _mujs: { sections: cfgSec, base: cfgBase } + } } = container; - const frameTimeout = container.timeouts.frame; - const cfgMap = memory.maps.cfg; - const rebuildCfg = () => { - for (const engine of cfg.engines) { - if (cfgMap.has(engine.name)) { - const inp = cfgMap.get(engine.name); - inp.checked = engine.enabled; - if (engine.name === 'github') { - const txt = cfgMap.get('github-token'); - dom.prop(txt, 'value', engine.token); - } + const reloadCfg = () => { + for (const base of cfgBase) { + const [, CONFIG, SUB_CONFIG] = /^(\w+)-(.+)/.exec(base.value) ?? []; + let d = DEFAULT_CONFIG[base.value]; + let v = cfg[base.value]; + if (base.tag === 'engine') { + const dEngine = DEFAULT_CONFIG.engines.find((engine) => engine.name === base.value); + const vEngine = cfg.engines.find((engine) => engine.name === base.value); + if (dEngine) d = dEngine; + if (vEngine) v = vEngine; + } else if (CONFIG) { + d = DEFAULT_CONFIG[CONFIG][SUB_CONFIG]; + v = cfg[CONFIG][SUB_CONFIG]; } - } - for (const [k, v] of Object.entries(cfg)) { - if (typeof v === 'boolean') { - if (cfgMap.has(k)) { - const inp = cfgMap.get(k); - if (inp.type === 'checkbox') { - inp.checked = v; + base.cache = v; + if (base.type === 'checkbox') { + if (CONFIG) { + if (CONFIG === 'filters') { + base.elem.checked = cfg[CONFIG][SUB_CONFIG].enabled; } else { - dom.prop(inp, 'value', v); + base.elem.checked = v; + } + } else if (base.tag === 'engine') { + base.elem.checked = v.enabled; + base.elemUrl.value = decode(v.query); + base.elemUrl.placeholder = decode(d.query); + if (base.elemToken) { + base.elemToken = v.token; } } + } else { + base.elem.value = v; } } - dom.prop(cfgMap.get('blacklist'), 'value', JSON.stringify(cfg.blacklist, null, ' ')); - dom.prop(cfgMap.get('theme'), 'value', JSON.stringify(cfg.theme, null, ' ')); - container.renderTheme(cfg.theme); + container.setTheme(); }; const doInstallProcess = async (installLink) => { const locObj = window.top.location; @@ -3222,41 +3507,158 @@ function primaryFN() { installLink.remove(); await init(); }; + const doDownloadProcess = async (details) => { + if (!details.url) { + return; + } + const a = make('a'); + a.href = details.url; + a.setAttribute('download', details.filename || ''); + a.setAttribute('type', 'text/plain'); + a.dispatchEvent(new MouseEvent('click')); + await init(); + }; + const applyTo = (ujs, name, elem, root) => { + const n = ujs._mujs.code[name] ?? ujs._mujs.code.data_meta[name]; + if (isEmpty(n)) { + const el = make('mujs-a', { + textContent: i18n$('listing_none') + }); + elem.append(el); + return; + } + dom.prop(elem, 'innerHTML', ''); + dom.cl.remove(root, 'hidden'); + if (isObj(n)) { + if (name === 'resource') { + for (const [k, v] of Object.entries(n)) { + const el = make('mujs-a', { + textContent: k ?? 'ERROR' + }); + if (v.startsWith('http')) { + el.dataset.command = 'open-tab'; + el.dataset.webpage = v; + } + elem.append(el); + } + } else { + const el = make('mujs-a', { + textContent: n.text + }); + if (n.domain) { + el.dataset.command = 'open-tab'; + el.dataset.webpage = `https://${n.text}`; + } + elem.append(el); + } + } else if (typeof n === 'string') { + const el = make('mujs-a', { + textContent: n + }); + elem.append(el); + } else { + for (const c of n) { + if (typeof c === 'string' && c.startsWith('http')) { + const el = make('mujs-a', { + textContent: c, + dataset: { + command: 'open-tab', + webpage: c + } + }); + elem.append(el); + } else if (isObj(c)) { + const el = make('mujs-a', { + textContent: c.text + }); + if (c.domain) { + el.dataset.command = 'open-tab'; + el.dataset.webpage = `https://${c.text}`; + } + elem.append(el); + } else { + const el = make('mujs-a', { + textContent: c + }); + elem.append(el); + } + } + } + }; // #region Main event handlers - ael(main, 'click', async (evt) => { + const frameTimeout = container.timeouts.frame; + ael(main, isMobile ? 'touchend' : 'click', async (evt) => { try { + if (isNull(evt.target)) return; /** @type { HTMLElement } */ - const target = evt.target.closest('[data-command]'); - if (!target) { - return; - } - const prmpt = /prompt-/.test(target.dataset.command); + const t = evt.target; + const target = t.closest('[data-command]'); + if (isNull(target)) return; let dataset = target.dataset; let cmd = dataset.command; - let prmptChoice = false; - if (prmpt) { + if (/^prompt-/.test(target.dataset.command)) { dataset = target.parentElement.dataset; cmd = dataset.command; - prmptChoice = /confirm/.test(target.dataset.command); - target.parentElement.parentElement.remove(); + let pElem = target.parentElement.parentElement; + if (/prompt-install/.test(target.dataset.command)) { + pElem = target.parentElement.parentElement.parentElement; + const a = make('a', { + onclick(evt) { + evt.preventDefault(); + doInstallProcess(evt.target); + } + }); + a.href = target.dataset.code_url; + a.click(); + } else if (/prompt-download/.test(target.dataset.command)) { + pElem = target.parentElement.parentElement.parentElement; + const dataUserJS = container.userjsCache.get(+target.dataset.userjs); + if (dataUserJS) { + const code_obj = await dataUserJS._mujs.code.request(false, target.dataset.code_url); + if (typeof code_obj.code === 'string') + doDownloadProcess({ + url: 'data:text/plain;charset=utf-8,' + encodeURIComponent(code_obj.code), + filename: `${dataUserJS.name}${isUserCSS(target.dataset.code_url) ? '.user.css' : '.user.js'}` + }); + } + } + pElem.remove(); + return; } - if (cmd === 'install-script' && dataset.userjs) { - let installCode = dataset.userjs; - if (!prmpt && dataset.userjs.endsWith('.user.css')) { - container.makePrompt(i18n$('prmpt_css'), dataset); + if (cmd === 'install-script') { + const dataUserJS = container.userjsCache.get(+dataset.userjs); + if (isNull(dataUserJS)) { return; - } else if (prmpt !== prmptChoice) { - installCode = dataset.userjs.replace(/\.user\.css$/, '.user.js'); } - const dlBtn = make('a', '', { - onclick(evt) { - evt.preventDefault(); - doInstallProcess(evt.target); + if (dataUserJS.code_urls.length > 1) { + const list = make('mujs-list', { + style: 'display: flex; flex-direction: column;' + }); + for (const ujs of dataUserJS.code_urls) { + const a = make('mujs-a', { + title: ujs.code_url, + textContent: ujs.name, + dataset: { + command: 'prompt-install', + code_url: ujs.code_url + } + }); + list.append(a); } - }); - dlBtn.href = installCode; - dlBtn.click(); - } else if (cmd === 'open-tab' && dataset.webpage) { + container.makePrompt(`Multiple detected: ${list.outerHTML}`, dataset, false); + } else { + const a = make('a', { + onclick(evt) { + evt.preventDefault(); + doInstallProcess(evt.target); + } + }); + a.href = dataUserJS.code_url; + a.click(); + } + } else if (/open-tab|more-info/.test(cmd) && dataset.webpage) { + if (cmd === 'more-info') evt.preventDefault(); + con.log(cmd); if (isGM) { if (isFN(GM.openInTab)) { return GM.openInTab(dataset.webpage); @@ -3269,15 +3671,19 @@ function primaryFN() { } return window.open(dataset.webpage, '_blank'); } else if (cmd === 'navigation') { - if (dom.cl.has(btngreasy, 'hidden')) { - dom.cl.remove([btncfg, btngreasy, btnhome, btnissue], 'hidden'); - } else { - dom.cl.add([btncfg, btngreasy, btnhome, btnissue], 'hidden'); + for (const e of normalizeTarget(qsA('mujs-btn', target.parentElement)).filter( + (e) => !dom.cl.has(e, 'nav') + )) { + dom.cl.toggle(e, 'hidden'); } } else if (cmd === 'list-description') { const arr = []; const ignoreTags = new Set(['TD', 'MUJS-A', 'MU-JS']); - for (const node of target.parentElement.childNodes) { + /** + * @type { import("../typings/UserJS.d.ts").mujsName } + */ + const p = target.parentElement; + for (const node of Object.values(p._mujs)) { if (ignoreTags.has(node.tagName)) { continue; } @@ -3299,170 +3705,199 @@ function primaryFN() { } } else if (cmd === 'close') { container.remove(); - } else if (cmd === 'show-filter') { - dom.cl.toggle(fsearch, 'hidden'); } else if (cmd === 'fullscreen') { if (dom.cl.has(btnfullscreen, 'expanded')) { dom.cl.remove([btnfullscreen, main], 'expanded'); - dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('fsOpen')); + dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('expand')); } else { dom.cl.add([btnfullscreen, main], 'expanded'); - dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('fsClose')); + dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('collapse')); } } else if (cmd === 'hide-list') { dom.cl.add(main, 'hidden'); dom.cl.remove(mainframe, 'hidden'); - timeoutFrame(); + container.timeoutFrame(); } else if (cmd === 'save') { - // if (!isNull(ghMsg)) { - // ghMsg = null; - // container.rebuild = true; - // dom.prop(rateContainer, 'innerHTML', ''); - // } container.rebuild = true; - dom.prop(rateContainer, 'innerHTML', ''); + dom.prop(container.rateContainer, 'innerHTML', ''); if (!dom.prop(target, 'disabled')) { - container.save(); - sleazyRedirect(); + const config = await container.save(); if (container.rebuild) { - container.cache.clear(); - MUList.build(); + if (config.autofetch) { + respHandles.build(); + } } container.unsaved = false; container.rebuild = false; } } else if (cmd === 'reset') { cfg = DEFAULT_CONFIG; - dom.cl.remove(mainframe, 'error'); - if (qs('.error', footer)) { - for (const elem of qsA('.error', footer)) { - elem.remove(); - } - } + dom.remove(qsA('.error', container.footer)); container.unsaved = true; container.rebuild = true; - rebuildCfg(); + reloadCfg(); } else if (cmd === 'settings') { if (container.unsaved) { showError('Unsaved changes'); } - tab.create('mujs:settings'); + Tabs.create('mujs:settings'); container.rebuild = false; } else if (cmd === 'new-tab') { - tab.create(); + Tabs.create(); } else if (cmd === 'switch-tab') { - tab.active(target); + Tabs.active(target); } else if (cmd === 'close-tab' && target.parentElement) { - tab.close(target.parentElement); + Tabs.close(target.parentElement); } else if (cmd === 'download-userjs') { - if (!container.userjsCache.has(+dataset.userjs)) { - return; - } const dataUserJS = container.userjsCache.get(+dataset.userjs); - let installCode = dataUserJS.code_url; - if (!prmpt && dataUserJS.code_url.endsWith('.user.css')) { - container.makePrompt('Download as UserStyle?', dataset); + if (isNull(dataUserJS)) { return; - } else if (prmpt !== prmptChoice) { - installCode = dataUserJS.code_url.replace(/\.user\.css$/, '.user.js'); } - const r = await dataUserJS._mujs.code.request(false, installCode); - const txt = r.data; - if (typeof txt !== 'string') { - return; + + if (dataUserJS.code_urls.length > 1) { + const list = make('mujs-list', { + style: 'display: flex; flex-direction: column;' + }); + for (const ujs of dataUserJS.code_urls) { + const a = make('mujs-a', { + title: ujs.code_url, + textContent: ujs.name, + dataset: { + command: 'prompt-download', + code_url: ujs.code_url, + userjs: dataset.userjs + } + }); + list.append(a); + } + container.makePrompt(`Multiple detected: ${list.outerHTML}`, dataset, false); + } else { + const code_obj = await dataUserJS._mujs.code.request(false); + if (typeof code_obj.code === 'string') + doDownloadProcess({ + url: 'data:text/plain;charset=utf-8,' + encodeURIComponent(code_obj.code), + filename: `${dataUserJS.name}${isUserCSS(dataUserJS.code_url) ? '.user.css' : '.user.js'}` + }); } - const userjsName = dataset.userjsName ?? dataset.userjs; - const userjsExt = prmpt !== prmptChoice ? '.user.js' : '.user.css'; - const makeUserJS = new Blob([txt], { type: 'text/plain' }); - const dlBtn = make('a', 'mujs_Downloader'); - dlBtn.href = URL.createObjectURL(makeUserJS); - dlBtn.download = `${userjsName}${userjsExt}`; - dlBtn.click(); - URL.revokeObjectURL(dlBtn.href); - dlBtn.remove(); - } else if (cmd === 'load-userjs') { + } else if (cmd === 'load-userjs' || cmd === 'load-header') { if (!container.userjsCache.has(+dataset.userjs)) { return; } const codeArea = qs('textarea', target.parentElement.parentElement); - if (!isEmpty(codeArea.value)) { + if (!isEmpty(codeArea.value) && cmd === codeArea.dataset.load) { dom.cl.toggle(codeArea, 'hidden'); return; } + codeArea.dataset.load = cmd; const dataUserJS = container.userjsCache.get(+dataset.userjs); - const r = await dataUserJS._mujs.code.request(); - const txt = r.data; - if (typeof txt !== 'string') { + const code_obj = await dataUserJS._mujs.code.request(); + if (typeof code_obj.data_code_block !== 'string') { + codeArea.value = 'An error occured'; return; } - const code_obj = new ParseUserJS(txt); - codeArea.value = code_obj.get_code_block(); + codeArea.value = + cmd === 'load-userjs' ? code_obj.data_code_block : code_obj.data_meta_block; dom.cl.remove(codeArea, 'hidden'); - const apTo = (name, elem, root) => { - const n = dataUserJS._mujs.code[name]; - if (isEmpty(n)) { - const el = make('mujs-a', '', { - textContent: i18n$('listing_none') + for (const e of qsA( + 'mujs-column[data-el="matches"]', + target.parentElement.parentElement + )) { + applyTo(dataUserJS, e.dataset.type, qs('.mujs-grants', e), e); + } + } else if (cmd === 'load-page') { + if (!container.userjsCache.has(+dataset.userjs)) { + return; + } + let pageArea = qs('mujs-page', target.parentElement.parentElement); + if (!pageArea) { + pageArea = make('mujs-page'); + target.parentElement.parentElement.append(pageArea); + const dataUserJS = container.userjsCache.get(+dataset.userjs); + const engine = dataUserJS._mujs.info.engine; + let pageURL; + if (engine.name.includes('fork')) { + const { + navigator: { language } + } = _self; + const { current } = Language; + pageURL = dataUserJS.url.replace( + /\/scripts/, + `/${/^(zh|fr|es)/.test(current) ? language : current}/scripts` + ); + } else if (engine.name.includes('github')) { + const page_url = await Network.req(dataUserJS.page_url, 'GET', 'json', { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' + } + }).catch(() => { + return {}; }); - elem.append(el); + if (!page_url.download_url) { + return; + } + const page = await Network.req(page_url.download_url, 'GET', 'text'); + if (container.shadowSupport) { + const shadow = pageArea.attachShadow({ mode: 'closed' }); + const div = make('div', { + innerHTML: page + }); + shadow.append(div); + } return; + } else { + pageURL = dataUserJS.url; } - dom.prop(elem, 'innerHTML', ''); - dom.cl.remove(root, 'hidden'); - for (const c of n) { - if (typeof c === 'string' && c.startsWith('http')) { - const el = make('mujs-a', '', { - textContent: c, - dataset: { - command: 'open-tab', - webpage: c - } - }); - elem.append(el); - } else if (isObj(c)) { - if (name === 'resource') { - for (const [k, v] of Object.entries(c)) { - const el = make('mujs-a', '', { - textContent: k ?? 'ERROR' - }); - if (v.startsWith('http')) { - el.dataset.command = 'open-tab'; - el.dataset.webpage = v; - } - elem.append(el); - } - } else { - const el = make('mujs-a', '', { - textContent: c.text - }); - if (c.domain) { - el.dataset.command = 'open-tab'; - el.dataset.webpage = `https://${c.text}`; - } - elem.append(el); + if (!pageURL) { + return; + } + const page = await Network.req(pageURL, 'GET', 'document'); + const getContent = () => { + let content = 'An error occured'; + const h = new URL(dataUserJS.url); + const root = qs('.user-content', page.documentElement); + for (const e of qsA('[href]', root)) { + e.target = '_blank'; + e.style = 'pointer-events: auto;'; + if (e.href.startsWith('/')) { + e.href = `${h.origin}${e.href}`; } + } + for (const e of qsA('img[src]', root)) { + e.style = + 'max-width: 25em; max-height: 25em; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;'; + } + if (root) { + content = root.innerHTML; } else { - const el = make('mujs-a', '', { - textContent: c - }); - elem.append(el); + content = 'No additional info available'; } + return content; + }; + if (container.shadowSupport) { + const shadow = pageArea.attachShadow({ mode: 'closed' }); + const div = make('div', { + style: 'pointer-events: none;', + innerHTML: getContent() + }); + shadow.append(div); } - }; - const m = qsA('mujs-column[data-el="matches"]', target.parentElement.parentElement); - for (const e of normalizeTarget(m)) { - apTo(e.dataset.type, qs('.mujs-grants', e), e); + return; + } + if (!dom.cl.has(pageArea, 'hidden')) { + dom.cl.add(pageArea, 'hidden'); + return; } + dom.cl.remove(pageArea, 'hidden'); } else if (/export-/.test(cmd)) { - const str = JSON.stringify(cmd === 'export-cfg' ? cfg : cfg.theme, null, ' '); - const bytes = new TextEncoder().encode(str); - const blob = new Blob([bytes], { type: 'application/json;charset=utf-8' }); - const dlBtn = make('a', 'mujs-exporter', { - href: URL.createObjectURL(blob), - download: `Magic_Userscript_${cmd === 'export-cfg' ? 'config' : 'theme'}.json` + const toCfg = cmd === 'export-cfg'; + doDownloadProcess({ + url: + 'data:text/plain;charset=utf-8,' + + encodeURIComponent(JSON.stringify(toCfg ? cfg : cfg.theme, null, ' ')), + filename: `Magic_Userscript_${toCfg ? 'config' : 'theme'}.json` }); - dlBtn.click(); - URL.revokeObjectURL(dlBtn.href); } else if (/import-/.test(cmd)) { if (qs('input', target.parentElement)) { qs('input', target.parentElement).click(); @@ -3472,40 +3907,35 @@ function primaryFN() { type: 'file', accept: '.json', onchange(evt) { - try { - [...evt.target.files].forEach((file) => { - const reader = new FileReader(); - reader.readAsText(file); - reader.onload = () => { - const result = JSON.parse(reader.result); - if (result.blacklist) { - log(`Imported config: { ${file.name} }`, result); - cfg = result; - container.unsaved = true; - container.rebuild = true; - rebuildCfg(); - container.save(); - sleazyRedirect(); - container.cache.clear(); - MUList.build(); - container.unsaved = false; - container.rebuild = false; - } else { - log(`Imported theme: { ${file.name} }`, result); - cfg.theme = result; - container.renderTheme(cfg.theme); + const file = evt.target.files[0]; + if (file === undefined || file.name === '') { + return; + } + const fr = new FileReader(); + fr.onload = function () { + if (typeof fr.result !== 'string') { + return; + } + const content = JSON.parse(fr.result); + if (content.blacklist) { + cfg = content; + container.unsaved = true; + container.rebuild = true; + reloadCfg(); + container.save().then((config) => { + if (config.autofetch) { + respHandles.build(); } - inpJSON.remove(); - }; - reader.onerror = () => { - showError(reader.error); - inpJSON.remove(); - }; - }); - } catch (ex) { - showError(ex); + container.unsaved = false; + container.rebuild = false; + }); + } else { + cfg.theme = content; + container.setTheme(); + } inpJSON.remove(); - } + }; + fr.readAsText(file); } }); target.parentElement.append(inpJSON); @@ -3527,9 +3957,9 @@ function primaryFN() { const dataset = target.dataset; const cmd = dataset.command; if (cmd === 'switch-tab' || cmd === 'close-tab') { - tab.close(target); + Tabs.close(target); } else if (cmd === 'new-tab') { - tab.create(); + Tabs.create(); } }); if (!isMobile) { @@ -3551,9 +3981,59 @@ function primaryFN() { }); } } + /** + * @param {CustomEvent} evt + */ + const updatedItem = (evt) => { + const ujs = evt.detail; + if (!ujs._mujs) return; + if (ujs.deleted === true) { + ujs._mujs.root.remove(); + container.userjsCache.delete(ujs.id); + Counter.reset(); + MUList.sortRecords(); + return; + } + if (!isEmpty(ujs.code_urls)) ujs.code_url = ujs.code_urls[0].code_url; + for (const elem of qsA('[data-name]', ujs._mujs.root)) { + const name = elem.dataset.name; + if (name === 'code') { + if (ujs._mujs.code.data_code_block) { + if (cfg.preview.code && !cfg.preview.metadata) { + elem.value = ujs._mujs.code.data_code_block; + } else if (cfg.preview.metadata && !cfg.preview.code) { + elem.value = ujs._mujs.code.data_meta_block; + } else { + elem.value = `${ujs._mujs.code.META_START_COMMENT}${ujs._mujs.code.data_meta_block}${ujs._mujs.code.META_END_COMMENT}${ujs._mujs.code.data_code_block}`; + } + } + continue; + } + if (!ujs[name]) continue; + if (name === 'license') { + dom.attr(elem, 'title', ujs.license ?? i18n$('no_license')); + dom.text(elem, `${i18n$('license')}: ${ujs.license ?? i18n$('no_license')}`); + } else if (name === 'code_updated_at') { + dom.text(elem, Language.toDate(ujs.code_updated_at)); + elem.dataset.value = new Date(ujs.code_updated_at).toISOString(); + } else if (name === 'created_date') { + dom.text(elem, `${i18n$('created_date')}: ${Language.toDate(ujs.created_at)}`); + elem.dataset.value = new Date(ujs.created_at).toISOString(); + } else if (name === 'total_installs') { + dom.text(elem, `${i18n$('total_installs')}: ${Language.toNumber(ujs.total_installs)}`); + } else { + dom.text(elem, ujs[name]); + } + } + if (ujs._mujs.code.data_code_block) { + for (const e of qsA('mujs-column[data-el="matches"]', ujs._mujs.root)) { + applyTo(ujs, e.dataset.type, qs('.mujs-grants', e), e); + } + } + if (container.userjsCache.has(ujs.id)) container.userjsCache.set(ujs.id, ujs); + }; + ael(main, 'updateditem', updatedItem); // #endregion - const META_START_COMMENT = '// ==UserScript=='; - const META_END_COMMENT = '// ==/UserScript=='; const TLD_EXPANSION = ['com', 'net', 'org', 'de', 'co.uk']; const APPLIES_TO_ALL_PATTERNS = [ 'http://*', @@ -3581,92 +4061,85 @@ function primaryFN() { */ data_code_block; /** - * @type { { [meta: string]: any } } + * @type { { [meta: string]: string | string[] | { [resource: string]: string } } } */ data_meta; /** - * @type { string[] } + * @type { {text: string;domain: boolean;tld_extra: boolean}[] } */ data_names; - constructor(code) { - this.code = code; - this.get_all(); - } - get_all() { - this.get_meta_block(); - this.get_code_block(); - this.parse_meta(); - this.calculate_applies_to_names(); + constructor(code, isCSS) { + this.isUserCSS = isCSS === true; + this.META_START_COMMENT = this.isUserCSS ? '/* ==UserStyle==' : '// ==UserScript=='; + this.META_END_COMMENT = this.isUserCSS ? '==/UserStyle== */' : '// ==/UserScript=='; + if (code) { + this.code = code; + this.get_meta_block(); + this.get_code_block(); + this.parse_meta(); + this.calculate_applies_to_names(); + } } get_meta_block() { if (isEmpty(this.code)) { - return null; + return ''; } if (this.data_meta_block) { return this.data_meta_block; } - const start_block = this.code.indexOf(META_START_COMMENT); + const start_block = this.code.indexOf(this.META_START_COMMENT); if (isNull(start_block)) { - return null; + return ''; } - const end_block = this.code.indexOf(META_END_COMMENT, start_block); + const end_block = this.code.indexOf(this.META_END_COMMENT, start_block); if (isNull(end_block)) { - return null; + return ''; } - const meta_block = this.code.substring(start_block + META_START_COMMENT.length, end_block); + const meta_block = this.code.substring( + start_block + this.META_START_COMMENT.length, + end_block + ); this.data_meta_block = meta_block; - return meta_block; + return this.data_meta_block; } get_code_block() { if (isEmpty(this.code)) { - return null; + return ''; } if (this.data_code_block) { return this.data_code_block; } - const start_block = this.code.indexOf(META_START_COMMENT); + const start_block = this.code.indexOf(this.META_START_COMMENT); if (isNull(start_block)) { return null; } - const end_block = this.code.indexOf(META_END_COMMENT, start_block); + const end_block = this.code.indexOf(this.META_END_COMMENT, start_block); if (isNull(end_block)) { return null; } const code_block = this.code.substring( - end_block + META_END_COMMENT.length, + end_block + this.META_END_COMMENT.length, this.code.length ); - const lines = []; - for (const l of code_block.split('\n')) { - if (isEmpty(l)) { - continue; - } - lines.push(l); - } - const clean_code = lines.join('\n'); - this.data_code_block = clean_code; - return clean_code; + this.data_code_block = code_block.split('\n').filter(Boolean).join('\n'); + return this.data_code_block; } parse_meta() { if (isEmpty(this.code)) { - return null; + return {}; } if (this.data_meta) { return this.data_meta; } const meta = {}; - const meta_block = this.get_meta_block(); const meta_block_map = new Map(); - for (const meta_line of meta_block.split('\n')) { - const meta_match = meta_line.match(/\/\/\s+@([a-zA-Z:-]+)\s+(.*)/); - if (isNull(meta_match)) { - continue; - } - const key = meta_match[1].trim(); - const value = meta_match[2].trim(); - if (!meta_block_map.has(key)) { - meta_block_map.set(key, []); - } + const reg = (this.isUserCSS && /@([a-zA-Z:-]+)\s+(.*)/) || /\/\/\s+@([a-zA-Z:-]+)\s+(.*)/; + for (const meta_line of this.get_meta_block().split('\n').filter(Boolean)) { + let [, key, value] = reg.exec(meta_line) ?? []; + if (!key) continue; + key = key.trim(); + value = value.trim(); + if (!meta_block_map.has(key)) meta_block_map.set(key, []); const meta_map = meta_block_map.get(key); meta_map.push(value); meta_block_map.set(key, meta_map); @@ -3681,20 +4154,16 @@ function primaryFN() { this.data_meta = meta; return this.data_meta; } - /** - * @returns { string[] } - */ calculate_applies_to_names() { if (isEmpty(this.code)) { - return null; + return []; } if (this.data_names) { return this.data_names; } - const meta = this.parse_meta(); let patterns = []; - for (const [k, v] of Object.entries(meta)) { - if (/include|match/.test(k)) { + for (const [k, v] of Object.entries(this.parse_meta())) { + if (/include|match/i.test(k)) { if (Array.isArray(v)) { patterns = patterns.concat(v); } else { @@ -3706,147 +4175,192 @@ function primaryFN() { return []; } if (this.intersect(patterns, APPLIES_TO_ALL_PATTERNS)) { - return []; + this.data_names = [ + { + domain: false, + text: 'All sites', + tld_extra: false + } + ]; + return this.data_names; } - const name_set = new Set(); + this.data_names = ParseUserJS.getNames(patterns); + return this.data_names; + } + intersect(a, ...arr) { + return !isBlank([...new Set(a)].filter((v) => arr.every((b) => b.includes(v)))); + } + static getNames(patterns = []) { + const name_map = new Map(); const addObj = (obj) => { - if (name_set.has(obj)) { + if (name_map.has(obj.text)) { return; } - name_set.add(obj); + name_map.set(obj.text, obj); }; for (let p of patterns) { - try { - const original_pattern = p; - let pre_wildcards = []; - if (p.match(/^\/(.*)\/$/)) { - pre_wildcards = [p]; - } else { - let m = p.match(/^\*(https?:.*)/i); - if (!isNull(m)) { - p = m[1]; - } - p = p - .replace(/^\*:/i, 'http:') - .replace(/^\*\/\//i, 'http://') - .replace(/^http\*:/i, 'http:') - .replace(/^(https?):([^/])/i, '$1://$2'); - m = p.match(/^([a-z]+:\/\/)\*\.?([a-z0-9-]+(?:.[a-z0-9-]+)+.*)/i); - if (!isNull(m)) { - p = m[1] + m[2]; - } - m = p.match(/^\*\.?([a-z0-9-]+\.[a-z0-9-]+.*)/i); - if (!isNull(m)) { - p = `http://${m[1]}`; - } - m = p.match(/^http\*(?:\/\/)?\.?((?:[a-z0-9-]+)(?:\.[a-z0-9-]+)+.*)/i); - if (!isNull(m)) { - p = `http://${m[1]}`; - } - m = p.match(/^([a-z]+:\/\/([a-z0-9-]+(?:\.[a-z0-9-]+)*\.))\*(.*)/); - if (!isNull(m)) { - if (m[2].match(/A([0-9]+\.){2,}z/)) { - p = `${m[1]}tld${m[3]}`; - pre_wildcards = [p.split('*')[0]]; - } else { - pre_wildcards = [p]; - } + const original_pattern = p; + let pre_wildcards = []; + if (p.match(/^\/(.*)\/$/)) { + pre_wildcards = [p]; + } else { + let m = /^\*(https?:.*)/i.exec(p); + if (m) { + p = m[1]; + } + p = p + .replace(/^\*:/i, 'http:') + .replace(/^\*\/\//i, 'http://') + .replace(/^http\*:/i, 'http:') + .replace(/^(https?):([^/])/i, '$1://$2'); + m = /^([a-z]+:\/\/)\*\.?([a-z0-9-]+(?:.[a-z0-9-]+)+.*)/i.exec(p); + if (m) { + p = m[1] + m[2]; + } + m = /^\*\.?([a-z0-9-]+\.[a-z0-9-]+.*)/i.exec(p); + if (m) { + p = `http://${m[1]}`; + } + m = /^http\*(?:\/\/)?\.?((?:[a-z0-9-]+)(?:\.[a-z0-9-]+)+.*)/i.exec(p); + if (m) { + p = `http://${m[1]}`; + } + m = /^([a-z]+:\/\/([a-z0-9-]+(?:\.[a-z0-9-]+)*\.))\*(.*)/.exec(p); + if (m) { + if (m[2].match(/A([0-9]+\.){2,}z/)) { + p = `${m[1]}tld${m[3]}`; + pre_wildcards = [p.split('*')[0]]; } else { pre_wildcards = [p]; } + } else { + pre_wildcards = [p]; } - for (const pre_wildcard of pre_wildcards) { - try { - const urlObj = new URL(pre_wildcard); - const { host } = urlObj; - if (isNull(host)) { - addObj({ text: original_pattern, domain: false, tld_extra: false }); - } else if (!host.includes('.') && host.includes('*')) { - addObj({ text: original_pattern, domain: false, tld_extra: false }); - } else if (host.endsWith('.tld')) { - for (let i = 0; i < TLD_EXPANSION.length; i++) { - const tld = TLD_EXPANSION[i]; - addObj({ - text: host.replace(/tld$/i, tld), - domain: true, - tld_extra: i != 0 - }); - } - } else if (host.endsWith('.')) { - addObj({ - text: host.slice(0, -1), - domain: true, - tld_extra: false - }); - } else { + } + for (const pre_wildcard of pre_wildcards) { + try { + const urlObj = new URL(pre_wildcard); + const { host } = urlObj; + if (isNull(host)) { + addObj({ text: original_pattern, domain: false, tld_extra: false }); + } else if (!host.includes('.') && host.includes('*')) { + addObj({ text: original_pattern, domain: false, tld_extra: false }); + } else if (host.endsWith('.tld')) { + for (let i = 0; i < TLD_EXPANSION.length; i++) { + const tld = TLD_EXPANSION[i]; addObj({ - text: host, + text: host.replace(/tld$/i, tld), domain: true, - tld_extra: false + tld_extra: i != 0 }); } - // eslint-disable-next-line no-unused-vars - } catch (ex) { - addObj({ text: original_pattern, domain: false, tld_extra: false }); + } else if (host.endsWith('.')) { + addObj({ + text: host.slice(0, -1), + domain: true, + tld_extra: false + }); + } else { + addObj({ + text: host, + domain: true, + tld_extra: false + }); } + } catch { + addObj({ text: original_pattern, domain: false, tld_extra: false }); } - } catch (ex) { - err(ex); } } - const arr = [...name_set]; - this.data_names = arr; - return this.data_names; + return [...name_map.values()]; } - intersect(a = [], b = []) { - for (const v of a) { - if (b.includes(v)) { - return true; + /** + * @template {import("../typings/types.d.ts").GSForkQuery} O + * @param {boolean} translate + * @param {O["code_url"]} code_url + * @param {O} [obj] + */ + async request(translate = false, code_url, obj) { + if (this.data_code_block) { + return this; + } + /** @type { string } */ + const code = await Network.req(code_url, 'GET', 'text').catch(err); + if (typeof code !== 'string') { + return this; + } + this.isUserCSS = isUserCSS(code_url); + this.META_START_COMMENT = this.isUserCSS ? '/* ==UserStyle==' : '// ==UserScript=='; + this.META_END_COMMENT = this.isUserCSS ? '==/UserStyle== */' : '// ==/UserScript=='; + this.code = code; + this.get_meta_block(); + this.get_code_block(); + this.parse_meta(); + this.calculate_applies_to_names(); + + const { data_meta } = this; + if (translate) { + if (data_meta[`name:${Language.current}`]) { + Object.assign(obj, { + name: data_meta[`name:${Language.current}`] + }); + this.translated = true; + } + if (data_meta[`description:${Language.current}`]) { + Object.assign(obj, { + description: data_meta[`description:${Language.current}`] + }); + this.translated = true; } } - for (const v of b) { - if (a.includes(v)) { - return true; + if (Array.isArray(data_meta.grant)) { + data_meta.grant = union(data_meta.grant); + } + if (data_meta.resource) { + const obj = {}; + if (typeof data_meta.resource === 'string') { + const [, key, value] = /(.+)\s+(.+)/.exec(data_meta.resource) ?? []; + if (key) { + obj[key.trim()] = value; + } + } else { + for (const r of data_meta.resource) { + const [, key, value] = /(.+)\s+(http.+)/.exec(r) ?? []; + if (key) { + obj[key.trim()] = value; + } + } } + data_meta.resource = obj; } - return false; + Object.assign(this, { + code_size: [Network.format(code.length)], + meta: data_meta + }); + + return this; } } - const mergeTemplate = (obj = {}) => { - const template = { - id: 0, - bad_ratings: 0, - good_ratings: 0, - ok_ratings: 0, - daily_installs: 0, - total_installs: 0, - name: 'NOT FOUND', - description: 'NOT FOUND', - version: '0.0.0', - url: BLANK_PAGE, - code_url: BLANK_PAGE, - created_at: Date.now(), - code_updated_at: Date.now(), - locale: 'NOT FOUND', - deleted: false, - users: [ - { - id: 0, - name: 'NOT FOUND', - url: BLANK_PAGE - } - ] - }; - for (const key in template) { - if (hasOwn(obj, key)) continue; - obj[key] = template[key]; - } - return obj; + const template = { + id: 0, + bad_ratings: 0, + good_ratings: 0, + ok_ratings: 0, + daily_installs: 0, + total_installs: 0, + name: 'NOT FOUND', + description: 'NOT FOUND', + version: '0.0.0', + url: BLANK_PAGE, + code_url: BLANK_PAGE, + created_at: Date.now(), + code_updated_at: Date.now(), + locale: 'NOT FOUND', + deleted: false, + users: [] }; const mkList = (txt = '', obj = {}) => { - if (!obj.root || !obj.type) { - return; - } + if (!obj.root || !obj.type) return; const { root, type } = obj; const appliesTo = make('mu-js', 'mujs-list', { textContent: `${txt}: ` @@ -3860,10 +4374,9 @@ function primaryFN() { }); ujsURLs.append(appliesTo, applyList); root.append(ujsURLs); - const list = obj.list ?? []; if (isEmpty(list)) { - const elem = make('mujs-a', '', { + const elem = make('mujs-a', { textContent: i18n$('listing_none') }); applyList.append(elem); @@ -3872,7 +4385,7 @@ function primaryFN() { } for (const c of list) { if (typeof c === 'string' && c.startsWith('http')) { - const elem = make('mujs-a', '', { + const elem = make('mujs-a', { textContent: c, dataset: { command: 'open-tab', @@ -3883,7 +4396,7 @@ function primaryFN() { } else if (isObj(c)) { if (type === 'resource') { for (const [k, v] of Object.entries(c)) { - const elem = make('mujs-a', '', { + const elem = make('mujs-a', { textContent: k ?? 'ERROR' }); if (v.startsWith('http')) { @@ -3893,7 +4406,7 @@ function primaryFN() { applyList.append(elem); } } else { - const elem = make('mujs-a', '', { + const elem = make('mujs-a', { textContent: c.text }); if (c.domain) { @@ -3903,52 +4416,45 @@ function primaryFN() { applyList.append(elem); } } else { - const elem = make('mujs-a', '', { + const elem = make('mujs-a', { textContent: c }); applyList.append(elem); } } }; - const timeoutFrame = async (time) => { - time = time ?? cfg.time; - frameTimeout.clear(...frameTimeout.ids); - if (dom.cl.has(mainframe, 'hidden')) { - return; - } - if (typeof time === 'number' && !Number.isNaN(time)) { - await frameTimeout.set(container.isBlacklisted ? time / 2 : time); - } else { - await frameTimeout.set(10000); - } - container.remove(); - return frameTimeout.clear(...frameTimeout.ids); - }; // #region Create UserJS /** * @param { import("../typings/types.d.ts").GSForkQuery } ujs * @param { string } engine */ const createjs = (ujs, engine) => { - ujs = mergeTemplate(ujs); - // Lets not add this UserJS to the list - if (ujs.id === 421603) { - return; - } - if (badUserJS.includes(ujs.id) || badUserJS.includes(ujs.url)) { - return; - } - if (!container.userjsCache.has(ujs.id)) { - container.userjsCache.set(ujs.id, ujs); - } + const a = [ + ujs.deleted === true, + ujs.id === 421603, // Lets not add this UserJS to the list + badUserJS.includes(ujs.id), + badUserJS.includes(ujs.url) + ].some((t) => t === true); + if (a) return; + if (!container.userjsCache.has(ujs.id)) container.userjsCache.set(ujs.id, ujs); const eframe = make('td', 'install-btn'); const uframe = make('td', 'mujs-uframe'); const fdaily = make('td', 'mujs-list', { - textContent: ujs.daily_installs + textContent: ujs.daily_installs, + dataset: { + name: 'daily_installs' + } }); const fupdated = make('td', 'mujs-list', { - textContent: language.toDate(ujs.code_updated_at) + textContent: Language.toDate(ujs.code_updated_at), + dataset: { + name: 'code_updated_at', + value: new Date(ujs.code_updated_at).toISOString() + } }); + /** + * @type { import("../typings/UserJS.d.ts").mujsName } + */ const fname = make('td', 'mujs-name'); const fmore = make('mujs-column', 'mujs-list hidden', { dataset: { @@ -3971,16 +4477,24 @@ function primaryFN() { textContent: `${i18n$('version_number')}: ${ujs.version}` }); const fcreated = make('mu-js', 'mujs-list', { - textContent: `${i18n$('created_date')}: ${language.toDate(ujs.created_at)}` + textContent: `${i18n$('created_date')}: ${Language.toDate(ujs.created_at)}`, + dataset: { + name: 'created_at', + value: new Date(ujs.created_at).toISOString() + } }); const flicense = make('mu-js', 'mujs-list', { title: ujs.license ?? i18n$('no_license'), textContent: `${i18n$('license')}: ${ujs.license ?? i18n$('no_license')}`, - style: - 'text-overflow: ellipsis; overflow: hidden; white-space: nowrap; width: fit-content; max-width: 20em;' + dataset: { + name: 'license' + } }); const ftotal = make('mu-js', 'mujs-list', { - textContent: `${i18n$('total_installs')}: ${language.toNumber(ujs.total_installs)}` + textContent: `${i18n$('total_installs')}: ${Language.toNumber(ujs.total_installs)}`, + dataset: { + name: 'total_installs' + } }); const fratings = make('mu-js', 'mujs-list', { title: i18n$('ratings'), @@ -3990,6 +4504,7 @@ function primaryFN() { title: i18n$('good'), textContent: ujs.good_ratings, dataset: { + name: 'good_ratings', el: 'good' } }); @@ -3997,6 +4512,7 @@ function primaryFN() { title: i18n$('ok'), textContent: ujs.ok_ratings, dataset: { + name: 'ok_ratings', el: 'ok' } }); @@ -4004,6 +4520,7 @@ function primaryFN() { title: i18n$('bad'), textContent: ujs.bad_ratings, dataset: { + name: 'bad_ratings', el: 'bad' } }); @@ -4019,11 +4536,11 @@ function primaryFN() { title: `${i18n$('install')} "${ujs.name}"`, dataset: { command: 'install-script', - userjs: ujs.code_url + userjs: ujs.id } }); - const scriptDownload = make('mu-jsbtn', '', { - innerHTML: `${iconSVG.load('install')} ${i18n$('saveFile')}`, + const scriptDownload = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('download')} ${i18n$('saveFile')}`, dataset: { command: 'download-userjs', userjs: ujs.id, @@ -4032,6 +4549,7 @@ function primaryFN() { }); const tr = make('tr', 'frame', { dataset: { + engine, scriptId: ujs.id } }); @@ -4044,27 +4562,25 @@ function primaryFN() { spellcheck: false, wrap: 'soft' }); - const loadCode = make('mu-jsbtn', '', { - innerHTML: `${iconSVG.load('search')} ${i18n$('preview_code')}`, + const loadCode = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('code')} ${i18n$('code')}`, dataset: { command: 'load-userjs', userjs: ujs.id } }); - if (engine) { - tr.dataset.engine = engine; - // if (engine.includes('fork')) { - // fdaily.dataset.command = 'open-tab'; - // fdaily.dataset.webpage = `${ujs.url}/stats`; - // fupdated.dataset.command = 'open-tab'; - // fupdated.dataset.webpage = `${ujs.url}/versions`; - // } - if (!engine.includes('fork') && cfg.recommend.others && goodUserJS.includes(ujs.url)) { - tr.dataset.good = 'upsell'; + const loadMetadata = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('code')} ${i18n$('metadata')}`, + dataset: { + command: 'load-header', + userjs: ujs.id } + }); + if (!engine.includes('fork') && cfg.recommend.others && goodUserJS.includes(ujs.url)) { + tr.dataset.good = 'upsell'; } for (const u of ujs.users) { - const user = make('mujs-a', '', { + const user = make('mujs-a', { innerHTML: u.name, title: u.url, dataset: { @@ -4072,13 +4588,16 @@ function primaryFN() { webpage: u.url } }); - if (cfg.recommend.author && u.id === authorID) { + if ( + cfg.recommend.author && + (u.id === authorID || u.url === 'https://github.com/magicoflolis') + ) { tr.dataset.author = 'upsell'; dom.prop(user, 'innerHTML', `${u.name} ${iconSVG.load('verified')}`); } uframe.append(user); } - if (engine.includes('fork') && cfg.recommend.others && goodUserJS.includes(ujs.id)) { + if (cfg.recommend.others && goodUserJS.includes(ujs.id)) { tr.dataset.good = 'upsell'; } eframe.append(scriptInstall); @@ -4086,333 +4605,331 @@ function primaryFN() { jsInfo.append(ftotal, ratings, fver, fcreated); mkList(i18n$('code_size'), { list: ujs._mujs.code.code_size, - type: 'size', + type: 'code_size', root: jsInfo }); jsInfoB.append(flicense); + const data_meta = ujs._mujs.code?.data_meta ?? {}; mkList(i18n$('antifeatures'), { - list: ujs._mujs.code.antifeatures, + list: data_meta.antifeatures ?? [], type: 'antifeatures', root: jsInfoB }); mkList(i18n$('applies_to'), { - list: ujs._mujs.code.match, - type: 'match', + list: ujs._mujs.code?.data_names ?? [], + type: 'data_names', root: jsInfoB }); mkList('@grant', { - list: ujs._mujs.code.grant, + list: data_meta.grant ?? [], type: 'grant', root: jsInfoB }); mkList('@require', { - list: ujs._mujs.code.meta?.require, + list: data_meta.require, type: 'require', root: jsInfoB }); - const script_resources = ujs._mujs.code.meta?.resource; mkList('@resource', { - list: isNull(script_resources) ? [] : [script_resources], + list: isNull(data_meta.resource) ? [] : [data_meta.resource], type: 'resource', root: jsInfoB }); fmore.append(jsInfo, jsInfoB); - fBtns.append(scriptDownload, loadCode); + fBtns.append(scriptDownload, loadCode, loadMetadata); fname.append(ftitle, fdesc, fmore, fBtns, codeArea); + fname._mujs = { fmore, fBtns, codeArea }; - const code_data = ujs._mujs.code.data; - if (code_data) { - const code_obj = new ParseUserJS(code_data); - codeArea.value = code_obj.get_code_block(); - } + const loadPage = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('pager')} Page`, + dataset: { + command: 'load-page', + userjs: ujs.id + } + }); + fBtns.append(loadPage); - for (const e of [fname, uframe, fdaily, fupdated, eframe]) { - tr.append(e); - } - tabbody.append(tr); - ujs._mujs.root = tr; + if (ujs._mujs.code?.translated) tr.classList.add('translated'); - return tr; + for (const e of [fname, uframe, fdaily, fupdated, eframe]) tr.append(e); + ujs._mujs.root = tr; + return ujs._mujs.root; }; // #endregion - // #region Build List - class List { - engines; - cache; - intHost; - constructor(host = undefined) { - if (isEmpty(host)) { - host = container.host; + const loadFilters = () => { + /** @type {Map} */ + const pool = new Map(); + const handles = { + pool, + enabled() { + return [...pool.values()].filter((o) => o.enabled); + }, + refresh() { + if (!Object.is(pool.size, 0)) pool.clear(); + for (const [key, value] of Object.entries(cfg.filters)) { + if (!pool.has(key)) + pool.set(key, { + ...value, + reg: new RegExp(value.regExp, value.flag), + keyReg: new RegExp(key.trim().toLocaleLowerCase(), 'gi'), + valueReg: new RegExp(value.name.trim().toLocaleLowerCase(), 'gi') + }); + } + return this; + }, + get(str) { + return [...pool.values()].find((v) => v.keyReg.test(str) || v.valueReg.test(str)); + }, + /** + * @param { import("../typings/types.d.ts").GSForkQuery } param0 + */ + match({ name, users }) { + const p = handles.enabled(); + if (Object.is(p.length, 0)) return true; + for (const v of p) { + if ([{ name }, ...users].find((o) => o.name.match(v.reg))) return false; + } + return true; } - this.engines = cfg.engines; - this.cache = container.cache.get(host); - this.host = host; + }; + for (const [key, value] of Object.entries(cfg.filters)) { + if (!pool.has(key)) + pool.set(key, { + ...value, + reg: new RegExp(value.regExp, value.flag), + keyReg: new RegExp(key.trim().toLocaleLowerCase(), 'gi'), + valueReg: new RegExp(value.name.trim().toLocaleLowerCase(), 'gi') + }); + } + return handles.refresh(); + }; + // #region List + /** + * @type { typeof import("../typings/UserJS.d.ts").List } + */ + const List = class { + #intEngines; + #intHost; + constructor(hostname = undefined) { + this.build = this.build.bind(this); + this.groupBy = this.groupBy.bind(this); + this.dispatch = this.dispatch.bind(this); + this.sortRecords = this.sortRecords.bind(this); + this.#intEngines = cfg.engines ?? []; + this.setHost(hostname); } - set host(hostname) { - this.intHost = hostname; - - if (!container.cache.has(hostname)) { - const engineTemplate = {}; - for (const engine of cfg.engines) { - engineTemplate[engine.name] = []; + setEngines(engines = []) { + const { host } = this; + const e = engines.filter((e) => { + if (!e.enabled) { + return false; } - container.cache.set(hostname, engineTemplate); - } - if (container.checkBlacklist(hostname)) { + const v = engineUnsupported[e.name] ?? []; + if (v.includes(host)) { + showError(`Engine: "${e.name}" unsupported on "${host}"`); + container.timeoutFrame(); + return false; + } + return true; + }); + this.#intEngines = e; + return e; + } + + setHost(hostname) { + if (isEmpty(hostname)) hostname = container.host; + this.#intHost = hostname; + this.blacklisted = container.checkBlacklist(hostname); + this.#intEngines = this.setEngines(this.engines); + this.domain = this.getDomain(this.#intHost); + if (this.blacklisted) { showError(`Blacklisted "${hostname}"`); - timeoutFrame(); - this.blacklisted = true; - } else { - this.blacklisted = false; + container.timeoutFrame(); } + return hostname; + } - const isSupported = (name) => { - for (const [k, v] of Object.entries(engineUnsupported)) { - if (k !== name) { - continue; - } - if (v.includes(hostname)) { - return false; - } - } - return true; - }; - this.engines = cfg.engines.filter((e) => e.enabled && isSupported(e.name)); - this.cache = container.cache.get(hostname); - // if (!isNull(ghMsg)) { - // const txt = make('mujs-row', 'legacy-config', { - // textContent: ghMsg - // }); - // rateContainer.append(txt); - // return; - // } + dispatch(ujs) { + const { CustomEvent } = _self; + const customEvent = new CustomEvent('updateditem', { detail: ujs }); + main.dispatchEvent(customEvent); + } + + get engines() { + return this.#intEngines; } get host() { - return this.intHost; + return this.#intHost; } - async build() { + getDomain(str = '') { + if (str === '*') { + return 'all-sites'; + } + return str.split('.').at(-2) ?? BLANK_PAGE; + } + + // #region Builder + build() { try { container.refresh(); - if (this.blacklisted) { - return; - } - if (isEmpty(this.engines)) { + const { blacklisted, engines, host, domain, dispatch } = this; + if (blacklisted || isEmpty(engines)) { container.opacityMin = '0'; mainframe.style.opacity = container.opacityMin; return; } - info(' Building list', { cache: this.cache, engines: this.engines }); - const customRecords = []; - const arr = []; - const host = this.host; - for (const engine of this.engines) { - const cEngine = this.cache[`${engine.name}`]; - if (!isEmpty(cEngine)) { - for (const ujs of cEngine) { - if (isElem(ujs._mujs.root)) { - tabbody.append(ujs._mujs.root); - } - } - updateCounter(cEngine.length, engine); - continue; - } - const _mujs = (d) => { - const obj = { - ...d, - _mujs: { - root: {}, - info: { - engine, - host - }, - code: { - antifeatures: [], - grant: [], - match: [], - meta: {}, - request: async function (translate = false, code_url) { - if (this.data) { - return this; - } - this.data = ''; - /** @type { string } */ - const code = await Network.req(code_url ?? d.code_url, 'GET', 'text').catch( - showError - ); - if (typeof code !== 'string') { - return this; - } - Object.assign(this, { - data: code, - code_size: [Network.format(code.length)] - }); - const grantSet = new Set(); - const afSet = new Set(); - const code_obj = new ParseUserJS(code); - const meta = code_obj.parse_meta(); - const applies_to_names = code_obj.calculate_applies_to_names(); - - if (translate) { - for (const lng of language.cache) { - if (meta[`name:${lng}`]) { - Object.assign(obj, { - name: meta[`name:${lng}`] - }); - this.translated = true; - } - if (meta[`description:${lng}`]) { - Object.assign(obj, { - description: meta[`description:${lng}`] - }); - this.translated = true; - } - } - } + const fetchRecords = []; + const bsFilter = loadFilters(); + const hostCache = Array.from(this); - for (const [key, value] of Object.entries(meta)) { - if (/grant/.test(key)) { - for (const v of normalizeTarget(value, false)) { - if (grantSet.has(v)) { - continue; - } - grantSet.add(v); - } - } else if (/antifeature/.test(key)) { - for (const v of normalizeTarget(value, false)) { - if (afSet.has(v)) { - continue; - } - afSet.add(v); - } - } - } - if (typeof meta.require === 'string') { - const s = meta.require; - meta.require = [s]; - } - if (meta.resource) { - const obj = {}; - if (typeof meta.resource === 'string') { - const reg = /(.+)\s+(.+)/.exec(meta.resource); - if (reg) { - obj[reg[1].trim()] = reg[2]; - } - } else { - for (const r of meta.resource) { - const reg = /(.+)\s+(http.+)/.exec(r); - if (reg) { - obj[reg[1].trim()] = reg[2]; - } - } - } - meta.resource = obj; - } - Object.assign(this, { - meta, - match: applies_to_names, - grant: [...grantSet], - antifeatures: [...afSet] - }); + info('Building list', { hostCache, engines, container, list: this }); - return this; + const g = this.groupBy(); + const toFetch = engines.filter((engine) => !g[engine.name]); + const isCached = toFetch.filter((engine) => + hostCache.find(({ _mujs }) => engine.name === _mujs.info.engine.name) + ); + if (!isBlank(toFetch) && isBlank(isCached)) { + for (const engine of engines) { + info(`Fetching from "${engine.name}" for "${host}"`); + const respError = (error) => { + if (!error.cause) error.cause = engine.name; + if (error.message.startsWith('429')) { + showError(`Engine: "${engine.name}" Too many requests...`); + return; + } + showError(`Engine: "${engine.name}"`, error.message); + }; + const _mujs = (d) => { + /** + * @type {import("../typings/types.d.ts").GSForkQuery} + */ + const ujs = { + ...template, + ...d, + code_urls: [], + _mujs: { + root: {}, + info: { + engine, + host + }, + code: { + meta: {} } } - } + }; + ujs._mujs.code.request = async (translate = false, code_url) => { + if (typeof ujs._mujs.code.data_code_block === 'string') { + return ujs._mujs.code; + } + const p = new ParseUserJS(); + await p.request(translate, code_url ?? ujs.code_url, ujs); + if (code_url) { + return p; + } + for (const [k, v] of Object.entries(p)) ujs._mujs.code[k] = v; + return ujs._mujs.code; + }; + return ujs; }; - return obj; - }; - const toQuery = (fallback) => { - if (engine.query) { - return decodeURIComponent(engine.query).replace(/\{host\}/g, host); - } - return fallback; - }; - /** - * @param { import("../typings/types.d.ts").GSFork } dataQ - */ - const forkFN = async (dataQ) => { - if (!dataQ) { - showError('Invalid data received from the server, check internet connection'); - return; - } - /** - * @type { import("../typings/types.d.ts").GSForkQuery[] } - */ - const dq = Array.isArray(dataQ) - ? dataQ - : Array.isArray(dataQ.query) - ? dataQ.query - : []; - const dataA = dq.filter((d) => !d.deleted); - if (isBlank(dataA)) { - return; - } - const data = dataA.map(_mujs); - const otherLng = []; /** - * @param {import("../typings/types.d.ts").GSForkQuery} d - * @returns {boolean} + * Prior to UserScript v7.0.0 + * @template {string} F + * @param {F} fallback + * @returns {F} */ - const inUserLanguage = (d) => { - const dlocal = d.locale.split('-')[0] ?? d.locale; - if (language.cache.includes(dlocal)) { - return true; + const toQuery = (fallback) => { + if (engine.query) { + return decode(engine.query) + .replace(/\{host\}/g, host) + .replace(/\{domain\}/g, domain); } - otherLng.push(d); - return false; + return fallback; }; - const filterLang = data.filter((d) => { - if (cfg.filterlang && !inUserLanguage(d)) { - return false; - } - return true; - }); - let finalList = filterLang; - const hds = []; - for (const ujs of otherLng) { - const c = await ujs._mujs.code.request(true); - if (c.translated) { - hds.push(ujs); - } - } - finalList = [...new Set([...hds, ...filterLang])]; - - for (const ujs of finalList) { - if (cfg.codePreview && !ujs._mujs.code.data) { - await ujs._mujs.code.request(); + /** + * @param { import("../typings/types.d.ts").GSFork } dataQ + */ + const forkFN = async (dataQ) => { + if (!dataQ) { + showError('Invalid data received from the server, check internet connection'); + return; } - createjs(ujs, engine.name); - } - this.cache[engine.name].push(...finalList); - updateCounter(finalList.length, engine); - }; - /** - * @param {Document} htmlDocument - */ - const openuserjs = async (htmlDocument) => { - try { - if (!htmlDocument) { - showError('Invalid data received from the server, TODO fix this'); + const dq = Array.isArray(dataQ) + ? dataQ + : Array.isArray(dataQ.query) + ? dataQ.query + : []; + const dataA = dq + .filter(Boolean) + .filter((d) => !d.deleted) + .filter(bsFilter.match); + if (isBlank(dataA)) { return; } - const selected = htmlDocument.documentElement; - if (/openuserjs/gi.test(engine.name)) { - for (const i of qsA('.col-sm-8 .tr-link', selected)) { - while (isNull(qs('.script-version', i))) { - await new Promise((resolve) => requestAnimationFrame(resolve)); + const { groupBy } = _self; + const g = groupBy(dataA.map(_mujs), ({ locale }) => { + const [current = locale] = locale.split('-'); + return current; + }); + for (const [k, list] of Object.entries(g)) { + if (!list) break; + for (const ujs of list) { + if (cfg.filterlang && k !== Language.current) { + const c = await ujs._mujs.code.request(true); + if (!c.translated) continue; } - const fixurl = dom - .prop(qs('.tr-link-a', i), 'href') - .replace( - new RegExp(document.location.origin, 'gi'), - 'https://openuserjs.org' + if ( + !ujs._mujs.code.data_code_block && + (cfg.preview.code || cfg.preview.metadata) + ) { + ujs._mujs.code.request().then(() => { + dispatch(ujs); + }); + } + if (isUserCSS(ujs.code_url)) { + ujs.code_urls.push( + { + name: `${ujs.name} (.user.css)`, + code_url: ujs.code_url + }, + { + name: `${ujs.name} (.user.js)`, + code_url: ujs.code_url.replace(/\.user\.css$/, '.user.js') + } ); - const ujs = mergeTemplate( - _mujs({ + } + createjs(ujs, engine.name); + } + } + }; + /** + * @param {Document} htmlDocument + */ + const openuserjs = async (htmlDocument) => { + try { + if (!htmlDocument) { + showError('Invalid data received from the server, TODO fix this'); + return; + } + const d = htmlDocument.documentElement; + if (/openuserjs/gi.test(engine.name)) { + const col = qsA('.col-sm-8 .tr-link', d) ?? []; + for (const i of col) { + while (isNull(qs('.script-version', i))) { + await new Promise((resolve) => requestAnimationFrame(resolve)); + } + const fixurl = dom + .prop(qs('.tr-link-a', i), 'href') + .replace( + new RegExp(document.location.origin, 'gi'), + 'https://openuserjs.org' + ); + const ujs = _mujs({ name: dom.text(qs('.tr-link-a', i)), description: dom.text(qs('p', i)), version: dom.text(qs('.script-version', i)), @@ -4427,187 +4944,416 @@ function primaryFN() { url: dom.prop(qs('.inline-block a', i), 'href') } ] - }) - ); - if (cfg.codePreview && !ujs._mujs.code.data) { - await ujs._mujs.code.request(); + }); + if (bsFilter.match(ujs)) { + continue; + } + if ( + !ujs._mujs.code.data_code_block && + (cfg.preview.code || cfg.preview.metadata) + ) { + ujs._mujs.code.request().then(() => { + dispatch(ujs); + }); + } + createjs(ujs, engine.name); } - createjs(ujs, engine.name); - customRecords.push(ujs); } + } catch (ex) { + showError(ex); } - this.cache[engine.name].push(...customRecords); - updateCounter(customRecords.length, engine); - } catch (ex) { - showError(ex); - } - }; - const gitFN = async (data) => { - try { - if (isBlank(data.items)) { - showError('Invalid data received from the server, TODO fix this'); - return; - } - for (const r of data.items) { - const ujs = mergeTemplate( - _mujs({ + }; + const gitFN = (data) => { + try { + if (isBlank(data.items)) { + return; + } + for (const r of data.items) { + const ujs = _mujs({ + id: r.id ?? 0, name: r.name, - description: isEmpty(r.repository.description) - ? i18n$('no_license') - : r.repository.description, + description: isEmpty(r.description) ? i18n$('no_license') : r.description, url: r.html_url, - code_url: r.html_url.replace(/\/blob\//g, '/raw/'), - code_updated_at: r.commit || Date.now(), - total_installs: r.score, + code_url: r.html_url, + page_url: `${r.url}/contents/README.md`, + created_at: r.created_at, + code_updated_at: r.updated_at || Date.now(), + daily_installs: r.watchers_count ?? 0, + good_ratings: r.stargazers_count ?? 0, users: [ { - name: r.repository.owner.login, - url: r.repository.owner.html_url + name: r.owner.login, + url: r.owner.html_url } ] - }) - ); - if (cfg.codePreview && !ujs._mujs.code.data) { - await ujs._mujs.code.request(); + }); + if (r.license?.name) ujs.license = r.license.name; + const rootPath = r.contents_url.replace(/\{\+path\}/, ''); + const fetchContent = async (dir) => { + const contents = await Network.req(dir, 'GET', 'json', { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' + } + }).catch(respError); + for (const content of contents) { + if (content.type === 'file') { + if (isUserJS(content.name)) { + ujs.code_urls.push({ + name: content.name, + code_url: content.download_url + }); + } else if (isUserCSS(content.name)) { + ujs.code_urls.push({ + name: content.name, + code_url: content.download_url + }); + } + } else if (content.type === 'dir') { + await fetchContent(`${rootPath}/${content.path}`); + } + } + }; + fetchContent(rootPath).then(() => { + if (isEmpty(ujs.code_urls)) { + ujs.deleted = true; + } else if ( + !ujs._mujs.code.data_code_block && + (cfg.preview.code || cfg.preview.metadata) + ) { + ujs._mujs.code.request().then(() => { + dispatch(ujs); + }); + return; + } + dispatch(ujs); + }); + createjs(ujs, engine.name); } - createjs(ujs, engine.name); - customRecords.push(ujs); + } catch (ex) { + showError(ex); } - this.cache[engine.name].push(...customRecords); - updateCounter(data.items.length, engine); - } catch (ex) { - showError(ex); - } - }; - let netFN; - if (/github/gi.test(engine.name)) { - if (isEmpty(engine.token)) { - showError(`"${engine.name}" requires a token to use`); - continue; - } - netFN = Network.req( - toQuery(`${engine.url}"// ==UserScript=="+${host}+ "// ==/UserScript=="+in:file+language:js&per_page=30`), - 'GET', - 'json', - { - headers: { - Accept: 'application/vnd.github+json', - Authorization: `Bearer ${engine.token}`, - 'X-GitHub-Api-Version': '2022-11-28' - } + }; + let netFN; + if (/github/gi.test(engine.name)) { + if (isEmpty(engine.token)) { + showError(`"${engine.name}" requires a token to use`); + continue; } - ) - .then(gitFN) - .then(() => { - Network.req('https://api.github.com/rate_limit', 'GET', 'json', { + Network.req( + `https://api.github.com/search/repositories?q=topic:${domain}+topic:userstyle`, + 'GET', + 'json', + { headers: { Accept: 'application/vnd.github+json', Authorization: `Bearer ${engine.token}`, 'X-GitHub-Api-Version': '2022-11-28' } - }) - .then((data) => { - for (const [key, value] of Object.entries(data.resources.code_search)) { - const txt = make('mujs-row', 'rate-info', { - textContent: `${key.toUpperCase()}: ${value}` - }); - rateContainer.append(txt); - } - }) - .catch(showError); - }) - .catch(showError); - } else if (/openuserjs/gi.test(engine.name)) { - netFN = Network.req(toQuery(`${engine.url}${host}`), 'GET', 'document') - .then(openuserjs) - .catch((error) => { - showError(`Engine: "${engine.name}"`, error); - }); - } else { - netFN = Network.req(toQuery(`${engine.url}/scripts/by-site/${host}.json?language=all`)) - .then(forkFN) - .catch(showError); - } - if (netFN) { - arr.push(netFN); + } + ) + .then(gitFN) + .catch(respError); + netFN = Network.req( + toQuery( + `https://api.github.com/search/repositories?q=topic:${domain}+topic:userscript` + ), + 'GET', + 'json', + { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' + } + } + ) + .then(gitFN) + .then(() => { + Network.req('https://api.github.com/rate_limit', 'GET', 'json', { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' + } + }) + .then((data) => { + for (const [key, value] of Object.entries(data.resources.code_search)) { + const txt = make('mujs-row', 'rate-info', { + textContent: `${key.toUpperCase()}: ${value}` + }); + container.rateContainer.append(txt); + } + }) + .catch(respError); + }); + } else if (/openuserjs/gi.test(engine.name)) { + netFN = Network.req(toQuery(`${engine.url}${host}`), 'GET', 'document').then( + openuserjs + ); + } else { + netFN = Network.req( + toQuery(`${engine.url}/scripts/by-site/${host}.json?language=all`) + ).then(forkFN); + } + if (netFN) { + fetchRecords.push(netFN.catch(respError)); + } } + } else { + for (const ujs of hostCache) container.tabbody.append(ujs._mujs.root); } - urlBar.placeholder = i18n$('search_placeholder'); - urlBar.value = ''; - Promise.allSettled(arr).then(() => { - tabhead.rows[0].cells[2].dispatchEvent(new MouseEvent('click')); - tabhead.rows[0].cells[2].dispatchEvent(new MouseEvent('click')); - }); + container.urlBar.placeholder = i18n$('search_placeholder'); + container.urlBar.value = ''; + + if (isBlank(fetchRecords)) { + this.sortRecords(); + return; + } + Promise.allSettled(fetchRecords).then(this.sortRecords).catch(showError); } catch (ex) { showError(ex); } } - } + // #endregion + + sortRecords() { + const arr = Array.from(this); + for (const ujs of arr.flat().sort((a, b) => { + const sortType = cfg.autoSort ?? 'daily_installs'; + return b[sortType] - a[sortType]; + })) { + if (isElem(ujs._mujs.root)) container.tabbody.append(ujs._mujs.root); + } + for (const [name, value] of Object.entries(this.groupBy(arr))) + Counter.update(value.length, { name }); + } + + groupBy() { + return _self.groupBy(Array.from(this), ({ _mujs }) => _mujs.info.engine.name); + } + + *[Symbol.iterator]() { + const { host, engines } = this; + const arr = Array.from(container).filter( + ({ _mujs }) => + _mujs.info.host === host && + engines.find((engine) => engine.enabled && engine.name === _mujs.info.engine.name) + ); + for (const userjs of arr) { + yield userjs; + } + } + }; const MUList = new List(); // #endregion // #region Make Config const makecfg = () => { - const makerow = (desc, type = null, nm, attrs = {}) => { - desc = desc ?? i18n$('no_license'); - nm = nm ?? i18n$('no_license'); - const sec = make('mujs-section', '', { + const cbtn = make('mu-js', 'mujs-sty-flex'); + const savebtn = make('mujs-btn', 'save', { + textContent: i18n$('save'), + dataset: { + command: 'save' + }, + disabled: false + }); + const resetbtn = make('mujs-btn', 'reset', { + textContent: i18n$('reset'), + dataset: { + command: 'reset' + } + }); + cbtn.append(resetbtn, savebtn); + + const makesection = (name, tag) => { + tag = tag ?? i18n$('no_license'); + name = name ?? i18n$('no_license'); + const sec = make('mujs-section', { + dataset: { + name: tag + } + }); + const lb = make('label', { dataset: { - name: nm + command: tag } }); - const lb = make('label'); - const divDesc = make('mu-js', '', { - textContent: desc + const divDesc = make('mu-js', { + innerHTML: name + }); + ael(sec, 'click', (evt) => { + /** @type { HTMLElement } */ + const target = evt.target.closest('[data-command]'); + if (!target) { + return; + } + const cmd = target.dataset.command; + if (cmd === tag) { + const a = qsA(`[data-${tag}]`, sec); + if (dom.cl.has(a, 'hidden')) { + dom.cl.remove(a, 'hidden'); + } else { + dom.cl.add(a, 'hidden'); + } + } }); lb.append(divDesc); sec.append(lb); - cfgpage.append(sec); - if (isNull(type)) { + container.cfgpage.append(sec); + if(!cfgSec.has(sec)) cfgSec.add(sec); + return sec; + }; + const sections = { + general: makesection('General', 'general'), + load: makesection('Automation', 'load'), + list: makesection('List', 'list'), + filters: makesection( + `List Filters ${iconSVG.load('info', { dataset: { command: 'more-info', webpage: 'https://greasyfork.org/scripts/12179' } })}`, + 'filters' + ), + blacklist: makesection('Blacklist (WIP)', 'blacklist'), + engine: makesection('Search Engines', 'engine'), + theme: makesection('Theme Colors', 'theme'), + exp: makesection('Import / Export', 'exp') + }; + const makeRow = (text, value, type = 'checkbox', tag = 'general', attrs = {}) => { + const [name, CONFIG, SUB_CONFIG] = /^(\w+)-(.+)/.exec(value) ?? []; + const lb = make('label', 'sub-section hidden', { + dataset: { + [tag]: text + } + }); + const txt = make('mu-js', { + innerHTML: text + }); + lb.append(txt); + const getDefault = () => { + if (tag === 'engine') { + const engine = DEFAULT_CONFIG.engines.find((engine) => engine.name === value); + if (engine) { + return engine; + } + } + if (CONFIG) return DEFAULT_CONFIG[CONFIG][SUB_CONFIG]; + return DEFAULT_CONFIG[value]; + }; + const getValue = () => { + if (tag === 'engine') { + const engine = cfg.engines.find((engine) => engine.name === value); + if (engine) { + return engine; + } + } + if (CONFIG) return cfg[CONFIG][SUB_CONFIG]; + return cfg[value]; + }; + const obj = { + text, + tag, + value, + type, + attrs, + default: getDefault(), + cache: getValue() + }; + if (type === 'select') { + const inp = make('select', { + dataset: { + [tag]: text + }, + ...attrs + }); + for (const selV of Object.keys(template)) { + if (selV === 'deleted' || selV === 'users') continue; + const o = make('option', { + value: selV, + textContent: selV + }); + inp.append(o); + } + inp.value = cfg[value]; + lb.append(inp); + if (sections[tag]) { + sections[tag].append(lb); + } + obj.elem = inp; + cfgBase.push(obj); return lb; } - const inp = make('input', '', { + const inp = make('input', { type, dataset: { - name: nm + [tag]: text }, ...attrs }); - if (!cfgMap.has(nm)) { - cfgMap.set(nm, inp); + + if (tag === 'engine') { + inp.dataset.name = value; + } + + if (sections[tag]) { + sections[tag].append(lb); } + if (type === 'checkbox') { const inlab = make('mu-js', 'mujs-inlab'); - const la = make('label', '', { + const la = make('label', { onclick() { inp.dispatchEvent(new MouseEvent('click')); } }); inlab.append(inp, la); lb.append(inlab); - if (nm.includes('-')) { - return inp; + + if (CONFIG) { + if (CONFIG === 'filters') { + inp.checked = cfg[CONFIG][SUB_CONFIG].enabled; + } else { + inp.checked = cfg[CONFIG][SUB_CONFIG]; + } + } else { + inp.checked = cfg[value]; } - if (/(greasy|sleazy)fork|openuserjs|gi(thub|st)/gi.test(nm)) { - for (const engine of cfg.engines) { - if (engine.name !== nm) continue; + ael(inp, 'change', (evt) => { + container.unsaved = true; + if (/filterlang/i.test(value)) { + container.rebuild = true; + } + if (CONFIG) { + if (CONFIG === 'filters') { + cfg[CONFIG][SUB_CONFIG].enabled = evt.target.checked; + } else { + cfg[CONFIG][SUB_CONFIG] = evt.target.checked; + } + } else { + cfg[value] = evt.target.checked; + } + }); + + if (tag === 'engine') { + const engine = cfg.engines.find((engine) => engine.name === value); + if (engine) { inp.checked = engine.enabled; inp.dataset.engine = engine.name; ael(inp, 'change', (evt) => { container.unsaved = true; container.rebuild = true; engine.enabled = evt.target.checked; + MUList.setEngines(cfg.engines); }); if (engine.query) { - const d = DEFAULT_CONFIG.engines.find(e => e.name === engine.name); - const urlInp = make('input', '', { + const d = DEFAULT_CONFIG.engines.find((e) => e.name === engine.name); + const urlInp = make('input', { type: 'text', defaultValue: '', - value: decodeURIComponent(engine.query) ?? '', - placeholder: decodeURIComponent(d.query) ?? '', + value: decode(engine.query), + placeholder: decode(d.query), dataset: { - name: nm, + name, engine: engine.name }, onchange(evt) { @@ -4615,15 +5361,17 @@ function primaryFN() { container.rebuild = true; try { engine.query = encodeURIComponent(new URL(evt.target.value).toString()); + MUList.setEngines(cfg.engines); } catch (ex) { err(ex); } } }); - sec.append(urlInp); + obj.elemUrl = urlInp; + lb.append(urlInp); } if (engine.name === 'github') { - const ghToken = make('input', '', { + const ghToken = make('input', { type: 'text', defaultValue: '', value: engine.token ?? '', @@ -4635,77 +5383,72 @@ function primaryFN() { container.unsaved = true; container.rebuild = true; engine.token = evt.target.value; + MUList.setEngines(cfg.engines); } }); - sec.append(ghToken); - cfgMap.set('github-token', ghToken); + obj.elemToken = ghToken; + lb.append(ghToken); } } - - } else { - inp.checked = cfg[nm]; - ael(inp, 'change', (evt) => { - container.unsaved = true; - if (/filterlang/i.test(nm)) { - container.rebuild = true; - } - cfg[nm] = evt.target.checked; - }); } } else { + if (type === 'text') { + inp.defaultValue = ''; + inp.value = value ?? ''; + inp.placeholder = value ?? ''; + + if (tag === 'theme') { + inp.dataset[tag] = text; + ael(inp, 'change', (evt) => { + let isvalid = true; + try { + const val = evt.target.value; + const sty = container.root.style; + const str = `--mujs-${text}`; + const prop = sty.getPropertyValue(str); + if (isEmpty(val)) { + cfg.theme[text] = DEFAULT_CONFIG.theme[text]; + sty.removeProperty(str); + return; + } + if (prop === val) { + return; + } + sty.removeProperty(str); + sty.setProperty(str, val); + cfg.theme[text] = val; + } catch (ex) { + err(ex); + isvalid = false; + } finally { + if (isvalid) { + dom.cl.remove(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', false); + } else { + dom.cl.add(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', true); + } + } + }); + } + } + lb.append(inp); } - return inp; + obj.elem = inp; + cfgBase.push(obj); + + return lb; }; if (isGM) { - makerow('Sync with GM', 'checkbox', 'cache'); - } - makerow(i18n$('userjs_fullscreen'), 'checkbox', 'autoexpand', { - onchange(e) { - if (e.target.checked) { - dom.cl.add([btnfullscreen, main], 'expanded'); - dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('fsClose')); - } else { - dom.cl.remove([btnfullscreen, main], 'expanded'); - dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('fsOpen')); - } - } - }); - makerow(i18n$('redirect'), 'checkbox', 'sleazyredirect'); - makerow(i18n$('filter'), 'checkbox', 'filterlang'); - makerow(i18n$('preview_code'), 'checkbox', 'codePreview'); - for (const inp of [ - makerow('Recommend author', 'checkbox', 'recommend-author'), - makerow('Recommend scripts', 'checkbox', 'recommend-others') - ]) { - const nm = inp.dataset.name === 'recommend-author' ? 'author' : 'others'; - inp.checked = cfg.recommend[nm]; - ael(inp, 'change', (evt) => { - container.unsaved = true; - cfg.recommend[nm] = evt.target.checked; - }); + makeRow(i18n$('userjs_sync'), 'cache'); + makeRow(i18n$('userjs_autoinject'), 'autoinject', 'checkbox', 'load'); } - makerow('Greasy Fork', 'checkbox', 'greasyfork'); - makerow('Sleazy Fork', 'checkbox', 'sleazyfork'); - makerow('Open UserJS', 'checkbox', 'openuserjs'); - makerow('GitHub API', 'checkbox', 'github'); - // const ghAPI = cfg.engines.find((c) => c.name === 'github'); - // const ghToken = makerow('GitHub API (Token)', 'text', 'github', { - // defaultValue: '', - // value: ghAPI.token ?? '', - // placeholder: 'Paste Access Token', - // onchange(evt) { - // container.unsaved = true; - // container.rebuild = true; - // ghAPI.token = evt.target.value; - // // if (isNull(ghMsg)) { - // // ghAPI.token = evt.target.value; - // // } - // } - // }); - // ghToken.dataset.engine = 'github-token'; - // cfgMap.set('github-token', ghToken); - makerow(`${i18n$('dtime')} (ms)`, 'number', 'time', { + makeRow( + `${i18n$('redirect')} ${iconSVG.load('info', { dataset: { command: 'more-info', webpage: 'https://greasyfork.org/scripts/23840' } })}`, + 'sleazyredirect' + ); + makeRow(`${i18n$('dtime')} (ms)`, 'time', 'number', 'general', { defaultValue: 10000, value: cfg.time, min: 0, @@ -4732,115 +5475,264 @@ function primaryFN() { } } }); - const cbtn = make('mu-js', 'mujs-sty-flex'); - const savebtn = make('mujs-btn', 'save', { - textContent: i18n$('save'), - dataset: { - command: 'save' - }, - disabled: false - }); - const resetbtn = make('mujs-btn', 'reset', { - textContent: i18n$('reset'), - dataset: { - command: 'reset' + + makeRow(i18n$('auto_fetch'), 'autofetch', 'checkbox', 'load'); + makeRow(i18n$('userjs_fullscreen'), 'autoexpand', 'checkbox', 'load', { + onchange(e) { + if (e.target.checked) { + dom.cl.add([btnfullscreen, main], 'expanded'); + dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('collapse')); + } else { + dom.cl.remove([btnfullscreen, main], 'expanded'); + dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('expand')); + } } }); - const blacklist = make('textarea', '', { - dataset: { - name: 'blacklist' - }, - rows: '10', - autocomplete: false, - spellcheck: false, - wrap: 'soft', - value: JSON.stringify(cfg.blacklist, null, ' '), - oninput(evt) { - let isvalid = true; - try { - cfg.blacklist = JSON.parse(evt.target.value); - isvalid = true; - } catch (ex) { - err(ex); - isvalid = false; - } finally { - if (isvalid) { - dom.cl.remove(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', false); - } else { - dom.cl.add(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', true); + makeRow('Clear on Tab close', 'clearTabCache', 'checkbox', 'load'); + + makeRow(i18n$('default_sort'), 'autoSort', 'select', 'list'); + makeRow(i18n$('filter'), 'filterlang', 'checkbox', 'list'); + makeRow(i18n$('preview_code'), 'preview-code', 'checkbox', 'list'); + makeRow(i18n$('preview_metadata'), 'preview-metadata', 'checkbox', 'list'); + makeRow(i18n$('recommend_author'), 'recommend-author', 'checkbox', 'list'); + makeRow(i18n$('recommend_other'), 'recommend-others', 'checkbox', 'list'); + + for (const [k, v] of Object.entries(cfg.filters)) + makeRow(v.name, `filters-${k}`, 'checkbox', 'filters'); + + makeRow('Greasy Fork', 'greasyfork', 'checkbox', 'engine'); + makeRow('Sleazy Fork', 'sleazyfork', 'checkbox', 'engine'); + makeRow('Open UserJS', 'openuserjs', 'checkbox', 'engine'); + makeRow( + `GitHub API ${iconSVG.load('info', { dataset: { command: 'more-info', webpage: 'https://docs.github.com/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens' } })}`, + 'github', + 'checkbox', + 'engine' + ); + + for (const [k, v] of Object.entries(cfg.theme)) makeRow(k, v, 'text', 'theme'); + + // const blacklist = make('textarea', { + // dataset: { + // name: 'blacklist' + // }, + // rows: '10', + // autocomplete: false, + // spellcheck: false, + // wrap: 'soft', + // value: JSON.stringify(cfg.blacklist, null, ' '), + // oninput(evt) { + // let isvalid = true; + // try { + // cfg.blacklist = JSON.parse(evt.target.value); + // isvalid = true; + // } catch (ex) { + // err(ex); + // isvalid = false; + // } finally { + // if (isvalid) { + // dom.cl.remove(evt.target, 'mujs-invalid'); + // dom.prop(savebtn, 'disabled', false); + // } else { + // dom.cl.add(evt.target, 'mujs-invalid'); + // dom.prop(savebtn, 'disabled', true); + // } + // } + // } + // }); + // const addList = make('mujs-add', { + // textContent: '+', + // dataset: { + // command: 'new-list' + // } + // }); + // const n = make('input', { + // type: 'text', + // defaultValue: '', + // value: '', + // placeholder: 'Name', + // }); + // const inpValue = make('input', { + // type: 'text', + // defaultValue: '', + // value: '', + // placeholder: 'Value', + // }); + // const label = make('label', 'new-list hidden', { + // dataset: { + // blacklist: 'new-list' + // } + // }); + // label.append(n, inpValue, addList); + // listSec.append(label); + // ael(addList, 'click', () => { + // if (isEmpty(n.value) || isEmpty(inpValue.value)) { + // return + // }; + // createList(n.value, n.value, inpValue.value); + // }); + const createList = (key, v = '', disabled = false, type = 'String') => { + let txt = key; + if (typeof key === 'string') { + if (key.startsWith('userjs-')) { + disabled = true; + const s = key.substring(7); + txt = `Built-in "${s}"`; + v = builtinList[s]; + } + } else if (!key.enabled) { + return; + } + + if (isRegExp(v)) { + v = v.toString(); + type = 'RegExp'; + } else { + v = JSON.stringify(v); + type = 'Object'; + } + + const lb = make('label', 'hidden', { + textContent: txt, + dataset: { + blacklist: key + } + }); + const inp = make('input', { + type: 'text', + defaultValue: '', + value: v ?? '', + placeholder: v ?? '', + dataset: { + blacklist: key + }, + onchange(evt) { + let isvalid = true; + try { + const val = evt.target.value; + if (isEmpty(val)) { + return; + } + isvalid = true; + } catch (ex) { + err(ex); + isvalid = false; + } finally { + if (isvalid) { + dom.cl.remove(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', false); + } else { + dom.cl.add(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', true); + } } } + }); + const selType = make('select', { + disabled, + dataset: { + blacklist: key + } + }); + if (disabled) { + inp.readOnly = true; + const o = make('option', { + value: type, + textContent: type + }); + selType.append(o); + } else { + for (const selV of ['String', 'RegExp', 'Object']) { + const o = make('option', { + value: selV, + textContent: selV + }); + selType.append(o); + } } - }); - const theme = make('textarea', '', { - dataset: { - name: 'theme' + selType.value = type; + lb.append(inp, selType); + sections.blacklist.append(lb); + }; + for (const key of cfg.blacklist) createList(key); + const transfers = { + export: { + cfg: make('mujs-btn', 'mujs-export sub-section hidden', { + textContent: i18n$('export_config'), + dataset: { + command: 'export-cfg', + exp: 'export-cfg' + } + }), + theme: make('mujs-btn', 'mujs-export sub-section hidden', { + textContent: i18n$('export_theme'), + dataset: { + command: 'export-theme', + exp: 'export-theme' + } + }) }, - rows: '10', - autocomplete: false, - spellcheck: false, - wrap: 'soft', - value: JSON.stringify(cfg.theme, null, ' '), - oninput(evt) { - let isvalid = true; - try { - cfg.theme = JSON.parse(evt.target.value); - container.renderTheme(JSON.parse(evt.target.value)); - isvalid = true; - } catch (ex) { - err(ex); - isvalid = false; - } finally { - if (isvalid) { - dom.cl.remove(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', false); - } else { - dom.cl.add(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', true); + import: { + cfg: make('mujs-btn', 'mujs-import sub-section hidden', { + textContent: i18n$('import_config'), + dataset: { + command: 'import-cfg', + exp: 'import-cfg' } - } + }), + theme: make('mujs-btn', 'mujs-import sub-section hidden', { + textContent: i18n$('import_theme'), + dataset: { + command: 'import-theme', + exp: 'import-theme' + } + }) } - }); - cfgMap.set('blacklist', blacklist); - cfgMap.set('theme', theme); - cbtn.append(resetbtn, savebtn); - cfgpage.append(blacklist, theme, cbtn); + }; + for (const value of Object.values(transfers)) { + for (const v of Object.values(value)) { + sections.exp.append(v); + } + } + container.cfgpage.append(cbtn); }; // #endregion - container.tab.custom = (host) => { - MUList.host = host; - MUList.build(); + container.Tabs.custom = (host) => { + MUList.setHost(host); + respHandles.build(); }; ael(mainframe, 'mouseenter', (evt) => { evt.preventDefault(); evt.stopPropagation(); + if (isNull(evt.target)) return; evt.target.style.opacity = container.opacityMax; frameTimeout.clear(...frameTimeout.ids); }); ael(mainframe, 'mouseleave', (evt) => { evt.preventDefault(); evt.stopPropagation(); + if (isNull(evt.target)) return; evt.target.style.opacity = container.opacityMin; - timeoutFrame(); + container.timeoutFrame(); }); + let initClick = true; ael(mainframe, 'click', (evt) => { evt.preventDefault(); frameTimeout.clear(...frameTimeout.ids); + if (initClick && !cfg.autofetch) { + initClick = false; + respHandles.build(); + } dom.cl.remove(main, 'hidden'); dom.cl.add(mainframe, 'hidden'); if (cfg.autoexpand) { - dom.cl.add([btnfullscreen, main], 'expanded'); - dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('fsClose')); - } - if (dom.cl.has(mainframe, 'error')) { - tab.create('mujs:settings'); + dom.cl.add([container.btnfullscreen, main], 'expanded'); + dom.prop(container.btnfullscreen, 'innerHTML', iconSVG.load('collapse')); } }); - ael(urlBar, 'input', (evt) => { + ael(container.urlBar, 'input', (evt) => { evt.preventDefault(); - if (urlBar.placeholder === i18n$('newTab')) { + if (container.urlBar.placeholder === i18n$('newTab')) { return; } /** @@ -4848,28 +5740,24 @@ function primaryFN() { */ const val = evt.target.value; if (isEmpty(val)) { - dom.cl.remove(qsA('tr[data-engine]', tabbody), 'hidden'); - dom.cl.remove(qsA('mujs-section[data-name]', cfgpage), 'hidden'); + dom.cl.remove([...container.toElem(), ...cfgSec], 'hidden'); return; } const finds = new Set(); - if (!dom.cl.has(cfgpage, 'hidden')) { + if (!dom.cl.has(container.cfgpage, 'hidden')) { const reg = new RegExp(val, 'gi'); - for (const elem of qsA('mujs-section[data-name]', cfgpage)) { - if (!elem) { - continue; - } - if (finds.has(elem)) { - continue; - } - if (elem.dataset.name.match(reg)) { - finds.add(elem); - } + for (const elem of cfgSec) { + if (!isElem(elem)) continue; + if (finds.has(elem)) continue; + if (elem.textContent.match(reg)) finds.add(elem); } - dom.cl.add(qsA('mujs-section[data-name]', cfgpage), 'hidden'); + dom.cl.add(cfgSec, 'hidden'); dom.cl.remove([...finds], 'hidden'); return; } + const cacheValues = Array.from(container).filter(({ _mujs }) => { + return !finds.has(_mujs.root); + }); /** * @param {RegExpMatchArray} regExp * @param {keyof import("../typings/types.d.ts").GSForkQuery} key @@ -4877,43 +5765,25 @@ function primaryFN() { const ezQuery = (regExp, key) => { const q_value = val.replace(regExp, ''); const reg = new RegExp(q_value, 'gi'); - for (const v of userjsCache.values()) { - const elem = v._mujs.root; - if (!elem) { - continue; - } - if (finds.has(elem)) { - continue; - } - let k = v[key]; + for (const v of cacheValues) { if (typeof k === 'number') { - k = `${v[key]}`; - } - if (k && k.match(reg)) { - finds.add(elem); + if (`${v[key]}`.match(reg)) finds.add(v._mujs.root); } } }; if (val.match(/^(code_url|url):/)) { ezQuery(/^(code_url|url):/, 'code_url'); } else if (val.match(/^(author|users?):/)) { - const q_value = val.replace(/^(author|users?):/, ''); - const reg = new RegExp(q_value, 'gi'); - for (const v of userjsCache.values()) { - const elem = v._mujs.root; - if (!elem) { - continue; - } - if (finds.has(elem)) { - continue; - } - if (v.users) { + const [, parts] = /^[\w_]+:(.+)/.exec(val) ?? []; + if (parts) { + const reg = new RegExp(parts, 'gi'); + for (const v of cacheValues.filter((v) => !isEmpty(v.users))) { for (const user of v.users) { for (const value of Object.values(user)) { if (typeof value === 'string' && value.match(reg)) { - finds.add(elem); + finds.add(v._mujs.root); } else if (typeof value === 'number' && `${value}`.match(reg)) { - finds.add(elem); + finds.add(v._mujs.root); } } } @@ -4930,117 +5800,120 @@ function primaryFN() { } else if (val.match(/^description:/)) { ezQuery(/^description:/, 'description'); } else if (val.match(/^(search_engine|engine):/)) { - const q_value = val.replace(/^(search_engine|engine):/, ''); - const reg = new RegExp(q_value, 'gi'); - for (const v of userjsCache.values()) { - const elem = v._mujs.root; - if (!elem) { - continue; - } - if (finds.has(elem)) { - continue; - } - if (v._mujs.info.engine) { - for (const value of Object.values(v._mujs.info.engine)) { - if (typeof value === 'string' && value.match(reg)) { - finds.add(elem); - } + const [, parts] = /^[\w_]+:(\w+)/.exec(val) ?? []; + if (parts) { + const reg = new RegExp(parts, 'gi'); + for (const { _mujs } of cacheValues) + if (_mujs.info.engine.name.match(reg)) finds.add(_mujs.root); + } + } else if (val.match(/^filter:/)) { + const [, parts] = /^\w+:(.+)/.exec(val) ?? []; + if (parts) { + const bsFilter = loadFilters(); + const filterType = bsFilter.get(parts.trim().toLocaleLowerCase()); + if (filterType) { + const { reg } = filterType; + for (const { name, users, _mujs } of cacheValues) { + if ([{ name }, ...users].find((o) => o.name.match(reg))) continue; + finds.add(_mujs.root); } } } + } else if (val.match(/^recommend:/)) { + for (const { url, id, users, _mujs } of cacheValues) { + if ( + users.find((u) => u.id === authorID) || + goodUserJS.includes(url) || + goodUserJS.includes(id) + ) { + finds.add(_mujs.root); + } + } } else { const reg = new RegExp(val, 'gi'); - for (const v of userjsCache.values()) { - const elem = v._mujs.root; - if (!elem) { - continue; - } - if (finds.has(elem)) { - continue; - } - if (v.name && v.name.match(reg)) { - finds.add(elem); - } - if (v.description && v.description.match(reg)) { - finds.add(elem); - } - const code_data = v._mujs.code.data; - if (code_data) { - const code_obj = new ParseUserJS(code_data); - const meta = code_obj.parse_meta(code_data); - for (const key of Object.keys(meta)) { - if (/name|desc/i.test(key) && key.match(reg)) { - finds.add(elem); - } - } - } + for (const v of cacheValues) { + if (v.name && v.name.match(reg)) finds.add(v._mujs.root); + if (v.description && v.description.match(reg)) finds.add(v._mujs.root); + if (v._mujs.code.data_meta) + for (const key of Object.keys(v._mujs.code.data_meta)) + if (/name|desc/i.test(key) && key.match(reg)) finds.add(v._mujs.root); } } - dom.cl.add(qsA('tr[data-engine]', tabbody), 'hidden'); + dom.cl.add(container.toElem(), 'hidden'); dom.cl.remove([...finds], 'hidden'); }); - ael(urlBar, 'change', (evt) => { + ael(container.urlBar, 'change', (evt) => { evt.preventDefault(); - const val = evt.target.value; - if (urlBar.placeholder === i18n$('newTab') && qs('mujs-tab.active', toolbar)) { - const tabElem = qs('mujs-tab.active', toolbar); - const tabHost = qs('mujs-host', tabElem); - if (val.startsWith(tab.protocal)) { - tab.close(tabElem); - if (tab.hasTab(val)) { - tab.active(tab.Tab.get(val)); + if (isNull(evt.target)) return; + const { target } = evt; + const tabElem = Tabs.getActive(); + if (container.urlBar.placeholder === i18n$('newTab') && tabElem) { + const tabHost = tabElem.firstElementChild; + const host = formatURL(normalizedHostname(target.value)); + if (Tabs.protoReg.test(target.value)) { + const createdTab = Tabs.getTab(target.value); + Tabs.close(tabElem); + if (createdTab) { + Tabs.active(createdTab); } else { - tab.create(val); + Tabs.create(target.value); } - return; - } else if (val === '*') { - tabElem.dataset.host = val; + target.placeholder = i18n$('search_placeholder'); + target.value = ''; + } else if (host === '*') { + tabElem.dataset.host = host; tabHost.title = ''; tabHost.textContent = ''; - MUList.host = val; - MUList.build(); - return; - } - const value = container.getHost(val); - if (container.checkBlacklist(value)) { - showError(`Blacklisted "${value}"`); - return; + MUList.setHost(host); + respHandles.build(); + } else if (container.checkBlacklist(host)) { + showError(`Blacklisted "${host}"`); + } else { + tabElem.dataset.host = host; + tabHost.title = host; + tabHost.textContent = host; + MUList.setHost(host); + respHandles.build(); } - tabElem.dataset.host = value; - tabHost.title = value; - tabHost.textContent = value; - MUList.host = value; - MUList.build(); - return; } }); - makecfg(); - MUList.build().then(timeoutFrame); - dbg('Container', container); + scheduler.postTask(makecfg, { priority: 'background' }); + + respHandles.build = async () => { + await scheduler.postTask(MUList.build, { priority: 'background' }); + container.timeoutFrame(); + }; + + if (cfg.autofetch) { + respHandles.build(); + } else { + container.timeoutFrame(); + } } catch (ex) { err(ex); container.remove(); } + return respHandles; } // #endregion /** - * @template { Function } F - * @param { (this: F, doc: Document) => any } onDomReady + * @template F + * @param { (this: F, doc: Document) => * } onDomReady */ const loadDOM = (onDomReady) => { - if (!isFN(onDomReady)) { - return; - } - if (document.readyState === 'interactive' || document.readyState === 'complete') { - onDomReady.call({}, document); + if (isFN(onDomReady)) { + if (document.readyState === 'interactive' || document.readyState === 'complete') { + onDomReady(document); + } else { + document.addEventListener('DOMContentLoaded', (evt) => onDomReady(evt.target), { + once: true + }); + } } - document.addEventListener('DOMContentLoaded', (evt) => onDomReady.call({}, evt.target), { - once: true - }); }; -const init = async () => { - const stored = await StorageSystem.getValue('Config', DEFAULT_CONFIG); +const init = async (prefix = 'Config') => { + const stored = await StorageSystem.getValue(prefix, DEFAULT_CONFIG); cfg = { ...DEFAULT_CONFIG, ...stored @@ -5048,19 +5921,20 @@ const init = async () => { info('Config:', cfg); loadDOM((doc) => { try { - if (window.location === null) { - throw new Error('"window.location" is null, reload the webpage or use a different one'); - } - if (doc === null) { - throw new Error('"doc" is null, reload the webpage or use a different one'); - } - if (window.trustedTypes && window.trustedTypes.createPolicy) { - window.trustedTypes.createPolicy('default', { - createHTML: (string) => string + if (window.location === null) + throw new Error('"window.location" is null, reload the webpage or use a different one', { + cause: 'loadDOM' + }); + if (doc === null) + throw new Error('"doc" is null, reload the webpage or use a different one', { + cause: 'loadDOM' }); + container.redirect(); + if (cfg.autoinject) { + container.inject(primaryFN, doc); + } else { + container.timeoutFrame(); } - sleazyRedirect(); - container.inject(primaryFN, doc); Command.register(i18n$('userjs_inject'), () => { container.inject(primaryFN, doc); }); @@ -5074,4 +5948,4 @@ const init = async () => { }; init(); -})(); \ No newline at end of file +})(); diff --git a/dist/userscript_plus-5.0.0.xpi b/dist/userscript_plus-5.0.0.xpi deleted file mode 100644 index eec2c4a..0000000 Binary files a/dist/userscript_plus-5.0.0.xpi and /dev/null differ diff --git a/eslint.config.js b/eslint.config.js index 7c77b70..85de384 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,94 +1,69 @@ -import js from '@eslint/js'; -import globals from 'globals'; -import eslintConfigPrettier from 'eslint-config-prettier'; +import { defineConfig } from 'eslint/config'; -const userJSGlobals = { - code: 'readonly', - metadata: 'readonly', - main_css: 'readonly', - languageList: 'readonly', - translations: 'readonly', - userjs: 'writable', - ...globals.es2023, - ...globals.browser, - ...globals.greasemonkey -}; -const webextGlobals = { - MU: 'writable', - sleazyfork_redirect: 'readonly', - webext: 'readonly', - brws: 'readonly', - userjs: 'writable', - ...globals.es2023, - ...globals.browser, - ...globals.webextensions -}; -const parserOptions = { - allowImportExportEverywhere: false, - ecmaFeatures: { - globalReturn: true, - arrowFunctions: true, - modules: true - } -}; -const rules = { - 'keyword-spacing': ['error', { before: true }], - 'no-var': 'error', - 'prefer-const': ['error', { destructuring: 'all' }], - 'prefer-promise-reject-errors': 'error', - 'prefer-regex-literals': ['error', { disallowRedundantWrapping: true }], - quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: false }], - 'space-before-blocks': ['error', 'always'] -}; +import globals from 'globals'; +import pluginJs from '@eslint/js'; +import eslintConfigPrettier from 'eslint-config-prettier/flat'; -export default [ - js.configs.recommended, +export default defineConfig([ + pluginJs.configs.recommended, eslintConfigPrettier, { - files: ['src/js/*.js'], + rules: { + 'no-var': 'error', + 'prefer-const': ['error', { destructuring: 'all' }], + 'prefer-promise-reject-errors': 'error', + 'prefer-regex-literals': ['error', { disallowRedundantWrapping: true }], + quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: false }], + 'space-before-blocks': ['error', 'always'] + }, languageOptions: { ecmaVersion: 'latest', sourceType: 'module', - globals: webextGlobals, - parserOptions - }, - rules + globals: globals.es2024 + } + }, + { + files: ['src/js/*.js'], + languageOptions: { + globals: { + ...globals.browser, + ...globals.webextensions + } + } }, { files: ['src/UserJS/main.js'], languageOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - globals: userJSGlobals, - parserOptions - }, - rules + sourceType: 'script', + globals: { + main_css: 'readonly', + translations: 'writable', + userjs: 'writable', + ...globals.browser, + ...globals.greasemonkey + } + } }, { files: ['src/UserJS/header.js'], languageOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - globals: userJSGlobals, - parserOptions + sourceType: 'script', + globals: { + code: 'readonly', + metadata: 'readonly', + languageList: 'readonly', + ...globals.browser + } }, rules: { - ...rules, quotes: 'off', 'no-unused-vars': 'off' } }, { - files: ['tools/*.js'], + files: ['tools/*.js', 'utils/**/*.js'], languageOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - globals: { - ...globals.es2021, - ...globals.node - }, - parserOptions - }, - rules + globals: globals.node + } } -]; +]); diff --git a/package.json b/package.json index e1429c5..11fdcd9 100644 --- a/package.json +++ b/package.json @@ -2,140 +2,58 @@ "name": "userscript-plus", "description": "Finds available userscripts for the current webpage.", "author": "Magic ", - "version": "5.0.2", + "version": "6.0.0", "license": "MIT", "homepage": "https://github.com/magicoflolis/Userscript-Plus", "bugs": { "url": "https://github.com/magicoflolis/Userscript-Plus/issues" }, - "userJS": { - "version": "7.0.0", - "name": "Magic Userscript+ : Show Site All UserJS", - "description": "Finds available userscripts for the current webpage.", - "bugs": "https://github.com/magicoflolis/Userscript-Plus/issues", - "homepage": "https://github.com/magicoflolis/Userscript-Plus", - "icon": "./src/img/icon_64.png", - "downloadURL": "https://github.com/magicoflolis/Userscript-Plus/raw/master/dist/magic-userjs.user.js", - "updateURL": "https://github.com/magicoflolis/Userscript-Plus/raw/master/dist/magic-userjs.meta.js", - "build": { - "source": { - "head": "./src/UserJS/header.js", - "body": "./src/UserJS/main.js", - "extras": { - "mainCSS": "./build/css/magicuserjs.css" - } - }, - "watch": { - "files": [ - "main.js", - "header.js", - "magicuserjs.css" - ], - "dirs": [ - "src/UserJS", - "build/css" - ] - }, - "paths": { - "dev": { - "fileName": "magic-userjs.dev", - "env": "./src/UserJS/.env", - "dir": "./web-server" - }, - "public": { - "fileName": "magic-userjs", - "env": "./dist/.env", - "dir": "./dist" - } - } - }, - "metadata": { - "compatible": [ - "chrome", - "firefox", - "edge", - "opera", - "safari" - ], - "connect": [ - "greasyfork.org", - "sleazyfork.org", - "github.com", - "openuserjs.org" - ], - "grant": [ - "GM_addElement", - "GM_info", - "GM_getValue", - "GM_openInTab", - "GM_setValue", - "GM_registerMenuCommand", - "GM_xmlhttpRequest", - "GM.addElement", - "GM.info", - "GM.getValue", - "GM.openInTab", - "GM.setValue", - "GM.registerMenuCommand", - "GM.xmlHttpRequest" - ], - "exclude": [], - "include": [], - "exclude-match": [], - "match": [ - "https://*/*" - ], - "noframes": true, - "resource": {}, - "require": [], - "run-at": "document-start" - } - }, "type": "module", "scripts": { - "dev:UserJS": "concurrently \"node ./tools/userscript.js\" \"pnpm run dev:Webserver\" \"sass --no-source-map -s expanded -w ./src/sass:./build/css\" ", + "dev:UserJS": "concurrently \"dotenvx run -f .env --quiet -- node ./tools/userscript.js\" \"http-server ./web-server -p 9090 -s --no-dotfiles -c-1\"", "dev:FF": "concurrently \"sass --embed-sources -s expanded -w ./src/sass:./build/firefox/css\" \"pnpm run webpack:FF\"", "dev:Cr": "concurrently \"sass --embed-sources -s expanded -w ./src/sass:./build/chrome/css\" \"pnpm run webpack:Cr\"", - "dev:Webserver": "http-server ./web-server -p 9090 -s --no-dotfiles -c-1", - "pub:UserJS": "concurrently \"sass --no-source-map -s expanded ./src/sass:./build/css\" \"node -r dotenv/config ./tools/userscript.js dotenv_config_path=./dist/.env\"", - "pub:FF": "webpack --mode production --config=tools/webpack.config.js --env brws=firefox", - "pub:Cr": "webpack --mode production --config=tools/webpack.config.js --env brws=chrome", + "pub:UserJS": "dotenvx run --env JS_ENV=production -f .env --quiet -- node ./tools/userscript.js", "webpack:Cr": "webpack --progress --mode development --config=tools/webpack.config.js --env brws=chrome", "webpack:FF": "webpack --progress --mode development --config=tools/webpack.config.js --env brws=firefox", - "web-run:firefox-android": "web-ext run --config=./tools/web-ext.js -t firefox-android --firefox-apk org.mozilla.firefox" + "web-ext:run": "web-ext run --config=./tools/web-ext.mjs", + "web-ext:android": "web-ext run --config=./tools/web-ext.mjs -t firefox-android --firefox-apk org.mozilla.firefox" }, "devDependencies": { - "@swc/cli": "^0.4.0", - "@swc/core": "^1.7.39", - "@types/chrome": "0.0.279", + "@dotenvx/dotenvx": "^1.39.0", + "@eslint/js": "^9.22.0", + "@swc/cli": "^0.6.0", + "@swc/core": "^1.11.11", + "@types/chrome": "0.0.309", + "@types/eslint-config-prettier": "^6.11.3", "@types/firefox-webext-browser": "120.0.4", "@types/greasemonkey": "4.0.7", - "@types/node": "^22.7.9", - "@types/tampermonkey": "^5.0.3", + "@types/node": "^22.13.10", + "@types/tampermonkey": "^5.0.4", + "@types/trusted-types": "^2.0.7", "@types/webpack": "^5.28.5", "@violentmonkey/types": "0.2.0", - "browserslist": "^4.24.2", - "concurrently": "^9.0.1", - "copy-webpack-plugin": "^12.0.2", + "browserslist": "^4.24.4", + "concurrently": "^9.1.2", + "copy-webpack-plugin": "^13.0.0", "crx3": "^1.1.3", "css-loader": "^7.1.2", - "dotenv": "^16.4.5", - "eslint": "^9.13.0", - "eslint-config-prettier": "^9.1.0", - "globals": "^15.11.0", + "eslint": "^9.22.0", + "eslint-config-prettier": "^10.1.1", + "globals": "^16.0.0", "http-server": "^14.1.1", - "mini-css-extract-plugin": "^2.9.1", + "mini-css-extract-plugin": "^2.9.2", "path-browserify": "^1.0.1", - "prettier": "^3.3.3", - "sass-embedded": "^1.80.4", - "sass-loader": "^16.0.2", + "prettier": "^3.5.3", + "sass-embedded": "^1.86.0", + "sass-loader": "^16.0.5", "swc-loader": "^0.2.6", - "terser-webpack-plugin": "^5.3.10", - "typescript": "^5.6.3", - "watchpack": "^2.4.2", - "web-ext": "^8.3.0", - "webpack": "^5.95.0", - "webpack-cli": "^5.1.4", + "terser-webpack-plugin": "^5.3.14", + "typescript": "^5.8.2", + "user.js": "workspace:^", + "web-ext": "^8.5.0", + "webpack": "^5.98.0", + "webpack-cli": "^6.0.1", "webpack-merge": "^6.0.1" }, "repository": { @@ -153,14 +71,21 @@ "violentmonkey" ], "webExt": { - "sourceDir": "./build/firefox/", - "artifactsDir": "./build" + "artifactsDir": "./build", + "build": { + "overwriteDest": true + }, + "sourceDir": "./build/firefox" }, - "browserslist": [ - "defaults" - ], "engines": { - "node": ">=20" + "node": ">=20", + "pnpm": ">=10" }, - "packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4" + "packageManager": "pnpm@10.3.0+sha512.ee592eda8815a8a293c206bb0917c4bb0ff274c50def7cbc17be05ec641fc2d1b02490ce660061356bd0d126a4d7eb2ec8830e6959fb8a447571c631d5a2442d", + "pnpm": { + "onlyBuiltDependencies": [ + "@swc/core", + "spawn-sync" + ] + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 449d7c6..d17a679 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,15 +8,24 @@ importers: .: devDependencies: + '@dotenvx/dotenvx': + specifier: ^1.39.0 + version: 1.39.0 + '@eslint/js': + specifier: ^9.22.0 + version: 9.22.0 '@swc/cli': - specifier: ^0.4.0 - version: 0.4.0(@swc/core@1.7.39) + specifier: ^0.6.0 + version: 0.6.0(@swc/core@1.11.11) '@swc/core': - specifier: ^1.7.39 - version: 1.7.39 + specifier: ^1.11.11 + version: 1.11.11 '@types/chrome': - specifier: 0.0.279 - version: 0.0.279 + specifier: 0.0.309 + version: 0.0.309 + '@types/eslint-config-prettier': + specifier: ^6.11.3 + version: 6.11.3 '@types/firefox-webext-browser': specifier: 120.0.4 version: 120.0.4 @@ -24,87 +33,113 @@ importers: specifier: 4.0.7 version: 4.0.7 '@types/node': - specifier: ^22.7.9 - version: 22.7.9 + specifier: ^22.13.10 + version: 22.13.10 '@types/tampermonkey': - specifier: ^5.0.3 - version: 5.0.3 + specifier: ^5.0.4 + version: 5.0.4 + '@types/trusted-types': + specifier: ^2.0.7 + version: 2.0.7 '@types/webpack': specifier: ^5.28.5 - version: 5.28.5(@swc/core@1.7.39)(webpack-cli@5.1.4) + version: 5.28.5(@swc/core@1.11.11)(webpack-cli@6.0.1) '@violentmonkey/types': specifier: 0.2.0 version: 0.2.0 browserslist: - specifier: ^4.24.2 - version: 4.24.2 + specifier: ^4.24.4 + version: 4.24.4 concurrently: - specifier: ^9.0.1 - version: 9.0.1 + specifier: ^9.1.2 + version: 9.1.2 copy-webpack-plugin: - specifier: ^12.0.2 - version: 12.0.2(webpack@5.95.0) + specifier: ^13.0.0 + version: 13.0.0(webpack@5.98.0) crx3: specifier: ^1.1.3 version: 1.1.3 css-loader: specifier: ^7.1.2 - version: 7.1.2(webpack@5.95.0) - dotenv: - specifier: ^16.4.5 - version: 16.4.5 + version: 7.1.2(webpack@5.98.0) eslint: - specifier: ^9.13.0 - version: 9.13.0 + specifier: ^9.22.0 + version: 9.22.0 eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@9.13.0) + specifier: ^10.1.1 + version: 10.1.1(eslint@9.22.0) globals: - specifier: ^15.11.0 - version: 15.11.0 + specifier: ^16.0.0 + version: 16.0.0 http-server: specifier: ^14.1.1 version: 14.1.1 mini-css-extract-plugin: - specifier: ^2.9.1 - version: 2.9.1(webpack@5.95.0) + specifier: ^2.9.2 + version: 2.9.2(webpack@5.98.0) path-browserify: specifier: ^1.0.1 version: 1.0.1 prettier: - specifier: ^3.3.3 - version: 3.3.3 + specifier: ^3.5.3 + version: 3.5.3 sass-embedded: - specifier: ^1.80.4 - version: 1.80.4 + specifier: ^1.86.0 + version: 1.86.0 sass-loader: - specifier: ^16.0.2 - version: 16.0.2(sass-embedded@1.80.4)(webpack@5.95.0) + specifier: ^16.0.5 + version: 16.0.5(sass-embedded@1.86.0)(webpack@5.98.0) swc-loader: specifier: ^0.2.6 - version: 0.2.6(@swc/core@1.7.39)(webpack@5.95.0) + version: 0.2.6(@swc/core@1.11.11)(webpack@5.98.0) terser-webpack-plugin: - specifier: ^5.3.10 - version: 5.3.10(@swc/core@1.7.39)(webpack@5.95.0) + specifier: ^5.3.14 + version: 5.3.14(@swc/core@1.11.11)(webpack@5.98.0) typescript: - specifier: ^5.6.3 - version: 5.6.3 - watchpack: - specifier: ^2.4.2 - version: 2.4.2 + specifier: ^5.8.2 + version: 5.8.2 + user.js: + specifier: workspace:^ + version: link:utils/user.js web-ext: - specifier: ^8.3.0 - version: 8.3.0 + specifier: ^8.5.0 + version: 8.5.0 webpack: - specifier: ^5.95.0 - version: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) + specifier: ^5.98.0 + version: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) webpack-cli: - specifier: ^5.1.4 - version: 5.1.4(webpack@5.95.0) + specifier: ^6.0.1 + version: 6.0.1(webpack@5.98.0) webpack-merge: specifier: ^6.0.1 version: 6.0.1 + utils/builder: + dependencies: + '@userjs/i18n': + specifier: workspace:^ + version: link:../i18n + minimist: + specifier: ^1.2.8 + version: 1.2.8 + sass-embedded: + specifier: ^1.85.1 + version: 1.85.1 + watchpack: + specifier: ^2.4.2 + version: 2.4.2 + + utils/i18n: {} + + utils/user.js: + dependencies: + '@userjs/builder': + specifier: workspace:^ + version: link:../builder + '@userjs/i18n': + specifier: workspace:^ + version: link:../i18n + packages: '@aashutoshrathi/word-wrap@1.2.6': @@ -123,8 +158,8 @@ packages: resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.25.6': - resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} + '@babel/runtime@7.26.10': + resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} engines: {node: '>=6.9.0'} '@bufbuild/protobuf@2.2.0': @@ -138,14 +173,24 @@ packages: resolution: {integrity: sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg==} engines: {node: '>= 0.10.4'} - '@devicefarmer/adbkit@3.2.6': - resolution: {integrity: sha512-8lO1hSeTgtxcOHhp4tTWq/JaOysp5KNbbyFoxNEBnwkCDZu/Bji3ZfOaG++Riv9jN6c9bgdLBOZqJTC5VJPRKQ==} + '@devicefarmer/adbkit@3.3.8': + resolution: {integrity: sha512-7rBLLzWQnBwutH2WZ0EWUkQdihqrnLYCUMaB44hSol9e0/cdIhuNFcqZO0xNheAU6qqHVA8sMiLofkYTgb+lmw==} engines: {node: '>= 0.10.4'} hasBin: true - '@discoveryjs/json-ext@0.5.7': - resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} - engines: {node: '>=10.0.0'} + '@discoveryjs/json-ext@0.6.3': + resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} + engines: {node: '>=14.17.0'} + + '@dotenvx/dotenvx@1.39.0': + resolution: {integrity: sha512-qGfDpL/3S17MQYXpR3HkBS5xNQ7wiFlqLdpr+iIQzv17aMRcSlgL4EjMIsYFZ540Dq17J+y5FVElA1AkVoXiUA==} + hasBin: true + + '@ecies/ciphers@0.2.2': + resolution: {integrity: sha512-ylfGR7PyTd+Rm2PqQowG08BCKA22QuX8NzrL+LxAAvazN10DMwdJ2fWwAzRj05FI/M8vNFGm3cv9Wq/GFWCBLg==} + engines: {bun: '>=1', deno: '>=2', node: '>=16'} + peerDependencies: + '@noble/ciphers': ^1.0.0 '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} @@ -153,52 +198,60 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.11.1': - resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==} + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.18.0': - resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} + '@eslint/config-array@0.19.2': + resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.7.0': - resolution: {integrity: sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==} + '@eslint/config-helpers@0.1.0': + resolution: {integrity: sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.12.0': + resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@2.1.4': resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/eslintrc@3.1.0': - resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + '@eslint/eslintrc@3.3.0': + resolution: {integrity: sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@8.57.1': resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/js@9.13.0': - resolution: {integrity: sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==} + '@eslint/js@9.22.0': + resolution: {integrity: sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.4': - resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.0': - resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==} + '@eslint/plugin-kit@0.2.7': + resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@fluent/syntax@0.19.0': resolution: {integrity: sha512-5D2qVpZrgpjtqU4eNOcWGp1gnUCgjfM+vKGE2y03kKN6z5EBhtx0qdRFbg8QuNNj8wXNoX93KJoYb+NqoxswmQ==} engines: {node: '>=14.0.0', npm: '>=7.0.0'} - '@humanfs/core@0.19.0': - resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==} + '@fregante/relaxed-json@2.0.0': + resolution: {integrity: sha512-PyUXQWB42s4jBli435TDiYuVsadwRHnMc27YaLouINktvTWsL3FcKrRMGawTayFk46X+n5bE23RjUTWQwrukWw==} + engines: {node: '>= 0.10.0'} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.5': - resolution: {integrity: sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==} + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} engines: {node: '>=18.18.0'} '@humanwhocodes/config-array@0.13.0': @@ -218,6 +271,10 @@ packages: resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} engines: {node: '>=18.18'} + '@humanwhocodes/retry@0.4.2': + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} + engines: {node: '>=18.18'} + '@jridgewell/gen-mapping@0.3.2': resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} engines: {node: '>=6.0.0'} @@ -236,15 +293,23 @@ packages: '@jridgewell/sourcemap-codec@1.4.14': resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - '@jridgewell/trace-mapping@0.3.22': - resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@mdn/browser-compat-data@5.6.0': - resolution: {integrity: sha512-xArvLyzuk0r2m6hFVjTMYoLvhWwys3h7W8pO15tjSAea+U39cErWDNfoUs4g2C08HVg6bDHyDMBc0LC6FKRpVw==} + '@mdn/browser-compat-data@5.7.3': + resolution: {integrity: sha512-ckygcngv0i7Qe0yOzzge/K7Gr5dnk2jNm/AYdqUd1ZTGa9pIEdDuVyWmL3bDU/NdJ8FtdSAjng98YfUuou9Csw==} - '@mole-inc/bin-wrapper@8.0.1': - resolution: {integrity: sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + '@noble/ciphers@1.2.1': + resolution: {integrity: sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.8.1': + resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + engines: {node: ^14.21.3 || >=16} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -270,87 +335,86 @@ packages: resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} engines: {node: '>=12'} - '@sindresorhus/is@4.6.0': - resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} - engines: {node: '>=10'} + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@sindresorhus/merge-streams@2.3.0': - resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} - engines: {node: '>=18'} + '@sindresorhus/is@5.6.0': + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} - '@swc/cli@0.4.0': - resolution: {integrity: sha512-4JdVrPtF/4rCMXp6Q1h5I6YkYZrCCcqod7Wk97ZQq7K8vNGzJUryBv4eHCvqx5sJOJBrbYm9fcswe1B0TygNoA==} + '@swc/cli@0.6.0': + resolution: {integrity: sha512-Q5FsI3Cw0fGMXhmsg7c08i4EmXCrcl+WnAxb6LYOLHw4JFFC3yzmx9LaXZ7QMbA+JZXbigU2TirI7RAfO0Qlnw==} engines: {node: '>= 16.14.0'} hasBin: true peerDependencies: '@swc/core': ^1.2.66 - chokidar: ^3.5.1 + chokidar: ^4.0.1 peerDependenciesMeta: chokidar: optional: true - '@swc/core-darwin-arm64@1.7.39': - resolution: {integrity: sha512-o2nbEL6scMBMCTvY9OnbyVXtepLuNbdblV9oNJEFia5v5eGj9WMrnRQiylH3Wp/G2NYkW7V1/ZVW+kfvIeYe9A==} + '@swc/core-darwin-arm64@1.11.11': + resolution: {integrity: sha512-vJcjGVDB8cZH7zyOkC0AfpFYI/7GHKG0NSsH3tpuKrmoAXJyCYspKPGid7FT53EAlWreN7+Pew+bukYf5j+Fmg==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.7.39': - resolution: {integrity: sha512-qMlv3XPgtPi/Fe11VhiPDHSLiYYk2dFYl747oGsHZPq+6tIdDQjIhijXPcsUHIXYDyG7lNpODPL8cP/X1sc9MA==} + '@swc/core-darwin-x64@1.11.11': + resolution: {integrity: sha512-/N4dGdqEYvD48mCF3QBSycAbbQd3yoZ2YHSzYesQf8usNc2YpIhYqEH3sql02UsxTjEFOJSf1bxZABDdhbSl6A==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.7.39': - resolution: {integrity: sha512-NP+JIkBs1ZKnpa3Lk2W1kBJMwHfNOxCUJXuTa2ckjFsuZ8OUu2gwdeLFkTHbR43dxGwH5UzSmuGocXeMowra/Q==} + '@swc/core-linux-arm-gnueabihf@1.11.11': + resolution: {integrity: sha512-hsBhKK+wVXdN3x9MrL5GW0yT8o9GxteE5zHAI2HJjRQel3HtW7m5Nvwaq+q8rwMf4YQRd8ydbvwl4iUOZx7i2Q==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.7.39': - resolution: {integrity: sha512-cPc+/HehyHyHcvAsk3ML/9wYcpWVIWax3YBaA+ScecJpSE04l/oBHPfdqKUPslqZ+Gcw0OWnIBGJT/fBZW2ayw==} + '@swc/core-linux-arm64-gnu@1.11.11': + resolution: {integrity: sha512-YOCdxsqbnn/HMPCNM6nrXUpSndLXMUssGTtzT7ffXqr7WuzRg2e170FVDVQFIkb08E7Ku5uOnnUVAChAJQbMOQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.7.39': - resolution: {integrity: sha512-8RxgBC6ubFem66bk9XJ0vclu3exJ6eD7x7CwDhp5AD/tulZslTYXM7oNPjEtje3xxabXuj/bEUMNvHZhQRFdqA==} + '@swc/core-linux-arm64-musl@1.11.11': + resolution: {integrity: sha512-nR2tfdQRRzwqR2XYw9NnBk9Fdvff/b8IiJzDL28gRR2QiJWLaE8LsRovtWrzCOYq6o5Uu9cJ3WbabWthLo4jLw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.7.39': - resolution: {integrity: sha512-3gtCPEJuXLQEolo9xsXtuPDocmXQx12vewEyFFSMSjOfakuPOBmOQMa0sVL8Wwius8C1eZVeD1fgk0omMqeC+Q==} + '@swc/core-linux-x64-gnu@1.11.11': + resolution: {integrity: sha512-b4gBp5HA9xNWNC5gsYbdzGBJWx4vKSGybGMGOVWWuF+ynx10+0sA/o4XJGuNHm8TEDuNh9YLKf6QkIO8+GPJ1g==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.7.39': - resolution: {integrity: sha512-mg39pW5x/eqqpZDdtjZJxrUvQNSvJF4O8wCl37fbuFUqOtXs4TxsjZ0aolt876HXxxhsQl7rS+N4KioEMSgTZw==} + '@swc/core-linux-x64-musl@1.11.11': + resolution: {integrity: sha512-dEvqmQVswjNvMBwXNb8q5uSvhWrJLdttBSef3s6UC5oDSwOr00t3RQPzyS3n5qmGJ8UMTdPRmsopxmqaODISdg==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.7.39': - resolution: {integrity: sha512-NZwuS0mNJowH3e9bMttr7B1fB8bW5svW/yyySigv9qmV5VcQRNz1kMlCvrCLYRsa93JnARuiaBI6FazSeG8mpA==} + '@swc/core-win32-arm64-msvc@1.11.11': + resolution: {integrity: sha512-aZNZznem9WRnw2FbTqVpnclvl8Q2apOBW2B316gZK+qxbe+ktjOUnYaMhdCG3+BYggyIBDOnaJeQrXbKIMmNdw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.7.39': - resolution: {integrity: sha512-qFmvv5UExbJPXhhvCVDBnjK5Duqxr048dlVB6ZCgGzbRxuarOlawCzzLK4N172230pzlAWGLgn9CWl3+N6zfHA==} + '@swc/core-win32-ia32-msvc@1.11.11': + resolution: {integrity: sha512-DjeJn/IfjgOddmJ8IBbWuDK53Fqw7UvOz7kyI/728CSdDYC3LXigzj3ZYs4VvyeOt+ZcQZUB2HA27edOifomGw==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.7.39': - resolution: {integrity: sha512-o+5IMqgOtj9+BEOp16atTfBgCogVak9svhBpwsbcJQp67bQbxGYhAPPDW/hZ2rpSSF7UdzbY9wudoX9G4trcuQ==} + '@swc/core-win32-x64-msvc@1.11.11': + resolution: {integrity: sha512-Gp/SLoeMtsU4n0uRoKDOlGrRC6wCfifq7bqLwSlAG8u8MyJYJCcwjg7ggm0rhLdC2vbiZ+lLVl3kkETp+JUvKg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.7.39': - resolution: {integrity: sha512-jns6VFeOT49uoTKLWIEfiQqJAlyqldNAt80kAr8f7a5YjX0zgnG3RBiLMpksx4Ka4SlK4O6TJ/lumIM3Trp82g==} + '@swc/core@1.11.11': + resolution: {integrity: sha512-pCVY2Wn6dV/labNvssk9b3Owi4WOYsapcbWm90XkIj4xH/56Z6gzja9fsU+4MdPuEfC2Smw835nZHcdCFGyX6A==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '*' @@ -361,24 +425,27 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/types@0.1.13': - resolution: {integrity: sha512-JL7eeCk6zWCbiYQg2xQSdLXQJl8Qoc9rXmG2cEKvHe3CKwMHwHGpfOb8frzNLmbycOo6I51qxnLnn9ESf4I20Q==} + '@swc/types@0.1.19': + resolution: {integrity: sha512-WkAZaAfj44kh/UFdAQcrMP1I0nwRqpt27u+08LMBYMqmQfwwMofYoMh/48NGkMMRfC4ynpfwRbJuu8ErfNloeA==} - '@szmarczak/http-timer@4.0.6': - resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} - engines: {node: '>=10'} + '@szmarczak/http-timer@5.0.1': + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@types/cacheable-request@6.0.3': - resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + '@types/chrome@0.0.309': + resolution: {integrity: sha512-ZFADzcp8b+roUrux68U8pti4cmNOLJXWkShk8lfxj9SBcjYqpJt7NypBprSJUJDJVakGZgd2Tt00QePIGh7oPA==} - '@types/chrome@0.0.279': - resolution: {integrity: sha512-wl0IxQ2OQiMazPZM5LimHQ7Jwd72/O8UvvzyptplXT2S4eUqXH5C0n8S+v8PtKhyX89p0igCPpNy3Bwksyk57g==} + '@types/eslint-config-prettier@6.11.3': + resolution: {integrity: sha512-3wXCiM8croUnhg9LdtZUJQwNcQYGWxxdOWDjPe1ykCqJFPVpzAKfs/2dgSoCtAvdPeaponcWPI7mPcGGp9dkKQ==} - '@types/estree@1.0.5': - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -398,8 +465,8 @@ packages: '@types/har-format@1.2.10': resolution: {integrity: sha512-o0J30wqycjF5miWDKYKKzzOU1ZTLuA42HZ4HE7/zqTOc/jTLdQ5NhYWvsRQo45Nfi1KHoRdNhteSI4BAxTF1Pg==} - '@types/http-cache-semantics@4.0.1': - resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} '@types/json-schema@7.0.11': resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} @@ -407,20 +474,17 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/keyv@3.1.4': - resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - '@types/minimatch@3.0.5': resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} - '@types/node@22.7.9': - resolution: {integrity: sha512-jrTfRC7FM6nChvU7X2KqcrgquofrWLFDeYC1hKfwNWomVvrn7JIksqf344WN2X/y8xrgqBd2dJATZV4GbatBfg==} + '@types/node@22.13.10': + resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==} - '@types/responselike@1.0.3': - resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + '@types/tampermonkey@5.0.4': + resolution: {integrity: sha512-HEJ0O5CkDZSwIgd9Zip4xM8o7gZC8zBmJuH1YEXNINIC+aCWEXD10pnpo3NQG5IWHx+Xr4fMSGq1jqeKXH2lpA==} - '@types/tampermonkey@5.0.3': - resolution: {integrity: sha512-i8+x8ipoib8xAfho25oNTbILgrO665oxq5tNr3LvH5DaW5UOPsc88Fe1mTVTDwsux5LYOcd8Syb6q+s95x878Q==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} '@types/webpack@5.28.5': resolution: {integrity: sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==} @@ -434,76 +498,116 @@ packages: '@violentmonkey/types@0.2.0': resolution: {integrity: sha512-JRPtwhyodYKNkENciULg6v+ZtsoBR2ybYsA1KDvjw4JBzLNmvHixhd8TTl2s8eS1hU7MA1FU8kd1Ren9SCjxew==} - '@webassemblyjs/ast@1.12.1': - resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} - '@webassemblyjs/floating-point-hex-parser@1.11.6': - resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} - '@webassemblyjs/helper-api-error@1.11.6': - resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} - '@webassemblyjs/helper-buffer@1.12.1': - resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==} + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} - '@webassemblyjs/helper-numbers@1.11.6': - resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} - '@webassemblyjs/helper-wasm-bytecode@1.11.6': - resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} - '@webassemblyjs/helper-wasm-section@1.12.1': - resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==} + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} - '@webassemblyjs/ieee754@1.11.6': - resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} - '@webassemblyjs/leb128@1.11.6': - resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} - '@webassemblyjs/utf8@1.11.6': - resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} - '@webassemblyjs/wasm-edit@1.12.1': - resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==} + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} - '@webassemblyjs/wasm-gen@1.12.1': - resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==} + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} - '@webassemblyjs/wasm-opt@1.12.1': - resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==} + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} - '@webassemblyjs/wasm-parser@1.12.1': - resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==} + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} - '@webassemblyjs/wast-printer@1.12.1': - resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} - '@webpack-cli/configtest@2.1.1': - resolution: {integrity: sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==} - engines: {node: '>=14.15.0'} + '@webpack-cli/configtest@3.0.1': + resolution: {integrity: sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==} + engines: {node: '>=18.12.0'} peerDependencies: - webpack: 5.x.x - webpack-cli: 5.x.x + webpack: ^5.82.0 + webpack-cli: 6.x.x - '@webpack-cli/info@2.0.2': - resolution: {integrity: sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==} - engines: {node: '>=14.15.0'} + '@webpack-cli/info@3.0.1': + resolution: {integrity: sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==} + engines: {node: '>=18.12.0'} peerDependencies: - webpack: 5.x.x - webpack-cli: 5.x.x + webpack: ^5.82.0 + webpack-cli: 6.x.x - '@webpack-cli/serve@2.0.5': - resolution: {integrity: sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==} - engines: {node: '>=14.15.0'} + '@webpack-cli/serve@3.0.1': + resolution: {integrity: sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==} + engines: {node: '>=18.12.0'} peerDependencies: - webpack: 5.x.x - webpack-cli: 5.x.x + webpack: ^5.82.0 + webpack-cli: 6.x.x webpack-dev-server: '*' peerDependenciesMeta: webpack-dev-server: optional: true + '@xhmikosr/archive-type@7.0.0': + resolution: {integrity: sha512-sIm84ZneCOJuiy3PpWR5bxkx3HaNt1pqaN+vncUBZIlPZCq8ASZH+hBVdu5H8znR7qYC6sKwx+ie2Q7qztJTxA==} + engines: {node: ^14.14.0 || >=16.0.0} + + '@xhmikosr/bin-check@7.0.3': + resolution: {integrity: sha512-4UnCLCs8DB+itHJVkqFp9Zjg+w/205/J2j2wNBsCEAm/BuBmtua2hhUOdAMQE47b1c7P9Xmddj0p+X1XVsfHsA==} + engines: {node: '>=18'} + + '@xhmikosr/bin-wrapper@13.0.5': + resolution: {integrity: sha512-DT2SAuHDeOw0G5bs7wZbQTbf4hd8pJ14tO0i4cWhRkIJfgRdKmMfkDilpaJ8uZyPA0NVRwasCNAmMJcWA67osw==} + engines: {node: '>=18'} + + '@xhmikosr/decompress-tar@8.0.1': + resolution: {integrity: sha512-dpEgs0cQKJ2xpIaGSO0hrzz3Kt8TQHYdizHsgDtLorWajuHJqxzot9Hbi0huRxJuAGG2qiHSQkwyvHHQtlE+fg==} + engines: {node: '>=18'} + + '@xhmikosr/decompress-tarbz2@8.0.1': + resolution: {integrity: sha512-OF+6DysDZP5YTDO8uHuGG6fMGZjc+HszFPBkVltjoje2Cf60hjBg/YP5OQndW1hfwVWOdP7f3CnJiPZHJUTtEg==} + engines: {node: '>=18'} + + '@xhmikosr/decompress-targz@8.0.1': + resolution: {integrity: sha512-mvy5AIDIZjQ2IagMI/wvauEiSNHhu/g65qpdM4EVoYHUJBAmkQWqcPJa8Xzi1aKVTmOA5xLJeDk7dqSjlHq8Mg==} + engines: {node: '>=18'} + + '@xhmikosr/decompress-unzip@7.0.0': + resolution: {integrity: sha512-GQMpzIpWTsNr6UZbISawsGI0hJ4KA/mz5nFq+cEoPs12UybAqZWKbyIaZZyLbJebKl5FkLpsGBkrplJdjvUoSQ==} + engines: {node: '>=18'} + + '@xhmikosr/decompress@10.0.1': + resolution: {integrity: sha512-6uHnEEt5jv9ro0CDzqWlFgPycdE+H+kbJnwyxgZregIMLQ7unQSCNVsYG255FoqU8cP46DyggI7F7LohzEl8Ag==} + engines: {node: '>=18'} + + '@xhmikosr/downloader@15.0.1': + resolution: {integrity: sha512-fiuFHf3Dt6pkX8HQrVBsK0uXtkgkVlhrZEh8b7VgoDqFf+zrgFBPyrwCqE/3nDwn3hLeNz+BsrS7q3mu13Lp1g==} + engines: {node: '>=18'} + + '@xhmikosr/os-filter-obj@3.0.0': + resolution: {integrity: sha512-siPY6BD5dQ2SZPl3I0OZBHL27ZqZvLEosObsZRQ1NUB8qcxegwt0T9eKtV96JMFQpIz1elhkzqOg4c/Ri6Dp9A==} + engines: {node: ^14.14.0 || >=16.0.0} + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -514,44 +618,29 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} - acorn-import-attributes@1.9.5: - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.10.0: - resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} - engines: {node: '>=0.4.0'} - hasBin: true - - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} - engines: {node: '>=0.4.0'} - hasBin: true - - acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true - addons-linter@7.1.0: - resolution: {integrity: sha512-UmkUB3dZSpf8bqhlulGDbpoxBwcfOk8JvaJTRvJ+AAXKHvTjqeNlFC+GRMqa0EjJJh/0yqpBaJzyaUIx+fjl+A==} + addons-linter@7.9.0: + resolution: {integrity: sha512-ehoh43VG+DGOVxdT0XsO6camSMaHRCJf7shHX2qtKMRQo/29M1fGwZ+ssRTL0SucqLCvsmu3hmv13fEhC+79aA==} engines: {node: '>=18.0.0'} hasBin: true addons-moz-compare@1.3.0: resolution: {integrity: sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==} - addons-scanner-utils@9.11.0: - resolution: {integrity: sha512-X95V8ymnue9EHmOLz3zJTGHvHDFlWKiavlH+kJKOlv2sJDWFvD3TWeJMHJgxS9GKOqT/545mOXvX3vuuGGum+g==} + addons-scanner-utils@9.12.0: + resolution: {integrity: sha512-Nn+mOgJhdSZKkycXd5GYfSfoyT1lEGbCVoCBKriEFceuP0dJxVBuFwuRD8+PuXegQQBw7COTS5DwM4nj+bK8Mg==} peerDependencies: - body-parser: 1.20.2 - express: 4.19.2 + body-parser: 1.20.3 + express: 4.21.0 node-fetch: 2.6.11 safe-compare: 1.1.4 peerDependenciesMeta: @@ -580,11 +669,6 @@ packages: ajv: optional: true - ajv-keywords@3.5.2: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} - peerDependencies: - ajv: ^6.9.1 - ajv-keywords@5.1.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: @@ -622,8 +706,8 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - arch@2.2.0: - resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} + arch@3.0.0: + resolution: {integrity: sha512-AmIAC+Wtm2AU8lGfTtHsw0Y9Qtftx2YXEEtiBP10xFUtMOA+sHHx6OAddyL52mUKh1vsXQ6/w1mVDptZCyUt4Q==} argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -649,9 +733,15 @@ packages: atomically@2.0.3: resolution: {integrity: sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==} + b4a@1.6.7: + resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + bare-events@2.5.4: + resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -663,10 +753,6 @@ packages: resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} engines: {node: '>=0.6'} - bin-check@4.1.0: - resolution: {integrity: sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==} - engines: {node: '>=4'} - bin-version-check@5.1.0: resolution: {integrity: sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==} engines: {node: '>=12'} @@ -699,8 +785,8 @@ packages: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} - browserslist@4.24.2: - resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -713,6 +799,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -720,13 +809,13 @@ packages: resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} engines: {node: '>=12'} - cacheable-lookup@5.0.4: - resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} - engines: {node: '>=10.6.0'} + cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} - cacheable-request@7.0.4: - resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} - engines: {node: '>=8'} + cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} @@ -739,8 +828,8 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} - caniuse-lite@1.0.30001669: - resolution: {integrity: sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==} + caniuse-lite@1.0.30001692: + resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==} chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -782,9 +871,6 @@ packages: resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} engines: {node: '>=6'} - clone-response@1.0.3: - resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} - clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -812,9 +898,13 @@ packages: resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} engines: {node: '>=8.0.0'} - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -823,6 +913,10 @@ packages: resolution: {integrity: sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==} engines: {node: '>= 0.6.x'} + commander@6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} @@ -842,8 +936,8 @@ packages: resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} engines: {'0': node >= 0.8} - concurrently@9.0.1: - resolution: {integrity: sha512-wYKvCd/f54sTXJMSfV6Ln/B8UrfLBKOYa+lzc6CHay3Qek+LorVSBdMVfyewFhRbH0Rbabsk4D+3PL/VjQ5gzg==} + concurrently@9.1.2: + resolution: {integrity: sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==} engines: {node: '>=18'} hasBin: true @@ -858,8 +952,8 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} - copy-webpack-plugin@12.0.2: - resolution: {integrity: sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==} + copy-webpack-plugin@13.0.0: + resolution: {integrity: sha512-FgR/h5a6hzJqATDGd9YG41SeDViH+0bkHn6WNXCi5zKAZkeESeSxLySSsFLHqLEVCh0E+rITmCf0dusXWYukeQ==} engines: {node: '>= 18.12.0'} peerDependencies: webpack: ^5.1.0 @@ -871,13 +965,14 @@ packages: resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} engines: {node: '>= 0.4.0'} - cross-spawn@5.1.0: - resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} - cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + crx3@1.1.3: resolution: {integrity: sha512-E4c3y/2ak83wAGN4h9Q8j3A2ii4bpRVf2vxinYYy+wJs8pyscMfEnKGzhgngZcRfOhUMr7/sxzy+lTZmQ/4KpQ==} engines: {node: '>=12'} @@ -965,6 +1060,10 @@ packages: defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + defaults@3.0.0: + resolution: {integrity: sha512-RsqXDEAALjfRTro+IFNKpcPCt0/Cy2FqHSIlnomiJp9YGadpQnrtbRpSgN2+np21qHcIKiva4fiOQGjS9/qR/A==} + engines: {node: '>=18'} + defer-to-connect@2.0.1: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} engines: {node: '>=10'} @@ -994,12 +1093,16 @@ packages: resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} engines: {node: '>=18'} - dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + dotenv@16.4.7: + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} engines: {node: '>=12'} - electron-to-chromium@1.5.42: - resolution: {integrity: sha512-gIfKavKDw1mhvic9nbzA5lZw8QSHpdMwLwXc0cWidQz9B15pDoDdDH4boIatuFfeoCatb3a/NGL6CYRVFxGZ9g==} + eciesjs@0.4.14: + resolution: {integrity: sha512-eJAgf9pdv214Hn98FlUzclRMYWF7WfoLlkS9nWMTm1qcCwn6Ad4EGD9lr9HXMBfSrZhYQujRE+p0adPRkctC6A==} + engines: {bun: '>=1', deno: '>=2', node: '>=16'} + + electron-to-chromium@1.5.80: + resolution: {integrity: sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==} emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -1007,9 +1110,6 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - enhanced-resolve@5.17.1: resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} engines: {node: '>=10.13.0'} @@ -1018,8 +1118,8 @@ packages: resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} engines: {node: '>=0.12'} - envinfo@7.8.1: - resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==} + envinfo@7.14.0: + resolution: {integrity: sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==} engines: {node: '>=4'} hasBin: true @@ -1048,20 +1148,16 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - - eslint-config-prettier@9.1.0: - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + eslint-config-prettier@10.1.1: + resolution: {integrity: sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-plugin-no-unsanitized@4.0.2: - resolution: {integrity: sha512-Pry0S9YmHoz8NCEMRQh7N0Yexh2MYCNPIlrV52hTmS7qXnTghWsjXouF08bgsrrZqaW9tt1ZiK3j5NEmPE+EjQ==} + eslint-plugin-no-unsanitized@4.1.2: + resolution: {integrity: sha512-ydF3PMFKEIkP71ZbLHFvu6/FW8SvRv6VV/gECfrQkqyD5+5oCAtPz8ZHy0GRuMDtNe2jsNdPCQXX4LSbkapAVQ==} peerDependencies: - eslint: ^6 || ^7 || ^8 + eslint: ^8 || ^9 eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} @@ -1071,20 +1167,16 @@ packages: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-scope@8.1.0: - resolution: {integrity: sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==} + eslint-scope@8.3.0: + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.0.0: - resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@4.1.0: - resolution: {integrity: sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==} + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint@8.57.1: @@ -1093,8 +1185,8 @@ packages: deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true - eslint@9.13.0: - resolution: {integrity: sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==} + eslint@9.22.0: + resolution: {integrity: sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1103,12 +1195,8 @@ packages: jiti: optional: true - espree@10.1.0: - resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - espree@10.2.0: - resolution: {integrity: sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==} + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} espree@9.6.1: @@ -1151,14 +1239,6 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - execa@0.7.0: - resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==} - engines: {node: '>=4'} - - execa@4.1.0: - resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} - engines: {node: '>=10'} - execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -1167,10 +1247,6 @@ packages: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} - executable@4.1.1: - resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} - engines: {node: '>=4'} - ext-list@2.2.2: resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} engines: {node: '>=0.10.0'} @@ -1182,6 +1258,9 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -1212,6 +1291,14 @@ packages: fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -1220,17 +1307,17 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - file-type@17.1.6: - resolution: {integrity: sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + file-type@19.6.0: + resolution: {integrity: sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ==} + engines: {node: '>=18'} filename-reserved-regex@3.0.0: resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - filenamify@5.1.1: - resolution: {integrity: sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==} - engines: {node: '>=12.20'} + filenamify@6.0.0: + resolution: {integrity: sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==} + engines: {node: '>=16'} fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} @@ -1281,6 +1368,10 @@ packages: debug: optional: true + form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + fs-extra@11.2.0: resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} engines: {node: '>=14.14'} @@ -1306,18 +1397,14 @@ packages: get-intrinsic@1.2.0: resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} - get-stream@3.0.0: - resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==} - engines: {node: '>=4'} - - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1345,17 +1432,13 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.11.0: - resolution: {integrity: sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==} - engines: {node: '>=18'} - - globby@14.0.1: - resolution: {integrity: sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==} + globals@16.0.0: + resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==} engines: {node: '>=18'} - got@11.8.6: - resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} - engines: {node: '>=10.19.0'} + got@13.0.0: + resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==} + engines: {node: '>=16'} graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} @@ -1399,8 +1482,8 @@ packages: htmlparser2@8.0.1: resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==} - http-cache-semantics@4.1.0: - resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} http-proxy@1.18.1: resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} @@ -1411,18 +1494,14 @@ packages: engines: {node: '>=12'} hasBin: true - http2-wrapper@1.0.3: - resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} engines: {node: '>=10.19.0'} https-proxy-agent@7.0.5: resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} - human-signals@1.1.1: - resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} - engines: {node: '>=8.12.0'} - human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -1444,20 +1523,20 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.2.4: - resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - image-size@1.1.1: - resolution: {integrity: sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==} + image-size@2.0.1: + resolution: {integrity: sha512-NI6NK/2zchlZopsQrcVIS7jxA0/rtIy74B+/rx5s7rKQyFebmQjZVhzxXgRZJROk+WhhOq+S6sUaODxp0L5hfg==} engines: {node: '>=16.x'} hasBin: true immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immutable@4.2.2: - resolution: {integrity: sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==} + immutable@5.0.3: + resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==} import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -1490,14 +1569,13 @@ packages: resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + inspect-with-kind@1.0.5: + resolution: {integrity: sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==} + interpret@3.1.1: resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} engines: {node: '>=10.13.0'} - invert-kv@3.0.1: - resolution: {integrity: sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw==} - engines: {node: '>=8'} - is-absolute@0.1.7: resolution: {integrity: sha512-Xi9/ZSn4NFapG8RP98iNPMOeaV3mXPisxKxzKtHVqr3g56j/fBn+yZmnxSVAA8lmZbl2J9b/a4kJvfU3hqQYgA==} engines: {node: '>=0.10.0'} @@ -1572,10 +1650,6 @@ packages: resolution: {integrity: sha512-wBOr+rNM4gkAZqoLRJI4myw5WzzIdQosFAAbnvfXP5z1LyzgAI3ivOKehC5KfqlQJZoihVhirgtCBj378Eg8GA==} engines: {node: '>=0.10.0'} - is-stream@1.1.0: - resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} - engines: {node: '>=0.10.0'} - is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -1584,6 +1658,10 @@ packages: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + is-utf8@0.2.1: resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} @@ -1600,19 +1678,20 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} - jed@1.1.1: - resolution: {integrity: sha512-z35ZSEcXHxLW4yumw0dF6L464NT36vmx3wxJw8MDpraBcWuNVgUPZgPJKcu1HekNgwlMFNqol7i/IpSbjhqwqA==} - jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} - jose@5.9.2: - resolution: {integrity: sha512-ILI2xx/I57b20sd7rHZvgiiQrmp2mcotwsAH+5ajbpFQbrYVQdNHYlQhoA5cFb78CgtBOxtC05TeA+mcgkuCqQ==} + jose@5.9.6: + resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1664,10 +1743,6 @@ packages: resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==} engines: {node: '>=18'} - lcid@3.1.1: - resolution: {integrity: sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==} - engines: {node: '>=8'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1700,31 +1775,24 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - lowercase-keys@2.0.0: - resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} - engines: {node: '>=8'} - - lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - map-age-cleaner@0.1.3: - resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==} - engines: {node: '>=6'} - marky@1.2.5: resolution: {integrity: sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==} - mem@5.1.1: - resolution: {integrity: sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw==} - engines: {node: '>=8'} - merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1757,16 +1825,16 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} - mimic-response@1.0.1: - resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} - engines: {node: '>=4'} - mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - mini-css-extract-plugin@2.9.1: - resolution: {integrity: sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==} + mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + mini-css-extract-plugin@2.9.2: + resolution: {integrity: sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 @@ -1834,20 +1902,16 @@ packages: node-notifier@10.0.1: resolution: {integrity: sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==} - node-releases@2.0.18: - resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-url@6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} - engines: {node: '>=10'} - - npm-run-path@2.0.2: - resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} - engines: {node: '>=4'} + normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} + engines: {node: '>=14.16'} npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} @@ -1863,6 +1927,10 @@ packages: object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + object-treeify@1.1.33: + resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==} + engines: {node: '>= 10'} + on-exit-leak-free@2.1.0: resolution: {integrity: sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==} @@ -1889,33 +1957,13 @@ packages: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} - os-filter-obj@2.0.0: - resolution: {integrity: sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==} - engines: {node: '>=4'} - - os-locale@5.0.0: - resolution: {integrity: sha512-tqZcNEDAIZKBEPnHPlVDvKrp7NzgLi7jRmhKiUoa2NUmhl13FtkAGLUVR+ZsYvApBQdBfYm43A4tXXQ4IrYLBA==} - engines: {node: '>=10'} - os-shim@0.1.3: resolution: {integrity: sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==} engines: {node: '>= 0.4.0'} - p-cancelable@2.1.1: - resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} - engines: {node: '>=8'} - - p-defer@1.0.0: - resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==} - engines: {node: '>=4'} - - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - - p-is-promise@2.1.0: - resolution: {integrity: sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==} - engines: {node: '>=6'} + p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} @@ -1969,10 +2017,6 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} - path-key@2.0.1: - resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} - engines: {node: '>=4'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1984,16 +2028,12 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-type@5.0.0: - resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} - engines: {node: '>=12'} - pbf@3.2.1: resolution: {integrity: sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==} hasBin: true - peek-readable@5.0.0: - resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==} + peek-readable@5.3.1: + resolution: {integrity: sha512-GVlENSDW6KHaXcd9zkZltB7tCLosKB/4Hg0fqBJkAoBgYG2Tn1xtMgXtSUuMU9AK/gCm/tTdT8mgAeF4YNeeqw==} engines: {node: '>=14.16'} pend@1.2.0: @@ -2009,12 +2049,9 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - - pino-abstract-transport@1.1.0: - resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} pino-abstract-transport@1.2.0: resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} @@ -2083,8 +2120,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} engines: {node: '>=14'} hasBin: true @@ -2111,12 +2148,6 @@ packages: protocol-buffers-schema@3.6.0: resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==} - pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - - pump@3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} - punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} @@ -2132,8 +2163,8 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - queue@6.0.2: - resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} + queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -2152,18 +2183,10 @@ packages: readable-stream@2.3.7: resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - readable-stream@4.3.0: resolution: {integrity: sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - readable-web-to-node-stream@3.0.2: - resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==} - engines: {node: '>=8'} - real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -2183,11 +2206,6 @@ packages: resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} engines: {node: '>=12'} - relaxed-json@1.0.3: - resolution: {integrity: sha512-b7wGPo7o2KE/g7SqkJDDbav6zmrEeP4TK2VpITU72J/M949TLe/23y/ZHJo+pskcGM52xIfFoT9hydwmgr1AEg==} - engines: {node: '>= 0.10.0'} - hasBin: true - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -2221,8 +2239,9 @@ packages: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true - responselike@2.0.1: - resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} @@ -2256,133 +2275,258 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass-embedded-android-arm64@1.80.4: - resolution: {integrity: sha512-htAuBmRvvN2d4smrqxZ6WBw4+OOURaoHzq5oZKqS/E35zYl5FHmrJzp4S5e26a0tEBcjca014tfb/uu9cQgnqA==} + sass-embedded-android-arm64@1.85.1: + resolution: {integrity: sha512-27oRheqNA3SJM2hAxpVbs7mCKUwKPWmEEhyiNFpBINb5ELVLg+Ck5RsGg+SJmo130ul5YX0vinmVB5uPWc8X5w==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [android] - sass-embedded-android-arm@1.80.4: - resolution: {integrity: sha512-iAZ7AiKTLGxQGTkZ37c2/7YC4lkbP1o3eP/K74YaF8O+qhKTLyLOwV7OcmzIywac7dqLcNuGqhFCmFqTYpewZw==} + sass-embedded-android-arm64@1.86.0: + resolution: {integrity: sha512-r7MZtlAI2VFUnKE8B5UOrpoE6OGpdf1dIB6ndoxb3oiURgMyfTVU7yvJcL12GGvtVwQ2boCj6dq//Lqq9CXPlQ==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [android] + + sass-embedded-android-arm@1.85.1: + resolution: {integrity: sha512-GkcgUGMZtEF9gheuE1dxCU0ZSAifuaFXi/aX7ZXvjtdwmTl9Zc/OHR9oiUJkc8IW9UI7H8TuwlTAA8+SwgwIeQ==} engines: {node: '>=14.0.0'} cpu: [arm] os: [android] - sass-embedded-android-ia32@1.80.4: - resolution: {integrity: sha512-IIee89Jco8/ad2s/oRJTFqpLhBMzg0UXteJyZ5waZPZmkeSR/t9l67Ef1lLQVh9t9/fJ1ViTTiGYm/g/zu6UGw==} + sass-embedded-android-arm@1.86.0: + resolution: {integrity: sha512-NS8v6BCbzskXUMBtzfuB+j2yQMgiwg5edKHTYfQU7gAWai2hkRhS06YNEMff3aRxV0IFInxPRHOobd8xWPHqeA==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [android] + + sass-embedded-android-ia32@1.85.1: + resolution: {integrity: sha512-f3x16NyRgtXFksIaO/xXKrUhttUBv8V0XsAR2Dhdb/yz4yrDrhzw9Wh8fmw7PlQqECcQvFaoDr3XIIM6lKzasw==} + engines: {node: '>=14.0.0'} + cpu: [ia32] + os: [android] + + sass-embedded-android-ia32@1.86.0: + resolution: {integrity: sha512-UjfElrGaOTNOnxLZLxf6MFndFIe7zyK+81f83BioZ7/jcoAd6iCHZT8yQMvu8wINyVodPcaXZl8KxlKcl62VAA==} engines: {node: '>=14.0.0'} cpu: [ia32] os: [android] - sass-embedded-android-riscv64@1.80.4: - resolution: {integrity: sha512-iJM2kqmWrOeE1aUyTp3uMAG86hyAqbpbOEV7tv828fUsMRDM4uHsHtmyp2n8P2Y0Y2FnLzJpvIm3SwDXGDzT1Q==} + sass-embedded-android-riscv64@1.85.1: + resolution: {integrity: sha512-IP6OijpJ8Mqo7XqCe0LsuZVbAxEFVboa0kXqqR5K55LebEplsTIA2GnmRyMay3Yr/2FVGsZbCb6Wlgkw23eCiA==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [android] - sass-embedded-android-x64@1.80.4: - resolution: {integrity: sha512-vd8VrLvUoHeTcsDoIJesXLbQYZH26a8lAzXy6u4+vEuAwikF4WiXBDFrpqiv38QeD3faLeoPtksRsFbAdQqJAA==} + sass-embedded-android-riscv64@1.86.0: + resolution: {integrity: sha512-TsqCLxHWLFS2mbpUkL/nge3jSkaPK2VmLkkoi5iO/EQT4SFvm1lNUgPwlLXu9DplZ+aqGVzRS9Y6Psjv+qW7kw==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [android] + + sass-embedded-android-x64@1.85.1: + resolution: {integrity: sha512-Mh7CA53wR3ADvXAYipFc/R3vV4PVOzoKwWzPxmq+7i8UZrtsVjKONxGtqWe9JG1mna0C9CRZAx0sv/BzbOJxWg==} engines: {node: '>=14.0.0'} cpu: [x64] os: [android] - sass-embedded-darwin-arm64@1.80.4: - resolution: {integrity: sha512-SJz7EM1i4NXa7CT/njIWMNYJ6CvbHljDIzUAZEe3V3u1KWl/eNO3pbWAnnDN62tBppwgWx/UdDUbAKowsT6Z8w==} + sass-embedded-android-x64@1.86.0: + resolution: {integrity: sha512-8Q263GgwGjz7Jkf7Eghp7NrwqskDL95WO9sKrNm9iOd2re/M48W7RN/lpdcZwrUnEOhueks0RRyYyZYBNRz8Tg==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [android] + + sass-embedded-darwin-arm64@1.85.1: + resolution: {integrity: sha512-msWxzhvcP9hqGVegxVePVEfv9mVNTlUgGr6k7O7Ihji702mbtrH/lKwF4aRkkt4g1j7tv10+JtQXmTNi/pi9kA==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [darwin] + + sass-embedded-darwin-arm64@1.86.0: + resolution: {integrity: sha512-d8oMEaIweq1tjrb/BT43igDviOMS1TeDpc51QF7vAHkt9drSjPmqEmbqStdFYPAGZj1j0RA4WCRoVl6jVixi/w==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [darwin] - sass-embedded-darwin-x64@1.80.4: - resolution: {integrity: sha512-J/QlBVO66DLtgALgCmM8rZ5zG0dBCIYW1eXIAnnDwC7vGkbAXMtO60M0O/2WNrAfmFfJz1hvKDLjlsxB2XGBLg==} + sass-embedded-darwin-x64@1.85.1: + resolution: {integrity: sha512-J4UFHUiyI9Z+mwYMwz11Ky9TYr3hY1fCxeQddjNGL/+ovldtb0yAIHvoVM0BGprQDm5JqhtUk8KyJ3RMJqpaAA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [darwin] + + sass-embedded-darwin-x64@1.86.0: + resolution: {integrity: sha512-5NLRtn0ZUDBkfpKOsgLGl9B34po4Qui8Nff/lXTO+YkxBQFX4GoMkYNk9EJqHwoLLzICsxIhNDMMDiPGz7Fdrw==} engines: {node: '>=14.0.0'} cpu: [x64] os: [darwin] - sass-embedded-linux-arm64@1.80.4: - resolution: {integrity: sha512-hI6zQyrR6qJbvyEHfj8UGXNB8VyUa72jel46406AuxUnViA0RyZDSqXUF8vwVw/Hjv1LkA5ihK9dBmWNbLz1zQ==} + sass-embedded-linux-arm64@1.85.1: + resolution: {integrity: sha512-jGadetB03BMFG2rq3OXub/uvC/lGpbQOiLGEz3NLb2nRZWyauRhzDtvZqkr6BEhxgIWtMtz2020yD8ZJSw/r2w==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - sass-embedded-linux-arm@1.80.4: - resolution: {integrity: sha512-vuaWhc4ebnaY1AgIWNvFv1snxmkWfvlCU7vnQf4qkn3R2Yyd2J+sjkO8o0NgMX8n5XRUSkAaYUJFCH+Nim6KgQ==} + sass-embedded-linux-arm64@1.86.0: + resolution: {integrity: sha512-50A+0rhahRDRkKkv+qS7GDAAkW1VPm2RCX4zY4JWydhV4NwMXr6HbkLnsJ2MGixCyibPh59iflMpNBhe7SEMNg==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [linux] + + sass-embedded-linux-arm@1.85.1: + resolution: {integrity: sha512-X0fDh95nNSw1wfRlnkE4oscoEA5Au4nnk785s9jghPFkTBg+A+5uB6trCjf0fM22+Iw6kiP4YYmDdw3BqxAKLQ==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [linux] + + sass-embedded-linux-arm@1.86.0: + resolution: {integrity: sha512-b6wm0+Il+blJDleRXAqA6JISGMjRb0/thTEg4NWgmiJwUoZjDycj5FTbfYPnLXjCEIMGaYmW3patrJ3JMJcT3Q==} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] - sass-embedded-linux-ia32@1.80.4: - resolution: {integrity: sha512-wcPExI8UbYrrJvGvo4v2Q+RktbCp44i3qZQ18hglPcVZOC1IzT9NPqZn0XmrqD4hmNbgsYR+picODkvqGw7iDA==} + sass-embedded-linux-ia32@1.85.1: + resolution: {integrity: sha512-7HlYY90d9mitDtNi5s+S+5wYZrTVbkBH2/kf7ixrzh2BFfT0YM81UHLJRnGX93y9aOMBL6DSZAIfkt1RsV9bkQ==} + engines: {node: '>=14.0.0'} + cpu: [ia32] + os: [linux] + + sass-embedded-linux-ia32@1.86.0: + resolution: {integrity: sha512-h0mr9w71TV3BRPk9JHr0flnRCznhkraY14gaj5T+t78vUFByOUMxp4hTr+JpZAR5mv0mIeoMwrQYwWJoqKI0mw==} engines: {node: '>=14.0.0'} cpu: [ia32] os: [linux] - sass-embedded-linux-musl-arm64@1.80.4: - resolution: {integrity: sha512-y8slzQ8Jjkl+53mUDkp3zxcDrTXVVxzpa+6nKh5Ue8l1YU2KdVZG1v2PoDXxE6o99B5I2TVBG8i02IsdYoL8jQ==} + sass-embedded-linux-musl-arm64@1.85.1: + resolution: {integrity: sha512-FLkIT0p18XOkR6wryJ13LqGBDsrYev2dRk9dtiU18NCpNXruKsdBQ1ZnWHVKB3h1dA9lFyEEisC0sooKdNfeOQ==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - sass-embedded-linux-musl-arm@1.80.4: - resolution: {integrity: sha512-HWo0G/9tuhj/uSEwte9KiDK2Xezrfh7nhdEH69ZIfOAqP5byTXL7o08TYagbvMAoljR43Vfna6MelV7NUX4WCw==} + sass-embedded-linux-musl-arm64@1.86.0: + resolution: {integrity: sha512-5OZjiJIUyhvKJIGNDEjyRUWDe+W91hq4Bji27sy8gdEuDzPWLx4NzwpKwsBUALUfyW/J5dxgi0ZAQnI3HieyQg==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [linux] + + sass-embedded-linux-musl-arm@1.85.1: + resolution: {integrity: sha512-5vcdEqE8QZnu6i6shZo7x2N36V7YUoFotWj2rGekII5ty7Nkaj+VtZhUEOp9tAzEOlaFuDp5CyO1kUCvweT64A==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [linux] + + sass-embedded-linux-musl-arm@1.86.0: + resolution: {integrity: sha512-KZU70jBMVykC9HzS+o2FhrJaprFLDk3LWXVPtBFxgLlkcQ/apCkUCh2WVNViLhI2U4NrMSnTvd4kDnC/0m8qIw==} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] - sass-embedded-linux-musl-ia32@1.80.4: - resolution: {integrity: sha512-A2WSwnomho491iCeHh3c0YRympfAoJOKr+IyxalTcRH/pjENOWZWZUt00WE2q0tTpEd2V+goWvgS5pmUGewgmg==} + sass-embedded-linux-musl-ia32@1.85.1: + resolution: {integrity: sha512-N1093T84zQJor1yyIAdYScB5eAuQarGK1tKgZ4uTnxVlgA7Xi1lXV8Eh7ox9sDqKCaWkVQ3MjqU26vYRBeRWyw==} engines: {node: '>=14.0.0'} cpu: [ia32] os: [linux] - sass-embedded-linux-musl-riscv64@1.80.4: - resolution: {integrity: sha512-tYQsAHZLr2mnlJQBJ8Z/n/ySIFJ9JWpsUsoLe9fYgGDaBUfItdzUnj15CChRWld8vFe/I84hb7fbCtYXrI60Jg==} + sass-embedded-linux-musl-ia32@1.86.0: + resolution: {integrity: sha512-vq9wJ7kaELrsNU6Ld6kvrIHxoIUWaD+5T6TQVj4SJP/iw1NjonyCDMQGGs6UgsIEzvaIwtlSlDbRewAq+4PchA==} + engines: {node: '>=14.0.0'} + cpu: [ia32] + os: [linux] + + sass-embedded-linux-musl-riscv64@1.85.1: + resolution: {integrity: sha512-WRsZS/7qlfYXsa93FBpSruieuURIu7ySfFhzYfF1IbKrNAGwmbduutkHZh2ddm5/vQMvQ0Rdosgv+CslaQHMcw==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [linux] + + sass-embedded-linux-musl-riscv64@1.86.0: + resolution: {integrity: sha512-UZJPu4zKe3phEzoSVRh5jcSicBBPe+jEbVNALHSSz881iOAYnDQXHITGeQ4mM1/7e/LTyryHk6EPBoaLOv6JrA==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] - sass-embedded-linux-musl-x64@1.80.4: - resolution: {integrity: sha512-NZnr+SYbWlmXx0IaSQ8oF0jYkOULp9qKWMmmZQ1mxuGQ3z7tJqFhpH3M+hYkrFNeOq+GaH+nhHGOD4ZNBxeRkg==} + sass-embedded-linux-musl-x64@1.85.1: + resolution: {integrity: sha512-+OlLIilA5TnP0YEqTQ8yZtkW+bJIQYvzoGoNLUEskeyeGuOiIyn2CwL6G4JQB4xZQFaxPHb7JD3EueFkQbH0Pw==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - sass-embedded-linux-riscv64@1.80.4: - resolution: {integrity: sha512-h/BmU7QONa7ScvQztFp4Th4aSo3X+Olu3I+RYsaH9s7P683WT3f2w5zr+wwP1V4roM5eyKDCRJBuefT3Fkkkgw==} + sass-embedded-linux-musl-x64@1.86.0: + resolution: {integrity: sha512-8taAgbWMk4QHneJcouWmWZJlmKa2O03g4I/CFo4bfMPL87bibY90pAsSDd+C+t81g0+2aK0/lY/BoB0r3qXLiA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [linux] + + sass-embedded-linux-riscv64@1.85.1: + resolution: {integrity: sha512-mKKlOwMGLN7yP1p0gB5yG/HX4fYLnpWaqstNuOOXH+fOzTaNg0+1hALg0H0CDIqypPO74M5MS9T6FAJZGdT6dQ==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] - sass-embedded-linux-x64@1.80.4: - resolution: {integrity: sha512-aZbZFs/X9bEmzDiBEiV4IAsKEA0zrCM+s/u2OzvrX4GRvZFJ+/XRTTvf+RTm7mgvTFgfPwCkNGVECQZ1eHh+6A==} + sass-embedded-linux-riscv64@1.86.0: + resolution: {integrity: sha512-yREY6o2sLwiiA03MWHVpnUliLscz0flEmFW/wzxYZJDqg9eZteB3hUWgZD63eLm2PTZsYxDQpjAHpa48nnIEmA==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [linux] + + sass-embedded-linux-x64@1.85.1: + resolution: {integrity: sha512-uKRTv0z8NgtHV7xSren78+yoWB79sNi7TMqI7Bxd8fcRNIgHQSA8QBdF8led2ETC004hr8h71BrY60RPO+SSvA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [linux] + + sass-embedded-linux-x64@1.86.0: + resolution: {integrity: sha512-sH0F8np9PTgTbFcJWxfr1NzPkL5ID2NcpMtZyKPTdnn9NkE/L2UwXSo6xOvY0Duc4Hg+58wSrDnj6KbvdeHCPg==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - sass-embedded-win32-arm64@1.80.4: - resolution: {integrity: sha512-8JiatFi2VVFqCdJzKNDteaPC4KPmh8/giaVh7TyMcDhKjnvRLeu3v5V1egTMiwwpnQHuwzU3uqBlm/llVNR2Pw==} + sass-embedded-win32-arm64@1.85.1: + resolution: {integrity: sha512-/GMiZXBOc6AEMBC3g25Rp+x8fq9Z6Ql7037l5rajBPhZ+DdFwtdHY0Ou3oIU6XuWUwD06U3ii4XufXVFhsP6PA==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [win32] + + sass-embedded-win32-arm64@1.86.0: + resolution: {integrity: sha512-4O1XVUxLTIjMOvrziYwEZgvFqC5sF6t0hTAPJ+h2uiAUZg9Joo0PvuEedXurjISgDBsb5W5DTL9hH9q1BbP4cQ==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [win32] - sass-embedded-win32-ia32@1.80.4: - resolution: {integrity: sha512-SodmTD6mjxEgoq44jWMibmBQvWkCfENK/70zp4qsztcBSOggg3nYUzwG0YpraClAMXpB1xOvzrArWu9/9fguAg==} + sass-embedded-win32-ia32@1.85.1: + resolution: {integrity: sha512-L+4BWkKKBGFOKVQ2PQ5HwFfkM5FvTf1Xx2VSRvEWt9HxPXp6SPDho6zC8fqNQ3hSjoaoASEIJcSvgfdQYO0gdg==} engines: {node: '>=14.0.0'} cpu: [ia32] os: [win32] - sass-embedded-win32-x64@1.80.4: - resolution: {integrity: sha512-7+oRRwCCcnOmw152qDiC7x7SphYBo1eLB4KdyThO+7+rYRO8AftXO+kqBPTVSkM8kGp4wxCMF9auPpYBZbjsow==} + sass-embedded-win32-ia32@1.86.0: + resolution: {integrity: sha512-zuSP2axkGm4VaJWt38P464H+4424Swr9bzFNfbbznxe3Ue4RuqSBqwiLiYdg9Q1cecTQ2WGH7G7WO56KK7WLwg==} + engines: {node: '>=14.0.0'} + cpu: [ia32] + os: [win32] + + sass-embedded-win32-x64@1.85.1: + resolution: {integrity: sha512-/FO0AGKWxVfCk4GKsC0yXWBpUZdySe3YAAbQQL0lL6xUd1OiUY8Kow6g4Kc1TB/+z0iuQKKTqI/acJMEYl4iTQ==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [win32] + + sass-embedded-win32-x64@1.86.0: + resolution: {integrity: sha512-GVX0CHtukr3kjqfqretSlPiJzV7V4JxUjpRZV+yC9gUMTiDErilJh2Chw1r0+MYiYvumCDUSDlticmvJs7v0tA==} engines: {node: '>=14.0.0'} cpu: [x64] os: [win32] - sass-embedded@1.80.4: - resolution: {integrity: sha512-lPzKX5g79ZxohlPxh0pXTPFseWj9RfgYI0cPm14CH5ok77Ujuheq/DCp7RStvNDWS8RCQ8Ii6gJC/5WTkGyrhA==} + sass-embedded@1.85.1: + resolution: {integrity: sha512-0i+3h2Df/c71afluxC1SXqyyMmJlnKWfu9ZGdzwuKRM1OftEa2XM2myt5tR36CF3PanYrMjFKtRIj8PfSf838w==} + engines: {node: '>=16.0.0'} + hasBin: true + + sass-embedded@1.86.0: + resolution: {integrity: sha512-Ibq5DzxjSf9f/IJmKeHVeXlVqiZWdRJF+RXy6v6UupvMYVMU5Ei+teSFBvvpPD5bB2QhhnU/OJlSM0EBCtfr9g==} engines: {node: '>=16.0.0'} hasBin: true - sass-loader@16.0.2: - resolution: {integrity: sha512-Ll6iXZ1EYwYT19SqW4mSBb76vSSi8JgzElmzIerhEGgzB5hRjDQIWsPmuk1UrAXkR16KJHqVY0eH+5/uw9Tmfw==} + sass-loader@16.0.5: + resolution: {integrity: sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==} engines: {node: '>= 18.12.0'} peerDependencies: '@rspack/core': 0.x || 1.x @@ -2405,17 +2549,21 @@ packages: sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} - schema-utils@3.3.0: - resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} - engines: {node: '>= 10.13.0'} - schema-utils@4.2.0: resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==} engines: {node: '>= 12.13.0'} + schema-utils@4.3.0: + resolution: {integrity: sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==} + engines: {node: '>= 10.13.0'} + secure-compare@3.0.1: resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} + seek-bzip@2.0.0: + resolution: {integrity: sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==} + hasBin: true + semver-regex@4.0.5: resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==} engines: {node: '>=12'} @@ -2434,8 +2582,10 @@ packages: engines: {node: '>=10'} hasBin: true - serialize-javascript@6.0.1: - resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==} + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -2447,18 +2597,10 @@ packages: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} - shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} - shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} @@ -2482,10 +2624,6 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - slash@5.1.0: - resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} - engines: {node: '>=14.16'} - sonic-boom@3.7.0: resolution: {integrity: sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==} @@ -2525,6 +2663,9 @@ packages: split@1.0.1: resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} + streamx@2.21.1: + resolution: {integrity: sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2556,9 +2697,8 @@ packages: resolution: {integrity: sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==} engines: {node: '>=12'} - strip-eof@1.0.0: - resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} - engines: {node: '>=0.10.0'} + strip-dirs@3.0.0: + resolution: {integrity: sha512-I0sdgcFTfKQlUPZyAqPJmSG3HLO9rWDFnxonnIbskYNM3DwFOeTNB5KzVq3dA1GdRAc/25b5Y7UO2TQfKWw4aQ==} strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} @@ -2580,13 +2720,9 @@ packages: resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} engines: {node: '>=14.16'} - strip-outer@2.0.0: - resolution: {integrity: sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - strtok3@7.0.0: - resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==} - engines: {node: '>=14.16'} + strtok3@9.1.1: + resolution: {integrity: sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw==} + engines: {node: '>=16'} stubborn-fs@1.2.5: resolution: {integrity: sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==} @@ -2613,12 +2749,23 @@ packages: '@swc/core': ^1.2.147 webpack: '>=2' + sync-child-process@1.0.2: + resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==} + engines: {node: '>=16.0.0'} + + sync-message-port@1.1.3: + resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==} + engines: {node: '>=16.0.0'} + tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} - terser-webpack-plugin@5.3.10: - resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + terser-webpack-plugin@5.3.14: + resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -2633,11 +2780,14 @@ packages: uglify-js: optional: true - terser@5.27.2: - resolution: {integrity: sha512-sHXmLSkImesJ4p5apTeT63DsV4Obe1s37qT8qvwHRmVxKTBH7Rv9Wr26VcAMmLbmk9UliiwK8z+657NyJHHy/w==} + terser@5.37.0: + resolution: {integrity: sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==} engines: {node: '>=10'} hasBin: true + text-decoder@1.2.3: + resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -2650,6 +2800,10 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tinyglobby@0.2.12: + resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} + engines: {node: '>=12.0.0'} + titleize@3.0.0: resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} engines: {node: '>=12'} @@ -2662,18 +2816,14 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - token-types@5.0.1: - resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==} + token-types@6.0.0: + resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==} engines: {node: '>=14.16'} tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - trim-repeated@2.0.0: - resolution: {integrity: sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==} - engines: {node: '>=12'} - tslib@2.4.1: resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} @@ -2696,18 +2846,21 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - - unicorn-magic@0.1.0: - resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + uint8array-extras@1.4.0: + resolution: {integrity: sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==} engines: {node: '>=18'} + unbzip2-stream@1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + union@0.5.0: resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} engines: {node: '>= 0.8.0'} @@ -2757,32 +2910,25 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - web-ext@8.3.0: - resolution: {integrity: sha512-mXSOiDtmm3n0KNpSuQ65fJpypAoNLAmZv3QkdlVbJ6etn0BK+hl/k+tjHefSIKdbzGUIeFbhn2oxWMe9Tdyrdg==} + web-ext@8.5.0: + resolution: {integrity: sha512-Cf7MLD4GDBLapBojGCywcDuScm7hi+M8n7JzqDYX1oNTrVvFirfWcu+A6RCqRyu1siMu3jY4ul/hAu9Chkf1Aw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - webpack-cli@5.1.4: - resolution: {integrity: sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==} - engines: {node: '>=14.15.0'} + webpack-cli@6.0.1: + resolution: {integrity: sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==} + engines: {node: '>=18.12.0'} hasBin: true peerDependencies: - '@webpack-cli/generators': '*' - webpack: 5.x.x + webpack: ^5.82.0 webpack-bundle-analyzer: '*' webpack-dev-server: '*' peerDependenciesMeta: - '@webpack-cli/generators': - optional: true webpack-bundle-analyzer: optional: true webpack-dev-server: optional: true - webpack-merge@5.10.0: - resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} - engines: {node: '>=10.0.0'} - webpack-merge@6.0.1: resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} engines: {node: '>=18.0.0'} @@ -2791,8 +2937,8 @@ packages: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} - webpack@5.95.0: - resolution: {integrity: sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==} + webpack@5.98.0: + resolution: {integrity: sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -2815,22 +2961,20 @@ packages: resolution: {integrity: sha512-zDRAqDSBudazdfM9zpiI30Fu9ve47htYXcGi3ln0wfKu2a7SmrT6F3VDoYONu//48V8Vz4TdCRNPjtvyRO3yBA==} hasBin: true - which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true + which@4.0.0: + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} + hasBin: true + widest-line@5.0.0: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} - wildcard@2.0.0: - resolution: {integrity: sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==} - wildcard@2.0.1: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} @@ -2848,8 +2992,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + ws@8.18.1: + resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2876,9 +3020,6 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -2893,6 +3034,10 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yauzl@3.2.0: + resolution: {integrity: sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==} + engines: {node: '>=12'} + yazl@2.5.1: resolution: {integrity: sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==} @@ -2921,7 +3066,7 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.0 - '@babel/runtime@7.25.6': + '@babel/runtime@7.26.10': dependencies: regenerator-runtime: 0.14.1 @@ -2931,7 +3076,7 @@ snapshots: '@devicefarmer/adbkit-monkey@1.2.1': {} - '@devicefarmer/adbkit@3.2.6': + '@devicefarmer/adbkit@3.3.8': dependencies: '@devicefarmer/adbkit-logcat': 2.1.2 '@devicefarmer/adbkit-monkey': 1.2.1 @@ -2943,29 +3088,49 @@ snapshots: transitivePeerDependencies: - supports-color - '@discoveryjs/json-ext@0.5.7': {} + '@discoveryjs/json-ext@0.6.3': {} + + '@dotenvx/dotenvx@1.39.0': + dependencies: + commander: 11.1.0 + dotenv: 16.4.7 + eciesjs: 0.4.14 + execa: 5.1.1 + fdir: 6.4.3(picomatch@4.0.2) + ignore: 5.3.2 + object-treeify: 1.1.33 + picomatch: 4.0.2 + which: 4.0.0 + + '@ecies/ciphers@0.2.2(@noble/ciphers@1.2.1)': + dependencies: + '@noble/ciphers': 1.2.1 '@eslint-community/eslint-utils@4.4.0(eslint@8.57.1)': dependencies: eslint: 8.57.1 eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.4.0(eslint@9.13.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.22.0)': dependencies: - eslint: 9.13.0 + eslint: 9.22.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.11.1': {} + '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.18.0': + '@eslint/config-array@0.19.2': dependencies: - '@eslint/object-schema': 2.1.4 + '@eslint/object-schema': 2.1.6 debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/core@0.7.0': {} + '@eslint/config-helpers@0.1.0': {} + + '@eslint/core@0.12.0': + dependencies: + '@types/json-schema': 7.0.15 '@eslint/eslintrc@2.1.4': dependencies: @@ -2973,7 +3138,7 @@ snapshots: debug: 4.3.4 espree: 9.6.1 globals: 13.23.0 - ignore: 5.2.4 + ignore: 5.3.2 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -2981,13 +3146,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/eslintrc@3.1.0': + '@eslint/eslintrc@3.3.0': dependencies: ajv: 6.12.6 debug: 4.3.4 - espree: 10.2.0 + espree: 10.3.0 globals: 14.0.0 - ignore: 5.2.4 + ignore: 5.3.2 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -2997,21 +3162,24 @@ snapshots: '@eslint/js@8.57.1': {} - '@eslint/js@9.13.0': {} + '@eslint/js@9.22.0': {} - '@eslint/object-schema@2.1.4': {} + '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.2.0': + '@eslint/plugin-kit@0.2.7': dependencies: + '@eslint/core': 0.12.0 levn: 0.4.1 '@fluent/syntax@0.19.0': {} - '@humanfs/core@0.19.0': {} + '@fregante/relaxed-json@2.0.0': {} + + '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.5': + '@humanfs/node@0.16.6': dependencies: - '@humanfs/core': 0.19.0 + '@humanfs/core': 0.19.1 '@humanwhocodes/retry': 0.3.1 '@humanwhocodes/config-array@0.13.0': @@ -3028,11 +3196,13 @@ snapshots: '@humanwhocodes/retry@0.3.1': {} + '@humanwhocodes/retry@0.4.2': {} + '@jridgewell/gen-mapping@0.3.2': dependencies: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.22 + '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/resolve-uri@3.1.0': {} @@ -3041,27 +3211,24 @@ snapshots: '@jridgewell/source-map@0.3.5': dependencies: '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.22 + '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/sourcemap-codec@1.4.14': {} - '@jridgewell/trace-mapping@0.3.22': + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 - '@mdn/browser-compat-data@5.6.0': {} + '@mdn/browser-compat-data@5.7.3': {} - '@mole-inc/bin-wrapper@8.0.1': + '@noble/ciphers@1.2.1': {} + + '@noble/curves@1.8.1': dependencies: - bin-check: 4.1.0 - bin-version-check: 5.1.0 - content-disposition: 0.5.4 - ext-name: 5.0.0 - file-type: 17.1.6 - filenamify: 5.1.1 - got: 11.8.6 - os-filter-obj: 2.0.0 + '@noble/hashes': 1.7.1 + + '@noble/hashes@1.7.1': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -3087,94 +3254,97 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@sindresorhus/is@4.6.0': {} + '@sec-ant/readable-stream@0.4.1': {} - '@sindresorhus/merge-streams@2.3.0': {} + '@sindresorhus/is@5.6.0': {} - '@swc/cli@0.4.0(@swc/core@1.7.39)': + '@swc/cli@0.6.0(@swc/core@1.11.11)': dependencies: - '@mole-inc/bin-wrapper': 8.0.1 - '@swc/core': 1.7.39 + '@swc/core': 1.11.11 '@swc/counter': 0.1.3 + '@xhmikosr/bin-wrapper': 13.0.5 commander: 8.3.0 fast-glob: 3.3.2 minimatch: 9.0.3 piscina: 4.3.2 - semver: 7.5.4 + semver: 7.6.3 slash: 3.0.0 source-map: 0.7.4 - '@swc/core-darwin-arm64@1.7.39': + '@swc/core-darwin-arm64@1.11.11': optional: true - '@swc/core-darwin-x64@1.7.39': + '@swc/core-darwin-x64@1.11.11': optional: true - '@swc/core-linux-arm-gnueabihf@1.7.39': + '@swc/core-linux-arm-gnueabihf@1.11.11': optional: true - '@swc/core-linux-arm64-gnu@1.7.39': + '@swc/core-linux-arm64-gnu@1.11.11': optional: true - '@swc/core-linux-arm64-musl@1.7.39': + '@swc/core-linux-arm64-musl@1.11.11': optional: true - '@swc/core-linux-x64-gnu@1.7.39': + '@swc/core-linux-x64-gnu@1.11.11': optional: true - '@swc/core-linux-x64-musl@1.7.39': + '@swc/core-linux-x64-musl@1.11.11': optional: true - '@swc/core-win32-arm64-msvc@1.7.39': + '@swc/core-win32-arm64-msvc@1.11.11': optional: true - '@swc/core-win32-ia32-msvc@1.7.39': + '@swc/core-win32-ia32-msvc@1.11.11': optional: true - '@swc/core-win32-x64-msvc@1.7.39': + '@swc/core-win32-x64-msvc@1.11.11': optional: true - '@swc/core@1.7.39': + '@swc/core@1.11.11': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.13 + '@swc/types': 0.1.19 optionalDependencies: - '@swc/core-darwin-arm64': 1.7.39 - '@swc/core-darwin-x64': 1.7.39 - '@swc/core-linux-arm-gnueabihf': 1.7.39 - '@swc/core-linux-arm64-gnu': 1.7.39 - '@swc/core-linux-arm64-musl': 1.7.39 - '@swc/core-linux-x64-gnu': 1.7.39 - '@swc/core-linux-x64-musl': 1.7.39 - '@swc/core-win32-arm64-msvc': 1.7.39 - '@swc/core-win32-ia32-msvc': 1.7.39 - '@swc/core-win32-x64-msvc': 1.7.39 + '@swc/core-darwin-arm64': 1.11.11 + '@swc/core-darwin-x64': 1.11.11 + '@swc/core-linux-arm-gnueabihf': 1.11.11 + '@swc/core-linux-arm64-gnu': 1.11.11 + '@swc/core-linux-arm64-musl': 1.11.11 + '@swc/core-linux-x64-gnu': 1.11.11 + '@swc/core-linux-x64-musl': 1.11.11 + '@swc/core-win32-arm64-msvc': 1.11.11 + '@swc/core-win32-ia32-msvc': 1.11.11 + '@swc/core-win32-x64-msvc': 1.11.11 '@swc/counter@0.1.3': {} - '@swc/types@0.1.13': + '@swc/types@0.1.19': dependencies: '@swc/counter': 0.1.3 - '@szmarczak/http-timer@4.0.6': + '@szmarczak/http-timer@5.0.1': dependencies: defer-to-connect: 2.0.1 '@tokenizer/token@0.3.0': {} - '@types/cacheable-request@6.0.3': - dependencies: - '@types/http-cache-semantics': 4.0.1 - '@types/keyv': 3.1.4 - '@types/node': 22.7.9 - '@types/responselike': 1.0.3 - - '@types/chrome@0.0.279': + '@types/chrome@0.0.309': dependencies: '@types/filesystem': 0.0.32 '@types/har-format': 1.2.10 - '@types/estree@1.0.5': {} + '@types/eslint-config-prettier@6.11.3': {} + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.6 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 '@types/estree@1.0.6': {} @@ -3190,33 +3360,27 @@ snapshots: '@types/har-format@1.2.10': {} - '@types/http-cache-semantics@4.0.1': {} + '@types/http-cache-semantics@4.0.4': {} '@types/json-schema@7.0.11': {} '@types/json-schema@7.0.15': {} - '@types/keyv@3.1.4': - dependencies: - '@types/node': 22.7.9 - '@types/minimatch@3.0.5': {} - '@types/node@22.7.9': + '@types/node@22.13.10': dependencies: - undici-types: 6.19.8 + undici-types: 6.20.0 - '@types/responselike@1.0.3': - dependencies: - '@types/node': 22.7.9 + '@types/tampermonkey@5.0.4': {} - '@types/tampermonkey@5.0.3': {} + '@types/trusted-types@2.0.7': {} - '@types/webpack@5.28.5(@swc/core@1.7.39)(webpack-cli@5.1.4)': + '@types/webpack@5.28.5(@swc/core@1.11.11)(webpack-cli@6.0.1)': dependencies: - '@types/node': 22.7.9 + '@types/node': 22.13.10 tapable: 2.2.1 - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) transitivePeerDependencies: - '@swc/core' - esbuild @@ -3225,102 +3389,170 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 22.7.9 + '@types/node': 22.13.10 '@ungap/structured-clone@1.2.0': {} '@violentmonkey/types@0.2.0': {} - '@webassemblyjs/ast@1.12.1': + '@webassemblyjs/ast@1.14.1': dependencies: - '@webassemblyjs/helper-numbers': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/floating-point-hex-parser@1.11.6': {} + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} - '@webassemblyjs/helper-api-error@1.11.6': {} + '@webassemblyjs/helper-api-error@1.13.2': {} - '@webassemblyjs/helper-buffer@1.12.1': {} + '@webassemblyjs/helper-buffer@1.14.1': {} - '@webassemblyjs/helper-numbers@1.11.6': + '@webassemblyjs/helper-numbers@1.13.2': dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.11.6 - '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 '@xtuc/long': 4.2.2 - '@webassemblyjs/helper-wasm-bytecode@1.11.6': {} + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.98.0)': + dependencies: + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.98.0) - '@webassemblyjs/helper-wasm-section@1.12.1': + '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.98.0)': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/wasm-gen': 1.12.1 + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.98.0) - '@webassemblyjs/ieee754@1.11.6': + '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack@5.98.0)': dependencies: - '@xtuc/ieee754': 1.2.0 + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.98.0) - '@webassemblyjs/leb128@1.11.6': + '@xhmikosr/archive-type@7.0.0': dependencies: - '@xtuc/long': 4.2.2 + file-type: 19.6.0 - '@webassemblyjs/utf8@1.11.6': {} + '@xhmikosr/bin-check@7.0.3': + dependencies: + execa: 5.1.1 + isexe: 2.0.0 - '@webassemblyjs/wasm-edit@1.12.1': + '@xhmikosr/bin-wrapper@13.0.5': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/helper-wasm-section': 1.12.1 - '@webassemblyjs/wasm-gen': 1.12.1 - '@webassemblyjs/wasm-opt': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - '@webassemblyjs/wast-printer': 1.12.1 + '@xhmikosr/bin-check': 7.0.3 + '@xhmikosr/downloader': 15.0.1 + '@xhmikosr/os-filter-obj': 3.0.0 + bin-version-check: 5.1.0 - '@webassemblyjs/wasm-gen@1.12.1': + '@xhmikosr/decompress-tar@8.0.1': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/ieee754': 1.11.6 - '@webassemblyjs/leb128': 1.11.6 - '@webassemblyjs/utf8': 1.11.6 + file-type: 19.6.0 + is-stream: 2.0.1 + tar-stream: 3.1.7 - '@webassemblyjs/wasm-opt@1.12.1': + '@xhmikosr/decompress-tarbz2@8.0.1': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/wasm-gen': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 + '@xhmikosr/decompress-tar': 8.0.1 + file-type: 19.6.0 + is-stream: 2.0.1 + seek-bzip: 2.0.0 + unbzip2-stream: 1.4.3 - '@webassemblyjs/wasm-parser@1.12.1': + '@xhmikosr/decompress-targz@8.0.1': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-api-error': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/ieee754': 1.11.6 - '@webassemblyjs/leb128': 1.11.6 - '@webassemblyjs/utf8': 1.11.6 + '@xhmikosr/decompress-tar': 8.0.1 + file-type: 19.6.0 + is-stream: 2.0.1 - '@webassemblyjs/wast-printer@1.12.1': + '@xhmikosr/decompress-unzip@7.0.0': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@xtuc/long': 4.2.2 + file-type: 19.6.0 + get-stream: 6.0.1 + yauzl: 3.2.0 - '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.95.0)': + '@xhmikosr/decompress@10.0.1': dependencies: - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack@5.95.0) + '@xhmikosr/decompress-tar': 8.0.1 + '@xhmikosr/decompress-tarbz2': 8.0.1 + '@xhmikosr/decompress-targz': 8.0.1 + '@xhmikosr/decompress-unzip': 7.0.0 + graceful-fs: 4.2.11 + make-dir: 4.0.0 + strip-dirs: 3.0.0 - '@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.95.0)': + '@xhmikosr/downloader@15.0.1': dependencies: - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack@5.95.0) + '@xhmikosr/archive-type': 7.0.0 + '@xhmikosr/decompress': 10.0.1 + content-disposition: 0.5.4 + defaults: 3.0.0 + ext-name: 5.0.0 + file-type: 19.6.0 + filenamify: 6.0.0 + get-stream: 6.0.1 + got: 13.0.0 - '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack@5.95.0)': + '@xhmikosr/os-filter-obj@3.0.0': dependencies: - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack@5.95.0) + arch: 3.0.0 '@xtuc/ieee754@1.2.0': {} @@ -3330,26 +3562,19 @@ snapshots: dependencies: event-target-shim: 5.0.1 - acorn-import-attributes@1.9.5(acorn@8.11.3): - dependencies: - acorn: 8.11.3 - - acorn-jsx@5.3.2(acorn@8.12.1): + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: - acorn: 8.12.1 - - acorn@8.10.0: {} + acorn: 8.14.0 - acorn@8.11.3: {} + acorn@8.14.0: {} - acorn@8.12.1: {} - - addons-linter@7.1.0: + addons-linter@7.9.0: dependencies: '@fluent/syntax': 0.19.0 - '@mdn/browser-compat-data': 5.6.0 + '@fregante/relaxed-json': 2.0.0 + '@mdn/browser-compat-data': 5.7.3 addons-moz-compare: 1.3.0 - addons-scanner-utils: 9.11.0 + addons-scanner-utils: 9.12.0 ajv: 8.17.1 chalk: 4.1.2 cheerio: 1.0.0-rc.12 @@ -3357,18 +3582,15 @@ snapshots: common-tags: 1.8.2 deepmerge: 4.3.1 eslint: 8.57.1 - eslint-plugin-no-unsanitized: 4.0.2(eslint@8.57.1) - eslint-visitor-keys: 4.0.0 - espree: 10.1.0 + eslint-plugin-no-unsanitized: 4.1.2(eslint@8.57.1) + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 esprima: 4.0.1 fast-json-patch: 3.1.1 - image-size: 1.1.1 - jed: 1.1.1 + image-size: 2.0.1 json-merge-patch: 1.0.2 - os-locale: 5.0.0 pino: 8.20.0 - relaxed-json: 1.0.3 - semver: 7.6.3 + semver: 7.7.1 source-map-support: 0.5.21 upath: 2.0.1 yargs: 17.7.2 @@ -3382,7 +3604,7 @@ snapshots: addons-moz-compare@1.3.0: {} - addons-scanner-utils@9.11.0: + addons-scanner-utils@9.12.0: dependencies: '@types/yauzl': 2.10.3 common-tags: 1.8.2 @@ -3403,15 +3625,20 @@ snapshots: optionalDependencies: ajv: 8.12.0 - ajv-keywords@3.5.2(ajv@6.12.6): - dependencies: - ajv: 6.12.6 + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 ajv-keywords@5.1.0(ajv@8.12.0): dependencies: ajv: 8.12.0 fast-deep-equal: 3.1.3 + ajv-keywords@5.1.0(ajv@8.17.1): + dependencies: + ajv: 8.17.1 + fast-deep-equal: 3.1.3 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3451,7 +3678,7 @@ snapshots: ansi-styles@6.2.1: {} - arch@2.2.0: {} + arch@3.0.0: {} argparse@2.0.1: {} @@ -3472,8 +3699,13 @@ snapshots: stubborn-fs: 1.2.5 when-exit: 2.1.3 + b4a@1.6.7: {} + balanced-match@1.0.2: {} + bare-events@2.5.4: + optional: true + base64-js@1.5.1: {} basic-auth@2.0.1: @@ -3482,15 +3714,10 @@ snapshots: big-integer@1.6.52: {} - bin-check@4.1.0: - dependencies: - execa: 0.7.0 - executable: 4.1.1 - bin-version-check@5.1.0: dependencies: bin-version: 6.0.0 - semver: 7.5.4 + semver: 7.6.3 semver-truncate: 3.0.0 bin-version@6.0.0: @@ -3530,12 +3757,12 @@ snapshots: dependencies: fill-range: 7.0.1 - browserslist@4.24.2: + browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001669 - electron-to-chromium: 1.5.42 - node-releases: 2.0.18 - update-browserslist-db: 1.1.1(browserslist@4.24.2) + caniuse-lite: 1.0.30001692 + electron-to-chromium: 1.5.80 + node-releases: 2.0.19 + update-browserslist-db: 1.1.1(browserslist@4.24.4) buffer-builder@0.2.0: {} @@ -3543,6 +3770,11 @@ snapshots: buffer-from@1.1.2: {} + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -3552,17 +3784,17 @@ snapshots: dependencies: run-applescript: 5.0.0 - cacheable-lookup@5.0.4: {} + cacheable-lookup@7.0.0: {} - cacheable-request@7.0.4: + cacheable-request@10.2.14: dependencies: - clone-response: 1.0.3 - get-stream: 5.2.0 - http-cache-semantics: 4.1.0 + '@types/http-cache-semantics': 4.0.4 + get-stream: 6.0.1 + http-cache-semantics: 4.1.1 keyv: 4.5.4 - lowercase-keys: 2.0.0 - normalize-url: 6.1.0 - responselike: 2.0.1 + mimic-response: 4.0.0 + normalize-url: 8.0.1 + responselike: 3.0.0 call-bind@1.0.2: dependencies: @@ -3573,7 +3805,7 @@ snapshots: camelcase@8.0.0: {} - caniuse-lite@1.0.30001669: {} + caniuse-lite@1.0.30001692: {} chalk@2.4.2: dependencies: @@ -3609,7 +3841,7 @@ snapshots: chrome-launcher@1.1.2: dependencies: - '@types/node': 22.7.9 + '@types/node': 22.13.10 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 2.0.1 @@ -3632,10 +3864,6 @@ snapshots: kind-of: 6.0.3 shallow-clone: 3.0.1 - clone-response@1.0.3: - dependencies: - mimic-response: 1.0.1 - clone@1.0.4: {} color-convert@1.9.3: @@ -3659,7 +3887,9 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 - commander@10.0.1: {} + commander@11.1.0: {} + + commander@12.1.0: {} commander@2.20.3: {} @@ -3667,6 +3897,8 @@ snapshots: dependencies: graceful-readlink: 1.0.1 + commander@6.2.1: {} + commander@8.3.0: {} commander@9.5.0: {} @@ -3682,7 +3914,7 @@ snapshots: readable-stream: 2.3.7 typedarray: 0.0.6 - concurrently@9.0.1: + concurrently@9.1.2: dependencies: chalk: 4.1.2 lodash: 4.17.21 @@ -3708,27 +3940,26 @@ snapshots: dependencies: safe-buffer: 5.2.1 - copy-webpack-plugin@12.0.2(webpack@5.95.0): + copy-webpack-plugin@13.0.0(webpack@5.98.0): dependencies: - fast-glob: 3.3.2 glob-parent: 6.0.2 - globby: 14.0.1 normalize-path: 3.0.0 - schema-utils: 4.2.0 + schema-utils: 4.3.0 serialize-javascript: 6.0.2 - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) + tinyglobby: 0.2.12 + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) core-util-is@1.0.3: {} corser@2.0.1: {} - cross-spawn@5.1.0: + cross-spawn@7.0.3: dependencies: - lru-cache: 4.1.5 - shebang-command: 1.2.0 - which: 1.3.1 + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 - cross-spawn@7.0.3: + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 @@ -3740,7 +3971,7 @@ snapshots: pbf: 3.2.1 yazl: 2.5.1 - css-loader@7.1.2(webpack@5.95.0): + css-loader@7.1.2(webpack@5.98.0): dependencies: icss-utils: 5.1.0(postcss@8.4.35) postcss: 8.4.35 @@ -3751,7 +3982,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.5.4 optionalDependencies: - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) css-select@5.1.0: dependencies: @@ -3807,6 +4038,8 @@ snapshots: dependencies: clone: 1.0.4 + defaults@3.0.0: {} + defer-to-connect@2.0.1: {} define-lazy-prop@3.0.0: {} @@ -3837,18 +4070,21 @@ snapshots: dependencies: type-fest: 4.26.1 - dotenv@16.4.5: {} + dotenv@16.4.7: {} - electron-to-chromium@1.5.42: {} + eciesjs@0.4.14: + dependencies: + '@ecies/ciphers': 0.2.2(@noble/ciphers@1.2.1) + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + + electron-to-chromium@1.5.80: {} emoji-regex@10.4.0: {} emoji-regex@8.0.0: {} - end-of-stream@1.4.4: - dependencies: - once: 1.4.0 - enhanced-resolve@5.17.1: dependencies: graceful-fs: 4.2.11 @@ -3856,7 +4092,7 @@ snapshots: entities@4.4.0: {} - envinfo@7.8.1: {} + envinfo@7.14.0: {} error-ex@1.3.2: dependencies: @@ -3874,13 +4110,11 @@ snapshots: escape-string-regexp@4.0.0: {} - escape-string-regexp@5.0.0: {} - - eslint-config-prettier@9.1.0(eslint@9.13.0): + eslint-config-prettier@10.1.1(eslint@9.22.0): dependencies: - eslint: 9.13.0 + eslint: 9.22.0 - eslint-plugin-no-unsanitized@4.0.2(eslint@8.57.1): + eslint-plugin-no-unsanitized@4.1.2(eslint@8.57.1): dependencies: eslint: 8.57.1 @@ -3894,21 +4128,19 @@ snapshots: esrecurse: 4.3.0 estraverse: 5.3.0 - eslint-scope@8.1.0: + eslint-scope@8.3.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.0.0: {} - - eslint-visitor-keys@4.1.0: {} + eslint-visitor-keys@4.2.0: {} eslint@8.57.1: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) - '@eslint-community/regexpp': 4.11.1 + '@eslint-community/regexpp': 4.12.1 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.1 '@humanwhocodes/config-array': 0.13.0 @@ -3917,7 +4149,7 @@ snapshots: '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 debug: 4.3.4 doctrine: 3.0.0 escape-string-regexp: 4.0.0 @@ -3932,7 +4164,7 @@ snapshots: glob-parent: 6.0.2 globals: 13.23.0 graphemer: 1.4.0 - ignore: 5.2.4 + ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 @@ -3948,35 +4180,36 @@ snapshots: transitivePeerDependencies: - supports-color - eslint@9.13.0: - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.13.0) - '@eslint-community/regexpp': 4.11.1 - '@eslint/config-array': 0.18.0 - '@eslint/core': 0.7.0 - '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.13.0 - '@eslint/plugin-kit': 0.2.0 - '@humanfs/node': 0.16.5 + eslint@9.22.0: + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.22.0) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.19.2 + '@eslint/config-helpers': 0.1.0 + '@eslint/core': 0.12.0 + '@eslint/eslintrc': 3.3.0 + '@eslint/js': 9.22.0 + '@eslint/plugin-kit': 0.2.7 + '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/retry': 0.4.2 '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 debug: 4.3.4 escape-string-regexp: 4.0.0 - eslint-scope: 8.1.0 - eslint-visitor-keys: 4.1.0 - espree: 10.2.0 + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - ignore: 5.2.4 + ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 @@ -3984,26 +4217,19 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.3 - text-table: 0.2.0 transitivePeerDependencies: - supports-color - espree@10.1.0: + espree@10.3.0: dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) - eslint-visitor-keys: 4.1.0 - - espree@10.2.0: - dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) - eslint-visitor-keys: 4.1.0 + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 espree@9.6.1: dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -4028,31 +4254,9 @@ snapshots: events@3.3.0: {} - execa@0.7.0: - dependencies: - cross-spawn: 5.1.0 - get-stream: 3.0.0 - is-stream: 1.1.0 - npm-run-path: 2.0.2 - p-finally: 1.0.0 - signal-exit: 3.0.7 - strip-eof: 1.0.0 - - execa@4.1.0: - dependencies: - cross-spawn: 7.0.3 - get-stream: 5.2.0 - human-signals: 1.1.1 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - execa@5.1.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 6.0.1 human-signals: 2.1.0 is-stream: 2.0.1 @@ -4064,7 +4268,7 @@ snapshots: execa@7.2.0: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 6.0.1 human-signals: 4.3.1 is-stream: 3.0.0 @@ -4074,10 +4278,6 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 3.0.0 - executable@4.1.1: - dependencies: - pify: 2.3.0 - ext-list@2.2.2: dependencies: mime-db: 1.52.0 @@ -4089,6 +4289,8 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-fifo@1.3.2: {} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4117,6 +4319,10 @@ snapshots: dependencies: pend: 1.2.0 + fdir@6.4.3(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + file-entry-cache@6.0.1: dependencies: flat-cache: 3.0.4 @@ -4125,19 +4331,18 @@ snapshots: dependencies: flat-cache: 4.0.1 - file-type@17.1.6: + file-type@19.6.0: dependencies: - readable-web-to-node-stream: 3.0.2 - strtok3: 7.0.0 - token-types: 5.0.1 + get-stream: 9.0.1 + strtok3: 9.1.1 + token-types: 6.0.0 + uint8array-extras: 1.4.0 filename-reserved-regex@3.0.0: {} - filenamify@5.1.1: + filenamify@6.0.0: dependencies: filename-reserved-regex: 3.0.0 - strip-outer: 2.0.0 - trim-repeated: 2.0.0 fill-range@7.0.1: dependencies: @@ -4183,6 +4388,8 @@ snapshots: follow-redirects@1.15.2: {} + form-data-encoder@2.1.4: {} + fs-extra@11.2.0: dependencies: graceful-fs: 4.2.11 @@ -4212,13 +4419,12 @@ snapshots: has: 1.0.3 has-symbols: 1.0.3 - get-stream@3.0.0: {} + get-stream@6.0.1: {} - get-stream@5.2.0: + get-stream@9.0.1: dependencies: - pump: 3.0.0 - - get-stream@6.0.1: {} + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 glob-parent@5.1.2: dependencies: @@ -4249,30 +4455,21 @@ snapshots: globals@14.0.0: {} - globals@15.11.0: {} + globals@16.0.0: {} - globby@14.0.1: + got@13.0.0: dependencies: - '@sindresorhus/merge-streams': 2.3.0 - fast-glob: 3.3.2 - ignore: 5.2.4 - path-type: 5.0.0 - slash: 5.1.0 - unicorn-magic: 0.1.0 - - got@11.8.6: - dependencies: - '@sindresorhus/is': 4.6.0 - '@szmarczak/http-timer': 4.0.6 - '@types/cacheable-request': 6.0.3 - '@types/responselike': 1.0.3 - cacheable-lookup: 5.0.4 - cacheable-request: 7.0.4 + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 decompress-response: 6.0.0 - http2-wrapper: 1.0.3 - lowercase-keys: 2.0.0 - p-cancelable: 2.1.1 - responselike: 2.0.1 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 graceful-fs@4.2.10: {} @@ -4307,7 +4504,7 @@ snapshots: domutils: 3.0.1 entities: 4.4.0 - http-cache-semantics@4.1.0: {} + http-cache-semantics@4.1.1: {} http-proxy@1.18.1: dependencies: @@ -4336,7 +4533,7 @@ snapshots: - debug - supports-color - http2-wrapper@1.0.3: + http2-wrapper@2.2.1: dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 @@ -4348,8 +4545,6 @@ snapshots: transitivePeerDependencies: - supports-color - human-signals@1.1.1: {} - human-signals@2.1.0: {} human-signals@4.3.1: {} @@ -4364,15 +4559,13 @@ snapshots: ieee754@1.2.1: {} - ignore@5.2.4: {} + ignore@5.3.2: {} - image-size@1.1.1: - dependencies: - queue: 6.0.2 + image-size@2.0.1: {} immediate@3.0.6: {} - immutable@4.2.2: {} + immutable@5.0.3: {} import-fresh@3.3.0: dependencies: @@ -4399,9 +4592,11 @@ snapshots: ini@4.1.3: {} - interpret@3.1.1: {} + inspect-with-kind@1.0.5: + dependencies: + kind-of: 6.0.3 - invert-kv@3.0.1: {} + interpret@3.1.1: {} is-absolute@0.1.7: dependencies: @@ -4452,12 +4647,12 @@ snapshots: is-relative@0.1.3: {} - is-stream@1.1.0: {} - is-stream@2.0.1: {} is-stream@3.0.0: {} + is-stream@4.0.1: {} + is-utf8@0.2.1: {} is-wsl@2.2.0: @@ -4470,17 +4665,17 @@ snapshots: isexe@2.0.0: {} - isobject@3.0.1: {} + isexe@3.1.1: {} - jed@1.1.1: {} + isobject@3.0.1: {} jest-worker@27.5.1: dependencies: - '@types/node': 22.7.9 + '@types/node': 22.13.10 merge-stream: 2.0.0 supports-color: 8.1.1 - jose@5.9.2: {} + jose@5.9.6: {} js-tokens@4.0.0: {} @@ -4529,10 +4724,6 @@ snapshots: dependencies: package-json: 10.0.1 - lcid@3.1.1: - dependencies: - invert-kv: 3.0.1 - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -4565,30 +4756,19 @@ snapshots: lodash@4.17.21: {} - lowercase-keys@2.0.0: {} - - lru-cache@4.1.5: - dependencies: - pseudomap: 1.0.2 - yallist: 2.1.2 + lowercase-keys@3.0.0: {} lru-cache@6.0.0: dependencies: yallist: 4.0.0 - make-error@1.3.6: {} - - map-age-cleaner@0.1.3: + make-dir@4.0.0: dependencies: - p-defer: 1.0.0 + semver: 7.6.3 - marky@1.2.5: {} + make-error@1.3.6: {} - mem@5.1.1: - dependencies: - map-age-cleaner: 0.1.3 - mimic-fn: 2.1.0 - p-is-promise: 2.1.0 + marky@1.2.5: {} merge-stream@2.0.0: {} @@ -4611,15 +4791,15 @@ snapshots: mimic-fn@4.0.0: {} - mimic-response@1.0.1: {} - mimic-response@3.1.0: {} - mini-css-extract-plugin@2.9.1(webpack@5.95.0): + mimic-response@4.0.0: {} + + mini-css-extract-plugin@2.9.2(webpack@5.98.0): dependencies: schema-utils: 4.2.0 tapable: 2.2.1 - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) minimatch@3.1.2: dependencies: @@ -4635,7 +4815,7 @@ snapshots: mkdirp@0.5.6: dependencies: - minimist: 1.2.7 + minimist: 1.2.8 mri@1.2.0: {} @@ -4676,20 +4856,16 @@ snapshots: dependencies: growly: 1.3.0 is-wsl: 2.2.0 - semver: 7.5.4 + semver: 7.6.3 shellwords: 0.1.1 uuid: 8.3.2 which: 2.0.2 - node-releases@2.0.18: {} + node-releases@2.0.19: {} normalize-path@3.0.0: {} - normalize-url@6.1.0: {} - - npm-run-path@2.0.2: - dependencies: - path-key: 2.0.1 + normalize-url@8.0.1: {} npm-run-path@4.0.1: dependencies: @@ -4705,6 +4881,8 @@ snapshots: object-inspect@1.12.3: {} + object-treeify@1.1.33: {} + on-exit-leak-free@2.1.0: {} once@1.4.0: @@ -4737,25 +4915,9 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - os-filter-obj@2.0.0: - dependencies: - arch: 2.2.0 - - os-locale@5.0.0: - dependencies: - execa: 4.1.0 - lcid: 3.1.1 - mem: 5.1.1 - os-shim@0.1.3: {} - p-cancelable@2.1.1: {} - - p-defer@1.0.0: {} - - p-finally@1.0.0: {} - - p-is-promise@2.1.0: {} + p-cancelable@3.0.0: {} p-limit@2.3.0: dependencies: @@ -4811,22 +4973,18 @@ snapshots: path-is-absolute@1.0.1: {} - path-key@2.0.1: {} - path-key@3.1.1: {} path-key@4.0.0: {} path-parse@1.0.7: {} - path-type@5.0.0: {} - pbf@3.2.1: dependencies: ieee754: 1.2.1 resolve-protobuf-schema: 2.1.0 - peek-readable@5.0.0: {} + peek-readable@5.3.1: {} pend@1.2.0: {} @@ -4836,12 +4994,7 @@ snapshots: picomatch@2.3.1: {} - pify@2.3.0: {} - - pino-abstract-transport@1.1.0: - dependencies: - readable-stream: 4.3.0 - split2: 4.1.0 + picomatch@4.0.2: {} pino-abstract-transport@1.2.0: dependencies: @@ -4857,7 +5010,7 @@ snapshots: atomic-sleep: 1.0.0 fast-redact: 3.1.2 on-exit-leak-free: 2.1.0 - pino-abstract-transport: 1.1.0 + pino-abstract-transport: 1.2.0 pino-std-serializers: 6.1.0 process-warning: 3.0.0 quick-format-unescaped: 4.0.4 @@ -4932,7 +5085,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.3.3: {} + prettier@3.5.3: {} process-nextick-args@2.0.1: {} @@ -4950,13 +5103,6 @@ snapshots: protocol-buffers-schema@3.6.0: {} - pseudomap@1.0.2: {} - - pump@3.0.0: - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - punycode@2.3.0: {} pupa@3.1.0: @@ -4969,9 +5115,7 @@ snapshots: queue-microtask@1.2.3: {} - queue@6.0.2: - dependencies: - inherits: 2.0.4 + queue-tick@1.0.1: {} quick-format-unescaped@4.0.4: {} @@ -4985,7 +5129,7 @@ snapshots: dependencies: deep-extend: 0.6.0 ini: 1.3.8 - minimist: 1.2.7 + minimist: 1.2.8 strip-json-comments: 2.0.1 readable-stream@2.3.7: @@ -4998,12 +5142,6 @@ snapshots: string_decoder: 1.1.1 util-deprecate: 1.0.2 - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - readable-stream@4.3.0: dependencies: abort-controller: 3.0.0 @@ -5011,10 +5149,6 @@ snapshots: events: 3.3.0 process: 0.11.10 - readable-web-to-node-stream@3.0.2: - dependencies: - readable-stream: 3.6.2 - real-require@0.2.0: {} rechoir@0.8.0: @@ -5031,11 +5165,6 @@ snapshots: dependencies: rc: 1.2.8 - relaxed-json@1.0.3: - dependencies: - chalk: 2.4.2 - commander: 2.20.3 - require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -5062,9 +5191,9 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - responselike@2.0.1: + responselike@3.0.0: dependencies: - lowercase-keys: 2.0.0 + lowercase-keys: 3.0.0 reusify@1.0.4: {} @@ -5092,112 +5221,199 @@ snapshots: safer-buffer@2.1.2: {} - sass-embedded-android-arm64@1.80.4: + sass-embedded-android-arm64@1.85.1: + optional: true + + sass-embedded-android-arm64@1.86.0: + optional: true + + sass-embedded-android-arm@1.85.1: + optional: true + + sass-embedded-android-arm@1.86.0: + optional: true + + sass-embedded-android-ia32@1.85.1: + optional: true + + sass-embedded-android-ia32@1.86.0: + optional: true + + sass-embedded-android-riscv64@1.85.1: + optional: true + + sass-embedded-android-riscv64@1.86.0: + optional: true + + sass-embedded-android-x64@1.85.1: + optional: true + + sass-embedded-android-x64@1.86.0: + optional: true + + sass-embedded-darwin-arm64@1.85.1: + optional: true + + sass-embedded-darwin-arm64@1.86.0: + optional: true + + sass-embedded-darwin-x64@1.85.1: + optional: true + + sass-embedded-darwin-x64@1.86.0: + optional: true + + sass-embedded-linux-arm64@1.85.1: + optional: true + + sass-embedded-linux-arm64@1.86.0: + optional: true + + sass-embedded-linux-arm@1.85.1: + optional: true + + sass-embedded-linux-arm@1.86.0: + optional: true + + sass-embedded-linux-ia32@1.85.1: + optional: true + + sass-embedded-linux-ia32@1.86.0: + optional: true + + sass-embedded-linux-musl-arm64@1.85.1: optional: true - sass-embedded-android-arm@1.80.4: + sass-embedded-linux-musl-arm64@1.86.0: optional: true - sass-embedded-android-ia32@1.80.4: + sass-embedded-linux-musl-arm@1.85.1: optional: true - sass-embedded-android-riscv64@1.80.4: + sass-embedded-linux-musl-arm@1.86.0: optional: true - sass-embedded-android-x64@1.80.4: + sass-embedded-linux-musl-ia32@1.85.1: optional: true - sass-embedded-darwin-arm64@1.80.4: + sass-embedded-linux-musl-ia32@1.86.0: optional: true - sass-embedded-darwin-x64@1.80.4: + sass-embedded-linux-musl-riscv64@1.85.1: optional: true - sass-embedded-linux-arm64@1.80.4: + sass-embedded-linux-musl-riscv64@1.86.0: optional: true - sass-embedded-linux-arm@1.80.4: + sass-embedded-linux-musl-x64@1.85.1: optional: true - sass-embedded-linux-ia32@1.80.4: + sass-embedded-linux-musl-x64@1.86.0: optional: true - sass-embedded-linux-musl-arm64@1.80.4: + sass-embedded-linux-riscv64@1.85.1: optional: true - sass-embedded-linux-musl-arm@1.80.4: + sass-embedded-linux-riscv64@1.86.0: optional: true - sass-embedded-linux-musl-ia32@1.80.4: + sass-embedded-linux-x64@1.85.1: optional: true - sass-embedded-linux-musl-riscv64@1.80.4: + sass-embedded-linux-x64@1.86.0: optional: true - sass-embedded-linux-musl-x64@1.80.4: + sass-embedded-win32-arm64@1.85.1: optional: true - sass-embedded-linux-riscv64@1.80.4: + sass-embedded-win32-arm64@1.86.0: optional: true - sass-embedded-linux-x64@1.80.4: + sass-embedded-win32-ia32@1.85.1: optional: true - sass-embedded-win32-arm64@1.80.4: + sass-embedded-win32-ia32@1.86.0: optional: true - sass-embedded-win32-ia32@1.80.4: + sass-embedded-win32-x64@1.85.1: optional: true - sass-embedded-win32-x64@1.80.4: + sass-embedded-win32-x64@1.86.0: optional: true - sass-embedded@1.80.4: + sass-embedded@1.85.1: + dependencies: + '@bufbuild/protobuf': 2.2.0 + buffer-builder: 0.2.0 + colorjs.io: 0.5.2 + immutable: 5.0.3 + rxjs: 7.8.1 + supports-color: 8.1.1 + sync-child-process: 1.0.2 + varint: 6.0.0 + optionalDependencies: + sass-embedded-android-arm: 1.85.1 + sass-embedded-android-arm64: 1.85.1 + sass-embedded-android-ia32: 1.85.1 + sass-embedded-android-riscv64: 1.85.1 + sass-embedded-android-x64: 1.85.1 + sass-embedded-darwin-arm64: 1.85.1 + sass-embedded-darwin-x64: 1.85.1 + sass-embedded-linux-arm: 1.85.1 + sass-embedded-linux-arm64: 1.85.1 + sass-embedded-linux-ia32: 1.85.1 + sass-embedded-linux-musl-arm: 1.85.1 + sass-embedded-linux-musl-arm64: 1.85.1 + sass-embedded-linux-musl-ia32: 1.85.1 + sass-embedded-linux-musl-riscv64: 1.85.1 + sass-embedded-linux-musl-x64: 1.85.1 + sass-embedded-linux-riscv64: 1.85.1 + sass-embedded-linux-x64: 1.85.1 + sass-embedded-win32-arm64: 1.85.1 + sass-embedded-win32-ia32: 1.85.1 + sass-embedded-win32-x64: 1.85.1 + + sass-embedded@1.86.0: dependencies: '@bufbuild/protobuf': 2.2.0 buffer-builder: 0.2.0 colorjs.io: 0.5.2 - immutable: 4.2.2 + immutable: 5.0.3 rxjs: 7.8.1 supports-color: 8.1.1 + sync-child-process: 1.0.2 varint: 6.0.0 optionalDependencies: - sass-embedded-android-arm: 1.80.4 - sass-embedded-android-arm64: 1.80.4 - sass-embedded-android-ia32: 1.80.4 - sass-embedded-android-riscv64: 1.80.4 - sass-embedded-android-x64: 1.80.4 - sass-embedded-darwin-arm64: 1.80.4 - sass-embedded-darwin-x64: 1.80.4 - sass-embedded-linux-arm: 1.80.4 - sass-embedded-linux-arm64: 1.80.4 - sass-embedded-linux-ia32: 1.80.4 - sass-embedded-linux-musl-arm: 1.80.4 - sass-embedded-linux-musl-arm64: 1.80.4 - sass-embedded-linux-musl-ia32: 1.80.4 - sass-embedded-linux-musl-riscv64: 1.80.4 - sass-embedded-linux-musl-x64: 1.80.4 - sass-embedded-linux-riscv64: 1.80.4 - sass-embedded-linux-x64: 1.80.4 - sass-embedded-win32-arm64: 1.80.4 - sass-embedded-win32-ia32: 1.80.4 - sass-embedded-win32-x64: 1.80.4 - - sass-loader@16.0.2(sass-embedded@1.80.4)(webpack@5.95.0): + sass-embedded-android-arm: 1.86.0 + sass-embedded-android-arm64: 1.86.0 + sass-embedded-android-ia32: 1.86.0 + sass-embedded-android-riscv64: 1.86.0 + sass-embedded-android-x64: 1.86.0 + sass-embedded-darwin-arm64: 1.86.0 + sass-embedded-darwin-x64: 1.86.0 + sass-embedded-linux-arm: 1.86.0 + sass-embedded-linux-arm64: 1.86.0 + sass-embedded-linux-ia32: 1.86.0 + sass-embedded-linux-musl-arm: 1.86.0 + sass-embedded-linux-musl-arm64: 1.86.0 + sass-embedded-linux-musl-ia32: 1.86.0 + sass-embedded-linux-musl-riscv64: 1.86.0 + sass-embedded-linux-musl-x64: 1.86.0 + sass-embedded-linux-riscv64: 1.86.0 + sass-embedded-linux-x64: 1.86.0 + sass-embedded-win32-arm64: 1.86.0 + sass-embedded-win32-ia32: 1.86.0 + sass-embedded-win32-x64: 1.86.0 + + sass-loader@16.0.5(sass-embedded@1.86.0)(webpack@5.98.0): dependencies: neo-async: 2.6.2 optionalDependencies: - sass-embedded: 1.80.4 - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) + sass-embedded: 1.86.0 + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) sax@1.2.4: {} - schema-utils@3.3.0: - dependencies: - '@types/json-schema': 7.0.11 - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) - schema-utils@4.2.0: dependencies: '@types/json-schema': 7.0.11 @@ -5205,13 +5421,24 @@ snapshots: ajv-formats: 2.1.1(ajv@8.12.0) ajv-keywords: 5.1.0(ajv@8.12.0) + schema-utils@4.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) + secure-compare@3.0.1: {} + seek-bzip@2.0.0: + dependencies: + commander: 6.2.1 + semver-regex@4.0.5: {} semver-truncate@3.0.0: dependencies: - semver: 7.5.4 + semver: 7.6.3 semver@7.5.4: dependencies: @@ -5219,9 +5446,7 @@ snapshots: semver@7.6.3: {} - serialize-javascript@6.0.1: - dependencies: - randombytes: 2.1.0 + semver@7.7.1: {} serialize-javascript@6.0.2: dependencies: @@ -5233,16 +5458,10 @@ snapshots: dependencies: kind-of: 6.0.3 - shebang-command@1.2.0: - dependencies: - shebang-regex: 1.0.0 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - shebang-regex@1.0.0: {} - shebang-regex@3.0.0: {} shell-quote@1.7.3: {} @@ -5261,8 +5480,6 @@ snapshots: slash@3.0.0: {} - slash@5.1.0: {} - sonic-boom@3.7.0: dependencies: atomic-sleep: 1.0.0 @@ -5301,6 +5518,14 @@ snapshots: dependencies: through: 2.3.8 + streamx@2.21.1: + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + text-decoder: 1.2.3 + optionalDependencies: + bare-events: 2.5.4 + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -5336,7 +5561,10 @@ snapshots: strip-bom@5.0.0: {} - strip-eof@1.0.0: {} + strip-dirs@3.0.0: + dependencies: + inspect-with-kind: 1.0.5 + is-plain-obj: 1.1.0 strip-final-newline@2.0.0: {} @@ -5348,12 +5576,10 @@ snapshots: strip-json-comments@5.0.1: {} - strip-outer@2.0.0: {} - - strtok3@7.0.0: + strtok3@9.1.1: dependencies: '@tokenizer/token': 0.3.0 - peek-readable: 5.0.0 + peek-readable: 5.3.1 stubborn-fs@1.2.5: {} @@ -5371,32 +5597,48 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - swc-loader@0.2.6(@swc/core@1.7.39)(webpack@5.95.0): + swc-loader@0.2.6(@swc/core@1.11.11)(webpack@5.98.0): dependencies: - '@swc/core': 1.7.39 + '@swc/core': 1.11.11 '@swc/counter': 0.1.3 - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) + + sync-child-process@1.0.2: + dependencies: + sync-message-port: 1.1.3 + + sync-message-port@1.1.3: {} tapable@2.2.1: {} - terser-webpack-plugin@5.3.10(@swc/core@1.7.39)(webpack@5.95.0): + tar-stream@3.1.7: + dependencies: + b4a: 1.6.7 + fast-fifo: 1.3.2 + streamx: 2.21.1 + + terser-webpack-plugin@5.3.14(@swc/core@1.11.11)(webpack@5.98.0): dependencies: - '@jridgewell/trace-mapping': 0.3.22 + '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 - schema-utils: 3.3.0 - serialize-javascript: 6.0.1 - terser: 5.27.2 - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) + schema-utils: 4.3.0 + serialize-javascript: 6.0.2 + terser: 5.37.0 + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) optionalDependencies: - '@swc/core': 1.7.39 + '@swc/core': 1.11.11 - terser@5.27.2: + terser@5.37.0: dependencies: '@jridgewell/source-map': 0.3.5 - acorn: 8.10.0 + acorn: 8.14.0 commander: 2.20.3 source-map-support: 0.5.21 + text-decoder@1.2.3: + dependencies: + b4a: 1.6.7 + text-table@0.2.0: {} thread-stream@2.3.0: @@ -5409,6 +5651,11 @@ snapshots: through@2.3.8: {} + tinyglobby@0.2.12: + dependencies: + fdir: 6.4.3(picomatch@4.0.2) + picomatch: 4.0.2 + titleize@3.0.0: {} tmp@0.2.3: {} @@ -5417,17 +5664,13 @@ snapshots: dependencies: is-number: 7.0.0 - token-types@5.0.1: + token-types@6.0.0: dependencies: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 tree-kill@1.2.2: {} - trim-repeated@2.0.0: - dependencies: - escape-string-regexp: 5.0.0 - tslib@2.4.1: {} type-check@0.4.0: @@ -5442,11 +5685,16 @@ snapshots: typedarray@0.0.6: {} - typescript@5.6.3: {} + typescript@5.8.2: {} + + uint8array-extras@1.4.0: {} - undici-types@6.19.8: {} + unbzip2-stream@1.4.3: + dependencies: + buffer: 5.7.1 + through: 2.3.8 - unicorn-magic@0.1.0: {} + undici-types@6.20.0: {} union@0.5.0: dependencies: @@ -5458,9 +5706,9 @@ snapshots: upath@2.0.1: {} - update-browserslist-db@1.1.1(browserslist@4.24.2): + update-browserslist-db@1.1.1(browserslist@4.24.4): dependencies: - browserslist: 4.24.2 + browserslist: 4.24.4 escalade: 3.2.0 picocolors: 1.1.0 @@ -5498,11 +5746,11 @@ snapshots: dependencies: defaults: 1.0.4 - web-ext@8.3.0: + web-ext@8.5.0: dependencies: - '@babel/runtime': 7.25.6 - '@devicefarmer/adbkit': 3.2.6 - addons-linter: 7.1.0 + '@babel/runtime': 7.26.10 + '@devicefarmer/adbkit': 3.3.8 + addons-linter: 7.9.0 camelcase: 8.0.0 chrome-launcher: 1.1.2 debounce: 1.2.1 @@ -5511,7 +5759,7 @@ snapshots: firefox-profile: 4.7.0 fx-runner: 1.4.0 https-proxy-agent: 7.0.5 - jose: 5.9.2 + jose: 5.9.6 jszip: 3.10.1 multimatch: 6.0.0 node-notifier: 10.0.1 @@ -5525,7 +5773,7 @@ snapshots: tmp: 0.2.3 update-notifier: 7.3.1 watchpack: 2.4.2 - ws: 8.18.0 + ws: 8.18.1 yargs: 17.7.2 zip-dir: 2.0.0 transitivePeerDependencies: @@ -5537,28 +5785,22 @@ snapshots: - supports-color - utf-8-validate - webpack-cli@5.1.4(webpack@5.95.0): + webpack-cli@6.0.1(webpack@5.98.0): dependencies: - '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.95.0) - '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.95.0) - '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack@5.95.0) + '@discoveryjs/json-ext': 0.6.3 + '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.98.0) + '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.98.0) + '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack@5.98.0) colorette: 2.0.19 - commander: 10.0.1 + commander: 12.1.0 cross-spawn: 7.0.3 - envinfo: 7.8.1 + envinfo: 7.14.0 fastest-levenshtein: 1.0.16 import-local: 3.1.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4) - webpack-merge: 5.10.0 - - webpack-merge@5.10.0: - dependencies: - clone-deep: 4.0.1 - flat: 5.0.2 - wildcard: 2.0.0 + webpack: 5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1) + webpack-merge: 6.0.1 webpack-merge@6.0.1: dependencies: @@ -5568,15 +5810,15 @@ snapshots: webpack-sources@3.2.3: {} - webpack@5.95.0(@swc/core@1.7.39)(webpack-cli@5.1.4): + webpack@5.98.0(@swc/core@1.11.11)(webpack-cli@6.0.1): dependencies: - '@types/estree': 1.0.5 - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/wasm-edit': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.11.3 - acorn-import-attributes: 1.9.5(acorn@8.11.3) - browserslist: 4.24.2 + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.14.0 + browserslist: 4.24.4 chrome-trace-event: 1.0.3 enhanced-resolve: 5.17.1 es-module-lexer: 1.3.0 @@ -5588,13 +5830,13 @@ snapshots: loader-runner: 4.3.0 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 3.3.0 + schema-utils: 4.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.7.39)(webpack@5.95.0) + terser-webpack-plugin: 5.3.14(@swc/core@1.11.11)(webpack@5.98.0) watchpack: 2.4.2 webpack-sources: 3.2.3 optionalDependencies: - webpack-cli: 5.1.4(webpack@5.95.0) + webpack-cli: 6.0.1(webpack@5.98.0) transitivePeerDependencies: - '@swc/core' - esbuild @@ -5613,20 +5855,18 @@ snapshots: is-absolute: 0.1.7 isexe: 1.1.2 - which@1.3.1: + which@2.0.2: dependencies: isexe: 2.0.0 - which@2.0.2: + which@4.0.0: dependencies: - isexe: 2.0.0 + isexe: 3.1.1 widest-line@5.0.0: dependencies: string-width: 7.2.0 - wildcard@2.0.0: {} - wildcard@2.0.1: {} winreg@0.0.12: {} @@ -5645,7 +5885,7 @@ snapshots: wrappy@1.0.2: {} - ws@8.18.0: {} + ws@8.18.1: {} xdg-basedir@5.1.0: {} @@ -5658,8 +5898,6 @@ snapshots: y18n@5.0.8: {} - yallist@2.1.2: {} - yallist@4.0.0: {} yargs-parser@21.1.1: {} @@ -5679,6 +5917,11 @@ snapshots: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 + yauzl@3.2.0: + dependencies: + buffer-crc32: 0.2.13 + pend: 1.2.0 + yazl@2.5.1: dependencies: buffer-crc32: 0.2.13 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..9a82b7a --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - 'utils/*' diff --git a/src/UserJS/.env.example b/src/UserJS/.env.example deleted file mode 100644 index 2d29ccc..0000000 --- a/src/UserJS/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -# Please do not change this .env variable -JS_ENV="development" diff --git a/src/UserJS/header.js b/src/UserJS/header.js index 1df7454..076c916 100644 --- a/src/UserJS/header.js +++ b/src/UserJS/header.js @@ -2,47 +2,43 @@ (() => { 'use strict'; /******************************************************************************/ -const inIframe = () => { +const inIframe = (() => { try { return window.self !== window.top; - } catch (e) { + } catch { return true; } -} -if (inIframe()) { - return; -} +})(); +if (inIframe) return; let userjs = self.userjs; /** * Skip text/plain documents, based on uBlock Origin `vapi.js` file * - * [Source Code](https://github.com/gorhill/uBlock/blob/master/platform/common/vapi.js) + * [source code](https://github.com/gorhill/uBlock/blob/68962453ff6eec7ff109615a738beb8699b9844a/platform/common/vapi.js#L35) */ if ( (document instanceof Document || (document instanceof XMLDocument && document.createElement('div') instanceof HTMLDivElement)) && - /^image\/|^text\/plain/.test(document.contentType || '') === false && + /^text\/html|^application\/(xhtml|xml)/.test(document.contentType || '') === true && (self.userjs instanceof Object === false || userjs.UserJS !== true) ) { userjs = self.userjs = { UserJS: true }; +} else { + console.error('[%cMagic Userscript+%c] %cERROR','color: rgb(29, 155, 240);','','color: rgb(249, 24, 128);', `MIME type is not a document, got "${document.contentType || ''}"`); } -if (!(typeof userjs === 'object' && userjs.UserJS)) { - return; +if (!(typeof userjs === 'object' && userjs.UserJS)) return; +{ + /** Native implementation exists */ + const excludePolicy = [ + 'outlook.office.com' + ]; + const hostname = location?.hostname || ''; + if (window.trustedTypes && window.trustedTypes.createPolicy && !hostname.includes(excludePolicy)) window.trustedTypes.createPolicy('default', { createHTML: (string) => string, createScript: (string) => string, createScriptURL: (string) => string }); } -/** - * Uncompressed locales + compiler - * - * [_locales](https://github.com/magicoflolis/Userscript-Plus/tree/master/src/_locales) - * - * [languageLoader.js](https://github.com/magicoflolis/Userscript-Plus/blob/master/tools/languageLoader.js) - */ +/** [i18n directory](https://github.com/magicoflolis/Userscript-Plus/tree/master/src/_locales) */ const translations = [[languageList]]; -/** - * To compile this CSS `pnpm run build:Sass` - * - * [Uncompiled Cascading Style Sheet](https://github.com/magicoflolis/Userscript-Plus/tree/master/src/sass) - */ +/** [source code](https://github.com/magicoflolis/Userscript-Plus/blob/master/src/sass/_main.scss) */ const main_css = `[[mainCSS]]`; /******************************************************************************/ [[code]] -})(); \ No newline at end of file +})(); diff --git a/src/UserJS/main.js b/src/UserJS/main.js index 8b98902..c0e4661 100644 --- a/src/UserJS/main.js +++ b/src/UserJS/main.js @@ -1,116 +1,280 @@ // #region Console -const dbg = (...msg) => { - const dt = new Date(); - console.debug( - '[%cMagic Userscript+%c] %cDBG', - 'color: rgb(29, 155, 240);', - '', - 'color: rgb(255, 212, 0);', - `[${dt.getHours()}:${('0' + dt.getMinutes()).slice(-2)}:${('0' + dt.getSeconds()).slice(-2)}]`, - ...msg - ); -}; -const err = (...msg) => { - console.error( - '[%cMagic Userscript+%c] %cERROR', - 'color: rgb(29, 155, 240);', - '', - 'color: rgb(249, 24, 128);', - ...msg - ); - for (const ex of msg) { - if (typeof ex === 'object' && 'cause' in ex && typeof alert !== 'undefined') { - alert(`[Magic Userscript+] (${ex.cause}) ${ex.message}`); +const con = { + title: '[%cMagic Userscript+%c]', + color: 'color: rgb(29, 155, 240);', + dbg(...msg) { + const dt = new Date(); + console.debug( + `${con.title} %cDBG`, + con.color, + '', + 'color: rgb(255, 212, 0);', + `[${dt.getHours()}:${('0' + dt.getMinutes()).slice(-2)}:${('0' + dt.getSeconds()).slice(-2)}]`, + ...msg + ); + }, + err(...msg) { + console.error(`${con.title} %cERROR`, con.color, '', 'color: rgb(249, 24, 128);', ...msg); + const a = typeof alert !== 'undefined' && alert; + const t = con.title.replace(/%c/g, ''); + for (const ex of msg) { + if (typeof ex === 'object' && 'cause' in ex && a) { + a(`${t} (${ex.cause}) ${ex.message}`); + } } + }, + info(...msg) { + console.info(`${con.title} %cINF`, con.color, '', 'color: rgb(0, 186, 124);', ...msg); + }, + log(...msg) { + console.log(`${con.title} %cLOG`, con.color, '', 'color: rgb(219, 160, 73);', ...msg); } }; -const info = (...msg) => { - console.info( - '[%cMagic Userscript+%c] %cINF', - 'color: rgb(29, 155, 240);', - '', - 'color: rgb(0, 186, 124);', - ...msg - ); -}; -const log = (...msg) => { - console.log( - '[%cMagic Userscript+%c] %cLOG', - 'color: rgb(29, 155, 240);', - '', - 'color: rgb(219, 160, 73);', - ...msg - ); -}; +const { err, info } = con; // #endregion /** * @type { import("../typings/types.d.ts").config } */ -let cfg = {}; -// let ghMsg = null; +let cfg; -/** @type { import("../typings/UserJS.d.ts").safeSelf } */ -function safeSelf() { - if (userjs.safeSelf) { - return userjs.safeSelf; - } - const g = globalThis; - const safe = { - XMLHttpRequest: g.XMLHttpRequest, - createElement: g.document.createElement.bind(g.document), - createElementNS: g.document.createElementNS.bind(g.document), - createTextNode: g.document.createTextNode.bind(g.document), - setTimeout: g.setTimeout, - clearTimeout: g.clearTimeout, - trustedTypes: g.trustedTypes +const BLANK_FN = function () {}; +const BLANK_ASYNC_FN = async function () {}; +const BLANK_PAGE = 'about:blank'; +/** + * @param {string} hn + */ +const normalizedHostname = (hn) => hn.replace(/^www\./, ''); +/** + * @param {string} txt + */ +const formatURL = (txt) => + txt + .split('.') + .splice(-2) + .join('.') + .replace(/\/|https:/g, ''); +/** + * @param {string} str + */ +const getHostname = (str) => formatURL(normalizedHostname(str)); +/** + * @type {URL | undefined} + */ +let url; +try { + url = new URL(window.location.href ?? BLANK_PAGE); +} catch { + /* empty */ +} + +// #region Validators +const objToStr = (obj) => Object.prototype.toString.call(obj); +const isRegExp = (obj) => /RegExp/.test(objToStr(obj)); +const isElem = (obj) => /Element/.test(objToStr(obj)); +const isHTML = (obj) => /object HTML/.test(objToStr(obj)); +const isObj = (obj) => /Object/.test(objToStr(obj)); +const isFN = (obj) => /Function/.test(objToStr(obj)); +const isUserCSS = (str) => /\.user\.css$/.test(str); +const isUserJS = (str) => /\.user\.js$/.test(str); +/** + * @type { import("../typings/types.d.ts").isNull } + */ +const isNull = (obj) => { + return Object.is(obj, null) || Object.is(obj, undefined); +}; +/** + * @type { import("../typings/types.d.ts").isBlank } + */ +const isBlank = (obj) => { + return ( + (typeof obj === 'string' && Object.is(obj.trim(), '')) || + ((obj instanceof Set || obj instanceof Map) && Object.is(obj.size, 0)) || + (Array.isArray(obj) && Object.is(obj.length, 0)) || + (isObj(obj) && Object.is(Object.keys(obj).length, 0)) + ); +}; +/** + * @type { import("../typings/types.d.ts").isEmpty } + */ +const isEmpty = (obj) => { + return isNull(obj) || isBlank(obj); +}; +// #endregion +// #region Globals +/** + * https://github.com/zloirock/core-js/blob/master/packages/core-js/internals/global-this.js + * @returns {typeof globalThis} + */ +function globalWin() { + const check = function (it) { + return it && it.Math === Math && it; }; - userjs.safeSelf = safe; - return safe; + return ( + check(typeof globalThis == 'object' && globalThis) || + check(typeof window == 'object' && window) || + check(typeof self == 'object' && self) || + check(typeof this == 'object' && this) || + (function () { + return this; + })() || + Function('return this')() + ); } +class Safe { + /** @type { import("../typings/UserJS.d.ts").safeHandles } */ + _safe; + create() { + try { + const g = globalWin(); + /** @type { import("../typings/UserJS.d.ts").safeHandles } */ + const safe = { + XMLHttpRequest: g.XMLHttpRequest, + CustomEvent: g.CustomEvent, + createElement: g.document.createElement.bind(g.document), + createElementNS: g.document.createElementNS.bind(g.document), + createTextNode: g.document.createTextNode.bind(g.document), + setTimeout: g.setTimeout, + clearTimeout: g.clearTimeout, + navigator: g.navigator, + scheduler: { + postTask(callback, options) { + if ('scheduler' in g && 'postTask' in g.scheduler) { + return g.scheduler.postTask(callback, options); + } -const BLANK_PAGE = 'about:blank'; -// Lets highlight me :) + options = Object.assign({}, options); + + if (options.delay === undefined) options.delay = 0; + options.delay = Number(options.delay); + if (options.delay < 0) { + return Promise.reject(new TypeError('"delay" must be a positive number.')); + } + return new Promise((resolve) => { + g.setTimeout(() => { + resolve(callback()); + }, options.delay); + }); + }, + yield() { + if ('scheduler' in g && 'yield' in g.scheduler) { + scheduler.yield(); + return g.scheduler.yield(); + } + return new Promise((resolve) => { + g.setTimeout(resolve, 0); + }); + } + }, + groupBy(arr, callback) { + if (isFN(Object.groupBy)) { + return Object.groupBy(arr, callback); + } + /** [Object.groupBy polyfill](https://gist.github.com/gtrabanco/7c97bd41aa74af974fa935bfb5044b6e) */ + return arr.reduce((acc = {}, ...args) => { + const key = callback(...args); + acc[key] ??= []; + acc[key].push(args[0]); + return acc; + }, {}); + } + }; + for (const [k, v] of Object.entries(safe)) { + if (/scheduler|navigator/.test(k) || isFN(v)) continue; + throw new Error(`Safe handles "${k}" returned "${v}"`, { cause: 'safeSelf' }); + } + this._safe = safe; + } catch (e) { + err(e); + this._safe = null; + } + return this._safe; + } + get _self() { + return this._safe ?? this.create(); + } + set _self(obj) { + this._safe = obj; + } +} +const { _self } = new Safe(); +// #endregion +// #region Constants +/** + * @type { import("../typings/types.d.ts").cfgBase } + */ +const cfgBase = []; +const cfgSec = new Set(); +/** Lets highlight me :) */ const authorID = 166061; -// Some UserJS I personally enjoy +/** + * Some UserJS I personally enjoy - `https://greasyfork.org/scripts/{id}` + */ const goodUserJS = [ + 33005, + 394820, + 438684, + 4870, + 394420, + 25068, + 483444, + 1682, + 22587, + 789, + 28497, + 386908, + 24204, + 404443, + 4336, + 368183, + 393396, + 473830, + 12179, 423001, 376510, 23840, 40525, 6456, + 'https://openuserjs.org/install/Patabugen/Always_Remember_Me.user.js', + 'https://openuserjs.org/install/nokeya/Direct_links_out.user.js', + 'https://github.com/jijirae/y2monkey/raw/main/y2monkey.user.js', + 'https://github.com/jijirae/r2monkey/raw/main/r2monkey.user.js', 'https://github.com/TagoDR/MangaOnlineViewer/raw/master/Manga_OnlineViewer.user.js', 'https://github.com/jesus2099/konami-command/raw/master/INSTALL-USER-SCRIPT.user.js', 'https://github.com/TagoDR/MangaOnlineViewer/raw/master/dist/Manga_OnlineViewer_Adult.user.js' ]; -// Remove UserJS from banned accounts -const badUserJS = [ - 478597, // Authors have been banned from GreasyFork -]; -// Unsupport webpages for each engine +/** Remove UserJS from banned accounts */ +const badUserJS = [478597]; +/** Unsupport host for search engines */ const engineUnsupported = { greasyfork: ['pornhub.com'], sleazyfork: ['pornhub.com'], openuserjs: [], github: [] }; -const getUAData = () => { - if (typeof userjs.isMobile !== 'undefined') { +const isMobile = (() => { + if (userjs.isMobile !== undefined) { return userjs.isMobile; } try { - const { platform, mobile } = navigator.userAgentData ?? {}; - userjs.isMobile = - /Mobile|Tablet/.test(navigator.userAgent ?? '') || - mobile || - /Android|Apple/.test(platform ?? ''); + const { navigator } = _self; + if (navigator) { + const { userAgent, userAgentData } = navigator; + const { platform, mobile } = userAgentData ? Object(userAgentData) : {}; + userjs.isMobile = + /Mobile|Tablet/.test(userAgent ? String(userAgent) : '') || + Boolean(mobile) || + /Android|Apple/.test(platform ? String(platform) : ''); + } else { + userjs.isMobile = false; + } } catch (ex) { - err({ cause: 'getUAData', message: ex.message }); userjs.isMobile = false; + ex.cause = 'getUAData'; + err(ex); } return userjs.isMobile; -}; -const isMobile = getUAData(); +})(); const isGM = typeof GM !== 'undefined'; const builtinList = { local: /localhost|router|gov|(\d+\.){3}\d+/, @@ -122,38 +286,49 @@ const builtinList = { pathname: '/hentai/.+/read/page/.+' } }; +// #endregion +// #region DEFAULT_CONFIG /** * @type { import("../typings/types.d.ts").config } */ const DEFAULT_CONFIG = { + autofetch: true, + autoinject: true, + autoSort: 'daily_installs', + clearTabCache: true, cache: true, - codePreview: false, autoexpand: false, filterlang: false, sleazyredirect: false, time: 10000, blacklist: ['userjs-local', 'userjs-finance', 'userjs-social', 'userjs-unsupported'], + preview: { + code: false, + metadata: false + }, engines: [ { enabled: true, name: 'greasyfork', - query: encodeURIComponent('https://greasyfork.org/scripts/by-site/{host}.json?language=all'), + query: encodeURIComponent('https://greasyfork.org/scripts/by-site/{host}.json?language=all') }, { enabled: false, name: 'sleazyfork', - query: encodeURIComponent('https://sleazyfork.org/scripts/by-site/{host}.json?language=all'), + query: encodeURIComponent('https://sleazyfork.org/scripts/by-site/{host}.json?language=all') }, { enabled: false, name: 'openuserjs', - query: encodeURIComponent('https://openuserjs.org/?q={host}'), + query: encodeURIComponent('https://openuserjs.org/?q={host}') }, { enabled: false, name: 'github', token: '', - query: encodeURIComponent('https://api.github.com/search/code?q="// ==UserScript=="+{host}+ "// ==/UserScript=="+in:file+language:js&per_page=30') + query: encodeURIComponent( + 'https://api.github.com/search/repositories?q=topic:{domain}+topic:userscript' + ) } ], theme: { @@ -183,149 +358,103 @@ const DEFAULT_CONFIG = { recommend: { author: true, others: true + }, + filters: { + ASCII: { + enabled: false, + name: 'Non-ASCII', + regExp: '[^\\x00-\\x7F\\s]+' + }, + Latin: { + enabled: false, + name: 'Non-Latin', + regExp: '[^\\u0000-\\u024F\\u2000-\\u214F\\s]+' + }, + Games: { + enabled: false, + name: 'Games', + flag: 'iu', + regExp: + 'Aimbot|AntiGame|Agar|agar\\.io|alis\\.io|angel\\.io|ExtencionRipXChetoMalo|AposBot|DFxLite|ZTx-Lite|AposFeedingBot|AposLoader|Balz|Blah Blah|Orc Clan Script|Astro\\s*Empires|^\\s*Attack|^\\s*Battle|BiteFight|Blood\\s*Wars|Bloble|Bonk|Bots|Bots4|Brawler|\\bBvS\\b|Business\\s*Tycoon|Castle\\s*Age|City\\s*Ville|chopcoin\\.io|Comunio|Conquer\\s*Club|CosmoPulse|cursors\\.io|Dark\\s*Orbit|Dead\\s*Frontier|Diep\\.io|\\bDOA\\b|doblons\\.io|DotD|Dossergame|Dragons\\s*of\\s*Atlantis|driftin\\.io|Dugout|\\bDS[a-z]+\\n|elites\\.io|Empire\\s*Board|eRep(ublik)?|Epicmafia|Epic.*War|ExoPlanet|Falcon Tools|Feuerwache|Farming|FarmVille|Fightinfo|Frontier\\s*Ville|Ghost\\s*Trapper|Gladiatus|Goalline|Gondal|gota\\.io|Grepolis|Hobopolis|\\bhwm(\\b|_)|Ikariam|\\bIT2\\b|Jellyneo|Kapi\\s*Hospital|Kings\\s*Age|Kingdoms?\\s*of|knastv(o|oe)gel|Knight\\s*Fight|\\b(Power)?KoC(Atta?ck)?\\b|\\bKOL\\b|Kongregate|Krunker|Last\\s*Emperor|Legends?\\s*of|Light\\s*Rising|lite\\.ext\\.io|Lockerz|\\bLoU\\b|Mafia\\s*(Wars|Mofo)|Menelgame|Mob\\s*Wars|Mouse\\s*Hunt|Molehill\\s*Empire|MooMoo|MyFreeFarm|narwhale\\.io|Neopets|NeoQuest|Nemexia|\\bOGame\\b|Ogar(io)?|Pardus|Pennergame|Pigskin\\s*Empire|PlayerScripts|pokeradar\\.io|Popmundo|Po?we?r\\s*(Bot|Tools)|PsicoTSI|Ravenwood|Schulterglatze|Skribbl|slither\\.io|slitherplus\\.io|slitheriogameplay|SpaceWars|splix\\.io|Survivio|\\bSW_[a-z]+\\n|\\bSnP\\b|The\\s*Crims|The\\s*West|torto\\.io|Travian|Treasure\\s*Isl(and|e)|Tribal\\s*Wars|TW.?PRO|Vampire\\s*Wars|vertix\\.io|War\\s*of\\s*Ninja|World\\s*of\\s*Tanks|West\\s*Wars|wings\\.io|\\bWoD\\b|World\\s*of\\s*Dungeons|wtf\\s*battles|Wurzelimperium|Yohoho|Zombs' + }, + SocialNetworks: { + enabled: false, + name: 'Social Networks', + flag: 'iu', + regExp: + 'Face\\s*book|Google(\\+| Plus)|\\bHabbo|Kaskus|\\bLepra|Leprosorium|MySpace|meinVZ|odnoklassniki|Одноклассники|Orkut|sch(ue|ü)ler(VZ|\\.cc)?|studiVZ|Unfriend|Valenth|VK|vkontakte|ВКонтакте|Qzone|Twitter|TweetDeck' + }, + Clutter: { + enabled: false, + name: 'Clutter', + flag: 'iu', + regExp: + "^\\s*(.{1,3})\\1+\\n|^\\s*(.+?)\\n+\\2\\n*$|^\\s*.{1,5}\\n|do\\s*n('|o)?t (install|download)|nicht installieren|(just )?(\\ban? |\\b)test(ing|s|\\d|\\b)|^\\s*.{0,4}test.{0,4}\\n|\\ntest(ing)?\\s*|^\\s*(\\{@|Smolka|Hacks)|\\[\\d{4,5}\\]|free\\s*download|theme|(night|dark) ?(mode)?" + } } }; -//#region i18n +// #endregion +// #region i18n +/** @type {Map} */ +const i18nMap = new Map(); class i18nHandler { constructor() { - /** - * @type { string[] } - */ - this.cache = []; - - const languages = navigator.languages ?? []; - for (const nlang of languages) { - const lg = nlang.split('-')[0]; - if (this.cache.indexOf(lg) === -1) { - this.cache.push(lg); - } - } - - const current = navigator.language.split('-')[0] ?? 'en'; - if (!this.cache.includes(current)) { - this.cache.push(current); + for (const [k, v] of Object.entries(translations)) { + if (!i18nMap.has(k)) i18nMap.set(k, v); } } + /** + * @param {string | Date | number} str + */ toDate(str = '') { - return new Intl.DateTimeFormat(navigator.language).format(new Date(str)); + const { navigator } = _self; + return new Intl.DateTimeFormat(navigator.language).format( + typeof str === 'string' ? new Date(str) : str + ); } + /** + * @param {number | bigint} number + */ toNumber(number) { + const { navigator } = _self; return new Intl.NumberFormat(navigator.language).format(number); } - i18n$(...keys) { + /** + * @type { import("../typings/UserJS.d.ts").i18n$ } + */ + i18n$(key) { + const { navigator } = _self; const current = navigator.language.split('-')[0] ?? 'en'; - const list = translations[cfg.language] ?? translations[current]; - const arr = []; - for (const key of keys) { - arr.push(list[key]); + try { + return i18nMap.get(current)?.[key] ?? 'Invalid Key'; + } catch (e) { + err(e); + return 'error'; } - return arr.length !== 1 ? arr : arr[0]; + } + + get current() { + const { navigator } = _self; + return navigator.language.split('-')[0] ?? 'en'; } } const language = new i18nHandler(); const { i18n$ } = language; -//#endregion +// #endregion // #region Utilities +const union = (...arr) => [...new Set(arr.flat())]; /** - * @type { import("../typings/types.d.ts").qs } - */ -const qs = (selector, root) => { - try { - return (root || document).querySelector(selector); - } catch (ex) { - err(ex); - } - return null; -}; -/** - * @type { import("../typings/types.d.ts").qsA } + * @param {string} str */ -const qsA = (selectors, root) => { +const decode = (str) => { try { - return (root || document).querySelectorAll(selectors); + if (decodeURI(str) !== decodeURIComponent(str)) { + return decode(decodeURIComponent(str)); + } } catch (ex) { err(ex); } - return []; -}; -/** - * @type { import("../typings/types.d.ts").hasOwn } - */ -const hasOwn = (o, v) => { - if (typeof Object.hasOwn !== 'undefined') { - return Object.hasOwn(o, v); - } - return Object.prototype.hasOwnProperty.call(o, v); -}; -/** - * @type { import("../typings/types.d.ts").objToStr } - */ -const objToStr = (obj) => { - return Object.prototype.toString.call(obj); -}; -/** - * @type { import("../typings/types.d.ts").strToURL } - */ -const strToURL = (str) => { - const WIN_LOCATION = window.location ?? BLANK_PAGE; - try { - str = str ?? WIN_LOCATION; - return objToStr(str).includes('URL') ? str : new URL(str); - } catch (ex) { - err({ cause: 'strToURL', message: ex.message }); - } - return WIN_LOCATION; -}; -/** - * @type { import("../typings/types.d.ts").isRegExp } - */ -const isRegExp = (obj) => { - const s = objToStr(obj); - return s.includes('RegExp'); -}; -/** - * @type { import("../typings/types.d.ts").isElem } - */ -const isElem = (obj) => { - const s = objToStr(obj); - return s.includes('Element'); -}; -/** - * @type { import("../typings/types.d.ts").isObj } - */ -const isObj = (obj) => { - const s = objToStr(obj); - return s.includes('Object'); -}; -/** - * @type { import("../typings/types.d.ts").isFN } - */ -const isFN = (obj) => { - const s = objToStr(obj); - return s.includes('Function'); -}; -/** - * @type { import("../typings/types.d.ts").isNull } - */ -const isNull = (obj) => { - return Object.is(obj, null) || Object.is(obj, undefined); -}; -/** - * @type { import("../typings/types.d.ts").isBlank } - */ -const isBlank = (obj) => { - return ( - (typeof obj === 'string' && Object.is(obj.trim(), '')) || - ((obj instanceof Set || obj instanceof Map) && Object.is(obj.size, 0)) || - (Array.isArray(obj) && Object.is(obj.length, 0)) || - (isObj(obj) && Object.is(Object.keys(obj).length, 0)) - ); -}; -/** - * @type { import("../typings/types.d.ts").isEmpty } - */ -const isEmpty = (obj) => { - return isNull(obj) || isBlank(obj); + return str; }; /** * @type { import("../typings/types.d.ts").normalizeTarget } @@ -338,31 +467,46 @@ const normalizeTarget = (target, toQuery = true, root) => { return target; } if (typeof target === 'string') { - return toQuery ? Array.from((root || document).querySelectorAll(target)) : [target]; + return toQuery ? Array.from((root || document).querySelectorAll(target)) : Array.of(target); } - if (isElem(target)) { - return [target]; + if (/object HTML/.test(Object.prototype.toString.call(target))) { + return Array.of(target); } return Array.from(target); }; /** - * @type { import("../typings/types.d.ts").ael } + * @type { import("../typings/types.d.ts").qs } */ -const ael = (el, type, listener, options = {}) => { +const qs = (selector, root) => { try { - for (const elem of normalizeTarget(el)) { - if (!elem) { - continue; - } - if (isMobile && type === 'click') { - elem.addEventListener('touchstart', listener, options); - continue; - } - elem.addEventListener(type, listener, options); - } + return (root || document).querySelector(selector); + } catch (ex) { + err(ex); + } + return null; +}; +/** + * @type { import("../typings/types.d.ts").qsA } + */ +const qsA = (selectors, root) => { + try { + return (root || document).querySelectorAll(selectors); } catch (ex) { err(ex); } + return []; +}; +/** + * @type { import("../typings/types.d.ts").ael } + */ +const ael = (el, type, listener, options = {}) => { + for (const elem of normalizeTarget(el).filter(isHTML)) { + if (isMobile && type === 'click') { + elem.addEventListener('touchstart', listener, options); + continue; + } + elem.addEventListener(type, listener, options); + } }; /** * @type { import("../typings/types.d.ts").formAttrs } @@ -394,8 +538,8 @@ const formAttrs = (elem, attr = {}) => { const make = (tagName, cname, attrs) => { let el; try { - const safe = safeSelf(); - el = safe.createElement(tagName); + const { createElement } = _self; + el = createElement(tagName.toLowerCase()); if (!isEmpty(cname)) { if (typeof cname === 'string') { el.className = cname; @@ -411,15 +555,14 @@ const make = (tagName, cname, attrs) => { } } } catch (ex) { + if (ex instanceof DOMException) throw new Error(`${ex.name}: ${ex.message}`, { cause: 'make' }); + ex.cause = 'make'; err(ex); } return el; }; -/** - * @type { import("../typings/UserJS.d.ts").getGMInfo } - */ -const getGMInfo = () => { +const $info = (() => { if (isGM) { if (isObj(GM.info)) { return GM.info; @@ -432,20 +575,19 @@ const getGMInfo = () => { icon: '', name: 'Magic Userscript+', namespace: 'https://github.com/magicoflolis/Userscript-Plus', - updateURL: 'https://github.com/magicoflolis/Userscript-Plus/releases', + updateURL: 'https://github.com/magicoflolis/Userscript-Plus/raw/master/dist/magic-userjs.js', version: 'Bookmarklet', bugs: 'https://github.com/magicoflolis/Userscript-Plus/issues' } }; -}; -const $info = getGMInfo(); +})(); // #endregion /** * @type { import("../typings/types.d.ts").dom } */ const dom = { attr(target, attr, value = undefined) { - for (const elem of normalizeTarget(target)) { + for (const elem of normalizeTarget(target).filter(isHTML)) { if (value === undefined) { return elem.getAttribute(attr); } @@ -457,7 +599,7 @@ const dom = { } }, prop(target, prop, value = undefined) { - for (const elem of normalizeTarget(target)) { + for (const elem of normalizeTarget(target).filter(isHTML)) { if (value === undefined) { return elem[prop]; } @@ -465,7 +607,7 @@ const dom = { } }, text(target, text) { - const targets = normalizeTarget(target); + const targets = normalizeTarget(target).filter(isHTML); if (text === undefined) { return targets.length !== 0 ? targets[0].textContent : undefined; } @@ -473,135 +615,91 @@ const dom = { elem.textContent = text; } }, + remove(target) { + normalizeTarget(target) + .filter(isHTML) + .some((elem) => elem.remove()); + }, cl: { add(target, token) { - if (Array.isArray(token)) { - for (const elem of normalizeTarget(target)) { - elem.classList.add(...token); - } - } else { - for (const elem of normalizeTarget(target)) { - elem.classList.add(token); - } - } + token = normalizeTarget(token, false); + return normalizeTarget(target) + .filter(isHTML) + .some((elem) => elem.classList.add(...token)); }, remove(target, token) { - if (Array.isArray(token)) { - for (const elem of normalizeTarget(target)) { - elem.classList.remove(...token); - } - } else { - for (const elem of normalizeTarget(target)) { - elem.classList.remove(token); - } - } + token = normalizeTarget(token, false); + return normalizeTarget(target) + .filter(isHTML) + .some((elem) => elem.classList.remove(...token)); }, toggle(target, token, force) { let r; - for (const elem of normalizeTarget(target)) { + for (const elem of normalizeTarget(target).filter(isHTML)) { r = elem.classList.toggle(token, force); } return r; }, has(target, token) { - for (const elem of normalizeTarget(target)) { - if (elem.classList.contains(token)) { - return true; - } - } - return false; + return normalizeTarget(target) + .filter(isHTML) + .some((elem) => elem.classList.contains(token)); } } }; -class Memorize { - constructor() { - /** - * @type {Map>} - */ - this.store = new Map(); - /** - * @type { { [key: string]: Map; userjs: Map } } - */ - this.maps = {}; - this.create('cfg', 'container', 'userjs'); - } - /** - * @template { string } S - * @param { ...S } maps - * @returns { S | S[] } - */ - create(...maps) { - const resp = []; - for (const key of maps) { - if (this.store.has(key)) { - return this.store.get(key); - } - const m = new Map(); - this.store.set(key, m); - this.maps[key] = m; - resp.push(this.store.get(key)); - } - return resp.length >= 2 ? resp : resp[0]; - } -} -const memory = new Memorize(); //#region Icon SVGs const iconSVG = { - cfg: { - viewBox: '0 0 24 24', - html: '' - }, close: { - viewBox: '0 0 24 24', - html: '' + viewBox: '0 0 384 512', + html: '' }, - filter: { - viewBox: '0 0 24 24', - html: '' + code: { + viewBox: '0 0 640 512', + html: '' }, - fsClose: { - viewBox: '0 0 24 24', - html: ' ' + collapse: { + viewBox: '0 0 448 512', + html: '' }, - fsOpen: { - viewBox: '0 0 24 24', - html: ' ' + download: { + viewBox: '0 0 384 512', + html: '' }, - fullscreen: { - viewBox: '0 0 96 96', - html: '' + expand: { + viewBox: '0 0 448 512', + html: '' }, - gf: { - viewBox: '0 0 510.4 510.4', - html: '' + gear: { + viewBox: '0 0 512 512', + html: '' }, - gh: { - viewBox: '0 0 16 16', - html: '' + github: { + viewBox: '0 0 496 512', + html: '' }, - hide: { - viewBox: '0 0 24 24', - html: ' ' + globe: { + viewBox: '0 0 512 512', + html: '' }, install: { - viewBox: '0 0 16 16', - html: '' + viewBox: '0 0 512 512', + html: '' }, issue: { - viewBox: '0 0 24 24', - html: '' + viewBox: '0 0 512 512', + html: '' }, - nav: { - viewBox: '0 0 24 24', - html: ' ' + minus: { + viewBox: '0 0 448 512', + html: '' }, - plus: { - viewBox: '0 0 24 24', - html: '' + nav: { + viewBox: '0 0 448 512', + html: '' }, - search: { - viewBox: '0 0 24 24', - html: '' + pager: { + viewBox: '0 0 512 512', + html: '' }, verified: { viewBox: '0 0 56 56', @@ -610,13 +708,13 @@ const iconSVG = { html: '' }, refresh: { - viewBox: '0 0 1024 1024', + viewBox: '0 0 512 512', fill: 'currentColor', - html: '' + html: '' }, load(type, container) { - const safe = safeSelf(); - const svgElem = safe.createElementNS('http://www.w3.org/2000/svg', 'svg'); + const { createElementNS } = _self; + const svgElem = createElementNS('http://www.w3.org/2000/svg', 'svg'); for (const [k, v] of Object.entries(iconSVG[type])) { if (k === 'html') { continue; @@ -626,6 +724,7 @@ const iconSVG = { try { if (typeof iconSVG[type].html === 'string') { svgElem.innerHTML = iconSVG[type].html; + svgElem.setAttribute('id', `mujs_${type ?? 'Unknown'}`); } // eslint-disable-next-line no-unused-vars } catch (ex) { @@ -644,11 +743,12 @@ const iconSVG = { */ const StorageSystem = { prefix: 'MUJS', + events: new Set(), getItem(key) { return window.localStorage.getItem(key); }, has(key) { - return !isNull(this.getItem(key)); + return !this.getItem(key); }, setItem(key, value) { window.localStorage.setItem(key, value); @@ -657,7 +757,10 @@ const StorageSystem = { window.localStorage.removeItem(key); }, async setValue(key, v) { - v = typeof v === 'string' ? v : JSON.stringify(v ?? {}); + if (!v) { + return; + } + v = typeof v === 'string' ? v : JSON.stringify(v); if (isGM) { if (isFN(GM.setValue)) { await GM.setValue(key, v); @@ -685,7 +788,9 @@ const StorageSystem = { ? JSON.parse(this.getItem(`${this.prefix}-${key}`)) : def; } catch (ex) { + ex.cause = 'getValue'; err(ex); + return def; } } }; @@ -714,19 +819,17 @@ const Command = { * @type { import("../typings/UserJS.d.ts").Network } */ const Network = { - async req(url, method = 'GET', responseType = 'json', data = {}, useFetch = false) { + async req(url, method = 'GET', responseType = 'json', data, useFetch = false) { if (isEmpty(url)) { throw new Error('"url" parameter is empty'); } + data = Object.assign({}, data); method = this.bscStr(method, false); - responseType = this.bscStr(responseType); + responseType = this.bscStr(responseType, true); const params = { method, ...data }; - if (params.hermes) { - delete params.hermes; - } if (isGM && !useFetch) { if (params.credentials) { Object.assign(params, { @@ -742,13 +845,15 @@ const Network = { } else if (params.onprogress) { delete params.onprogress; } - return await new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { if (isGM && !useFetch) { Network.xmlRequest({ url, responseType, ...params, - onerror: reject, + onerror: (r_1) => { + reject(new Error(`${r_1.status} ${url}`)); + }, onload: (r_1) => { if (r_1.status !== 200) reject(new Error(`${r_1.status} ${url}`)); if (responseType.match(/basic/)) resolve(r_1); @@ -775,8 +880,8 @@ const Network = { } else if (responseType.match(/clone/)) { resolve(check('clone')); } else if (responseType.match(/document/)) { - const domParser = new DOMParser(); const respTxt = check('text'); + const domParser = new DOMParser(); if (respTxt instanceof Promise) { respTxt.then((txt) => { const doc = domParser.parseFromString(txt, 'text/html'); @@ -811,8 +916,8 @@ const Network = { } } return await new Promise((resolve, reject) => { - const safe = safeSelf(); - const req = new safe.XMLHttpRequest(); + const { XMLHttpRequest } = _self; + const req = new XMLHttpRequest(); let method = 'GET'; let url = BLANK_PAGE; let body; @@ -872,73 +977,93 @@ const Network = { return txt.replaceAll(/\W/g, ''); } }; -const sleazyRedirect = () => { - const locObj = window.top.location; - const { hostname } = locObj; - const gfSite = /greasyfork\.org/.test(hostname); - if (!gfSite && cfg.sleazyredirect) { - return; - } - const otherSite = gfSite ? 'sleazyfork' : 'greasyfork'; - if (!qs('span.sign-in-link')) { - return; - } - if (!/scripts\/\d+/.test(locObj.href)) { - return; - } - if ( - !qs('#script-info') && - (otherSite == 'greasyfork' || qs('div.width-constraint>section>p>a')) - ) { - const str = locObj.href.replace( - /\/\/([^.]+\.)?(greasyfork|sleazyfork)\.org/, - '//$1' + otherSite + '.org' - ); - info(`Redirecting to "${str}"`); - if (isFN(locObj.assign)) { - locObj.assign(str); - } else { - locObj.href = str; +const Counter = { + cnt: { + total: { + count: 0 } - } -}; -// #region Container -/** - * @type { import("../typings/UserJS.d.ts").Container } - */ -class Container { + }, + set(engine) { + if (!this.cnt[engine.name]) { + const counter = make('count-frame', engine.enabled ? '' : 'hidden', { + dataset: { + counter: engine.name + }, + title: decode(engine.query ?? engine.url), + textContent: '0' + }); + this.cnt[engine.name] = { + root: counter, + count: 0 + }; + return counter; + } + return this.cnt[engine.name].root; + }, + update(count, engine) { + this.cnt[engine.name].count += count; + this.cnt.total.count += count; + this.updateAll(); + }, + updateAll() { + for (const v of Object.values(this.cnt)) dom.text(v.root, v.count); + }, + reset() { + for (const [k, v] of Object.entries(this.cnt)) { + dom.text(v.root, 0); + v.count = 0; + const engine = cfg.engines.find((engine) => k === engine.name); + if (engine) { + dom.cl[engine.enabled ? 'remove' : 'add'](v.root, 'hidden'); + } + } + } +}; +// #region Container +/** + * @type { import("../typings/UserJS.d.ts").Container } + */ +class Container { webpage; host; - domain; - ready; injected; shadowRoot; supported; frame; - cache; userjsCache; root; unsaved; isBlacklisted; rebuild; - counters; opacityMin; opacityMax; - constructor(url) { + constructor() { this.remove = this.remove.bind(this); this.refresh = this.refresh.bind(this); - this.updateCounter = this.updateCounter.bind(this); - this.updateCounters = this.updateCounters.bind(this); this.showError = this.showError.bind(this); - this.webpage = strToURL(url); - this.host = this.getHost(this.webpage.host); - this.domain = this.getDomain(this.webpage.host); - this.ready = false; + this.toElem = this.toElem.bind(this); + + this.webpage = url; + this.host = getHostname(url?.hostname ?? BLANK_PAGE); this.injected = false; this.shadowRoot = undefined; this.supported = isFN(make('main-userjs').attachShadow); + + // /** furture update */ + // if (!this.supported) throw new Error('Failed to initialize: "attachShadow not supported"', { cause: 'Container' }); + // this.frame = make('main-userjs', { + // style: 'visibility: visible;', + // dataset: { + // insertedBy: $info.script.name, + // role: 'primary-container' + // } + // }); + // this.shadowRoot = this.frame.attachShadow({ mode: 'closed' }); + + this.ready = false; this.frame = this.supported - ? make('main-userjs', '', { + ? make('main-userjs', { + style: 'visibility: visible;', dataset: { insertedBy: $info.script.name, role: 'primary-container' @@ -952,37 +1077,26 @@ class Container { loading: 'lazy', src: BLANK_PAGE, style: - 'position: fixed;bottom: 1rem;right: 1rem;height: 525px;width: 90%;margin: 0px 1rem;z-index: 100000000000000020 !important;', - onload: (iFrame) => { - /** - * @type { HTMLIFrameElement } - */ - const target = iFrame.target; - if (!target.contentDocument) { - return; + 'visibility: visible;position: fixed;bottom: 1rem;right: 1rem;height: 525px;width: 90%;margin: 0px 1rem;z-index: 100000000000000020 !important;', + onload: ({ target }) => { + if (target.contentDocument) { + this.shadowRoot = target.contentDocument.documentElement; + this.ready = true; + dom.cl.add([this.shadowRoot, target.contentDocument.body], 'mujs-iframe'); } - this.shadowRoot = target.contentDocument.documentElement; - this.ready = true; - dom.cl.add([this.shadowRoot, target.contentDocument.body], 'mujs-iframe'); } }); if (this.supported) { - this.shadowRoot = this.frame.attachShadow({ - mode: 'open', - clonable: false, - delegatesfocus: false - }); + this.shadowRoot = this.frame.attachShadow({ mode: 'closed' }); this.ready = true; } - this.cache = memory.maps.container; - this.userjsCache = memory.maps.userjs; + + this.hostCache = new Map(); + this.userjsCache = new Map(); this.root = make('mujs-root'); this.unsaved = false; this.isBlacklisted = false; this.rebuild = false; - this.counters = { - total: 0 - }; this.opacityMin = '0.15'; this.opacityMax = '1'; this.elementsReady = this.init(); @@ -993,6 +1107,7 @@ class Container { } set(delay, reason) { + const { setTimeout } = _self; return new Promise((resolve, reject) => { const id = setTimeout(() => { Object.is(reason, null) || Object.is(reason, undefined) ? resolve() : reject(reason); @@ -1003,6 +1118,7 @@ class Container { } clear(...ids) { + const { clearTimeout } = _self; this.ids = this.ids.filter((id) => { if (ids.includes(id)) { clearTimeout(id); @@ -1017,10 +1133,12 @@ class Container { mouse: new Timeout() }; + this.injFN = BLANK_FN; + window.addEventListener('beforeunload', this.remove); } /** - * @param { Function } callback + * @param { function(): * } callback * @param { Document } doc */ async inject(callback, doc) { @@ -1029,72 +1147,51 @@ class Container { this.remove(); return; } - if (!this.shadowRoot) { - return; - } - if (doc === null) { - return; - } + if (!this.shadowRoot || doc === null) return; while (this.ready === false) { await new Promise((resolve) => requestAnimationFrame(resolve)); } try { doc.documentElement.appendChild(this.frame); if (this.injected) { + if (isFN(this.injFN.build)) { + this.injFN.build(); + } return; } this.shadowRoot.append(this.root); - if (isNull(this.loadCSS(main_css, 'primary-stylesheet'))) { + if (isNull(this.loadCSS(main_css, 'primary-stylesheet'))) throw new Error('Failed to initialize script!', { cause: 'loadCSS' }); - } this.injected = true; this.initFn(); - if (this.elementsReady && isFN(callback)) { - callback.call(this, this.shadowRoot); - } + if (isFN(callback) && this.elementsReady) this.injFN = callback.call(this, this.shadowRoot); } catch (ex) { err(ex); this.remove(); } } initFn() { - this.renderTheme(cfg.theme); - - for (const engine of cfg.engines) { - if (engine.enabled) { - const counter = make('count-frame', '', { - dataset: { - counter: engine.name - }, - title: engine.query ? decodeURIComponent(engine.query) : engine.url, - textContent: '0' - }); - this.countframe.append(counter); - } - this.counters[engine.name] = 0; - } + this.setTheme(); - const cfgpage = this.cfgpage; - const table = this.table; - const exBtn = this.exBtn; - const supported = this.supported; - const frame = this.frame; - const refresh = this.refresh; - const cache = this.cache; - const cHost = this.host; - const urlBar = this.urlBar; + Counter.cnt.total.root = this.mainbtn; + if (this.countframe) + for (const engine of cfg.engines) this.countframe.append(Counter.set(engine)); + const { cfgpage, table, supported, frame, refresh, hostCache, urlBar, host } = this; class Tabs { /** * @param { HTMLElement } root */ constructor(root) { - this.Tab = new Map(); + /** + * @type { Set } + */ + this.pool = new Set(); this.blank = BLANK_PAGE; this.protocal = 'mujs:'; - this.protoReg = new RegExp(`${this.protocal}(.+)`); + this.protoReg = new RegExp(`${this.protocal}(.+)`, 'i'); this.el = { - add: make('mujs-addtab', '', { + add: make('mujs-addtab', { textContent: '+', dataset: { command: 'new-tab' @@ -1105,115 +1202,92 @@ class Container { }; this.el.head.append(this.el.add); this.el.root.append(this.el.head); - this.custom = () => {}; - } - hasTab(...params) { - for (const p of params) { - if (!this.Tab.has(p)) { - return false; - } - const content = normalizeTarget(this.Tab.get(p)).filter((t) => p === t.dataset.host); - if (isBlank(content)) { - return false; - } - } - return true; + this.custom = BLANK_FN; } - storeTab(host) { - const h = host ?? this.blank; - if (!this.Tab.has(h)) { - this.Tab.set(h, new Set()); - } - return this.Tab.get(h); + /** + * @param {string} hostname + */ + getTab(hostname) { + return [...this.pool].find(({ dataset }) => hostname === dataset.host); } - cache(host, ...tabs) { - const h = host ?? this.blank; - const tabCache = this.storeTab(h); - for (const t of normalizeTarget(tabs)) { - if (tabCache.has(t)) { - continue; - } - tabCache.add(t); - } - this.Tab.set(h, tabCache); - return tabCache; + getActive() { + return [...this.pool].find((tab) => tab.classList.contains('active')); } - intFN(host) { - if (!host.startsWith(this.protocal)) { + /** + * @param {string} hostname + */ + intFN(hostname) { + const p = this.protoReg.exec(hostname); + if (!p) { return; } - const type = host.match(this.protoReg)[1]; - if (type === 'settings') { - dom.cl.remove([cfgpage, exBtn], 'hidden'); + if (p[1] === 'settings') { + dom.cl.remove(cfgpage, 'hidden'); dom.cl.add(table, 'hidden'); + urlBar.placeholder = 'Search settings'; if (!supported) { dom.attr(frame, 'style', 'height: 100%;'); } } } + /** + * @param {HTMLElement} tab + * @param {boolean} [build] + */ active(tab, build = true) { - for (const t of normalizeTarget(tab, false)) { - dom.cl.add([cfgpage, exBtn], 'hidden'); + if (!this.pool.has(tab)) this.pool.add(tab); + dom.cl.add([table, cfgpage], 'hidden'); + dom.cl.remove([...this.pool], 'active'); + dom.cl.add(tab, 'active'); + if (!build) { dom.cl.remove(table, 'hidden'); - dom.cl.remove(qsA('mujs-tab', this.el.head), 'active'); - dom.cl.add(t, 'active'); - if (!build) { - continue; - } - const host = t.dataset.host ?? this.blank; - if (host === this.blank) { - refresh(); - } else if (host.startsWith(this.protocal)) { - this.intFN(host); - } else { - this.custom(host); - // MUList.host = host; - // MUList.build(); - } + return; + } + const host = tab.dataset.host ?? this.blank; + if (host === this.blank) { + dom.cl.add(cfgpage, 'hidden'); + dom.cl.remove(table, 'hidden'); + refresh(); + } else if (host.startsWith(this.protocal)) { + this.intFN(host); + } else { + dom.cl.add(cfgpage, 'hidden'); + dom.cl.remove(table, 'hidden'); + this.custom(host); } } /** @param { HTMLElement } tab */ close(tab) { - for (const t of normalizeTarget(tab, false)) { - const host = t.dataset.host; - if (cache.has(host)) { - cache.delete(host); - } - if (dom.cl.has(t, 'active')) { - refresh(); - } - const sibling = t.previousElementSibling ?? t.nextElementSibling; - if (sibling) { - if (sibling.dataset.command !== 'new-tab') { - this.active(sibling); - } + if (this.pool.has(tab)) this.pool.delete(tab); + const host = tab.dataset.host; + if (cfg.clearTabCache && hostCache.has(host)) hostCache.delete(host); + if (tab.classList.contains('active')) refresh(); + const sibling = tab.nextElementSibling ?? tab.previousElementSibling; + if (sibling) { + if (sibling.dataset.command !== 'new-tab') { + this.active(sibling); } - if (this.Tab.has(host)) { - this.Tab.delete(host); - } - t.remove(); } + tab.remove(); } - create(host = undefined) { - if (typeof host === 'string') { - if (host.startsWith(this.protocal) && this.hasTab(host)) { - this.active(this.Tab.get(host)); - return; - } - const content = normalizeTarget(this.storeTab(host)).filter( - (t) => host === t.dataset.host - ); - if (!isEmpty(content)) { + /** + * @param {string} [hostname] + */ + create(hostname = undefined) { + if (typeof hostname === 'string') { + const createdTab = this.getTab(hostname); + if (this.protoReg.test(hostname) && createdTab) { + this.active(createdTab); return; } } - const tab = make('mujs-tab', '', { + const tab = make('mujs-tab', { dataset: { command: 'switch-tab' }, style: `order: ${this.el.head.childElementCount};` }); - const tabClose = make('mu-js', '', { + const tabClose = make('mu-js', { dataset: { command: 'close-tab' }, @@ -1221,35 +1295,34 @@ class Container { textContent: 'X' }); const tabHost = make('mujs-host'); + const p = this.protoReg.exec(hostname); tab.append(tabHost, tabClose); this.el.head.append(tab); this.active(tab, false); - this.cache(host, tab); - if (isNull(host)) { + if (isNull(hostname)) { refresh(); - urlBar.placeholder = i18n$('newTab'); tab.dataset.host = this.blank; tabHost.title = i18n$('newTab'); tabHost.textContent = i18n$('newTab'); - } else if (host.startsWith(this.protocal)) { - const type = host.match(this.protoReg)[1]; - tab.dataset.host = host || cHost; - tabHost.title = type || tab.dataset.host; + } else if (p) { + tab.dataset.host = hostname || host; + tabHost.title = p[1] || tab.dataset.host; tabHost.textContent = tabHost.title; - this.intFN(host); + this.intFN(hostname); } else { - tab.dataset.host = host || cHost; - tabHost.title = host || cHost; + tab.dataset.host = hostname || host; + tabHost.title = hostname || host; tabHost.textContent = tabHost.title; } return tab; } } - this.tab = new Tabs(this.toolbar); - this.tab.create(this.host); + this.Tabs = new Tabs(this.toolbar); + this.Tabs.create(host); const tabbody = this.tabbody; - const getCellValue = (tr, idx) => tr.children[idx].innerText || tr.children[idx].textContent; + const getCellValue = (tr, idx) => + tr.children[idx].dataset.value || tr.children[idx].textContent; const comparer = (idx, asc) => (a, b) => ((v1, v2) => v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) @@ -1295,50 +1368,24 @@ class Container { this.header = make('mujs-header'); this.tbody = make('mujs-body'); this.cfgpage = make('mujs-row', 'mujs-cfg hidden'); + this.main = make('mujs-main', 'hidden'); + this.urlContainer = make('mujs-url'); this.btnframe = make('mujs-column', 'btn-frame'); - this.exBtn = make('mujs-column', 'mujs-sty-flex hidden'); - this.fsearch = make('mujs-btn', 'hidden'); - this.exportCFG = make('mujs-btn', 'mujs-export', { - textContent: i18n$('export_config'), - dataset: { - command: 'export-cfg' - } - }); - this.importCFG = make('mujs-btn', 'mujs-import', { - textContent: i18n$('import_config'), - dataset: { - command: 'import-cfg' - } - }); - this.exportTheme = make('mujs-btn', 'mujs-export', { - textContent: i18n$('export_theme'), - dataset: { - command: 'export-theme' - } - }); - this.importTheme = make('mujs-btn', 'mujs-import', { - textContent: i18n$('import_theme'), - dataset: { - command: 'import-theme' - } - }); this.btnHandles = make('mujs-column', 'btn-handles'); this.btnHide = make('mujs-btn', 'hide-list', { title: i18n$('min'), - innerHTML: iconSVG.load('hide'), + innerHTML: iconSVG.load('minus'), dataset: { command: 'hide-list' } }); this.btnfullscreen = make('mujs-btn', 'fullscreen', { title: i18n$('max'), - innerHTML: iconSVG.load('fullscreen'), + innerHTML: iconSVG.load('expand'), dataset: { command: 'fullscreen' } }); - this.main = make('mujs-main', 'hidden'); - this.urlContainer = make('mujs-url'); this.closebtn = make('mujs-btn', 'close', { title: i18n$('close'), innerHTML: iconSVG.load('close'), @@ -1348,18 +1395,18 @@ class Container { }); this.btncfg = make('mujs-btn', 'settings hidden', { title: 'Settings', - innerHTML: iconSVG.load('cfg'), + innerHTML: iconSVG.load('gear'), dataset: { command: 'settings' } }); this.btnhome = make('mujs-btn', 'github hidden', { title: `GitHub (v${ - $info.script.version.includes('.') || $info.script.version.includes('Book') + /\d+\.\d+\.\d+|Book/.test($info.script.version) ? $info.script.version : $info.script.version.slice(0, 5) })`, - innerHTML: iconSVG.load('gh'), + innerHTML: iconSVG.load('github'), dataset: { command: 'open-tab', webpage: $info.script.namespace @@ -1375,7 +1422,7 @@ class Container { }); this.btngreasy = make('mujs-btn', 'greasy hidden', { title: 'Greasy Fork', - innerHTML: iconSVG.load('gf'), + innerHTML: iconSVG.load('globe'), dataset: { command: 'open-tab', webpage: 'https://greasyfork.org/scripts/421603' @@ -1388,9 +1435,9 @@ class Container { command: 'navigation' } }); - const makeTHead = (rows) => { + const makeTHead = (rows = []) => { const tr = make('tr'); - for (const r of normalizeTarget(rows)) { + for (const r of rows) { const tparent = make('th', r.class ?? '', r); tr.append(tparent); } @@ -1416,17 +1463,28 @@ class Container { } ]); // #endregion - - this.btnHandles.append(this.btnHide, this.btnfullscreen, this.closebtn); - this.btnframe.append(this.btnhome, this.btngreasy, this.btnissue, this.btncfg, this.btnnav); + if (isMobile) { + dom.cl.add([this.btnHide, this.btnfullscreen, this.closebtn], 'hidden'); + this.btnframe.append( + this.btnHide, + this.btnfullscreen, + this.closebtn, + this.btnhome, + this.btngreasy, + this.btnissue, + this.btncfg, + this.btnnav + ); + } else { + this.btnHandles.append(this.btnHide, this.btnfullscreen, this.closebtn); + this.btnframe.append(this.btnhome, this.btngreasy, this.btnissue, this.btncfg, this.btnnav); + } this.toolbar.append(this.btnHandles); this.urlContainer.append(this.urlBar); this.header.append(this.urlContainer, this.rateContainer, this.countframe, this.btnframe); this.tbody.append(this.table, this.cfgpage); this.main.append(this.toolbar, this.header, this.tbody, this.footer, this.promptElem); this.mainframe.append(this.mainbtn); - this.exBtn.append(this.importCFG, this.importTheme, this.exportCFG, this.exportTheme); - this.header.append(this.exBtn); this.root.append(this.mainframe, this.main); return true; @@ -1436,55 +1494,47 @@ class Container { return false; } remove() { - memory.store.clear(); - if (this.frame) { - this.frame.remove(); - } + this.hostCache.clear(); + this.userjsCache.clear(); + dom.remove(this.frame); } async save() { this.unsaved = false; await StorageSystem.setValue('Config', cfg); - info('Saved:', cfg); + info('Saved config:', cfg); + this.redirect(); return cfg; } /** * @param { string } css - CSS to inject * @param { string } name - Name of stylesheet - * @return { HTMLStyleElement } Style element + * @return { HTMLStyleElement | undefined } Style element */ loadCSS(css, name = 'CSS') { try { - if (typeof name !== 'string') { + if (typeof name !== 'string') throw new Error('"name" must be a typeof "string"', { cause: 'loadCSS' }); - } - if (qs(`style[data-role="${name}"]`, this.root)) { + if (qs(`style[data-role="${name}"]`, this.root)) return qs(`style[data-role="${name}"]`, this.root); - } - if (typeof css !== 'string') { + if (typeof css !== 'string') throw new Error('"css" must be a typeof "string"', { cause: 'loadCSS' }); - } - if (isBlank(css)) { + if (isBlank(css)) throw new Error(`"${name}" contains empty CSS string`, { cause: 'loadCSS' }); - } const parent = isEmpty(this.root.shadowRoot) ? this.root : this.root.shadowRoot; if (isGM) { - let sty; - if (isFN(GM.addElement)) { - sty = GM.addElement(parent, 'style', { - textContent: css - }); - } else if (isFN(GM_addElement)) { - sty = GM_addElement(parent, 'style', { - textContent: css - }); - } + const fn = isFN(GM.addElement) + ? GM.addElement + : isFN(GM_addElement) + ? GM_addElement + : BLANK_FN; + const sty = fn(parent, 'style', { textContent: css }); if (isElem(sty)) { sty.dataset.insertedBy = $info.script.name; sty.dataset.role = name; return sty; } } - const sty = make('style', '', { + const sty = make('style', { textContent: css, dataset: { insertedBy: $info.script.name, @@ -1496,74 +1546,44 @@ class Container { } catch (ex) { err(ex); } - return null; } checkBlacklist(str) { str = str || this.host; - let blacklisted = false; if (/accounts*\.google\./.test(this.webpage.host)) { - blacklisted = true; + this.isBlacklisted = true; + return this.isBlacklisted; } + let blacklisted = false; for (const b of normalizeTarget(cfg.blacklist)) { if (typeof b === 'string') { if (b.startsWith('userjs-')) { const r = /userjs-(\w+)/.exec(b)[1]; const biList = builtinList[r]; if (isRegExp(biList)) { - if (!biList.test(str)) continue; - blacklisted = true; + if (biList.test(str)) blacklisted = true; } else if (isObj(biList) && biList.host === this.host) { blacklisted = true; } } } else if (isObj(b)) { - if (!b.enabled) { - continue; - } + if (!b.enabled) continue; if (b.regex === true) { const reg = new RegExp(b.url, b.flags); - if (!reg.test(str)) continue; - blacklisted = true; + if (reg.test(str)) blacklisted = true; } if (Array.isArray(b.url)) { for (const c of b.url) { - if (!str.includes(c)) continue; - blacklisted = true; + if (str.includes(c)) blacklisted = true; } } - if (!str.includes(b.url)) continue; - blacklisted = true; + if (str.includes(b.url)) blacklisted = true; } } this.isBlacklisted = blacklisted; - return blacklisted; + return this.isBlacklisted; } - getInfo(url) { - const webpage = strToURL(url || this.webpage); - const host = this.getHost(webpage.host); - const domain = this.getDomain(webpage.host); - return { - domain, - host, - webpage - }; - } - /** - * @template { string } S - * @param { S } str - */ - getHost(str = '') { - return str.split('.').splice(-2).join('.'); - } - /** - * @template { string } S - * @param { S } str - */ - getDomain(str = '') { - return str.split('.').at(-2) ?? BLANK_PAGE; - } - renderTheme(theme) { - theme = theme || cfg.theme; + setTheme() { + const theme = cfg.theme ?? DEFAULT_CONFIG.theme; if (theme === DEFAULT_CONFIG.theme) { return; } @@ -1571,23 +1591,15 @@ class Container { for (const [k, v] of Object.entries(theme)) { const str = `--mujs-${k}`; const prop = sty.getPropertyValue(str); - if (isEmpty(v)) { - theme[k] = prop; - } - if (prop === v) { - continue; - } + if (isEmpty(v)) theme[k] = prop; + if (prop === v) continue; sty.removeProperty(str); sty.setProperty(str, v); } } makePrompt(txt, dataset = {}, usePrompt = true) { - if (qs('.prompt', this.promptElem)) { - for (const elem of qsA('.prompt', this.promptElem)) { - if (elem.dataset.prompt) { - elem.remove(); - } - } + for (const elem of normalizeTarget(qsA('.prompt', this.promptElem))) { + if (elem.dataset.prompt) elem.remove(); } const el = make('mu-js', 'prompt', { dataset: { @@ -1614,110 +1626,172 @@ class Container { }); elPrompt.append(elYes, elNo); el.append(elPrompt); + } else { + const elPrompt = make('mu-js', 'prompt-body'); + const elNo = make('mujs-btn', 'prompt-deny', { + textContent: i18n$('close') + }); + ael(elNo, isMobile ? 'touchend' : 'click', () => { + el.remove(); + }); + elPrompt.append(elNo); + el.append(elPrompt); } this.promptElem.append(el); + return el; } - makeError(...ex) { - const safe = safeSelf(); + /** + * @template {string | Error} E + * @param {...E} ex + */ + showError(...ex) { + err(...ex); const error = make('mu-js', 'error'); let str = ''; for (const e of ex) { - str += `${typeof e === 'string' ? e : `${e.cause ? `[${e.cause}] ` : ''}${e.message} ${e.stack ?? ''}`}\n`; - if (isObj(e)) { - if (e.notify) { - dom.cl.add(this.mainframe, 'error'); - } - } + str += `${typeof e === 'string' ? e : `${e.cause ? `[${e.cause}] ` : ''}${e.message}${e.stack ? ` ${e.stack}` : ''}`}\n`; } - error.appendChild(safe.createTextNode(str)); + const { createTextNode } = _self; + error.appendChild(createTextNode(str)); this.footer.append(error); } - showError(...ex) { - err(...ex); - this.makeError(...ex); - } - updateCounter(count, engine) { - this.counters[engine.name] += count; - this.counters.total += count; - this.updateCounters(); + refresh() { + this.urlBar.placeholder = i18n$('newTab'); + Counter.reset(); + dom.cl.remove(this.toElem(), 'hidden'); + dom.cl.remove(cfgSec, 'hidden'); + dom.prop([this.tabbody, this.rateContainer, this.footer], 'innerHTML', ''); } - updateCounters() { - for (const [k, v] of Object.entries(this.counters)) { - if (k === 'total') { - continue; - } - if (qs(`count-frame[data-counter="${k}"]`, this.countframe)) { - dom.text(qs(`count-frame[data-counter="${k}"]`, this.countframe), v); + /** + * Redirects sleazyfork userscripts from greasyfork.org to sleazyfork.org + * + * Taken from: https://greasyfork.org/scripts/23840 + */ + redirect() { + const locObj = window.top.location; + const { hostname } = locObj; + const gfSite = /greasyfork\.org/.test(hostname); + if (!gfSite && cfg.sleazyredirect) return; + const otherSite = gfSite ? 'sleazyfork' : 'greasyfork'; + if (!qs('span.sign-in-link')) return; + if (!/scripts\/\d+/.test(locObj.href)) return; + if ( + !qs('#script-info') && + (otherSite == 'greasyfork' || qs('div.width-constraint>section>p>a')) + ) { + const str = locObj.href.replace( + /\/\/([^.]+\.)?(greasyfork|sleazyfork)\.org/, + '//$1' + otherSite + '.org' + ); + info(`Redirecting to "${str}"`); + if (isFN(locObj.assign)) { + locObj.assign(str); + } else { + locObj.href = str; } } - dom.text(this.mainbtn, this.counters.total); } - refresh() { - this.urlBar.placeholder = i18n$('newTab'); - this.counters.total = 0; - for (const engine of cfg.engines) { - this.counters[engine.name] = 0; + /** + * @param {number} [time] + */ + async timeoutFrame(time) { + const frameTimeout = this.timeouts.frame; + frameTimeout.clear(...frameTimeout.ids); + if (dom.cl.has(this.mainframe, 'hidden')) { + return; + } + time = time ?? cfg.time ?? DEFAULT_CONFIG.time; + let n = 10000; + if (typeof time === 'number' && !Number.isNaN(time)) { + n = this.isBlacklisted ? time / 2 : time; + } + await frameTimeout.set(n); + this.remove(); + frameTimeout.clear(...frameTimeout.ids); + } + toElem() { + return Array.from(this).map(({ _mujs }) => { + return _mujs.root; + }); + } + *[Symbol.iterator]() { + const arr = Array.from(this.userjsCache.values()).filter(({ _mujs }) => { + return !isEmpty(_mujs) && _mujs.info.engine.enabled; + }); + for (const userjs of arr) { + yield userjs; } - this.updateCounters(); - dom.cl.remove(qsA('tr[data-engine]', this.tabbody), 'hidden'); - dom.cl.remove(qsA('mujs-section[data-name]', this.cfgpage), 'hidden'); - dom.prop([this.tabbody, this.rateContainer, this.footer], 'innerHTML', ''); } } const container = new Container(); // #endregion // #region Primary Function function primaryFN() { + const respHandles = { + build: BLANK_ASYNC_FN + }; try { + const { scheduler } = _self; const { mainframe, urlBar, rateContainer, footer, tabbody, - toolbar, - tabhead, cfgpage, - fsearch, btnfullscreen, main, - btncfg, - btnhome, - btnissue, - btngreasy, - tab, - userjsCache, - updateCounter, + Tabs, showError } = container; - const frameTimeout = container.timeouts.frame; - const cfgMap = memory.maps.cfg; - const rebuildCfg = () => { - for (const engine of cfg.engines) { - if (cfgMap.has(engine.name)) { - const inp = cfgMap.get(engine.name); - inp.checked = engine.enabled; - if (engine.name === 'github') { - const txt = cfgMap.get('github-token'); - dom.prop(txt, 'value', engine.token); - } - } - } - for (const [k, v] of Object.entries(cfg)) { - if (typeof v === 'boolean') { - if (cfgMap.has(k)) { - const inp = cfgMap.get(k); - if (inp.type === 'checkbox') { - inp.checked = v; + const reloadCfg = () => { + for (const base of cfgBase) { + const nm = /^(\w+)-(.+)/.exec(base.value); + const d = (() => { + if (base.tag === 'engine') { + const engine = DEFAULT_CONFIG.engines.find((engine) => engine.name === base.value); + if (engine) { + return engine; + } + } + if (nm) { + return DEFAULT_CONFIG[nm[1]][nm[2]]; + } + return DEFAULT_CONFIG[base.value]; + })(); + const v = (() => { + if (base.tag === 'engine') { + const engine = cfg.engines.find((engine) => engine.name === base.value); + if (engine) { + return engine; + } + } + if (nm) { + return cfg[nm[1]][nm[2]]; + } + return cfg[base.value]; + })(); + base.cache = v; + if (base.type === 'checkbox') { + if (nm) { + if (nm[1] === 'filters') { + base.elem.checked = cfg[nm[1]][nm[2]].enabled; } else { - dom.prop(inp, 'value', v); + base.elem.checked = v; + } + } else if (base.tag === 'engine') { + base.elem.checked = v.enabled; + base.elemUrl.value = decode(v.query); + base.elemUrl.placeholder = decode(d.query); + if (base.elemToken) { + base.elemToken = v.token; } } + } else { + base.elem.value = v; } } - dom.prop(cfgMap.get('blacklist'), 'value', JSON.stringify(cfg.blacklist, null, ' ')); - dom.prop(cfgMap.get('theme'), 'value', JSON.stringify(cfg.theme, null, ' ')); - container.renderTheme(cfg.theme); + container.setTheme(); }; const doInstallProcess = async (installLink) => { const locObj = window.top.location; @@ -1729,40 +1803,155 @@ function primaryFN() { installLink.remove(); await init(); }; + const doDownloadProcess = async (details) => { + if (!details.url) { + return; + } + const a = make('a'); + a.href = details.url; + a.setAttribute('download', details.filename || ''); + a.setAttribute('type', 'text/plain'); + a.dispatchEvent(new MouseEvent('click')); + await init(); + }; + const applyTo = (ujs, name, elem, root) => { + const n = ujs._mujs.code[name] ?? ujs._mujs.code.data_meta[name]; + if (isEmpty(n)) { + const el = make('mujs-a', { + textContent: i18n$('listing_none') + }); + elem.append(el); + return; + } + dom.prop(elem, 'innerHTML', ''); + dom.cl.remove(root, 'hidden'); + if (isObj(n)) { + if (name === 'resource') { + for (const [k, v] of Object.entries(n)) { + const el = make('mujs-a', { + textContent: k ?? 'ERROR' + }); + if (v.startsWith('http')) { + el.dataset.command = 'open-tab'; + el.dataset.webpage = v; + } + elem.append(el); + } + } else { + const el = make('mujs-a', { + textContent: n.text + }); + if (n.domain) { + el.dataset.command = 'open-tab'; + el.dataset.webpage = `https://${n.text}`; + } + elem.append(el); + } + } else if (typeof n === 'string') { + const el = make('mujs-a', { + textContent: n + }); + elem.append(el); + } else { + for (const c of n) { + if (typeof c === 'string' && c.startsWith('http')) { + const el = make('mujs-a', { + textContent: c, + dataset: { + command: 'open-tab', + webpage: c + } + }); + elem.append(el); + } else if (isObj(c)) { + const el = make('mujs-a', { + textContent: c.text + }); + if (c.domain) { + el.dataset.command = 'open-tab'; + el.dataset.webpage = `https://${c.text}`; + } + elem.append(el); + } else { + const el = make('mujs-a', { + textContent: c + }); + elem.append(el); + } + } + } + }; // #region Main event handlers - ael(main, 'click', async (evt) => { + const frameTimeout = container.timeouts.frame; + ael(main, isMobile ? 'touchend' : 'click', async (evt) => { try { /** @type { HTMLElement } */ const target = evt.target.closest('[data-command]'); if (!target) { return; } - const prmpt = /prompt-/.test(target.dataset.command); let dataset = target.dataset; let cmd = dataset.command; - let prmptChoice = false; - if (prmpt) { + if (/^prompt-/.test(target.dataset.command)) { dataset = target.parentElement.dataset; cmd = dataset.command; - prmptChoice = /confirm/.test(target.dataset.command); - target.parentElement.parentElement.remove(); + let pElem = target.parentElement.parentElement; + if (/prompt-install/.test(target.dataset.command)) { + pElem = target.parentElement.parentElement.parentElement; + const a = make('a', { + onclick(evt) { + evt.preventDefault(); + doInstallProcess(evt.target); + } + }); + a.href = target.dataset.code_url; + a.click(); + } else if (/prompt-download/.test(target.dataset.command)) { + pElem = target.parentElement.parentElement.parentElement; + const dataUserJS = container.userjsCache.get(+target.dataset.userjs); + if (dataUserJS) { + const code_obj = await dataUserJS._mujs.code.request(false, target.dataset.code_url); + if (typeof code_obj.code === 'string') + doDownloadProcess({ + url: 'data:text/plain;charset=utf-8,' + encodeURIComponent(code_obj.code), + filename: `${dataUserJS.name}${isUserCSS(target.dataset.code_url) ? '.user.css' : '.user.js'}` + }); + } + } + pElem.remove(); + return; } - if (cmd === 'install-script' && dataset.userjs) { - let installCode = dataset.userjs; - if (!prmpt && dataset.userjs.endsWith('.user.css')) { - container.makePrompt(i18n$('prmpt_css'), dataset); + if (cmd === 'install-script') { + const dataUserJS = container.userjsCache.get(+dataset.userjs); + if (isNull(dataUserJS)) { return; - } else if (prmpt !== prmptChoice) { - installCode = dataset.userjs.replace(/\.user\.css$/, '.user.js'); } - const dlBtn = make('a', '', { - onclick(evt) { - evt.preventDefault(); - doInstallProcess(evt.target); + if (dataUserJS.code_urls.length > 1) { + const list = make('mujs-list', { + style: 'display: flex; flex-direction: column;' + }); + for (const ujs of dataUserJS.code_urls) { + const a = make('mujs-a', { + title: ujs.code_url, + textContent: ujs.name, + dataset: { + command: 'prompt-install', + code_url: ujs.code_url + } + }); + list.append(a); } - }); - dlBtn.href = installCode; - dlBtn.click(); + container.makePrompt(`Multiple detected: ${list.outerHTML}`, dataset, false); + } else { + const a = make('a', { + onclick(evt) { + evt.preventDefault(); + doInstallProcess(evt.target); + } + }); + a.href = dataUserJS.code_url; + a.click(); + } } else if (cmd === 'open-tab' && dataset.webpage) { if (isGM) { if (isFN(GM.openInTab)) { @@ -1776,15 +1965,15 @@ function primaryFN() { } return window.open(dataset.webpage, '_blank'); } else if (cmd === 'navigation') { - if (dom.cl.has(btngreasy, 'hidden')) { - dom.cl.remove([btncfg, btngreasy, btnhome, btnissue], 'hidden'); - } else { - dom.cl.add([btncfg, btngreasy, btnhome, btnissue], 'hidden'); + for (const e of normalizeTarget(qsA('mujs-btn', target.parentElement)).filter( + (e) => !dom.cl.has(e, 'nav') + )) { + dom.cl.toggle(e, 'hidden'); } } else if (cmd === 'list-description') { const arr = []; const ignoreTags = new Set(['TD', 'MUJS-A', 'MU-JS']); - for (const node of target.parentElement.childNodes) { + for (const node of Object.values(target.parentElement._mujs)) { if (ignoreTags.has(node.tagName)) { continue; } @@ -1806,170 +1995,197 @@ function primaryFN() { } } else if (cmd === 'close') { container.remove(); - } else if (cmd === 'show-filter') { - dom.cl.toggle(fsearch, 'hidden'); } else if (cmd === 'fullscreen') { if (dom.cl.has(btnfullscreen, 'expanded')) { dom.cl.remove([btnfullscreen, main], 'expanded'); - dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('fsOpen')); + dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('expand')); } else { dom.cl.add([btnfullscreen, main], 'expanded'); - dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('fsClose')); + dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('collapse')); } } else if (cmd === 'hide-list') { dom.cl.add(main, 'hidden'); dom.cl.remove(mainframe, 'hidden'); - timeoutFrame(); + container.timeoutFrame(); } else if (cmd === 'save') { - // if (!isNull(ghMsg)) { - // ghMsg = null; - // container.rebuild = true; - // dom.prop(rateContainer, 'innerHTML', ''); - // } container.rebuild = true; dom.prop(rateContainer, 'innerHTML', ''); if (!dom.prop(target, 'disabled')) { - container.save(); - sleazyRedirect(); + const config = await container.save(); if (container.rebuild) { - container.cache.clear(); - MUList.build(); + if (config.autofetch) { + respHandles.build(); + } } container.unsaved = false; container.rebuild = false; } } else if (cmd === 'reset') { cfg = DEFAULT_CONFIG; - dom.cl.remove(mainframe, 'error'); - if (qs('.error', footer)) { - for (const elem of qsA('.error', footer)) { - elem.remove(); - } - } + dom.remove(qsA('.error', footer)); container.unsaved = true; container.rebuild = true; - rebuildCfg(); + reloadCfg(); } else if (cmd === 'settings') { if (container.unsaved) { showError('Unsaved changes'); } - tab.create('mujs:settings'); + Tabs.create('mujs:settings'); container.rebuild = false; } else if (cmd === 'new-tab') { - tab.create(); + Tabs.create(); } else if (cmd === 'switch-tab') { - tab.active(target); + Tabs.active(target); } else if (cmd === 'close-tab' && target.parentElement) { - tab.close(target.parentElement); + Tabs.close(target.parentElement); } else if (cmd === 'download-userjs') { - if (!container.userjsCache.has(+dataset.userjs)) { - return; - } const dataUserJS = container.userjsCache.get(+dataset.userjs); - let installCode = dataUserJS.code_url; - if (!prmpt && dataUserJS.code_url.endsWith('.user.css')) { - container.makePrompt('Download as UserStyle?', dataset); + if (isNull(dataUserJS)) { return; - } else if (prmpt !== prmptChoice) { - installCode = dataUserJS.code_url.replace(/\.user\.css$/, '.user.js'); } - const r = await dataUserJS._mujs.code.request(false, installCode); - const txt = r.data; - if (typeof txt !== 'string') { - return; + + if (dataUserJS.code_urls.length > 1) { + const list = make('mujs-list', { + style: 'display: flex; flex-direction: column;' + }); + for (const ujs of dataUserJS.code_urls) { + const a = make('mujs-a', { + title: ujs.code_url, + textContent: ujs.name, + dataset: { + command: 'prompt-download', + code_url: ujs.code_url, + userjs: dataset.userjs + } + }); + list.append(a); + } + container.makePrompt(`Multiple detected: ${list.outerHTML}`, dataset, false); + } else { + const code_obj = await dataUserJS._mujs.code.request(false); + if (typeof code_obj.code === 'string') + doDownloadProcess({ + url: 'data:text/plain;charset=utf-8,' + encodeURIComponent(code_obj.code), + filename: `${dataUserJS.name}${isUserCSS(dataUserJS.code_url) ? '.user.css' : '.user.js'}` + }); } - const userjsName = dataset.userjsName ?? dataset.userjs; - const userjsExt = prmpt !== prmptChoice ? '.user.js' : '.user.css'; - const makeUserJS = new Blob([txt], { type: 'text/plain' }); - const dlBtn = make('a', 'mujs_Downloader'); - dlBtn.href = URL.createObjectURL(makeUserJS); - dlBtn.download = `${userjsName}${userjsExt}`; - dlBtn.click(); - URL.revokeObjectURL(dlBtn.href); - dlBtn.remove(); - } else if (cmd === 'load-userjs') { + } else if (cmd === 'load-userjs' || cmd === 'load-header') { if (!container.userjsCache.has(+dataset.userjs)) { return; } const codeArea = qs('textarea', target.parentElement.parentElement); - if (!isEmpty(codeArea.value)) { + if (!isEmpty(codeArea.value) && cmd === codeArea.dataset.load) { dom.cl.toggle(codeArea, 'hidden'); return; } + codeArea.dataset.load = cmd; const dataUserJS = container.userjsCache.get(+dataset.userjs); - const r = await dataUserJS._mujs.code.request(); - const txt = r.data; - if (typeof txt !== 'string') { + const code_obj = await dataUserJS._mujs.code.request(); + if (typeof code_obj.data_code_block !== 'string') { + codeArea.value = 'An error occured'; return; } - const code_obj = new ParseUserJS(txt); - codeArea.value = code_obj.get_code_block(); + codeArea.value = + cmd === 'load-userjs' ? code_obj.data_code_block : code_obj.data_meta_block; dom.cl.remove(codeArea, 'hidden'); - const apTo = (name, elem, root) => { - const n = dataUserJS._mujs.code[name]; - if (isEmpty(n)) { - const el = make('mujs-a', '', { - textContent: i18n$('listing_none') + for (const e of qsA( + 'mujs-column[data-el="matches"]', + target.parentElement.parentElement + )) { + applyTo(dataUserJS, e.dataset.type, qs('.mujs-grants', e), e); + } + } else if (cmd === 'load-page') { + if (!container.userjsCache.has(+dataset.userjs)) { + return; + } + let pageArea = qs('mujs-page', target.parentElement.parentElement); + if (!pageArea) { + pageArea = make('mujs-page'); + target.parentElement.parentElement.append(pageArea); + const dataUserJS = container.userjsCache.get(+dataset.userjs); + const engine = dataUserJS._mujs.info.engine; + let pageURL; + if (engine.name.includes('fork')) { + const { navigator } = _self; + const current = navigator.language.split('-')[0] ?? 'en'; + pageURL = dataUserJS.url.replace( + /\/scripts/, + `/${/^(zh|fr|es)/.test(current) ? navigator.language : current}/scripts` + ); + } else if (engine.name.includes('github')) { + const page_url = await Network.req(dataUserJS.page_url, 'GET', 'json', { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' + } + }).catch(() => { + return {}; }); - elem.append(el); + if (!page_url.download_url) { + return; + } + const page = await Network.req(page_url.download_url, 'GET', 'text'); + if (container.supported) { + const shadow = pageArea.attachShadow({ mode: 'closed' }); + const div = make('div', { + innerHTML: page + }); + shadow.append(div); + } return; + } else { + pageURL = dataUserJS.url; } - dom.prop(elem, 'innerHTML', ''); - dom.cl.remove(root, 'hidden'); - for (const c of n) { - if (typeof c === 'string' && c.startsWith('http')) { - const el = make('mujs-a', '', { - textContent: c, - dataset: { - command: 'open-tab', - webpage: c - } - }); - elem.append(el); - } else if (isObj(c)) { - if (name === 'resource') { - for (const [k, v] of Object.entries(c)) { - const el = make('mujs-a', '', { - textContent: k ?? 'ERROR' - }); - if (v.startsWith('http')) { - el.dataset.command = 'open-tab'; - el.dataset.webpage = v; - } - elem.append(el); - } - } else { - const el = make('mujs-a', '', { - textContent: c.text - }); - if (c.domain) { - el.dataset.command = 'open-tab'; - el.dataset.webpage = `https://${c.text}`; - } - elem.append(el); + if (!pageURL) { + return; + } + const page = await Network.req(pageURL, 'GET', 'document'); + const getContent = () => { + let content = 'An error occured'; + const h = new URL(dataUserJS.url); + const root = qs('.user-content', page.documentElement); + for (const e of qsA('[href]', root)) { + e.target = '_blank'; + e.style = 'pointer-events: auto;'; + if (e.href.startsWith('/')) { + e.href = `${h.origin}${e.href}`; } + } + for (const e of qsA('img[src]', root)) { + e.style = + 'max-width: 25em; max-height: 25em; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;'; + } + if (root) { + content = root.innerHTML; } else { - const el = make('mujs-a', '', { - textContent: c - }); - elem.append(el); + content = 'No additional info available'; } - } - }; - const m = qsA('mujs-column[data-el="matches"]', target.parentElement.parentElement); - for (const e of normalizeTarget(m)) { - apTo(e.dataset.type, qs('.mujs-grants', e), e); + return content; + }; + if (container.supported) { + const shadow = pageArea.attachShadow({ mode: 'closed' }); + const div = make('div', { + style: 'pointer-events: none;', + innerHTML: getContent() + }); + shadow.append(div); + } + return; } + if (!dom.cl.has(pageArea, 'hidden')) { + dom.cl.add(pageArea, 'hidden'); + return; + } + dom.cl.remove(pageArea, 'hidden'); } else if (/export-/.test(cmd)) { - const str = JSON.stringify(cmd === 'export-cfg' ? cfg : cfg.theme, null, ' '); - const bytes = new TextEncoder().encode(str); - const blob = new Blob([bytes], { type: 'application/json;charset=utf-8' }); - const dlBtn = make('a', 'mujs-exporter', { - href: URL.createObjectURL(blob), - download: `Magic_Userscript_${cmd === 'export-cfg' ? 'config' : 'theme'}.json` + const toCfg = cmd === 'export-cfg'; + doDownloadProcess({ + url: + 'data:text/plain;charset=utf-8,' + + encodeURIComponent(JSON.stringify(toCfg ? cfg : cfg.theme, null, ' ')), + filename: `Magic_Userscript_${toCfg ? 'config' : 'theme'}.json` }); - dlBtn.click(); - URL.revokeObjectURL(dlBtn.href); } else if (/import-/.test(cmd)) { if (qs('input', target.parentElement)) { qs('input', target.parentElement).click(); @@ -1979,40 +2195,35 @@ function primaryFN() { type: 'file', accept: '.json', onchange(evt) { - try { - [...evt.target.files].forEach((file) => { - const reader = new FileReader(); - reader.readAsText(file); - reader.onload = () => { - const result = JSON.parse(reader.result); - if (result.blacklist) { - log(`Imported config: { ${file.name} }`, result); - cfg = result; - container.unsaved = true; - container.rebuild = true; - rebuildCfg(); - container.save(); - sleazyRedirect(); - container.cache.clear(); - MUList.build(); - container.unsaved = false; - container.rebuild = false; - } else { - log(`Imported theme: { ${file.name} }`, result); - cfg.theme = result; - container.renderTheme(cfg.theme); + const file = evt.target.files[0]; + if (file === undefined || file.name === '') { + return; + } + const fr = new FileReader(); + fr.onload = function () { + if (typeof fr.result !== 'string') { + return; + } + const content = JSON.parse(fr.result); + if (content.blacklist) { + cfg = content; + container.unsaved = true; + container.rebuild = true; + reloadCfg(); + container.save().then((config) => { + if (config.autofetch) { + respHandles.build(); } - inpJSON.remove(); - }; - reader.onerror = () => { - showError(reader.error); - inpJSON.remove(); - }; - }); - } catch (ex) { - showError(ex); + container.unsaved = false; + container.rebuild = false; + }); + } else { + cfg.theme = content; + container.setTheme(); + } inpJSON.remove(); - } + }; + fr.readAsText(file); } }); target.parentElement.append(inpJSON); @@ -2034,9 +2245,9 @@ function primaryFN() { const dataset = target.dataset; const cmd = dataset.command; if (cmd === 'switch-tab' || cmd === 'close-tab') { - tab.close(target); + Tabs.close(target); } else if (cmd === 'new-tab') { - tab.create(); + Tabs.create(); } }); if (!isMobile) { @@ -2058,9 +2269,59 @@ function primaryFN() { }); } } + /** + * @param {CustomEvent} evt + */ + const updatedItem = (evt) => { + const ujs = evt.detail; + if (!ujs._mujs) return; + if (ujs.deleted === true) { + ujs._mujs.root.remove(); + container.userjsCache.delete(ujs.id); + Counter.reset(); + MUList.sortRecords(); + return; + } + if (!isEmpty(ujs.code_urls)) ujs.code_url = ujs.code_urls[0].code_url; + for (const elem of qsA('[data-name]', ujs._mujs.root)) { + const name = elem.dataset.name; + if (name === 'code') { + if (ujs._mujs.code.data_code_block) { + if (cfg.preview.code && !cfg.preview.metadata) { + elem.value = ujs._mujs.code.data_code_block; + } else if (cfg.preview.metadata && !cfg.preview.code) { + elem.value = ujs._mujs.code.data_meta_block; + } else { + elem.value = `${ujs._mujs.code.META_START_COMMENT}${ujs._mujs.code.data_meta_block}${ujs._mujs.code.META_END_COMMENT}${ujs._mujs.code.data_code_block}`; + } + } + continue; + } + if (!ujs[name]) continue; + if (name === 'license') { + dom.attr(elem, 'title', ujs.license ?? i18n$('no_license')); + dom.text(elem, `${i18n$('license')}: ${ujs.license ?? i18n$('no_license')}`); + } else if (name === 'code_updated_at') { + dom.text(elem, language.toDate(ujs.code_updated_at)); + elem.dataset.value = new Date(ujs.code_updated_at).toISOString(); + } else if (name === 'created_date') { + dom.text(elem, `${i18n$('created_date')}: ${language.toDate(ujs.created_at)}`); + elem.dataset.value = new Date(ujs.created_at).toISOString(); + } else if (name === 'total_installs') { + dom.text(elem, `${i18n$('total_installs')}: ${language.toNumber(ujs.total_installs)}`); + } else { + dom.text(elem, ujs[name]); + } + } + if (ujs._mujs.code.data_code_block) { + for (const e of qsA('mujs-column[data-el="matches"]', ujs._mujs.root)) { + applyTo(ujs, e.dataset.type, qs('.mujs-grants', e), e); + } + } + if (container.userjsCache.has(ujs.id)) container.userjsCache.set(ujs.id, ujs); + }; + ael(main, 'updateditem', updatedItem); // #endregion - const META_START_COMMENT = '// ==UserScript=='; - const META_END_COMMENT = '// ==/UserScript=='; const TLD_EXPANSION = ['com', 'net', 'org', 'de', 'co.uk']; const APPLIES_TO_ALL_PATTERNS = [ 'http://*', @@ -2088,92 +2349,84 @@ function primaryFN() { */ data_code_block; /** - * @type { { [meta: string]: any } } + * @type { { [meta: string]: string | string[] | { [resource: string]: string } } } */ data_meta; /** - * @type { string[] } + * @type { {text: string;domain: boolean;tld_extra: boolean}[] } */ data_names; - constructor(code) { - this.code = code; - this.get_all(); - } - get_all() { - this.get_meta_block(); - this.get_code_block(); - this.parse_meta(); - this.calculate_applies_to_names(); + constructor(code, isCSS) { + this.isUserCSS = isCSS === true; + this.META_START_COMMENT = this.isUserCSS ? '/* ==UserStyle==' : '// ==UserScript=='; + this.META_END_COMMENT = this.isUserCSS ? '==/UserStyle== */' : '// ==/UserScript=='; + if (code) { + this.code = code; + this.get_meta_block(); + this.get_code_block(); + this.parse_meta(); + this.calculate_applies_to_names(); + } } get_meta_block() { if (isEmpty(this.code)) { - return null; + return ''; } if (this.data_meta_block) { return this.data_meta_block; } - const start_block = this.code.indexOf(META_START_COMMENT); + const start_block = this.code.indexOf(this.META_START_COMMENT); if (isNull(start_block)) { - return null; + return ''; } - const end_block = this.code.indexOf(META_END_COMMENT, start_block); + const end_block = this.code.indexOf(this.META_END_COMMENT, start_block); if (isNull(end_block)) { - return null; + return ''; } - const meta_block = this.code.substring(start_block + META_START_COMMENT.length, end_block); + const meta_block = this.code.substring( + start_block + this.META_START_COMMENT.length, + end_block + ); this.data_meta_block = meta_block; - return meta_block; + return this.data_meta_block; } get_code_block() { if (isEmpty(this.code)) { - return null; + return ''; } if (this.data_code_block) { return this.data_code_block; } - const start_block = this.code.indexOf(META_START_COMMENT); + const start_block = this.code.indexOf(this.META_START_COMMENT); if (isNull(start_block)) { return null; } - const end_block = this.code.indexOf(META_END_COMMENT, start_block); + const end_block = this.code.indexOf(this.META_END_COMMENT, start_block); if (isNull(end_block)) { return null; } const code_block = this.code.substring( - end_block + META_END_COMMENT.length, + end_block + this.META_END_COMMENT.length, this.code.length ); - const lines = []; - for (const l of code_block.split('\n')) { - if (isEmpty(l)) { - continue; - } - lines.push(l); - } - const clean_code = lines.join('\n'); - this.data_code_block = clean_code; - return clean_code; + this.data_code_block = code_block.split('\n').filter(Boolean).join('\n'); + return this.data_code_block; } parse_meta() { if (isEmpty(this.code)) { - return null; + return {}; } if (this.data_meta) { return this.data_meta; } const meta = {}; - const meta_block = this.get_meta_block(); const meta_block_map = new Map(); - for (const meta_line of meta_block.split('\n')) { - const meta_match = meta_line.match(/\/\/\s+@([a-zA-Z:-]+)\s+(.*)/); - if (isNull(meta_match)) { - continue; - } + for (const meta_line of this.get_meta_block().split('\n').filter(Boolean)) { + const meta_match = /\/\/\s+@([a-zA-Z:-]+)\s+(.*)/.exec(meta_line); + if (!meta_match) continue; const key = meta_match[1].trim(); const value = meta_match[2].trim(); - if (!meta_block_map.has(key)) { - meta_block_map.set(key, []); - } + if (!meta_block_map.has(key)) meta_block_map.set(key, []); const meta_map = meta_block_map.get(key); meta_map.push(value); meta_block_map.set(key, meta_map); @@ -2188,20 +2441,16 @@ function primaryFN() { this.data_meta = meta; return this.data_meta; } - /** - * @returns { string[] } - */ calculate_applies_to_names() { if (isEmpty(this.code)) { - return null; + return []; } if (this.data_names) { return this.data_names; } - const meta = this.parse_meta(); let patterns = []; - for (const [k, v] of Object.entries(meta)) { - if (/include|match/.test(k)) { + for (const [k, v] of Object.entries(this.parse_meta())) { + if (/include|match/i.test(k)) { if (Array.isArray(v)) { patterns = patterns.concat(v); } else { @@ -2213,147 +2462,192 @@ function primaryFN() { return []; } if (this.intersect(patterns, APPLIES_TO_ALL_PATTERNS)) { - return []; + this.data_names = [ + { + domain: false, + text: 'All sites', + tld_extra: false + } + ]; + return this.data_names; } - const name_set = new Set(); + this.data_names = ParseUserJS.getNames(patterns); + return this.data_names; + } + intersect(a, ...arr) { + return !isBlank([...new Set(a)].filter((v) => arr.every((b) => b.includes(v)))); + } + static getNames(patterns = []) { + const name_map = new Map(); const addObj = (obj) => { - if (name_set.has(obj)) { + if (name_map.has(obj.text)) { return; } - name_set.add(obj); + name_map.set(obj.text, obj); }; for (let p of patterns) { - try { - const original_pattern = p; - let pre_wildcards = []; - if (p.match(/^\/(.*)\/$/)) { - pre_wildcards = [p]; - } else { - let m = p.match(/^\*(https?:.*)/i); - if (!isNull(m)) { - p = m[1]; - } - p = p - .replace(/^\*:/i, 'http:') - .replace(/^\*\/\//i, 'http://') - .replace(/^http\*:/i, 'http:') - .replace(/^(https?):([^/])/i, '$1://$2'); - m = p.match(/^([a-z]+:\/\/)\*\.?([a-z0-9-]+(?:.[a-z0-9-]+)+.*)/i); - if (!isNull(m)) { - p = m[1] + m[2]; - } - m = p.match(/^\*\.?([a-z0-9-]+\.[a-z0-9-]+.*)/i); - if (!isNull(m)) { - p = `http://${m[1]}`; - } - m = p.match(/^http\*(?:\/\/)?\.?((?:[a-z0-9-]+)(?:\.[a-z0-9-]+)+.*)/i); - if (!isNull(m)) { - p = `http://${m[1]}`; - } - m = p.match(/^([a-z]+:\/\/([a-z0-9-]+(?:\.[a-z0-9-]+)*\.))\*(.*)/); - if (!isNull(m)) { - if (m[2].match(/A([0-9]+\.){2,}z/)) { - p = `${m[1]}tld${m[3]}`; - pre_wildcards = [p.split('*')[0]]; - } else { - pre_wildcards = [p]; - } + const original_pattern = p; + let pre_wildcards = []; + if (p.match(/^\/(.*)\/$/)) { + pre_wildcards = [p]; + } else { + let m = /^\*(https?:.*)/i.exec(p); + if (m) { + p = m[1]; + } + p = p + .replace(/^\*:/i, 'http:') + .replace(/^\*\/\//i, 'http://') + .replace(/^http\*:/i, 'http:') + .replace(/^(https?):([^/])/i, '$1://$2'); + m = /^([a-z]+:\/\/)\*\.?([a-z0-9-]+(?:.[a-z0-9-]+)+.*)/i.exec(p); + if (m) { + p = m[1] + m[2]; + } + m = /^\*\.?([a-z0-9-]+\.[a-z0-9-]+.*)/i.exec(p); + if (m) { + p = `http://${m[1]}`; + } + m = /^http\*(?:\/\/)?\.?((?:[a-z0-9-]+)(?:\.[a-z0-9-]+)+.*)/i.exec(p); + if (m) { + p = `http://${m[1]}`; + } + m = /^([a-z]+:\/\/([a-z0-9-]+(?:\.[a-z0-9-]+)*\.))\*(.*)/.exec(p); + if (m) { + if (m[2].match(/A([0-9]+\.){2,}z/)) { + p = `${m[1]}tld${m[3]}`; + pre_wildcards = [p.split('*')[0]]; } else { pre_wildcards = [p]; } + } else { + pre_wildcards = [p]; } - for (const pre_wildcard of pre_wildcards) { - try { - const urlObj = new URL(pre_wildcard); - const { host } = urlObj; - if (isNull(host)) { - addObj({ text: original_pattern, domain: false, tld_extra: false }); - } else if (!host.includes('.') && host.includes('*')) { - addObj({ text: original_pattern, domain: false, tld_extra: false }); - } else if (host.endsWith('.tld')) { - for (let i = 0; i < TLD_EXPANSION.length; i++) { - const tld = TLD_EXPANSION[i]; - addObj({ - text: host.replace(/tld$/i, tld), - domain: true, - tld_extra: i != 0 - }); - } - } else if (host.endsWith('.')) { - addObj({ - text: host.slice(0, -1), - domain: true, - tld_extra: false - }); - } else { + } + for (const pre_wildcard of pre_wildcards) { + try { + const urlObj = new URL(pre_wildcard); + const { host } = urlObj; + if (isNull(host)) { + addObj({ text: original_pattern, domain: false, tld_extra: false }); + } else if (!host.includes('.') && host.includes('*')) { + addObj({ text: original_pattern, domain: false, tld_extra: false }); + } else if (host.endsWith('.tld')) { + for (let i = 0; i < TLD_EXPANSION.length; i++) { + const tld = TLD_EXPANSION[i]; addObj({ - text: host, + text: host.replace(/tld$/i, tld), domain: true, - tld_extra: false + tld_extra: i != 0 }); } - // eslint-disable-next-line no-unused-vars - } catch (ex) { - addObj({ text: original_pattern, domain: false, tld_extra: false }); + } else if (host.endsWith('.')) { + addObj({ + text: host.slice(0, -1), + domain: true, + tld_extra: false + }); + } else { + addObj({ + text: host, + domain: true, + tld_extra: false + }); } + } catch { + addObj({ text: original_pattern, domain: false, tld_extra: false }); } - } catch (ex) { - err(ex); } } - const arr = [...name_set]; - this.data_names = arr; - return this.data_names; + return [...name_map.values()]; } - intersect(a = [], b = []) { - for (const v of a) { - if (b.includes(v)) { - return true; + /** + * @template {import("../typings/types.d.ts").GSForkQuery} O + * @param {boolean} translate + * @param {O["code_url"]} code_url + * @param {O} [obj] + */ + async request(translate = false, code_url, obj) { + if (this.data_code_block) { + return this; + } + /** @type { string } */ + const code = await Network.req(code_url, 'GET', 'text').catch(err); + if (typeof code !== 'string') { + return this; + } + this.isUserCSS = isUserCSS(code_url); + this.META_START_COMMENT = this.isUserCSS ? '/* ==UserStyle==' : '// ==UserScript=='; + this.META_END_COMMENT = this.isUserCSS ? '==/UserStyle== */' : '// ==/UserScript=='; + this.code = code; + this.get_meta_block(); + this.get_code_block(); + this.parse_meta(); + this.calculate_applies_to_names(); + + const { data_meta } = this; + if (translate) { + if (data_meta[`name:${language.current}`]) { + Object.assign(obj, { + name: data_meta[`name:${language.current}`] + }); + this.translated = true; + } + if (data_meta[`description:${language.current}`]) { + Object.assign(obj, { + description: data_meta[`description:${language.current}`] + }); + this.translated = true; } } - for (const v of b) { - if (a.includes(v)) { - return true; + if (Array.isArray(data_meta.grant)) { + data_meta.grant = union(data_meta.grant); + } + if (data_meta.resource) { + const obj = {}; + if (typeof data_meta.resource === 'string') { + const reg = /(.+)\s+(.+)/.exec(data_meta.resource); + if (reg) { + obj[reg[1].trim()] = reg[2]; + } + } else { + for (const r of data_meta.resource) { + const reg = /(.+)\s+(http.+)/.exec(r); + if (reg) { + obj[reg[1].trim()] = reg[2]; + } + } } + data_meta.resource = obj; } - return false; + Object.assign(this, { + code_size: [Network.format(code.length)], + meta: data_meta + }); + + return this; } } - const mergeTemplate = (obj = {}) => { - const template = { - id: 0, - bad_ratings: 0, - good_ratings: 0, - ok_ratings: 0, - daily_installs: 0, - total_installs: 0, - name: 'NOT FOUND', - description: 'NOT FOUND', - version: '0.0.0', - url: BLANK_PAGE, - code_url: BLANK_PAGE, - created_at: Date.now(), - code_updated_at: Date.now(), - locale: 'NOT FOUND', - deleted: false, - users: [ - { - id: 0, - name: 'NOT FOUND', - url: BLANK_PAGE - } - ] - }; - for (const key in template) { - if (hasOwn(obj, key)) continue; - obj[key] = template[key]; - } - return obj; + const template = { + id: 0, + bad_ratings: 0, + good_ratings: 0, + ok_ratings: 0, + daily_installs: 0, + total_installs: 0, + name: 'NOT FOUND', + description: 'NOT FOUND', + version: '0.0.0', + url: BLANK_PAGE, + code_url: BLANK_PAGE, + created_at: Date.now(), + code_updated_at: Date.now(), + locale: 'NOT FOUND', + deleted: false, + users: [] }; const mkList = (txt = '', obj = {}) => { - if (!obj.root || !obj.type) { - return; - } + if (!obj.root || !obj.type) return; const { root, type } = obj; const appliesTo = make('mu-js', 'mujs-list', { textContent: `${txt}: ` @@ -2370,7 +2664,7 @@ function primaryFN() { const list = obj.list ?? []; if (isEmpty(list)) { - const elem = make('mujs-a', '', { + const elem = make('mujs-a', { textContent: i18n$('listing_none') }); applyList.append(elem); @@ -2379,7 +2673,7 @@ function primaryFN() { } for (const c of list) { if (typeof c === 'string' && c.startsWith('http')) { - const elem = make('mujs-a', '', { + const elem = make('mujs-a', { textContent: c, dataset: { command: 'open-tab', @@ -2390,7 +2684,7 @@ function primaryFN() { } else if (isObj(c)) { if (type === 'resource') { for (const [k, v] of Object.entries(c)) { - const elem = make('mujs-a', '', { + const elem = make('mujs-a', { textContent: k ?? 'ERROR' }); if (v.startsWith('http')) { @@ -2400,7 +2694,7 @@ function primaryFN() { applyList.append(elem); } } else { - const elem = make('mujs-a', '', { + const elem = make('mujs-a', { textContent: c.text }); if (c.domain) { @@ -2410,51 +2704,43 @@ function primaryFN() { applyList.append(elem); } } else { - const elem = make('mujs-a', '', { + const elem = make('mujs-a', { textContent: c }); applyList.append(elem); } } }; - const timeoutFrame = async (time) => { - time = time ?? cfg.time; - frameTimeout.clear(...frameTimeout.ids); - if (dom.cl.has(mainframe, 'hidden')) { - return; - } - if (typeof time === 'number' && !Number.isNaN(time)) { - await frameTimeout.set(container.isBlacklisted ? time / 2 : time); - } else { - await frameTimeout.set(10000); - } - container.remove(); - return frameTimeout.clear(...frameTimeout.ids); - }; // #region Create UserJS /** * @param { import("../typings/types.d.ts").GSForkQuery } ujs * @param { string } engine */ const createjs = (ujs, engine) => { - ujs = mergeTemplate(ujs); - // Lets not add this UserJS to the list - if (ujs.id === 421603) { - return; - } - if (badUserJS.includes(ujs.id) || badUserJS.includes(ujs.url)) { + const a = [ + ujs.deleted === true, + ujs.id === 421603, // Lets not add this UserJS to the list + badUserJS.includes(ujs.id), + badUserJS.includes(ujs.url) + ].some((t) => t === true); + if (a) { return; } - if (!container.userjsCache.has(ujs.id)) { - container.userjsCache.set(ujs.id, ujs); - } + if (!container.userjsCache.has(ujs.id)) container.userjsCache.set(ujs.id, ujs); const eframe = make('td', 'install-btn'); const uframe = make('td', 'mujs-uframe'); const fdaily = make('td', 'mujs-list', { - textContent: ujs.daily_installs + textContent: ujs.daily_installs, + dataset: { + name: 'daily_installs' + } }); const fupdated = make('td', 'mujs-list', { - textContent: language.toDate(ujs.code_updated_at) + textContent: language.toDate(ujs.code_updated_at), + dataset: { + name: 'code_updated_at', + value: new Date(ujs.code_updated_at).toISOString() + } }); const fname = make('td', 'mujs-name'); const fmore = make('mujs-column', 'mujs-list hidden', { @@ -2478,16 +2764,24 @@ function primaryFN() { textContent: `${i18n$('version_number')}: ${ujs.version}` }); const fcreated = make('mu-js', 'mujs-list', { - textContent: `${i18n$('created_date')}: ${language.toDate(ujs.created_at)}` + textContent: `${i18n$('created_date')}: ${language.toDate(ujs.created_at)}`, + dataset: { + name: 'created_at', + value: new Date(ujs.created_at).toISOString() + } }); const flicense = make('mu-js', 'mujs-list', { title: ujs.license ?? i18n$('no_license'), textContent: `${i18n$('license')}: ${ujs.license ?? i18n$('no_license')}`, - style: - 'text-overflow: ellipsis; overflow: hidden; white-space: nowrap; width: fit-content; max-width: 20em;' + dataset: { + name: 'license' + } }); const ftotal = make('mu-js', 'mujs-list', { - textContent: `${i18n$('total_installs')}: ${language.toNumber(ujs.total_installs)}` + textContent: `${i18n$('total_installs')}: ${language.toNumber(ujs.total_installs)}`, + dataset: { + name: 'total_installs' + } }); const fratings = make('mu-js', 'mujs-list', { title: i18n$('ratings'), @@ -2497,6 +2791,7 @@ function primaryFN() { title: i18n$('good'), textContent: ujs.good_ratings, dataset: { + name: 'good_ratings', el: 'good' } }); @@ -2504,6 +2799,7 @@ function primaryFN() { title: i18n$('ok'), textContent: ujs.ok_ratings, dataset: { + name: 'ok_ratings', el: 'ok' } }); @@ -2511,6 +2807,7 @@ function primaryFN() { title: i18n$('bad'), textContent: ujs.bad_ratings, dataset: { + name: 'bad_ratings', el: 'bad' } }); @@ -2526,11 +2823,11 @@ function primaryFN() { title: `${i18n$('install')} "${ujs.name}"`, dataset: { command: 'install-script', - userjs: ujs.code_url + userjs: ujs.id } }); - const scriptDownload = make('mu-jsbtn', '', { - innerHTML: `${iconSVG.load('install')} ${i18n$('saveFile')}`, + const scriptDownload = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('download')} ${i18n$('saveFile')}`, dataset: { command: 'download-userjs', userjs: ujs.id, @@ -2551,27 +2848,26 @@ function primaryFN() { spellcheck: false, wrap: 'soft' }); - const loadCode = make('mu-jsbtn', '', { - innerHTML: `${iconSVG.load('search')} ${i18n$('preview_code')}`, + const loadCode = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('code')} ${i18n$('code')}`, dataset: { command: 'load-userjs', userjs: ujs.id } }); - if (engine) { - tr.dataset.engine = engine; - // if (engine.includes('fork')) { - // fdaily.dataset.command = 'open-tab'; - // fdaily.dataset.webpage = `${ujs.url}/stats`; - // fupdated.dataset.command = 'open-tab'; - // fupdated.dataset.webpage = `${ujs.url}/versions`; - // } - if (!engine.includes('fork') && cfg.recommend.others && goodUserJS.includes(ujs.url)) { - tr.dataset.good = 'upsell'; + const loadMetadata = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('code')} ${i18n$('metadata')}`, + dataset: { + command: 'load-header', + userjs: ujs.id } + }); + tr.dataset.engine = engine; + if (!engine.includes('fork') && cfg.recommend.others && goodUserJS.includes(ujs.url)) { + tr.dataset.good = 'upsell'; } for (const u of ujs.users) { - const user = make('mujs-a', '', { + const user = make('mujs-a', { innerHTML: u.name, title: u.url, dataset: { @@ -2579,13 +2875,16 @@ function primaryFN() { webpage: u.url } }); - if (cfg.recommend.author && u.id === authorID) { + if ( + cfg.recommend.author && + (u.id === authorID || u.url === 'https://github.com/magicoflolis') + ) { tr.dataset.author = 'upsell'; dom.prop(user, 'innerHTML', `${u.name} ${iconSVG.load('verified')}`); } uframe.append(user); } - if (engine.includes('fork') && cfg.recommend.others && goodUserJS.includes(ujs.id)) { + if (cfg.recommend.others && goodUserJS.includes(ujs.id)) { tr.dataset.good = 'upsell'; } eframe.append(scriptInstall); @@ -2593,340 +2892,342 @@ function primaryFN() { jsInfo.append(ftotal, ratings, fver, fcreated); mkList(i18n$('code_size'), { list: ujs._mujs.code.code_size, - type: 'size', + type: 'code_size', root: jsInfo }); jsInfoB.append(flicense); + const data_meta = ujs._mujs.code?.data_meta ?? {}; mkList(i18n$('antifeatures'), { - list: ujs._mujs.code.antifeatures, + list: data_meta.antifeatures ?? [], type: 'antifeatures', root: jsInfoB }); mkList(i18n$('applies_to'), { - list: ujs._mujs.code.match, - type: 'match', + list: ujs._mujs.code?.data_names ?? [], + type: 'data_names', root: jsInfoB }); mkList('@grant', { - list: ujs._mujs.code.grant, + list: data_meta.grant ?? [], type: 'grant', root: jsInfoB }); mkList('@require', { - list: ujs._mujs.code.meta?.require, + list: data_meta.require, type: 'require', root: jsInfoB }); - const script_resources = ujs._mujs.code.meta?.resource; mkList('@resource', { - list: isNull(script_resources) ? [] : [script_resources], + list: isNull(data_meta.resource) ? [] : [data_meta.resource], type: 'resource', root: jsInfoB }); fmore.append(jsInfo, jsInfoB); - fBtns.append(scriptDownload, loadCode); + fBtns.append(scriptDownload, loadCode, loadMetadata); fname.append(ftitle, fdesc, fmore, fBtns, codeArea); + fname._mujs = { fmore, fBtns, codeArea }; - const code_data = ujs._mujs.code.data; - if (code_data) { - const code_obj = new ParseUserJS(code_data); - codeArea.value = code_obj.get_code_block(); - } + const loadPage = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('pager')} Page`, + dataset: { + command: 'load-page', + userjs: ujs.id + } + }); + fBtns.append(loadPage); - for (const e of [fname, uframe, fdaily, fupdated, eframe]) { - tr.append(e); - } - tabbody.append(tr); - ujs._mujs.root = tr; + if (ujs._mujs.code?.translated) tr.classList.add('translated'); - return tr; + for (const e of [fname, uframe, fdaily, fupdated, eframe]) tr.append(e); + ujs._mujs.root = tr; + return ujs._mujs.root; }; // #endregion - // #region Build List + const loadFilters = () => { + /** @type {Map} */ + const pool = new Map(); + const handles = { + pool, + enabled() { + return [...pool.values()].filter((o) => o.enabled); + }, + refresh() { + if (!Object.is(pool.size, 0)) pool.clear(); + for (const [key, value] of Object.entries(cfg.filters)) { + if (!pool.has(key)) + pool.set(key, { + ...value, + reg: new RegExp(value.regExp, value.flag), + keyReg: new RegExp(key.trim().toLocaleLowerCase(), 'gi'), + valueReg: new RegExp(value.name.trim().toLocaleLowerCase(), 'gi') + }); + } + return this; + }, + get(str) { + return [...pool.values()].find((v) => v.keyReg.test(str) || v.valueReg.test(str)); + }, + /** + * @param { import("../typings/types.d.ts").GSForkQuery } param0 + */ + match({ name, users }) { + const p = handles.enabled(); + if (Object.is(p.length, 0)) return true; + for (const v of p) { + if ([{ name }, ...users].find((o) => o.name.match(v.reg))) return false; + } + return true; + } + }; + for (const [key, value] of Object.entries(cfg.filters)) { + if (!pool.has(key)) + pool.set(key, { + ...value, + reg: new RegExp(value.regExp, value.flag), + keyReg: new RegExp(key.trim().toLocaleLowerCase(), 'gi'), + valueReg: new RegExp(value.name.trim().toLocaleLowerCase(), 'gi') + }); + } + return handles.refresh(); + }; + // #region List class List { - engines; - cache; + intEngines; intHost; - constructor(host = undefined) { - if (isEmpty(host)) { - host = container.host; - } - this.engines = cfg.engines; - this.cache = container.cache.get(host); - this.host = host; + constructor(hostname = undefined) { + this.build = this.build.bind(this); + this.groupBy = this.groupBy.bind(this); + this.dispatch = this.dispatch.bind(this); + this.sortRecords = this.sortRecords.bind(this); + this.container = container; + this.intEngines = cfg.engines ?? []; + this.host = hostname; + } + + setEngines(engines = []) { + const { host } = this; + return engines.filter((e) => { + if (!e.enabled) { + return false; + } + const v = engineUnsupported[e.name] ?? []; + if (v.includes(host)) { + showError(`Engine: "${e.name}" unsupported on "${host}"`); + container.timeoutFrame(); + return false; + } + return true; + }); + } + + dispatch(ujs) { + const { CustomEvent } = _self; + const customEvent = new CustomEvent('updateditem', { detail: ujs }); + main.dispatchEvent(customEvent); + } + + set engines(engines) { + this.intEngines = this.setEngines(engines); + } + + get engines() { + return this.intEngines; } set host(hostname) { + if (isEmpty(hostname)) hostname = this.container.host; this.intHost = hostname; - - if (!container.cache.has(hostname)) { - const engineTemplate = {}; - for (const engine of cfg.engines) { - engineTemplate[engine.name] = []; - } - container.cache.set(hostname, engineTemplate); - } - if (container.checkBlacklist(hostname)) { + this.blacklisted = this.container.checkBlacklist(hostname); + this.intEngines = this.setEngines(this.engines); + this.domain = this.getDomain(this.intHost); + if (this.blacklisted) { showError(`Blacklisted "${hostname}"`); - timeoutFrame(); - this.blacklisted = true; - } else { - this.blacklisted = false; + this.container.timeoutFrame(); } - - const isSupported = (name) => { - for (const [k, v] of Object.entries(engineUnsupported)) { - if (k !== name) { - continue; - } - if (v.includes(hostname)) { - return false; - } - } - return true; - }; - this.engines = cfg.engines.filter((e) => e.enabled && isSupported(e.name)); - this.cache = container.cache.get(hostname); - // if (!isNull(ghMsg)) { - // const txt = make('mujs-row', 'legacy-config', { - // textContent: ghMsg - // }); - // rateContainer.append(txt); - // return; - // } } get host() { return this.intHost; } - async build() { + /** + * @template { string } S + * @param { S } str + */ + getDomain(str = '') { + if (str === '*') { + return 'all-sites'; + } + return str.split('.').at(-2) ?? BLANK_PAGE; + } + + // #region Builder + build() { try { container.refresh(); - if (this.blacklisted) { - return; - } - if (isEmpty(this.engines)) { + const { blacklisted, engines, host, domain, dispatch } = this; + if (blacklisted || isEmpty(engines)) { container.opacityMin = '0'; mainframe.style.opacity = container.opacityMin; return; } - info(' Building list', { cache: this.cache, engines: this.engines }); - const customRecords = []; - const arr = []; - const host = this.host; - for (const engine of this.engines) { - const cEngine = this.cache[`${engine.name}`]; - if (!isEmpty(cEngine)) { - for (const ujs of cEngine) { - if (isElem(ujs._mujs.root)) { - tabbody.append(ujs._mujs.root); - } - } - updateCounter(cEngine.length, engine); - continue; - } - const _mujs = (d) => { - const obj = { - ...d, - _mujs: { - root: {}, - info: { - engine, - host - }, - code: { - antifeatures: [], - grant: [], - match: [], - meta: {}, - request: async function (translate = false, code_url) { - if (this.data) { - return this; - } - this.data = ''; - /** @type { string } */ - const code = await Network.req(code_url ?? d.code_url, 'GET', 'text').catch( - showError - ); - if (typeof code !== 'string') { - return this; - } - Object.assign(this, { - data: code, - code_size: [Network.format(code.length)] - }); - const grantSet = new Set(); - const afSet = new Set(); - const code_obj = new ParseUserJS(code); - const meta = code_obj.parse_meta(); - const applies_to_names = code_obj.calculate_applies_to_names(); - - if (translate) { - for (const lng of language.cache) { - if (meta[`name:${lng}`]) { - Object.assign(obj, { - name: meta[`name:${lng}`] - }); - this.translated = true; - } - if (meta[`description:${lng}`]) { - Object.assign(obj, { - description: meta[`description:${lng}`] - }); - this.translated = true; - } - } - } + const fetchRecords = []; + const bsFilter = loadFilters(); + const hostCache = Array.from(this); - for (const [key, value] of Object.entries(meta)) { - if (/grant/.test(key)) { - for (const v of normalizeTarget(value, false)) { - if (grantSet.has(v)) { - continue; - } - grantSet.add(v); - } - } else if (/antifeature/.test(key)) { - for (const v of normalizeTarget(value, false)) { - if (afSet.has(v)) { - continue; - } - afSet.add(v); - } - } - } - if (typeof meta.require === 'string') { - const s = meta.require; - meta.require = [s]; - } - if (meta.resource) { - const obj = {}; - if (typeof meta.resource === 'string') { - const reg = /(.+)\s+(.+)/.exec(meta.resource); - if (reg) { - obj[reg[1].trim()] = reg[2]; - } - } else { - for (const r of meta.resource) { - const reg = /(.+)\s+(http.+)/.exec(r); - if (reg) { - obj[reg[1].trim()] = reg[2]; - } - } - } - meta.resource = obj; - } - Object.assign(this, { - meta, - match: applies_to_names, - grant: [...grantSet], - antifeatures: [...afSet] - }); + info('Building list', { hostCache, engines, container, list: this }); - return this; + const g = this.groupBy(); + const toFetch = engines.filter((engine) => !g[engine.name]); + const isCached = toFetch.filter((engine) => + hostCache.find(({ _mujs }) => engine.name === _mujs.info.engine.name) + ); + if (!isBlank(toFetch) && isBlank(isCached)) { + for (const engine of engines) { + info(`Fetching from "${engine.name}" for "${host}"`); + const respError = (error) => { + if (!error.cause) error.cause = engine.name; + if (error.message.startsWith('429')) { + showError(`Engine: "${engine.name}" Too many requests...`); + return; + } + showError(`Engine: "${engine.name}"`, error.message); + }; + const _mujs = (d) => { + /** + * @type {import("../typings/types.d.ts").GSForkQuery} + */ + const ujs = { + ...template, + ...d, + code_urls: [], + _mujs: { + root: {}, + info: { + engine, + host + }, + code: { + meta: {} } } - } + }; + ujs._mujs.code.request = async (translate = false, code_url) => { + if (typeof ujs._mujs.code.data_code_block === 'string') { + return ujs._mujs.code; + } + const p = new ParseUserJS(); + await p.request(translate, code_url ?? ujs.code_url, ujs); + if (code_url) { + return p; + } + for (const [k, v] of Object.entries(p)) ujs._mujs.code[k] = v; + return ujs._mujs.code; + }; + return ujs; }; - return obj; - }; - const toQuery = (fallback) => { - if (engine.query) { - return decodeURIComponent(engine.query).replace(/\{host\}/g, host); - } - return fallback; - }; - /** - * @param { import("../typings/types.d.ts").GSFork } dataQ - */ - const forkFN = async (dataQ) => { - if (!dataQ) { - showError('Invalid data received from the server, check internet connection'); - return; - } - /** - * @type { import("../typings/types.d.ts").GSForkQuery[] } - */ - const dq = Array.isArray(dataQ) - ? dataQ - : Array.isArray(dataQ.query) - ? dataQ.query - : []; - const dataA = dq.filter((d) => !d.deleted); - if (isBlank(dataA)) { - return; - } - const data = dataA.map(_mujs); - const otherLng = []; /** - * @param {import("../typings/types.d.ts").GSForkQuery} d - * @returns {boolean} + * Prior to UserScript v7.0.0 + * @template {string} F + * @param {F} fallback + * @returns {F} */ - const inUserLanguage = (d) => { - const dlocal = d.locale.split('-')[0] ?? d.locale; - if (language.cache.includes(dlocal)) { - return true; + const toQuery = (fallback) => { + if (engine.query) { + return decode(engine.query) + .replace(/\{host\}/g, host) + .replace(/\{domain\}/g, domain); } - otherLng.push(d); - return false; + return fallback; }; - const filterLang = data.filter((d) => { - if (cfg.filterlang && !inUserLanguage(d)) { - return false; - } - return true; - }); - let finalList = filterLang; - const hds = []; - for (const ujs of otherLng) { - const c = await ujs._mujs.code.request(true); - if (c.translated) { - hds.push(ujs); - } - } - finalList = [...new Set([...hds, ...filterLang])]; - - for (const ujs of finalList) { - if (cfg.codePreview && !ujs._mujs.code.data) { - await ujs._mujs.code.request(); + /** + * @param { import("../typings/types.d.ts").GSFork } dataQ + */ + const forkFN = async (dataQ) => { + if (!dataQ) { + showError('Invalid data received from the server, check internet connection'); + return; } - createjs(ujs, engine.name); - } - this.cache[engine.name].push(...finalList); - updateCounter(finalList.length, engine); - }; - /** - * @param {Document} htmlDocument - */ - const openuserjs = async (htmlDocument) => { - try { - if (!htmlDocument) { - showError('Invalid data received from the server, TODO fix this'); + const dq = Array.isArray(dataQ) + ? dataQ + : Array.isArray(dataQ.query) + ? dataQ.query + : []; + const dataA = dq + .filter(Boolean) + .filter((d) => !d.deleted) + .filter(bsFilter.match); + if (isBlank(dataA)) { return; } - const selected = htmlDocument.documentElement; - if (/openuserjs/gi.test(engine.name)) { - for (const i of qsA('.col-sm-8 .tr-link', selected)) { - while (isNull(qs('.script-version', i))) { - await new Promise((resolve) => requestAnimationFrame(resolve)); + const { groupBy } = _self; + /** + * @type { {[key: string]: import("../typings/types.d.ts").GSForkQuery[]} } + */ + const g = groupBy(dataA.map(_mujs), ({ locale }) => { + return locale.split('-')[0] ?? locale; + }); + for (const [k, list] of Object.entries(g)) { + for (const ujs of list) { + if (cfg.filterlang && k !== language.current) { + const c = await ujs._mujs.code.request(true); + if (!c.translated) continue; } - const fixurl = dom - .prop(qs('.tr-link-a', i), 'href') - .replace( - new RegExp(document.location.origin, 'gi'), - 'https://openuserjs.org' + if ( + !ujs._mujs.code.data_code_block && + (cfg.preview.code || cfg.preview.metadata) + ) { + ujs._mujs.code.request().then(() => { + dispatch(ujs); + }); + } + if (isUserCSS(ujs.code_url)) { + ujs.code_urls.push( + { + name: `${ujs.name} (.user.css)`, + code_url: ujs.code_url + }, + { + name: `${ujs.name} (.user.js)`, + code_url: ujs.code_url.replace(/\.user\.css$/, '.user.js') + } ); - const ujs = mergeTemplate( - _mujs({ - name: dom.text(qs('.tr-link-a', i)), - description: dom.text(qs('p', i)), - version: dom.text(qs('.script-version', i)), - url: fixurl, - code_url: `${fixurl.replace(/\/scripts/gi, '/install')}.user.js`, - total_installs: dom.text(qs('td:nth-child(2) p', i)), - created_at: dom.attr(qs('td:nth-child(4) time', i), 'datetime'), + } + createjs(ujs, engine.name); + } + } + }; + /** + * @param {Document} htmlDocument + */ + const openuserjs = async (htmlDocument) => { + try { + if (!htmlDocument) { + showError('Invalid data received from the server, TODO fix this'); + return; + } + const selected = htmlDocument.documentElement; + if (/openuserjs/gi.test(engine.name)) { + const col = qsA('.col-sm-8 .tr-link', selected) ?? []; + for (const i of col) { + while (isNull(qs('.script-version', i))) { + await new Promise((resolve) => requestAnimationFrame(resolve)); + } + const fixurl = dom + .prop(qs('.tr-link-a', i), 'href') + .replace( + new RegExp(document.location.origin, 'gi'), + 'https://openuserjs.org' + ); + const ujs = _mujs({ + name: dom.text(qs('.tr-link-a', i)), + description: dom.text(qs('p', i)), + version: dom.text(qs('.script-version', i)), + url: fixurl, + code_url: `${fixurl.replace(/\/scripts/gi, '/install')}.user.js`, + total_installs: dom.text(qs('td:nth-child(2) p', i)), + created_at: dom.attr(qs('td:nth-child(4) time', i), 'datetime'), code_updated_at: dom.attr(qs('td:nth-child(4) time', i), 'datetime'), users: [ { @@ -2934,185 +3235,415 @@ function primaryFN() { url: dom.prop(qs('.inline-block a', i), 'href') } ] - }) - ); - if (cfg.codePreview && !ujs._mujs.code.data) { - await ujs._mujs.code.request(); + }); + if (bsFilter.match(ujs)) { + continue; + } + if ( + !ujs._mujs.code.data_code_block && + (cfg.preview.code || cfg.preview.metadata) + ) { + ujs._mujs.code.request().then(() => { + dispatch(ujs); + }); + } + createjs(ujs, engine.name); } - createjs(ujs, engine.name); - customRecords.push(ujs); } + } catch (ex) { + showError(ex); } - this.cache[engine.name].push(...customRecords); - updateCounter(customRecords.length, engine); - } catch (ex) { - showError(ex); - } - }; - const gitFN = async (data) => { - try { - if (isBlank(data.items)) { - showError('Invalid data received from the server, TODO fix this'); - return; - } - for (const r of data.items) { - const ujs = mergeTemplate( - _mujs({ + }; + const gitFN = (data) => { + try { + if (isBlank(data.items)) { + return; + } + for (const r of data.items) { + const ujs = _mujs({ + id: r.id ?? 0, name: r.name, - description: isEmpty(r.repository.description) - ? i18n$('no_license') - : r.repository.description, + description: isEmpty(r.description) ? i18n$('no_license') : r.description, url: r.html_url, - code_url: r.html_url.replace(/\/blob\//g, '/raw/'), - code_updated_at: r.commit || Date.now(), - total_installs: r.score, + code_url: r.html_url, + page_url: `${r.url}/contents/README.md`, + created_at: r.created_at, + code_updated_at: r.updated_at || Date.now(), + daily_installs: r.watchers_count ?? 0, + good_ratings: r.stargazers_count ?? 0, users: [ { - name: r.repository.owner.login, - url: r.repository.owner.html_url + name: r.owner.login, + url: r.owner.html_url } ] - }) - ); - if (cfg.codePreview && !ujs._mujs.code.data) { - await ujs._mujs.code.request(); + }); + if (r.license?.name) ujs.license = r.license.name; + const rootPath = r.contents_url.replace(/\{\+path\}/, ''); + const fetchContent = async (dir) => { + const contents = await Network.req(dir, 'GET', 'json', { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' + } + }).catch(respError); + for (const content of contents) { + if (content.type === 'file') { + if (isUserJS(content.name)) { + ujs.code_urls.push({ + name: content.name, + code_url: content.download_url + }); + } else if (isUserCSS(content.name)) { + ujs.code_urls.push({ + name: content.name, + code_url: content.download_url + }); + } + } else if (content.type === 'dir') { + await fetchContent(`${rootPath}/${content.path}`); + } + } + }; + fetchContent(rootPath).then(() => { + if (isEmpty(ujs.code_urls)) { + ujs.deleted = true; + } else if ( + !ujs._mujs.code.data_code_block && + (cfg.preview.code || cfg.preview.metadata) + ) { + ujs._mujs.code.request().then(() => { + dispatch(ujs); + }); + return; + } + dispatch(ujs); + }); + createjs(ujs, engine.name); } - createjs(ujs, engine.name); - customRecords.push(ujs); + } catch (ex) { + showError(ex); } - this.cache[engine.name].push(...customRecords); - updateCounter(data.items.length, engine); - } catch (ex) { - showError(ex); - } - }; - let netFN; - if (/github/gi.test(engine.name)) { - if (isEmpty(engine.token)) { - showError(`"${engine.name}" requires a token to use`); - continue; - } - netFN = Network.req( - toQuery(`${engine.url}"// ==UserScript=="+${host}+ "// ==/UserScript=="+in:file+language:js&per_page=30`), - 'GET', - 'json', - { - headers: { - Accept: 'application/vnd.github+json', - Authorization: `Bearer ${engine.token}`, - 'X-GitHub-Api-Version': '2022-11-28' - } + }; + let netFN; + if (/github/gi.test(engine.name)) { + if (isEmpty(engine.token)) { + showError(`"${engine.name}" requires a token to use`); + continue; } - ) - .then(gitFN) - .then(() => { - Network.req('https://api.github.com/rate_limit', 'GET', 'json', { + Network.req( + `https://api.github.com/search/repositories?q=topic:${domain}+topic:userstyle`, + 'GET', + 'json', + { headers: { Accept: 'application/vnd.github+json', Authorization: `Bearer ${engine.token}`, 'X-GitHub-Api-Version': '2022-11-28' } - }) - .then((data) => { - for (const [key, value] of Object.entries(data.resources.code_search)) { - const txt = make('mujs-row', 'rate-info', { - textContent: `${key.toUpperCase()}: ${value}` - }); - rateContainer.append(txt); + } + ) + .then(gitFN) + .catch(respError); + netFN = Network.req( + toQuery( + `https://api.github.com/search/repositories?q=topic:${domain}+topic:userscript` + ), + 'GET', + 'json', + { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' + } + } + ) + .then(gitFN) + .then(() => { + Network.req('https://api.github.com/rate_limit', 'GET', 'json', { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' } }) - .catch(showError); - }) - .catch(showError); - } else if (/openuserjs/gi.test(engine.name)) { - netFN = Network.req(toQuery(`${engine.url}${host}`), 'GET', 'document') - .then(openuserjs) - .catch((error) => { - showError(`Engine: "${engine.name}"`, error); - }); - } else { - netFN = Network.req(toQuery(`${engine.url}/scripts/by-site/${host}.json?language=all`)) - .then(forkFN) - .catch(showError); - } - if (netFN) { - arr.push(netFN); + .then((data) => { + for (const [key, value] of Object.entries(data.resources.code_search)) { + const txt = make('mujs-row', 'rate-info', { + textContent: `${key.toUpperCase()}: ${value}` + }); + rateContainer.append(txt); + } + }) + .catch(respError); + }); + } else if (/openuserjs/gi.test(engine.name)) { + netFN = Network.req(toQuery(`${engine.url}${host}`), 'GET', 'document').then( + openuserjs + ); + } else { + netFN = Network.req( + toQuery(`${engine.url}/scripts/by-site/${host}.json?language=all`) + ).then(forkFN); + } + if (netFN) { + fetchRecords.push(netFN.catch(respError)); + } } + } else { + for (const ujs of hostCache) tabbody.append(ujs._mujs.root); } urlBar.placeholder = i18n$('search_placeholder'); urlBar.value = ''; - Promise.allSettled(arr).then(() => { - tabhead.rows[0].cells[2].dispatchEvent(new MouseEvent('click')); - tabhead.rows[0].cells[2].dispatchEvent(new MouseEvent('click')); - }); + + if (isBlank(fetchRecords)) { + this.sortRecords(); + return; + } + Promise.allSettled(fetchRecords).then(this.sortRecords).catch(showError); } catch (ex) { showError(ex); } } + // #endregion + + sortRecords() { + const arr = Array.from(this); + for (const ujs of arr.flat().sort((a, b) => { + const sortType = cfg.autoSort ?? 'daily_installs'; + return b[sortType] - a[sortType]; + })) { + if (isElem(ujs._mujs.root)) tabbody.append(ujs._mujs.root); + } + for (const [name, value] of Object.entries(this.groupBy(arr))) + Counter.update(value.length, { name }); + } + + groupBy() { + const { groupBy } = _self; + return groupBy(Array.from(this), ({ _mujs }) => _mujs.info.engine.name); + } + + *[Symbol.iterator]() { + const { intHost, engines } = this; + const arr = Array.from(this.container).filter( + ({ _mujs }) => + _mujs.info.host === intHost && + engines.find((engine) => engine.enabled && engine.name === _mujs.info.engine.name) + ); + for (const userjs of arr) { + yield userjs; + } + } } const MUList = new List(); // #endregion // #region Make Config const makecfg = () => { - const makerow = (desc, type = null, nm, attrs = {}) => { - desc = desc ?? i18n$('no_license'); - nm = nm ?? i18n$('no_license'); - const sec = make('mujs-section', '', { + const cbtn = make('mu-js', 'mujs-sty-flex'); + const savebtn = make('mujs-btn', 'save', { + textContent: i18n$('save'), + dataset: { + command: 'save' + }, + disabled: false + }); + const resetbtn = make('mujs-btn', 'reset', { + textContent: i18n$('reset'), + dataset: { + command: 'reset' + } + }); + cbtn.append(resetbtn, savebtn); + + const makesection = (name, tag) => { + tag = tag ?? i18n$('no_license'); + name = name ?? i18n$('no_license'); + const sec = make('mujs-section', { + dataset: { + name: tag + } + }); + const lb = make('label', { dataset: { - name: nm + command: tag } }); - const lb = make('label'); - const divDesc = make('mu-js', '', { - textContent: desc + const divDesc = make('mu-js', { + textContent: name + }); + ael(sec, 'click', (evt) => { + /** @type { HTMLElement } */ + const target = evt.target.closest('[data-command]'); + if (!target) { + return; + } + const cmd = target.dataset.command; + if (cmd === tag) { + const a = qsA(`[data-${tag}]`, sec); + if (dom.cl.has(a, 'hidden')) { + dom.cl.remove(a, 'hidden'); + } else { + dom.cl.add(a, 'hidden'); + } + } }); lb.append(divDesc); sec.append(lb); cfgpage.append(sec); - if (isNull(type)) { + !cfgSec.has(sec) && cfgSec.add(sec); + return sec; + }; + const sections = { + general: makesection('General', 'general'), + load: makesection('Automation', 'load'), + list: makesection('List', 'list'), + filters: makesection('List Filters', 'filters'), + blacklist: makesection('Blacklist (WIP)', 'blacklist'), + engine: makesection('Search Engines', 'engine'), + theme: makesection('Theme Colors', 'theme'), + exp: makesection('Import / Export', 'exp') + }; + const makeRow = (text, value, type = 'checkbox', tag = 'general', attrs = {}) => { + const lb = make('label', 'sub-section hidden', { + textContent: text, + dataset: { + [tag]: text + } + }); + const getDefault = () => { + if (tag === 'engine') { + const engine = DEFAULT_CONFIG.engines.find((engine) => engine.name === value); + if (engine) { + return engine; + } + } + const nm = /^(\w+)-(.+)/.exec(value); + if (nm) { + return DEFAULT_CONFIG[nm[1]][nm[2]]; + } + return DEFAULT_CONFIG[value]; + }; + const getValue = () => { + if (tag === 'engine') { + const engine = cfg.engines.find((engine) => engine.name === value); + if (engine) { + return engine; + } + } + const nm = /^(\w+)-(.+)/.exec(value); + if (nm) { + return cfg[nm[1]][nm[2]]; + } + return cfg[value]; + }; + const obj = { + text, + tag, + value, + type, + attrs, + default: getDefault(), + cache: getValue() + }; + if (type === 'select') { + const inp = make('select', { + dataset: { + [tag]: text + }, + ...attrs + }); + for (const selV of Object.keys(template)) { + if (selV === 'deleted' || selV === 'users') continue; + const o = make('option', { + value: selV, + textContent: selV + }); + inp.append(o); + } + inp.value = cfg[value]; + lb.append(inp); + if (sections[tag]) { + sections[tag].append(lb); + } + obj.elem = inp; + cfgBase.push(obj); return lb; } - const inp = make('input', '', { + const inp = make('input', { type, dataset: { - name: nm + [tag]: text }, ...attrs }); - if (!cfgMap.has(nm)) { - cfgMap.set(nm, inp); + + if (tag === 'engine') { + inp.dataset.name = value; + } + + if (sections[tag]) { + sections[tag].append(lb); } + if (type === 'checkbox') { const inlab = make('mu-js', 'mujs-inlab'); - const la = make('label', '', { + const la = make('label', { onclick() { inp.dispatchEvent(new MouseEvent('click')); } }); inlab.append(inp, la); lb.append(inlab); - if (nm.includes('-')) { - return inp; + + const nm = /^(\w+)-(.+)/.exec(value); + if (nm) { + if (nm[1] === 'filters') { + inp.checked = cfg[nm[1]][nm[2]].enabled; + } else { + inp.checked = cfg[nm[1]][nm[2]]; + } + } else { + inp.checked = cfg[value]; } - if (/(greasy|sleazy)fork|openuserjs|gi(thub|st)/gi.test(nm)) { - for (const engine of cfg.engines) { - if (engine.name !== nm) continue; + ael(inp, 'change', (evt) => { + container.unsaved = true; + if (/filterlang/i.test(value)) { + container.rebuild = true; + } + if (nm) { + if (nm[1] === 'filters') { + cfg[nm[1]][nm[2]].enabled = evt.target.checked; + } else { + cfg[nm[1]][nm[2]] = evt.target.checked; + } + } else { + cfg[value] = evt.target.checked; + } + }); + + if (tag === 'engine') { + const engine = cfg.engines.find((engine) => engine.name === value); + if (engine) { inp.checked = engine.enabled; inp.dataset.engine = engine.name; ael(inp, 'change', (evt) => { container.unsaved = true; container.rebuild = true; engine.enabled = evt.target.checked; + MUList.engines = cfg.engines; }); if (engine.query) { - const d = DEFAULT_CONFIG.engines.find(e => e.name === engine.name); - const urlInp = make('input', '', { + const d = DEFAULT_CONFIG.engines.find((e) => e.name === engine.name); + const urlInp = make('input', { type: 'text', defaultValue: '', - value: decodeURIComponent(engine.query) ?? '', - placeholder: decodeURIComponent(d.query) ?? '', + value: decode(engine.query), + placeholder: decode(d.query), dataset: { name: nm, engine: engine.name @@ -3122,15 +3653,17 @@ function primaryFN() { container.rebuild = true; try { engine.query = encodeURIComponent(new URL(evt.target.value).toString()); + MUList.engines = cfg.engines; } catch (ex) { err(ex); } } }); - sec.append(urlInp); + obj.elemUrl = urlInp; + lb.append(urlInp); } if (engine.name === 'github') { - const ghToken = make('input', '', { + const ghToken = make('input', { type: 'text', defaultValue: '', value: engine.token ?? '', @@ -3142,77 +3675,69 @@ function primaryFN() { container.unsaved = true; container.rebuild = true; engine.token = evt.target.value; + MUList.engines = cfg.engines; } }); - sec.append(ghToken); - cfgMap.set('github-token', ghToken); + obj.elemToken = ghToken; + lb.append(ghToken); } } - - } else { - inp.checked = cfg[nm]; - ael(inp, 'change', (evt) => { - container.unsaved = true; - if (/filterlang/i.test(nm)) { - container.rebuild = true; - } - cfg[nm] = evt.target.checked; - }); } } else { + if (type === 'text') { + inp.defaultValue = ''; + inp.value = value ?? ''; + inp.placeholder = value ?? ''; + + if (tag === 'theme') { + inp.dataset[tag] = text; + ael(inp, 'change', (evt) => { + let isvalid = true; + try { + const val = evt.target.value; + const sty = container.root.style; + const str = `--mujs-${text}`; + const prop = sty.getPropertyValue(str); + if (isEmpty(val)) { + cfg.theme[text] = DEFAULT_CONFIG.theme[text]; + sty.removeProperty(str); + return; + } + if (prop === val) { + return; + } + sty.removeProperty(str); + sty.setProperty(str, val); + cfg.theme[text] = val; + } catch (ex) { + err(ex); + isvalid = false; + } finally { + if (isvalid) { + dom.cl.remove(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', false); + } else { + dom.cl.add(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', true); + } + } + }); + } + } + lb.append(inp); } - return inp; + obj.elem = inp; + cfgBase.push(obj); + + return lb; }; if (isGM) { - makerow('Sync with GM', 'checkbox', 'cache'); + makeRow(i18n$('userjs_sync'), 'cache'); + makeRow(i18n$('userjs_autoinject'), 'autoinject', 'checkbox', 'load'); } - makerow(i18n$('userjs_fullscreen'), 'checkbox', 'autoexpand', { - onchange(e) { - if (e.target.checked) { - dom.cl.add([btnfullscreen, main], 'expanded'); - dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('fsClose')); - } else { - dom.cl.remove([btnfullscreen, main], 'expanded'); - dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('fsOpen')); - } - } - }); - makerow(i18n$('redirect'), 'checkbox', 'sleazyredirect'); - makerow(i18n$('filter'), 'checkbox', 'filterlang'); - makerow(i18n$('preview_code'), 'checkbox', 'codePreview'); - for (const inp of [ - makerow('Recommend author', 'checkbox', 'recommend-author'), - makerow('Recommend scripts', 'checkbox', 'recommend-others') - ]) { - const nm = inp.dataset.name === 'recommend-author' ? 'author' : 'others'; - inp.checked = cfg.recommend[nm]; - ael(inp, 'change', (evt) => { - container.unsaved = true; - cfg.recommend[nm] = evt.target.checked; - }); - } - makerow('Greasy Fork', 'checkbox', 'greasyfork'); - makerow('Sleazy Fork', 'checkbox', 'sleazyfork'); - makerow('Open UserJS', 'checkbox', 'openuserjs'); - makerow('GitHub API', 'checkbox', 'github'); - // const ghAPI = cfg.engines.find((c) => c.name === 'github'); - // const ghToken = makerow('GitHub API (Token)', 'text', 'github', { - // defaultValue: '', - // value: ghAPI.token ?? '', - // placeholder: 'Paste Access Token', - // onchange(evt) { - // container.unsaved = true; - // container.rebuild = true; - // ghAPI.token = evt.target.value; - // // if (isNull(ghMsg)) { - // // ghAPI.token = evt.target.value; - // // } - // } - // }); - // ghToken.dataset.engine = 'github-token'; - // cfgMap.set('github-token', ghToken); - makerow(`${i18n$('dtime')} (ms)`, 'number', 'time', { + makeRow(i18n$('redirect'), 'sleazyredirect'); + makeRow(`${i18n$('dtime')} (ms)`, 'time', 'number', 'general', { defaultValue: 10000, value: cfg.time, min: 0, @@ -3239,86 +3764,228 @@ function primaryFN() { } } }); - const cbtn = make('mu-js', 'mujs-sty-flex'); - const savebtn = make('mujs-btn', 'save', { - textContent: i18n$('save'), - dataset: { - command: 'save' - }, - disabled: false - }); - const resetbtn = make('mujs-btn', 'reset', { - textContent: i18n$('reset'), - dataset: { - command: 'reset' + + makeRow(i18n$('auto_fetch'), 'autofetch', 'checkbox', 'load'); + makeRow(i18n$('userjs_fullscreen'), 'autoexpand', 'checkbox', 'load', { + onchange(e) { + if (e.target.checked) { + dom.cl.add([btnfullscreen, main], 'expanded'); + dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('collapse')); + } else { + dom.cl.remove([btnfullscreen, main], 'expanded'); + dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('expand')); + } } }); - const blacklist = make('textarea', '', { - dataset: { - name: 'blacklist' - }, - rows: '10', - autocomplete: false, - spellcheck: false, - wrap: 'soft', - value: JSON.stringify(cfg.blacklist, null, ' '), - oninput(evt) { - let isvalid = true; - try { - cfg.blacklist = JSON.parse(evt.target.value); - isvalid = true; - } catch (ex) { - err(ex); - isvalid = false; - } finally { - if (isvalid) { - dom.cl.remove(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', false); - } else { - dom.cl.add(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', true); + makeRow('Clear on Tab close', 'clearTabCache', 'checkbox', 'load'); + + makeRow(i18n$('default_sort'), 'autoSort', 'select', 'list'); + makeRow(i18n$('filter'), 'filterlang', 'checkbox', 'list'); + makeRow(i18n$('preview_code'), 'preview-code', 'checkbox', 'list'); + makeRow(i18n$('preview_metadata'), 'preview-metadata', 'checkbox', 'list'); + makeRow(i18n$('recommend_author'), 'recommend-author', 'checkbox', 'list'); + makeRow(i18n$('recommend_other'), 'recommend-others', 'checkbox', 'list'); + + for (const [k, v] of Object.entries(cfg.filters)) + makeRow(v.name, `filters-${k}`, 'checkbox', 'filters'); + + makeRow('Greasy Fork', 'greasyfork', 'checkbox', 'engine'); + makeRow('Sleazy Fork', 'sleazyfork', 'checkbox', 'engine'); + makeRow('Open UserJS', 'openuserjs', 'checkbox', 'engine'); + makeRow('GitHub API', 'github', 'checkbox', 'engine'); + + for (const [k, v] of Object.entries(cfg.theme)) makeRow(k, v, 'text', 'theme'); + + // const blacklist = make('textarea', { + // dataset: { + // name: 'blacklist' + // }, + // rows: '10', + // autocomplete: false, + // spellcheck: false, + // wrap: 'soft', + // value: JSON.stringify(cfg.blacklist, null, ' '), + // oninput(evt) { + // let isvalid = true; + // try { + // cfg.blacklist = JSON.parse(evt.target.value); + // isvalid = true; + // } catch (ex) { + // err(ex); + // isvalid = false; + // } finally { + // if (isvalid) { + // dom.cl.remove(evt.target, 'mujs-invalid'); + // dom.prop(savebtn, 'disabled', false); + // } else { + // dom.cl.add(evt.target, 'mujs-invalid'); + // dom.prop(savebtn, 'disabled', true); + // } + // } + // } + // }); + // const addList = make('mujs-add', { + // textContent: '+', + // dataset: { + // command: 'new-list' + // } + // }); + // const n = make('input', { + // type: 'text', + // defaultValue: '', + // value: '', + // placeholder: 'Name', + // }); + // const inpValue = make('input', { + // type: 'text', + // defaultValue: '', + // value: '', + // placeholder: 'Value', + // }); + // const label = make('label', 'new-list hidden', { + // dataset: { + // blacklist: 'new-list' + // } + // }); + // label.append(n, inpValue, addList); + // listSec.append(label); + // ael(addList, 'click', () => { + // if (isEmpty(n.value) || isEmpty(inpValue.value)) { + // return + // }; + // createList(n.value, n.value, inpValue.value); + // }); + const createList = (key, v = '', disabled = false, type = 'String') => { + let txt = key; + if (typeof key === 'string') { + if (key.startsWith('userjs-')) { + disabled = true; + const s = key.substring(7); + txt = `Built-in "${s}"`; + v = builtinList[s]; + } + } else { + if (!key.enabled) { + return; + } + } + + if (isRegExp(v)) { + v = v.toString(); + type = 'RegExp'; + } else { + v = JSON.stringify(v); + type = 'Object'; + } + + const lb = make('label', 'hidden', { + textContent: txt, + dataset: { + blacklist: key + } + }); + const inp = make('input', { + type: 'text', + defaultValue: '', + value: v ?? '', + placeholder: v ?? '', + dataset: { + blacklist: key + }, + onchange(evt) { + let isvalid = true; + try { + const val = evt.target.value; + if (isEmpty(val)) { + return; + } + isvalid = true; + } catch (ex) { + err(ex); + isvalid = false; + } finally { + if (isvalid) { + dom.cl.remove(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', false); + } else { + dom.cl.add(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', true); + } } } + }); + const selType = make('select', { + disabled, + dataset: { + blacklist: key + } + }); + if (disabled) { + inp.readOnly = true; + const o = make('option', { + value: type, + textContent: type + }); + selType.append(o); + } else { + for (const selV of ['String', 'RegExp', 'Object']) { + const o = make('option', { + value: selV, + textContent: selV + }); + selType.append(o); + } } - }); - const theme = make('textarea', '', { - dataset: { - name: 'theme' + selType.value = type; + lb.append(inp, selType); + sections.blacklist.append(lb); + }; + for (const key of cfg.blacklist) createList(key); + const transfers = { + export: { + cfg: make('mujs-btn', 'mujs-export sub-section hidden', { + textContent: i18n$('export_config'), + dataset: { + command: 'export-cfg', + exp: 'export-cfg' + } + }), + theme: make('mujs-btn', 'mujs-export sub-section hidden', { + textContent: i18n$('export_theme'), + dataset: { + command: 'export-theme', + exp: 'export-theme' + } + }) }, - rows: '10', - autocomplete: false, - spellcheck: false, - wrap: 'soft', - value: JSON.stringify(cfg.theme, null, ' '), - oninput(evt) { - let isvalid = true; - try { - cfg.theme = JSON.parse(evt.target.value); - container.renderTheme(JSON.parse(evt.target.value)); - isvalid = true; - } catch (ex) { - err(ex); - isvalid = false; - } finally { - if (isvalid) { - dom.cl.remove(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', false); - } else { - dom.cl.add(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', true); + import: { + cfg: make('mujs-btn', 'mujs-import sub-section hidden', { + textContent: i18n$('import_config'), + dataset: { + command: 'import-cfg', + exp: 'import-cfg' } - } + }), + theme: make('mujs-btn', 'mujs-import sub-section hidden', { + textContent: i18n$('import_theme'), + dataset: { + command: 'import-theme', + exp: 'import-theme' + } + }) } - }); - cfgMap.set('blacklist', blacklist); - cfgMap.set('theme', theme); - cbtn.append(resetbtn, savebtn); - cfgpage.append(blacklist, theme, cbtn); + }; + for (const value of Object.values(transfers)) { + for (const v of Object.values(value)) { + sections.exp.append(v); + } + } + cfgpage.append(cbtn); }; // #endregion - container.tab.custom = (host) => { + container.Tabs.custom = (host) => { MUList.host = host; - MUList.build(); + respHandles.build(); }; ael(mainframe, 'mouseenter', (evt) => { evt.preventDefault(); @@ -3330,7 +3997,7 @@ function primaryFN() { evt.preventDefault(); evt.stopPropagation(); evt.target.style.opacity = container.opacityMin; - timeoutFrame(); + container.timeoutFrame(); }); ael(mainframe, 'click', (evt) => { evt.preventDefault(); @@ -3339,10 +4006,7 @@ function primaryFN() { dom.cl.add(mainframe, 'hidden'); if (cfg.autoexpand) { dom.cl.add([btnfullscreen, main], 'expanded'); - dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('fsClose')); - } - if (dom.cl.has(mainframe, 'error')) { - tab.create('mujs:settings'); + dom.prop(btnfullscreen, 'innerHTML', iconSVG.load('collapse')); } }); ael(urlBar, 'input', (evt) => { @@ -3354,29 +4018,27 @@ function primaryFN() { * @type { string } */ const val = evt.target.value; + const section = qsA('mujs-section[data-name]', cfgpage); if (isEmpty(val)) { - dom.cl.remove(qsA('tr[data-engine]', tabbody), 'hidden'); - dom.cl.remove(qsA('mujs-section[data-name]', cfgpage), 'hidden'); + dom.cl.remove(container.toElem(), 'hidden'); + dom.cl.remove(section, 'hidden'); return; } const finds = new Set(); if (!dom.cl.has(cfgpage, 'hidden')) { const reg = new RegExp(val, 'gi'); - for (const elem of qsA('mujs-section[data-name]', cfgpage)) { - if (!elem) { - continue; - } - if (finds.has(elem)) { - continue; - } - if (elem.dataset.name.match(reg)) { - finds.add(elem); - } + for (const elem of section) { + if (!isElem(elem)) continue; + if (finds.has(elem)) continue; + if (elem.dataset.name.match(reg)) finds.add(elem); } - dom.cl.add(qsA('mujs-section[data-name]', cfgpage), 'hidden'); + dom.cl.add(section, 'hidden'); dom.cl.remove([...finds], 'hidden'); return; } + const cacheValues = Array.from(container).filter(({ _mujs }) => { + return !finds.has(_mujs.root); + }); /** * @param {RegExpMatchArray} regExp * @param {keyof import("../typings/types.d.ts").GSForkQuery} key @@ -3384,43 +4046,29 @@ function primaryFN() { const ezQuery = (regExp, key) => { const q_value = val.replace(regExp, ''); const reg = new RegExp(q_value, 'gi'); - for (const v of userjsCache.values()) { - const elem = v._mujs.root; - if (!elem) { - continue; - } - if (finds.has(elem)) { - continue; - } + for (const v of cacheValues) { let k = v[key]; if (typeof k === 'number') { k = `${v[key]}`; } if (k && k.match(reg)) { - finds.add(elem); + finds.add(v._mujs.root); } } }; if (val.match(/^(code_url|url):/)) { ezQuery(/^(code_url|url):/, 'code_url'); } else if (val.match(/^(author|users?):/)) { - const q_value = val.replace(/^(author|users?):/, ''); - const reg = new RegExp(q_value, 'gi'); - for (const v of userjsCache.values()) { - const elem = v._mujs.root; - if (!elem) { - continue; - } - if (finds.has(elem)) { - continue; - } - if (v.users) { + const parts = /^[\w_]+:(.+)/.exec(val); + if (parts) { + const reg = new RegExp(parts[1], 'gi'); + for (const v of cacheValues.filter((v) => !isEmpty(v.users))) { for (const user of v.users) { for (const value of Object.values(user)) { if (typeof value === 'string' && value.match(reg)) { - finds.add(elem); + finds.add(v._mujs.root); } else if (typeof value === 'number' && `${value}`.match(reg)) { - finds.add(elem); + finds.add(v._mujs.root); } } } @@ -3437,50 +4085,43 @@ function primaryFN() { } else if (val.match(/^description:/)) { ezQuery(/^description:/, 'description'); } else if (val.match(/^(search_engine|engine):/)) { - const q_value = val.replace(/^(search_engine|engine):/, ''); - const reg = new RegExp(q_value, 'gi'); - for (const v of userjsCache.values()) { - const elem = v._mujs.root; - if (!elem) { - continue; - } - if (finds.has(elem)) { - continue; - } - if (v._mujs.info.engine) { - for (const value of Object.values(v._mujs.info.engine)) { - if (typeof value === 'string' && value.match(reg)) { - finds.add(elem); - } + const parts = /^[\w_]+:(\w+)/.exec(val); + if (parts) { + const reg = new RegExp(parts[1], 'gi'); + for (const { _mujs } of cacheValues) + if (_mujs.info.engine.name.match(reg)) finds.add(_mujs.root); + } + } else if (val.match(/^filter:/)) { + const parts = /^\w+:(.+)/.exec(val); + if (parts) { + const bsFilter = loadFilters(); + const filterType = bsFilter.get(parts[1].trim().toLocaleLowerCase()); + if (filterType) { + const { reg } = filterType; + for (const { name, users, _mujs } of cacheValues) { + if ([{ name }, ...users].find((o) => o.name.match(reg))) continue; + finds.add(_mujs.root); } } } + } else if (val.match(/^recommend:/)) { + for (const { url, id, users, _mujs } of cacheValues) { + if ( + users.find((u) => u.id === authorID) || + goodUserJS.includes(url) || + goodUserJS.includes(id) + ) { + finds.add(_mujs.root); + } + } } else { const reg = new RegExp(val, 'gi'); - for (const v of userjsCache.values()) { - const elem = v._mujs.root; - if (!elem) { - continue; - } - if (finds.has(elem)) { - continue; - } - if (v.name && v.name.match(reg)) { - finds.add(elem); - } - if (v.description && v.description.match(reg)) { - finds.add(elem); - } - const code_data = v._mujs.code.data; - if (code_data) { - const code_obj = new ParseUserJS(code_data); - const meta = code_obj.parse_meta(code_data); - for (const key of Object.keys(meta)) { - if (/name|desc/i.test(key) && key.match(reg)) { - finds.add(elem); - } - } - } + for (const v of cacheValues) { + if (v.name && v.name.match(reg)) finds.add(v._mujs.root); + if (v.description && v.description.match(reg)) finds.add(v._mujs.root); + if (v._mujs.code.data_meta) + for (const key of Object.keys(v._mujs.code.data_meta)) + if (/name|desc/i.test(key) && key.match(reg)) finds.add(v._mujs.root); } } dom.cl.add(qsA('tr[data-engine]', tabbody), 'hidden'); @@ -3489,65 +4130,74 @@ function primaryFN() { ael(urlBar, 'change', (evt) => { evt.preventDefault(); const val = evt.target.value; - if (urlBar.placeholder === i18n$('newTab') && qs('mujs-tab.active', toolbar)) { - const tabElem = qs('mujs-tab.active', toolbar); - const tabHost = qs('mujs-host', tabElem); - if (val.startsWith(tab.protocal)) { - tab.close(tabElem); - if (tab.hasTab(val)) { - tab.active(tab.Tab.get(val)); + const tabElem = Tabs.getActive(); + if (urlBar.placeholder === i18n$('newTab') && tabElem) { + const tabHost = tabElem.firstElementChild; + const host = formatURL(normalizedHostname(val)); + if (Tabs.protoReg.test(val)) { + const createdTab = Tabs.getTab(val); + Tabs.close(tabElem); + if (createdTab) { + Tabs.active(createdTab); } else { - tab.create(val); + Tabs.create(val); } - return; - } else if (val === '*') { - tabElem.dataset.host = val; + evt.target.placeholder = i18n$('search_placeholder'); + evt.target.value = ''; + } else if (host === '*') { + tabElem.dataset.host = host; tabHost.title = ''; tabHost.textContent = ''; - MUList.host = val; - MUList.build(); - return; - } - const value = container.getHost(val); - if (container.checkBlacklist(value)) { - showError(`Blacklisted "${value}"`); - return; + MUList.host = host; + respHandles.build(); + } else if (container.checkBlacklist(host)) { + showError(`Blacklisted "${host}"`); + } else { + tabElem.dataset.host = host; + tabHost.title = host; + tabHost.textContent = host; + MUList.host = host; + respHandles.build(); } - tabElem.dataset.host = value; - tabHost.title = value; - tabHost.textContent = value; - MUList.host = value; - MUList.build(); - return; } }); - makecfg(); - MUList.build().then(timeoutFrame); - dbg('Container', container); + scheduler.postTask(makecfg, { priority: 'background' }); + + respHandles.build = async () => { + await scheduler.postTask(MUList.build, { priority: 'background' }); + container.timeoutFrame(); + }; + + if (cfg.autofetch) { + respHandles.build(); + } else { + container.timeoutFrame(); + } } catch (ex) { err(ex); container.remove(); } + return respHandles; } // #endregion /** * @template { Function } F - * @param { (this: F, doc: Document) => any } onDomReady + * @param { (this: F, doc: Document) => * } onDomReady */ const loadDOM = (onDomReady) => { - if (!isFN(onDomReady)) { - return; - } - if (document.readyState === 'interactive' || document.readyState === 'complete') { - onDomReady.call({}, document); + if (isFN(onDomReady)) { + if (document.readyState === 'interactive' || document.readyState === 'complete') { + onDomReady(document); + } else { + document.addEventListener('DOMContentLoaded', (evt) => onDomReady(evt.target), { + once: true + }); + } } - document.addEventListener('DOMContentLoaded', (evt) => onDomReady.call({}, evt.target), { - once: true - }); }; -const init = async () => { - const stored = await StorageSystem.getValue('Config', DEFAULT_CONFIG); +const init = async (prefix = 'Config') => { + const stored = await StorageSystem.getValue(prefix, DEFAULT_CONFIG); cfg = { ...DEFAULT_CONFIG, ...stored @@ -3555,19 +4205,20 @@ const init = async () => { info('Config:', cfg); loadDOM((doc) => { try { - if (window.location === null) { - throw new Error('"window.location" is null, reload the webpage or use a different one'); - } - if (doc === null) { - throw new Error('"doc" is null, reload the webpage or use a different one'); - } - if (window.trustedTypes && window.trustedTypes.createPolicy) { - window.trustedTypes.createPolicy('default', { - createHTML: (string) => string + if (window.location === null) + throw new Error('"window.location" is null, reload the webpage or use a different one', { + cause: 'loadDOM' + }); + if (doc === null) + throw new Error('"doc" is null, reload the webpage or use a different one', { + cause: 'loadDOM' }); + container.redirect(); + if (cfg.autoinject) { + container.inject(primaryFN, doc); + } else { + container.timeoutFrame(); } - sleazyRedirect(); - container.inject(primaryFN, doc); Command.register(i18n$('userjs_inject'), () => { container.inject(primaryFN, doc); }); diff --git a/src/UserJS/svg/bug-svgrepo-com.svg b/src/UserJS/svg/bug-svgrepo-com.svg deleted file mode 100644 index 32b7852..0000000 --- a/src/UserJS/svg/bug-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/UserJS/svg/close-svgrepo-com.svg b/src/UserJS/svg/close-svgrepo-com.svg deleted file mode 100644 index a4a1e54..0000000 --- a/src/UserJS/svg/close-svgrepo-com.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/src/UserJS/svg/download-svgrepo-com.svg b/src/UserJS/svg/download-svgrepo-com.svg deleted file mode 100644 index 9b8cf09..0000000 --- a/src/UserJS/svg/download-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/UserJS/svg/expand-full-fullscreen-svgrepo-com.svg b/src/UserJS/svg/expand-full-fullscreen-svgrepo-com.svg deleted file mode 100644 index 53218ff..0000000 --- a/src/UserJS/svg/expand-full-fullscreen-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/UserJS/svg/filter-svgrepo-com.svg b/src/UserJS/svg/filter-svgrepo-com.svg deleted file mode 100644 index 8a5d29e..0000000 --- a/src/UserJS/svg/filter-svgrepo-com.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/UserJS/svg/fork-svgrepo-com.svg b/src/UserJS/svg/fork-svgrepo-com.svg deleted file mode 100644 index 8087aa8..0000000 --- a/src/UserJS/svg/fork-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/UserJS/svg/fullscreen-svgrepo-com.svg b/src/UserJS/svg/fullscreen-svgrepo-com.svg deleted file mode 100644 index 15379af..0000000 --- a/src/UserJS/svg/fullscreen-svgrepo-com.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UserJS/svg/github-svgrepo-com.svg b/src/UserJS/svg/github-svgrepo-com.svg deleted file mode 100644 index eaac757..0000000 --- a/src/UserJS/svg/github-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/UserJS/svg/minus-svgrepo-com.svg b/src/UserJS/svg/minus-svgrepo-com.svg deleted file mode 100644 index 4f655c0..0000000 --- a/src/UserJS/svg/minus-svgrepo-com.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/UserJS/svg/navigation-svgrepo-com.svg b/src/UserJS/svg/navigation-svgrepo-com.svg deleted file mode 100644 index 3b865b1..0000000 --- a/src/UserJS/svg/navigation-svgrepo-com.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - ic_fluent_navigation_24_filled - Created with Sketch. - - - - - - \ No newline at end of file diff --git a/src/UserJS/svg/plus-svgrepo-com.svg b/src/UserJS/svg/plus-svgrepo-com.svg deleted file mode 100644 index 66a5b01..0000000 --- a/src/UserJS/svg/plus-svgrepo-com.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/UserJS/svg/search-svgrepo-com.svg b/src/UserJS/svg/search-svgrepo-com.svg deleted file mode 100644 index eb7f58d..0000000 --- a/src/UserJS/svg/search-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/UserJS/svg/settings-svgrepo-com.svg b/src/UserJS/svg/settings-svgrepo-com.svg deleted file mode 100644 index eaeef67..0000000 --- a/src/UserJS/svg/settings-svgrepo-com.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index 00a9859..9810e02 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "إغلاق Userscript+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "Code" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 3e51fdc..4666f53 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "Userscript+ schließen" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "Quelltext" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 87c1660..7a9e9d8 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -93,7 +93,7 @@ "message": "Preview Code" }, "saveFile": { - "message": "Save File" + "message": "Download" }, "newTab": { "message": "New Tab" @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "Close Userscript+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "Code" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/en_GB/messages.json b/src/_locales/en_GB/messages.json index e824ab4..7a9e9d8 100644 --- a/src/_locales/en_GB/messages.json +++ b/src/_locales/en_GB/messages.json @@ -42,7 +42,7 @@ "message": "Search" }, "search_placeholder": { - "message": "Search scripts" + "message": "Search for userscripts" }, "install": { "message": "Install" @@ -66,7 +66,7 @@ "message": "Good" }, "ok": { - "message": "Ok" + "message": "Okay" }, "bad": { "message": "Bad" @@ -93,7 +93,7 @@ "message": "Preview Code" }, "saveFile": { - "message": "Save File" + "message": "Download" }, "newTab": { "message": "New Tab" @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "Close Userscript+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "Code" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index eec7c40..3091ecb 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "Cerrar Userscript+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "Código" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index fdf5c44..f4d7dc4 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "Fermer Userscript+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "Code" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index 398f3ec..105a874 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "Userscript+ を閉じる" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "コード" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index edc9988..6d3ee8f 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "Sluit Userscript+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "Code" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index 57e4ce7..9313aae 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "Zamknij Userscript+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "Kod" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index cb00ae7..f30c5cc 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "Закрыть Userscript+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "Исходный код" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/zh/messages.json b/src/_locales/zh/messages.json index d527783..1c40f1c 100644 --- a/src/_locales/zh/messages.json +++ b/src/_locales/zh/messages.json @@ -4,7 +4,7 @@ "description": "Must not exceed 45 characters" }, "extDesc": { - "message": "The power of Greasy Fork on the go! Finds available UserJS for the current webpage.", + "message": "为当前网页查找可用的 UserJS,随时随地发挥 Greasy Fork 的威力!", "description": "Must not exceed 132 characters" }, "extShortDesc": { @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "关闭 Userscript+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "代码" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index d527783..5015338 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "关闭 Userscript+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "代码" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index e5cce7e..7059ad6 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -139,5 +139,32 @@ }, "userjs_close": { "message": "關閉用戶腳本+" + }, + "userjs_sync": { + "message": "Sync" + }, + "userjs_autoinject": { + "message": "Inject on load" + }, + "auto_fetch": { + "message": "Fetch on load" + }, + "code": { + "message": "代碼" + }, + "metadata": { + "message": "Metadata" + }, + "preview_metadata": { + "message": "Preview Metadata" + }, + "recommend_author": { + "message": "Recommend Author" + }, + "recommend_other": { + "message": "Recommend Others" + }, + "default_sort": { + "message": "Default Sort" } } diff --git a/src/html/background.html b/src/html/background.html deleted file mode 100644 index b268206..0000000 --- a/src/html/background.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - Magic UserJS+ Background Page - - - - - - - diff --git a/src/html/form.html b/src/html/form.html deleted file mode 100644 index 4d9e1b8..0000000 --- a/src/html/form.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - Reset - Save - diff --git a/src/html/popup.html b/src/html/popup.html index 111813b..2357f06 100644 --- a/src/html/popup.html +++ b/src/html/popup.html @@ -2,56 +2,17 @@ - - - + + - + - - - - + - - - - - + >
- +
- - - diff --git a/src/html/settings.html b/src/html/settings.html deleted file mode 100644 index 335a789..0000000 --- a/src/html/settings.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - UserJS - Settings - - - - -

Settings

- - -
- -
-
-
- - - - - - diff --git a/src/img/icon_16.png b/src/img/icon_16.png index 03a9c49..950710b 100644 Binary files a/src/img/icon_16.png and b/src/img/icon_16.png differ diff --git a/src/img/icon_32.png b/src/img/icon_32.png index c4c3b59..962d9eb 100644 Binary files a/src/img/icon_32.png and b/src/img/icon_32.png differ diff --git a/src/img/icon_48.png b/src/img/icon_48.png new file mode 100644 index 0000000..ebf1fe4 Binary files /dev/null and b/src/img/icon_48.png differ diff --git a/src/img/icon_64.png b/src/img/icon_64.png new file mode 100644 index 0000000..d445fdc Binary files /dev/null and b/src/img/icon_64.png differ diff --git a/src/img/verified-svgrepo-com.svg b/src/img/verified-svgrepo-com.svg new file mode 100644 index 0000000..b5d5c48 --- /dev/null +++ b/src/img/verified-svgrepo-com.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/js/background.js b/src/js/background.js index e8c2464..82394d6 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -1,133 +1,306 @@ 'use strict'; + +import { webext, runtime } from './ext.js'; +import { hermes } from './messager.js'; import { log, err, info } from './logger.js'; -import { i18n$ } from './i18n.js'; -// import Config from './config.js'; import Network from './network.js'; -import { reqCode } from './request-code.js'; -import { language } from './language.js'; -import { isBlank, isNull, isEmpty, normalizeTarget, template } from './util.js'; -import { XMap } from './XMap.js'; -import storage, { storageByPrefix } from './storage.js'; - -const MUJS = { - cache: new XMap() -}; - -window.MUJS = MUJS; -window.Config = storage; -window.storageByPrefix = storageByPrefix; +import { + isBlank, + isNull, + isEmpty, + loadFilters, + isRegExp, + isObj, + formatURL, + decode +} from './util.js'; +import { BLANK_PAGE, template, builtinList } from './constants.js'; +import storage, { DEFAULT_CONFIG } from './storage.js'; +import { BaseContainer, BaseList } from './container.js'; +import { i18n$ } from './i18n.js'; /** * @type { import("../typings/types").config } */ -const cfg = {}; -/** - * @type { import("../typings/types").config } - */ -const DEFAULT_CONFIG = { - // cache: true, - codePreview: false, - // autoexpand: false, - filterlang: false, - sleazyredirect: false, - // time: 10000, - blacklist: [ - { - enabled: true, - regex: true, - flags: '', - name: 'Blacklist 1', - url: '(gov|cart|checkout|login|join|signin|signup|sign-up|password|reset|password_reset)' - }, - { - enabled: true, - regex: true, - flags: '', - name: 'Blacklist 2', - url: '(pay|bank|money|localhost|authorize|checkout|bill|wallet|router)' - }, - { - enabled: true, - regex: false, - flags: '', - name: 'Blacklist 3', - url: 'https://home.bluesnap.com' - }, - { - enabled: true, - regex: false, - flags: '', - name: 'Blacklist 4', - url: ['zalo.me', 'skrill.com'] - } - ], - engines: [ - { - enabled: true, - name: 'greasyfork', - url: 'https://greasyfork.org' - }, - { - enabled: false, - name: 'sleazyfork', - url: 'https://sleazyfork.org' - }, - { - enabled: false, - name: 'openuserjs', - url: 'https://openuserjs.org/?q=' - }, - { - enabled: false, - name: 'github', - url: 'https://api.github.com/search/code?q=', - token: '' - } - ], - theme: { - 'even-row': '', - 'odd-row': '', - 'even-err': '', - 'odd-err': '', - 'background-color': '', - 'gf-color': '', - 'sf-color': '', - 'border-b-color': '', - 'gf-btn-color': '', - 'sf-btn-color': '', - 'sf-txt-color': '', - 'txt-color': '', - 'chck-color': '', - 'chck-gf': '', - 'chck-git': '', - 'chck-open': '', - placeholder: '', - 'position-top': '', - 'position-bottom': '', - 'position-left': '', - 'position-right': '', - 'font-family': '' - }, - recommend: { - author: true, - others: true - } -}; - +let cfg = {}; const initCfg = async () => { const c = await storage.config.getMulti(); if (!c) { await storage.config.set(DEFAULT_CONFIG); } - Object.assign(cfg, c); + cfg = { + ...DEFAULT_CONFIG, + ...c + }; return cfg; }; initCfg(); -const { hermes } = userjs; +class Container extends BaseContainer { + constructor(url) { + super(url); + } + + checkBlacklist(str) { + str = str || this.host; + let blacklisted = false; + if (/accounts*\.google\./.test(str)) { + blacklisted = true; + } + const blacklist = cfg.blacklist ?? []; + for (const b of blacklist) { + if (typeof b === 'string') { + if (b.startsWith('userjs-')) { + const r = /userjs-(\w+)/.exec(b)[1]; + const biList = builtinList[r]; + if (isRegExp(biList)) { + if (!biList.test(str)) continue; + blacklisted = true; + } else if (isObj(biList) && biList.host === this.host) { + blacklisted = true; + } + } + } else if (isObj(b)) { + if (!b.enabled) { + continue; + } + if (b.regex === true) { + const reg = new RegExp(b.url, b.flags); + if (!reg.test(str)) continue; + blacklisted = true; + } + if (Array.isArray(b.url)) { + for (const c of b.url) { + if (!str.includes(c)) continue; + blacklisted = true; + } + } + if (!str.includes(b.url)) continue; + blacklisted = true; + } + } + this.isBlacklisted = blacklisted; + return this.isBlacklisted; + } +} +const container = new Container(); +// #region List +class List extends BaseList { + constructor(hostname = undefined, tabId = -2) { + super(hostname, container, cfg); + this.build = this.build.bind(this); + if (isEmpty(hostname)) hostname = BLANK_PAGE; + this.engines = cfg.engines; + this.host = hostname; + this.tabId = tabId; + } + + // #region Builder + async build() { + await initCfg(); + this.engines = cfg.engines; + const { container, blacklisted, engines, host, domain, tabId } = this; + if (blacklisted || isEmpty(engines) || host === BLANK_PAGE) { + return []; + } + const records = []; + const fetchRecords = []; + const bsFilter = loadFilters(cfg); + const hostCache = Array.from(this); + info('Building list', { hostCache, engines, container, list: this }); + try { + const g = this.groupBy(); + if (isEmpty(g)) { + const toFetch = engines.filter((engine) => !g[engine.name]); + for (const engine of toFetch) { + info(`Fetching from "${engine.name}" for "${host}"`); + const respError = (error) => { + if (!error.cause) error.cause = engine.name; + if (error.message.startsWith('429')) { + err(`Engine: "${engine.name}" Too many requests...`); + return; + } + err(`Engine: "${engine.name}"`, error.message); + }; + const _mujs = (d) => { + const obj = { + ...template, + ...d, + _mujs: { + root: {}, + info: { + tabId, + engine, + host + }, + code: { + meta: {} + } + } + }; + return obj; + }; + /** + * Prior to UserScript v7.0.0 + * @template {string} F + * @param {F} fallback + * @returns {F} + */ + const toQuery = (fallback) => { + if (engine.query) { + return decode(engine.query) + .replace(/\{host\}/g, host) + .replace(/\{domain\}/g, domain); + } + return fallback; + }; + /** + * @param { import("../typings/types.d.ts").GSFork } dataQ + */ + const forkFN = async (dataQ) => { + if (!dataQ) { + err('Invalid data received from the server, check internet connection'); + return; + } + /** + * @type { import("../typings/types.d.ts").GSForkQuery[] } + */ + const dq = Array.isArray(dataQ) ? dataQ : Array.isArray(dataQ.query) ? dataQ.query : []; + const dataA = dq + .filter(Boolean) + .filter((d) => !d.deleted) + .filter(bsFilter.match); + if (isBlank(dataA)) { + return; + } + const data = dataA.map(_mujs); + + for (const ujs of data) { + if (!ujs._mujs.code.data_code_block && (cfg.preview.code || cfg.preview.metadata)) { + await ujs._mujs.code.request(); + } + if (!container.userjsCache.has(ujs.id)) container.userjsCache.set(ujs.id, ujs); + } + records.push(...data); + }; + const gitFN = async (data) => { + try { + if (isBlank(data.items)) { + return; + } + for (const r of data.items) { + const ujs = _mujs({ + id: r.id ?? 0, + name: r.name, + description: isEmpty(r.description) ? i18n$('no_license') : r.description, + url: r.html_url, + code_url: r.html_url, + page_url: `${r.url}/contents/README.md`, + created_at: r.created_at, + code_updated_at: r.updated_at || Date.now(), + daily_installs: r.watchers_count ?? 0, + good_ratings: r.stargazers_count ?? 0, + users: [ + { + name: r.owner.login, + url: r.owner.html_url + } + ] + }); + if (r.license?.name) ujs.license = r.license.name; + const rootPath = r.contents_url.replace(/\{\+path\}/, ''); + const fetchContent = async (dir) => { + const contents = await Network.req(dir, 'GET', 'json', { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' + } + }).catch(respError); + for (const content of contents) { + if (content.type === 'file') { + if (/\.user\.js$/.test(content.name)) { + ujs.code_urls.push({ + name: content.name, + code_url: content.download_url + }); + } else if (/\.user\.css$/.test(content.name)) { + ujs.code_urls.push({ + name: content.name, + code_url: content.download_url + }); + } + } else if (content.type === 'dir') { + await fetchContent(`${rootPath}/${content.path}`); + } + } + }; + await fetchContent(rootPath); + if (isEmpty(ujs.code_urls)) { + ujs.deleted = true; + } else if ( + !ujs._mujs.code.data_code_block && + (cfg.preview.code || cfg.preview.metadata) + ) { + await ujs._mujs.code.request(); + } + records.push(ujs); + } + } catch (ex) { + err(ex); + } + }; + let netFN; + if (/github/gi.test(engine.name)) { + if (isEmpty(engine.token)) { + err(`"${engine.name}" requires a token to use`); + continue; + } + const data = { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' + } + }; + Network.req( + `https://api.github.com/search/repositories?q=topic:userstyle+topic:${domain}`, + 'GET', + 'json', + data + ) + .then(gitFN) + .catch(respError); + netFN = Network.req( + toQuery( + `https://api.github.com/search/repositories?q=topic:userscript+topic:${domain}` + ), + 'GET', + 'json', + data + ).then(gitFN); + } else { + netFN = Network.req( + toQuery(`${engine.url}/scripts/by-site/${host}.json?language=all`) + ).then(forkFN); + } + if (netFN) fetchRecords.push(netFN.catch(respError)); + } + } + if (!isBlank(fetchRecords)) { + await Promise.allSettled(fetchRecords); + } + } catch (ex) { + err(ex); + } + return Array.from(this); + } + // #endregion +} +// #endregion const msgCache = {}; -webext.runtime.onConnect.addListener((p) => { +runtime.onConnect.addListener((p) => { hermes.port = p; /** * Default post message to send to all connected scripts @@ -138,532 +311,148 @@ webext.runtime.onConnect.addListener((p) => { log('Background Script: received message from content script', root); const r = root.msg; if (root.channel === 'Save') { - const v = isNull(r.value) ? cfg[r.prop] : r.value; - storage.config.setOne(r.prop, v); - } - if (root.channel === 'Reset') { + if (r.cfg) { + storage.config.set(r.cfg).then(initCfg); + } else { + const v = isNull(r.value) ? cfg[r.prop] : r.value; + storage.config.setOne(r.prop, v).then(initCfg); + } + } else if (root.channel === 'Reset') { storage.config.set(DEFAULT_CONFIG); + } else if (root.channel === 'Clear') { + const cache = Array.from(container).filter(({ _mujs }) => _mujs.info.host === r.host); + for (const ujs of cache) container.userjsCache.delete(ujs.id); + } else if (root.channel === 'Engine' && cfg.engines) { + const engine = cfg.engines.find((engine) => engine.name === r.engine.name); + for (const [k, v] of Object.entries(r.engine)) { + engine[k] = v; + } + storage.config.set(cfg).then(initCfg); } }); }); -// Unsupport webpages for each engine -const unsupported = { - greasyfork: ['pornhub.com'], - sleazyfork: ['pornhub.com'], - openuserjs: [], - github: [] -}; - -const formatURL = (txt) => - txt - .split('.') - .splice(-2) - .join('.') - .replace(/\/|https:/g, ''); - +const MUList = new List(); /** * @param {chrome.tabs.Tab} tab - * @returns {string} */ -const getTabUrl = (tab) => tab.pendingUrl || tab.url || ''; -// const queryOptions = { active: true, lastFocusedWindow: true }; -const queryOptions = { active: true, currentWindow: true }; - -// class Website { -// constructor(e) { -// this.engine = e.engine ?? {}; -// this.data = e.data ?? []; -// this.host = e.host ?? 'about:blank'; -// this.link = e.link ?? 'about:blank'; -// } - -// setData(v) { -// this.data = v ?? []; -// } -// } - -// class Engine { -// constructor(host, engine) { -// this.host = host ?? 'about:blank'; -// this.engine = engine ?? {}; -// this.data = []; -// } - -// setEngine(engine) { -// this.engine = engine; -// return this; -// } - -// setHost(host) { -// if (host !== this.host) { -// this.host = host; -// } -// return this; -// } - -// async getData() { -// if (isEmpty(this.host) || this.host === 'about:blank') { -// return [] -// } -// const engine = this.engine; -// /** -// * @param { import("../typings/types").GSFork } dataQ -// */ -// const forkFN = async (dataQ) => { -// if (!dataQ) { -// err('Invalid data received from the server, check internet connection'); -// return []; -// } -// /** -// * @type { import("../typings/types").GSForkQuery[] } -// */ -// const dq = Array.isArray(dataQ) ? dataQ : Array.isArray(dataQ.query) ? dataQ.query : []; -// const data = dq.filter((d) => !d.deleted); -// if (isBlank(data)) { -// return []; -// } -// const hideData = []; -// const inUserLanguage = (d) => { -// const dlocal = d.locale.split('-')[0] ?? d.locale; -// if (language.cache.includes(dlocal)) { -// return true; -// } -// hideData.push(d); -// return false; -// }; -// const filterLang = data.filter((d) => { -// if (cfg.filterlang && !inUserLanguage(d)) { -// return false; -// } -// return true; -// }); - -// let finalList = filterLang; -// const hds = []; -// for (const ujs of hideData) { -// await reqCode(ujs, true); -// if (ujs.translated) { -// hds.push(ujs); -// } -// } -// finalList = [...new Set([...hds, ...filterLang])]; - -// this.data.push(...finalList); -// return finalList; -// }; -// const gitFN = async (data) => { -// const records = []; -// try { -// if (isBlank(data.items)) { -// err('Invalid data received from the server, TODO fix this'); -// return; -// } -// for (const r of data.items) { -// const ujs = template.merge({ -// name: r.name, -// description: isEmpty(r.repository.description) -// ? i18n$('no_license') -// : r.repository.description, -// url: r.html_url, -// code_url: r.html_url.replace(/\/blob\//g, '/raw/'), -// code_updated_at: r.commit || Date.now(), -// total_installs: r.score, -// users: [ -// { -// name: r.repository.owner.login, -// url: r.repository.owner.html_url -// } -// ] -// }); -// if (cfg.codePreview && !ujs.code_data) { -// await reqCode(ujs); -// } -// records.push(ujs); -// } -// this.data.push(...records); -// } catch (ex) { -// err(ex); -// } -// return records; -// }; -// if (engine.name.includes('fork')) { -// await Network.req(`${engine.url}/scripts/by-site/${this.host}.json?language=all`) -// .then(forkFN) -// .catch(err); -// } else if (/github/gi.test(engine.name)) { -// if (isEmpty(engine.token)) { -// err(`"${engine.name}" requires a token to use`); -// return; -// } -// await Network.req( -// `${engine.url}"// ==UserScript=="+${this.host}+ "// ==/UserScript=="+in:file+language:js&per_page=30`, -// 'GET', -// 'json', -// { -// headers: { -// Accept: 'application/vnd.github+json', -// Authorization: `Bearer ${engine.token}`, -// 'X-GitHub-Api-Version': '2022-11-28' -// } -// } -// ) -// .then(gitFN) -// .catch(err); -// } -// return this.data; -// } -// } -// const eng = new Engine(); - -// const cac = new Map(); - -const webFetcher = async (loc, tabId = -2, link) => { - if (isBlank(loc)) { - return []; +const setCount = (tab, tabId) => { + let url; + try { + url = new URL(tab.url); + } catch (ex) { + err(ex); } - const host = formatURL(loc); - - let isBlacklisted = false; - for (const b of cfg.blacklist.filter((b) => b.enabled)) { - if (b.regex === true) { - const reg = new RegExp(b.url, b.flags); - if (!reg.test(host)) continue; - isBlacklisted = true; - } - if (Array.isArray(b.url)) { - for (const c of b.url) { - if (!host.includes(c)) continue; - isBlacklisted = true; - } - } - if (!host.includes(b.url)) continue; - isBlacklisted = true; - } - - // log('Blacklisted:', isBlacklisted, host); - if (isBlacklisted) { - return []; - } - - if (!MUJS.cache.has(host)) { - const engineTemplate = {}; - for (const engine of cfg.engines) { - engineTemplate[engine.name] = []; - } - MUJS.cache.set(host, engineTemplate); - } - - const isSupported = (name) => { - for (const [k, v] of Object.entries(unsupported)) { - if (k !== name) { - continue; - } - if (v.includes(host)) { - return false; - } - } - return true; - }; - const engines = cfg.engines.filter((e) => e.enabled && isSupported(e.name)); - if (isEmpty(engines)) { - return []; - } - const cache = MUJS.cache.get(host); - const sites = []; - - info('Building list', { cfg, cache, engines, allCache: MUJS.cache, sites }); - - - // for (const engine of engines) { - // if (!isEmpty(cache[`${engine.name}`])) { - // sites.push({ - // engine, - // data: normalizeTarget(cache[`${engine.name}`]), - // host, - // link - // // tab - // }); - // continue; - // } - // eng.setHost(host).setEngine(engine); - // await eng.getData(); - // } - // log(eng.data); - - for (const engine of engines) { - if (!isEmpty(cache[`${engine.name}`])) { - sites.push({ - engine, - data: normalizeTarget(cache[`${engine.name}`]), - host, - link - // tab + if (url !== undefined) { + if (!Object.is(url.origin, 'null')) { + MUList.host = formatURL(url.host); + MUList.tabId = tabId ?? tab.id ?? -2; + webext.action.setBadgeText({ + text: `${Array.from(MUList).length ?? 0}` }); - continue; + return; } - const engineArr = cache[engine.name]; - /** - * @param { import("../typings/types").GSFork } dataQ - */ - const forkFN = async (dataQ) => { - if (!dataQ) { - err('Invalid data received from the server, check internet connection'); - return []; - } - /** - * @type { import("../typings/types").GSForkQuery[] } - */ - const dq = Array.isArray(dataQ) ? dataQ : Array.isArray(dataQ.query) ? dataQ.query : []; - const data = dq.filter((d) => !d.deleted); - if (isBlank(data)) { - return []; - } - const hideData = []; - const inUserLanguage = (d) => { - const dlocal = d.locale.split('-')[0] ?? d.locale; - if (language.cache.includes(dlocal)) { - return true; - } - hideData.push(d); - return false; - }; - const filterLang = data.filter((d) => { - if (cfg.filterlang && !inUserLanguage(d)) { - return false; - } - return true; - }); - - let finalList = filterLang; - const hds = []; - for (const ujs of hideData) { - await reqCode(ujs, true); - if (ujs.translated) { - hds.push(ujs); - } - } - finalList = [...new Set([...hds, ...filterLang])]; - - // for (const ujs of finalList) { - // if (!cac.has(ujs.id)) { - // cac.set(ujs.id, { - // ...ujs, - // mujs: { - // engine, - // host, - // timestamp: Date.now(), - // } - // }) - // } - // } - - engineArr.push(...finalList); - return finalList; - }; - const gitFN = async (data) => { - const records = []; - try { - if (isBlank(data.items)) { - err('Invalid data received from the server, TODO fix this'); - return; - } - for (const r of data.items) { - const ujs = template.merge({ - name: r.name, - description: isEmpty(r.repository.description) - ? i18n$('no_license') - : r.repository.description, - url: r.html_url, - code_url: r.html_url.replace(/\/blob\//g, '/raw/'), - code_updated_at: r.commit || Date.now(), - total_installs: r.score, - users: [ - { - name: r.repository.owner.login, - url: r.repository.owner.html_url - } - ] - }); - if (cfg.codePreview && !ujs.code_data) { - await reqCode(ujs); - } - // createjs(ujs, engine.name); - records.push(ujs); - } - engineArr.push(...records); - } catch (ex) { - err(ex); - } - return records; - }; - if (engine.name.includes('fork')) { - const data = await Network.req(`${engine.url}/scripts/by-site/${host}.json?language=all`) - .then(forkFN) - .catch(err); - sites.push({ - engine, - data, - host, - link - // tab - }); - } else if (/github/gi.test(engine.name)) { - if (isEmpty(engine.token)) { - err(`"${engine.name}" requires a token to use`); - continue; - } - const data = await Network.req( - `${engine.url}"// ==UserScript=="+${host}+ "// ==/UserScript=="+in:file+language:js&per_page=30`, - 'GET', - 'json', - { - headers: { - Accept: 'application/vnd.github+json', - Authorization: `Bearer ${engine.token}`, - 'X-GitHub-Api-Version': '2022-11-28' - } - } - ) - .then(gitFN) - .catch(err); - sites.push({ - engine, - data, - host, - link - // tab - }); - } - } - if (tabId > -1) { - webext.tabs.get(tabId, updateCount); } - // log(cac); - return sites; + webext.action.setBadgeText({ + text: '0' + }); }; let requestId = -2; -// let isRedirect = false; webext.webRequest.onHeadersReceived.addListener( (e) => { if (Object.is(e.type, 'main_frame')) { - // log(e, isRedirect); - if (requestId !== e.requestId) { + if (cfg.autofetch && requestId !== e.requestId) { requestId = e.requestId; const loc = new URL(e.url); - webFetcher(loc.host, e.tabId, e.url); + MUList.host = formatURL(loc.host); + MUList.tabId = e.tabId ?? -2; + MUList.build().then(() => { + setCount(e, e.tabId); + }); } } }, { - urls: [ - // 'http://*/*', - 'https://*/*' - ] + urls: ['https://*/*'] } ); -// webext.webRequest.onBeforeRedirect.addListener( -// (e) => { -// if (Object.is(e.type, 'main_frame')) { -// log(e); -// if (requestId === e.requestId) { -// isRedirect = true; -// requestId = -2; -// } -// // if (requestId !== e.requestId) { -// // log(e); -// // requestId = e.requestId; -// // const loc = new URL(e.url); -// // webFetcher(loc.host, e.tabId); -// // } -// } -// }, -// { -// urls: [ -// // 'http://*/*', -// 'https://*/*' -// ] -// } -// ); /** * [onMessage description] - * @param {*} message - The message itself. This is a JSON-ifiable object. + * @param {object} message - The message itself. This is a JSON-ifiable object. * @param {chrome.runtime.MessageSender} sender * @param {(response: any) => void} sendResponse - A function to call, at most once, to send a response to the message. The function takes a single argument, which may be any JSON-ifiable object. This argument is passed back to the message sender. */ function onMessage(message, sender, sendResponse) { if (sender.url.includes('popup.html')) { - if (message.location) { - webFetcher(message.location).then((data) => sendResponse(data)); - } else { - webext.tabs.query(queryOptions, (tabs) => { - const tab = tabs[0]; - const loc = new URL(getTabUrl(tab)); - webFetcher(loc.host, tab.id, getTabUrl(tab)).then((data) => sendResponse(data)); - }); - } - } - - if (message.name) { - if (sender.url.includes('settings.html')) { - storage.config.setOne(message.name, message.value); - sendResponse({ - name: message.name, - value: message.value + if (message.type === 'getData') { + if (MUList.host !== message.hostname) { + MUList.host = message.hostname ?? BLANK_PAGE; + MUList.tabId = message.currentTab?.id ?? -2; + } + MUList.build().then((data) => { + if (message.init) { + sendResponse({ cfg, data }); + } else { + sendResponse({ data }); + } }); + } else if (message.type === 'save') { + if (message.cfg) { + storage.config.set(message.cfg).then(initCfg); + } else { + const v = isNull(message.value) ? cfg[message.prop] : message.value; + storage.config.setOne(message.prop, v).then(initCfg); + } + } else if (message.type === 'reset') { + storage.config.set(DEFAULT_CONFIG); + } else if (message.type === 'clear') { + const cache = Array.from(container).filter(({ _mujs }) => _mujs.info.host === message.host); + for (const ujs of cache) container.userjsCache.delete(ujs.id); + } else if (message.type === 'engine') { + const engine = cfg.engines.find((engine) => engine.name === message.engine.name); + for (const [k, v] of Object.entries(message.engine)) { + engine[k] = v; + } + storage.config.set(cfg).then(initCfg); + } else if (message.location) { + MUList.host = formatURL(message.location); + MUList.build().then(sendResponse); } else { - sendResponse({ value: cfg[message.name] }); + sendResponse({ cfg }); } } return true; } -webext.runtime.onMessage.addListener(onMessage); +function start() { + runtime.onMessage.addListener(onMessage); -const tc = (tab) => { - const loc = new URL(tab.url); - const host = formatURL(loc.host); - return { - loc, - host, - cache: MUJS.cache.get(host) - }; -}; -const updateCount = (tab) => { - const { cache } = tc(tab); - let cnt = 0; - if (cache) { - for (const v of Object.values(cache)) { - cnt += v.length; - } - } - webext.browserAction.setBadgeText({ - text: `${cnt}` + webext.tabs.onRemoved.addListener((tabId) => { + const cache = Array.from(container.userjsCache.values()).filter(({ _mujs }) => { + return !isEmpty(_mujs) && _mujs.info.tabId === tabId; + }); + for (const ujs of cache) container.userjsCache.delete(ujs.id); }); -}; -// webext.tabs.onCreated.addListener((tab) => { -// updateCount(tab); -// }); + webext.tabs.onCreated.addListener(setCount); -webext.tabs.onActivated.addListener((activeInfo) => { - const { tabId } = activeInfo; - webext.tabs.get(tabId, updateCount); - // if (currentTab.tabId !== tabId) { - // currentTab.tabId = tabId; - // } - // log('onActivated', currentTab); -}); + webext.tabs.onActivated.addListener((activeInfo) => { + const { tabId } = activeInfo; + webext.tabs.get(tabId, setCount); + }); -webext.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { - updateCount(tab); - // if (changeInfo.status === 'complete') { - // if (currentTab.tabId !== tabId) { - // currentTab.tabId = tabId; - // currentTab.url = getTabUrl(tab); - // } - // } -}); + webext.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { + if (tab.status === 'complete') { + setCount(tab, tabId); + } + }); + + webext.action.setBadgeText({ + text: '0' + }); +} + +start(); diff --git a/src/js/config.js b/src/js/config.js deleted file mode 100644 index 65599d1..0000000 --- a/src/js/config.js +++ /dev/null @@ -1,210 +0,0 @@ -'use strict'; - -const storage = webext.storage; -const Config = { - configLocalListeners: [], - configSyncListeners: [], - syncDefaults: { - // cache: true, - codePreview: false, - // autoexpand: false, - filterlang: false, - sleazyredirect: false, - // time: 10000, - blacklist: [ - { - enabled: true, - regex: true, - flags: '', - name: 'Blacklist 1', - url: '(gov|cart|checkout|login|join|signin|signup|sign-up|password|reset|password_reset)' - }, - { - enabled: true, - regex: true, - flags: '', - name: 'Blacklist 2', - url: '(pay|bank|money|localhost|authorize|checkout|bill|wallet|router)' - }, - { - enabled: true, - regex: false, - flags: '', - name: 'Blacklist 3', - url: 'https://home.bluesnap.com' - }, - { - enabled: true, - regex: false, - flags: '', - name: 'Blacklist 4', - url: ['zalo.me', 'skrill.com'] - } - ], - engines: [ - { - enabled: true, - name: 'greasyfork', - url: 'https://greasyfork.org' - }, - { - enabled: false, - name: 'sleazyfork', - url: 'https://sleazyfork.org' - }, - { - enabled: false, - name: 'openuserjs', - url: 'https://openuserjs.org/?q=' - }, - { - enabled: false, - name: 'github', - url: 'https://api.github.com/search/code?q=', - token: '' - } - ], - theme: { - 'even-row': '', - 'odd-row': '', - 'even-err': '', - 'odd-err': '', - 'background-color': '', - 'gf-color': '', - 'sf-color': '', - 'border-b-color': '', - 'gf-btn-color': '', - 'sf-btn-color': '', - 'sf-txt-color': '', - 'txt-color': '', - 'chck-color': '', - 'chck-gf': '', - 'chck-git': '', - 'chck-open': '', - placeholder: '', - 'position-top': '', - 'position-bottom': '', - 'position-left': '', - 'position-right': '', - 'font-family': '' - }, - recommend: { - author: true, - others: true - } - }, - cachedSyncConfig: {}, - /** - * @type { import("../typings/types").config } - */ - cachedLocalStorage: {}, - resetToDefault -}; - -function configProxy() { - storage.onChanged.addListener((changes = storage.StorageChange, areaName) => { - if (areaName === 'sync') { - for (const key in changes) { - Config.cachedSyncConfig[key] = changes[key].newValue; - } - for (const callback of Config.configSyncListeners) { - callback(changes); - } - } else if (areaName === 'local') { - for (const key in changes) { - Config.cachedLocalStorage[key] = changes[key].newValue; - } - for (const callback of Config.configLocalListeners) { - callback(changes); - } - } - }); - - const syncHandler = { - set(prop, value) { - Config.cachedSyncConfig[prop] = value; - storage.sync.set({ - [prop]: value - }); - return true; - }, - get(obj, prop) { - const data = Config.cachedSyncConfig[prop]; - return obj[prop] || data; - }, - deleteProperty(prop) { - storage.sync.remove(prop); - return true; - } - }; - - const localHandler = { - set(prop, value) { - Config.cachedLocalStorage[prop] = value; - storage.local.set({ - [prop]: value - }); - return true; - }, - get(obj, prop) { - const data = Config.cachedLocalStorage[prop]; - return obj[prop] || data; - }, - deleteProperty(prop) { - storage.local.remove(prop); - return true; - } - }; - return { - sync: new Proxy({ handler: syncHandler }, syncHandler), - local: new Proxy({ handler: localHandler }, localHandler) - }; -} - -async function fetchConfig() { - await Promise.all([ - new Promise((resolve) => { - storage.sync.get(null, function (items) { - Config.cachedSyncConfig = items; - resolve(); - }); - }), - new Promise((resolve) => { - storage.local.get(null, function (items) { - Config.cachedLocalStorage = items; - resolve(); - }); - }) - ]); -} - -async function setupConfig() { - await fetchConfig(); - for (const key in Config.syncDefaults) { - if (!Object.hasOwn(Config.cachedSyncConfig, key)) { - Config.cachedSyncConfig[key] = Config.syncDefaults[key]; - } - if (!Object.hasOwn(Config.cachedLocalStorage, key)) { - Config.cachedLocalStorage[key] = Config.syncDefaults[key]; - } - } - const config = configProxy(); - Config.config = config.sync; - Config.local = config.local; -} - -function resetToDefault() { - Config.cachedLocalStorage = Config.syncDefaults; - Config.cachedSyncConfig = Config.syncDefaults; - storage.local.set({ - ...Config.syncDefaults - }); - storage.sync.set({ - ...Config.syncDefaults - }); - return Config; -} - -setupConfig(); - -export default Config; diff --git a/src/js/constants.js b/src/js/constants.js new file mode 100644 index 0000000..a479978 --- /dev/null +++ b/src/js/constants.js @@ -0,0 +1,88 @@ +'use strict'; + +const template = { + id: 0, + bad_ratings: 0, + good_ratings: 0, + ok_ratings: 0, + daily_installs: 0, + total_installs: 0, + name: 'NOT FOUND', + description: 'NOT FOUND', + version: '0.0.0', + url: 'about:blank', + code_url: 'about:blank', + created_at: Date.now(), + code_updated_at: Date.now(), + locale: 'NOT FOUND', + deleted: false, + users: [] +}; +/** Unsupport host for search engines */ +const engineUnsupported = { + greasyfork: ['pornhub.com'], + sleazyfork: ['pornhub.com'], + openuserjs: [], + github: [] +}; +const builtinList = { + local: /localhost|router|gov|(\d+\.){3}\d+/, + finance: + /school|pay|bank|money|cart|checkout|authorize|bill|wallet|venmo|zalo|skrill|bluesnap|coin|crypto|currancy|insurance|finance/, + social: /login|join|signin|signup|sign-up|password|reset|password_reset/, + unsupported: { + host: 'fakku.net', + pathname: '/hentai/.+/read/page/.+' + } +}; +const BLANK_PAGE = 'about:blank'; +/** Lets highlight me :) */ +const authorID = 166061; +/** + * Some UserJS I personally enjoy - `https://greasyfork.org/scripts/{id}` + */ +const goodUserJS = [ + 33005, + 394820, + 438684, + 4870, + 394420, + 25068, + 483444, + 1682, + 22587, + 789, + 28497, + 386908, + 24204, + 404443, + 4336, + 368183, + 393396, + 473830, + 12179, + 423001, + 376510, + 23840, + 40525, + 6456, + 'https://openuserjs.org/install/Patabugen/Always_Remember_Me.user.js', + 'https://openuserjs.org/install/nokeya/Direct_links_out.user.js', + 'https://github.com/jijirae/y2monkey/raw/main/y2monkey.user.js', + 'https://github.com/jijirae/r2monkey/raw/main/r2monkey.user.js', + 'https://github.com/TagoDR/MangaOnlineViewer/raw/master/Manga_OnlineViewer.user.js', + 'https://github.com/jesus2099/konami-command/raw/master/INSTALL-USER-SCRIPT.user.js', + 'https://github.com/TagoDR/MangaOnlineViewer/raw/master/dist/Manga_OnlineViewer_Adult.user.js' +]; +/** Remove UserJS from banned accounts */ +const badUserJS = [478597]; + +export { + authorID, + template, + engineUnsupported, + builtinList, + BLANK_PAGE, + goodUserJS, + badUserJS +} diff --git a/src/js/container.js b/src/js/container.js new file mode 100644 index 0000000..bf89591 --- /dev/null +++ b/src/js/container.js @@ -0,0 +1,140 @@ +'use strict'; + +import { isEmpty, isFN } from './util.js'; +import { BLANK_PAGE, engineUnsupported } from './constants.js'; +import { webext } from './ext.js'; + +const MUJS_ORIGIN = webext.runtime.getURL('').replace(/\/$/, ''); + +/** + * @type { import("../typings/UserJS.d.ts").Container } + */ +export class BaseContainer { + constructor() { + this.toElem = this.toElem.bind(this); + this.cache = new Map(); + this.userjsCache = new Map(); + } + + checkBlacklist() { + return true; + } + + toElem() { + return Array.from(this).map(({ _mujs }) => { + return _mujs.root; + }); + } + + *[Symbol.iterator]() { + const arr = Array.from(this.userjsCache.values()).filter(({ _mujs }) => { + return !isEmpty(_mujs) && _mujs.info.engine.enabled; + }); + for (const userjs of arr) { + yield userjs; + } + } +} + +export class BaseList { + intEngines; + /** + * @type { string | undefined } + */ + intHost; + /** + * @param {string} [hostname] + * @param {BaseContainer} [container] + * @param {import("../typings/types").config} [cfg] + */ + constructor(hostname = undefined, container = new BaseContainer(), cfg = {}) { + this.groupBy = this.groupBy.bind(this); + this.container = container; + this.intEngines = cfg.engines ?? []; + this.host = hostname; + } + + setEngines(engines = []) { + const { host } = this; + return engines.filter((e) => { + if (!e.enabled) { + return false; + } + const v = engineUnsupported[e.name] ?? []; + if (v.includes(host)) { + return false; + } + return true; + }); + } + + set engines(engines) { + this.intEngines = this.setEngines(engines); + } + + get engines() { + return this.intEngines; + } + + set host(hostname) { + if (!MUJS_ORIGIN.includes(hostname)) { + this.intHost = hostname ?? BLANK_PAGE; + } else if (isEmpty(this.intHost)) { + this.intHost = BLANK_PAGE; + } + + if (!this.container.cache.has(hostname)) { + const engineTemplate = {}; + for (const engine of this.engines) { + engineTemplate[engine.name] = []; + } + this.container.cache.set(hostname, engineTemplate); + } + this.blacklisted = this.container.checkBlacklist(hostname); + this.intEngines = this.setEngines(this.engines); + this.domain = this.getDomain(this.intHost); + } + + get host() { + return this.intHost; + } + + /** + * @template { string } S + * @param { S } str + * @returns { S | 'all-sites' } + */ + getDomain(str) { + if (str === '*') { + return 'all-sites'; + } + return str.split('.').at(-2) ?? BLANK_PAGE; + } + + groupBy() { + const arr = Array.from(this); + const callback = ({ _mujs }) => _mujs.info.engine.name; + if (isFN(Object.groupBy)) { + return Object.groupBy(arr, callback); + } + /** [Object.groupBy polyfill](https://gist.github.com/gtrabanco/7c97bd41aa74af974fa935bfb5044b6e) */ + return arr.reduce((acc = {}, ...args) => { + const key = callback(...args); + acc[key] ??= []; + acc[key].push(args[0]); + return acc; + }, {}); + } + + *[Symbol.iterator]() { + const { intHost, engines } = this; + const arr = Array.from(this.container).filter( + ({ _mujs }) => + _mujs.info.host === intHost && + engines.find((engine) => engine.enabled && engine.name === _mujs.info.engine.name) + ); + for (const userjs of arr) { + yield userjs; + } + } +} diff --git a/src/js/ext.js b/src/js/ext.js new file mode 100644 index 0000000..ebd6ec7 --- /dev/null +++ b/src/js/ext.js @@ -0,0 +1,34 @@ +export const webext = + self.browser instanceof Object && self.browser instanceof Element === false + ? self.browser + : self.chrome; +export const runtime = webext.runtime; + +/******************************************************************************/ + +// The extension's service worker can be evicted at any time, so when we +// send a message, we try a few more times when the message fails to be sent. + +export function sendMessage(msg) { + return new Promise((resolve, reject) => { + let i = 5; + const send = () => { + runtime + .sendMessage(msg) + .then((response) => { + resolve(response); + }) + .catch((reason) => { + i -= 1; + if (i <= 0) { + reject(reason); + } else { + setTimeout(send, 200); + } + }); + }; + send(); + }); +} + +/******************************************************************************/ diff --git a/src/js/i18n.js b/src/js/i18n.js index 959a1f3..c8a39a0 100644 --- a/src/js/i18n.js +++ b/src/js/i18n.js @@ -1,17 +1,37 @@ 'use strict'; -/******************************************************************************/ - const i18n = self.browser instanceof Object && self.browser instanceof Element === false ? self.browser.i18n : self.chrome.i18n; -const i18n$ = (...args) => i18n.getMessage(...args); +class i18nHandler { + /** + * @param {string | Date | number} str + */ + static toDate(str = '') { + return new Intl.DateTimeFormat(navigator.language).format( + typeof str === 'string' ? new Date(str) : str + ); + } + /** + * @param {number | bigint} number + */ + static toNumber(number) { + return new Intl.NumberFormat(navigator.language).format(number); + } + /** + * @param {...string} args + */ + static i18n$(...args) { + return i18n.getMessage(...args); + } +} -/******************************************************************************/ +const { i18n$, toDate, toNumber } = i18nHandler; +const language = { i18n$, toDate, toNumber }; -const isBackgroundProcess = document.title === 'Magic UserJS+ Background Page'; +const isBackgroundProcess = document && document.title === 'Magic UserJS+ Background Page'; if (isBackgroundProcess !== true) { // Helper to deal with the i18n'ing of HTML files. @@ -36,4 +56,4 @@ if (isBackgroundProcess !== true) { i18n.render(); } -export { i18n, i18n$ }; +export { i18n, i18n$, language }; diff --git a/src/js/language.js b/src/js/language.js deleted file mode 100644 index f1f817e..0000000 --- a/src/js/language.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -class LanguageHandler { - constructor() { - this.current = (navigator.language ?? 'en').split('-')[0] ?? 'en'; - this.cache = []; - - const languages = navigator.languages ?? []; - for (const nlang of languages) { - const lg = nlang.split('-')[0]; - if (this.cache.indexOf(lg) === -1) { - this.cache.push(lg); - } - } - - if (!this.cache.includes(this.current)) { - this.cache.push(this.current); - } - } -} - -const language = new LanguageHandler(); - -export { - language -}; \ No newline at end of file diff --git a/src/js/logger.js b/src/js/logger.js index 40c1863..cbf0e2f 100644 --- a/src/js/logger.js +++ b/src/js/logger.js @@ -20,11 +20,6 @@ const err = (...msg) => { 'color: rgb(249, 24, 128);', ...msg ); - for (const ex of msg) { - if (typeof ex === 'object' && 'cause' in ex && typeof alert !== 'undefined') { - alert(`[Magic Userscript+] (${ex.cause}) ${ex.message}`); - } - } }; const info = (...msg) => { console.info( diff --git a/src/js/messager.js b/src/js/messager.js new file mode 100644 index 0000000..934ab67 --- /dev/null +++ b/src/js/messager.js @@ -0,0 +1,124 @@ +'use strict'; + +import { webext } from './ext.js'; + +export const hermes = { + port: null, + portTimer: null, + portTimerDelay: 10000, + msgIdGenerator: 1, + pending: new Map(), + waitStartTime: 0, + shuttingDown: false, + shutdown: function () { + this.shuttingDown = true; + this.destroyPort(); + }, + disconnectListener: function () { + void webext.runtime.lastError; + this.port = null; + this.destroyPort(); + }, + disconnectListenerBound: null, + messageListener: function (details) { + if (typeof details !== 'object' || details === null) { + return; + } + // Response to specific message previously sent + if (details.msgId !== undefined) { + const resolver = this.pending.get(details.msgId); + if (resolver !== undefined) { + this.pending.delete(details.msgId); + resolver(details.msg); + return; + } + } + }, + messageListenerBound: null, + canDestroyPort: function () { + return this.pending.size === 0; + }, + portPoller: function () { + this.portTimer = null; + if (this.port !== null && this.canDestroyPort()) { + return this.destroyPort(); + } + this.portTimer = setTimeout(this.portPollerBound, this.portTimerDelay); + this.portTimerDelay = Math.min(this.portTimerDelay * 2, 60 * 60 * 1000); + }, + portPollerBound: null, + destroyPort: function () { + if (this.portTimer !== null) { + clearTimeout(this.portTimer); + this.portTimer = null; + } + const port = this.port; + if (port !== null) { + port.disconnect(); + port.onMessage.removeListener(this.messageListenerBound); + port.onDisconnect.removeListener(this.disconnectListenerBound); + this.port = null; + } + // service pending callbacks + if (this.pending.size !== 0) { + const pending = this.pending; + this.pending = new Map(); + for (const resolver of pending.values()) { + resolver(); + } + } + }, + createPort: function () { + if (this.shuttingDown) { + return null; + } + if (this.messageListenerBound === null) { + this.messageListenerBound = this.messageListener.bind(this); + this.disconnectListenerBound = this.disconnectListener.bind(this); + this.portPollerBound = this.portPoller.bind(this); + } + try { + this.port = webext.runtime.connect({ name: 'hermes' }) || null; + // eslint-disable-next-line no-unused-vars + } catch (ex) { + this.port = null; + } + if (this.port === null) { + return null; + } + this.port.onMessage.addListener(this.messageListenerBound); + this.port.onDisconnect.addListener(this.disconnectListenerBound); + this.portTimerDelay = 10000; + if (this.portTimer === null) { + this.portTimer = setTimeout(this.portPollerBound, this.portTimerDelay); + } + return this.port; + }, + getPort: function () { + return this.port ?? this.createPort(); + }, + send: function (channel, msg) { + // Too large a gap between the last request and the last response means + // the main process is no longer reachable: memory leaks and bad + // performance become a risk -- especially for long-lived, dynamic + // pages. Guard against this. + if (this.pending.size > 64) { + if (Date.now() - this.waitStartTime > 60000) { + this.shutdown(); + } + } + const port = this.getPort(); + if (port === null) { + return Promise.resolve(); + } + if (this.pending.size === 0) { + this.waitStartTime = Date.now(); + } + const msgId = this.msgIdGenerator++; + const promise = new Promise((resolve) => { + this.pending.set(msgId, resolve); + }); + port.postMessage({ channel, msgId, msg }); + return promise; + } +}; diff --git a/src/js/mu-common.js b/src/js/mu-common.js deleted file mode 100644 index a7d6863..0000000 --- a/src/js/mu-common.js +++ /dev/null @@ -1,385 +0,0 @@ -'use strict'; - -/** - * @typedef { object } userjs - */ - -// if (typeof userjs === 'undefined') { -// let userjs = (self.userjs = {}); -// } - -if (typeof userjs === 'object' && userjs.isNull instanceof Function === false) { - const win = self ?? window; - const doc = win.document; - const isMobile = /Mobile|Tablet/.test(navigator.userAgent); - /** - * Object is typeof `Element` - * @template O - * @param { O } obj - * @returns { boolean } - */ - const isElem = (obj) => { - /** @type { string } */ - const s = Object.prototype.toString.call(obj); - return s.includes('Element'); - }; - /** - * Object is typeof `Function` - * @template O - * @param { O } obj - * @returns { boolean } - */ - const isFN = (obj) => { - /** @type { string } */ - const s = Object.prototype.toString.call(obj); - return s.includes('Function'); - }; - /** - * Object is typeof `object` / JSON Object - * @template O - * @param { O } obj - * @returns { boolean } - */ - const isObj = (obj) => { - /** @type { string } */ - const s = Object.prototype.toString.call(obj); - return s.includes('Object'); - }; - /** - * Object is `null` or `undefined` - * @template O - * @param { O } obj - * @returns { boolean } - */ - const isNull = (obj) => { - return Object.is(obj, null) || Object.is(obj, undefined); - }; - /** - * Object is Blank - * @template O - * @param { O } obj - * @returns { boolean } - */ - const isBlank = (obj) => { - return ( - (typeof obj === 'string' && Object.is(obj.trim(), '')) || - ((obj instanceof Set || obj instanceof Map) && Object.is(obj.size, 0)) || - (Array.isArray(obj) && Object.is(obj.length, 0)) || - (isObj(obj) && Object.is(Object.keys(obj).length, 0)) - ); - }; - /** - * Object is Empty - * @template O - * @param { O } obj - * @returns { boolean } - */ - const isEmpty = (obj) => { - return isNull(obj) || isBlank(obj); - }; - /** - * @template T - * @param { T } target - * @param { boolean } toQuery - * @param { Element | Document | undefined } root - * @returns { T[] } - */ - const normalizeTarget = (target, toQuery = true, root) => { - if (Object.is(target, null) || Object.is(target, undefined)) { - return []; - } - if (Array.isArray(target)) { - return target; - } - if (typeof target === 'string') { - return toQuery ? Array.from((root || document).querySelectorAll(target)) : [target]; - } - if (isElem(target)) { - return [target]; - } - return Array.from(target); - }; - /** - * Add Event Listener - * @template { HTMLElement } E - * @template { keyof HTMLElementEventMap } K - * @param { E } el - * @param { K } event - * @param { (this: E, ev: HTMLElementEventMap[K]) => any } callback - * @param { boolean | AddEventListenerOptions } options - */ - const ael = (el, event, callback, options = {}) => { - try { - for (const elem of normalizeTarget(el)) { - if (!elem) { - continue; - } - if (isMobile && event === 'click') { - elem.addEventListener('touchstart', callback); - return; - } - if (event === 'fclick') { - event = 'click'; - } - elem.addEventListener(event, callback, options); - } - } catch (ex) { - console.error(ex); - } - }; - /** - * Form Attributes of Element - * @template { keyof HTMLElementTagNameMap } K - * @param { K } elem - * @param { keyof HTMLElement } attr - */ - const formAttrs = (elem, attr = {}) => { - for (const key in attr) { - if (typeof attr[key] === 'object') { - formAttrs(elem[key], attr[key]); - } else if (isFN(attr[key])) { - if (key === 'container') { - key(); - continue; - } - if (/^on/.test(key)) { - elem[key] = attr[key]; - continue; - } - ael(elem, key, attr[key]); - } else if (key === 'class') { - elem.className = attr[key]; - } else { - elem[key] = attr[key]; - } - } - }; - /** - * Make Element - * @template { keyof HTMLElementTagNameMap } K - * @param { K } tagName - * @param { string } cname - * @param { keyof HTMLElement } attrs - * @returns { HTMLElementTagNameMap[K] } - */ - const make = (tagName, cname, attrs = {}) => { - let el = null; - try { - el = document.createElement(tagName); - if (typeof cname === 'string' && !isEmpty(cname)) { - el.className = cname; - } - if (!isEmpty(attrs)) { - formAttrs(el, attrs); - } - } catch (ex) { - console.error(ex); - } - return el; - }; - /** - * setTimeout w/ Promise - * @param { number } timeout - Timeout in milliseconds (ms) - * @returns { Promise } Promise object - */ - const delay = (timeout = 5000) => new Promise((resolve) => setTimeout(resolve, timeout)); - const Network = { - /** - * Fetch a URL with fetch API as fallback - * - * When GM is supported, makes a request like XMLHttpRequest, with some special capabilities, not restricted by same-origin policy - * @link https://violentmonkey.github.io/api/gm/#gm_xmlhttprequest - * @link https://developer.mozilla.org/docs/Web/API/Fetch_API - * @param { RequestInfo | URL } url - The URL to fetch - * @param { Request['method'] } method - Fetch method - * @param { 'buffer' | 'json' | 'text' | 'blob' | 'document' } responseType - Response type - * @param { RequestInit } data - Fetch parameters - * @returns { Promise } Fetch results - */ - async req(url, method = 'GET', responseType = 'json', data = {}) { - if (isEmpty(url)) { - throw new Error('"url" parameter is empty'); - } - method = Network.bscStr(method, false); - responseType = Network.bscStr(responseType); - const params = { - method, - ...data - }; - return await new Promise((resolve, reject) => { - /** - * @param { Response } response - * @returns { Response | Document } - */ - const fetchResp = (response_1) => { - if (!response_1.ok) reject(response_1); - const check = (str_2 = 'text') => { - return isFN(response_1[str_2]) ? response_1[str_2]() : response_1; - }; - if (responseType.match(/buffer/i)) { - resolve(check('arrayBuffer')); - } else if (responseType.match(/json/i)) { - resolve(check('json')); - } else if (responseType.match(/text/i)) { - resolve(check('text')); - } else if (responseType.match(/blob/i)) { - resolve(check('blob')); - } else if (responseType.match(/formdata/i)) { - resolve(check('formData')); - } else if (responseType.match(/clone/i)) { - resolve(check('clone')); - } else if (responseType.match(/document/i) && isFN(response_1.text)) { - const domParser = new DOMParser(); - const respTxt = response_1.text(); - if (respTxt instanceof Promise) { - respTxt.then((txt) => { - const doc = domParser.parseFromString(txt, 'text/html'); - resolve(doc); - }); - } else { - const doc = domParser.parseFromString(respTxt, 'text/html'); - resolve(doc); - } - } else { - resolve(response_1); - } - }; - fetch(url, params).then(fetchResp).catch(reject); - }); - }, - format(bytes, decimals = 2) { - if (Number.isNaN(bytes)) return '0 Bytes'; - const k = 1024; - const dm = decimals < 0 ? 0 : decimals; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${Network.sizes[i]}`; - }, - sizes: ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], - /** - * @template { string } S - * @param { S } str - * @param { boolean } lowerCase - * @returns { S } - */ - bscStr(str = '', lowerCase = true) { - const txt = str[lowerCase ? 'toLowerCase' : 'toUpperCase'](); - return txt.replaceAll(/\W/g, ''); - } - }; - // const i18n$ = (...args) => { - // const arr = []; - // for (const arg of args) { - // arr.push(webext.i18n.getMessage(arg)); - // } - // return arr.length !== 1 ? arr : arr[0]; - // }; - - class Timeout { - constructor() { - this.ids = []; - } - - set(delay, reason) { - return new Promise((resolve, reject) => { - const id = setTimeout(() => { - isNull(reason) ? resolve() : reject(reason); - this.clear(id); - }, delay); - this.ids.push(id); - }); - } - - clear(...ids) { - this.ids = this.ids.filter((id) => { - if (ids.includes(id)) { - clearTimeout(id); - return false; - } - return true; - }); - } - } - class MUError extends Error { - /** - * @param {string} fnName - (Optional) Function name - * @param {...string} params - Extra error parameters - */ - constructor(fnName = 'muError', ...params) { - super(...params); - if (Error.captureStackTrace) { - Error.captureStackTrace(this, MUError); - } else { - this.stack = new Error().stack; - } - this.fn = `[${fnName}]`; - this.name = this.constructor.name; - } - } - - userjs.error = MUError; - userjs.isMobile = isMobile; - userjs.Timeout = Timeout; - userjs.isElem = isElem; - userjs.isFN = isFN; - userjs.isObj = isObj; - userjs.isNull = isNull; - userjs.isBlank = isBlank; - userjs.isEmpty = isEmpty; - userjs.ael = ael; - userjs.formAttrs = formAttrs; - userjs.make = make; - userjs.delay = delay; - userjs.normalizeTarget = normalizeTarget; - - Object.assign(userjs, { - makeImage(imgSrc = '', attrs = {}, cname) { - const img = new Image(); - img.alt = ''; - img.referrerPolicy = 'no-referrer'; - img.src = imgSrc; - if (!isEmpty(cname)) { - img.className = cname; - } - if (!isEmpty(attrs)) { - for (const key in attrs) { - img[key] = attrs[key]; - } - } - return img; - }, - halt(e) { - e.preventDefault(); - e.stopPropagation(); - }, - injScript(text, remove = true) { - const inj = make('script', 'mu-injected', { - type: 'text/javascript', - innerHTML: text - }); - (doc.head || doc.documentElement || doc).appendChild(inj); - if (!remove) { - return inj; - } - delay(1000).then(() => inj.remove()); - }, - /** - * @param {Node} element - * @param {MutationCallback} callback - * @param {MutationObserverInit} options - */ - observe(element, callback, options = { subtree: true, childList: true }) { - const observer = new MutationObserver(callback); - callback([], observer); - observer.observe(element, options); - return observer; - }, - /** - * @param {string} url - URL of webpage to open - * @param {object} params - GM parameters - */ - openInTab(url, params = {}) { - params = Object.is(params, {}) ? '_blank' : params; - return win.open(url, params); - } - }); -} diff --git a/src/js/mu-hermes.js b/src/js/mu-hermes.js deleted file mode 100644 index 9772b05..0000000 --- a/src/js/mu-hermes.js +++ /dev/null @@ -1,126 +0,0 @@ -'use strict'; - -if (typeof userjs === 'object') { - userjs.hermes = { - port: null, - portTimer: null, - portTimerDelay: 10000, - msgIdGenerator: 1, - pending: new Map(), - waitStartTime: 0, - shuttingDown: false, - shutdown: function () { - this.shuttingDown = true; - this.destroyPort(); - }, - disconnectListener: function () { - void webext.runtime.lastError; - this.port = null; - this.destroyPort(); - }, - disconnectListenerBound: null, - messageListener: function (details) { - if (typeof details !== 'object' || details === null) { - return; - } - // Response to specific message previously sent - if (details.msgId !== undefined) { - const resolver = this.pending.get(details.msgId); - if (resolver !== undefined) { - this.pending.delete(details.msgId); - resolver(details.msg); - return; - } - } - }, - messageListenerBound: null, - canDestroyPort: function () { - return this.pending.size === 0; - }, - portPoller: function () { - this.portTimer = null; - if (this.port !== null && this.canDestroyPort()) { - return this.destroyPort(); - } - this.portTimer = setTimeout(this.portPollerBound, this.portTimerDelay); - this.portTimerDelay = Math.min(this.portTimerDelay * 2, 60 * 60 * 1000); - }, - portPollerBound: null, - destroyPort: function () { - if (this.portTimer !== null) { - clearTimeout(this.portTimer); - this.portTimer = null; - } - const port = this.port; - if (port !== null) { - port.disconnect(); - port.onMessage.removeListener(this.messageListenerBound); - port.onDisconnect.removeListener(this.disconnectListenerBound); - this.port = null; - } - // service pending callbacks - if (this.pending.size !== 0) { - const pending = this.pending; - this.pending = new Map(); - for (const resolver of pending.values()) { - resolver(); - } - } - }, - createPort: function () { - if (this.shuttingDown) { - return null; - } - if (this.messageListenerBound === null) { - this.messageListenerBound = this.messageListener.bind(this); - this.disconnectListenerBound = this.disconnectListener.bind(this); - this.portPollerBound = this.portPoller.bind(this); - } - try { - this.port = webext.runtime.connect({ name: 'hermes' }) || null; - // eslint-disable-next-line no-unused-vars - } catch (ex) { - this.port = null; - } - if (this.port === null) { - return null; - } - this.port.onMessage.addListener(this.messageListenerBound); - this.port.onDisconnect.addListener(this.disconnectListenerBound); - this.portTimerDelay = 10000; - if (this.portTimer === null) { - this.portTimer = setTimeout(this.portPollerBound, this.portTimerDelay); - } - return this.port; - }, - getPort: function () { - return this.port !== null ? this.port : this.createPort(); - }, - send: function (channel, msg) { - // Too large a gap between the last request and the last response means - // the main process is no longer reachable: memory leaks and bad - // performance become a risk -- especially for long-lived, dynamic - // pages. Guard against this. - if (this.pending.size > 64) { - if (Date.now() - this.waitStartTime > 60000) { - this.shutdown(); - } - } - const port = this.getPort(); - if (port === null) { - return Promise.resolve(); - } - if (this.pending.size === 0) { - this.waitStartTime = Date.now(); - } - const msgId = this.msgIdGenerator++; - const promise = new Promise((resolve) => { - this.pending.set(msgId, resolve); - }); - port.postMessage({ channel, msgId, msg }); - return promise; - } - }; -} - -void 0; \ No newline at end of file diff --git a/src/js/network.js b/src/js/network.js index 50fc613..7a8440d 100644 --- a/src/js/network.js +++ b/src/js/network.js @@ -9,59 +9,57 @@ const Network = { if (isEmpty(url)) { throw new Error('"url" parameter is empty'); } - method = Network.bscStr(method, false); - responseType = Network.bscStr(responseType); + data = Object.assign({}, data); + method = this.bscStr(method, false); + responseType = this.bscStr(responseType); const params = { method, ...data }; - return await new Promise((resolve, reject) => { - /** - * @param { Response } response - * @returns { Response | Document } - */ - const fetchResp = (response_1) => { - if (!response_1.ok) reject(response_1); - const check = (str_2 = 'text') => { - return isFN(response_1[str_2]) ? response_1[str_2]() : response_1; - }; - if (responseType.match(/buffer/i)) { - resolve(check('arrayBuffer')); - } else if (responseType.match(/json/i)) { - resolve(check('json')); - } else if (responseType.match(/text/i)) { - resolve(check('text')); - } else if (responseType.match(/blob/i)) { - resolve(check('blob')); - } else if (responseType.match(/formdata/i)) { - resolve(check('formData')); - } else if (responseType.match(/clone/i)) { - resolve(check('clone')); - } else if (responseType.match(/document/i) && isFN(response_1.text)) { - const domParser = new DOMParser(); - const respTxt = response_1.text(); - if (respTxt instanceof Promise) { - respTxt.then((txt) => { - const doc = domParser.parseFromString(txt, 'text/html'); + return new Promise((resolve, reject) => { + fetch(url, params) + .then((response_1) => { + if (!response_1.ok) reject(response_1); + const check = (str_2 = 'text') => { + return isFN(response_1[str_2]) ? response_1[str_2]() : response_1; + }; + if (responseType.match(/buffer/)) { + resolve(check('arrayBuffer')); + } else if (responseType.match(/json/)) { + resolve(check('json')); + } else if (responseType.match(/text/)) { + resolve(check('text')); + } else if (responseType.match(/blob/)) { + resolve(check('blob')); + } else if (responseType.match(/formdata/)) { + resolve(check('formData')); + } else if (responseType.match(/clone/)) { + resolve(check('clone')); + } else if (responseType.match(/document/)) { + const respTxt = check('text'); + const domParser = new DOMParser(); + if (respTxt instanceof Promise) { + respTxt.then((txt) => { + const doc = domParser.parseFromString(txt, 'text/html'); + resolve(doc); + }); + } else { + const doc = domParser.parseFromString(respTxt, 'text/html'); resolve(doc); - }); + } } else { - const doc = domParser.parseFromString(respTxt, 'text/html'); - resolve(doc); + resolve(response_1); } - } else { - resolve(response_1); - } - }; - fetch(url, params).then(fetchResp).catch(reject); + }) + .catch(reject); }); }, format(bytes, decimals = 2) { - if (Number.isNaN(bytes)) return '0 Bytes'; + if (Number.isNaN(bytes)) return `0 ${this.sizes[0]}`; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const i = Math.floor(Math.log(bytes) / Math.log(k)); - return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${Network.sizes[i]}`; + return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${this.sizes[i]}`; }, sizes: ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], bscStr(str = '', lowerCase = true) { @@ -70,4 +68,4 @@ const Network = { } }; -export default Network; \ No newline at end of file +export default Network; diff --git a/src/js/popup.js b/src/js/popup.js index cde4b29..4f6febe 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -1,74 +1,90 @@ 'use strict'; + +import { webext, runtime, sendMessage } from './ext.js'; import { err, log, info } from './logger.js'; -import { i18n$ } from './i18n.js'; -import { dom, normalizeTarget, qs, qsA } from './querySelector.js'; -import { reqCode, parse_meta } from './request-code.js'; -import { ael, make, isBlank, isNull, isEmpty, isObj, isString, hasOwn, openInTab } from './util.js'; -import { XMap } from './XMap.js'; +import { i18n$, language } from './i18n.js'; +import { dom, qs, qsA } from './querySelector.js'; +import { ParseUserJS } from './request-code.js'; +import Network from './network.js'; +import { + ael, + make, + isNull, + isEmpty, + isElem, + isObj, + openInTab, + strToURL, + isRegExp, + loadFilters, + formatURL, + normalizedHostname +} from './util.js'; +import { builtinList, template, BLANK_PAGE, badUserJS, authorID, goodUserJS } from './constants.js'; +import { DEFAULT_CONFIG } from './storage.js'; +import { BaseContainer, BaseList } from './container.js'; +/******************************************************************************/ /** * @type { import("../typings/types").config } */ let cfg = {}; +/******************************************************************************/ + +/** + * @type { chrome.tabs.Tab | browser.tabs.Tab } + */ +const currentTab = {}; +const tabURL = new URL(runtime.getURL('/')); -const { hermes } = userjs; +/******************************************************************************/ -// Lets highlight me :) -const authorID = 166061; -// Some UserJS I personally enjoy -const goodUserJS = [ - 423001, - 376510, - 23840, - 40525, - 6456, - 'https://github.com/TagoDR/MangaOnlineViewer/raw/master/Manga_OnlineViewer.user.js', - 'https://github.com/jesus2099/konami-command/raw/master/INSTALL-USER-SCRIPT.user.js', - 'https://github.com/TagoDR/MangaOnlineViewer/raw/master/dist/Manga_OnlineViewer_Adult.user.js' -]; +const params = new URLSearchParams(location.search); +//#region Icon SVGs const iconSVG = { - install: { - viewBox: '0 0 16 16', - html: '' - }, - search: { - viewBox: '0 0 24 24', - html: '' - }, + close: 'fa-solid fa-xmark', + code: 'fa-solid fa-code', + collapse: 'fa-solid fa-compress', + download: 'fa-solid fa-file-arrow-down', + expand: 'fa-solid fa-arrow-up-right-from-square', // fa-expand + gear: 'fa-solid fa-gear', + github: 'fa-brands fa-github', + globe: 'fa-solid fa-globe', + install: 'fa-solid fa-download', + issue: 'fa-solid fa-bug', + minus: 'fa-solid fa-minus', + nav: 'fa-solid fa-bars', + pager: 'fa-solid fa-pager', verified: { viewBox: '0 0 56 56', fill: 'currentColor', stroke: 'currentColor', html: '' }, - refresh: { - viewBox: '0 0 1024 1024', - fill: 'currentColor', - html: '' - }, - // get(type, container) { - // return new Promise((resolve) => { - // const img = new Image(); - // img.src = webext.runtime.getURL(`/img/${type}.svg`); - // img.onload = () => { - // if (container) { - // container.appendChild(img); - // } - // resolve(img); - // } - // }) - // }, + refresh: 'fa-solid fa-rotate-right', load(type, container) { - const xmlns = 'http://www.w3.org/2000/svg'; - const svgElem = document.createElementNS(xmlns, 'svg'); - for (const [k, v] of Object.entries(iconSVG[type])) { - if (k === 'html') { - continue; + let svgElem; + if (typeof iconSVG[type] === 'string') { + svgElem = make('i', iconSVG[type], { + id: `mujs_${type ?? 'Unknown'}` + }); + } else { + svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + for (const [k, v] of Object.entries(iconSVG[type])) { + if (k === 'html') { + continue; + } + svgElem.setAttributeNS(null, k, v); + } + try { + if (typeof iconSVG[type].html === 'string') { + svgElem.innerHTML = iconSVG[type].html; + dom.attr(svgElem, 'id', `mujs_${type ?? 'Unknown'}`); + } + // eslint-disable-next-line no-unused-vars + } catch (ex) { + /* empty */ } - svgElem.setAttributeNS(null, k, v); - } - if (isString(iconSVG[type].html)) { - svgElem.innerHTML = iconSVG[type].html; } if (container) { container.appendChild(svgElem); @@ -77,78 +93,8 @@ const iconSVG = { return svgElem.outerHTML; } }; - -const main = qs('mujs-main'); -const cfgpage = qs('form'); -const footer = qs('.mujs-footer'); -const rateContainer = qs('.rate-container'); - -const table = qs('table'); -const tabbody = qs('tbody'); -const tabhead = qs('thead'); - -const urlBar = qs('.mujs-url-bar'); -const btncfg = qs('mujs-btn.settings'); -const btngreasy = qs('mujs-btn.greasy'); -const btnissue = qs('mujs-btn.issue'); -const btnhome = qs('mujs-btn.github'); - -const toolbar = qs('mujs-toolbar'); - -const promptElem = make('mujs-row', 'mujs-prompt'); -main.append(promptElem); - -const makeTHead = (rows) => { - const tr = make('tr'); - for (const r of normalizeTarget(rows)) { - const tparent = make('th', r.class ?? '', r); - tr.append(tparent); - } - tabhead.append(tr); - table.append(tabhead, tabbody); -}; -makeTHead([ - { - class: 'mujs-header-name', - textContent: i18n$('name') - }, - { - textContent: i18n$('createdby') - }, - { - textContent: i18n$('daily_installs') - }, - { - textContent: i18n$('updated') - }, - { - textContent: i18n$('install') - } -]); - -const renderTheme = (theme) => { - theme = theme || cfg.theme; - if (isEmpty(theme)) { - return; - } - // if (theme === defcfg.theme) { - // return; - // } - const sty = qs('mujs-root').style; - for (const [k, v] of Object.entries(theme)) { - const str = `--mujs-${k}`; - const prop = sty.getPropertyValue(str); - if (isEmpty(v)) { - theme[k] = prop; - } - if (prop === v) { - continue; - } - sty.removeProperty(str); - sty.setProperty(str, v); - } -}; -const cfgMap = new XMap(); +//#endregion +const cfgMap = new Map(); const rebuildCfg = () => { for (const engine of cfg.engines) { if (cfgMap.has(engine.name)) { @@ -172,88 +118,452 @@ const rebuildCfg = () => { } } } - dom.prop(cfgMap.get('blacklist'), 'value', JSON.stringify(cfg.blacklist, null, ' ')); - dom.prop(cfgMap.get('theme'), 'value', JSON.stringify(cfg.theme, null, ' ')); - renderTheme(cfg.theme); + // dom.prop(cfgMap.get('blacklist'), 'value', JSON.stringify(cfg.blacklist, null, ' ')); + for (const [k, v] of Object.entries(cfg.theme)) { + dom.prop(cfgMap.get(k), 'value', v); + } + container.renderTheme(cfg.theme); }; -const getHost = (str = '') => { - return str.split('.').splice(-2).join('.'); +const Counter = { + cnt: { + total: { + count: 0 + } + }, + set(engine) { + if (!this.cnt[engine.name]) { + const counter = make('count-frame', engine.enabled ? '' : 'hidden', { + dataset: { + counter: engine.name + }, + title: engine.query ? decodeURIComponent(engine.query) : engine.url, + textContent: '0' + }); + this.cnt[engine.name] = { + root: counter, + count: 0 + }; + return counter; + } + return this.cnt[engine.name].root; + }, + update(count, engine) { + this.cnt[engine.name].count += count; + this.cnt.total.count += count; + this.updateAll(); + }, + updateAll() { + for (const v of Object.values(this.cnt)) dom.text(v.root, v.count); + webext.action.setBadgeText({ + text: `${this.cnt.total.count}` + }); + document.title = `Magic UserJS+ (${this.cnt.total.count})`; + }, + reset() { + for (const [k, v] of Object.entries(this.cnt)) { + dom.text(v.root, 0); + v.count = 0; + const engine = cfg.engines.find((engine) => k === engine.name); + if (engine) { + dom.cl[engine.enabled ? 'remove' : 'add'](v.root, 'hidden'); + } + } + webext.action.setBadgeText({ + text: '0' + }); + document.title = 'Magic UserJS+ (0)'; + } }; -const ContainerHandler = class { - constructor() { +// #region Container +class Container extends BaseContainer { + constructor(url) { + super(url); + this.refresh = this.refresh.bind(this); this.showError = this.showError.bind(this); - // this.cleanup = this.cleanup.bind(this); - this.setHost(window.top.document.location.href); - this.cache = new XMap(); - this.userjsCache = new XMap(); + + this.webpage = strToURL(url); + this.host = this.getHost(this.webpage.host); + this.domain = this.getDomain(this.webpage.host); + this.unsaved = false; this.isBlacklisted = false; this.rebuild = false; - this.counters = { - total: 0 - }; - } + this.root = qs('mujs-root'); - setHost(link) { + this.init(); + } + // #region init + init() { try { - this.webpage = new URL(link); + this.mainbtn = make('count-frame', 'mainbtn', { + textContent: '0' + }); + this.promptElem = make('mujs-row', 'mujs-prompt'); + + this.urlBar = qs('.mujs-url-bar'); + this.rateContainer = qs('.rate-container'); + this.footer = qs('.mujs-footer'); + this.countframe = qs('.counter-container'); + this.toolbar = qs('mujs-toolbar'); + this.table = qs('table'); + this.tabhead = qs('thead'); + this.cfgpage = qs('.mujs-cfg'); + this.main = qs('mujs-main'); + this.tabbody = qs('tbody'); + this.btnframe = qs('.btn-frame'); + + this.btnfullscreen = make('mujs-btn', 'fullscreen', { + title: i18n$('max'), + innerHTML: iconSVG.load('expand'), + dataset: { + command: 'fullscreen' + } + }); + this.btnHandles = make('mujs-column', 'btn-handles'); + this.btncfg = make('mujs-btn', 'settings hidden', { + title: 'Settings', + innerHTML: iconSVG.load('gear'), + dataset: { + command: 'settings' + } + }); + this.btnhome = make('mujs-btn', 'github hidden', { + title: 'GitHub', + innerHTML: iconSVG.load('github'), + dataset: { + command: 'open-tab', + webpage: 'https://github.com/magicoflolis/Userscript-Plus' + } + }); + this.btnissue = make('mujs-btn', 'issue hidden', { + innerHTML: iconSVG.load('issue'), + title: i18n$('issue'), + dataset: { + command: 'open-tab', + webpage: 'https://github.com/magicoflolis/Userscript-Plus/issues' + } + }); + this.btngreasy = make('mujs-btn', 'greasy hidden', { + title: 'Greasy Fork', + innerHTML: iconSVG.load('globe'), + dataset: { + command: 'open-tab', + webpage: 'https://greasyfork.org/scripts/421603' + } + }); + this.btnnav = make('mujs-btn', 'nav', { + title: 'Navigation', + innerHTML: iconSVG.load('nav'), + dataset: { + command: 'navigation' + } + }); + if (params.size === 0) { + this.btnHandles.append(this.btnfullscreen); + } + + this.toolbar.append(this.btnHandles); + this.btnframe.append(this.btnhome, this.btngreasy, this.btnissue, this.btncfg, this.btnnav); + this.main.append(this.promptElem); + + const { host, refresh, cache, table } = this; + class Tabs { + /** + * @param { HTMLElement } root + */ + constructor(root) { + /** + * @type { Set } + */ + this.pool = new Set(); + this.blank = BLANK_PAGE; + this.protocal = 'mujs:'; + this.protoReg = new RegExp(`${this.protocal}(.+)`, 'i'); + this.el = { + add: make('mujs-addtab', { + textContent: '+', + dataset: { + command: 'new-tab' + } + }), + head: make('mujs-tabs'), + root + }; + this.el.head.append(this.el.add); + this.el.root.append(this.el.head); + this.custom = () => {}; + } + /** + * @param {string} hostname + */ + getTab(hostname) { + return [...this.pool].find(({ dataset }) => hostname === dataset.host); + } + getActive() { + return [...this.pool].find((tab) => tab.classList.contains('active')); + } + /** + * @param {string} hostname + */ + intFN(hostname) { + const p = this.protoReg.exec(hostname); + if (!p) { + return; + } + if (p[1] === 'settings') { + dom.cl.remove(cfgpage, 'hidden'); + dom.cl.add(table, 'hidden'); + urlBar.placeholder = 'Search settings'; + } + } + /** + * @param {HTMLElement} tab + * @param {boolean} [build] + */ + active(tab, build = true) { + if (!this.pool.has(tab)) this.pool.add(tab); + dom.cl.add([table, cfgpage], 'hidden'); + dom.cl.remove([...this.pool], 'active'); + dom.cl.add(tab, 'active'); + if (!build) { + dom.cl.remove(table, 'hidden'); + return; + } + const host = tab.dataset.host ?? this.blank; + if (host === this.blank) { + dom.cl.add(cfgpage, 'hidden'); + dom.cl.remove(table, 'hidden'); + refresh(); + } else if (host.startsWith(this.protocal)) { + this.intFN(host); + } else { + dom.cl.add(cfgpage, 'hidden'); + dom.cl.remove(table, 'hidden'); + this.custom(host); + } + } + /** @param { HTMLElement } tab */ + close(tab) { + if (this.pool.has(tab)) this.pool.delete(tab); + const host = tab.dataset.host; + if (cfg.clearTabCache && cache.has(host)) { + cache.delete(host); + sendMessage({ + type: 'clear', + host + }); + } + if (tab.classList.contains('active')) refresh(); + const sibling = tab.nextElementSibling ?? tab.previousElementSibling; + if (sibling) { + if (sibling.dataset.command !== 'new-tab') { + this.active(sibling); + } + } + tab.remove(); + } + /** + * @param {string} [hostname] + */ + create(hostname = undefined) { + if (typeof hostname === 'string') { + const createdTab = this.getTab(hostname); + if (this.protoReg.test(hostname) && createdTab) { + this.active(createdTab); + return; + } + } + const tab = make('mujs-tab', { + dataset: { + command: 'switch-tab' + }, + style: `order: ${this.el.head.childElementCount};` + }); + const tabClose = make('mu-js', { + dataset: { + command: 'close-tab' + }, + title: i18n$('close'), + textContent: 'X' + }); + const tabHost = make('mujs-host'); + const p = this.protoReg.exec(hostname); + tab.append(tabHost, tabClose); + this.el.head.append(tab); + this.active(tab, false); + if (isNull(hostname)) { + refresh(); + tab.dataset.host = this.blank; + tabHost.title = i18n$('newTab'); + tabHost.textContent = i18n$('newTab'); + } else if (p) { + tab.dataset.host = hostname || host; + tabHost.title = p[1] || tab.dataset.host; + tabHost.textContent = tabHost.title; + this.intFN(hostname); + } else { + tab.dataset.host = hostname || host; + tabHost.title = hostname || host; + tabHost.textContent = tabHost.title; + } + return tab; + } + } + this.Tabs = new Tabs(this.toolbar); + + const makeTHead = (rows = []) => { + const tr = make('tr'); + for (const r of rows) { + const tparent = make('th', r.class ?? '', r); + tr.append(tparent); + } + this.tabhead.append(tr); + this.table.append(this.tabhead, this.tabbody); + }; + makeTHead([ + { + class: 'mujs-header-name', + textContent: i18n$('name') + }, + { + textContent: i18n$('createdby') + }, + { + textContent: i18n$('daily_installs') + }, + { + textContent: i18n$('updated') + }, + { + textContent: i18n$('install') + } + ]); + + const tabbody = this.tabbody; + const getCellValue = (tr, idx) => + tr.children[idx].dataset.value || tr.children[idx].textContent; + const comparer = (idx, asc) => (a, b) => + ((v1, v2) => + v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) + ? v1 - v2 + : v1.toString().localeCompare(v2))( + getCellValue(asc ? a : b, idx), + getCellValue(asc ? b : a, idx) + ); + for (const th of this.tabhead.rows[0].cells) { + if (dom.text(th) === i18n$('install')) continue; + dom.cl.add(th, 'mujs-pointer'); + ael(th, 'click', () => { + /** [Stack Overflow Reference](https://stackoverflow.com/questions/14267781/sorting-html-table-with-javascript/53880407#53880407) */ + Array.from(tabbody.querySelectorAll('tr')) + .sort(comparer(Array.from(th.parentNode.children).indexOf(th), (this.asc = !this.asc))) + .forEach((tr) => tabbody.appendChild(tr)); + }); + } + return true; } catch (ex) { - err(ex, { cause: 'ContainerHandler' }); - this.webpage = window.location; + err(ex); } - this.host = getHost(this.webpage.hostname); + return false; + } + initFn() { + Counter.cnt.total.root = this.mainbtn; + for (const engine of cfg.engines) this.countframe.append(Counter.set(engine)); + } + // #endregion + setHost(link) { + this.webpage = strToURL(link); + this.host = this.getHost(this.webpage.host); + this.domain = this.getDomain(this.webpage.host); } - checkBlacklist(str) { str = str || this.host; let blacklisted = false; - for (const b of cfg.blacklist.filter((b) => b.enabled)) { - if (b.regex === true) { - const reg = new RegExp(b.url, b.flags); - if (!reg.test(str)) continue; - blacklisted = true; - } - if (Array.isArray(b.url)) { - for (const c of b.url) { - if (!str.includes(c)) continue; + if (/accounts*\.google\./.test(this.webpage.host)) { + blacklisted = true; + } + const blacklist = cfg.blacklist ?? []; + for (const b of blacklist) { + if (typeof b === 'string') { + if (b.startsWith('userjs-')) { + const r = /userjs-(\w+)/.exec(b)[1]; + const biList = builtinList[r]; + if (isRegExp(biList)) { + if (!biList.test(str)) continue; + blacklisted = true; + } else if (isObj(biList) && biList.host === this.host) { + blacklisted = true; + } + } + } else if (isObj(b)) { + if (!b.enabled) { + continue; + } + if (b.regex === true) { + const reg = new RegExp(b.url, b.flags); + if (!reg.test(str)) continue; blacklisted = true; } + if (Array.isArray(b.url)) { + for (const c of b.url) { + if (!str.includes(c)) continue; + blacklisted = true; + } + } + if (!str.includes(b.url)) continue; + blacklisted = true; } - if (!str.includes(b.url)) continue; - blacklisted = true; - } - if (blacklisted) { - this.showError('Blacklisted'); } this.isBlacklisted = blacklisted; - return blacklisted; + return this.isBlacklisted; } - - updateCounter(count, engine) { - this.counters[engine.name] += count; - this.counters.total += count; - this.updateCounters(); + getInfo(url) { + const webpage = strToURL(url || this.webpage); + const host = this.getHost(webpage.host); + const domain = this.getDomain(webpage.host); + return { + domain, + host, + webpage + }; } - - updateCounters() { - for (const [k, v] of Object.entries(this.counters)) { - if (k === 'total') { - continue; + /** + * @template { string } S + * @param { S } str + */ + getHost(str = '') { + return str.split('.').splice(-2).join('.'); + } + /** + * @template { string } S + * @param { S } str + */ + getDomain(str = '') { + return str.split('.').at(-2) ?? BLANK_PAGE; + } + renderTheme(theme) { + theme = theme || cfg.theme || DEFAULT_CONFIG.theme; + if (theme === DEFAULT_CONFIG.theme) { + return; + } + const sty = this.root.style; + for (const [k, v] of Object.entries(theme)) { + const str = `--mujs-${k}`; + const prop = sty.getPropertyValue(str); + if (isEmpty(v)) { + theme[k] = prop; } - if (qs(`count-frame[data-counter="${k}"]`)) { - dom.text(qs(`count-frame[data-counter="${k}"]`), v); + if (prop === v) { + continue; } + sty.removeProperty(str); + sty.setProperty(str, v); } - webext.browserAction.setBadgeText({ - text: `${this.counters.total}` - }); - document.title = `Magic UserJS+ (${this.counters.total})`; } - - makePrompt(txt, dataset = {}, usePrompt = true) { - if (qs('.prompt', promptElem)) { - for (const elem of qsA('.prompt', promptElem)) { + makePrompt(txt) { + if (qs('.prompt', this.promptElem)) { + for (const elem of qsA('.prompt', this.promptElem)) { if (elem.dataset.prompt) { elem.remove(); } @@ -267,235 +577,67 @@ const ContainerHandler = class { const elHead = make('mu-js', 'prompt-head', { innerHTML: `${iconSVG.load('refresh')} ${txt}` }); - el.append(elHead); - if (usePrompt) { - const elPrompt = make('mu-js', 'prompt-body', { dataset }); - const elYes = make('mujs-btn', 'prompt-confirm', { - innerHTML: 'Confirm', - dataset: { - command: 'prompt-confirm' - } - }); - const elNo = make('mujs-btn', 'prompt-deny', { - innerHTML: 'Deny', - dataset: { - command: 'prompt-deny' - } - }); - elPrompt.append(elYes, elNo); - el.append(elPrompt); - } - promptElem.append(el); - } + const elPrompt = make('mu-js', 'prompt-body'); + const elClose = make('mujs-btn', 'prompt-deny', { + textContent: i18n$('close') + }); + ael(elClose, 'click', () => { + el.remove(); + }); + elPrompt.append(elClose); + el.append(elHead, elPrompt); + this.promptElem.append(el); + return el; + } save() { this.unsaved = false; - // MU.storage.setValue('Config', cfg); + sendMessage({ + type: 'save', + cfg + }); info('Saved:', cfg); } - + /** + * @template {string | Error} E + * @param {...E} ex + */ showError(...ex) { err(...ex); const error = make('mu-js', 'error'); let str = ''; for (const e of ex) { - str += `${isString(e) ? e : `${e.message} ${e.stack}`}\n`; - } - error.appendChild(document.createTextNode(str)); - footer.append(error); - } - - refresh() { - urlBar.placeholder = i18n$('newTab'); - this.counters.total = 0; - for (const engine of cfg.engines) { - this.counters[engine.name] = 0; - } - this.updateCounters(); - dom.prop([tabbody, rateContainer, footer], 'innerHTML', ''); - } -}; -const MUJS = new ContainerHandler(); - -class Tabs { - constructor() { - this.Tab = new XMap(); - this.blank = 'about:blank'; - this.protocal = 'mujs:'; - this.protoReg = new RegExp(`${this.protocal}(.+)`); - this.el = { - add: make('mujs-addtab', '', { - textContent: '+', - dataset: { - command: 'new-tab' - } - }), - head: make('mujs-tabs') - }; - this.el.head.append(this.el.add); - toolbar.append(this.el.head); - } - hasTab(...params) { - for (const p of params) { - if (!this.Tab.has(p)) { - return false; - } - const content = normalizeTarget(this.Tab.get(p)).filter((t) => p === t.dataset.host); - if (isBlank(content)) { - return false; - } - } - return true; - } - storeTab(host) { - const h = host ?? this.blank; - if (!this.Tab.has(h)) { - this.Tab.set(h, new Set()); - } - return this.Tab.get(h); - } - cache(host, ...tabs) { - const h = host ?? this.blank; - const tabCache = this.storeTab(h); - for (const t of normalizeTarget(tabs)) { - if (tabCache.has(t)) { - continue; - } - tabCache.add(t); - } - this.Tab.set(h, tabCache); - return tabCache; - } - mujs(host) { - if (!host.startsWith(this.protocal)) { - return; - } - const type = host.match(this.protoReg)[1]; - if (type === 'settings') { - dom.cl.remove([cfgpage], 'hidden'); - dom.cl.add(table, 'hidden'); - urlBar.placeholder = i18n$('search_placeholder'); - } - } - active(tab, build = true) { - for (const t of normalizeTarget(tab, false)) { - dom.cl.add([cfgpage], 'hidden'); - dom.cl.remove(table, 'hidden'); - dom.cl.remove(qsA('mujs-tab', this.el.head), 'active'); - dom.cl.add(t, 'active'); - if (!build) { - continue; - } - const host = t.dataset.host ?? this.blank; - if (host === this.blank) { - MUJS.refresh(); - } else if (host.startsWith(this.protocal)) { - this.mujs(host); - } else { - buildlist(host); - } - } - } - /** @param { HTMLElement } tab */ - close(tab) { - for (const t of normalizeTarget(tab, false)) { - const host = t.dataset.host; - if (MUJS.cache.has(host)) { - MUJS.cache.delete(host); - } - if (dom.cl.has(t, 'active')) { - MUJS.refresh(); - } - const sibling = t.previousElementSibling ?? t.nextElementSibling; - if (sibling) { - if (sibling.dataset.command !== 'new-tab') { - this.active(sibling); + str += `${typeof e === 'string' ? e : `${e.cause ? `[${e.cause}] ` : ''}${e.message}${e.stack ? ` ${e.stack}` : ''}`}\n`; + if (isObj(e)) { + if (e.notify) { + dom.cl.add(this.mainframe, 'error'); } } - if (this.Tab.has(host)) { - this.Tab.delete(host); - } - t.remove(); } + error.appendChild(document.createTextNode(str)); + this.footer.append(error); } - create(host = undefined) { - if (isString(host)) { - if (host.startsWith(this.protocal) && this.hasTab(host)) { - this.active(this.Tab.get(host)); - return; - } - const content = normalizeTarget(this.storeTab(host)).filter((t) => host === t.dataset.host); - if (!isEmpty(content)) { - return; - } - } - const tab = make('mujs-tab', '', { - dataset: { - command: 'switch-tab' - }, - style: `order: ${this.el.head.childElementCount};` - }); - const tabClose = make('mu-js', '', { - dataset: { - command: 'close-tab' - }, - title: i18n$('close'), - textContent: 'X' - }); - const tabHost = make('mujs-host'); - tab.append(tabHost, tabClose); - this.el.head.append(tab); - this.active(tab, false); - this.cache(host, tab); - if (isNull(host)) { - MUJS.refresh(); - // urlBar.placeholder = i18n$('newTab'); - tab.dataset.host = this.blank; - tabHost.title = i18n$('newTab'); - tabHost.textContent = i18n$('newTab'); - } else if (host.startsWith(this.protocal)) { - const type = host.match(this.protoReg)[1]; - tab.dataset.host = host || MUJS.host; - tabHost.title = type || tab.dataset.host; - tabHost.textContent = tabHost.title; - this.mujs(host); - } else { - tab.dataset.host = host || MUJS.host; - tabHost.title = host || MUJS.host; - tabHost.textContent = tabHost.title; - } - return tab; + refresh() { + this.urlBar.placeholder = i18n$('newTab'); + Counter.reset(); + dom.cl.remove(this.toElem(), 'hidden'); + dom.cl.remove(qsA('mujs-section[data-name]', this.cfgpage), 'hidden'); + dom.prop([this.tabbody, this.rateContainer, this.footer], 'innerHTML', ''); } } -const tab = new Tabs(); - -const template = { - id: 0, - bad_ratings: 0, - good_ratings: 0, - ok_ratings: 0, - daily_installs: 0, - total_installs: 0, - name: 'NOT FOUND', - description: 'NOT FOUND', - version: '0.0.0', - url: 'about:blank', - code_url: 'about:blank', - created_at: Date.now(), - code_updated_at: Date.now(), - users: [ - { - name: '', - url: '' - } - ] +const container = new Container(window.top.document.location.href); +// #endregion +const { urlBar, tabbody, cfgpage, main, Tabs, showError } = container; +container.Tabs.custom = (host) => { + MUList.host = host; + MUList.build(); }; + const mkList = (txt = '', obj = {}) => { if (!obj.root || !obj.type) { return; } const { root, type } = obj; - const list = obj.list ?? []; const appliesTo = make('mu-js', 'mujs-list', { textContent: `${txt}: ` }); @@ -508,26 +650,48 @@ const mkList = (txt = '', obj = {}) => { }); ujsURLs.append(appliesTo, applyList); root.append(ujsURLs); + + const list = obj.list ?? []; if (isEmpty(list)) { const elem = make('mujs-a', '', { textContent: i18n$('listing_none') }); applyList.append(elem); - if (type === 'antifeatures') { - dom.cl.add(ujsURLs, 'hidden'); - } + dom.cl.add(ujsURLs, 'hidden'); return; } for (const c of list) { - if (isObj(c)) { + if (typeof c === 'string' && c.startsWith('http')) { const elem = make('mujs-a', '', { - textContent: c.text + textContent: c, + dataset: { + command: 'open-tab', + webpage: c + } }); - if (c.domain) { - elem.dataset.command = 'open-tab'; - elem.dataset.webpage = `https://${c.text}`; - } applyList.append(elem); + } else if (isObj(c)) { + if (type === 'resource') { + for (const [k, v] of Object.entries(c)) { + const elem = make('mujs-a', '', { + textContent: k ?? 'ERROR' + }); + if (v.startsWith('http')) { + elem.dataset.command = 'open-tab'; + elem.dataset.webpage = v; + } + applyList.append(elem); + } + } else { + const elem = make('mujs-a', '', { + textContent: c.text + }); + if (c.domain) { + elem.dataset.command = 'open-tab'; + elem.dataset.webpage = `https://${c.text}`; + } + applyList.append(elem); + } } else { const elem = make('mujs-a', '', { textContent: c @@ -535,35 +699,37 @@ const mkList = (txt = '', obj = {}) => { applyList.append(elem); } } - if (type === 'antifeatures') { - dom.cl.remove(ujsURLs, 'hidden'); - } -}; -const toLocaleDate = (str = '') => { - return new Intl.DateTimeFormat(navigator.language).format(new Date(str)); }; // #region Create UserJS +/** + * @param { import("../typings/types.d.ts").GSForkQuery } ujs + * @param { string } engine + */ const createjs = (ujs, engine) => { - for (const key in template) { - if (hasOwn(ujs, key)) { - continue; - } - ujs[key] = template[key]; - } - // Lets not add this UserJS to the list - if (ujs.id === 421603) { + const a = [ + ujs.deleted === true, + ujs.id === 421603, // Lets not add this UserJS to the list + badUserJS.includes(ujs.id), + badUserJS.includes(ujs.url) + ].find((t) => t === true); + if (a) { return; } - if (!MUJS.userjsCache.has(ujs.id)) { - MUJS.userjsCache.set(ujs.id, ujs); - } + if (!container.userjsCache.has(ujs.id)) container.userjsCache.set(ujs.id, ujs); const eframe = make('td', 'install-btn'); const uframe = make('td', 'mujs-uframe'); const fdaily = make('td', 'mujs-list', { - textContent: ujs.daily_installs + textContent: ujs.daily_installs, + dataset: { + name: 'daily_installs' + } }); const fupdated = make('td', 'mujs-list', { - textContent: toLocaleDate(ujs.code_updated_at) + textContent: language.toDate(ujs.code_updated_at), + dataset: { + name: 'code_updated_at', + value: new Date(ujs.code_updated_at).toISOString() + } }); const fname = make('td', 'mujs-name'); const fmore = make('mujs-column', 'mujs-list hidden', { @@ -587,16 +753,24 @@ const createjs = (ujs, engine) => { textContent: `${i18n$('version_number')}: ${ujs.version}` }); const fcreated = make('mu-js', 'mujs-list', { - textContent: `${i18n$('created_date')}: ${toLocaleDate(ujs.created_at)}` + textContent: `${i18n$('created_date')}: ${language.toDate(ujs.created_at)}`, + dataset: { + name: 'created_at', + value: new Date(ujs.created_at).toISOString() + } }); const flicense = make('mu-js', 'mujs-list', { title: ujs.license ?? i18n$('no_license'), textContent: `${i18n$('license')}: ${ujs.license ?? i18n$('no_license')}`, - style: - 'text-overflow: ellipsis; overflow: hidden; white-space: nowrap; width: fit-content; max-width: 20em;' + dataset: { + name: 'license' + } }); const ftotal = make('mu-js', 'mujs-list', { - textContent: `${i18n$('total_installs')}: ${ujs.total_installs}` + textContent: `${i18n$('total_installs')}: ${language.toNumber(ujs.total_installs)}`, + dataset: { + name: 'total_installs' + } }); const fratings = make('mu-js', 'mujs-list', { title: i18n$('ratings'), @@ -606,6 +780,7 @@ const createjs = (ujs, engine) => { title: i18n$('good'), textContent: ujs.good_ratings, dataset: { + name: 'good_ratings', el: 'good' } }); @@ -613,6 +788,7 @@ const createjs = (ujs, engine) => { title: i18n$('ok'), textContent: ujs.ok_ratings, dataset: { + name: 'ok_ratings', el: 'ok' } }); @@ -620,6 +796,7 @@ const createjs = (ujs, engine) => { title: i18n$('bad'), textContent: ujs.bad_ratings, dataset: { + name: 'bad_ratings', el: 'bad' } }); @@ -630,18 +807,16 @@ const createjs = (ujs, engine) => { command: 'list-description' } }); - const sIcon = ujs.code_url.endsWith('.user.css') ? '🧾' : ujs.code_url.endsWith('.user.js') ? '🐵' : '💾'; const scriptInstall = make('mu-jsbtn', 'install', { - // innerHTML: `${iconSVG.load('install')} ${i18n$('install')}`, - innerHTML: `${sIcon} ${i18n$('install')}`, + innerHTML: `${iconSVG.load('install')} ${i18n$('install')}`, title: `${i18n$('install')} "${ujs.name}"`, dataset: { command: 'install-script', userjs: ujs.code_url } }); - const scriptDownload = make('mu-jsbtn', '', { - innerHTML: `${iconSVG.load('install')} ${i18n$('saveFile')}`, + const scriptDownload = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('download')} ${i18n$('saveFile')}`, dataset: { command: 'download-userjs', userjs: ujs.id, @@ -662,21 +837,26 @@ const createjs = (ujs, engine) => { spellcheck: false, wrap: 'soft' }); - const loadCode = make('mu-jsbtn', '', { - innerHTML: `${iconSVG.load('search')} ${i18n$('preview_code')}`, + const loadCode = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('code')} ${i18n$('preview_code')}`, dataset: { command: 'load-userjs', userjs: ujs.id } }); - if (engine) { - tr.dataset.engine = engine; - if (!engine.includes('fork') && cfg.recommend.others && goodUserJS.includes(ujs.url)) { - tr.dataset.good = 'upsell'; + const loadMetadata = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('code')} Metadata`, + dataset: { + command: 'load-header', + userjs: ujs.id } + }); + tr.dataset.engine = engine; + if (!engine.includes('fork') && cfg.recommend.others && goodUserJS.includes(ujs.url)) { + tr.dataset.good = 'upsell'; } for (const u of ujs.users) { - const user = make('mujs-a', '', { + const user = make('mujs-a', { innerHTML: u.name, title: u.url, dataset: { @@ -690,54 +870,223 @@ const createjs = (ujs, engine) => { } uframe.append(user); } - if (engine.includes('fork') && cfg.recommend.others && goodUserJS.includes(ujs.id)) { + if (cfg.recommend.others && goodUserJS.includes(ujs.id)) { tr.dataset.good = 'upsell'; } eframe.append(scriptInstall); ratings.append(fratings, fgood, fok, fbad); jsInfo.append(ftotal, ratings, fver, fcreated); - mkList('Size', { - list: ujs.code_size, - type: 'size', + mkList(i18n$('code_size'), { + list: ujs._mujs.code.code_size, + type: 'code_size', root: jsInfo }); jsInfoB.append(flicense); + const data_meta = ujs._mujs.code?.data_meta ?? {}; mkList(i18n$('antifeatures'), { - list: ujs.antifeatures, + list: data_meta.antifeatures ?? [], type: 'antifeatures', root: jsInfoB }); mkList(i18n$('applies_to'), { - list: ujs.code_match, - type: 'match-urls', + list: ujs._mujs.code?.data_names ?? [], + type: 'data_names', root: jsInfoB }); mkList('@grant', { - list: ujs.code_grant, - type: 'grants', + list: data_meta.grant ?? [], + type: 'grant', root: jsInfoB }); - fmore.append(jsInfo, jsInfoB); - fBtns.append(scriptDownload, loadCode); - fname.append(ftitle, fdesc, fmore, fBtns, codeArea); - - if (ujs.code_data) { - codeArea.value = ujs.code_data; - } + mkList('@require', { + list: data_meta.require, + type: 'require', + root: jsInfoB + }); + mkList('@resource', { + list: isNull(data_meta.resource) ? [] : [data_meta.resource], + type: 'resource', + root: jsInfoB + }); + fmore.append(jsInfo, jsInfoB); + fBtns.append(scriptDownload, loadCode, loadMetadata); + fname.append(ftitle, fdesc, fmore, fBtns, codeArea); - for (const e of [fname, uframe, fdaily, fupdated, eframe]) { - tr.append(e); - } - tabbody.append(tr); + const loadPage = make('mu-jsbtn', { + innerHTML: `${iconSVG.load('pager')} Page`, + dataset: { + command: 'load-page', + userjs: ujs.id + } + }); + fBtns.append(loadPage); + + if (ujs._mujs.code?.translated) tr.classList.add('translated'); + + for (const e of [fname, uframe, fdaily, fupdated, eframe]) tr.append(e); + ujs._mujs.root = tr; + return ujs._mujs.root; }; // #endregion +const applyTo = (ujs, name, elem, root) => { + const n = ujs._mujs.code[name] ?? ujs._mujs.code.data_meta[name]; + if (isEmpty(n)) { + const el = make('mujs-a', { + textContent: i18n$('listing_none') + }); + elem.append(el); + return; + } + dom.prop(elem, 'innerHTML', ''); + dom.cl.remove(root, 'hidden'); + if (isObj(n)) { + if (name === 'resource') { + for (const [k, v] of Object.entries(n)) { + const el = make('mujs-a', { + textContent: k ?? 'ERROR' + }); + if (v.startsWith('http')) { + el.dataset.command = 'open-tab'; + el.dataset.webpage = v; + } + elem.append(el); + } + } else { + const el = make('mujs-a', { + textContent: n.text + }); + if (n.domain) { + el.dataset.command = 'open-tab'; + el.dataset.webpage = `https://${n.text}`; + } + elem.append(el); + } + } else if (typeof n === 'string') { + const el = make('mujs-a', { + textContent: n + }); + elem.append(el); + } else { + for (const c of n) { + if (typeof c === 'string' && c.startsWith('http')) { + const el = make('mujs-a', { + textContent: c, + dataset: { + command: 'open-tab', + webpage: c + } + }); + elem.append(el); + } else if (isObj(c)) { + const el = make('mujs-a', { + textContent: c.text + }); + if (c.domain) { + el.dataset.command = 'open-tab'; + el.dataset.webpage = `https://${c.text}`; + } + elem.append(el); + } else { + const el = make('mujs-a', { + textContent: c + }); + elem.append(el); + } + } + } +}; const doInstallProcess = (installLink) => { - const queryOptions = { active: true, currentWindow: true }; - webext.tabs.query(queryOptions, (tabs) => { - webext.tabs.update(tabs[0].id, { url: installLink }); - }); + if (typeof currentTab.id === 'number') { + webext.tabs.update(currentTab.id, { url: installLink }); + } +}; +const doDownloadProcess = (details) => { + if (!details.url) { + return; + } + const a = make('a'); + a.href = details.url; + a.setAttribute('download', details.filename || ''); + a.setAttribute('type', 'text/plain'); + a.dispatchEvent(new MouseEvent('click')); }; + +// #region List +class List extends BaseList { + constructor(hostname = undefined) { + super(hostname, container, cfg); + this.build = this.build.bind(this); + this.sortRecords = this.sortRecords.bind(this); + } + + // #region Builder + async build(results) { + try { + const { engines, container, host } = this; + if (isEmpty(engines)) { + for (const e of cfg.engines.filter((engine) => engine.enabled)) { + showError(`Engine: "${e.name}" unsupported on "${host}"`); + } + return; + } + if (container.checkBlacklist(host)) { + showError(`Blacklisted "${host}"`); + return; + } + container.refresh(); + if (!results) { + const { data } = await sendMessage({ + type: 'getData', + currentTab, + hostname: host + }); + results = data; + } + for (const ujs of results ?? []) { + // translate = false, code_url + ujs._mujs.code.request = async (code_url) => { + if (ujs._mujs.code.data_code_block) { + return ujs._mujs.code; + } + const p = new ParseUserJS(); + await p.request(code_url ?? ujs.code_url); + // await p.request(translate, code_url ?? ujs.code_url, ujs); + for (const [k, v] of Object.entries(p)) { + ujs._mujs.code[k] = v; + } + return ujs._mujs.code; + }; + createjs(ujs, ujs._mujs.info.engine.name); + } + urlBar.placeholder = i18n$('search_placeholder'); + urlBar.value = ''; + this.sortRecords(); + } catch (ex) { + showError(ex); + } + } + + sortRecords() { + const arr = Array.from(this); + for (const ujs of arr.flat().sort((a, b) => { + const sortType = cfg.autoSort ?? 'daily_installs'; + return b[sortType] - a[sortType]; + })) { + if (isElem(ujs._mujs.root)) tabbody.append(ujs._mujs.root); + } + for (const [name, value] of Object.entries(this.groupBy(arr))) { + Counter.update(value.length, { name }); + } + } + // #endregion +} +const MUList = new List(); +// #endregion + +container.renderTheme(); + +// #region Main event handlers ael(main, 'click', async (evt) => { try { /** @type { HTMLElement } */ @@ -748,29 +1097,56 @@ ael(main, 'click', async (evt) => { const prmpt = /prompt-/.test(target.dataset.command); let dataset = target.dataset; let cmd = dataset.command; - let prmptChoice = false; if (prmpt) { dataset = target.parentElement.dataset; cmd = dataset.command; - prmptChoice = /confirm/.test(target.dataset.command); - target.parentElement.parentElement.remove(); + if (/prompt-install/.test(target.dataset.command)) { + doInstallProcess(target.dataset.code_url); + target.parentElement.parentElement.parentElement.remove(); + } else if (/prompt-download/.test(target.dataset.command)) { + target.parentElement.parentElement.parentElement.remove(); + } else { + target.parentElement.parentElement.remove(); + } + return; } - if (cmd === 'install-script' && dataset.userjs) { - let installCode = dataset.userjs; - if (!prmpt && dataset.userjs.endsWith('.user.css')) { - MUJS.makePrompt(i18n$('prmpt_css'), dataset); + if (cmd === 'install-script') { + if (!container.userjsCache.has(+dataset.userjs)) { return; - } else if (prmpt !== prmptChoice) { - installCode = dataset.userjs.replace(/\.user\.css$/, '.user.js'); } - doInstallProcess(installCode); + const dataUserJS = container.userjsCache.get(+dataset.userjs); + if (dataUserJS.code_urls.length > 1) { + const list = make('mujs-list', { + style: 'display: flex; flex-direction: column;' + }); + for (const ujs of dataUserJS.code_urls) { + const a = make('mujs-a', { + title: ujs.code_url, + textContent: ujs.name, + dataset: { + command: 'prompt-install', + code_url: ujs.code_url + } + }); + list.append(a); + } + container.makePrompt(`Multiple detected: ${list.outerHTML}`, dataset, false); + } else { + doInstallProcess(dataUserJS.code_url); + } } else if (cmd === 'open-tab' && dataset.webpage) { openInTab(dataset.webpage); + } else if (cmd === 'fullscreen') { + const tab = container.Tabs.getActive(); + openInTab(`${location.href}?host=${tab.dataset.host}`); } else if (cmd === 'navigation') { - if (dom.cl.has(btngreasy, 'hidden')) { - dom.cl.remove([btncfg, btngreasy, btnhome, btnissue], 'hidden'); - } else { - dom.cl.add([btncfg, btngreasy, btnhome, btnissue], 'hidden'); + for (const e of qsA('mujs-btn', target.parentElement)) { + if (dom.cl.has(e, 'nav')) continue; + if (dom.cl.has(e, 'hidden')) { + dom.cl.remove(e, 'hidden'); + } else { + dom.cl.add(e, 'hidden'); + } } } else if (cmd === 'list-description') { const arr = []; @@ -795,119 +1171,164 @@ ael(main, 'click', async (evt) => { } else { dom.cl.add(arr, 'hidden'); } - } else if (cmd === 'show-filter') { - dom.cl.toggle(qs('.mujs-fltlist').parentElement, 'hidden'); } else if (cmd === 'save') { if (!dom.prop(target, 'disabled')) { - MUJS.save(); - // sleazyRedirect(); - if (MUJS.rebuild) { - MUJS.cache.clear(); - buildlist(); + container.save(); + if (container.rebuild) { + container.cache.clear(); + MUList.build(); } - MUJS.unsaved = false; - MUJS.rebuild = false; + container.unsaved = false; + container.rebuild = false; } } else if (cmd === 'reset') { - // cfg = defcfg; - MUJS.unsaved = true; - MUJS.rebuild = true; + sendMessage({ type: 'reset' }); + cfg = DEFAULT_CONFIG; + container.unsaved = true; + container.rebuild = true; rebuildCfg(); } else if (cmd === 'settings') { - if (MUJS.unsaved) { - MUJS.showError('Unsaved changes'); + if (container.unsaved) { + showError('Unsaved changes'); } - tab.create('mujs:settings'); - MUJS.rebuild = false; + Tabs.create('mujs:settings'); + container.rebuild = false; } else if (cmd === 'new-tab') { - tab.create(); + Tabs.create(); } else if (cmd === 'switch-tab') { - tab.active(target); + Tabs.active(target); } else if (cmd === 'close-tab' && target.parentElement) { - tab.close(target.parentElement); + Tabs.close(target.parentElement); } else if (cmd === 'download-userjs') { - if (!MUJS.userjsCache.has(+dataset.userjs)) { + if (!container.userjsCache.has(+dataset.userjs)) { return; } - const dataUserJS = MUJS.userjsCache.get(+dataset.userjs); - const txt = await reqCode(dataUserJS); - if (typeof txt !== 'string') { - return; + const dataUserJS = container.userjsCache.get(+dataset.userjs); + + if (dataUserJS.code_urls.length > 1) { + const list = make('mujs-list', { + style: 'display: flex; flex-direction: column;' + }); + for (const ujs of dataUserJS.code_urls) { + const a = make('mujs-a', { + title: ujs.code_url, + textContent: ujs.name, + dataset: { + command: 'prompt-download', + code_url: ujs.code_url + } + }); + list.append(a); + } + container.makePrompt(`Multiple detected: ${list.outerHTML}`, dataset, false); + } else { + const code_obj = await dataUserJS._mujs.code.request(); + if (typeof code_obj.code === 'string') + doDownloadProcess({ + url: 'data:text/plain;charset=utf-8,' + encodeURIComponent(code_obj.code), + filename: `${dataUserJS.name}${/\.user\.css$/.test(dataUserJS.code_url) ? '.user.css' : '.user.js'}` + }); } - const makeUserJS = new Blob([txt], { type: 'text/plain' }); - const dlBtn = make('a', 'mujs_Downloader'); - dlBtn.href = URL.createObjectURL(makeUserJS); - dlBtn.download = `${dataset.userjsName ?? dataset.userjs}.user.js`; - dlBtn.click(); - URL.revokeObjectURL(dlBtn.href); - dlBtn.remove(); - } else if (cmd === 'load-userjs') { - if (!MUJS.userjsCache.has(+dataset.userjs)) { + } else if (cmd === 'load-userjs' || cmd === 'load-header') { + if (!container.userjsCache.has(+dataset.userjs)) { return; } const codeArea = qs('textarea', target.parentElement.parentElement); - if (!isEmpty(codeArea.value)) { + if (!isEmpty(codeArea.value) && cmd === codeArea.dataset.load) { dom.cl.toggle(codeArea, 'hidden'); return; } - const dataUserJS = MUJS.userjsCache.get(+dataset.userjs); - const txt = await reqCode(dataUserJS); - if (typeof txt !== 'string') { + codeArea.dataset.load = cmd; + const dataUserJS = container.userjsCache.get(+dataset.userjs); + const code_obj = await dataUserJS._mujs.code.request(); + if (typeof code_obj.data_code_block !== 'string') { + codeArea.value = 'An error occured'; return; } - codeArea.value = txt; + codeArea.value = cmd === 'load-userjs' ? code_obj.data_code_block : code_obj.data_meta_block; dom.cl.remove(codeArea, 'hidden'); - - const apTo = (name, elem) => { - if (isEmpty(dataUserJS[name])) { - const el = make('mujs-a', '', { - textContent: i18n$('listing_none') + for (const e of qsA('mujs-column[data-el="matches"]', target.parentElement.parentElement)) { + applyTo(dataUserJS, e.dataset.type, qs('.mujs-grants', e), e); + } + } else if (cmd === 'load-page') { + if (!container.userjsCache.has(+dataset.userjs)) { + return; + } + let pageArea = qs('mujs-page', target.parentElement.parentElement); + if (!pageArea) { + pageArea = make('mujs-page'); + target.parentElement.parentElement.append(pageArea); + const dataUserJS = container.userjsCache.get(+dataset.userjs); + const engine = dataUserJS._mujs.info.engine; + let pageURL; + if (engine.name.includes('fork')) { + const current = navigator.language.split('-')[0] ?? 'en'; + pageURL = dataUserJS.url.replace( + /\/scripts/, + `/${/^(zh|fr|es)/.test(current) ? navigator.language : current}/scripts` + ); + } else if (engine.name.includes('github')) { + const page_url = await Network.req(dataUserJS.page_url, 'GET', 'json', { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${engine.token}`, + 'X-GitHub-Api-Version': '2022-11-28' + } + }).catch(() => { + return {}; }); - elem.append(el); - if (name === 'antifeatures') { - dom.cl.add(elem, 'hidden'); + if (!page_url.download_url) { + return; } + const page = await Network.req(page_url.download_url, 'GET', 'text'); + const shadow = pageArea.attachShadow({ mode: 'closed' }); + const div = make('div', { + innerHTML: page + }); + shadow.append(div); + return; } else { - for (const c of dataUserJS[name]) { - if (isObj(c)) { - const el = make('mujs-a', '', { - textContent: c.text - }); - if (c.domain) { - el.dataset.command = 'open-tab'; - el.dataset.webpage = `https://${c.text}`; - } - elem.append(el); - } else { - const el = make('mujs-a', '', { - textContent: c - }); - elem.append(el); + pageURL = dataUserJS.url; + } + if (!pageURL) { + return; + } + const page = await Network.req(pageURL, 'GET', 'document'); + const getContent = () => { + let content = 'An error occured'; + const h = new URL(dataUserJS.url); + const root = qs('.user-content', page.documentElement); + for (const e of qsA('[href]', root)) { + e.target = '_blank'; + e.style = 'pointer-events: auto;'; + if (e.href.startsWith('/')) { + e.href = `${h.origin}${e.href}`; } } - if (name === 'antifeatures') { - dom.cl.remove(elem, 'hidden'); + for (const e of qsA('img[src]', root)) { + e.style = + 'max-width: 25em; max-height: 25em; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;'; } - } - }; - const matchElem = qs( - '[data-type="match-urls"] > .mujs-grants', - target.parentElement.parentElement - ); - const grantElem = qs( - '[data-type="grants"] > .mujs-grants', - target.parentElement.parentElement - ); - const afElem = qs( - '[data-type="antifeatures"] > .mujs-grants', - target.parentElement.parentElement - ); - const sizeElem = qs('[data-type="size"] > .mujs-grants', target.parentElement.parentElement); - dom.prop([matchElem, grantElem, afElem, sizeElem], 'innerHTML', ''); - apTo('code_match', matchElem); - apTo('code_grant', grantElem); - apTo('antifeatures', afElem); - apTo('code_size', sizeElem); + if (root) { + content = root.innerHTML; + } else { + content = 'No additional info available'; + } + return content; + }; + const shadow = pageArea.attachShadow({ mode: 'closed' }); + const div = make('div', { + style: 'pointer-events: none;', + innerHTML: getContent() + }); + shadow.append(div); + return; + } + if (!dom.cl.has(pageArea, 'hidden')) { + dom.cl.add(pageArea, 'hidden'); + return; + } + dom.cl.remove(pageArea, 'hidden'); } else if (/export-/.test(cmd)) { const str = JSON.stringify(cmd === 'export-cfg' ? cfg : cfg.theme, null, ' '); const bytes = new TextEncoder().encode(str); @@ -936,29 +1357,28 @@ ael(main, 'click', async (evt) => { if (result.blacklist) { log(`Imported config: { ${file.name} }`, result); cfg = result; - MUJS.unsaved = true; - MUJS.rebuild = true; + container.unsaved = true; + container.rebuild = true; rebuildCfg(); - MUJS.save(); - // sleazyRedirect(); - MUJS.cache.clear(); - buildlist(); - MUJS.unsaved = false; - MUJS.rebuild = false; + container.save(); + container.cache.clear(); + MUList.build(); + container.unsaved = false; + container.rebuild = false; } else { log(`Imported theme: { ${file.name} }`, result); cfg.theme = result; - renderTheme(cfg.theme); + container.renderTheme(cfg.theme); } inpJSON.remove(); }; reader.onerror = () => { - MUJS.showError(reader.error); + showError(reader.error); inpJSON.remove(); }; }); } catch (ex) { - MUJS.showError(ex); + showError(ex); inpJSON.remove(); } } @@ -967,7 +1387,7 @@ ael(main, 'click', async (evt) => { inpJSON.click(); } } catch (ex) { - MUJS.showError(ex); + showError(ex); } }); ael(main, 'auxclick', (evt) => { @@ -982,61 +1402,179 @@ ael(main, 'auxclick', (evt) => { const dataset = target.dataset; const cmd = dataset.command; if (cmd === 'switch-tab' || cmd === 'close-tab') { - tab.close(target); + Tabs.close(target); } else if (cmd === 'new-tab') { - tab.create(); + Tabs.create(); } }); -renderTheme(cfg.theme); -const getCellValue = (tr, idx) => tr.children[idx].innerText || tr.children[idx].textContent; -const comparer = (idx, asc) => (a, b) => - ((v1, v2) => - v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) ? v1 - v2 : v1.toString().localeCompare(v2))( - getCellValue(asc ? a : b, idx), - getCellValue(asc ? b : a, idx) - ); -for (const th of tabhead.rows[0].cells) { - if (dom.text(th) === i18n$('install')) continue; - dom.cl.add(th, 'mujs-pointer'); - ael(th, 'click', () => { - /** [Stack Overflow Reference](https://stackoverflow.com/questions/14267781/sorting-html-table-with-javascript/53880407#53880407) */ - Array.from(tabbody.querySelectorAll('tr')) - .sort(comparer(Array.from(th.parentNode.children).indexOf(th), (this.asc = !this.asc))) - .forEach((tr) => tabbody.appendChild(tr)); - }); -} +ael(main, 'updateditem', (evt) => { + /** + * @type {import("../typings/types.d.ts").GSForkQuery} + */ + const ujs = evt.detail; + if (!ujs._mujs) return; + for (const elem of qsA('[data-name]', ujs._mujs.root)) { + const name = elem.dataset.name; + if (name === 'code') { + if (ujs._mujs.code.data_code_block) { + if (cfg.preview.code && !cfg.preview.metadata) { + elem.value = ujs._mujs.code.data_code_block; + } else if (cfg.preview.metadata && !cfg.preview.code) { + elem.value = ujs._mujs.code.data_meta_block; + } else { + elem.value = `${ujs._mujs.code.META_START_COMMENT}${ujs._mujs.code.data_meta_block}${ujs._mujs.code.META_END_COMMENT}${ujs._mujs.code.data_code_block}`; + } + } + continue; + } + if (!ujs[name]) continue; + if (name === 'license') { + dom.attr(elem, 'title', ujs.license ?? i18n$('no_license')); + dom.text(elem, `${i18n$('license')}: ${ujs.license ?? i18n$('no_license')}`); + } else if (name === 'code_updated_at') { + dom.text(elem, language.toDate(ujs.code_updated_at)); + elem.dataset.value = new Date(ujs.code_updated_at).toISOString(); + } else if (name === 'created_date') { + dom.text(elem, `${i18n$('created_date')}: ${language.toDate(ujs.created_at)}`); + elem.dataset.value = new Date(ujs.created_at).toISOString(); + } else if (name === 'total_installs') { + dom.text(elem, `${i18n$('total_installs')}: ${language.toNumber(ujs.total_installs)}`); + } else { + dom.text(elem, ujs[name]); + } + } + if (ujs._mujs.code.data_code_block) { + for (const e of qsA('mujs-column[data-el="matches"]', ujs._mujs.root)) { + applyTo(ujs, e.dataset.type, qs('.mujs-grants', e), e); + } + } + if (container.userjsCache.has(ujs.id)) container.userjsCache.set(ujs.id, ujs); +}); ael(urlBar, 'input', (evt) => { evt.preventDefault(); if (urlBar.placeholder === i18n$('newTab')) { return; } + /** + * @type { string } + */ const val = evt.target.value; + const section = qsA('mujs-section[data-name]', cfgpage); if (isEmpty(val)) { - dom.cl.remove(qsA('tr[data-engine]', tabbody), 'hidden'); + dom.cl.remove(container.toElem(), 'hidden'); + dom.cl.remove(section, 'hidden'); return; } - const reg = new RegExp(val, 'gi'); const finds = new Set(); - const userjsCache = MUJS.userjsCache; - for (const [k, v] of userjsCache) { - const elem = qs(`tr[data-script-id="${k}"]`, tabbody); - if (!elem) { - continue; + if (!dom.cl.has(cfgpage, 'hidden')) { + const reg = new RegExp(val, 'gi'); + for (const elem of section) { + if (!isElem(elem)) { + continue; + } + if (finds.has(elem)) { + continue; + } + if (dom.text(elem).match(reg)) { + finds.add(elem); + } } - if (finds.has(elem)) { - continue; + dom.cl.add(section, 'hidden'); + dom.cl.remove([...finds], 'hidden'); + return; + } + const cacheValues = Array.from(container).filter(({ _mujs }) => { + return !finds.has(_mujs.root); + }); + /** + * @param {RegExpMatchArray} regExp + * @param {keyof import("../typings/types.d.ts").GSForkQuery} key + */ + const ezQuery = (regExp, key) => { + const q_value = val.replace(regExp, ''); + const reg = new RegExp(q_value, 'gi'); + for (const v of cacheValues) { + let k = v[key]; + if (typeof k === 'number') { + k = `${v[key]}`; + } + if (k && k.match(reg)) { + finds.add(v._mujs.root); + } + } + }; + if (val.match(/^(code_url|url):/)) { + ezQuery(/^(code_url|url):/, 'code_url'); + } else if (val.match(/^(author|users?):/)) { + const parts = /^[\w_]+:(.+)/.exec(val); + if (parts) { + const reg = new RegExp(parts[1], 'gi'); + for (const v of cacheValues.filter((v) => !isEmpty(v.users))) { + for (const user of v.users) { + for (const value of Object.values(user)) { + if (typeof value === 'string' && value.match(reg)) { + finds.add(v._mujs.root); + } else if (typeof value === 'number' && `${value}`.match(reg)) { + finds.add(v._mujs.root); + } + } + } + } + } + } else if (val.match(/^(locale|i18n):/)) { + ezQuery(/^(locale|i18n):/, 'locale'); + } else if (val.match(/^id:/)) { + ezQuery(/^id:/, 'id'); + } else if (val.match(/^license:/)) { + ezQuery(/^license:/, 'license'); + } else if (val.match(/^name:/)) { + ezQuery(/^name:/, 'name'); + } else if (val.match(/^description:/)) { + ezQuery(/^description:/, 'description'); + } else if (val.match(/^(search_engine|engine):/)) { + const parts = /^[\w_]+:(\w+)/.exec(val); + if (parts) { + const reg = new RegExp(parts[1], 'gi'); + for (const { _mujs } of cacheValues) { + if (!_mujs.info.engine.name.match(reg)) { + continue; + } + finds.add(_mujs.root); + } } - if (v.name && v.name.match(reg)) { - finds.add(elem); + } else if (val.match(/^filter:/)) { + const parts = /^\w+:(.+)/.exec(val); + if (parts) { + const bsFilter = loadFilters(cfg); + const filterType = bsFilter.get(parts[1].trim().toLocaleLowerCase()); + if (filterType) { + const { reg } = filterType; + for (const { name, users, _mujs } of cacheValues) { + if ([{ name }, ...users].find((o) => o.name.match(reg))) { + continue; + } + finds.add(_mujs.root); + } + } } - if (v.description && v.description.match(reg)) { - finds.add(elem); + } else if (val.match(/^recommend:/)) { + for (const { url, id, users, _mujs } of cacheValues) { + if ( + users.find((u) => u.id === authorID) || + goodUserJS.includes(url) || + goodUserJS.includes(id) + ) { + finds.add(_mujs.root); + } } - if (v.code_data) { - const meta = parse_meta(v.code_data); - for (const key of Object.keys(meta)) { - if (/name|desc/i.test(key) && key.match(reg)) { - finds.add(elem); + } else { + const reg = new RegExp(val, 'gi'); + for (const v of cacheValues) { + if (v.name && v.name.match(reg)) finds.add(v._mujs.root); + if (v.description && v.description.match(reg)) finds.add(v._mujs.root); + if (v._mujs.code.data_meta) { + for (const key of Object.keys(v._mujs.code.data_meta)) { + if (/name|desc/i.test(key) && key.match(reg)) finds.add(v._mujs.root); } } } @@ -1047,356 +1585,543 @@ ael(urlBar, 'input', (evt) => { ael(urlBar, 'change', (evt) => { evt.preventDefault(); const val = evt.target.value; - if (urlBar.placeholder === i18n$('newTab') && qs('mujs-tab.active', toolbar)) { - const tabElem = qs('mujs-tab.active', toolbar); - const tabHost = qs('mujs-host', tabElem); - if (val.startsWith('mujs:')) { - tab.close(tabElem); - if (tab.hasTab(val)) { - tab.active(tab.Tab.get(val)); + const tabElem = Tabs.getActive(); + if (urlBar.placeholder === i18n$('newTab') && tabElem) { + const tabHost = tabElem.firstElementChild; + const host = formatURL(normalizedHostname(val)); + if (Tabs.protoReg.test(val)) { + const createdTab = Tabs.getTab(val); + Tabs.close(tabElem); + if (createdTab) { + Tabs.active(createdTab); } else { - tab.create(val); + Tabs.create(val); } - return; - } else if (val === '*') { - tabElem.dataset.host = val; + evt.target.placeholder = i18n$('search_placeholder'); + evt.target.value = ''; + } else if (host === '*') { + tabElem.dataset.host = host; tabHost.title = ''; tabHost.textContent = ''; - buildlist(val); - return; - } - const value = getHost(val); - if (MUJS.checkBlacklist(value)) { - MUJS.showError(`Host blacklisted "${value}"`); - return; + MUList.host = host; + MUList.build(); + } else if (container.checkBlacklist(host)) { + showError(`Blacklisted "${host}"`); + } else { + tabElem.dataset.host = host; + tabHost.title = host; + tabHost.textContent = host; + MUList.host = host; + MUList.build(); } - tabElem.dataset.host = value; - tabHost.title = value; - tabHost.textContent = value; - buildlist(value); - return; } }); +// #endregion -async function buildlist(host = undefined) { - try { - if (isEmpty(host)) { - host = MUJS.host; - } - if (MUJS.checkBlacklist(host)) { - return; - } - if (!qs(`mujs-tab[data-host="${host}"]`)) { - tab.create(host); - } - MUJS.refresh(); - webext.runtime.sendMessage( - { - location: host - }, - (data) => { - if (isEmpty(data)) { - return; - } - for (const tabData of data) { - const { engine, host } = tabData; - if (!qs(`mujs-tab[data-host="${host}"]`)) { - tab.create(host); - } - for (const ujs of tabData.data) { - createjs(ujs, engine.name); - } - MUJS.updateCounter(tabData.data.length, engine); - } - urlBar.placeholder = i18n$('search_placeholder'); - urlBar.value = ''; - } - ); - } catch (ex) { - err(ex); - } -} +Tabs.custom = (host) => { + MUList.host = host; + MUList.build(); +}; -//#region Make Config +// #region Make Config const makecfg = () => { - const exBtn = make('mu-js', 'mujs-sty-flex'); - const exportCFG = make('mujs-btn', 'mujs-export', { - textContent: i18n$('export_config'), - dataset: { - command: 'export-cfg' - } - }); - const importCFG = make('mujs-btn', 'mujs-import', { - textContent: i18n$('import_config'), - dataset: { - command: 'import-cfg' - } - }); - const exportTheme = make('mujs-btn', 'mujs-export', { - textContent: i18n$('export_theme'), + const cbtn = make('mu-js', 'mujs-sty-flex'); + const savebtn = make('mujs-btn', 'save', { + textContent: i18n$('save'), dataset: { - command: 'export-theme' - } + command: 'save' + }, + disabled: false }); - const importTheme = make('mujs-btn', 'mujs-import', { - textContent: i18n$('import_theme'), + const resetbtn = make('mujs-btn', 'reset', { + textContent: i18n$('reset'), dataset: { - command: 'import-theme' + command: 'reset' } }); - exBtn.append(importCFG, importTheme, exportCFG, exportTheme); - cfgpage.append(exBtn); + cbtn.append(resetbtn, savebtn); - const makerow = (desc, type = null, nm, attrs = {}) => { - desc = desc ?? i18n$('no_license'); - nm = nm ?? i18n$('no_license'); - const sec = make('mujs-section', 'mujs-cfg-section', { - style: nm === 'cache' ? 'display: none;' : '' + const makesection = (name, tag) => { + tag = tag ?? i18n$('no_license'); + name = name ?? i18n$('no_license'); + const sec = make('mujs-section', { + dataset: { + name: tag + } + }); + const lb = make('label', { + dataset: { + command: tag + } }); - const lb = make('label'); - const divDesc = make('mu-js', 'mujs-cfg-desc', { - textContent: desc + const divDesc = make('mu-js', { + textContent: name }); + ael(sec, 'click', (evt) => { + /** @type { HTMLElement } */ + const target = evt.target.closest('[data-command]'); + if (!target) { + return; + } + const cmd = target.dataset.command; + if (cmd === tag) { + const a = qsA(`[data-${tag}]`, sec); + if (dom.cl.has(a, 'hidden')) { + dom.cl.remove(a, 'hidden'); + } else { + dom.cl.add(a, 'hidden'); + } + } + }); + lb.append(divDesc); sec.append(lb); cfgpage.append(sec); - if (isNull(type)) { + return sec; + }; + const sections = { + general: makesection('General', 'general'), + load: makesection('Automation', 'load'), + list: makesection('List', 'list'), + filters: makesection('List Filters', 'filters'), + blacklist: makesection('Blacklist (WIP)', 'blacklist'), + engine: makesection('Search Engines', 'engine'), + theme: makesection('Theme Colors', 'theme'), + exp: makesection('Import / Export', 'exp') + }; + const makeRow = (text, value, type = 'checkbox', tag = 'general', attrs = {}) => { + const lb = make('label', 'sub-section hidden', { + textContent: text, + dataset: { + [tag]: text + } + }); + cfgMap.set(text, value); + if (type === 'select') { + const inp = make('select', { + dataset: { + [tag]: text + }, + ...attrs + }); + for (const selV of Object.keys(template)) { + if (selV === 'deleted' || selV === 'users') continue; + const o = make('option', { + value: selV, + textContent: selV + }); + inp.append(o); + } + inp.value = cfg[value]; + lb.append(inp); + if (sections[tag]) { + sections[tag].append(lb); + } return lb; } - const inp = make('input', 'mujs-cfg-input', { + const inp = make('input', { type, dataset: { - name: nm + [tag]: text }, ...attrs }); - if (!cfgMap.has(nm)) { - cfgMap.set(nm, inp); + + if (tag === 'engine') { + inp.dataset.name = value; } + + if (sections[tag]) { + sections[tag].append(lb); + } + if (type === 'checkbox') { const inlab = make('mu-js', 'mujs-inlab'); - const la = make('label', '', { + const la = make('label', { onclick() { inp.dispatchEvent(new MouseEvent('click')); } }); inlab.append(inp, la); lb.append(inlab); - if (nm.includes('-')) { - return inp; - } - if (/(greasy|sleazy)fork|openuserjs|gi(thub|st)/gi.test(nm)) { - for (const i of cfg.engines) { - if (i.name !== nm) continue; - inp.checked = i.enabled; - inp.dataset.engine = i.name; + + const nm = /^(\w+)-(.+)/.exec(value); + if (nm) { + if (nm[1] === 'filters') { + inp.checked = cfg[nm[1]][nm[2]].enabled; + } else { + inp.checked = cfg[nm[1]][nm[2]]; + } + } else { + inp.checked = cfg[value]; + } + ael(inp, 'change', (evt) => { + container.unsaved = true; + if (/filterlang/i.test(value)) { + container.rebuild = true; + } + if (nm) { + if (nm[1] === 'filters') { + cfg[nm[1]][nm[2]].enabled = evt.target.checked; + } else { + cfg[nm[1]][nm[2]] = evt.target.checked; + } + } else { + cfg[value] = evt.target.checked; + } + sendMessage({ + type: 'save', + cfg + }); + }); + + if (tag === 'engine') { + const engine = cfg.engines.find((engine) => engine.name === value); + if (engine) { + inp.checked = engine.enabled; + inp.dataset.engine = engine.name; ael(inp, 'change', (evt) => { - // MUJS.unsaved = true; - // MUJS.rebuild = true; - i.enabled = evt.target.checked; - hermes.send('Save', { - prop: 'engines', - value: cfg.engines + container.unsaved = true; + container.rebuild = true; + engine.enabled = evt.target.checked; + sendMessage({ + type: 'engine', + engine }); }); + + if (engine.query) { + const d = DEFAULT_CONFIG.engines.find((e) => e.name === engine.name); + const urlInp = make('input', { + type: 'text', + defaultValue: '', + value: decodeURIComponent(engine.query) ?? '', + placeholder: decodeURIComponent(d.query) ?? '', + dataset: { + name: nm, + engine: engine.name + }, + onchange(evt) { + container.unsaved = true; + container.rebuild = true; + try { + engine.query = encodeURIComponent(new URL(evt.target.value).toString()); + sendMessage({ + type: 'engine', + engine + }); + } catch (ex) { + err(ex); + } + } + }); + lb.append(urlInp); + } + if (engine.name === 'github') { + const ghToken = make('input', { + type: 'text', + defaultValue: '', + value: engine.token ?? '', + placeholder: 'Paste Access Token', + dataset: { + engine: 'github-token' + }, + onchange(evt) { + container.unsaved = true; + container.rebuild = true; + engine.token = evt.target.value; + sendMessage({ + type: 'engine', + engine + }); + } + }); + lb.append(ghToken); + cfgMap.set('github-token', ghToken); + } } - } else { - inp.checked = cfg[nm]; - ael(inp, 'change', (evt) => { - // MUJS.unsaved = true; - // if (/filterlang/i.test(nm)) { - // MUJS.rebuild = true; - // } - cfg[nm] = evt.target.checked; - hermes.send('Save', { - prop: nm, - value: cfg[nm] - }); - }); } } else { + if (type === 'text') { + inp.defaultValue = ''; + inp.value = value ?? ''; + inp.placeholder = value ?? ''; + } + lb.append(inp); } - return inp; + + return lb; }; - makerow(i18n$('redirect'), 'checkbox', 'sleazyredirect'); - makerow(i18n$('filter'), 'checkbox', 'filterlang'); - makerow(i18n$('preview_code'), 'checkbox', 'codePreview'); - for (const inp of [ - makerow('Recommend author', 'checkbox', 'recommend-author'), - makerow('Recommend scripts', 'checkbox', 'recommend-others') - ]) { - const nm = inp.dataset.name === 'recommend-author' ? 'author' : 'others'; - inp.checked = cfg.recommend[nm]; - ael(inp, 'change', (evt) => { - // MUJS.unsaved = true; - cfg.recommend[nm] = evt.target.checked; - hermes.send('Save', { - prop: 'recommend', - value: cfg.recommend - }); + makeRow(i18n$('redirect'), 'sleazyredirect'); + + makeRow(i18n$('auto_fetch'), 'autofetch', 'checkbox', 'load'); + makeRow('Clear on Tab close', 'clearTabCache', 'checkbox', 'load'); + + makeRow(i18n$('default_sort'), 'autoSort', 'select', 'list'); + makeRow(i18n$('filter'), 'filterlang', 'checkbox', 'list'); + makeRow(i18n$('preview_code'), 'preview-code', 'checkbox', 'list'); + makeRow(i18n$('preview_metadata'), 'preview-metadata', 'checkbox', 'list'); + makeRow(i18n$('recommend_author'), 'recommend-author', 'checkbox', 'list'); + makeRow(i18n$('recommend_others'), 'recommend-others', 'checkbox', 'list'); + + for (const [k, v] of Object.entries(cfg.filters)) { + makeRow(v.name, `filters-${k}`, 'checkbox', 'filters'); + } + + makeRow('Greasy Fork', 'greasyfork', 'checkbox', 'engine'); + makeRow('Sleazy Fork', 'sleazyfork', 'checkbox', 'engine'); + // makeRow('Open UserJS', 'openuserjs', 'checkbox', 'engine'); + makeRow('GitHub API', 'github', 'checkbox', 'engine'); + + for (const [k, v] of Object.entries(cfg.theme)) { + const lb = make('label', 'hidden', { + textContent: k, + dataset: { + theme: k + } }); + const inp = make('input', { + type: 'text', + defaultValue: '', + value: v ?? '', + placeholder: v ?? '', + dataset: { + theme: k + }, + onchange(evt) { + let isvalid = true; + try { + const val = evt.target.value; + const sty = container.root.style; + const str = `--mujs-${k}`; + const prop = sty.getPropertyValue(str); + if (isEmpty(val)) { + cfg.theme[k] = DEFAULT_CONFIG.theme[k]; + sty.removeProperty(str); + return; + } + if (prop === val) { + return; + } + sty.removeProperty(str); + sty.setProperty(str, val); + cfg.theme[k] = val; + sendMessage({ + type: 'save', + cfg + }); + } catch (ex) { + err(ex); + isvalid = false; + } finally { + if (isvalid) { + dom.cl.remove(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', false); + } else { + dom.cl.add(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', true); + } + } + } + }); + cfgMap.set(k, inp); + lb.append(inp); + sections.theme.append(lb); } - makerow('Greasy Fork', 'checkbox', 'greasyfork'); - makerow('Sleazy Fork', 'checkbox', 'sleazyfork'); - makerow('Open UserJS', 'checkbox', 'openuserjs'); - makerow('GitHub API', 'checkbox', 'github'); - const ghAPI = cfg.engines.find((c) => c.name === 'github'); - const ghToken = makerow('GitHub API (Token)', 'text', 'github', { - defaultValue: '', - value: ghAPI.token ?? '', - placeholder: 'Paste Access Token', - onchange(evt) { - MUJS.unsaved = true; - MUJS.rebuild = true; - ghAPI.token = evt.target.value; - hermes.send('Save', { - prop: 'engines', - value: cfg.engines - }); + const createList = (key, v = '', disabled = false, type = 'String') => { + let txt = key; + if (typeof key === 'string') { + if (key.startsWith('userjs-')) { + disabled = true; + const s = key.substring(7); + txt = `Built-in "${s}"`; + v = builtinList[s]; + } + } else { + if (!key.enabled) { + return; + } } - }); - ghToken.dataset.engine = 'github-token'; - cfgMap.set('github-token', ghToken); - const cbtn = make('mu-js', 'mujs-sty-flex'); - const savebtn = make('mujs-btn', 'save', { - textContent: i18n$('save'), - dataset: { - command: 'save' - }, - disabled: false - }); - const resetbtn = make('mujs-btn', 'reset', { - textContent: i18n$('reset'), - dataset: { - command: 'reset' + if (isRegExp(v)) { + v = v.toString(); + type = 'RegExp'; + } else { + v = JSON.stringify(v); + type = 'Object'; } - }); - const blacklist = make('textarea', '', { - dataset: { - name: 'blacklist' - }, - rows: '10', - autocomplete: false, - spellcheck: false, - wrap: 'soft', - value: JSON.stringify(cfg.blacklist, null, ' '), - oninput(evt) { - let isvalid = true; - try { - cfg.blacklist = JSON.parse(evt.target.value); - isvalid = true; - } catch (ex) { - err(ex); - isvalid = false; - } finally { - if (isvalid) { - dom.cl.remove(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', false); - } else { - dom.cl.add(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', true); - } + + const lb = make('label', 'hidden', { + textContent: txt, + dataset: { + blacklist: key } - } - }); - const theme = make('textarea', '', { - dataset: { - name: 'theme' - }, - rows: '10', - autocomplete: false, - spellcheck: false, - wrap: 'soft', - value: JSON.stringify(cfg.theme, null, ' '), - oninput(evt) { - let isvalid = true; - try { - cfg.theme = JSON.parse(evt.target.value); - isvalid = true; - renderTheme(JSON.parse(evt.target.value)); - } catch (ex) { - err(ex); - isvalid = false; - } finally { - if (isvalid) { - dom.cl.remove(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', false); - } else { - dom.cl.add(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', true); + }); + const inp = make('input', { + type: 'text', + defaultValue: '', + value: v ?? '', + placeholder: v ?? '', + dataset: { + blacklist: key + }, + onchange(evt) { + let isvalid = true; + try { + const val = evt.target.value; + if (isEmpty(val)) { + return; + } + isvalid = true; + } catch (ex) { + err(ex); + isvalid = false; + } finally { + if (isvalid) { + dom.cl.remove(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', false); + } else { + dom.cl.add(evt.target, 'mujs-invalid'); + dom.prop(savebtn, 'disabled', true); + } } } + }); + const selType = make('select', { + disabled, + dataset: { + blacklist: key + } + }); + if (disabled) { + inp.readOnly = true; + const o = make('option', { + value: type, + textContent: type + }); + selType.append(o); + } else { + for (const selV of ['String', 'RegExp', 'Object']) { + const o = make('option', { + value: selV, + textContent: selV + }); + selType.append(o); + } } - }); - cfgMap.set('blacklist', blacklist); - cfgMap.set('theme', theme); - cbtn.append(resetbtn, savebtn); - cfgpage.append(blacklist, theme, cbtn); -}; -//#endregion - -const locSearch = location.search; + selType.value = type; + lb.append(inp, selType); + sections.blacklist.append(lb); + }; + for (const key of cfg.blacklist) { + createList(key); + } -const p = hermes.getPort(); -p.onMessage.addListener((root = {}) => { - const m = root.msg; - if (root.channel === 'Config' && isEmpty(cfg)) { - cfg = m.cfg || cfg; - for (const engine of cfg.engines) { - MUJS.counters[engine.name] = 0; - if (!engine.enabled) { - continue; - } - const counter = make('count-frame', '', { + const transfers = { + export: { + cfg: make('mujs-btn', 'mujs-export sub-section hidden', { + textContent: i18n$('export_config'), dataset: { - counter: engine.name - }, - title: engine.url, - textContent: '0' - }); - qs('.counter-container').append(counter); + command: 'export-cfg', + exp: 'export-cfg' + } + }), + theme: make('mujs-btn', 'mujs-export sub-section hidden', { + textContent: i18n$('export_theme'), + dataset: { + command: 'export-theme', + exp: 'export-theme' + } + }) + }, + import: { + cfg: make('mujs-btn', 'mujs-import sub-section hidden', { + textContent: i18n$('import_config'), + dataset: { + command: 'import-cfg', + exp: 'import-cfg' + } + }), + theme: make('mujs-btn', 'mujs-import sub-section hidden', { + textContent: i18n$('import_theme'), + dataset: { + command: 'import-theme', + exp: 'import-theme' + } + }) } - makecfg(); - if (/mujs=/.test(locSearch)) { - if (/settings/.test(locSearch)) { - tab.create('mujs:settings'); - } + }; + for (const value of Object.values(transfers)) { + for (const v of Object.values(value)) { + sections.exp.append(v); } } -}); -function primaryFN(data) { + cfgpage.append(cbtn); +}; +// #endregion + +async function init() { + const [tab] = await webext.tabs.query({ + active: true, + currentWindow: true + }); + const data = []; + if (tab instanceof Object === false) { + return true; + } + Object.assign(currentTab, tab); + let url; try { - if (isEmpty(data)) { - tab.create(); - return; - } - for (const tabData of data) { - const { engine, host } = tabData; - if (MUJS.checkBlacklist(host)) { - continue; - } - if (!qs(`mujs-tab[data-host="${host}"]`)) { - tab.create(host); - if (tabData.link) { - MUJS.setHost(tabData.link); - } + url = new URL(currentTab.url); + tabURL.href = url.href || ''; + } catch (ex) { + err(ex); + } + + if (url !== undefined) { + const hostname = formatURL(normalizedHostname(tabURL.hostname)); + const response = await sendMessage({ + init: true, + type: 'getData', + currentTab, + hostname + }); + if (response instanceof Object) { + Object.assign(cfg, response.cfg ?? DEFAULT_CONFIG); + if (Array.isArray(response.data)) { + data.push(...response.data); } - if (tabData.data) { - for (const ujs of tabData.data) { - createjs(ujs, engine.name); + container.initFn(); + makecfg(); + MUList.cfg = cfg; + MUList.engines = cfg.engines; + if (params.has('mujs', 'settings')) { + Tabs.create('mujs:settings'); + } else if (params.has('host')) { + const host = params.get('host'); + Tabs.create(host); + if (!host.startsWith(Tabs.protocal)) { + MUList.host = host; + MUList.build(data); } - MUJS.updateCounter(tabData.data.length, engine); + } else if (!url.origin.startsWith('https://')) { + Tabs.create(); + } else { + Tabs.create(hostname); + MUList.host = hostname; + MUList.build(data); } } - urlBar.placeholder = i18n$('search_placeholder'); - urlBar.value = ''; - } catch (ex) { - err(ex); } } -if (!/mujs=/.test(locSearch)) { - webext.runtime.sendMessage({}, primaryFN); +async function tryInit() { + try { + await init(); + } catch { + setTimeout(tryInit, 100); + } } + +tryInit(); diff --git a/src/js/punycode.js b/src/js/punycode.js new file mode 100644 index 0000000..a7f990d --- /dev/null +++ b/src/js/punycode.js @@ -0,0 +1,493 @@ +/*! https://mths.be/punycode v1.3.2 by @mathias */ +export default (function() { + /** + * The `punycode` object. + * @name punycode + * @type Object + */ + var punycode, + + /** Highest positive signed 32-bit float value */ + maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 + + /** Bootstring parameters */ + base = 36, + tMin = 1, + tMax = 26, + skew = 38, + damp = 700, + initialBias = 72, + initialN = 128, // 0x80 + delimiter = '-', // '\x2D' + + /** Regular expressions */ + regexPunycode = /^xn--/, + regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars + regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators + + /** Error messages */ + errors = { + 'overflow': 'Overflow: input needs wider integers to process', + 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', + 'invalid-input': 'Invalid input' + }, + + /** Convenience shortcuts */ + baseMinusTMin = base - tMin, + floor = Math.floor, + stringFromCharCode = String.fromCharCode, + + /** Temporary variable */ + key; + + /*--------------------------------------------------------------------------*/ + + /** + * A generic error utility function. + * @private + * @param {String} type The error type. + * @returns {Error} Throws a `RangeError` with the applicable error message. + */ + function error(type) { + throw new RangeError(errors[type]); + } + + /** + * A generic `Array#map` utility function. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} callback The function that gets called for every array + * item. + * @returns {Array} A new array of values returned by the callback function. + */ + function map(array, fn) { + var length = array.length; + var result = []; + while (length--) { + result[length] = fn(array[length]); + } + return result; + } + + /** + * A simple `Array#map`-like wrapper to work with domain name strings or email + * addresses. + * @private + * @param {String} domain The domain name or email address. + * @param {Function} callback The function that gets called for every + * character. + * @returns {Array} A new string of characters returned by the callback + * function. + */ + function mapDomain(string, fn) { + var parts = string.split('@'); + var result = ''; + if (parts.length > 1) { + // In email addresses, only the domain name should be punycoded. Leave + // the local part (i.e. everything up to `@`) intact. + result = parts[0] + '@'; + string = parts[1]; + } + // Avoid `split(regex)` for IE8 compatibility. See #17. + string = string.replace(regexSeparators, '\x2E'); + var labels = string.split('.'); + var encoded = map(labels, fn).join('.'); + return result + encoded; + } + + /** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * @see `punycode.ucs2.encode` + * @see + * @memberOf punycode.ucs2 + * @name decode + * @param {String} string The Unicode input string (UCS-2). + * @returns {Array} The new array of code points. + */ + function ucs2decode(string) { + var output = [], + counter = 0, + length = string.length, + value, + extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } + + /** + * Creates a string based on an array of numeric code points. + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 + * @name encode + * @param {Array} codePoints The array of numeric code points. + * @returns {String} The new Unicode string (UCS-2). + */ + function ucs2encode(array) { + return map(array, function(value) { + var output = ''; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + return output; + }).join(''); + } + + /** + * Converts a basic code point into a digit/integer. + * @see `digitToBasic()` + * @private + * @param {Number} codePoint The basic numeric code point value. + * @returns {Number} The numeric value of a basic code point (for use in + * representing integers) in the range `0` to `base - 1`, or `base` if + * the code point does not represent a value. + */ + function basicToDigit(codePoint) { + if (codePoint - 48 < 10) { + return codePoint - 22; + } + if (codePoint - 65 < 26) { + return codePoint - 65; + } + if (codePoint - 97 < 26) { + return codePoint - 97; + } + return base; + } + + /** + * Converts a digit/integer into a basic code point. + * @see `basicToDigit()` + * @private + * @param {Number} digit The numeric value of a basic code point. + * @returns {Number} The basic code point whose value (when used for + * representing integers) is `digit`, which needs to be in the range + * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is + * used; else, the lowercase form is used. The behavior is undefined + * if `flag` is non-zero and `digit` has no uppercase form. + */ + function digitToBasic(digit, flag) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); + } + + /** + * Bias adaptation function as per section 3.4 of RFC 3492. + * http://tools.ietf.org/html/rfc3492#section-3.4 + * @private + */ + function adapt(delta, numPoints, firstTime) { + var k = 0; + delta = firstTime ? floor(delta / damp) : delta >> 1; + delta += floor(delta / numPoints); + for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { + delta = floor(delta / baseMinusTMin); + } + return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); + } + + /** + * Converts a Punycode string of ASCII-only symbols to a string of Unicode + * symbols. + * @memberOf punycode + * @param {String} input The Punycode string of ASCII-only symbols. + * @returns {String} The resulting string of Unicode symbols. + */ + function decode(input) { + // Don't use UCS-2 + var output = [], + inputLength = input.length, + out, + i = 0, + n = initialN, + bias = initialBias, + basic, + j, + index, + oldi, + w, + k, + digit, + t, + /** Cached calculation results */ + baseMinusT; + + // Handle the basic code points: let `basic` be the number of input code + // points before the last delimiter, or `0` if there is none, then copy + // the first basic code points to the output. + + basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; + } + + for (j = 0; j < basic; ++j) { + // if it's not a basic code point + if (input.charCodeAt(j) >= 0x80) { + error('not-basic'); + } + output.push(input.charCodeAt(j)); + } + + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. + + for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { + + // `index` is the index of the next character to be consumed. + // Decode a generalized variable-length integer into `delta`, + // which gets added to `i`. The overflow checking is easier + // if we increase `i` as we go, then subtract off its starting + // value at the end to obtain `delta`. + for (oldi = i, w = 1, k = base; /* no condition */; k += base) { + + if (index >= inputLength) { + error('invalid-input'); + } + + digit = basicToDigit(input.charCodeAt(index++)); + + if (digit >= base || digit > floor((maxInt - i) / w)) { + error('overflow'); + } + + i += digit * w; + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + + if (digit < t) { + break; + } + + baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { + error('overflow'); + } + + w *= baseMinusT; + + } + + out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); + + // `i` was supposed to wrap around from `out` to `0`, + // incrementing `n` each time, so we'll fix that now: + if (floor(i / out) > maxInt - n) { + error('overflow'); + } + + n += floor(i / out); + i %= out; + + // Insert `n` at position `i` of the output + output.splice(i++, 0, n); + + } + + return ucs2encode(output); + } + + /** + * Converts a string of Unicode symbols (e.g. a domain name label) to a + * Punycode string of ASCII-only symbols. + * @memberOf punycode + * @param {String} input The string of Unicode symbols. + * @returns {String} The resulting Punycode string of ASCII-only symbols. + */ + function encode(input) { + var n, + delta, + handledCPCount, + basicLength, + bias, + j, + m, + q, + k, + t, + currentValue, + output = [], + /** `inputLength` will hold the number of code points in `input`. */ + inputLength, + /** Cached calculation results */ + handledCPCountPlusOne, + baseMinusT, + qMinusT; + + // Convert the input in UCS-2 to Unicode + input = ucs2decode(input); + + // Cache the length + inputLength = input.length; + + // Initialize the state + n = initialN; + delta = 0; + bias = initialBias; + + // Handle the basic code points + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue < 0x80) { + output.push(stringFromCharCode(currentValue)); + } + } + + handledCPCount = basicLength = output.length; + + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. + + // Finish the basic string - if it is not empty - with a delimiter + if (basicLength) { + output.push(delimiter); + } + + // Main encoding loop: + while (handledCPCount < inputLength) { + + // All non-basic code points < n have been handled already. Find the next + // larger one: + for (m = maxInt, j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } + + // Increase `delta` enough to advance the decoder's state to , + // but guard against overflow + handledCPCountPlusOne = handledCPCount + 1; + if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { + error('overflow'); + } + + delta += (m - n) * handledCPCountPlusOne; + n = m; + + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + + if (currentValue < n && ++delta > maxInt) { + error('overflow'); + } + + if (currentValue == n) { + // Represent delta as a generalized variable-length integer + for (q = delta, k = base; /* no condition */; k += base) { + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (q < t) { + break; + } + qMinusT = q - t; + baseMinusT = base - t; + output.push( + stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) + ); + q = floor(qMinusT / baseMinusT); + } + + output.push(stringFromCharCode(digitToBasic(q, 0))); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + delta = 0; + ++handledCPCount; + } + } + + ++delta; + ++n; + + } + return output.join(''); + } + + /** + * Converts a Punycode string representing a domain name or an email address + * to Unicode. Only the Punycoded parts of the input will be converted, i.e. + * it doesn't matter if you call it on a string that has already been + * converted to Unicode. + * @memberOf punycode + * @param {String} input The Punycoded domain name or email address to + * convert to Unicode. + * @returns {String} The Unicode representation of the given Punycode + * string. + */ + function toUnicode(input) { + return mapDomain(input, function(string) { + return regexPunycode.test(string) + ? decode(string.slice(4).toLowerCase()) + : string; + }); + } + + /** + * Converts a Unicode string representing a domain name or an email address to + * Punycode. Only the non-ASCII parts of the domain name will be converted, + * i.e. it doesn't matter if you call it with a domain that's already in + * ASCII. + * @memberOf punycode + * @param {String} input The domain name or email address to convert, as a + * Unicode string. + * @returns {String} The Punycode representation of the given domain name or + * email address. + */ + function toASCII(input) { + return mapDomain(input, function(string) { + return regexNonASCII.test(string) + ? 'xn--' + encode(string) + : string; + }); + } + + /*--------------------------------------------------------------------------*/ + + /** Define the public API */ + punycode = { + /** + * A string representing the current Punycode.js version number. + * @memberOf punycode + * @type String + */ + 'version': '1.3.2', + /** + * An object of methods to convert from JavaScript's internal character + * representation (UCS-2) to Unicode code points, and back. + * @see + * @memberOf punycode + * @type Object + */ + 'ucs2': { + 'decode': ucs2decode, + 'encode': ucs2encode + }, + 'decode': decode, + 'encode': encode, + 'toASCII': toASCII, + 'toUnicode': toUnicode + }; + + return punycode; +})(); diff --git a/src/js/querySelector.js b/src/js/querySelector.js index a2e5376..3a0dc90 100644 --- a/src/js/querySelector.js +++ b/src/js/querySelector.js @@ -57,7 +57,7 @@ const SafeAnimationFrame = class { } }; /** - * @type { import("../typings/types").isElem } + * @type { import("../typings/types.d.ts").isElem } */ const isElem = (obj) => { /** @type { string } */ @@ -65,7 +65,7 @@ const isElem = (obj) => { return s.includes('Element'); }; /** - * @type { import("../typings/types").normalizeTarget } + * @type { import("../typings/types.d.ts").normalizeTarget } */ const normalizeTarget = (target, toQuery = true, root) => { if (Object.is(target, null) || Object.is(target, undefined)) { @@ -320,7 +320,7 @@ dom.cl = class { /******************************************************************************/ /** - * @type { import("../typings/types").qsA } + * @type { import("../typings/types.d.ts").qsA } */ const qsA = (selectors, root) => { try { @@ -331,7 +331,7 @@ const qsA = (selectors, root) => { return []; }; /** - * @type { import("../typings/types").qs } + * @type { import("../typings/types.d.ts").qs } */ const qs = (selector, root) => { try { @@ -343,7 +343,7 @@ const qs = (selector, root) => { }; /** - * @type { import("../typings/WebExt").query } + * @type { import("../typings/WebExt.d.ts").query } */ const query = async (selector, root) => { let el = null; diff --git a/src/js/request-code.js b/src/js/request-code.js index e4fac3e..e0bdf5a 100644 --- a/src/js/request-code.js +++ b/src/js/request-code.js @@ -1,11 +1,9 @@ 'use strict'; +import { err } from './logger.js'; import Network from './network.js'; -import { intersect, isNull, isEmpty, normalizeTarget } from './util.js'; -import { language } from './language.js'; +import { isNull, isBlank, isEmpty, union } from './util.js'; -const META_START_COMMENT = '// ==UserScript=='; -const META_END_COMMENT = '// ==/UserScript=='; const TLD_EXPANSION = ['com', 'net', 'org', 'de', 'co.uk']; const APPLIES_TO_ALL_PATTERNS = [ 'http://*', @@ -19,237 +17,288 @@ const APPLIES_TO_ALL_PATTERNS = [ '*://*/*', 'http*' ]; -/** - * @param { string } code - */ -const get_meta_block = (code) => { - if (isEmpty(code)) { - return null; - } - const start_block = code.indexOf(META_START_COMMENT); - if (isNull(start_block)) { - return null; - } - const end_block = code.indexOf(META_END_COMMENT, start_block); - if (isNull(end_block)) { - return null; - } - return code.substring(start_block + META_START_COMMENT.length, end_block); -}; -/** - * @param { string } code - */ -const parse_meta = (code) => { - if (isEmpty(code)) { - return null; - } - const meta = {}; - const meta_block = get_meta_block(code); - const meta_block_map = new Map(); - for (const meta_line of meta_block.split('\n')) { - const meta_match = meta_line.match(/\/\/\s+@([a-zA-Z:-]+)\s+(.*)/); - if (isNull(meta_match)) { - continue; - } - const key = meta_match[1].trim(); - const value = meta_match[2].trim(); - if (!meta_block_map.has(key)) { - meta_block_map.set(key, []); - } - const meta_map = meta_block_map.get(key); - meta_map.push(value); - meta_block_map.set(key, meta_map); +class ParseUserJS { + /** + * @type { string } + */ + code; + /** + * @type { string } + */ + data_meta_block; + /** + * @type { string } + */ + data_code_block; + /** + * @type { { [meta: string]: string | string[] | { [resource: string]: string } } } + */ + data_meta; + /** + * @type { {text: string;domain: boolean;tld_extra: boolean}[] } + */ + data_names; + constructor(code, isUserCSS) { + this.isUserCSS = isUserCSS ?? false; + this.META_START_COMMENT = this.isUserCSS ? '/* ==UserStyle==' : '// ==UserScript=='; + this.META_END_COMMENT = this.isUserCSS ? '==/UserStyle== */' : '// ==/UserScript=='; + if (code) { + this.code = code; + this.get_meta_block(); + this.get_code_block(); + this.parse_meta(); + this.calculate_applies_to_names(); + } } - for (const [key, value] of meta_block_map) { - if (value.length > 1) { - meta[key] = value; - } else { - meta[key] = value[0]; + get_meta_block() { + if (isEmpty(this.code)) { + return null; + } + if (this.data_meta_block) { + return this.data_meta_block; + } + const start_block = this.code.indexOf(this.META_START_COMMENT); + if (isNull(start_block)) { + return null; + } + const end_block = this.code.indexOf(this.META_END_COMMENT, start_block); + if (isNull(end_block)) { + return null; } + const meta_block = this.code.substring(start_block + this.META_START_COMMENT.length, end_block); + this.data_meta_block = meta_block; + return this.data_meta_block; } - return meta; -}; -/** - * @template { string } S - * @param { S } code - * @returns { S[] } - */ -const calculate_applies_to_names = (code) => { - if (isEmpty(code)) { - return null; + get_code_block() { + if (isEmpty(this.code)) { + return null; + } + if (this.data_code_block) { + return this.data_code_block; + } + const start_block = this.code.indexOf(this.META_START_COMMENT); + if (isNull(start_block)) { + return null; + } + const end_block = this.code.indexOf(this.META_END_COMMENT, start_block); + if (isNull(end_block)) { + return null; + } + const code_block = this.code.substring( + end_block + this.META_END_COMMENT.length, + this.code.length + ); + this.data_code_block = code_block + .split('\n') + .filter((l) => !isEmpty(l)) + .join('\n'); + return this.data_code_block; } - const meta = parse_meta(code); - let patterns = []; - for (const [k, v] of Object.entries(meta)) { - if (/include|match/.test(k)) { - if (Array.isArray(v)) { - patterns = patterns.concat(v); + parse_meta() { + if (isEmpty(this.code)) { + return null; + } + if (this.data_meta) { + return this.data_meta; + } + const meta = {}; + const meta_block_map = new Map(); + for (const meta_line of this.get_meta_block().split('\n')) { + const meta_match = /\/\/\s+@([a-zA-Z:-]+)\s+(.*)/.exec(meta_line); + if (!meta_match) { + continue; + } + const key = meta_match[1].trim(); + const value = meta_match[2].trim(); + if (!meta_block_map.has(key)) { + meta_block_map.set(key, []); + } + const meta_map = meta_block_map.get(key); + meta_map.push(value); + meta_block_map.set(key, meta_map); + } + for (const [key, value] of meta_block_map) { + if (value.length > 1) { + meta[key] = value; } else { - patterns = patterns.concat([v]); + meta[key] = value[0]; } } + this.data_meta = meta; + return this.data_meta; } - if (isEmpty(patterns)) { - return []; - } - if (intersect(patterns, APPLIES_TO_ALL_PATTERNS)) { - return []; - } - const name_set = new Set(); - const addObj = (obj) => { - if (name_set.has(obj)) { - return; - } - name_set.add(obj); - }; - for (let p of patterns) { - try { - const original_pattern = p; - let pre_wildcards = []; - if (p.match(/^\/(.*)\/$/)) { - pre_wildcards = [p]; - } else { - let m = p.match(/^\*(https?:.*)/i); - if (!isNull(m)) { - p = m[1]; - } - p = p - .replace(/^\*:/i, 'http:') - .replace(/^\*\/\//i, 'http://') - .replace(/^http\*:/i, 'http:') - .replace(/^(https?):([^/])/i, '$1://$2'); - m = p.match(/^([a-z]+:\/\/)\*\.?([a-z0-9-]+(?:.[a-z0-9-]+)+.*)/i); - if (!isNull(m)) { - p = m[1] + m[2]; - } - m = p.match(/^\*\.?([a-z0-9-]+\.[a-z0-9-]+.*)/i); - if (!isNull(m)) { - p = `http://${m[1]}`; + calculate_applies_to_names() { + if (isEmpty(this.code)) { + return null; + } + if (this.data_names) { + return this.data_names; + } + let patterns = []; + for (const [k, v] of Object.entries(this.parse_meta())) { + if (/include|match/i.test(k)) { + if (Array.isArray(v)) { + patterns = patterns.concat(v); + } else { + patterns = patterns.concat([v]); } - m = p.match(/^http\*(?:\/\/)?\.?((?:[a-z0-9-]+)(?:\.[a-z0-9-]+)+.*)/i); - if (!isNull(m)) { - p = `http://${m[1]}`; + } + } + if (isEmpty(patterns)) { + return []; + } + if (this.intersect(patterns, APPLIES_TO_ALL_PATTERNS)) { + this.data_names = [ + { + domain: false, + text: 'All sites', + tld_extra: false } - m = p.match(/^([a-z]+:\/\/([a-z0-9-]+(?:\.[a-z0-9-]+)*\.))\*(.*)/); - if (!isNull(m)) { - if (m[2].match(/A([0-9]+\.){2,}z/)) { - p = `${m[1]}tld${m[3]}`; - pre_wildcards = [p.split('*')[0]]; + ]; + return this.data_names; + } + this.data_names = ParseUserJS.getNames(patterns); + return this.data_names; + } + intersect(a, ...arr) { + return !isBlank([...new Set(a)].filter((v) => arr.every((b) => b.includes(v)))); + } + static getNames(patterns = []) { + const name_map = new Map(); + const addObj = (obj) => { + if (name_map.has(obj.text)) { + return; + } + name_map.set(obj.text, obj); + }; + for (let p of patterns) { + try { + const original_pattern = p; + let pre_wildcards = []; + if (p.match(/^\/(.*)\/$/)) { + pre_wildcards = [p]; + } else { + let m = /^\*(https?:.*)/i.exec(p); + if (m) { + p = m[1]; + } + p = p + .replace(/^\*:/i, 'http:') + .replace(/^\*\/\//i, 'http://') + .replace(/^http\*:/i, 'http:') + .replace(/^(https?):([^/])/i, '$1://$2'); + m = /^([a-z]+:\/\/)\*\.?([a-z0-9-]+(?:.[a-z0-9-]+)+.*)/i.exec(p); + if (m) { + p = m[1] + m[2]; + } + m = /^\*\.?([a-z0-9-]+\.[a-z0-9-]+.*)/i.exec(p); + if (m) { + p = `http://${m[1]}`; + } + m = /^http\*(?:\/\/)?\.?((?:[a-z0-9-]+)(?:\.[a-z0-9-]+)+.*)/i.exec(p); + if (m) { + p = `http://${m[1]}`; + } + m = /^([a-z]+:\/\/([a-z0-9-]+(?:\.[a-z0-9-]+)*\.))\*(.*)/.exec(p); + if (m) { + if (m[2].match(/A([0-9]+\.){2,}z/)) { + p = `${m[1]}tld${m[3]}`; + pre_wildcards = [p.split('*')[0]]; + } else { + pre_wildcards = [p]; + } } else { pre_wildcards = [p]; } - } else { - pre_wildcards = [p]; } - } - for (const pre_wildcard of pre_wildcards) { - try { - const uri = new URL(pre_wildcard); - if (isNull(uri.host)) { - addObj({ text: original_pattern, domain: false, tld_extra: false }); - } else if (!uri.host.includes('.') && uri.host.includes('*')) { - addObj({ text: original_pattern, domain: false, tld_extra: false }); - } else if (uri.host.endsWith('.tld')) { - for (let i = 0; i < TLD_EXPANSION.length; i++) { - const tld = TLD_EXPANSION[i]; + for (const pre_wildcard of pre_wildcards) { + try { + const urlObj = new URL(pre_wildcard); + const { host } = urlObj; + if (isNull(host)) { + addObj({ text: original_pattern, domain: false, tld_extra: false }); + } else if (!host.includes('.') && host.includes('*')) { + addObj({ text: original_pattern, domain: false, tld_extra: false }); + } else if (host.endsWith('.tld')) { + for (let i = 0; i < TLD_EXPANSION.length; i++) { + const tld = TLD_EXPANSION[i]; + addObj({ + text: host.replace(/tld$/i, tld), + domain: true, + tld_extra: i != 0 + }); + } + } else if (host.endsWith('.')) { + addObj({ + text: host.slice(0, -1), + domain: true, + tld_extra: false + }); + } else { addObj({ - text: uri.host.replace(/tld$/i, tld), + text: host, domain: true, - tld_extra: i != 0 + tld_extra: false }); } - } else if (uri.host.endsWith('.')) { - addObj({ - text: uri.host.slice(0, -1), - domain: true, - tld_extra: false - }); - } else { - addObj({ - text: uri.host, - domain: true, - tld_extra: false - }); + // eslint-disable-next-line no-unused-vars + } catch (ex) { + addObj({ text: original_pattern, domain: false, tld_extra: false }); } - // eslint-disable-next-line no-unused-vars - } catch (error) { - addObj({ text: original_pattern, domain: false, tld_extra: false }); } + } catch (ex) { + err(ex); } - } catch (ex) { - console.error(ex); } + return [...name_map.values()]; } - return [...name_set]; -}; -const reqCode = async (obj = {}, translate = false) => { - if (obj.code_data) { - return obj.code_data; - } - /** @type { string } */ - const code = await Network.req(obj.code_url, 'GET', 'text'); - if (typeof code !== 'string') { - return; - } - Object.assign(obj, { - code_data: code, - code_meta: {}, - code_size: [Network.format(code.length)], - code_match: [], - code_grant: [], - antifeatures: [] - }); - const grantSet = new Set(); - const afSet = new Set(); - const meta = parse_meta(code); - const applies_to_names = calculate_applies_to_names(code); - - if (translate) { - for (const lng of language.cache) { - if (meta[`name:${lng}`]) { - Object.assign(obj, { - name: meta[`name:${lng}`], - translated: true - }); - } - if (meta[`description:${lng}`]) { - Object.assign(obj, { - description: meta[`description:${lng}`], - translated: true - }); - } + async request(code_url) { + if (this.data_code_block) { + return this; } - } + /** @type { string } */ + const code = await Network.req(code_url, 'GET', 'text').catch(err); + if (typeof code !== 'string') { + return this; + } + this.isUserCSS = /\.user\.css$/.test(code_url); + this.META_START_COMMENT = this.isUserCSS ? '/* ==UserStyle==' : '// ==UserScript=='; + this.META_END_COMMENT = this.isUserCSS ? '==/UserStyle== */' : '// ==/UserScript=='; + + this.code = code; + this.get_meta_block(); + this.get_code_block(); + this.parse_meta(); + this.calculate_applies_to_names(); - for (const [key, value] of Object.entries(meta)) { - if (/grant/.test(key)) { - for (const v of normalizeTarget(value, false)) { - if (grantSet.has(v)) { - continue; + const { data_meta } = this; + if (Array.isArray(data_meta.grant)) { + data_meta.grant = union(data_meta.grant); + } + if (data_meta.resource) { + const obj = {}; + if (typeof data_meta.resource === 'string') { + const reg = /(.+)\s+(.+)/.exec(data_meta.resource); + if (reg) { + obj[reg[1].trim()] = reg[2]; } - grantSet.add(v); - } - } else if (/antifeature/.test(key)) { - for (const v of normalizeTarget(value, false)) { - if (afSet.has(v)) { - continue; + } else { + for (const r of data_meta.resource) { + const reg = /(.+)\s+(http.+)/.exec(r); + if (reg) { + obj[reg[1].trim()] = reg[2]; + } } - afSet.add(v); } + data_meta.resource = obj; } + Object.assign(this, { + code_size: [Network.format(code.length)], + meta: data_meta + }); + + return this; } - Object.assign(obj, { - code_meta: meta, - code_match: applies_to_names, - code_grant: [...grantSet], - antifeatures: [...afSet] - }); - return code; -}; +} -export { - calculate_applies_to_names, - get_meta_block, - parse_meta, - reqCode -} \ No newline at end of file +export { ParseUserJS }; diff --git a/src/js/settings.js b/src/js/settings.js deleted file mode 100644 index c4d51cc..0000000 --- a/src/js/settings.js +++ /dev/null @@ -1,294 +0,0 @@ -'use strict'; -import { err } from './logger.js'; -import { XMap } from './XMap.js'; -import { i18n$ } from './i18n.js'; -import { dom, qs } from './querySelector.js'; -import { ael, make, isNull, isEmpty } from './util.js'; - -let cfg = {}; - -const { hermes } = userjs; - -const renderTheme = (theme) => { - theme = theme || cfg.theme; - if (isEmpty(theme)) { - return; - } - // if (theme === defcfg.theme) { - // return; - // } - const sty = qs('mujs-root').style; - for (const [k, v] of Object.entries(theme)) { - const str = `--mujs-${k}`; - const prop = sty.getPropertyValue(str); - if (isEmpty(v)) { - theme[k] = prop; - } - if (prop === v) { - continue; - } - sty.removeProperty(str); - sty.setProperty(str, v); - } -}; - -const cfgMap = new XMap(); - -//#region Make Config -const cfgpage = qs('form'); -const makecfg = () => { - const exBtn = make('mu-js', 'mujs-sty-flex'); - const exportCFG = make('mujs-btn', 'mujs-export', { - textContent: i18n$('export_config'), - dataset: { - command: 'export-cfg' - } - }); - const importCFG = make('mujs-btn', 'mujs-import', { - textContent: i18n$('import_config'), - dataset: { - command: 'import-cfg' - } - }); - const exportTheme = make('mujs-btn', 'mujs-export', { - textContent: i18n$('export_theme'), - dataset: { - command: 'export-theme' - } - }); - const importTheme = make('mujs-btn', 'mujs-import', { - textContent: i18n$('import_theme'), - dataset: { - command: 'import-theme' - } - }); - exBtn.append(importCFG, importTheme, exportCFG, exportTheme); - cfgpage.append(exBtn); - - const makerow = (desc, type = null, nm, attrs = {}) => { - desc = desc ?? i18n$('no_license'); - nm = nm ?? i18n$('no_license'); - const sec = make('mujs-section', 'mujs-cfg-section', { - style: nm === 'cache' ? 'display: none;' : '' - }); - const lb = make('label'); - const divDesc = make('mu-js', 'mujs-cfg-desc', { - textContent: desc - }); - lb.append(divDesc); - sec.append(lb); - cfgpage.append(sec); - if (isNull(type)) { - return lb; - } - const inp = make('input', 'mujs-cfg-input', { - type, - dataset: { - name: nm - }, - ...attrs - }); - if (!cfgMap.has(nm)) { - cfgMap.set(nm, inp); - } - if (type === 'checkbox') { - const inlab = make('mu-js', 'mujs-inlab'); - const la = make('label', '', { - onclick() { - inp.dispatchEvent(new MouseEvent('click')); - } - }); - inlab.append(inp, la); - lb.append(inlab); - if (nm.includes('-')) { - return inp; - } - if (/(greasy|sleazy)fork|openuserjs|gi(thub|st)/gi.test(nm)) { - for (const i of cfg.engines) { - if (i.name !== nm) continue; - inp.checked = i.enabled; - inp.dataset.engine = i.name; - ael(inp, 'change', (evt) => { - // MUJS.unsaved = true; - // MUJS.rebuild = true; - i.enabled = evt.target.checked; - hermes.send('Save', { - prop: 'engines', - value: cfg.engines - }); - }); - } - } else { - inp.checked = cfg[nm]; - ael(inp, 'change', (evt) => { - // MUJS.unsaved = true; - // if (/filterlang/i.test(nm)) { - // MUJS.rebuild = true; - // } - cfg[nm] = evt.target.checked; - hermes.send('Save', { - prop: nm, - value: cfg[nm] - }); - }); - } - } else { - lb.append(inp); - } - return inp; - }; - makerow('Sync with GM', 'checkbox', 'cache'); - makerow(i18n$('redirect'), 'checkbox', 'sleazyredirect'); - makerow(i18n$('filter'), 'checkbox', 'filterlang'); - makerow(i18n$('preview_code'), 'checkbox', 'codePreview'); - for (const inp of [ - makerow('Recommend author', 'checkbox', 'recommend-author'), - makerow('Recommend scripts', 'checkbox', 'recommend-others') - ]) { - const nm = inp.dataset.name === 'recommend-author' ? 'author' : 'others'; - inp.checked = cfg.recommend[nm]; - ael(inp, 'change', (evt) => { - // MUJS.unsaved = true; - cfg.recommend[nm] = evt.target.checked; - hermes.send('Save', { - prop: 'recommend', - value: cfg.recommend - }); - }); - } - makerow('Greasy Fork', 'checkbox', 'greasyfork'); - makerow('Sleazy Fork', 'checkbox', 'sleazyfork'); - makerow('Open UserJS', 'checkbox', 'openuserjs'); - makerow('GitHub API', 'checkbox', 'github'); - const ghAPI = cfg.engines.find((c) => c.name === 'github'); - const ghToken = makerow('GitHub API (Token)', 'text', 'github', { - defaultValue: '', - value: ghAPI.token ?? '', - placeholder: 'Paste Access Token' - // onchange(evt) { - // MUJS.unsaved = true; - // MUJS.rebuild = true; - // if (isNull(legacyMsg)) { - // ghAPI.token = evt.target.value; - // } - // } - }); - ghToken.dataset.engine = 'github-token'; - cfgMap.set('github-token', ghToken); - makerow(`${i18n$('dtime')} (ms)`, 'number', 'time', { - defaultValue: 10000, - value: cfg.time, - min: 0, - step: 500, - onbeforeinput(evt) { - if (evt.target.validity.badInput) { - dom.cl.add(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', true); - } else { - dom.cl.remove(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', false); - } - }, - oninput(evt) { - // MUJS.unsaved = true; - const t = evt.target; - if (t.validity.badInput || (t.validity.rangeUnderflow && t.value !== '-1')) { - dom.cl.add(t, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', true); - } else { - dom.cl.remove(t, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', false); - cfg.time = isEmpty(t.value) ? cfg.time : parseFloat(t.value); - } - } - }); - - const cbtn = make('mu-js', 'mujs-sty-flex'); - const savebtn = make('mujs-btn', 'save', { - textContent: i18n$('save'), - dataset: { - command: 'save' - }, - disabled: false - }); - const resetbtn = make('mujs-btn', 'reset', { - textContent: i18n$('reset'), - dataset: { - command: 'reset' - } - }); - const blacklist = make('textarea', '', { - dataset: { - name: 'blacklist' - }, - rows: '10', - autocomplete: false, - spellcheck: false, - wrap: 'soft', - value: JSON.stringify(cfg.blacklist, null, ' '), - oninput(evt) { - let isvalid = true; - try { - cfg.blacklist = JSON.parse(evt.target.value); - isvalid = true; - } catch (ex) { - err(ex); - isvalid = false; - } finally { - if (isvalid) { - dom.cl.remove(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', false); - } else { - dom.cl.add(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', true); - } - } - } - }); - const theme = make('textarea', '', { - dataset: { - name: 'theme' - }, - rows: '10', - autocomplete: false, - spellcheck: false, - wrap: 'soft', - value: JSON.stringify(cfg.theme, null, ' '), - oninput(evt) { - let isvalid = true; - try { - cfg.theme = JSON.parse(evt.target.value); - isvalid = true; - renderTheme(JSON.parse(evt.target.value)); - } catch (ex) { - err(ex); - isvalid = false; - } finally { - if (isvalid) { - dom.cl.remove(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', false); - } else { - dom.cl.add(evt.target, 'mujs-invalid'); - dom.prop(savebtn, 'disabled', true); - } - } - } - }); - cfgMap.set('blacklist', blacklist); - cfgMap.set('theme', theme); - cbtn.append(resetbtn, savebtn); - cfgpage.append(blacklist, theme, cbtn); -}; -//#endregion - -/** - * @type {chrome.runtime.Port | browser.runtime.Port | null} - */ -const p = hermes.getPort(); -p.onMessage.addListener((root = {}) => { - const m = root.msg; - if (root.channel === 'Config' && isEmpty(cfg)) { - cfg = m.cfg || cfg; - makecfg(); - } -}); diff --git a/src/js/storage.js b/src/js/storage.js index 3b990c6..19b5300 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -1,7 +1,117 @@ 'use strict'; +import { webext } from './ext.js'; + let api = webext.storage.local; +// #region DEFAULT_CONFIG +/** + * @type { import("../typings/types.d.ts").config } + */ +export const DEFAULT_CONFIG = { + autofetch: true, + // autoinject: true, + autoSort: 'daily_installs', + clearTabCache: true, + // cache: true, + // autoexpand: false, + filterlang: false, + sleazyredirect: false, + // time: 10000, + blacklist: ['userjs-local', 'userjs-finance', 'userjs-social', 'userjs-unsupported'], + preview: { + code: false, + metadata: false + }, + engines: [ + { + enabled: true, + name: 'greasyfork', + query: encodeURIComponent('https://greasyfork.org/scripts/by-site/{host}.json?language=all') + }, + { + enabled: false, + name: 'sleazyfork', + query: encodeURIComponent('https://sleazyfork.org/scripts/by-site/{host}.json?language=all') + }, + // { + // enabled: false, + // name: 'openuserjs', + // query: encodeURIComponent('https://openuserjs.org/?q={host}') + // }, + { + enabled: false, + name: 'github', + token: '', + query: encodeURIComponent( + 'https://api.github.com/search/code?q="// ==UserScript=="+{host}+ "// ==/UserScript=="+in:file+language:js&per_page=30' + ) + } + ], + theme: { + 'even-row': '', + 'odd-row': '', + 'even-err': '', + 'odd-err': '', + 'background-color': '', + 'gf-color': '', + 'sf-color': '', + 'border-b-color': '', + 'gf-btn-color': '', + 'sf-btn-color': '', + 'sf-txt-color': '', + 'txt-color': '', + 'chck-color': '', + 'chck-gf': '', + 'chck-git': '', + 'chck-open': '', + placeholder: '', + 'position-top': '', + 'position-bottom': '', + 'position-left': '', + 'position-right': '', + 'font-family': '' + }, + recommend: { + author: true, + others: true + }, + filters: { + ASCII: { + enabled: false, + name: 'Non-ASCII', + regExp: '[^\\x00-\\x7F\\s]+' + }, + Latin: { + enabled: false, + name: 'Non-Latin', + regExp: '[^\\u0000-\\u024F\\u2000-\\u214F\\s]+' + }, + Games: { + enabled: false, + name: 'Games', + flag: 'iu', + regExp: + 'Aimbot|AntiGame|Agar|agar\\.io|alis\\.io|angel\\.io|ExtencionRipXChetoMalo|AposBot|DFxLite|ZTx-Lite|AposFeedingBot|AposLoader|Balz|Blah Blah|Orc Clan Script|Astro\\s*Empires|^\\s*Attack|^\\s*Battle|BiteFight|Blood\\s*Wars|Bloble|Bonk|Bots|Bots4|Brawler|\\bBvS\\b|Business\\s*Tycoon|Castle\\s*Age|City\\s*Ville|chopcoin\\.io|Comunio|Conquer\\s*Club|CosmoPulse|cursors\\.io|Dark\\s*Orbit|Dead\\s*Frontier|Diep\\.io|\\bDOA\\b|doblons\\.io|DotD|Dossergame|Dragons\\s*of\\s*Atlantis|driftin\\.io|Dugout|\\bDS[a-z]+\\n|elites\\.io|Empire\\s*Board|eRep(ublik)?|Epicmafia|Epic.*War|ExoPlanet|Falcon Tools|Feuerwache|Farming|FarmVille|Fightinfo|Frontier\\s*Ville|Ghost\\s*Trapper|Gladiatus|Goalline|Gondal|gota\\.io|Grepolis|Hobopolis|\\bhwm(\\b|_)|Ikariam|\\bIT2\\b|Jellyneo|Kapi\\s*Hospital|Kings\\s*Age|Kingdoms?\\s*of|knastv(o|oe)gel|Knight\\s*Fight|\\b(Power)?KoC(Atta?ck)?\\b|\\bKOL\\b|Kongregate|Krunker|Last\\s*Emperor|Legends?\\s*of|Light\\s*Rising|lite\\.ext\\.io|Lockerz|\\bLoU\\b|Mafia\\s*(Wars|Mofo)|Menelgame|Mob\\s*Wars|Mouse\\s*Hunt|Molehill\\s*Empire|MooMoo|MyFreeFarm|narwhale\\.io|Neopets|NeoQuest|Nemexia|\\bOGame\\b|Ogar(io)?|Pardus|Pennergame|Pigskin\\s*Empire|PlayerScripts|pokeradar\\.io|Popmundo|Po?we?r\\s*(Bot|Tools)|PsicoTSI|Ravenwood|Schulterglatze|Skribbl|slither\\.io|slitherplus\\.io|slitheriogameplay|SpaceWars|splix\\.io|Survivio|\\bSW_[a-z]+\\n|\\bSnP\\b|The\\s*Crims|The\\s*West|torto\\.io|Travian|Treasure\\s*Isl(and|e)|Tribal\\s*Wars|TW.?PRO|Vampire\\s*Wars|vertix\\.io|War\\s*of\\s*Ninja|World\\s*of\\s*Tanks|West\\s*Wars|wings\\.io|\\bWoD\\b|World\\s*of\\s*Dungeons|wtf\\s*battles|Wurzelimperium|Yohoho|Zombs' + }, + SocialNetworks: { + enabled: false, + name: 'Social Networks', + flag: 'iu', + regExp: + 'Face\\s*book|Google(\\+| Plus)|\\bHabbo|Kaskus|\\bLepra|Leprosorium|MySpace|meinVZ|odnoklassniki|Одноклассники|Orkut|sch(ue|ü)ler(VZ|\\.cc)?|studiVZ|Unfriend|Valenth|VK|vkontakte|ВКонтакте|Qzone|Twitter|TweetDeck' + }, + Clutter: { + enabled: false, + name: 'Clutter', + flag: 'iu', + regExp: + "^\\s*(.{1,3})\\1+\\n|^\\s*(.+?)\\n+\\2\\n*$|^\\s*.{1,5}\\n|do\\s*n('|o)?t (install|download)|nicht installieren|(just )?(\\ban? |\\b)test(ing|s|\\d|\\b)|^\\s*.{0,4}test.{0,4}\\n|\\ntest(ing)?\\s*|^\\s*(\\{@|Smolka|Hacks)|\\[\\d{4,5}\\]|free\\s*download|theme|(night|dark) ?(mode)?" + } + } +}; +// #endregion + /** @type {{ [prefix: string]: StorageArea }} */ export const storageByPrefix = {}; @@ -56,9 +166,7 @@ class StorageArea { async getMulti(ids, transform) { const keys = ids?.map(this.toKey, this); const data = await api.get(keys); - return transform || this.prefix - ? mapEntry.call(data, transform, 'toId', this) - : data; + return transform || this.prefix ? mapEntry.call(data, transform, 'toId', this) : data; } /** @@ -92,10 +200,10 @@ const storage = { api = val; }, /** @return {?StorageArea} */ - forKey: key => storageByPrefix[/^\w+:|$/.exec(key)[0]], + forKey: (key) => storageByPrefix[/^\w+:|$/.exec(key)[0]], base: new StorageArea('base', ''), config: new StorageArea('config', 'cfg:'), - cache: new StorageArea('cache', 'cac:'), + cache: new StorageArea('cache', 'cac:') }; export default storage; diff --git a/src/js/util.js b/src/js/util.js index bb22e94..e890117 100644 --- a/src/js/util.js +++ b/src/js/util.js @@ -1,35 +1,33 @@ 'use strict'; + +import { webext } from './ext.js'; import { err } from './logger.js'; // #region Utilities -let isMob; -// const isMobile = /Mobile|Tablet/.test(navigator.userAgent); +const userjs = {}; const getUAData = () => { - if (typeof isMob !== 'undefined') { - return isMob; + if (userjs.isMobile !== undefined) { + return userjs.isMobile; } try { - const { platform, mobile } = navigator.userAgentData ?? {}; - isMob = - /Mobile|Tablet/.test(navigator.userAgent ?? '') || - mobile || - /Android|Apple/.test(platform ?? ''); + if (navigator) { + const { userAgent, userAgentData } = navigator; + const { platform, mobile } = userAgentData ? Object(userAgentData) : {}; + userjs.isMobile = + /Mobile|Tablet/.test(userAgent ? String(userAgent) : '') || + Boolean(mobile) || + /Android|Apple/.test(platform ? String(platform) : ''); + } else { + userjs.isMobile = false; + } } catch (ex) { - err({ cause: 'getUAData', message: ex.message }); - isMob = false; + userjs.isMobile = false; + ex.cause = 'getUAData'; + err(ex); } - return isMob; + return userjs.isMobile; }; const isMobile = getUAData(); -/** - * @type { import("../typings/types").hasOwn } - */ -const hasOwn = (obj, prop) => { - if (typeof Object.hasOwn !== 'undefined') { - return Object.hasOwn(obj, prop); - } - return Object.prototype.hasOwnProperty.call(obj, prop); -}; const isString = (val) => typeof val === 'string'; /** * @type { import("../typings/types").objToStr } @@ -38,16 +36,21 @@ const objToStr = (obj) => { return Object.prototype.toString.call(obj); }; /** - * @type { import("../typings/types").strToURL } + * @template {string | URL} S + * @param {S} str */ const strToURL = (str) => { + let url; try { - str = str ?? window.location ?? 'about:blank'; - return objToStr(str).includes('URL') ? str : new URL(str); + url = objToStr(str).includes('URL') ? str : new URL(str); } catch (ex) { - err({ cause: 'strToURL', message: ex.message }); - return window.location; + ex.cause = 'strToURL'; + err(ex); + } + if (url !== undefined) { + return url } + return str; }; /** * @type { import("../typings/types").isRegExp } @@ -119,7 +122,7 @@ const normalizeTarget = (target, toQuery = true, root) => { return Array.from(target); }; /** - * @type { import("../typings/types").ael } + * @type { import("../typings/types.d.ts").ael } */ const ael = (el, type, listener, options = {}) => { try { @@ -134,11 +137,12 @@ const ael = (el, type, listener, options = {}) => { elem.addEventListener(type, listener, options); } } catch (ex) { + ex.cause = 'ael'; err(ex); } }; /** - * @type { import("../typings/types").formAttrs } + * @type { import("../typings/types.d.ts").formAttrs } */ const formAttrs = (elem, attr = {}) => { if (!elem) { @@ -162,29 +166,28 @@ const formAttrs = (elem, attr = {}) => { return elem; }; /** - * @type { import("../typings/types").make } + * @type { import("../typings/types.d.ts").make } */ const make = (tagName, cname, attrs) => { let el; try { - // el = - // tagName === 'fragment' ? document.createDocumentFragment() : document.createElement(tagName); el = document.createElement(tagName); if (!isEmpty(cname)) { - if (isString(cname)) { + if (typeof cname === 'string') { el.className = cname; } else if (isObj(cname)) { formAttrs(el, cname); } } if (!isEmpty(attrs)) { - if (isString(attrs)) { + if (typeof attrs === 'string') { el.textContent = attrs; } else if (isObj(attrs)) { formAttrs(el, attrs); } } } catch (ex) { + ex.cause = 'make'; err(ex); } return el; @@ -198,57 +201,119 @@ const openInTab = async (url) => { const newTab = await webext.tabs.create({ url }); return newTab; }; -const intersect = (a = [], b = []) => { - for (const v of a) { - if (b.includes(v)) { +const union = (...arr) => [...new Set(arr.flat())]; +const loadFilters = (cfg) => { + /** @type {Map} */ + const pool = new Map(); + const handles = { + pool, + enabled() { + return [...pool.values()].filter((o) => o.enabled); + }, + refresh() { + if (!Object.is(pool.size, 0)) pool.clear(); + for (const [key, value] of Object.entries(cfg.filters)) { + if (!pool.has(key)) + pool.set(key, { + ...value, + reg: new RegExp(value.regExp, value.flag), + keyReg: new RegExp(key.trim().toLocaleLowerCase(), 'gi'), + valueReg: new RegExp(value.name.trim().toLocaleLowerCase(), 'gi') + }); + } + return this; + }, + get(str) { + return [...pool.values()].find((v) => v.keyReg.test(str) || v.valueReg.test(str)); + }, + /** + * @param { import("../typings/types.d.ts").GSForkQuery } param0 + */ + match({ name, users }) { + const p = handles.enabled(); + if (Object.is(p.length, 0)) return true; + for (const v of p) { + if ([{ name }, ...users].find((o) => o.name.match(v.reg))) return false; + } return true; } + }; + for (const [key, value] of Object.entries(cfg.filters)) { + if (!pool.has(key)) + pool.set(key, { + ...value, + reg: new RegExp(value.regExp, value.flag), + keyReg: new RegExp(key.trim().toLocaleLowerCase(), 'gi'), + valueReg: new RegExp(value.name.trim().toLocaleLowerCase(), 'gi') + }); } - for (const v of b) { - if (a.includes(v)) { - return true; + return handles.refresh(); +}; +/** + * @param {string} txt + */ +const formatURL = (txt) => + txt + .split('.') + .splice(-2) + .join('.') + .replace(/\/|https:/g, ''); +const matchesFromHostnames = (hostnames) => { + const out = []; + for (const hn of hostnames) { + if (hn === '*' || hn === 'all-urls') { + out.length = 0; + out.push(''); + break; } + out.push(`*://*.${hn}/*`); } - return false; + return out; }; -const template = { - data: { - id: 0, - bad_ratings: 0, - good_ratings: 0, - ok_ratings: 0, - daily_installs: 0, - total_installs: 0, - name: 'NOT FOUND', - description: 'NOT FOUND', - version: '0.0.0', - url: 'about:blank', - code_url: 'about:blank', - created_at: Date.now(), - code_updated_at: Date.now(), - users: [ - { - name: '', - url: '' - } - ] - }, - merge(obj = {}) { - for (const key in template.data) { - if (hasOwn(obj, key)) continue; - obj[key] = template.data[key]; +/** + * @param {string[]} origins + */ +const hostnamesFromMatches = (origins) => { + const out = []; + for (const origin of origins) { + if (origin === '') { + out.push('all-urls'); + continue; + } + const match = /^\*:\/\/(?:\*\.)?([^/]+)\/\*/.exec(origin); + if (match === null) { + continue; } - return obj; + out.push(match[1]); + } + return out; +}; +/** + * @param {string} hn + */ +const normalizedHostname = (hn) => { + return hn.replace(/^www\./, ''); +}; +/** + * @param {string} str + */ +const decode = (str) => { + try { + if (decodeURI(str) !== decodeURIComponent(str)) { + return decode(decodeURIComponent(str)); + } + } catch (ex) { + err(ex); } + return str; }; // #endregion export { - isMobile, - hasOwn, + decode, + formatURL, objToStr, strToURL, - intersect, isRegExp, isElem, isObj, @@ -262,5 +327,9 @@ export { formAttrs, make, openInTab, - template + union, + loadFilters, + matchesFromHostnames, + hostnamesFromMatches, + normalizedHostname }; diff --git a/src/manifest/chrome.json b/src/manifest/chrome.json index 5581779..78eefbe 100644 --- a/src/manifest/chrome.json +++ b/src/manifest/chrome.json @@ -1,38 +1,41 @@ { - "manifest_version": 2, + "manifest_version": 3, "default_locale": "en", "name": "__MSG_extName__", "description": "__MSG_extShortDesc__", "short_name": "userJSPlus", "background": { - "page": "background.html" + "service_worker": "/js/background.js", + "type": "module" }, - "browser_action": { + "action": { "default_icon": { - "16": "img/icon_16.png", + "16": "img/icon_16.png", "32": "img/icon_32.png", - "64": "img/icon_64.png" + "48": "img/icon_48.png", + "64": "img/icon_64.png", + "128": "img/icon_128.png" }, "default_popup": "popup.html", "default_title": "Magic UserJS+" }, - "options_ui": { - "page": "popup.html?mujs=settings", - "open_in_tab": true - }, + "minimum_chrome_version": "122.0", + "options_page": "popup.html?mujs=settings", "permissions": [ - "tabs", - "", "storage", "unlimitedStorage", + "tabs", + "activeTab", "webRequest" ], - "content_security_policy": "script-src 'self'; object-src 'self'", + "host_permissions": [ + "https://*/*" + ], "icons": { "16": "img/icon_16.png", "32": "img/icon_32.png", + "48": "img/icon_48.png", "64": "img/icon_64.png", "128": "img/icon_128.png" - }, - "commands": {} + } } diff --git a/src/manifest/firefox.json b/src/manifest/firefox.json index cbdb471..3a7ac1b 100644 --- a/src/manifest/firefox.json +++ b/src/manifest/firefox.json @@ -1,48 +1,58 @@ { - "$schema": "https://json.schemastore.org/webextension.json", - "manifest_version": 2, + "manifest_version": 3, "default_locale": "en", "name": "__MSG_extName__", "description": "__MSG_extShortDesc__", "short_name": "userJSPlus", "background": { - "page": "background.html" + "scripts": [ + "/js/background.js" + ], + "type": "module" }, - "browser_action": { - "browser_style": false, + "action": { "default_area": "navbar", "default_icon": { - "16": "img/icon_16.png", + "16": "img/icon_16.png", "32": "img/icon_32.png", - "64": "img/icon_64.png" + "48": "img/icon_48.png", + "64": "img/icon_64.png", + "128": "img/icon_128.png" }, "default_popup": "popup.html", "default_title": "Magic UserJS+" }, + "browser_specific_settings": { + "gecko": { + "id": "uscriptplus@mol.com", + "strict_min_version": "128.0" + }, + "gecko_android": { + "strict_min_version": "128.0" + } + }, "options_ui": { - "page": "popup.html?mujs=settings", - "open_in_tab": true + "open_in_tab": true, + "page": "popup.html?mujs=settings" }, "permissions": [ - "tabs", - "", "storage", "unlimitedStorage", + "tabs", + "activeTab", "webRequest" ], - "browser_specific_settings": { - "gecko": { - "id": "uscriptplus@mol.com", - "strict_min_version": "91.1.0" - } + "host_permissions": [ + "https://*/*" + ], + "content_security_policy": { + "extension_pages": "script-src 'self'; object-src 'self'" }, - "content_security_policy": "script-src 'self'; object-src 'self'", "icons": { - "16": "img/userjs.svg", - "32": "img/userjs.svg", - "48": "img/userjs.svg", - "64": "img/userjs.svg", - "96": "img/userjs.svg" - }, - "commands": {} + "16": "img/icon_16.png", + "32": "img/icon_32.png", + "48": "img/icon_48.png", + "64": "img/icon_64.png", + "128": "img/icon_128.png" + } } diff --git a/src/sass/_main.scss b/src/sass/_main.scss index 192f688..c7e78e9 100644 --- a/src/sass/_main.scss +++ b/src/sass/_main.scss @@ -30,6 +30,12 @@ mujs-root { } mujs-root * { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + scrollbar-color: var(--mujs-txt-color, hsl(0, 0%, 100%)) hsl(224, 14%, 21%); + scrollbar-width: thin; + @supports not (scrollbar-width: thin) { ::-webkit-scrollbar { width: 1.4vw; @@ -47,30 +53,29 @@ mujs-root * { ::-webkit-scrollbar-thumb { border-radius: 16px; background-color: var(--mujs-txt-color, hsl(0, 0%, 100%)); - background-image: -webkit-linear-gradient(45deg, - hsla(0, 0%, 100%, 0.2) 25%, - transparent 25%, - transparent 50%, - hsla(0, 0%, 100%, 0.2) 50%, - hsla(0, 0%, 100%, 0.2) 75%, - transparent 75%, - transparent); + background-image: -webkit-linear-gradient( + 45deg, + hsla(0, 0%, 100%, 0.2) 25%, + transparent 25%, + transparent 50%, + hsla(0, 0%, 100%, 0.2) 50%, + hsla(0, 0%, 100%, 0.2) 75%, + transparent 75%, + transparent + ); } ::-webkit-scrollbar-thumb:hover { background: var(--mujs-txt-color, hsl(0, 0%, 100%)); } } - - scrollbar-color: var(--mujs-txt-color, hsl(0, 0%, 100%)) hsl(224, 14%, 21%); - scrollbar-width: thin; } @at-root mu-js { line-height: normal; } -mujs-section>label, +mujs-section > label, .mujs-homepag e, td.mujs-list, .install { @@ -82,15 +87,11 @@ td.mujs-list, font-weight: 700; } -mujs-section>label, +mujs-section > label, td.mujs-list { font-weight: 500; } -.mujs-sty-flex>mujs-btn { - margin: auto; -} - .mujs-invalid { border-radius: 8px !important; border-width: 2px !important; @@ -111,27 +112,27 @@ mujs-row { } mujs-column { - @media screen and (max-width: 800px) { - flex-flow: row wrap; - } - count-frame { - &[data-counter="greasyfork"] { + &[data-counter='greasyfork'] { background: var(--mujs-gf-color, hsl(204, 100%, 40%)); } - &[data-counter="sleazyfork"] { + &[data-counter='sleazyfork'] { background: var(--mujs-sf-color, hsl(12, 86%, 50%)); } - &[data-counter="github"] { + &[data-counter='github'] { background: hsla(213, 13%, 16%); } - &[data-counter="openuserjs"] { + &[data-counter='openuserjs'] { background: hsla(12, 86%, 50%, 0.568); } } + + @media screen and (max-width: 800px) { + flex-flow: row wrap; + } } mujs-row { @@ -148,6 +149,13 @@ mu-js { } mujs-main { + width: 100%; + width: -moz-available; + width: -webkit-fill-available; + background: var(--mujs-background-color, hsl(222, 14%, 33%)) !important; + border: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); + border-radius: 16px; + @media screen and (max-height: 720px) { &:not(.webext-page) { height: 100% !important; @@ -157,13 +165,6 @@ mujs-main { } } - width: 100%; - width: -moz-available; - width: -webkit-fill-available; - background: var(--mujs-background-color, hsl(222, 14%, 33%)) !important; - border: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); - border-radius: 16px; - &.expanded { height: 100% !important; bottom: 0rem !important; @@ -187,7 +188,7 @@ mujs-main { flex-direction: column !important; } - >* { + > * { width: 100%; width: -moz-available; width: -webkit-fill-available; @@ -195,7 +196,7 @@ mujs-main { mujs-toolbar { order: 0; - padding: .5em; + padding: 0.5em; display: flex; place-content: space-between; @@ -209,7 +210,7 @@ mujs-main { order: 999999999999; } - >* { + > * { width: -webkit-fit-content; width: -moz-fit-content; width: fit-content; @@ -240,6 +241,10 @@ mujs-main { border-radius: 4px; background: transparent; + @media screen and (max-width: 800px) { + min-width: 6em !important; + } + &.active { background: var(--mujs-even-row, hsl(222, 14%, 18%)); } @@ -289,25 +294,33 @@ mujs-main { // background: var(--mujs-background-color, hsl(222, 14%, 33%)); background: hsla(0, 0%, 0%, 0); color: var(--mujs-txt-color, hsl(0, 0%, 100%)); - } - input:not([type='checkbox']) { - border: transparent; - outline: none !important; + &:not([type='checkbox']) { + border: transparent; + outline: none !important; + } } + mujs-page, textarea { background: inherit; overflow-y: auto; - color: var(--mujs-placeholder, hsl(81, 56%, 54%)); - // background: var(--mujs-even-row, hsl(222, 14%, 18%)); - // border: 1px solid transparent; border: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); - border-radius: 10px; - resize: vertical; + border-radius: 5px; outline: none; font-family: monospace; font-size: 14px; + } + + mujs-page { + padding: 0.5em; + margin: 0.5em; + } + + textarea { + overflow-y: auto; + color: var(--mujs-placeholder, hsl(81, 56%, 54%)); + resize: vertical; &:focus { outline: none; @@ -328,7 +341,7 @@ mujs-main { text-align: center; border-radius: 16px; - >* { + > * { min-height: 50px; } @@ -347,20 +360,19 @@ mujs-main { align-items: center; justify-content: center; - svg, - img { + svg { width: 14px; height: 14px; background: transparent; } - >.prompt { + > .prompt { position: absolute; background: var(--mujs-background-color, hsl(222, 14%, 33%)) !important; border: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); border-radius: 16px; text-align: center; - padding: .5em; + padding: 0.5em; z-index: 1; .prompt-head { @@ -370,12 +382,12 @@ mujs-main { .prompt-body { display: grid; grid-auto-flow: column; - grid-gap: .5em; - padding-top: .5em; + grid-gap: 0.5em; + padding-top: 0.5em; } mujs-btn { - &[data-command="prompt-deny"] { + &.prompt-deny { background: var(--mujs-sf-btn-color, hsl(12, 86%, 50%)); border-color: var(--mujs-sf-btn-color, hsl(12, 86%, 50%)); @@ -385,7 +397,7 @@ mujs-main { } } - &[data-command="prompt-confirm"] { + &.prompt-confirm { background: var(--mujs-gf-color, hsl(204, 100%, 40%)); border-color: var(--mujs-gf-color, hsl(204, 100%, 40%)); @@ -450,12 +462,9 @@ mujs-header { order: 1; display: flex; border-bottom: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); - // border-top-left-radius: 10px; - // border-top-right-radius: 10px; - // padding: .5em; - padding-left: .5em; - padding-right: .5em; - padding-bottom: .5em; + padding-left: 0.5em; + padding-right: 0.5em; + padding-bottom: 0.5em; font-size: 1em; place-content: space-between; height: fit-content; @@ -463,7 +472,7 @@ mujs-header { height: -webkit-fit-content; gap: 1em; - >*:not(mujs-url) { + > *:not(mujs-url) { height: fit-content; height: -moz-fit-content; height: -webkit-fit-content; @@ -473,7 +482,7 @@ mujs-header { order: 0; flex-grow: 1; - >input { + > input { width: 100%; height: 100%; background: var(--mujs-even-row, hsl(222, 14%, 18%)); @@ -486,10 +495,6 @@ mujs-header { order: 1; } - .mujs-sty-flex { - order: 2; - } - .btn-frame { order: 999999999999; } @@ -500,7 +505,6 @@ mujs-body { overflow-x: hidden; padding: 0px; height: 100%; - border: 1px solid var(--mujs-border-b-color, hsla(0, 0%, 0%, 0)); border-bottom-left-radius: 16px; border-bottom-right-radius: 16px; @@ -528,8 +532,12 @@ mujs-body { } table { + width: 100%; + width: -moz-available; + width: -webkit-fill-available; + @media screen and (max-width: 1180px) { - thead>tr { + thead > tr { display: table-column; } @@ -538,24 +546,46 @@ mujs-body { display: flex; flex-flow: row wrap; align-items: center; - border-bottom: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); - padding-top: .5em; - padding-bottom: .5em; + padding-top: 0.5em; + padding-bottom: 0.5em; td { margin: auto; - border-bottom: 1px solid transparent; - >mujs-a, - >mu-js, - >mujs-column { + > mujs-a, + > mu-js, + > mujs-column { text-align: center; justify-content: center; } + > mujs-a { + width: 100%; + } + + > mujs-column { + @media screen and (max-width: 800px) { + flex-flow: column wrap; + + > mujs-row { + align-content: center; + } + + mujs-column { + justify-content: center; + } + } + } + &:not(.mujs-name, .install-btn) { width: 25%; } + + &.install-btn { + @media screen and (max-width: 800px) { + width: 100%; + } + } } .mujs-name { @@ -576,40 +606,40 @@ mujs-body { } } - width: 100%; - width: -moz-available; - width: -webkit-fill-available; - - th, - td { - border-bottom: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); - } - th { position: -webkit-sticky; position: sticky; top: 0; background: hsla(222, 14%, 33%, 0.75); + border-bottom: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); &.mujs-header-name { + width: 50%; + @media screen and (max-width: 800px) { width: auto !important; } - - width: 50%; } } .frame { &:nth-child(even) { background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; + + textarea { + background: var(--mujs-odd-row, hsl(222, 14%, 33%)) !important; + } } &:nth-child(odd) { background: var(--mujs-odd-row, hsl(222, 14%, 33%)) !important; + + textarea { + background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; + } } - &:not([data-engine="sleazyfork"], [data-engine="greasyfork"]) { + &:not([data-engine='sleazyfork'], [data-engine='greasyfork']) { mujs-a { color: var(--mujs-sf-txt-color, hsl(12, 79%, 55%)); } @@ -625,8 +655,8 @@ mujs-body { } } - &[data-engine="sleazyfork"], - &[data-engine="greasyfork"] { + &[data-engine='sleazyfork'], + &[data-engine='greasyfork'] { mujs-a { color: var(--mujs-gf-color, hsl(197, 100%, 50%)); @@ -672,20 +702,41 @@ mujs-body { } } + &.translated:not([data-good], [data-author]) { + mujs-a { + color: hsl(249, 56%, 65%); + + &:hover { + color: hsl(249, 56%, 85%); + } + } + + mu-jsbtn { + color: hsl(215, 47%, 85%); + background: hsl(249, 56%, 65%); + border-color: hsl(249, 56%, 65%); + + &:hover { + background: hsl(249, 56%, 65%); + border-color: hsl(249, 56%, 65%); + } + } + } + .mujs-ratings { - &[data-el="good"] { + &[data-el='good'] { border-color: hsl(120, 50%, 40%); background-color: hsla(120, 50%, 40%, 0.102); color: hsl(120, 100%, 60%); } - &[data-el="ok"] { + &[data-el='ok'] { border-color: hsl(60, 100%, 30%); background-color: hsla(60, 100%, 30%, 0.102); color: hsl(60, 100%, 50%); } - &[data-el="bad"] { + &[data-el='bad'] { border-color: hsl(0, 100%, 30%); background-color: hsla(0, 50%, 40%, 0.102); color: hsl(0, 100%, 50%); @@ -693,22 +744,18 @@ mujs-body { } svg { + width: 12px; + height: 12px; fill: currentColor; - } - - svg, - img { - width: 14px; - height: 14px; background: transparent; } - >td:not(.mujs-name) { + > td:not(.mujs-name) { text-align: center; } - >.mujs-name { - >mujs-a { + > .mujs-name { + > mujs-a { width: -webkit-fit-content; width: -moz-fit-content; width: fit-content; @@ -721,34 +768,38 @@ mujs-body { height: fit-content; } - >mu-jsbtn { + > mu-jsbtn { margin: auto; } - >mujs-column { - >mu-jsbtn { + > mujs-column { + > mu-jsbtn { padding: 0px 7px; + + @media screen and (max-width: 800px) { + width: 100%; + } } } } - >.mujs-uframe { - >mujs-a { + > .mujs-uframe { + > mujs-a { font-size: 16px; font-weight: 500; - padding-left: .5rem; - padding-right: .5rem; + padding-left: 0.5rem; + padding-right: 0.5rem; } } - [data-el="more-info"] { - >mujs-row { - gap: .25em; + [data-el='more-info'] { + > mujs-row { + gap: 0.25em; } } - [data-el="matches"] { - gap: .25em; + [data-el='matches'] { + gap: 0.25em; max-width: 40em; .mujs-grants { @@ -762,9 +813,9 @@ mujs-body { width: -moz-fit-content; width: fit-content; max-height: 5em; - gap: .2em; + gap: 0.2em; - >mujs-a { + > mujs-a { display: inline; &:not([data-command]) { @@ -773,15 +824,35 @@ mujs-body { } &::after { - content: ", "; + content: ', '; color: var(--mujs-txt-color, hsl(0, 0%, 100%)); } &:last-child::after { - content: ""; + content: ''; } } } + + @media screen and (max-width: 800px) { + align-self: center; + width: 30em !important; + } + } + + [data-name='license'] { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + + @media screen and (max-width: 800px) { + width: 100% !important; + width: -moz-available !important; + width: -webkit-fill-available !important; + } } } } @@ -794,33 +865,93 @@ mujs-body { } .mujs-cfg { - @media screen and (max-height: 720px) { - &:not(.webext-page) { - height: 100%; - height: -moz-available; - height: -webkit-fill-available; - width: 100%; - width: -moz-available; - width: -webkit-fill-available; - overflow-x: auto; - padding: .5em; - } - } - - height: fit-content; - height: -moz-fit-content; - height: -webkit-fit-content; - mujs-section { border-radius: 16px; - padding: .5em; + padding: 0.5em; &:nth-child(even) { background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; + + input, + select { + background: var(--mujs-odd-row, hsl(222, 14%, 33%)); + } + + select option { + background: var(--mujs-odd-row, hsl(222, 14%, 33%)); + + &:hover { + background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; + } + } } &:nth-child(odd) { background: var(--mujs-odd-row, hsl(222, 14%, 33%)) !important; + + input, + select { + background: var(--mujs-even-row, hsl(222, 14%, 18%)); + } + + select option { + background: var(--mujs-even-row, hsl(222, 14%, 18%)); + + &:hover { + background: var(--mujs-odd-row, hsl(222, 14%, 33%)) !important; + } + } + } + + &[data-name='theme'] { + .sub-section { + border-radius: 4px; + + &:nth-child(even) { + background: var(--mujs-even-row, hsl(222, 14%, 18%)); + } + &:nth-child(odd) { + background: var(--mujs-odd-row, hsl(222, 14%, 33%)); + } + } + input, + select { + background: inherit; + } + + select option { + background: inherit; + + &:hover { + background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; + } + } + } + + svg { + width: 14px; + height: 14px; + fill: currentColor; + background: transparent; + } + + // &[data-name='theme'], + &[data-name='exp'], + &[data-name='blacklist'] { + display: flex; + justify-content: space-between; + flex-direction: column; + gap: 0.25em; + + > mujs-btn { + &:hover { + background: var(--mujs-even-row, hsl(222, 14%, 18%)) !important; + } + + width: 100%; + width: -moz-available; + width: -webkit-fill-available; + } } input[type='text']::-webkit-input-placeholder { @@ -843,9 +974,28 @@ mujs-body { color: var(--mujs-placeholder, hsl(81, 56%, 54%)); } - >label { - display: flex; - justify-content: space-between; + > label { + &:not([data-blacklist]) { + display: flex; + justify-content: space-between; + } + + &[data-blacklist] { + display: grid; + grid-auto-flow: column; + + &:not(.new-list) { + grid-template-columns: repeat(2, 1fr); + } + } + + &.new-list { + order: 999999999999; + + mujs-add { + font-size: 20px; + } + } input:not([type='checkbox']) { font-size: 14px; @@ -853,34 +1003,38 @@ mujs-body { border-radius: 4px; border: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); } + } + + select, + select option { + color: var(--mujs-txt-color, hsl(0, 0%, 100%)); + border: 1px solid transparent; + list-style: none; + outline-style: none; + pointer-events: auto; + } - // input[type='text']::-webkit-input-placeholder { - // color: var(--mujs-placeholder, hsl(81, 56%, 54%)); - // } + select { + text-align: center; + border-radius: 4px; + } - // input[type='text']::-moz-placeholder { - // color: var(--mujs-placeholder, hsl(81, 56%, 54%)); - // } + > *.sub-section { + padding: 0.2em; - // input[type='text']:-ms-input-placeholder { - // color: var(--mujs-placeholder, hsl(81, 56%, 54%)); - // } + &[data-engine] { + flex-wrap: wrap; - // input[type='text']::-ms-input-placeholder { - // color: var(--mujs-placeholder, hsl(81, 56%, 54%)); - // } + input { + width: 100%; + width: -moz-available; + width: -webkit-fill-available; + } + } - // input[type='text']::placeholder { - // color: var(--mujs-placeholder, hsl(81, 56%, 54%)); - // } - } - >input { - width: 100%; - width: -moz-available; - width: -webkit-fill-available; - margin-top: 5px; - border-radius: 4px; - border: 1px solid var(--mujs-txt-color, hsl(0, 0%, 100%)); + input[type='text'] { + margin: 0.2em 0px; + } } } @@ -891,7 +1045,7 @@ mujs-body { input[type='checkbox'] { display: none; - &:checked+label { + &:checked + label { margin-left: 0; background: var(--mujs-chck-color, hsla(0, 0%, 100%, 0.568)); @@ -900,16 +1054,19 @@ mujs-body { } } - &[data-name='greasyfork']:checked+label, - &[data-name='sleazyfork']:checked+label { - background: var(--mujs-chck-gf, hsla(197, 100%, 50%, 0.568)); + &[data-name='greasyfork']:checked + label { + background: var(--mujs-gf-color, hsl(204, 100%, 40%)); + } + + &[data-name='sleazyfork']:checked + label { + background: var(--mujs-sf-color, hsl(12, 86%, 50%)); } - &[data-name='openuserjs']:checked+label { + &[data-name='openuserjs']:checked + label { background: var(--mujs-chck-open, hsla(12, 86%, 50%, 0.568)); } - &[data-name='github']:checked+label { + &[data-name='github']:checked + label { background: var(--mujs-chck-git, hsla(213, 13%, 16%, 0.568)); } } @@ -939,7 +1096,9 @@ mujs-body { .mujs-sty-flex { mujs-btn { - &[data-command="reset"] { + margin: auto; + + &[data-command='reset'] { background: var(--mujs-sf-btn-color, hsl(12, 86%, 50%)); border-color: var(--mujs-sf-btn-color, hsl(12, 86%, 50%)); @@ -949,7 +1108,7 @@ mujs-body { } } - &[data-command="save"] { + &[data-command='save'] { background: var(--mujs-gf-color, hsl(204, 100%, 40%)); border-color: var(--mujs-gf-color, hsl(204, 100%, 40%)); @@ -964,6 +1123,23 @@ mujs-body { &:not(.webext-page) { margin: 1rem 25rem; } + + height: fit-content; + height: -moz-fit-content; + height: -webkit-fit-content; + + @media screen and (max-height: 720px) { + &:not(.webext-page) { + height: 100%; + height: -moz-available; + height: -webkit-fill-available; + width: 100%; + width: -moz-available; + width: -webkit-fill-available; + overflow-x: auto; + padding: 0.5em; + } + } } mujs-a { @@ -973,7 +1149,7 @@ mujs-a { .mujs-name { display: flex; flex-flow: column wrap; - gap: .5em; + gap: 0.5em; span { font-size: 0.8em !important; @@ -994,13 +1170,9 @@ mujs-btn { padding: 6px 15px; svg { - fill: var(--mujs-txt-color, hsl(0, 0%, 100%)); - } - - svg, - img { width: 14px; height: 14px; + fill: var(--mujs-txt-color, hsl(0, 0%, 100%)); } } @@ -1019,25 +1191,11 @@ mu-jsbtn { mujs-a, mu-jsbtn, .mujs-pointer, -.mujs-cfg mujs-section *:not(input[type='password'], input[type='text'], input[type='number']), +.mujs-cfg + mujs-section + *:not(input[type='text'], input[type='number'], [data-theme], [data-blacklist]), .mainbtn, .mainframe, mujs-btn { cursor: pointer !important; } - -// .install-link { -// transition: box-shadow .2s; -// display: inline-block; -// background-color: hsl(120, 100%, 16%); -// padding: .5em 1em; -// color: hsl(0, 0%, 100%); -// text-decoration: none; -// border-radius: .25rem 0 0 .25rem; -// } - -// .install-help { -// background-color: hsl(120, 67%, 35%); -// color: hsl(0, 0%, 100%); -// border-radius: 0 .25rem .25rem 0; -// } diff --git a/src/sass/fontawesome/_animated.scss b/src/sass/fontawesome/_animated.scss new file mode 100644 index 0000000..779125e --- /dev/null +++ b/src/sass/fontawesome/_animated.scss @@ -0,0 +1,152 @@ +// animating icons +// -------------------------- + +.#{$fa-css-prefix}-beat { + animation-name: #{$fa-css-prefix}-beat; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out); +} + +.#{$fa-css-prefix}-bounce { + animation-name: #{$fa-css-prefix}-bounce; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(0.280, 0.840, 0.420, 1)); +} + +.#{$fa-css-prefix}-fade { + animation-name: #{$fa-css-prefix}-fade; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1)); +} + +.#{$fa-css-prefix}-beat-fade { + animation-name: #{$fa-css-prefix}-beat-fade; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1)); +} + +.#{$fa-css-prefix}-flip { + animation-name: #{$fa-css-prefix}-flip; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out); +} + +.#{$fa-css-prefix}-shake { + animation-name: #{$fa-css-prefix}-shake; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear); +} + +.#{$fa-css-prefix}-spin { + animation-name: #{$fa-css-prefix}-spin; + animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0s); + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 2s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear); +} + +.#{$fa-css-prefix}-spin-reverse { + --#{$fa-css-prefix}-animation-direction: reverse; +} + +.#{$fa-css-prefix}-pulse, +.#{$fa-css-prefix}-spin-pulse { + animation-name: #{$fa-css-prefix}-spin; + animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal); + animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s); + animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite); + animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, steps(8)); +} + +// if agent or operating system prefers reduced motion, disable animations +// see: https://www.smashingmagazine.com/2020/09/design-reduced-motion-sensitivities/ +// see: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion +@media (prefers-reduced-motion: reduce) { + .#{$fa-css-prefix}-beat, + .#{$fa-css-prefix}-bounce, + .#{$fa-css-prefix}-fade, + .#{$fa-css-prefix}-beat-fade, + .#{$fa-css-prefix}-flip, + .#{$fa-css-prefix}-pulse, + .#{$fa-css-prefix}-shake, + .#{$fa-css-prefix}-spin, + .#{$fa-css-prefix}-spin-pulse { + animation-delay: -1ms; + animation-duration: 1ms; + animation-iteration-count: 1; + transition-delay: 0s; + transition-duration: 0s; + } +} + +@keyframes #{$fa-css-prefix}-beat { + 0%, 90% { transform: scale(1); } + 45% { transform: scale(var(--#{$fa-css-prefix}-beat-scale, 1.25)); } +} + +@keyframes #{$fa-css-prefix}-bounce { + 0% { transform: scale(1,1) translateY(0); } + 10% { transform: scale(var(--#{$fa-css-prefix}-bounce-start-scale-x, 1.1),var(--#{$fa-css-prefix}-bounce-start-scale-y, 0.9)) translateY(0); } + 30% { transform: scale(var(--#{$fa-css-prefix}-bounce-jump-scale-x, 0.9),var(--#{$fa-css-prefix}-bounce-jump-scale-y, 1.1)) translateY(var(--#{$fa-css-prefix}-bounce-height, -0.5em)); } + 50% { transform: scale(var(--#{$fa-css-prefix}-bounce-land-scale-x, 1.05),var(--#{$fa-css-prefix}-bounce-land-scale-y, 0.95)) translateY(0); } + 57% { transform: scale(1,1) translateY(var(--#{$fa-css-prefix}-bounce-rebound, -0.125em)); } + 64% { transform: scale(1,1) translateY(0); } + 100% { transform: scale(1,1) translateY(0); } +} + +@keyframes #{$fa-css-prefix}-fade { + 50% { opacity: var(--#{$fa-css-prefix}-fade-opacity, 0.4); } +} + +@keyframes #{$fa-css-prefix}-beat-fade { + 0%, 100% { + opacity: var(--#{$fa-css-prefix}-beat-fade-opacity, 0.4); + transform: scale(1); + } + 50% { + opacity: 1; + transform: scale(var(--#{$fa-css-prefix}-beat-fade-scale, 1.125)); + } +} + +@keyframes #{$fa-css-prefix}-flip { + 50% { + transform: rotate3d(var(--#{$fa-css-prefix}-flip-x, 0), var(--#{$fa-css-prefix}-flip-y, 1), var(--#{$fa-css-prefix}-flip-z, 0), var(--#{$fa-css-prefix}-flip-angle, -180deg)); + } +} + +@keyframes #{$fa-css-prefix}-shake { + 0% { transform: rotate(-15deg); } + 4% { transform: rotate(15deg); } + 8%, 24% { transform: rotate(-18deg); } + 12%, 28% { transform: rotate(18deg); } + 16% { transform: rotate(-22deg); } + 20% { transform: rotate(22deg); } + 32% { transform: rotate(-12deg); } + 36% { transform: rotate(12deg); } + 40%, 100% { transform: rotate(0deg); } +} + +@keyframes #{$fa-css-prefix}-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} diff --git a/src/sass/fontawesome/_bordered-pulled.scss b/src/sass/fontawesome/_bordered-pulled.scss new file mode 100644 index 0000000..06bf72d --- /dev/null +++ b/src/sass/fontawesome/_bordered-pulled.scss @@ -0,0 +1,20 @@ +// bordered + pulled icons +// ------------------------- + +.#{$fa-css-prefix}-border { + border-color: var(--#{$fa-css-prefix}-border-color, #{$fa-border-color}); + border-radius: var(--#{$fa-css-prefix}-border-radius, #{$fa-border-radius}); + border-style: var(--#{$fa-css-prefix}-border-style, #{$fa-border-style}); + border-width: var(--#{$fa-css-prefix}-border-width, #{$fa-border-width}); + padding: var(--#{$fa-css-prefix}-border-padding, #{$fa-border-padding}); +} + +.#{$fa-css-prefix}-pull-left { + float: left; + margin-right: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin}); +} + +.#{$fa-css-prefix}-pull-right { + float: right; + margin-left: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin}); +} diff --git a/src/sass/fontawesome/_core.scss b/src/sass/fontawesome/_core.scss new file mode 100644 index 0000000..a850b0f --- /dev/null +++ b/src/sass/fontawesome/_core.scss @@ -0,0 +1,49 @@ +// base icon class definition +// ------------------------- + +.#{$fa-css-prefix} { + font-family: var(--#{$fa-css-prefix}-style-family, '#{$fa-style-family}'); + font-weight: var(--#{$fa-css-prefix}-style, #{$fa-style}); +} + +.fas, +.far, +.fab, +.#{$fa-css-prefix}-solid, +.#{$fa-css-prefix}-regular, +.#{$fa-css-prefix}-brands, +.#{$fa-css-prefix} { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: var(--#{$fa-css-prefix}-display, #{$fa-display}); + font-style: normal; + font-variant: normal; + line-height: 1; + text-rendering: auto; +} + +.fas::before, +.far::before, +.fab::before, +.#{$fa-css-prefix}-solid::before, +.#{$fa-css-prefix}-regular::before, +.#{$fa-css-prefix}-brands::before, +.#{$fa-css-prefix}::before { + content: var(#{$fa-icon-property}); +} + +.#{$fa-css-prefix}-classic, +.fas, +.#{$fa-css-prefix}-solid, +.far, +.#{$fa-css-prefix}-regular { + font-family: 'Font Awesome 6 Free'; +} +.#{$fa-css-prefix}-brands, +.fab { + font-family: 'Font Awesome 6 Brands'; +} + +%fa-icon { + @include fa-icon; +} diff --git a/src/sass/fontawesome/_fixed-width.scss b/src/sass/fontawesome/_fixed-width.scss new file mode 100644 index 0000000..7234236 --- /dev/null +++ b/src/sass/fontawesome/_fixed-width.scss @@ -0,0 +1,7 @@ +// fixed-width icons +// ------------------------- + +.#{$fa-css-prefix}-fw { + text-align: center; + width: $fa-fw-width; +} diff --git a/src/sass/fontawesome/_functions.scss b/src/sass/fontawesome/_functions.scss new file mode 100644 index 0000000..a17ffe8 --- /dev/null +++ b/src/sass/fontawesome/_functions.scss @@ -0,0 +1,57 @@ +// functions +// -------------------------- + +// fa-content: convenience function used to set content property +@function fa-content($fa-var) { + @return unquote("\"#{ $fa-var }\""); +} + +// fa-divide: Originally obtained from the Bootstrap https://github.com/twbs/bootstrap +// +// Licensed under: The MIT License (MIT) +// +// Copyright (c) 2011-2021 Twitter, Inc. +// Copyright (c) 2011-2021 The Bootstrap Authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +@function fa-divide($dividend, $divisor, $precision: 10) { + $sign: if($dividend > 0 and $divisor > 0, 1, -1); + $dividend: abs($dividend); + $divisor: abs($divisor); + $quotient: 0; + $remainder: $dividend; + @if $dividend == 0 { + @return 0; + } + @if $divisor == 0 { + @error "Cannot divide by 0"; + } + @if $divisor == 1 { + @return $dividend; + } + @while $remainder >= $divisor { + $quotient: $quotient + 1; + $remainder: $remainder - $divisor; + } + @if $remainder > 0 and $precision > 0 { + $remainder: fa-divide($remainder * 10, $divisor, $precision - 1) * .1; + } + @return ($quotient + $remainder) * $sign; +} diff --git a/src/sass/fontawesome/_icons.scss b/src/sass/fontawesome/_icons.scss new file mode 100644 index 0000000..2f13535 --- /dev/null +++ b/src/sass/fontawesome/_icons.scss @@ -0,0 +1,12 @@ +// specific icon class definition +// ------------------------- + +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen +readers do not read off random characters that represent icons */ + +@each $name, $icon in $fa-icons { + .#{$fa-css-prefix}-#{$name} { + #{$fa-icon-property}: unquote("\"#{ $icon }\""); + } +} + diff --git a/src/sass/fontawesome/_list.scss b/src/sass/fontawesome/_list.scss new file mode 100644 index 0000000..120506a --- /dev/null +++ b/src/sass/fontawesome/_list.scss @@ -0,0 +1,18 @@ +// icons in a list +// ------------------------- + +.#{$fa-css-prefix}-ul { + list-style-type: none; + margin-left: var(--#{$fa-css-prefix}-li-margin, #{$fa-li-margin}); + padding-left: 0; + + > li { position: relative; } +} + +.#{$fa-css-prefix}-li { + left: calc(-1 * var(--#{$fa-css-prefix}-li-width, #{$fa-li-width})); + position: absolute; + text-align: center; + width: var(--#{$fa-css-prefix}-li-width, #{$fa-li-width}); + line-height: inherit; +} diff --git a/src/sass/fontawesome/_mixins.scss b/src/sass/fontawesome/_mixins.scss new file mode 100644 index 0000000..71f99ad --- /dev/null +++ b/src/sass/fontawesome/_mixins.scss @@ -0,0 +1,65 @@ +// mixins +// -------------------------- + +// base rendering for an icon +@mixin fa-icon { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + display: inline-block; + font-style: normal; + font-variant: normal; + font-weight: normal; + line-height: 1; +} + +// sets relative font-sizing and alignment (in _sizing) +@mixin fa-size ($font-size) { + font-size: fa-divide($font-size, $fa-size-scale-base) * 1em; // converts step in sizing scale into an em-based value that's relative to the scale's base + line-height: fa-divide(1, $font-size) * 1em; // sets the line-height of the icon back to that of it's parent + vertical-align: (fa-divide(6, $font-size) - fa-divide(3, 8)) * 1em; // vertically centers the icon taking into account the surrounding text's descender +} + +// only display content to screen readers +// see: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/ +// see: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/ +@mixin fa-sr-only() { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +// use in conjunction with .sr-only to only display content when it's focused +@mixin fa-sr-only-focusable() { + &:not(:focus) { + @include fa-sr-only(); + } +} + +// sets a specific icon family to use alongside style + icon mixins +@mixin fa-family-classic() { + @extend .fa-classic; +} + +// convenience mixins for declaring pseudo-elements by CSS variable, +// including all style-specific font properties +@mixin fa-icon-solid($fa-var) { + @extend .fa-solid; + + & { #{$fa-icon-property}: unquote("\"#{ $fa-var }\""); #{$fa-duotone-icon-property}: unquote("\"#{ $fa-var }#{ $fa-var }\""); } +} +@mixin fa-icon-regular($fa-var) { + @extend .fa-regular; + + & { #{$fa-icon-property}: unquote("\"#{ $fa-var }\""); #{$fa-duotone-icon-property}: unquote("\"#{ $fa-var }#{ $fa-var }\""); } +} +@mixin fa-icon-brands($fa-var) { + @extend .fa-brands; + + & { #{$fa-icon-property}: unquote("\"#{ $fa-var }\""); #{$fa-duotone-icon-property}: unquote("\"#{ $fa-var }#{ $fa-var }\""); } +} diff --git a/src/sass/fontawesome/_rotated-flipped.scss b/src/sass/fontawesome/_rotated-flipped.scss new file mode 100644 index 0000000..0bd4b7c --- /dev/null +++ b/src/sass/fontawesome/_rotated-flipped.scss @@ -0,0 +1,31 @@ +// rotating + flipping icons +// ------------------------- + +.#{$fa-css-prefix}-rotate-90 { + transform: rotate(90deg); +} + +.#{$fa-css-prefix}-rotate-180 { + transform: rotate(180deg); +} + +.#{$fa-css-prefix}-rotate-270 { + transform: rotate(270deg); +} + +.#{$fa-css-prefix}-flip-horizontal { + transform: scale(-1, 1); +} + +.#{$fa-css-prefix}-flip-vertical { + transform: scale(1, -1); +} + +.#{$fa-css-prefix}-flip-both, +.#{$fa-css-prefix}-flip-horizontal.#{$fa-css-prefix}-flip-vertical { + transform: scale(-1, -1); +} + +.#{$fa-css-prefix}-rotate-by { + transform: rotate(var(--#{$fa-css-prefix}-rotate-angle, 0)); +} diff --git a/src/sass/fontawesome/_screen-reader.scss b/src/sass/fontawesome/_screen-reader.scss new file mode 100644 index 0000000..2beb887 --- /dev/null +++ b/src/sass/fontawesome/_screen-reader.scss @@ -0,0 +1,14 @@ +// screen-reader utilities +// ------------------------- + +// only display content to screen readers +.sr-only, +.#{$fa-css-prefix}-sr-only { + @include fa-sr-only; +} + +// use in conjunction with .sr-only to only display content when it's focused +.sr-only-focusable, +.#{$fa-css-prefix}-sr-only-focusable { + @include fa-sr-only-focusable; +} diff --git a/src/sass/fontawesome/_shims.scss b/src/sass/fontawesome/_shims.scss new file mode 100644 index 0000000..3fb6d41 --- /dev/null +++ b/src/sass/fontawesome/_shims.scss @@ -0,0 +1,1578 @@ +.#{$fa-css-prefix}.#{$fa-css-prefix}-glass { #{$fa-icon-property}: unquote("\"#{ $fa-var-martini-glass-empty }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-envelope }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-star }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-remove { #{$fa-icon-property}: unquote("\"#{ $fa-var-xmark }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-close { #{$fa-icon-property}: unquote("\"#{ $fa-var-xmark }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-gear { #{$fa-icon-property}: unquote("\"#{ $fa-var-gear }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-trash-can }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-home { #{$fa-icon-property}: unquote("\"#{ $fa-var-house }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-clock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-clock-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-clock }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-down { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-down }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-up { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-up }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-play-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-play-circle-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-play }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-repeat { #{$fa-icon-property}: unquote("\"#{ $fa-var-arrow-rotate-right }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-rotate-right { #{$fa-icon-property}: unquote("\"#{ $fa-var-arrow-rotate-right }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-refresh { #{$fa-icon-property}: unquote("\"#{ $fa-var-arrows-rotate }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-list-alt { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-list-alt { #{$fa-icon-property}: unquote("\"#{ $fa-var-rectangle-list }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-dedent { #{$fa-icon-property}: unquote("\"#{ $fa-var-outdent }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-video-camera { #{$fa-icon-property}: unquote("\"#{ $fa-var-video }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-picture-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-picture-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-image }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-photo { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-photo { #{$fa-icon-property}: unquote("\"#{ $fa-var-image }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-image { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-image { #{$fa-icon-property}: unquote("\"#{ $fa-var-image }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-marker { #{$fa-icon-property}: unquote("\"#{ $fa-var-location-dot }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-pen-to-square }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-edit { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-edit { #{$fa-icon-property}: unquote("\"#{ $fa-var-pen-to-square }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-share-square-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-share-from-square }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-square-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-check }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows { #{$fa-icon-property}: unquote("\"#{ $fa-var-up-down-left-right }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-circle-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-xmark }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-check-circle-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-check }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-forward { #{$fa-icon-property}: unquote("\"#{ $fa-var-share }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-expand { #{$fa-icon-property}: unquote("\"#{ $fa-var-up-right-and-down-left-from-center }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-compress { #{$fa-icon-property}: unquote("\"#{ $fa-var-down-left-and-up-right-to-center }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-eye { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-eye-slash { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-warning { #{$fa-icon-property}: unquote("\"#{ $fa-var-triangle-exclamation }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar { #{$fa-icon-property}: unquote("\"#{ $fa-var-calendar-days }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-v { #{$fa-icon-property}: unquote("\"#{ $fa-var-up-down }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-h { #{$fa-icon-property}: unquote("\"#{ $fa-var-left-right }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-bar-chart { #{$fa-icon-property}: unquote("\"#{ $fa-var-chart-column }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-bar-chart-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-chart-column }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-twitter }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-facebook }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-gears { #{$fa-icon-property}: unquote("\"#{ $fa-var-gears }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-up { #{$fa-icon-property}: unquote("\"#{ $fa-var-thumbs-up }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumbs-o-down { #{$fa-icon-property}: unquote("\"#{ $fa-var-thumbs-down }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-heart-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-heart-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-heart }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sign-out { #{$fa-icon-property}: unquote("\"#{ $fa-var-right-from-bracket }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-linkedin }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-thumb-tack { #{$fa-icon-property}: unquote("\"#{ $fa-var-thumbtack }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-external-link { #{$fa-icon-property}: unquote("\"#{ $fa-var-up-right-from-square }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sign-in { #{$fa-icon-property}: unquote("\"#{ $fa-var-right-to-bracket }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-github }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-lemon-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lemon-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-lemon }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-square-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-square }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-bookmark-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bookmark-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-bookmark }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitter { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook { #{$fa-icon-property}: unquote("\"#{ $fa-var-facebook-f }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-f { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-f { #{$fa-icon-property}: unquote("\"#{ $fa-var-facebook-f }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-github { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-credit-card { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-feed { #{$fa-icon-property}: unquote("\"#{ $fa-var-rss }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hdd-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hdd-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hard-drive }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-right { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-point-right }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-left { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-point-left }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-up { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-point-up }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-o-down { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-point-down }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-globe { #{$fa-icon-property}: unquote("\"#{ $fa-var-earth-americas }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-tasks { #{$fa-icon-property}: unquote("\"#{ $fa-var-bars-progress }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrows-alt { #{$fa-icon-property}: unquote("\"#{ $fa-var-maximize }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-group { #{$fa-icon-property}: unquote("\"#{ $fa-var-users }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-chain { #{$fa-icon-property}: unquote("\"#{ $fa-var-link }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-cut { #{$fa-icon-property}: unquote("\"#{ $fa-var-scissors }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-files-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-files-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-copy }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-floppy-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-floppy-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-floppy-disk }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-save { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-save { #{$fa-icon-property}: unquote("\"#{ $fa-var-floppy-disk }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-navicon { #{$fa-icon-property}: unquote("\"#{ $fa-var-bars }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-reorder { #{$fa-icon-property}: unquote("\"#{ $fa-var-bars }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-magic { #{$fa-icon-property}: unquote("\"#{ $fa-var-wand-magic-sparkles }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-pinterest }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-google-plus }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus { #{$fa-icon-property}: unquote("\"#{ $fa-var-google-plus-g }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-money { #{$fa-icon-property}: unquote("\"#{ $fa-var-money-bill-1 }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-unsorted { #{$fa-icon-property}: unquote("\"#{ $fa-var-sort }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-desc { #{$fa-icon-property}: unquote("\"#{ $fa-var-sort-down }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-asc { #{$fa-icon-property}: unquote("\"#{ $fa-var-sort-up }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-linkedin { #{$fa-icon-property}: unquote("\"#{ $fa-var-linkedin-in }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-rotate-left { #{$fa-icon-property}: unquote("\"#{ $fa-var-arrow-rotate-left }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-legal { #{$fa-icon-property}: unquote("\"#{ $fa-var-gavel }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-tachometer { #{$fa-icon-property}: unquote("\"#{ $fa-var-gauge-high }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-dashboard { #{$fa-icon-property}: unquote("\"#{ $fa-var-gauge-high }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-comment-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-comment-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-comment }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-comments-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-comments-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-comments }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-flash { #{$fa-icon-property}: unquote("\"#{ $fa-var-bolt }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-clipboard { #{$fa-icon-property}: unquote("\"#{ $fa-var-paste }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-lightbulb-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lightbulb-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-lightbulb }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-exchange { #{$fa-icon-property}: unquote("\"#{ $fa-var-right-left }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-cloud-download { #{$fa-icon-property}: unquote("\"#{ $fa-var-cloud-arrow-down }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-cloud-upload { #{$fa-icon-property}: unquote("\"#{ $fa-var-cloud-arrow-up }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-bell }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-cutlery { #{$fa-icon-property}: unquote("\"#{ $fa-var-utensils }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-lines }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-building-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-building-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-building }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hospital-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hospital-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hospital }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-tablet { #{$fa-icon-property}: unquote("\"#{ $fa-var-tablet-screen-button }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-mobile { #{$fa-icon-property}: unquote("\"#{ $fa-var-mobile-screen-button }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-mobile-phone { #{$fa-icon-property}: unquote("\"#{ $fa-var-mobile-screen-button }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-reply { #{$fa-icon-property}: unquote("\"#{ $fa-var-reply }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-github-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-folder }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-open-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-folder-open-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-folder-open }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-smile-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-smile-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-face-smile }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-frown-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-frown-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-face-frown }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-meh-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-meh-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-face-meh }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-keyboard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-keyboard-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-keyboard }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-flag-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-flag-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-flag }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-mail-reply-all { #{$fa-icon-property}: unquote("\"#{ $fa-var-reply-all }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-star-half-stroke }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-empty { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-empty { #{$fa-icon-property}: unquote("\"#{ $fa-var-star-half-stroke }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-full { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-star-half-full { #{$fa-icon-property}: unquote("\"#{ $fa-var-star-half-stroke }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-code-fork { #{$fa-icon-property}: unquote("\"#{ $fa-var-code-branch }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-chain-broken { #{$fa-icon-property}: unquote("\"#{ $fa-var-link-slash }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-unlink { #{$fa-icon-property}: unquote("\"#{ $fa-var-link-slash }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-calendar }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-maxcdn { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-html5 { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-css3 { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-unlock-alt { #{$fa-icon-property}: unquote("\"#{ $fa-var-unlock }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-minus-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-minus-square-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-minus }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-level-up { #{$fa-icon-property}: unquote("\"#{ $fa-var-turn-up }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-level-down { #{$fa-icon-property}: unquote("\"#{ $fa-var-turn-down }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-pencil-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-pen }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-external-link-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-up-right }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-compass { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-down { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-caret-down }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-down { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-caret-down }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-up { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-caret-up }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-up { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-caret-up }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-right { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-caret-right }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-right { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-caret-right }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-eur { #{$fa-icon-property}: unquote("\"#{ $fa-var-euro-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-euro { #{$fa-icon-property}: unquote("\"#{ $fa-var-euro-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-gbp { #{$fa-icon-property}: unquote("\"#{ $fa-var-sterling-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-usd { #{$fa-icon-property}: unquote("\"#{ $fa-var-dollar-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-dollar { #{$fa-icon-property}: unquote("\"#{ $fa-var-dollar-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-inr { #{$fa-icon-property}: unquote("\"#{ $fa-var-indian-rupee-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-rupee { #{$fa-icon-property}: unquote("\"#{ $fa-var-indian-rupee-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-jpy { #{$fa-icon-property}: unquote("\"#{ $fa-var-yen-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-cny { #{$fa-icon-property}: unquote("\"#{ $fa-var-yen-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-rmb { #{$fa-icon-property}: unquote("\"#{ $fa-var-yen-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-yen { #{$fa-icon-property}: unquote("\"#{ $fa-var-yen-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-rub { #{$fa-icon-property}: unquote("\"#{ $fa-var-ruble-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-ruble { #{$fa-icon-property}: unquote("\"#{ $fa-var-ruble-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-rouble { #{$fa-icon-property}: unquote("\"#{ $fa-var-ruble-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-krw { #{$fa-icon-property}: unquote("\"#{ $fa-var-won-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-won { #{$fa-icon-property}: unquote("\"#{ $fa-var-won-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-btc { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitcoin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitcoin { #{$fa-icon-property}: unquote("\"#{ $fa-var-btc }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-text { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-lines }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-alpha-asc { #{$fa-icon-property}: unquote("\"#{ $fa-var-arrow-down-a-z }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-alpha-desc { #{$fa-icon-property}: unquote("\"#{ $fa-var-arrow-down-z-a }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-amount-asc { #{$fa-icon-property}: unquote("\"#{ $fa-var-arrow-down-short-wide }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-amount-desc { #{$fa-icon-property}: unquote("\"#{ $fa-var-arrow-down-wide-short }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-numeric-asc { #{$fa-icon-property}: unquote("\"#{ $fa-var-arrow-down-1-9 }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sort-numeric-desc { #{$fa-icon-property}: unquote("\"#{ $fa-var-arrow-down-9-1 }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-youtube }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-xing-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-xing }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-play { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-youtube-play { #{$fa-icon-property}: unquote("\"#{ $fa-var-youtube }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-dropbox { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-stack-overflow { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-instagram { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-flickr { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-adn { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bitbucket-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-bitbucket }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-tumblr-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-tumblr }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-down { #{$fa-icon-property}: unquote("\"#{ $fa-var-down-long }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-up { #{$fa-icon-property}: unquote("\"#{ $fa-var-up-long }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-left { #{$fa-icon-property}: unquote("\"#{ $fa-var-left-long }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-long-arrow-right { #{$fa-icon-property}: unquote("\"#{ $fa-var-right-long }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-apple { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-windows { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-android { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-linux { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-dribbble { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-skype { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-foursquare { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-trello { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-gratipay { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-gittip { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-gittip { #{$fa-icon-property}: unquote("\"#{ $fa-var-gratipay }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sun-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-sun-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-sun }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-moon-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-moon-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-moon }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-vk { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-weibo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-renren { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pagelines { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-stack-exchange { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-right { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-right }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-arrow-circle-o-left { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-left }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-caret-square-o-left { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-caret-left }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-toggle-left { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-caret-left }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-dot-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-dot-circle-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-dot }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-vimeo }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-try { #{$fa-icon-property}: unquote("\"#{ $fa-var-turkish-lira-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-turkish-lira { #{$fa-icon-property}: unquote("\"#{ $fa-var-turkish-lira-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-plus-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-plus-square-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-plus }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-slack { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wordpress { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-openid { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-institution { #{$fa-icon-property}: unquote("\"#{ $fa-var-building-columns }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-bank { #{$fa-icon-property}: unquote("\"#{ $fa-var-building-columns }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-mortar-board { #{$fa-icon-property}: unquote("\"#{ $fa-var-graduation-cap }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-yahoo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-reddit }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-stumbleupon-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-stumbleupon { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-delicious { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-digg { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper-pp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-drupal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-joomla { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-behance-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-behance }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-steam-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-steam }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-automobile { #{$fa-icon-property}: unquote("\"#{ $fa-var-car }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-cab { #{$fa-icon-property}: unquote("\"#{ $fa-var-taxi }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-spotify { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-deviantart { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-soundcloud { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-pdf-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-pdf-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-pdf }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-word-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-word-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-word }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-excel-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-excel-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-excel }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-powerpoint-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-powerpoint-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-powerpoint }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-image-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-image-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-image }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-photo-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-photo-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-image }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-picture-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-picture-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-image }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-archive-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-archive-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-zipper }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-zip-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-zip-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-zipper }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-audio-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-audio-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-audio }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-sound-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-sound-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-audio }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-video-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-video-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-video }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-movie-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-movie-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-video }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-code-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-file-code-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-file-code }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-vine { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-codepen { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-jsfiddle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-bouy { #{$fa-icon-property}: unquote("\"#{ $fa-var-life-ring }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-buoy { #{$fa-icon-property}: unquote("\"#{ $fa-var-life-ring }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-life-saver { #{$fa-icon-property}: unquote("\"#{ $fa-var-life-ring }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-support { #{$fa-icon-property}: unquote("\"#{ $fa-var-life-ring }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-o-notch { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-notch }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-rebel { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ra { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ra { #{$fa-icon-property}: unquote("\"#{ $fa-var-rebel }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-resistance { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-resistance { #{$fa-icon-property}: unquote("\"#{ $fa-var-rebel }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-empire { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ge { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ge { #{$fa-icon-property}: unquote("\"#{ $fa-var-empire }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-git-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-git-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-git }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-git { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hacker-news { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-hacker-news }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-hacker-news }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-tencent-weibo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-qq { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-weixin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wechat { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wechat { #{$fa-icon-property}: unquote("\"#{ $fa-var-weixin }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-send { #{$fa-icon-property}: unquote("\"#{ $fa-var-paper-plane }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-paper-plane-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-paper-plane-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-paper-plane }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-send-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-send-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-paper-plane }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-thin { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-circle-thin { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-header { #{$fa-icon-property}: unquote("\"#{ $fa-var-heading }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-futbol-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-futbol-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-futbol }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-soccer-ball-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-soccer-ball-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-futbol }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-slideshare { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-twitch { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yelp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-newspaper-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-newspaper-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-newspaper }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-paypal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-wallet { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-visa { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-mastercard { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-discover { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-amex { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-paypal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-stripe { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-slash-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bell-slash-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-bell-slash }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-trash { #{$fa-icon-property}: unquote("\"#{ $fa-var-trash-can }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-copyright { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-eyedropper { #{$fa-icon-property}: unquote("\"#{ $fa-var-eye-dropper }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-area-chart { #{$fa-icon-property}: unquote("\"#{ $fa-var-chart-area }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-pie-chart { #{$fa-icon-property}: unquote("\"#{ $fa-var-chart-pie }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-line-chart { #{$fa-icon-property}: unquote("\"#{ $fa-var-chart-line }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-lastfm-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-lastfm }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-ioxhost { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-angellist { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc { #{$fa-icon-property}: unquote("\"#{ $fa-var-closed-captioning }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-ils { #{$fa-icon-property}: unquote("\"#{ $fa-var-shekel-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-shekel { #{$fa-icon-property}: unquote("\"#{ $fa-var-shekel-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-sheqel { #{$fa-icon-property}: unquote("\"#{ $fa-var-shekel-sign }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-buysellads { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-connectdevelop { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-dashcube { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-forumbee { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-leanpub { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-sellsy { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-shirtsinbulk { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-simplybuilt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-skyatlas { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-diamond { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-diamond { #{$fa-icon-property}: unquote("\"#{ $fa-var-gem }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-transgender { #{$fa-icon-property}: unquote("\"#{ $fa-var-mars-and-venus }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-intersex { #{$fa-icon-property}: unquote("\"#{ $fa-var-mars-and-venus }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-transgender-alt { #{$fa-icon-property}: unquote("\"#{ $fa-var-transgender }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-official { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-facebook-official { #{$fa-icon-property}: unquote("\"#{ $fa-var-facebook }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-pinterest-p { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-whatsapp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hotel { #{$fa-icon-property}: unquote("\"#{ $fa-var-bed }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-viacoin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-medium { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-y-combinator { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yc { #{$fa-icon-property}: unquote("\"#{ $fa-var-y-combinator }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-optin-monster { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-opencart { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-expeditedssl { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-4 { #{$fa-icon-property}: unquote("\"#{ $fa-var-battery-full }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery { #{$fa-icon-property}: unquote("\"#{ $fa-var-battery-full }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-3 { #{$fa-icon-property}: unquote("\"#{ $fa-var-battery-three-quarters }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-2 { #{$fa-icon-property}: unquote("\"#{ $fa-var-battery-half }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-1 { #{$fa-icon-property}: unquote("\"#{ $fa-var-battery-quarter }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-battery-0 { #{$fa-icon-property}: unquote("\"#{ $fa-var-battery-empty }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-object-group { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-object-ungroup { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-sticky-note-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-sticky-note-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-note-sticky }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-jcb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-cc-diners-club { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-clone { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hourglass }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-1 { #{$fa-icon-property}: unquote("\"#{ $fa-var-hourglass-start }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-2 { #{$fa-icon-property}: unquote("\"#{ $fa-var-hourglass-half }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hourglass-3 { #{$fa-icon-property}: unquote("\"#{ $fa-var-hourglass-end }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-rock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-rock-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-back-fist }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-grab-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-grab-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-back-fist }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-paper-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-paper-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-stop-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-stop-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-scissors-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-scissors-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-scissors }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-lizard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-lizard-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-lizard }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-spock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-spock-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-spock }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-pointer-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-pointer-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-pointer }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-peace-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-hand-peace-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-hand-peace }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-registered { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-creative-commons { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-gg { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-gg-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-odnoklassniki-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-odnoklassniki }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-get-pocket { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wikipedia-w { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-safari { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-chrome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-firefox { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-opera { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-internet-explorer { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-television { #{$fa-icon-property}: unquote("\"#{ $fa-var-tv }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-contao { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-500px { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-amazon { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-plus-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-plus-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-calendar-plus }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-minus-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-minus-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-calendar-minus }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-times-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-times-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-calendar-xmark }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-check-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-calendar-check-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-calendar-check }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-map-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-map }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting { #{$fa-icon-property}: unquote("\"#{ $fa-var-comment-dots }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-commenting-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-comment-dots }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-houzz { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vimeo { #{$fa-icon-property}: unquote("\"#{ $fa-var-vimeo-v }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-black-tie { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-fonticons { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-reddit-alien { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-edge { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-credit-card-alt { #{$fa-icon-property}: unquote("\"#{ $fa-var-credit-card }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-codiepie { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-modx { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-fort-awesome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-usb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-product-hunt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-mixcloud { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-scribd { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pause-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-pause-circle-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-pause }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-stop-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-stop-circle-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-stop }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-bluetooth { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-bluetooth-b { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-gitlab { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpbeginner { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpforms { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-envira { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wheelchair-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wheelchair-alt { #{$fa-icon-property}: unquote("\"#{ $fa-var-accessible-icon }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-question-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-question-circle-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-question }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-volume-control-phone { #{$fa-icon-property}: unquote("\"#{ $fa-var-phone-volume }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-asl-interpreting { #{$fa-icon-property}: unquote("\"#{ $fa-var-hands-asl-interpreting }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-deafness { #{$fa-icon-property}: unquote("\"#{ $fa-var-ear-deaf }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-hard-of-hearing { #{$fa-icon-property}: unquote("\"#{ $fa-var-ear-deaf }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-glide { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-glide-g { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-signing { #{$fa-icon-property}: unquote("\"#{ $fa-var-hands }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-viadeo-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-viadeo }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-ghost { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-ghost { #{$fa-icon-property}: unquote("\"#{ $fa-var-snapchat }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snapchat-square { #{$fa-icon-property}: unquote("\"#{ $fa-var-square-snapchat }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-pied-piper { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-first-order { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-yoast { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-themeisle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-official { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-official { #{$fa-icon-property}: unquote("\"#{ $fa-var-google-plus }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-google-plus-circle { #{$fa-icon-property}: unquote("\"#{ $fa-var-google-plus }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-font-awesome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-fa { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-fa { #{$fa-icon-property}: unquote("\"#{ $fa-var-font-awesome }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-handshake-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-handshake-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-handshake }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-open-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-envelope-open-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-envelope-open }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-linode { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-book-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-book-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-address-book }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard { #{$fa-icon-property}: unquote("\"#{ $fa-var-address-card }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-card-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-address-card-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-address-card }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-vcard-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-address-card }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-circle-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-circle-user }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-user-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-user }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-badge { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license { #{$fa-icon-property}: unquote("\"#{ $fa-var-id-card }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-card-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-id-card-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-id-card }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-drivers-license-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-id-card }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-quora { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-free-code-camp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-telegram { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-4 { #{$fa-icon-property}: unquote("\"#{ $fa-var-temperature-full }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer { #{$fa-icon-property}: unquote("\"#{ $fa-var-temperature-full }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-3 { #{$fa-icon-property}: unquote("\"#{ $fa-var-temperature-three-quarters }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-2 { #{$fa-icon-property}: unquote("\"#{ $fa-var-temperature-half }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-1 { #{$fa-icon-property}: unquote("\"#{ $fa-var-temperature-quarter }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-thermometer-0 { #{$fa-icon-property}: unquote("\"#{ $fa-var-temperature-empty }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-bathtub { #{$fa-icon-property}: unquote("\"#{ $fa-var-bath }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-s15 { #{$fa-icon-property}: unquote("\"#{ $fa-var-bath }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-maximize { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-restore { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle { #{$fa-icon-property}: unquote("\"#{ $fa-var-rectangle-xmark }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-close-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-window-close-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-rectangle-xmark }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-times-rectangle-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-rectangle-xmark }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-bandcamp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-grav { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-etsy { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-imdb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-ravelry { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-eercast { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-eercast { #{$fa-icon-property}: unquote("\"#{ $fa-var-sellcast }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-snowflake-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-snowflake-o { #{$fa-icon-property}: unquote("\"#{ $fa-var-snowflake }\""); } +.#{$fa-css-prefix}.#{$fa-css-prefix}-superpowers { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-wpexplorer { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} +.#{$fa-css-prefix}.#{$fa-css-prefix}-meetup { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; +} diff --git a/src/sass/fontawesome/_sizing.scss b/src/sass/fontawesome/_sizing.scss new file mode 100644 index 0000000..e171e7d --- /dev/null +++ b/src/sass/fontawesome/_sizing.scss @@ -0,0 +1,16 @@ +// sizing icons +// ------------------------- + +// literal magnification scale +@for $i from 1 through 10 { + .#{$fa-css-prefix}-#{$i}x { + font-size: $i * 1em; + } +} + +// step-based scale (with alignment) +@each $size, $value in $fa-sizes { + .#{$fa-css-prefix}-#{$size} { + @include fa-size($value); + } +} diff --git a/src/sass/fontawesome/_stacked.scss b/src/sass/fontawesome/_stacked.scss new file mode 100644 index 0000000..d9a9d4e --- /dev/null +++ b/src/sass/fontawesome/_stacked.scss @@ -0,0 +1,32 @@ +// stacking icons +// ------------------------- + +.#{$fa-css-prefix}-stack { + display: inline-block; + height: 2em; + line-height: 2em; + position: relative; + vertical-align: $fa-stack-vertical-align; + width: $fa-stack-width; +} + +.#{$fa-css-prefix}-stack-1x, +.#{$fa-css-prefix}-stack-2x { + left: 0; + position: absolute; + text-align: center; + width: 100%; + z-index: var(--#{$fa-css-prefix}-stack-z-index, #{$fa-stack-z-index}); +} + +.#{$fa-css-prefix}-stack-1x { + line-height: inherit; +} + +.#{$fa-css-prefix}-stack-2x { + font-size: 2em; +} + +.#{$fa-css-prefix}-inverse { + color: var(--#{$fa-css-prefix}-inverse, #{$fa-inverse}); +} diff --git a/src/sass/fontawesome/_variables.scss b/src/sass/fontawesome/_variables.scss new file mode 100644 index 0000000..87d19d3 --- /dev/null +++ b/src/sass/fontawesome/_variables.scss @@ -0,0 +1,5044 @@ +// variables +// -------------------------- + +$fa-css-prefix : fa !default; +$fa-style : 900 !default; +$fa-style-family : "Font Awesome 6 Free" !default; + +$fa-icon-property : --fa; +$fa-duotone-icon-property : --fa--fa; + +$fa-display : inline-block !default; + +$fa-fw-width : fa-divide(20em, 16) !default; +$fa-inverse : #fff !default; + +$fa-border-color : #eee !default; +$fa-border-padding : .2em .25em .15em !default; +$fa-border-radius : .1em !default; +$fa-border-style : solid !default; +$fa-border-width : .08em !default; + +$fa-size-scale-2xs : 10 !default; +$fa-size-scale-xs : 12 !default; +$fa-size-scale-sm : 14 !default; +$fa-size-scale-base : 16 !default; +$fa-size-scale-lg : 20 !default; +$fa-size-scale-xl : 24 !default; +$fa-size-scale-2xl : 32 !default; + +$fa-sizes: ( + "2xs" : $fa-size-scale-2xs, + "xs" : $fa-size-scale-xs, + "sm" : $fa-size-scale-sm, + "lg" : $fa-size-scale-lg, + "xl" : $fa-size-scale-xl, + "2xl" : $fa-size-scale-2xl +) !default; + +$fa-li-width : 2em !default; +$fa-li-margin : $fa-li-width * fa-divide(5, 4) !default; + +$fa-pull-margin : .3em !default; + +$fa-primary-opacity : 1 !default; +$fa-secondary-opacity : .4 !default; + +$fa-stack-vertical-align : middle !default; +$fa-stack-width : ($fa-fw-width * 2) !default; +$fa-stack-z-index : auto !default; + +$fa-font-display : block !default; +$fa-font-path : "../../webfonts" !default; + +$fa-var-0: \30; +$fa-var-1: \31; +$fa-var-2: \32; +$fa-var-3: \33; +$fa-var-4: \34; +$fa-var-5: \35; +$fa-var-6: \36; +$fa-var-7: \37; +$fa-var-8: \38; +$fa-var-9: \39; +$fa-var-fill-drip: \f576; +$fa-var-arrows-to-circle: \e4bd; +$fa-var-circle-chevron-right: \f138; +$fa-var-chevron-circle-right: \f138; +$fa-var-at: \40; +$fa-var-trash-can: \f2ed; +$fa-var-trash-alt: \f2ed; +$fa-var-text-height: \f034; +$fa-var-user-xmark: \f235; +$fa-var-user-times: \f235; +$fa-var-stethoscope: \f0f1; +$fa-var-message: \f27a; +$fa-var-comment-alt: \f27a; +$fa-var-info: \f129; +$fa-var-down-left-and-up-right-to-center: \f422; +$fa-var-compress-alt: \f422; +$fa-var-explosion: \e4e9; +$fa-var-file-lines: \f15c; +$fa-var-file-alt: \f15c; +$fa-var-file-text: \f15c; +$fa-var-wave-square: \f83e; +$fa-var-ring: \f70b; +$fa-var-building-un: \e4d9; +$fa-var-dice-three: \f527; +$fa-var-calendar-days: \f073; +$fa-var-calendar-alt: \f073; +$fa-var-anchor-circle-check: \e4aa; +$fa-var-building-circle-arrow-right: \e4d1; +$fa-var-volleyball: \f45f; +$fa-var-volleyball-ball: \f45f; +$fa-var-arrows-up-to-line: \e4c2; +$fa-var-sort-down: \f0dd; +$fa-var-sort-desc: \f0dd; +$fa-var-circle-minus: \f056; +$fa-var-minus-circle: \f056; +$fa-var-door-open: \f52b; +$fa-var-right-from-bracket: \f2f5; +$fa-var-sign-out-alt: \f2f5; +$fa-var-atom: \f5d2; +$fa-var-soap: \e06e; +$fa-var-icons: \f86d; +$fa-var-heart-music-camera-bolt: \f86d; +$fa-var-microphone-lines-slash: \f539; +$fa-var-microphone-alt-slash: \f539; +$fa-var-bridge-circle-check: \e4c9; +$fa-var-pump-medical: \e06a; +$fa-var-fingerprint: \f577; +$fa-var-hand-point-right: \f0a4; +$fa-var-magnifying-glass-location: \f689; +$fa-var-search-location: \f689; +$fa-var-forward-step: \f051; +$fa-var-step-forward: \f051; +$fa-var-face-smile-beam: \f5b8; +$fa-var-smile-beam: \f5b8; +$fa-var-flag-checkered: \f11e; +$fa-var-football: \f44e; +$fa-var-football-ball: \f44e; +$fa-var-school-circle-exclamation: \e56c; +$fa-var-crop: \f125; +$fa-var-angles-down: \f103; +$fa-var-angle-double-down: \f103; +$fa-var-users-rectangle: \e594; +$fa-var-people-roof: \e537; +$fa-var-people-line: \e534; +$fa-var-beer-mug-empty: \f0fc; +$fa-var-beer: \f0fc; +$fa-var-diagram-predecessor: \e477; +$fa-var-arrow-up-long: \f176; +$fa-var-long-arrow-up: \f176; +$fa-var-fire-flame-simple: \f46a; +$fa-var-burn: \f46a; +$fa-var-person: \f183; +$fa-var-male: \f183; +$fa-var-laptop: \f109; +$fa-var-file-csv: \f6dd; +$fa-var-menorah: \f676; +$fa-var-truck-plane: \e58f; +$fa-var-record-vinyl: \f8d9; +$fa-var-face-grin-stars: \f587; +$fa-var-grin-stars: \f587; +$fa-var-bong: \f55c; +$fa-var-spaghetti-monster-flying: \f67b; +$fa-var-pastafarianism: \f67b; +$fa-var-arrow-down-up-across-line: \e4af; +$fa-var-spoon: \f2e5; +$fa-var-utensil-spoon: \f2e5; +$fa-var-jar-wheat: \e517; +$fa-var-envelopes-bulk: \f674; +$fa-var-mail-bulk: \f674; +$fa-var-file-circle-exclamation: \e4eb; +$fa-var-circle-h: \f47e; +$fa-var-hospital-symbol: \f47e; +$fa-var-pager: \f815; +$fa-var-address-book: \f2b9; +$fa-var-contact-book: \f2b9; +$fa-var-strikethrough: \f0cc; +$fa-var-k: \4b; +$fa-var-landmark-flag: \e51c; +$fa-var-pencil: \f303; +$fa-var-pencil-alt: \f303; +$fa-var-backward: \f04a; +$fa-var-caret-right: \f0da; +$fa-var-comments: \f086; +$fa-var-paste: \f0ea; +$fa-var-file-clipboard: \f0ea; +$fa-var-code-pull-request: \e13c; +$fa-var-clipboard-list: \f46d; +$fa-var-truck-ramp-box: \f4de; +$fa-var-truck-loading: \f4de; +$fa-var-user-check: \f4fc; +$fa-var-vial-virus: \e597; +$fa-var-sheet-plastic: \e571; +$fa-var-blog: \f781; +$fa-var-user-ninja: \f504; +$fa-var-person-arrow-up-from-line: \e539; +$fa-var-scroll-torah: \f6a0; +$fa-var-torah: \f6a0; +$fa-var-broom-ball: \f458; +$fa-var-quidditch: \f458; +$fa-var-quidditch-broom-ball: \f458; +$fa-var-toggle-off: \f204; +$fa-var-box-archive: \f187; +$fa-var-archive: \f187; +$fa-var-person-drowning: \e545; +$fa-var-arrow-down-9-1: \f886; +$fa-var-sort-numeric-desc: \f886; +$fa-var-sort-numeric-down-alt: \f886; +$fa-var-face-grin-tongue-squint: \f58a; +$fa-var-grin-tongue-squint: \f58a; +$fa-var-spray-can: \f5bd; +$fa-var-truck-monster: \f63b; +$fa-var-w: \57; +$fa-var-earth-africa: \f57c; +$fa-var-globe-africa: \f57c; +$fa-var-rainbow: \f75b; +$fa-var-circle-notch: \f1ce; +$fa-var-tablet-screen-button: \f3fa; +$fa-var-tablet-alt: \f3fa; +$fa-var-paw: \f1b0; +$fa-var-cloud: \f0c2; +$fa-var-trowel-bricks: \e58a; +$fa-var-face-flushed: \f579; +$fa-var-flushed: \f579; +$fa-var-hospital-user: \f80d; +$fa-var-tent-arrow-left-right: \e57f; +$fa-var-gavel: \f0e3; +$fa-var-legal: \f0e3; +$fa-var-binoculars: \f1e5; +$fa-var-microphone-slash: \f131; +$fa-var-box-tissue: \e05b; +$fa-var-motorcycle: \f21c; +$fa-var-bell-concierge: \f562; +$fa-var-concierge-bell: \f562; +$fa-var-pen-ruler: \f5ae; +$fa-var-pencil-ruler: \f5ae; +$fa-var-people-arrows: \e068; +$fa-var-people-arrows-left-right: \e068; +$fa-var-mars-and-venus-burst: \e523; +$fa-var-square-caret-right: \f152; +$fa-var-caret-square-right: \f152; +$fa-var-scissors: \f0c4; +$fa-var-cut: \f0c4; +$fa-var-sun-plant-wilt: \e57a; +$fa-var-toilets-portable: \e584; +$fa-var-hockey-puck: \f453; +$fa-var-table: \f0ce; +$fa-var-magnifying-glass-arrow-right: \e521; +$fa-var-tachograph-digital: \f566; +$fa-var-digital-tachograph: \f566; +$fa-var-users-slash: \e073; +$fa-var-clover: \e139; +$fa-var-reply: \f3e5; +$fa-var-mail-reply: \f3e5; +$fa-var-star-and-crescent: \f699; +$fa-var-house-fire: \e50c; +$fa-var-square-minus: \f146; +$fa-var-minus-square: \f146; +$fa-var-helicopter: \f533; +$fa-var-compass: \f14e; +$fa-var-square-caret-down: \f150; +$fa-var-caret-square-down: \f150; +$fa-var-file-circle-question: \e4ef; +$fa-var-laptop-code: \f5fc; +$fa-var-swatchbook: \f5c3; +$fa-var-prescription-bottle: \f485; +$fa-var-bars: \f0c9; +$fa-var-navicon: \f0c9; +$fa-var-people-group: \e533; +$fa-var-hourglass-end: \f253; +$fa-var-hourglass-3: \f253; +$fa-var-heart-crack: \f7a9; +$fa-var-heart-broken: \f7a9; +$fa-var-square-up-right: \f360; +$fa-var-external-link-square-alt: \f360; +$fa-var-face-kiss-beam: \f597; +$fa-var-kiss-beam: \f597; +$fa-var-film: \f008; +$fa-var-ruler-horizontal: \f547; +$fa-var-people-robbery: \e536; +$fa-var-lightbulb: \f0eb; +$fa-var-caret-left: \f0d9; +$fa-var-circle-exclamation: \f06a; +$fa-var-exclamation-circle: \f06a; +$fa-var-school-circle-xmark: \e56d; +$fa-var-arrow-right-from-bracket: \f08b; +$fa-var-sign-out: \f08b; +$fa-var-circle-chevron-down: \f13a; +$fa-var-chevron-circle-down: \f13a; +$fa-var-unlock-keyhole: \f13e; +$fa-var-unlock-alt: \f13e; +$fa-var-cloud-showers-heavy: \f740; +$fa-var-headphones-simple: \f58f; +$fa-var-headphones-alt: \f58f; +$fa-var-sitemap: \f0e8; +$fa-var-circle-dollar-to-slot: \f4b9; +$fa-var-donate: \f4b9; +$fa-var-memory: \f538; +$fa-var-road-spikes: \e568; +$fa-var-fire-burner: \e4f1; +$fa-var-flag: \f024; +$fa-var-hanukiah: \f6e6; +$fa-var-feather: \f52d; +$fa-var-volume-low: \f027; +$fa-var-volume-down: \f027; +$fa-var-comment-slash: \f4b3; +$fa-var-cloud-sun-rain: \f743; +$fa-var-compress: \f066; +$fa-var-wheat-awn: \e2cd; +$fa-var-wheat-alt: \e2cd; +$fa-var-ankh: \f644; +$fa-var-hands-holding-child: \e4fa; +$fa-var-asterisk: \2a; +$fa-var-square-check: \f14a; +$fa-var-check-square: \f14a; +$fa-var-peseta-sign: \e221; +$fa-var-heading: \f1dc; +$fa-var-header: \f1dc; +$fa-var-ghost: \f6e2; +$fa-var-list: \f03a; +$fa-var-list-squares: \f03a; +$fa-var-square-phone-flip: \f87b; +$fa-var-phone-square-alt: \f87b; +$fa-var-cart-plus: \f217; +$fa-var-gamepad: \f11b; +$fa-var-circle-dot: \f192; +$fa-var-dot-circle: \f192; +$fa-var-face-dizzy: \f567; +$fa-var-dizzy: \f567; +$fa-var-egg: \f7fb; +$fa-var-house-medical-circle-xmark: \e513; +$fa-var-campground: \f6bb; +$fa-var-folder-plus: \f65e; +$fa-var-futbol: \f1e3; +$fa-var-futbol-ball: \f1e3; +$fa-var-soccer-ball: \f1e3; +$fa-var-paintbrush: \f1fc; +$fa-var-paint-brush: \f1fc; +$fa-var-lock: \f023; +$fa-var-gas-pump: \f52f; +$fa-var-hot-tub-person: \f593; +$fa-var-hot-tub: \f593; +$fa-var-map-location: \f59f; +$fa-var-map-marked: \f59f; +$fa-var-house-flood-water: \e50e; +$fa-var-tree: \f1bb; +$fa-var-bridge-lock: \e4cc; +$fa-var-sack-dollar: \f81d; +$fa-var-pen-to-square: \f044; +$fa-var-edit: \f044; +$fa-var-car-side: \f5e4; +$fa-var-share-nodes: \f1e0; +$fa-var-share-alt: \f1e0; +$fa-var-heart-circle-minus: \e4ff; +$fa-var-hourglass-half: \f252; +$fa-var-hourglass-2: \f252; +$fa-var-microscope: \f610; +$fa-var-sink: \e06d; +$fa-var-bag-shopping: \f290; +$fa-var-shopping-bag: \f290; +$fa-var-arrow-down-z-a: \f881; +$fa-var-sort-alpha-desc: \f881; +$fa-var-sort-alpha-down-alt: \f881; +$fa-var-mitten: \f7b5; +$fa-var-person-rays: \e54d; +$fa-var-users: \f0c0; +$fa-var-eye-slash: \f070; +$fa-var-flask-vial: \e4f3; +$fa-var-hand: \f256; +$fa-var-hand-paper: \f256; +$fa-var-om: \f679; +$fa-var-worm: \e599; +$fa-var-house-circle-xmark: \e50b; +$fa-var-plug: \f1e6; +$fa-var-chevron-up: \f077; +$fa-var-hand-spock: \f259; +$fa-var-stopwatch: \f2f2; +$fa-var-face-kiss: \f596; +$fa-var-kiss: \f596; +$fa-var-bridge-circle-xmark: \e4cb; +$fa-var-face-grin-tongue: \f589; +$fa-var-grin-tongue: \f589; +$fa-var-chess-bishop: \f43a; +$fa-var-face-grin-wink: \f58c; +$fa-var-grin-wink: \f58c; +$fa-var-ear-deaf: \f2a4; +$fa-var-deaf: \f2a4; +$fa-var-deafness: \f2a4; +$fa-var-hard-of-hearing: \f2a4; +$fa-var-road-circle-check: \e564; +$fa-var-dice-five: \f523; +$fa-var-square-rss: \f143; +$fa-var-rss-square: \f143; +$fa-var-land-mine-on: \e51b; +$fa-var-i-cursor: \f246; +$fa-var-stamp: \f5bf; +$fa-var-stairs: \e289; +$fa-var-i: \49; +$fa-var-hryvnia-sign: \f6f2; +$fa-var-hryvnia: \f6f2; +$fa-var-pills: \f484; +$fa-var-face-grin-wide: \f581; +$fa-var-grin-alt: \f581; +$fa-var-tooth: \f5c9; +$fa-var-v: \56; +$fa-var-bangladeshi-taka-sign: \e2e6; +$fa-var-bicycle: \f206; +$fa-var-staff-snake: \e579; +$fa-var-rod-asclepius: \e579; +$fa-var-rod-snake: \e579; +$fa-var-staff-aesculapius: \e579; +$fa-var-head-side-cough-slash: \e062; +$fa-var-truck-medical: \f0f9; +$fa-var-ambulance: \f0f9; +$fa-var-wheat-awn-circle-exclamation: \e598; +$fa-var-snowman: \f7d0; +$fa-var-mortar-pestle: \f5a7; +$fa-var-road-barrier: \e562; +$fa-var-school: \f549; +$fa-var-igloo: \f7ae; +$fa-var-joint: \f595; +$fa-var-angle-right: \f105; +$fa-var-horse: \f6f0; +$fa-var-q: \51; +$fa-var-g: \47; +$fa-var-notes-medical: \f481; +$fa-var-temperature-half: \f2c9; +$fa-var-temperature-2: \f2c9; +$fa-var-thermometer-2: \f2c9; +$fa-var-thermometer-half: \f2c9; +$fa-var-dong-sign: \e169; +$fa-var-capsules: \f46b; +$fa-var-poo-storm: \f75a; +$fa-var-poo-bolt: \f75a; +$fa-var-face-frown-open: \f57a; +$fa-var-frown-open: \f57a; +$fa-var-hand-point-up: \f0a6; +$fa-var-money-bill: \f0d6; +$fa-var-bookmark: \f02e; +$fa-var-align-justify: \f039; +$fa-var-umbrella-beach: \f5ca; +$fa-var-helmet-un: \e503; +$fa-var-bullseye: \f140; +$fa-var-bacon: \f7e5; +$fa-var-hand-point-down: \f0a7; +$fa-var-arrow-up-from-bracket: \e09a; +$fa-var-folder: \f07b; +$fa-var-folder-blank: \f07b; +$fa-var-file-waveform: \f478; +$fa-var-file-medical-alt: \f478; +$fa-var-radiation: \f7b9; +$fa-var-chart-simple: \e473; +$fa-var-mars-stroke: \f229; +$fa-var-vial: \f492; +$fa-var-gauge: \f624; +$fa-var-dashboard: \f624; +$fa-var-gauge-med: \f624; +$fa-var-tachometer-alt-average: \f624; +$fa-var-wand-magic-sparkles: \e2ca; +$fa-var-magic-wand-sparkles: \e2ca; +$fa-var-e: \45; +$fa-var-pen-clip: \f305; +$fa-var-pen-alt: \f305; +$fa-var-bridge-circle-exclamation: \e4ca; +$fa-var-user: \f007; +$fa-var-school-circle-check: \e56b; +$fa-var-dumpster: \f793; +$fa-var-van-shuttle: \f5b6; +$fa-var-shuttle-van: \f5b6; +$fa-var-building-user: \e4da; +$fa-var-square-caret-left: \f191; +$fa-var-caret-square-left: \f191; +$fa-var-highlighter: \f591; +$fa-var-key: \f084; +$fa-var-bullhorn: \f0a1; +$fa-var-globe: \f0ac; +$fa-var-synagogue: \f69b; +$fa-var-person-half-dress: \e548; +$fa-var-road-bridge: \e563; +$fa-var-location-arrow: \f124; +$fa-var-c: \43; +$fa-var-tablet-button: \f10a; +$fa-var-building-lock: \e4d6; +$fa-var-pizza-slice: \f818; +$fa-var-money-bill-wave: \f53a; +$fa-var-chart-area: \f1fe; +$fa-var-area-chart: \f1fe; +$fa-var-house-flag: \e50d; +$fa-var-person-circle-minus: \e540; +$fa-var-ban: \f05e; +$fa-var-cancel: \f05e; +$fa-var-camera-rotate: \e0d8; +$fa-var-spray-can-sparkles: \f5d0; +$fa-var-air-freshener: \f5d0; +$fa-var-star: \f005; +$fa-var-repeat: \f363; +$fa-var-cross: \f654; +$fa-var-box: \f466; +$fa-var-venus-mars: \f228; +$fa-var-arrow-pointer: \f245; +$fa-var-mouse-pointer: \f245; +$fa-var-maximize: \f31e; +$fa-var-expand-arrows-alt: \f31e; +$fa-var-charging-station: \f5e7; +$fa-var-shapes: \f61f; +$fa-var-triangle-circle-square: \f61f; +$fa-var-shuffle: \f074; +$fa-var-random: \f074; +$fa-var-person-running: \f70c; +$fa-var-running: \f70c; +$fa-var-mobile-retro: \e527; +$fa-var-grip-lines-vertical: \f7a5; +$fa-var-spider: \f717; +$fa-var-hands-bound: \e4f9; +$fa-var-file-invoice-dollar: \f571; +$fa-var-plane-circle-exclamation: \e556; +$fa-var-x-ray: \f497; +$fa-var-spell-check: \f891; +$fa-var-slash: \f715; +$fa-var-computer-mouse: \f8cc; +$fa-var-mouse: \f8cc; +$fa-var-arrow-right-to-bracket: \f090; +$fa-var-sign-in: \f090; +$fa-var-shop-slash: \e070; +$fa-var-store-alt-slash: \e070; +$fa-var-server: \f233; +$fa-var-virus-covid-slash: \e4a9; +$fa-var-shop-lock: \e4a5; +$fa-var-hourglass-start: \f251; +$fa-var-hourglass-1: \f251; +$fa-var-blender-phone: \f6b6; +$fa-var-building-wheat: \e4db; +$fa-var-person-breastfeeding: \e53a; +$fa-var-right-to-bracket: \f2f6; +$fa-var-sign-in-alt: \f2f6; +$fa-var-venus: \f221; +$fa-var-passport: \f5ab; +$fa-var-thumbtack-slash: \e68f; +$fa-var-thumb-tack-slash: \e68f; +$fa-var-heart-pulse: \f21e; +$fa-var-heartbeat: \f21e; +$fa-var-people-carry-box: \f4ce; +$fa-var-people-carry: \f4ce; +$fa-var-temperature-high: \f769; +$fa-var-microchip: \f2db; +$fa-var-crown: \f521; +$fa-var-weight-hanging: \f5cd; +$fa-var-xmarks-lines: \e59a; +$fa-var-file-prescription: \f572; +$fa-var-weight-scale: \f496; +$fa-var-weight: \f496; +$fa-var-user-group: \f500; +$fa-var-user-friends: \f500; +$fa-var-arrow-up-a-z: \f15e; +$fa-var-sort-alpha-up: \f15e; +$fa-var-chess-knight: \f441; +$fa-var-face-laugh-squint: \f59b; +$fa-var-laugh-squint: \f59b; +$fa-var-wheelchair: \f193; +$fa-var-circle-arrow-up: \f0aa; +$fa-var-arrow-circle-up: \f0aa; +$fa-var-toggle-on: \f205; +$fa-var-person-walking: \f554; +$fa-var-walking: \f554; +$fa-var-l: \4c; +$fa-var-fire: \f06d; +$fa-var-bed-pulse: \f487; +$fa-var-procedures: \f487; +$fa-var-shuttle-space: \f197; +$fa-var-space-shuttle: \f197; +$fa-var-face-laugh: \f599; +$fa-var-laugh: \f599; +$fa-var-folder-open: \f07c; +$fa-var-heart-circle-plus: \e500; +$fa-var-code-fork: \e13b; +$fa-var-city: \f64f; +$fa-var-microphone-lines: \f3c9; +$fa-var-microphone-alt: \f3c9; +$fa-var-pepper-hot: \f816; +$fa-var-unlock: \f09c; +$fa-var-colon-sign: \e140; +$fa-var-headset: \f590; +$fa-var-store-slash: \e071; +$fa-var-road-circle-xmark: \e566; +$fa-var-user-minus: \f503; +$fa-var-mars-stroke-up: \f22a; +$fa-var-mars-stroke-v: \f22a; +$fa-var-champagne-glasses: \f79f; +$fa-var-glass-cheers: \f79f; +$fa-var-clipboard: \f328; +$fa-var-house-circle-exclamation: \e50a; +$fa-var-file-arrow-up: \f574; +$fa-var-file-upload: \f574; +$fa-var-wifi: \f1eb; +$fa-var-wifi-3: \f1eb; +$fa-var-wifi-strong: \f1eb; +$fa-var-bath: \f2cd; +$fa-var-bathtub: \f2cd; +$fa-var-underline: \f0cd; +$fa-var-user-pen: \f4ff; +$fa-var-user-edit: \f4ff; +$fa-var-signature: \f5b7; +$fa-var-stroopwafel: \f551; +$fa-var-bold: \f032; +$fa-var-anchor-lock: \e4ad; +$fa-var-building-ngo: \e4d7; +$fa-var-manat-sign: \e1d5; +$fa-var-not-equal: \f53e; +$fa-var-border-top-left: \f853; +$fa-var-border-style: \f853; +$fa-var-map-location-dot: \f5a0; +$fa-var-map-marked-alt: \f5a0; +$fa-var-jedi: \f669; +$fa-var-square-poll-vertical: \f681; +$fa-var-poll: \f681; +$fa-var-mug-hot: \f7b6; +$fa-var-car-battery: \f5df; +$fa-var-battery-car: \f5df; +$fa-var-gift: \f06b; +$fa-var-dice-two: \f528; +$fa-var-chess-queen: \f445; +$fa-var-glasses: \f530; +$fa-var-chess-board: \f43c; +$fa-var-building-circle-check: \e4d2; +$fa-var-person-chalkboard: \e53d; +$fa-var-mars-stroke-right: \f22b; +$fa-var-mars-stroke-h: \f22b; +$fa-var-hand-back-fist: \f255; +$fa-var-hand-rock: \f255; +$fa-var-square-caret-up: \f151; +$fa-var-caret-square-up: \f151; +$fa-var-cloud-showers-water: \e4e4; +$fa-var-chart-bar: \f080; +$fa-var-bar-chart: \f080; +$fa-var-hands-bubbles: \e05e; +$fa-var-hands-wash: \e05e; +$fa-var-less-than-equal: \f537; +$fa-var-train: \f238; +$fa-var-eye-low-vision: \f2a8; +$fa-var-low-vision: \f2a8; +$fa-var-crow: \f520; +$fa-var-sailboat: \e445; +$fa-var-window-restore: \f2d2; +$fa-var-square-plus: \f0fe; +$fa-var-plus-square: \f0fe; +$fa-var-torii-gate: \f6a1; +$fa-var-frog: \f52e; +$fa-var-bucket: \e4cf; +$fa-var-image: \f03e; +$fa-var-microphone: \f130; +$fa-var-cow: \f6c8; +$fa-var-caret-up: \f0d8; +$fa-var-screwdriver: \f54a; +$fa-var-folder-closed: \e185; +$fa-var-house-tsunami: \e515; +$fa-var-square-nfi: \e576; +$fa-var-arrow-up-from-ground-water: \e4b5; +$fa-var-martini-glass: \f57b; +$fa-var-glass-martini-alt: \f57b; +$fa-var-square-binary: \e69b; +$fa-var-rotate-left: \f2ea; +$fa-var-rotate-back: \f2ea; +$fa-var-rotate-backward: \f2ea; +$fa-var-undo-alt: \f2ea; +$fa-var-table-columns: \f0db; +$fa-var-columns: \f0db; +$fa-var-lemon: \f094; +$fa-var-head-side-mask: \e063; +$fa-var-handshake: \f2b5; +$fa-var-gem: \f3a5; +$fa-var-dolly: \f472; +$fa-var-dolly-box: \f472; +$fa-var-smoking: \f48d; +$fa-var-minimize: \f78c; +$fa-var-compress-arrows-alt: \f78c; +$fa-var-monument: \f5a6; +$fa-var-snowplow: \f7d2; +$fa-var-angles-right: \f101; +$fa-var-angle-double-right: \f101; +$fa-var-cannabis: \f55f; +$fa-var-circle-play: \f144; +$fa-var-play-circle: \f144; +$fa-var-tablets: \f490; +$fa-var-ethernet: \f796; +$fa-var-euro-sign: \f153; +$fa-var-eur: \f153; +$fa-var-euro: \f153; +$fa-var-chair: \f6c0; +$fa-var-circle-check: \f058; +$fa-var-check-circle: \f058; +$fa-var-circle-stop: \f28d; +$fa-var-stop-circle: \f28d; +$fa-var-compass-drafting: \f568; +$fa-var-drafting-compass: \f568; +$fa-var-plate-wheat: \e55a; +$fa-var-icicles: \f7ad; +$fa-var-person-shelter: \e54f; +$fa-var-neuter: \f22c; +$fa-var-id-badge: \f2c1; +$fa-var-marker: \f5a1; +$fa-var-face-laugh-beam: \f59a; +$fa-var-laugh-beam: \f59a; +$fa-var-helicopter-symbol: \e502; +$fa-var-universal-access: \f29a; +$fa-var-circle-chevron-up: \f139; +$fa-var-chevron-circle-up: \f139; +$fa-var-lari-sign: \e1c8; +$fa-var-volcano: \f770; +$fa-var-person-walking-dashed-line-arrow-right: \e553; +$fa-var-sterling-sign: \f154; +$fa-var-gbp: \f154; +$fa-var-pound-sign: \f154; +$fa-var-viruses: \e076; +$fa-var-square-person-confined: \e577; +$fa-var-user-tie: \f508; +$fa-var-arrow-down-long: \f175; +$fa-var-long-arrow-down: \f175; +$fa-var-tent-arrow-down-to-line: \e57e; +$fa-var-certificate: \f0a3; +$fa-var-reply-all: \f122; +$fa-var-mail-reply-all: \f122; +$fa-var-suitcase: \f0f2; +$fa-var-person-skating: \f7c5; +$fa-var-skating: \f7c5; +$fa-var-filter-circle-dollar: \f662; +$fa-var-funnel-dollar: \f662; +$fa-var-camera-retro: \f083; +$fa-var-circle-arrow-down: \f0ab; +$fa-var-arrow-circle-down: \f0ab; +$fa-var-file-import: \f56f; +$fa-var-arrow-right-to-file: \f56f; +$fa-var-square-arrow-up-right: \f14c; +$fa-var-external-link-square: \f14c; +$fa-var-box-open: \f49e; +$fa-var-scroll: \f70e; +$fa-var-spa: \f5bb; +$fa-var-location-pin-lock: \e51f; +$fa-var-pause: \f04c; +$fa-var-hill-avalanche: \e507; +$fa-var-temperature-empty: \f2cb; +$fa-var-temperature-0: \f2cb; +$fa-var-thermometer-0: \f2cb; +$fa-var-thermometer-empty: \f2cb; +$fa-var-bomb: \f1e2; +$fa-var-registered: \f25d; +$fa-var-address-card: \f2bb; +$fa-var-contact-card: \f2bb; +$fa-var-vcard: \f2bb; +$fa-var-scale-unbalanced-flip: \f516; +$fa-var-balance-scale-right: \f516; +$fa-var-subscript: \f12c; +$fa-var-diamond-turn-right: \f5eb; +$fa-var-directions: \f5eb; +$fa-var-burst: \e4dc; +$fa-var-house-laptop: \e066; +$fa-var-laptop-house: \e066; +$fa-var-face-tired: \f5c8; +$fa-var-tired: \f5c8; +$fa-var-money-bills: \e1f3; +$fa-var-smog: \f75f; +$fa-var-crutch: \f7f7; +$fa-var-cloud-arrow-up: \f0ee; +$fa-var-cloud-upload: \f0ee; +$fa-var-cloud-upload-alt: \f0ee; +$fa-var-palette: \f53f; +$fa-var-arrows-turn-right: \e4c0; +$fa-var-vest: \e085; +$fa-var-ferry: \e4ea; +$fa-var-arrows-down-to-people: \e4b9; +$fa-var-seedling: \f4d8; +$fa-var-sprout: \f4d8; +$fa-var-left-right: \f337; +$fa-var-arrows-alt-h: \f337; +$fa-var-boxes-packing: \e4c7; +$fa-var-circle-arrow-left: \f0a8; +$fa-var-arrow-circle-left: \f0a8; +$fa-var-group-arrows-rotate: \e4f6; +$fa-var-bowl-food: \e4c6; +$fa-var-candy-cane: \f786; +$fa-var-arrow-down-wide-short: \f160; +$fa-var-sort-amount-asc: \f160; +$fa-var-sort-amount-down: \f160; +$fa-var-cloud-bolt: \f76c; +$fa-var-thunderstorm: \f76c; +$fa-var-text-slash: \f87d; +$fa-var-remove-format: \f87d; +$fa-var-face-smile-wink: \f4da; +$fa-var-smile-wink: \f4da; +$fa-var-file-word: \f1c2; +$fa-var-file-powerpoint: \f1c4; +$fa-var-arrows-left-right: \f07e; +$fa-var-arrows-h: \f07e; +$fa-var-house-lock: \e510; +$fa-var-cloud-arrow-down: \f0ed; +$fa-var-cloud-download: \f0ed; +$fa-var-cloud-download-alt: \f0ed; +$fa-var-children: \e4e1; +$fa-var-chalkboard: \f51b; +$fa-var-blackboard: \f51b; +$fa-var-user-large-slash: \f4fa; +$fa-var-user-alt-slash: \f4fa; +$fa-var-envelope-open: \f2b6; +$fa-var-handshake-simple-slash: \e05f; +$fa-var-handshake-alt-slash: \e05f; +$fa-var-mattress-pillow: \e525; +$fa-var-guarani-sign: \e19a; +$fa-var-arrows-rotate: \f021; +$fa-var-refresh: \f021; +$fa-var-sync: \f021; +$fa-var-fire-extinguisher: \f134; +$fa-var-cruzeiro-sign: \e152; +$fa-var-greater-than-equal: \f532; +$fa-var-shield-halved: \f3ed; +$fa-var-shield-alt: \f3ed; +$fa-var-book-atlas: \f558; +$fa-var-atlas: \f558; +$fa-var-virus: \e074; +$fa-var-envelope-circle-check: \e4e8; +$fa-var-layer-group: \f5fd; +$fa-var-arrows-to-dot: \e4be; +$fa-var-archway: \f557; +$fa-var-heart-circle-check: \e4fd; +$fa-var-house-chimney-crack: \f6f1; +$fa-var-house-damage: \f6f1; +$fa-var-file-zipper: \f1c6; +$fa-var-file-archive: \f1c6; +$fa-var-square: \f0c8; +$fa-var-martini-glass-empty: \f000; +$fa-var-glass-martini: \f000; +$fa-var-couch: \f4b8; +$fa-var-cedi-sign: \e0df; +$fa-var-italic: \f033; +$fa-var-table-cells-column-lock: \e678; +$fa-var-church: \f51d; +$fa-var-comments-dollar: \f653; +$fa-var-democrat: \f747; +$fa-var-z: \5a; +$fa-var-person-skiing: \f7c9; +$fa-var-skiing: \f7c9; +$fa-var-road-lock: \e567; +$fa-var-a: \41; +$fa-var-temperature-arrow-down: \e03f; +$fa-var-temperature-down: \e03f; +$fa-var-feather-pointed: \f56b; +$fa-var-feather-alt: \f56b; +$fa-var-p: \50; +$fa-var-snowflake: \f2dc; +$fa-var-newspaper: \f1ea; +$fa-var-rectangle-ad: \f641; +$fa-var-ad: \f641; +$fa-var-circle-arrow-right: \f0a9; +$fa-var-arrow-circle-right: \f0a9; +$fa-var-filter-circle-xmark: \e17b; +$fa-var-locust: \e520; +$fa-var-sort: \f0dc; +$fa-var-unsorted: \f0dc; +$fa-var-list-ol: \f0cb; +$fa-var-list-1-2: \f0cb; +$fa-var-list-numeric: \f0cb; +$fa-var-person-dress-burst: \e544; +$fa-var-money-check-dollar: \f53d; +$fa-var-money-check-alt: \f53d; +$fa-var-vector-square: \f5cb; +$fa-var-bread-slice: \f7ec; +$fa-var-language: \f1ab; +$fa-var-face-kiss-wink-heart: \f598; +$fa-var-kiss-wink-heart: \f598; +$fa-var-filter: \f0b0; +$fa-var-question: \3f; +$fa-var-file-signature: \f573; +$fa-var-up-down-left-right: \f0b2; +$fa-var-arrows-alt: \f0b2; +$fa-var-house-chimney-user: \e065; +$fa-var-hand-holding-heart: \f4be; +$fa-var-puzzle-piece: \f12e; +$fa-var-money-check: \f53c; +$fa-var-star-half-stroke: \f5c0; +$fa-var-star-half-alt: \f5c0; +$fa-var-code: \f121; +$fa-var-whiskey-glass: \f7a0; +$fa-var-glass-whiskey: \f7a0; +$fa-var-building-circle-exclamation: \e4d3; +$fa-var-magnifying-glass-chart: \e522; +$fa-var-arrow-up-right-from-square: \f08e; +$fa-var-external-link: \f08e; +$fa-var-cubes-stacked: \e4e6; +$fa-var-won-sign: \f159; +$fa-var-krw: \f159; +$fa-var-won: \f159; +$fa-var-virus-covid: \e4a8; +$fa-var-austral-sign: \e0a9; +$fa-var-f: \46; +$fa-var-leaf: \f06c; +$fa-var-road: \f018; +$fa-var-taxi: \f1ba; +$fa-var-cab: \f1ba; +$fa-var-person-circle-plus: \e541; +$fa-var-chart-pie: \f200; +$fa-var-pie-chart: \f200; +$fa-var-bolt-lightning: \e0b7; +$fa-var-sack-xmark: \e56a; +$fa-var-file-excel: \f1c3; +$fa-var-file-contract: \f56c; +$fa-var-fish-fins: \e4f2; +$fa-var-building-flag: \e4d5; +$fa-var-face-grin-beam: \f582; +$fa-var-grin-beam: \f582; +$fa-var-object-ungroup: \f248; +$fa-var-poop: \f619; +$fa-var-location-pin: \f041; +$fa-var-map-marker: \f041; +$fa-var-kaaba: \f66b; +$fa-var-toilet-paper: \f71e; +$fa-var-helmet-safety: \f807; +$fa-var-hard-hat: \f807; +$fa-var-hat-hard: \f807; +$fa-var-eject: \f052; +$fa-var-circle-right: \f35a; +$fa-var-arrow-alt-circle-right: \f35a; +$fa-var-plane-circle-check: \e555; +$fa-var-face-rolling-eyes: \f5a5; +$fa-var-meh-rolling-eyes: \f5a5; +$fa-var-object-group: \f247; +$fa-var-chart-line: \f201; +$fa-var-line-chart: \f201; +$fa-var-mask-ventilator: \e524; +$fa-var-arrow-right: \f061; +$fa-var-signs-post: \f277; +$fa-var-map-signs: \f277; +$fa-var-cash-register: \f788; +$fa-var-person-circle-question: \e542; +$fa-var-h: \48; +$fa-var-tarp: \e57b; +$fa-var-screwdriver-wrench: \f7d9; +$fa-var-tools: \f7d9; +$fa-var-arrows-to-eye: \e4bf; +$fa-var-plug-circle-bolt: \e55b; +$fa-var-heart: \f004; +$fa-var-mars-and-venus: \f224; +$fa-var-house-user: \e1b0; +$fa-var-home-user: \e1b0; +$fa-var-dumpster-fire: \f794; +$fa-var-house-crack: \e3b1; +$fa-var-martini-glass-citrus: \f561; +$fa-var-cocktail: \f561; +$fa-var-face-surprise: \f5c2; +$fa-var-surprise: \f5c2; +$fa-var-bottle-water: \e4c5; +$fa-var-circle-pause: \f28b; +$fa-var-pause-circle: \f28b; +$fa-var-toilet-paper-slash: \e072; +$fa-var-apple-whole: \f5d1; +$fa-var-apple-alt: \f5d1; +$fa-var-kitchen-set: \e51a; +$fa-var-r: \52; +$fa-var-temperature-quarter: \f2ca; +$fa-var-temperature-1: \f2ca; +$fa-var-thermometer-1: \f2ca; +$fa-var-thermometer-quarter: \f2ca; +$fa-var-cube: \f1b2; +$fa-var-bitcoin-sign: \e0b4; +$fa-var-shield-dog: \e573; +$fa-var-solar-panel: \f5ba; +$fa-var-lock-open: \f3c1; +$fa-var-elevator: \e16d; +$fa-var-money-bill-transfer: \e528; +$fa-var-money-bill-trend-up: \e529; +$fa-var-house-flood-water-circle-arrow-right: \e50f; +$fa-var-square-poll-horizontal: \f682; +$fa-var-poll-h: \f682; +$fa-var-circle: \f111; +$fa-var-backward-fast: \f049; +$fa-var-fast-backward: \f049; +$fa-var-recycle: \f1b8; +$fa-var-user-astronaut: \f4fb; +$fa-var-plane-slash: \e069; +$fa-var-trademark: \f25c; +$fa-var-basketball: \f434; +$fa-var-basketball-ball: \f434; +$fa-var-satellite-dish: \f7c0; +$fa-var-circle-up: \f35b; +$fa-var-arrow-alt-circle-up: \f35b; +$fa-var-mobile-screen-button: \f3cd; +$fa-var-mobile-alt: \f3cd; +$fa-var-volume-high: \f028; +$fa-var-volume-up: \f028; +$fa-var-users-rays: \e593; +$fa-var-wallet: \f555; +$fa-var-clipboard-check: \f46c; +$fa-var-file-audio: \f1c7; +$fa-var-burger: \f805; +$fa-var-hamburger: \f805; +$fa-var-wrench: \f0ad; +$fa-var-bugs: \e4d0; +$fa-var-rupee-sign: \f156; +$fa-var-rupee: \f156; +$fa-var-file-image: \f1c5; +$fa-var-circle-question: \f059; +$fa-var-question-circle: \f059; +$fa-var-plane-departure: \f5b0; +$fa-var-handshake-slash: \e060; +$fa-var-book-bookmark: \e0bb; +$fa-var-code-branch: \f126; +$fa-var-hat-cowboy: \f8c0; +$fa-var-bridge: \e4c8; +$fa-var-phone-flip: \f879; +$fa-var-phone-alt: \f879; +$fa-var-truck-front: \e2b7; +$fa-var-cat: \f6be; +$fa-var-anchor-circle-exclamation: \e4ab; +$fa-var-truck-field: \e58d; +$fa-var-route: \f4d7; +$fa-var-clipboard-question: \e4e3; +$fa-var-panorama: \e209; +$fa-var-comment-medical: \f7f5; +$fa-var-teeth-open: \f62f; +$fa-var-file-circle-minus: \e4ed; +$fa-var-tags: \f02c; +$fa-var-wine-glass: \f4e3; +$fa-var-forward-fast: \f050; +$fa-var-fast-forward: \f050; +$fa-var-face-meh-blank: \f5a4; +$fa-var-meh-blank: \f5a4; +$fa-var-square-parking: \f540; +$fa-var-parking: \f540; +$fa-var-house-signal: \e012; +$fa-var-bars-progress: \f828; +$fa-var-tasks-alt: \f828; +$fa-var-faucet-drip: \e006; +$fa-var-cart-flatbed: \f474; +$fa-var-dolly-flatbed: \f474; +$fa-var-ban-smoking: \f54d; +$fa-var-smoking-ban: \f54d; +$fa-var-terminal: \f120; +$fa-var-mobile-button: \f10b; +$fa-var-house-medical-flag: \e514; +$fa-var-basket-shopping: \f291; +$fa-var-shopping-basket: \f291; +$fa-var-tape: \f4db; +$fa-var-bus-simple: \f55e; +$fa-var-bus-alt: \f55e; +$fa-var-eye: \f06e; +$fa-var-face-sad-cry: \f5b3; +$fa-var-sad-cry: \f5b3; +$fa-var-audio-description: \f29e; +$fa-var-person-military-to-person: \e54c; +$fa-var-file-shield: \e4f0; +$fa-var-user-slash: \f506; +$fa-var-pen: \f304; +$fa-var-tower-observation: \e586; +$fa-var-file-code: \f1c9; +$fa-var-signal: \f012; +$fa-var-signal-5: \f012; +$fa-var-signal-perfect: \f012; +$fa-var-bus: \f207; +$fa-var-heart-circle-xmark: \e501; +$fa-var-house-chimney: \e3af; +$fa-var-home-lg: \e3af; +$fa-var-window-maximize: \f2d0; +$fa-var-face-frown: \f119; +$fa-var-frown: \f119; +$fa-var-prescription: \f5b1; +$fa-var-shop: \f54f; +$fa-var-store-alt: \f54f; +$fa-var-floppy-disk: \f0c7; +$fa-var-save: \f0c7; +$fa-var-vihara: \f6a7; +$fa-var-scale-unbalanced: \f515; +$fa-var-balance-scale-left: \f515; +$fa-var-sort-up: \f0de; +$fa-var-sort-asc: \f0de; +$fa-var-comment-dots: \f4ad; +$fa-var-commenting: \f4ad; +$fa-var-plant-wilt: \e5aa; +$fa-var-diamond: \f219; +$fa-var-face-grin-squint: \f585; +$fa-var-grin-squint: \f585; +$fa-var-hand-holding-dollar: \f4c0; +$fa-var-hand-holding-usd: \f4c0; +$fa-var-chart-diagram: \e695; +$fa-var-bacterium: \e05a; +$fa-var-hand-pointer: \f25a; +$fa-var-drum-steelpan: \f56a; +$fa-var-hand-scissors: \f257; +$fa-var-hands-praying: \f684; +$fa-var-praying-hands: \f684; +$fa-var-arrow-rotate-right: \f01e; +$fa-var-arrow-right-rotate: \f01e; +$fa-var-arrow-rotate-forward: \f01e; +$fa-var-redo: \f01e; +$fa-var-biohazard: \f780; +$fa-var-location-crosshairs: \f601; +$fa-var-location: \f601; +$fa-var-mars-double: \f227; +$fa-var-child-dress: \e59c; +$fa-var-users-between-lines: \e591; +$fa-var-lungs-virus: \e067; +$fa-var-face-grin-tears: \f588; +$fa-var-grin-tears: \f588; +$fa-var-phone: \f095; +$fa-var-calendar-xmark: \f273; +$fa-var-calendar-times: \f273; +$fa-var-child-reaching: \e59d; +$fa-var-head-side-virus: \e064; +$fa-var-user-gear: \f4fe; +$fa-var-user-cog: \f4fe; +$fa-var-arrow-up-1-9: \f163; +$fa-var-sort-numeric-up: \f163; +$fa-var-door-closed: \f52a; +$fa-var-shield-virus: \e06c; +$fa-var-dice-six: \f526; +$fa-var-mosquito-net: \e52c; +$fa-var-file-fragment: \e697; +$fa-var-bridge-water: \e4ce; +$fa-var-person-booth: \f756; +$fa-var-text-width: \f035; +$fa-var-hat-wizard: \f6e8; +$fa-var-pen-fancy: \f5ac; +$fa-var-person-digging: \f85e; +$fa-var-digging: \f85e; +$fa-var-trash: \f1f8; +$fa-var-gauge-simple: \f629; +$fa-var-gauge-simple-med: \f629; +$fa-var-tachometer-average: \f629; +$fa-var-book-medical: \f7e6; +$fa-var-poo: \f2fe; +$fa-var-quote-right: \f10e; +$fa-var-quote-right-alt: \f10e; +$fa-var-shirt: \f553; +$fa-var-t-shirt: \f553; +$fa-var-tshirt: \f553; +$fa-var-cubes: \f1b3; +$fa-var-divide: \f529; +$fa-var-tenge-sign: \f7d7; +$fa-var-tenge: \f7d7; +$fa-var-headphones: \f025; +$fa-var-hands-holding: \f4c2; +$fa-var-hands-clapping: \e1a8; +$fa-var-republican: \f75e; +$fa-var-arrow-left: \f060; +$fa-var-person-circle-xmark: \e543; +$fa-var-ruler: \f545; +$fa-var-align-left: \f036; +$fa-var-dice-d6: \f6d1; +$fa-var-restroom: \f7bd; +$fa-var-j: \4a; +$fa-var-users-viewfinder: \e595; +$fa-var-file-video: \f1c8; +$fa-var-up-right-from-square: \f35d; +$fa-var-external-link-alt: \f35d; +$fa-var-table-cells: \f00a; +$fa-var-th: \f00a; +$fa-var-file-pdf: \f1c1; +$fa-var-book-bible: \f647; +$fa-var-bible: \f647; +$fa-var-o: \4f; +$fa-var-suitcase-medical: \f0fa; +$fa-var-medkit: \f0fa; +$fa-var-user-secret: \f21b; +$fa-var-otter: \f700; +$fa-var-person-dress: \f182; +$fa-var-female: \f182; +$fa-var-comment-dollar: \f651; +$fa-var-business-time: \f64a; +$fa-var-briefcase-clock: \f64a; +$fa-var-table-cells-large: \f009; +$fa-var-th-large: \f009; +$fa-var-book-tanakh: \f827; +$fa-var-tanakh: \f827; +$fa-var-phone-volume: \f2a0; +$fa-var-volume-control-phone: \f2a0; +$fa-var-hat-cowboy-side: \f8c1; +$fa-var-clipboard-user: \f7f3; +$fa-var-child: \f1ae; +$fa-var-lira-sign: \f195; +$fa-var-satellite: \f7bf; +$fa-var-plane-lock: \e558; +$fa-var-tag: \f02b; +$fa-var-comment: \f075; +$fa-var-cake-candles: \f1fd; +$fa-var-birthday-cake: \f1fd; +$fa-var-cake: \f1fd; +$fa-var-envelope: \f0e0; +$fa-var-angles-up: \f102; +$fa-var-angle-double-up: \f102; +$fa-var-paperclip: \f0c6; +$fa-var-arrow-right-to-city: \e4b3; +$fa-var-ribbon: \f4d6; +$fa-var-lungs: \f604; +$fa-var-arrow-up-9-1: \f887; +$fa-var-sort-numeric-up-alt: \f887; +$fa-var-litecoin-sign: \e1d3; +$fa-var-border-none: \f850; +$fa-var-circle-nodes: \e4e2; +$fa-var-parachute-box: \f4cd; +$fa-var-indent: \f03c; +$fa-var-truck-field-un: \e58e; +$fa-var-hourglass: \f254; +$fa-var-hourglass-empty: \f254; +$fa-var-mountain: \f6fc; +$fa-var-user-doctor: \f0f0; +$fa-var-user-md: \f0f0; +$fa-var-circle-info: \f05a; +$fa-var-info-circle: \f05a; +$fa-var-cloud-meatball: \f73b; +$fa-var-camera: \f030; +$fa-var-camera-alt: \f030; +$fa-var-square-virus: \e578; +$fa-var-meteor: \f753; +$fa-var-car-on: \e4dd; +$fa-var-sleigh: \f7cc; +$fa-var-arrow-down-1-9: \f162; +$fa-var-sort-numeric-asc: \f162; +$fa-var-sort-numeric-down: \f162; +$fa-var-hand-holding-droplet: \f4c1; +$fa-var-hand-holding-water: \f4c1; +$fa-var-water: \f773; +$fa-var-calendar-check: \f274; +$fa-var-braille: \f2a1; +$fa-var-prescription-bottle-medical: \f486; +$fa-var-prescription-bottle-alt: \f486; +$fa-var-landmark: \f66f; +$fa-var-truck: \f0d1; +$fa-var-crosshairs: \f05b; +$fa-var-person-cane: \e53c; +$fa-var-tent: \e57d; +$fa-var-vest-patches: \e086; +$fa-var-check-double: \f560; +$fa-var-arrow-down-a-z: \f15d; +$fa-var-sort-alpha-asc: \f15d; +$fa-var-sort-alpha-down: \f15d; +$fa-var-money-bill-wheat: \e52a; +$fa-var-cookie: \f563; +$fa-var-arrow-rotate-left: \f0e2; +$fa-var-arrow-left-rotate: \f0e2; +$fa-var-arrow-rotate-back: \f0e2; +$fa-var-arrow-rotate-backward: \f0e2; +$fa-var-undo: \f0e2; +$fa-var-hard-drive: \f0a0; +$fa-var-hdd: \f0a0; +$fa-var-face-grin-squint-tears: \f586; +$fa-var-grin-squint-tears: \f586; +$fa-var-dumbbell: \f44b; +$fa-var-rectangle-list: \f022; +$fa-var-list-alt: \f022; +$fa-var-tarp-droplet: \e57c; +$fa-var-house-medical-circle-check: \e511; +$fa-var-person-skiing-nordic: \f7ca; +$fa-var-skiing-nordic: \f7ca; +$fa-var-calendar-plus: \f271; +$fa-var-plane-arrival: \f5af; +$fa-var-circle-left: \f359; +$fa-var-arrow-alt-circle-left: \f359; +$fa-var-train-subway: \f239; +$fa-var-subway: \f239; +$fa-var-chart-gantt: \e0e4; +$fa-var-indian-rupee-sign: \e1bc; +$fa-var-indian-rupee: \e1bc; +$fa-var-inr: \e1bc; +$fa-var-crop-simple: \f565; +$fa-var-crop-alt: \f565; +$fa-var-money-bill-1: \f3d1; +$fa-var-money-bill-alt: \f3d1; +$fa-var-left-long: \f30a; +$fa-var-long-arrow-alt-left: \f30a; +$fa-var-dna: \f471; +$fa-var-virus-slash: \e075; +$fa-var-minus: \f068; +$fa-var-subtract: \f068; +$fa-var-chess: \f439; +$fa-var-arrow-left-long: \f177; +$fa-var-long-arrow-left: \f177; +$fa-var-plug-circle-check: \e55c; +$fa-var-street-view: \f21d; +$fa-var-franc-sign: \e18f; +$fa-var-volume-off: \f026; +$fa-var-hands-asl-interpreting: \f2a3; +$fa-var-american-sign-language-interpreting: \f2a3; +$fa-var-asl-interpreting: \f2a3; +$fa-var-hands-american-sign-language-interpreting: \f2a3; +$fa-var-gear: \f013; +$fa-var-cog: \f013; +$fa-var-droplet-slash: \f5c7; +$fa-var-tint-slash: \f5c7; +$fa-var-mosque: \f678; +$fa-var-mosquito: \e52b; +$fa-var-star-of-david: \f69a; +$fa-var-person-military-rifle: \e54b; +$fa-var-cart-shopping: \f07a; +$fa-var-shopping-cart: \f07a; +$fa-var-vials: \f493; +$fa-var-plug-circle-plus: \e55f; +$fa-var-place-of-worship: \f67f; +$fa-var-grip-vertical: \f58e; +$fa-var-hexagon-nodes: \e699; +$fa-var-arrow-turn-up: \f148; +$fa-var-level-up: \f148; +$fa-var-u: \55; +$fa-var-square-root-variable: \f698; +$fa-var-square-root-alt: \f698; +$fa-var-clock: \f017; +$fa-var-clock-four: \f017; +$fa-var-backward-step: \f048; +$fa-var-step-backward: \f048; +$fa-var-pallet: \f482; +$fa-var-faucet: \e005; +$fa-var-baseball-bat-ball: \f432; +$fa-var-s: \53; +$fa-var-timeline: \e29c; +$fa-var-keyboard: \f11c; +$fa-var-caret-down: \f0d7; +$fa-var-house-chimney-medical: \f7f2; +$fa-var-clinic-medical: \f7f2; +$fa-var-temperature-three-quarters: \f2c8; +$fa-var-temperature-3: \f2c8; +$fa-var-thermometer-3: \f2c8; +$fa-var-thermometer-three-quarters: \f2c8; +$fa-var-mobile-screen: \f3cf; +$fa-var-mobile-android-alt: \f3cf; +$fa-var-plane-up: \e22d; +$fa-var-piggy-bank: \f4d3; +$fa-var-battery-half: \f242; +$fa-var-battery-3: \f242; +$fa-var-mountain-city: \e52e; +$fa-var-coins: \f51e; +$fa-var-khanda: \f66d; +$fa-var-sliders: \f1de; +$fa-var-sliders-h: \f1de; +$fa-var-folder-tree: \f802; +$fa-var-network-wired: \f6ff; +$fa-var-map-pin: \f276; +$fa-var-hamsa: \f665; +$fa-var-cent-sign: \e3f5; +$fa-var-flask: \f0c3; +$fa-var-person-pregnant: \e31e; +$fa-var-wand-sparkles: \f72b; +$fa-var-ellipsis-vertical: \f142; +$fa-var-ellipsis-v: \f142; +$fa-var-ticket: \f145; +$fa-var-power-off: \f011; +$fa-var-right-long: \f30b; +$fa-var-long-arrow-alt-right: \f30b; +$fa-var-flag-usa: \f74d; +$fa-var-laptop-file: \e51d; +$fa-var-tty: \f1e4; +$fa-var-teletype: \f1e4; +$fa-var-diagram-next: \e476; +$fa-var-person-rifle: \e54e; +$fa-var-house-medical-circle-exclamation: \e512; +$fa-var-closed-captioning: \f20a; +$fa-var-person-hiking: \f6ec; +$fa-var-hiking: \f6ec; +$fa-var-venus-double: \f226; +$fa-var-images: \f302; +$fa-var-calculator: \f1ec; +$fa-var-people-pulling: \e535; +$fa-var-n: \4e; +$fa-var-cable-car: \f7da; +$fa-var-tram: \f7da; +$fa-var-cloud-rain: \f73d; +$fa-var-building-circle-xmark: \e4d4; +$fa-var-ship: \f21a; +$fa-var-arrows-down-to-line: \e4b8; +$fa-var-download: \f019; +$fa-var-face-grin: \f580; +$fa-var-grin: \f580; +$fa-var-delete-left: \f55a; +$fa-var-backspace: \f55a; +$fa-var-eye-dropper: \f1fb; +$fa-var-eye-dropper-empty: \f1fb; +$fa-var-eyedropper: \f1fb; +$fa-var-file-circle-check: \e5a0; +$fa-var-forward: \f04e; +$fa-var-mobile: \f3ce; +$fa-var-mobile-android: \f3ce; +$fa-var-mobile-phone: \f3ce; +$fa-var-face-meh: \f11a; +$fa-var-meh: \f11a; +$fa-var-align-center: \f037; +$fa-var-book-skull: \f6b7; +$fa-var-book-dead: \f6b7; +$fa-var-id-card: \f2c2; +$fa-var-drivers-license: \f2c2; +$fa-var-outdent: \f03b; +$fa-var-dedent: \f03b; +$fa-var-heart-circle-exclamation: \e4fe; +$fa-var-house: \f015; +$fa-var-home: \f015; +$fa-var-home-alt: \f015; +$fa-var-home-lg-alt: \f015; +$fa-var-calendar-week: \f784; +$fa-var-laptop-medical: \f812; +$fa-var-b: \42; +$fa-var-file-medical: \f477; +$fa-var-dice-one: \f525; +$fa-var-kiwi-bird: \f535; +$fa-var-arrow-right-arrow-left: \f0ec; +$fa-var-exchange: \f0ec; +$fa-var-rotate-right: \f2f9; +$fa-var-redo-alt: \f2f9; +$fa-var-rotate-forward: \f2f9; +$fa-var-utensils: \f2e7; +$fa-var-cutlery: \f2e7; +$fa-var-arrow-up-wide-short: \f161; +$fa-var-sort-amount-up: \f161; +$fa-var-mill-sign: \e1ed; +$fa-var-bowl-rice: \e2eb; +$fa-var-skull: \f54c; +$fa-var-tower-broadcast: \f519; +$fa-var-broadcast-tower: \f519; +$fa-var-truck-pickup: \f63c; +$fa-var-up-long: \f30c; +$fa-var-long-arrow-alt-up: \f30c; +$fa-var-stop: \f04d; +$fa-var-code-merge: \f387; +$fa-var-upload: \f093; +$fa-var-hurricane: \f751; +$fa-var-mound: \e52d; +$fa-var-toilet-portable: \e583; +$fa-var-compact-disc: \f51f; +$fa-var-file-arrow-down: \f56d; +$fa-var-file-download: \f56d; +$fa-var-caravan: \f8ff; +$fa-var-shield-cat: \e572; +$fa-var-bolt: \f0e7; +$fa-var-zap: \f0e7; +$fa-var-glass-water: \e4f4; +$fa-var-oil-well: \e532; +$fa-var-vault: \e2c5; +$fa-var-mars: \f222; +$fa-var-toilet: \f7d8; +$fa-var-plane-circle-xmark: \e557; +$fa-var-yen-sign: \f157; +$fa-var-cny: \f157; +$fa-var-jpy: \f157; +$fa-var-rmb: \f157; +$fa-var-yen: \f157; +$fa-var-ruble-sign: \f158; +$fa-var-rouble: \f158; +$fa-var-rub: \f158; +$fa-var-ruble: \f158; +$fa-var-sun: \f185; +$fa-var-guitar: \f7a6; +$fa-var-face-laugh-wink: \f59c; +$fa-var-laugh-wink: \f59c; +$fa-var-horse-head: \f7ab; +$fa-var-bore-hole: \e4c3; +$fa-var-industry: \f275; +$fa-var-circle-down: \f358; +$fa-var-arrow-alt-circle-down: \f358; +$fa-var-arrows-turn-to-dots: \e4c1; +$fa-var-florin-sign: \e184; +$fa-var-arrow-down-short-wide: \f884; +$fa-var-sort-amount-desc: \f884; +$fa-var-sort-amount-down-alt: \f884; +$fa-var-less-than: \3c; +$fa-var-angle-down: \f107; +$fa-var-car-tunnel: \e4de; +$fa-var-head-side-cough: \e061; +$fa-var-grip-lines: \f7a4; +$fa-var-thumbs-down: \f165; +$fa-var-user-lock: \f502; +$fa-var-arrow-right-long: \f178; +$fa-var-long-arrow-right: \f178; +$fa-var-anchor-circle-xmark: \e4ac; +$fa-var-ellipsis: \f141; +$fa-var-ellipsis-h: \f141; +$fa-var-chess-pawn: \f443; +$fa-var-kit-medical: \f479; +$fa-var-first-aid: \f479; +$fa-var-person-through-window: \e5a9; +$fa-var-toolbox: \f552; +$fa-var-hands-holding-circle: \e4fb; +$fa-var-bug: \f188; +$fa-var-credit-card: \f09d; +$fa-var-credit-card-alt: \f09d; +$fa-var-car: \f1b9; +$fa-var-automobile: \f1b9; +$fa-var-hand-holding-hand: \e4f7; +$fa-var-book-open-reader: \f5da; +$fa-var-book-reader: \f5da; +$fa-var-mountain-sun: \e52f; +$fa-var-arrows-left-right-to-line: \e4ba; +$fa-var-dice-d20: \f6cf; +$fa-var-truck-droplet: \e58c; +$fa-var-file-circle-xmark: \e5a1; +$fa-var-temperature-arrow-up: \e040; +$fa-var-temperature-up: \e040; +$fa-var-medal: \f5a2; +$fa-var-bed: \f236; +$fa-var-square-h: \f0fd; +$fa-var-h-square: \f0fd; +$fa-var-podcast: \f2ce; +$fa-var-temperature-full: \f2c7; +$fa-var-temperature-4: \f2c7; +$fa-var-thermometer-4: \f2c7; +$fa-var-thermometer-full: \f2c7; +$fa-var-bell: \f0f3; +$fa-var-superscript: \f12b; +$fa-var-plug-circle-xmark: \e560; +$fa-var-star-of-life: \f621; +$fa-var-phone-slash: \f3dd; +$fa-var-paint-roller: \f5aa; +$fa-var-handshake-angle: \f4c4; +$fa-var-hands-helping: \f4c4; +$fa-var-location-dot: \f3c5; +$fa-var-map-marker-alt: \f3c5; +$fa-var-file: \f15b; +$fa-var-greater-than: \3e; +$fa-var-person-swimming: \f5c4; +$fa-var-swimmer: \f5c4; +$fa-var-arrow-down: \f063; +$fa-var-droplet: \f043; +$fa-var-tint: \f043; +$fa-var-eraser: \f12d; +$fa-var-earth-americas: \f57d; +$fa-var-earth: \f57d; +$fa-var-earth-america: \f57d; +$fa-var-globe-americas: \f57d; +$fa-var-person-burst: \e53b; +$fa-var-dove: \f4ba; +$fa-var-battery-empty: \f244; +$fa-var-battery-0: \f244; +$fa-var-socks: \f696; +$fa-var-inbox: \f01c; +$fa-var-section: \e447; +$fa-var-gauge-high: \f625; +$fa-var-tachometer-alt: \f625; +$fa-var-tachometer-alt-fast: \f625; +$fa-var-envelope-open-text: \f658; +$fa-var-hospital: \f0f8; +$fa-var-hospital-alt: \f0f8; +$fa-var-hospital-wide: \f0f8; +$fa-var-wine-bottle: \f72f; +$fa-var-chess-rook: \f447; +$fa-var-bars-staggered: \f550; +$fa-var-reorder: \f550; +$fa-var-stream: \f550; +$fa-var-dharmachakra: \f655; +$fa-var-hotdog: \f80f; +$fa-var-person-walking-with-cane: \f29d; +$fa-var-blind: \f29d; +$fa-var-drum: \f569; +$fa-var-ice-cream: \f810; +$fa-var-heart-circle-bolt: \e4fc; +$fa-var-fax: \f1ac; +$fa-var-paragraph: \f1dd; +$fa-var-check-to-slot: \f772; +$fa-var-vote-yea: \f772; +$fa-var-star-half: \f089; +$fa-var-boxes-stacked: \f468; +$fa-var-boxes: \f468; +$fa-var-boxes-alt: \f468; +$fa-var-link: \f0c1; +$fa-var-chain: \f0c1; +$fa-var-ear-listen: \f2a2; +$fa-var-assistive-listening-systems: \f2a2; +$fa-var-tree-city: \e587; +$fa-var-play: \f04b; +$fa-var-font: \f031; +$fa-var-table-cells-row-lock: \e67a; +$fa-var-rupiah-sign: \e23d; +$fa-var-magnifying-glass: \f002; +$fa-var-search: \f002; +$fa-var-table-tennis-paddle-ball: \f45d; +$fa-var-ping-pong-paddle-ball: \f45d; +$fa-var-table-tennis: \f45d; +$fa-var-person-dots-from-line: \f470; +$fa-var-diagnoses: \f470; +$fa-var-trash-can-arrow-up: \f82a; +$fa-var-trash-restore-alt: \f82a; +$fa-var-naira-sign: \e1f6; +$fa-var-cart-arrow-down: \f218; +$fa-var-walkie-talkie: \f8ef; +$fa-var-file-pen: \f31c; +$fa-var-file-edit: \f31c; +$fa-var-receipt: \f543; +$fa-var-square-pen: \f14b; +$fa-var-pen-square: \f14b; +$fa-var-pencil-square: \f14b; +$fa-var-suitcase-rolling: \f5c1; +$fa-var-person-circle-exclamation: \e53f; +$fa-var-chevron-down: \f078; +$fa-var-battery-full: \f240; +$fa-var-battery: \f240; +$fa-var-battery-5: \f240; +$fa-var-skull-crossbones: \f714; +$fa-var-code-compare: \e13a; +$fa-var-list-ul: \f0ca; +$fa-var-list-dots: \f0ca; +$fa-var-school-lock: \e56f; +$fa-var-tower-cell: \e585; +$fa-var-down-long: \f309; +$fa-var-long-arrow-alt-down: \f309; +$fa-var-ranking-star: \e561; +$fa-var-chess-king: \f43f; +$fa-var-person-harassing: \e549; +$fa-var-brazilian-real-sign: \e46c; +$fa-var-landmark-dome: \f752; +$fa-var-landmark-alt: \f752; +$fa-var-arrow-up: \f062; +$fa-var-tv: \f26c; +$fa-var-television: \f26c; +$fa-var-tv-alt: \f26c; +$fa-var-shrimp: \e448; +$fa-var-list-check: \f0ae; +$fa-var-tasks: \f0ae; +$fa-var-jug-detergent: \e519; +$fa-var-circle-user: \f2bd; +$fa-var-user-circle: \f2bd; +$fa-var-user-shield: \f505; +$fa-var-wind: \f72e; +$fa-var-car-burst: \f5e1; +$fa-var-car-crash: \f5e1; +$fa-var-y: \59; +$fa-var-person-snowboarding: \f7ce; +$fa-var-snowboarding: \f7ce; +$fa-var-truck-fast: \f48b; +$fa-var-shipping-fast: \f48b; +$fa-var-fish: \f578; +$fa-var-user-graduate: \f501; +$fa-var-circle-half-stroke: \f042; +$fa-var-adjust: \f042; +$fa-var-clapperboard: \e131; +$fa-var-circle-radiation: \f7ba; +$fa-var-radiation-alt: \f7ba; +$fa-var-baseball: \f433; +$fa-var-baseball-ball: \f433; +$fa-var-jet-fighter-up: \e518; +$fa-var-diagram-project: \f542; +$fa-var-project-diagram: \f542; +$fa-var-copy: \f0c5; +$fa-var-volume-xmark: \f6a9; +$fa-var-volume-mute: \f6a9; +$fa-var-volume-times: \f6a9; +$fa-var-hand-sparkles: \e05d; +$fa-var-grip: \f58d; +$fa-var-grip-horizontal: \f58d; +$fa-var-share-from-square: \f14d; +$fa-var-share-square: \f14d; +$fa-var-child-combatant: \e4e0; +$fa-var-child-rifle: \e4e0; +$fa-var-gun: \e19b; +$fa-var-square-phone: \f098; +$fa-var-phone-square: \f098; +$fa-var-plus: \2b; +$fa-var-add: \2b; +$fa-var-expand: \f065; +$fa-var-computer: \e4e5; +$fa-var-xmark: \f00d; +$fa-var-close: \f00d; +$fa-var-multiply: \f00d; +$fa-var-remove: \f00d; +$fa-var-times: \f00d; +$fa-var-arrows-up-down-left-right: \f047; +$fa-var-arrows: \f047; +$fa-var-chalkboard-user: \f51c; +$fa-var-chalkboard-teacher: \f51c; +$fa-var-peso-sign: \e222; +$fa-var-building-shield: \e4d8; +$fa-var-baby: \f77c; +$fa-var-users-line: \e592; +$fa-var-quote-left: \f10d; +$fa-var-quote-left-alt: \f10d; +$fa-var-tractor: \f722; +$fa-var-trash-arrow-up: \f829; +$fa-var-trash-restore: \f829; +$fa-var-arrow-down-up-lock: \e4b0; +$fa-var-lines-leaning: \e51e; +$fa-var-ruler-combined: \f546; +$fa-var-copyright: \f1f9; +$fa-var-equals: \3d; +$fa-var-blender: \f517; +$fa-var-teeth: \f62e; +$fa-var-shekel-sign: \f20b; +$fa-var-ils: \f20b; +$fa-var-shekel: \f20b; +$fa-var-sheqel: \f20b; +$fa-var-sheqel-sign: \f20b; +$fa-var-map: \f279; +$fa-var-rocket: \f135; +$fa-var-photo-film: \f87c; +$fa-var-photo-video: \f87c; +$fa-var-folder-minus: \f65d; +$fa-var-hexagon-nodes-bolt: \e69a; +$fa-var-store: \f54e; +$fa-var-arrow-trend-up: \e098; +$fa-var-plug-circle-minus: \e55e; +$fa-var-sign-hanging: \f4d9; +$fa-var-sign: \f4d9; +$fa-var-bezier-curve: \f55b; +$fa-var-bell-slash: \f1f6; +$fa-var-tablet: \f3fb; +$fa-var-tablet-android: \f3fb; +$fa-var-school-flag: \e56e; +$fa-var-fill: \f575; +$fa-var-angle-up: \f106; +$fa-var-drumstick-bite: \f6d7; +$fa-var-holly-berry: \f7aa; +$fa-var-chevron-left: \f053; +$fa-var-bacteria: \e059; +$fa-var-hand-lizard: \f258; +$fa-var-notdef: \e1fe; +$fa-var-disease: \f7fa; +$fa-var-briefcase-medical: \f469; +$fa-var-genderless: \f22d; +$fa-var-chevron-right: \f054; +$fa-var-retweet: \f079; +$fa-var-car-rear: \f5de; +$fa-var-car-alt: \f5de; +$fa-var-pump-soap: \e06b; +$fa-var-video-slash: \f4e2; +$fa-var-battery-quarter: \f243; +$fa-var-battery-2: \f243; +$fa-var-radio: \f8d7; +$fa-var-baby-carriage: \f77d; +$fa-var-carriage-baby: \f77d; +$fa-var-traffic-light: \f637; +$fa-var-thermometer: \f491; +$fa-var-vr-cardboard: \f729; +$fa-var-hand-middle-finger: \f806; +$fa-var-percent: \25; +$fa-var-percentage: \25; +$fa-var-truck-moving: \f4df; +$fa-var-glass-water-droplet: \e4f5; +$fa-var-display: \e163; +$fa-var-face-smile: \f118; +$fa-var-smile: \f118; +$fa-var-thumbtack: \f08d; +$fa-var-thumb-tack: \f08d; +$fa-var-trophy: \f091; +$fa-var-person-praying: \f683; +$fa-var-pray: \f683; +$fa-var-hammer: \f6e3; +$fa-var-hand-peace: \f25b; +$fa-var-rotate: \f2f1; +$fa-var-sync-alt: \f2f1; +$fa-var-spinner: \f110; +$fa-var-robot: \f544; +$fa-var-peace: \f67c; +$fa-var-gears: \f085; +$fa-var-cogs: \f085; +$fa-var-warehouse: \f494; +$fa-var-arrow-up-right-dots: \e4b7; +$fa-var-splotch: \f5bc; +$fa-var-face-grin-hearts: \f584; +$fa-var-grin-hearts: \f584; +$fa-var-dice-four: \f524; +$fa-var-sim-card: \f7c4; +$fa-var-transgender: \f225; +$fa-var-transgender-alt: \f225; +$fa-var-mercury: \f223; +$fa-var-arrow-turn-down: \f149; +$fa-var-level-down: \f149; +$fa-var-person-falling-burst: \e547; +$fa-var-award: \f559; +$fa-var-ticket-simple: \f3ff; +$fa-var-ticket-alt: \f3ff; +$fa-var-building: \f1ad; +$fa-var-angles-left: \f100; +$fa-var-angle-double-left: \f100; +$fa-var-qrcode: \f029; +$fa-var-clock-rotate-left: \f1da; +$fa-var-history: \f1da; +$fa-var-face-grin-beam-sweat: \f583; +$fa-var-grin-beam-sweat: \f583; +$fa-var-file-export: \f56e; +$fa-var-arrow-right-from-file: \f56e; +$fa-var-shield: \f132; +$fa-var-shield-blank: \f132; +$fa-var-arrow-up-short-wide: \f885; +$fa-var-sort-amount-up-alt: \f885; +$fa-var-comment-nodes: \e696; +$fa-var-house-medical: \e3b2; +$fa-var-golf-ball-tee: \f450; +$fa-var-golf-ball: \f450; +$fa-var-circle-chevron-left: \f137; +$fa-var-chevron-circle-left: \f137; +$fa-var-house-chimney-window: \e00d; +$fa-var-pen-nib: \f5ad; +$fa-var-tent-arrow-turn-left: \e580; +$fa-var-tents: \e582; +$fa-var-wand-magic: \f0d0; +$fa-var-magic: \f0d0; +$fa-var-dog: \f6d3; +$fa-var-carrot: \f787; +$fa-var-moon: \f186; +$fa-var-wine-glass-empty: \f5ce; +$fa-var-wine-glass-alt: \f5ce; +$fa-var-cheese: \f7ef; +$fa-var-yin-yang: \f6ad; +$fa-var-music: \f001; +$fa-var-code-commit: \f386; +$fa-var-temperature-low: \f76b; +$fa-var-person-biking: \f84a; +$fa-var-biking: \f84a; +$fa-var-broom: \f51a; +$fa-var-shield-heart: \e574; +$fa-var-gopuram: \f664; +$fa-var-earth-oceania: \e47b; +$fa-var-globe-oceania: \e47b; +$fa-var-square-xmark: \f2d3; +$fa-var-times-square: \f2d3; +$fa-var-xmark-square: \f2d3; +$fa-var-hashtag: \23; +$fa-var-up-right-and-down-left-from-center: \f424; +$fa-var-expand-alt: \f424; +$fa-var-oil-can: \f613; +$fa-var-t: \54; +$fa-var-hippo: \f6ed; +$fa-var-chart-column: \e0e3; +$fa-var-infinity: \f534; +$fa-var-vial-circle-check: \e596; +$fa-var-person-arrow-down-to-line: \e538; +$fa-var-voicemail: \f897; +$fa-var-fan: \f863; +$fa-var-person-walking-luggage: \e554; +$fa-var-up-down: \f338; +$fa-var-arrows-alt-v: \f338; +$fa-var-cloud-moon-rain: \f73c; +$fa-var-calendar: \f133; +$fa-var-trailer: \e041; +$fa-var-bahai: \f666; +$fa-var-haykal: \f666; +$fa-var-sd-card: \f7c2; +$fa-var-dragon: \f6d5; +$fa-var-shoe-prints: \f54b; +$fa-var-circle-plus: \f055; +$fa-var-plus-circle: \f055; +$fa-var-face-grin-tongue-wink: \f58b; +$fa-var-grin-tongue-wink: \f58b; +$fa-var-hand-holding: \f4bd; +$fa-var-plug-circle-exclamation: \e55d; +$fa-var-link-slash: \f127; +$fa-var-chain-broken: \f127; +$fa-var-chain-slash: \f127; +$fa-var-unlink: \f127; +$fa-var-clone: \f24d; +$fa-var-person-walking-arrow-loop-left: \e551; +$fa-var-arrow-up-z-a: \f882; +$fa-var-sort-alpha-up-alt: \f882; +$fa-var-fire-flame-curved: \f7e4; +$fa-var-fire-alt: \f7e4; +$fa-var-tornado: \f76f; +$fa-var-file-circle-plus: \e494; +$fa-var-book-quran: \f687; +$fa-var-quran: \f687; +$fa-var-anchor: \f13d; +$fa-var-border-all: \f84c; +$fa-var-face-angry: \f556; +$fa-var-angry: \f556; +$fa-var-cookie-bite: \f564; +$fa-var-arrow-trend-down: \e097; +$fa-var-rss: \f09e; +$fa-var-feed: \f09e; +$fa-var-draw-polygon: \f5ee; +$fa-var-scale-balanced: \f24e; +$fa-var-balance-scale: \f24e; +$fa-var-gauge-simple-high: \f62a; +$fa-var-tachometer: \f62a; +$fa-var-tachometer-fast: \f62a; +$fa-var-shower: \f2cc; +$fa-var-desktop: \f390; +$fa-var-desktop-alt: \f390; +$fa-var-m: \4d; +$fa-var-table-list: \f00b; +$fa-var-th-list: \f00b; +$fa-var-comment-sms: \f7cd; +$fa-var-sms: \f7cd; +$fa-var-book: \f02d; +$fa-var-user-plus: \f234; +$fa-var-check: \f00c; +$fa-var-battery-three-quarters: \f241; +$fa-var-battery-4: \f241; +$fa-var-house-circle-check: \e509; +$fa-var-angle-left: \f104; +$fa-var-diagram-successor: \e47a; +$fa-var-truck-arrow-right: \e58b; +$fa-var-arrows-split-up-and-left: \e4bc; +$fa-var-hand-fist: \f6de; +$fa-var-fist-raised: \f6de; +$fa-var-cloud-moon: \f6c3; +$fa-var-briefcase: \f0b1; +$fa-var-person-falling: \e546; +$fa-var-image-portrait: \f3e0; +$fa-var-portrait: \f3e0; +$fa-var-user-tag: \f507; +$fa-var-rug: \e569; +$fa-var-earth-europe: \f7a2; +$fa-var-globe-europe: \f7a2; +$fa-var-cart-flatbed-suitcase: \f59d; +$fa-var-luggage-cart: \f59d; +$fa-var-rectangle-xmark: \f410; +$fa-var-rectangle-times: \f410; +$fa-var-times-rectangle: \f410; +$fa-var-window-close: \f410; +$fa-var-baht-sign: \e0ac; +$fa-var-book-open: \f518; +$fa-var-book-journal-whills: \f66a; +$fa-var-journal-whills: \f66a; +$fa-var-handcuffs: \e4f8; +$fa-var-triangle-exclamation: \f071; +$fa-var-exclamation-triangle: \f071; +$fa-var-warning: \f071; +$fa-var-database: \f1c0; +$fa-var-share: \f064; +$fa-var-mail-forward: \f064; +$fa-var-bottle-droplet: \e4c4; +$fa-var-mask-face: \e1d7; +$fa-var-hill-rockslide: \e508; +$fa-var-right-left: \f362; +$fa-var-exchange-alt: \f362; +$fa-var-paper-plane: \f1d8; +$fa-var-road-circle-exclamation: \e565; +$fa-var-dungeon: \f6d9; +$fa-var-align-right: \f038; +$fa-var-money-bill-1-wave: \f53b; +$fa-var-money-bill-wave-alt: \f53b; +$fa-var-life-ring: \f1cd; +$fa-var-hands: \f2a7; +$fa-var-sign-language: \f2a7; +$fa-var-signing: \f2a7; +$fa-var-calendar-day: \f783; +$fa-var-water-ladder: \f5c5; +$fa-var-ladder-water: \f5c5; +$fa-var-swimming-pool: \f5c5; +$fa-var-arrows-up-down: \f07d; +$fa-var-arrows-v: \f07d; +$fa-var-face-grimace: \f57f; +$fa-var-grimace: \f57f; +$fa-var-wheelchair-move: \e2ce; +$fa-var-wheelchair-alt: \e2ce; +$fa-var-turn-down: \f3be; +$fa-var-level-down-alt: \f3be; +$fa-var-person-walking-arrow-right: \e552; +$fa-var-square-envelope: \f199; +$fa-var-envelope-square: \f199; +$fa-var-dice: \f522; +$fa-var-bowling-ball: \f436; +$fa-var-brain: \f5dc; +$fa-var-bandage: \f462; +$fa-var-band-aid: \f462; +$fa-var-calendar-minus: \f272; +$fa-var-circle-xmark: \f057; +$fa-var-times-circle: \f057; +$fa-var-xmark-circle: \f057; +$fa-var-gifts: \f79c; +$fa-var-hotel: \f594; +$fa-var-earth-asia: \f57e; +$fa-var-globe-asia: \f57e; +$fa-var-id-card-clip: \f47f; +$fa-var-id-card-alt: \f47f; +$fa-var-magnifying-glass-plus: \f00e; +$fa-var-search-plus: \f00e; +$fa-var-thumbs-up: \f164; +$fa-var-user-clock: \f4fd; +$fa-var-hand-dots: \f461; +$fa-var-allergies: \f461; +$fa-var-file-invoice: \f570; +$fa-var-window-minimize: \f2d1; +$fa-var-mug-saucer: \f0f4; +$fa-var-coffee: \f0f4; +$fa-var-brush: \f55d; +$fa-var-file-half-dashed: \e698; +$fa-var-mask: \f6fa; +$fa-var-magnifying-glass-minus: \f010; +$fa-var-search-minus: \f010; +$fa-var-ruler-vertical: \f548; +$fa-var-user-large: \f406; +$fa-var-user-alt: \f406; +$fa-var-train-tram: \e5b4; +$fa-var-user-nurse: \f82f; +$fa-var-syringe: \f48e; +$fa-var-cloud-sun: \f6c4; +$fa-var-stopwatch-20: \e06f; +$fa-var-square-full: \f45c; +$fa-var-magnet: \f076; +$fa-var-jar: \e516; +$fa-var-note-sticky: \f249; +$fa-var-sticky-note: \f249; +$fa-var-bug-slash: \e490; +$fa-var-arrow-up-from-water-pump: \e4b6; +$fa-var-bone: \f5d7; +$fa-var-table-cells-row-unlock: \e691; +$fa-var-user-injured: \f728; +$fa-var-face-sad-tear: \f5b4; +$fa-var-sad-tear: \f5b4; +$fa-var-plane: \f072; +$fa-var-tent-arrows-down: \e581; +$fa-var-exclamation: \21; +$fa-var-arrows-spin: \e4bb; +$fa-var-print: \f02f; +$fa-var-turkish-lira-sign: \e2bb; +$fa-var-try: \e2bb; +$fa-var-turkish-lira: \e2bb; +$fa-var-dollar-sign: \24; +$fa-var-dollar: \24; +$fa-var-usd: \24; +$fa-var-x: \58; +$fa-var-magnifying-glass-dollar: \f688; +$fa-var-search-dollar: \f688; +$fa-var-users-gear: \f509; +$fa-var-users-cog: \f509; +$fa-var-person-military-pointing: \e54a; +$fa-var-building-columns: \f19c; +$fa-var-bank: \f19c; +$fa-var-institution: \f19c; +$fa-var-museum: \f19c; +$fa-var-university: \f19c; +$fa-var-umbrella: \f0e9; +$fa-var-trowel: \e589; +$fa-var-d: \44; +$fa-var-stapler: \e5af; +$fa-var-masks-theater: \f630; +$fa-var-theater-masks: \f630; +$fa-var-kip-sign: \e1c4; +$fa-var-hand-point-left: \f0a5; +$fa-var-handshake-simple: \f4c6; +$fa-var-handshake-alt: \f4c6; +$fa-var-jet-fighter: \f0fb; +$fa-var-fighter-jet: \f0fb; +$fa-var-square-share-nodes: \f1e1; +$fa-var-share-alt-square: \f1e1; +$fa-var-barcode: \f02a; +$fa-var-plus-minus: \e43c; +$fa-var-video: \f03d; +$fa-var-video-camera: \f03d; +$fa-var-graduation-cap: \f19d; +$fa-var-mortar-board: \f19d; +$fa-var-hand-holding-medical: \e05c; +$fa-var-person-circle-check: \e53e; +$fa-var-turn-up: \f3bf; +$fa-var-level-up-alt: \f3bf; + +$fa-var-monero: \f3d0; +$fa-var-hooli: \f427; +$fa-var-yelp: \f1e9; +$fa-var-cc-visa: \f1f0; +$fa-var-lastfm: \f202; +$fa-var-shopware: \f5b5; +$fa-var-creative-commons-nc: \f4e8; +$fa-var-aws: \f375; +$fa-var-redhat: \f7bc; +$fa-var-yoast: \f2b1; +$fa-var-cloudflare: \e07d; +$fa-var-ups: \f7e0; +$fa-var-pixiv: \e640; +$fa-var-wpexplorer: \f2de; +$fa-var-dyalog: \f399; +$fa-var-bity: \f37a; +$fa-var-stackpath: \f842; +$fa-var-buysellads: \f20d; +$fa-var-first-order: \f2b0; +$fa-var-modx: \f285; +$fa-var-guilded: \e07e; +$fa-var-vnv: \f40b; +$fa-var-square-js: \f3b9; +$fa-var-js-square: \f3b9; +$fa-var-microsoft: \f3ca; +$fa-var-qq: \f1d6; +$fa-var-orcid: \f8d2; +$fa-var-java: \f4e4; +$fa-var-invision: \f7b0; +$fa-var-creative-commons-pd-alt: \f4ed; +$fa-var-centercode: \f380; +$fa-var-glide-g: \f2a6; +$fa-var-drupal: \f1a9; +$fa-var-jxl: \e67b; +$fa-var-dart-lang: \e693; +$fa-var-hire-a-helper: \f3b0; +$fa-var-creative-commons-by: \f4e7; +$fa-var-unity: \e049; +$fa-var-whmcs: \f40d; +$fa-var-rocketchat: \f3e8; +$fa-var-vk: \f189; +$fa-var-untappd: \f405; +$fa-var-mailchimp: \f59e; +$fa-var-css3-alt: \f38b; +$fa-var-square-reddit: \f1a2; +$fa-var-reddit-square: \f1a2; +$fa-var-vimeo-v: \f27d; +$fa-var-contao: \f26d; +$fa-var-square-font-awesome: \e5ad; +$fa-var-deskpro: \f38f; +$fa-var-brave: \e63c; +$fa-var-sistrix: \f3ee; +$fa-var-square-instagram: \e055; +$fa-var-instagram-square: \e055; +$fa-var-battle-net: \f835; +$fa-var-the-red-yeti: \f69d; +$fa-var-square-hacker-news: \f3af; +$fa-var-hacker-news-square: \f3af; +$fa-var-edge: \f282; +$fa-var-threads: \e618; +$fa-var-napster: \f3d2; +$fa-var-square-snapchat: \f2ad; +$fa-var-snapchat-square: \f2ad; +$fa-var-google-plus-g: \f0d5; +$fa-var-artstation: \f77a; +$fa-var-markdown: \f60f; +$fa-var-sourcetree: \f7d3; +$fa-var-google-plus: \f2b3; +$fa-var-diaspora: \f791; +$fa-var-foursquare: \f180; +$fa-var-stack-overflow: \f16c; +$fa-var-github-alt: \f113; +$fa-var-phoenix-squadron: \f511; +$fa-var-pagelines: \f18c; +$fa-var-algolia: \f36c; +$fa-var-red-river: \f3e3; +$fa-var-creative-commons-sa: \f4ef; +$fa-var-safari: \f267; +$fa-var-google: \f1a0; +$fa-var-square-font-awesome-stroke: \f35c; +$fa-var-font-awesome-alt: \f35c; +$fa-var-atlassian: \f77b; +$fa-var-linkedin-in: \f0e1; +$fa-var-digital-ocean: \f391; +$fa-var-nimblr: \f5a8; +$fa-var-chromecast: \f838; +$fa-var-evernote: \f839; +$fa-var-hacker-news: \f1d4; +$fa-var-creative-commons-sampling: \f4f0; +$fa-var-adversal: \f36a; +$fa-var-creative-commons: \f25e; +$fa-var-watchman-monitoring: \e087; +$fa-var-fonticons: \f280; +$fa-var-weixin: \f1d7; +$fa-var-shirtsinbulk: \f214; +$fa-var-codepen: \f1cb; +$fa-var-git-alt: \f841; +$fa-var-lyft: \f3c3; +$fa-var-rev: \f5b2; +$fa-var-windows: \f17a; +$fa-var-wizards-of-the-coast: \f730; +$fa-var-square-viadeo: \f2aa; +$fa-var-viadeo-square: \f2aa; +$fa-var-meetup: \f2e0; +$fa-var-centos: \f789; +$fa-var-adn: \f170; +$fa-var-cloudsmith: \f384; +$fa-var-opensuse: \e62b; +$fa-var-pied-piper-alt: \f1a8; +$fa-var-square-dribbble: \f397; +$fa-var-dribbble-square: \f397; +$fa-var-codiepie: \f284; +$fa-var-node: \f419; +$fa-var-mix: \f3cb; +$fa-var-steam: \f1b6; +$fa-var-cc-apple-pay: \f416; +$fa-var-scribd: \f28a; +$fa-var-debian: \e60b; +$fa-var-openid: \f19b; +$fa-var-instalod: \e081; +$fa-var-files-pinwheel: \e69f; +$fa-var-expeditedssl: \f23e; +$fa-var-sellcast: \f2da; +$fa-var-square-twitter: \f081; +$fa-var-twitter-square: \f081; +$fa-var-r-project: \f4f7; +$fa-var-delicious: \f1a5; +$fa-var-freebsd: \f3a4; +$fa-var-vuejs: \f41f; +$fa-var-accusoft: \f369; +$fa-var-ioxhost: \f208; +$fa-var-fonticons-fi: \f3a2; +$fa-var-app-store: \f36f; +$fa-var-cc-mastercard: \f1f1; +$fa-var-itunes-note: \f3b5; +$fa-var-golang: \e40f; +$fa-var-kickstarter: \f3bb; +$fa-var-square-kickstarter: \f3bb; +$fa-var-grav: \f2d6; +$fa-var-weibo: \f18a; +$fa-var-uncharted: \e084; +$fa-var-firstdraft: \f3a1; +$fa-var-square-youtube: \f431; +$fa-var-youtube-square: \f431; +$fa-var-wikipedia-w: \f266; +$fa-var-wpressr: \f3e4; +$fa-var-rendact: \f3e4; +$fa-var-angellist: \f209; +$fa-var-galactic-republic: \f50c; +$fa-var-nfc-directional: \e530; +$fa-var-skype: \f17e; +$fa-var-joget: \f3b7; +$fa-var-fedora: \f798; +$fa-var-stripe-s: \f42a; +$fa-var-meta: \e49b; +$fa-var-laravel: \f3bd; +$fa-var-hotjar: \f3b1; +$fa-var-bluetooth-b: \f294; +$fa-var-square-letterboxd: \e62e; +$fa-var-sticker-mule: \f3f7; +$fa-var-creative-commons-zero: \f4f3; +$fa-var-hips: \f452; +$fa-var-css: \e6a2; +$fa-var-behance: \f1b4; +$fa-var-reddit: \f1a1; +$fa-var-discord: \f392; +$fa-var-chrome: \f268; +$fa-var-app-store-ios: \f370; +$fa-var-cc-discover: \f1f2; +$fa-var-wpbeginner: \f297; +$fa-var-confluence: \f78d; +$fa-var-shoelace: \e60c; +$fa-var-mdb: \f8ca; +$fa-var-dochub: \f394; +$fa-var-accessible-icon: \f368; +$fa-var-ebay: \f4f4; +$fa-var-amazon: \f270; +$fa-var-unsplash: \e07c; +$fa-var-yarn: \f7e3; +$fa-var-square-steam: \f1b7; +$fa-var-steam-square: \f1b7; +$fa-var-500px: \f26e; +$fa-var-square-vimeo: \f194; +$fa-var-vimeo-square: \f194; +$fa-var-asymmetrik: \f372; +$fa-var-font-awesome: \f2b4; +$fa-var-font-awesome-flag: \f2b4; +$fa-var-font-awesome-logo-full: \f2b4; +$fa-var-gratipay: \f184; +$fa-var-apple: \f179; +$fa-var-hive: \e07f; +$fa-var-gitkraken: \f3a6; +$fa-var-keybase: \f4f5; +$fa-var-apple-pay: \f415; +$fa-var-padlet: \e4a0; +$fa-var-amazon-pay: \f42c; +$fa-var-square-github: \f092; +$fa-var-github-square: \f092; +$fa-var-stumbleupon: \f1a4; +$fa-var-fedex: \f797; +$fa-var-phoenix-framework: \f3dc; +$fa-var-shopify: \e057; +$fa-var-neos: \f612; +$fa-var-square-threads: \e619; +$fa-var-hackerrank: \f5f7; +$fa-var-researchgate: \f4f8; +$fa-var-swift: \f8e1; +$fa-var-angular: \f420; +$fa-var-speakap: \f3f3; +$fa-var-angrycreative: \f36e; +$fa-var-y-combinator: \f23b; +$fa-var-empire: \f1d1; +$fa-var-envira: \f299; +$fa-var-google-scholar: \e63b; +$fa-var-square-gitlab: \e5ae; +$fa-var-gitlab-square: \e5ae; +$fa-var-studiovinari: \f3f8; +$fa-var-pied-piper: \f2ae; +$fa-var-wordpress: \f19a; +$fa-var-product-hunt: \f288; +$fa-var-firefox: \f269; +$fa-var-linode: \f2b8; +$fa-var-goodreads: \f3a8; +$fa-var-square-odnoklassniki: \f264; +$fa-var-odnoklassniki-square: \f264; +$fa-var-jsfiddle: \f1cc; +$fa-var-sith: \f512; +$fa-var-themeisle: \f2b2; +$fa-var-page4: \f3d7; +$fa-var-hashnode: \e499; +$fa-var-react: \f41b; +$fa-var-cc-paypal: \f1f4; +$fa-var-squarespace: \f5be; +$fa-var-cc-stripe: \f1f5; +$fa-var-creative-commons-share: \f4f2; +$fa-var-bitcoin: \f379; +$fa-var-keycdn: \f3ba; +$fa-var-opera: \f26a; +$fa-var-itch-io: \f83a; +$fa-var-umbraco: \f8e8; +$fa-var-galactic-senate: \f50d; +$fa-var-ubuntu: \f7df; +$fa-var-draft2digital: \f396; +$fa-var-stripe: \f429; +$fa-var-houzz: \f27c; +$fa-var-gg: \f260; +$fa-var-dhl: \f790; +$fa-var-square-pinterest: \f0d3; +$fa-var-pinterest-square: \f0d3; +$fa-var-xing: \f168; +$fa-var-blackberry: \f37b; +$fa-var-creative-commons-pd: \f4ec; +$fa-var-playstation: \f3df; +$fa-var-quinscape: \f459; +$fa-var-less: \f41d; +$fa-var-blogger-b: \f37d; +$fa-var-opencart: \f23d; +$fa-var-vine: \f1ca; +$fa-var-signal-messenger: \e663; +$fa-var-paypal: \f1ed; +$fa-var-gitlab: \f296; +$fa-var-typo3: \f42b; +$fa-var-reddit-alien: \f281; +$fa-var-yahoo: \f19e; +$fa-var-dailymotion: \e052; +$fa-var-affiliatetheme: \f36b; +$fa-var-pied-piper-pp: \f1a7; +$fa-var-bootstrap: \f836; +$fa-var-odnoklassniki: \f263; +$fa-var-nfc-symbol: \e531; +$fa-var-mintbit: \e62f; +$fa-var-ethereum: \f42e; +$fa-var-speaker-deck: \f83c; +$fa-var-creative-commons-nc-eu: \f4e9; +$fa-var-patreon: \f3d9; +$fa-var-avianex: \f374; +$fa-var-ello: \f5f1; +$fa-var-gofore: \f3a7; +$fa-var-bimobject: \f378; +$fa-var-brave-reverse: \e63d; +$fa-var-facebook-f: \f39e; +$fa-var-square-google-plus: \f0d4; +$fa-var-google-plus-square: \f0d4; +$fa-var-web-awesome: \e682; +$fa-var-mandalorian: \f50f; +$fa-var-first-order-alt: \f50a; +$fa-var-osi: \f41a; +$fa-var-google-wallet: \f1ee; +$fa-var-d-and-d-beyond: \f6ca; +$fa-var-periscope: \f3da; +$fa-var-fulcrum: \f50b; +$fa-var-cloudscale: \f383; +$fa-var-forumbee: \f211; +$fa-var-mizuni: \f3cc; +$fa-var-schlix: \f3ea; +$fa-var-square-xing: \f169; +$fa-var-xing-square: \f169; +$fa-var-bandcamp: \f2d5; +$fa-var-wpforms: \f298; +$fa-var-cloudversify: \f385; +$fa-var-usps: \f7e1; +$fa-var-megaport: \f5a3; +$fa-var-magento: \f3c4; +$fa-var-spotify: \f1bc; +$fa-var-optin-monster: \f23c; +$fa-var-fly: \f417; +$fa-var-square-bluesky: \e6a3; +$fa-var-aviato: \f421; +$fa-var-itunes: \f3b4; +$fa-var-cuttlefish: \f38c; +$fa-var-blogger: \f37c; +$fa-var-flickr: \f16e; +$fa-var-viber: \f409; +$fa-var-soundcloud: \f1be; +$fa-var-digg: \f1a6; +$fa-var-tencent-weibo: \f1d5; +$fa-var-letterboxd: \e62d; +$fa-var-symfony: \f83d; +$fa-var-maxcdn: \f136; +$fa-var-etsy: \f2d7; +$fa-var-facebook-messenger: \f39f; +$fa-var-audible: \f373; +$fa-var-think-peaks: \f731; +$fa-var-bilibili: \e3d9; +$fa-var-erlang: \f39d; +$fa-var-x-twitter: \e61b; +$fa-var-cotton-bureau: \f89e; +$fa-var-dashcube: \f210; +$fa-var-42-group: \e080; +$fa-var-innosoft: \e080; +$fa-var-stack-exchange: \f18d; +$fa-var-elementor: \f430; +$fa-var-square-pied-piper: \e01e; +$fa-var-pied-piper-square: \e01e; +$fa-var-creative-commons-nd: \f4eb; +$fa-var-palfed: \f3d8; +$fa-var-superpowers: \f2dd; +$fa-var-resolving: \f3e7; +$fa-var-xbox: \f412; +$fa-var-square-web-awesome-stroke: \e684; +$fa-var-searchengin: \f3eb; +$fa-var-tiktok: \e07b; +$fa-var-square-facebook: \f082; +$fa-var-facebook-square: \f082; +$fa-var-renren: \f18b; +$fa-var-linux: \f17c; +$fa-var-glide: \f2a5; +$fa-var-linkedin: \f08c; +$fa-var-hubspot: \f3b2; +$fa-var-deploydog: \f38e; +$fa-var-twitch: \f1e8; +$fa-var-flutter: \e694; +$fa-var-ravelry: \f2d9; +$fa-var-mixer: \e056; +$fa-var-square-lastfm: \f203; +$fa-var-lastfm-square: \f203; +$fa-var-vimeo: \f40a; +$fa-var-mendeley: \f7b3; +$fa-var-uniregistry: \f404; +$fa-var-figma: \f799; +$fa-var-creative-commons-remix: \f4ee; +$fa-var-cc-amazon-pay: \f42d; +$fa-var-dropbox: \f16b; +$fa-var-instagram: \f16d; +$fa-var-cmplid: \e360; +$fa-var-upwork: \e641; +$fa-var-facebook: \f09a; +$fa-var-gripfire: \f3ac; +$fa-var-jedi-order: \f50e; +$fa-var-uikit: \f403; +$fa-var-fort-awesome-alt: \f3a3; +$fa-var-phabricator: \f3db; +$fa-var-ussunnah: \f407; +$fa-var-earlybirds: \f39a; +$fa-var-trade-federation: \f513; +$fa-var-autoprefixer: \f41c; +$fa-var-whatsapp: \f232; +$fa-var-square-upwork: \e67c; +$fa-var-slideshare: \f1e7; +$fa-var-google-play: \f3ab; +$fa-var-viadeo: \f2a9; +$fa-var-line: \f3c0; +$fa-var-google-drive: \f3aa; +$fa-var-servicestack: \f3ec; +$fa-var-simplybuilt: \f215; +$fa-var-bitbucket: \f171; +$fa-var-imdb: \f2d8; +$fa-var-deezer: \e077; +$fa-var-raspberry-pi: \f7bb; +$fa-var-jira: \f7b1; +$fa-var-docker: \f395; +$fa-var-screenpal: \e570; +$fa-var-bluetooth: \f293; +$fa-var-gitter: \f426; +$fa-var-d-and-d: \f38d; +$fa-var-microblog: \e01a; +$fa-var-cc-diners-club: \f24c; +$fa-var-gg-circle: \f261; +$fa-var-pied-piper-hat: \f4e5; +$fa-var-kickstarter-k: \f3bc; +$fa-var-yandex: \f413; +$fa-var-readme: \f4d5; +$fa-var-html5: \f13b; +$fa-var-sellsy: \f213; +$fa-var-square-web-awesome: \e683; +$fa-var-sass: \f41e; +$fa-var-wirsindhandwerk: \e2d0; +$fa-var-wsh: \e2d0; +$fa-var-buromobelexperte: \f37f; +$fa-var-salesforce: \f83b; +$fa-var-octopus-deploy: \e082; +$fa-var-medapps: \f3c6; +$fa-var-ns8: \f3d5; +$fa-var-pinterest-p: \f231; +$fa-var-apper: \f371; +$fa-var-fort-awesome: \f286; +$fa-var-waze: \f83f; +$fa-var-bluesky: \e671; +$fa-var-cc-jcb: \f24b; +$fa-var-snapchat: \f2ab; +$fa-var-snapchat-ghost: \f2ab; +$fa-var-fantasy-flight-games: \f6dc; +$fa-var-rust: \e07a; +$fa-var-wix: \f5cf; +$fa-var-square-behance: \f1b5; +$fa-var-behance-square: \f1b5; +$fa-var-supple: \f3f9; +$fa-var-webflow: \e65c; +$fa-var-rebel: \f1d0; +$fa-var-css3: \f13c; +$fa-var-staylinked: \f3f5; +$fa-var-kaggle: \f5fa; +$fa-var-space-awesome: \e5ac; +$fa-var-deviantart: \f1bd; +$fa-var-cpanel: \f388; +$fa-var-goodreads-g: \f3a9; +$fa-var-square-git: \f1d2; +$fa-var-git-square: \f1d2; +$fa-var-square-tumblr: \f174; +$fa-var-tumblr-square: \f174; +$fa-var-trello: \f181; +$fa-var-creative-commons-nc-jp: \f4ea; +$fa-var-get-pocket: \f265; +$fa-var-perbyte: \e083; +$fa-var-grunt: \f3ad; +$fa-var-weebly: \f5cc; +$fa-var-connectdevelop: \f20e; +$fa-var-leanpub: \f212; +$fa-var-black-tie: \f27e; +$fa-var-themeco: \f5c6; +$fa-var-python: \f3e2; +$fa-var-android: \f17b; +$fa-var-bots: \e340; +$fa-var-free-code-camp: \f2c5; +$fa-var-hornbill: \f592; +$fa-var-js: \f3b8; +$fa-var-ideal: \e013; +$fa-var-git: \f1d3; +$fa-var-dev: \f6cc; +$fa-var-sketch: \f7c6; +$fa-var-yandex-international: \f414; +$fa-var-cc-amex: \f1f3; +$fa-var-uber: \f402; +$fa-var-github: \f09b; +$fa-var-php: \f457; +$fa-var-alipay: \f642; +$fa-var-youtube: \f167; +$fa-var-skyatlas: \f216; +$fa-var-firefox-browser: \e007; +$fa-var-replyd: \f3e6; +$fa-var-suse: \f7d6; +$fa-var-jenkins: \f3b6; +$fa-var-twitter: \f099; +$fa-var-rockrms: \f3e9; +$fa-var-pinterest: \f0d2; +$fa-var-buffer: \f837; +$fa-var-npm: \f3d4; +$fa-var-yammer: \f840; +$fa-var-btc: \f15a; +$fa-var-dribbble: \f17d; +$fa-var-stumbleupon-circle: \f1a3; +$fa-var-internet-explorer: \f26b; +$fa-var-stubber: \e5c7; +$fa-var-telegram: \f2c6; +$fa-var-telegram-plane: \f2c6; +$fa-var-old-republic: \f510; +$fa-var-odysee: \e5c6; +$fa-var-square-whatsapp: \f40c; +$fa-var-whatsapp-square: \f40c; +$fa-var-node-js: \f3d3; +$fa-var-edge-legacy: \e078; +$fa-var-slack: \f198; +$fa-var-slack-hash: \f198; +$fa-var-medrt: \f3c8; +$fa-var-usb: \f287; +$fa-var-tumblr: \f173; +$fa-var-vaadin: \f408; +$fa-var-quora: \f2c4; +$fa-var-square-x-twitter: \e61a; +$fa-var-reacteurope: \f75d; +$fa-var-medium: \f23a; +$fa-var-medium-m: \f23a; +$fa-var-amilia: \f36d; +$fa-var-mixcloud: \f289; +$fa-var-flipboard: \f44d; +$fa-var-viacoin: \f237; +$fa-var-critical-role: \f6c9; +$fa-var-sitrox: \e44a; +$fa-var-discourse: \f393; +$fa-var-joomla: \f1aa; +$fa-var-mastodon: \f4f6; +$fa-var-airbnb: \f834; +$fa-var-wolf-pack-battalion: \f514; +$fa-var-buy-n-large: \f8a6; +$fa-var-gulp: \f3ae; +$fa-var-creative-commons-sampling-plus: \f4f1; +$fa-var-strava: \f428; +$fa-var-ember: \f423; +$fa-var-canadian-maple-leaf: \f785; +$fa-var-teamspeak: \f4f9; +$fa-var-pushed: \f3e1; +$fa-var-wordpress-simple: \f411; +$fa-var-nutritionix: \f3d6; +$fa-var-wodu: \e088; +$fa-var-google-pay: \e079; +$fa-var-intercom: \f7af; +$fa-var-zhihu: \f63f; +$fa-var-korvue: \f42f; +$fa-var-pix: \e43a; +$fa-var-steam-symbol: \f3f6; + +$fa-icons: ( + "0": $fa-var-0, + "1": $fa-var-1, + "2": $fa-var-2, + "3": $fa-var-3, + "4": $fa-var-4, + "5": $fa-var-5, + "6": $fa-var-6, + "7": $fa-var-7, + "8": $fa-var-8, + "9": $fa-var-9, + "fill-drip": $fa-var-fill-drip, + "arrows-to-circle": $fa-var-arrows-to-circle, + "circle-chevron-right": $fa-var-circle-chevron-right, + "chevron-circle-right": $fa-var-chevron-circle-right, + "at": $fa-var-at, + "trash-can": $fa-var-trash-can, + "trash-alt": $fa-var-trash-alt, + "text-height": $fa-var-text-height, + "user-xmark": $fa-var-user-xmark, + "user-times": $fa-var-user-times, + "stethoscope": $fa-var-stethoscope, + "message": $fa-var-message, + "comment-alt": $fa-var-comment-alt, + "info": $fa-var-info, + "down-left-and-up-right-to-center": $fa-var-down-left-and-up-right-to-center, + "compress-alt": $fa-var-compress-alt, + "explosion": $fa-var-explosion, + "file-lines": $fa-var-file-lines, + "file-alt": $fa-var-file-alt, + "file-text": $fa-var-file-text, + "wave-square": $fa-var-wave-square, + "ring": $fa-var-ring, + "building-un": $fa-var-building-un, + "dice-three": $fa-var-dice-three, + "calendar-days": $fa-var-calendar-days, + "calendar-alt": $fa-var-calendar-alt, + "anchor-circle-check": $fa-var-anchor-circle-check, + "building-circle-arrow-right": $fa-var-building-circle-arrow-right, + "volleyball": $fa-var-volleyball, + "volleyball-ball": $fa-var-volleyball-ball, + "arrows-up-to-line": $fa-var-arrows-up-to-line, + "sort-down": $fa-var-sort-down, + "sort-desc": $fa-var-sort-desc, + "circle-minus": $fa-var-circle-minus, + "minus-circle": $fa-var-minus-circle, + "door-open": $fa-var-door-open, + "right-from-bracket": $fa-var-right-from-bracket, + "sign-out-alt": $fa-var-sign-out-alt, + "atom": $fa-var-atom, + "soap": $fa-var-soap, + "icons": $fa-var-icons, + "heart-music-camera-bolt": $fa-var-heart-music-camera-bolt, + "microphone-lines-slash": $fa-var-microphone-lines-slash, + "microphone-alt-slash": $fa-var-microphone-alt-slash, + "bridge-circle-check": $fa-var-bridge-circle-check, + "pump-medical": $fa-var-pump-medical, + "fingerprint": $fa-var-fingerprint, + "hand-point-right": $fa-var-hand-point-right, + "magnifying-glass-location": $fa-var-magnifying-glass-location, + "search-location": $fa-var-search-location, + "forward-step": $fa-var-forward-step, + "step-forward": $fa-var-step-forward, + "face-smile-beam": $fa-var-face-smile-beam, + "smile-beam": $fa-var-smile-beam, + "flag-checkered": $fa-var-flag-checkered, + "football": $fa-var-football, + "football-ball": $fa-var-football-ball, + "school-circle-exclamation": $fa-var-school-circle-exclamation, + "crop": $fa-var-crop, + "angles-down": $fa-var-angles-down, + "angle-double-down": $fa-var-angle-double-down, + "users-rectangle": $fa-var-users-rectangle, + "people-roof": $fa-var-people-roof, + "people-line": $fa-var-people-line, + "beer-mug-empty": $fa-var-beer-mug-empty, + "beer": $fa-var-beer, + "diagram-predecessor": $fa-var-diagram-predecessor, + "arrow-up-long": $fa-var-arrow-up-long, + "long-arrow-up": $fa-var-long-arrow-up, + "fire-flame-simple": $fa-var-fire-flame-simple, + "burn": $fa-var-burn, + "person": $fa-var-person, + "male": $fa-var-male, + "laptop": $fa-var-laptop, + "file-csv": $fa-var-file-csv, + "menorah": $fa-var-menorah, + "truck-plane": $fa-var-truck-plane, + "record-vinyl": $fa-var-record-vinyl, + "face-grin-stars": $fa-var-face-grin-stars, + "grin-stars": $fa-var-grin-stars, + "bong": $fa-var-bong, + "spaghetti-monster-flying": $fa-var-spaghetti-monster-flying, + "pastafarianism": $fa-var-pastafarianism, + "arrow-down-up-across-line": $fa-var-arrow-down-up-across-line, + "spoon": $fa-var-spoon, + "utensil-spoon": $fa-var-utensil-spoon, + "jar-wheat": $fa-var-jar-wheat, + "envelopes-bulk": $fa-var-envelopes-bulk, + "mail-bulk": $fa-var-mail-bulk, + "file-circle-exclamation": $fa-var-file-circle-exclamation, + "circle-h": $fa-var-circle-h, + "hospital-symbol": $fa-var-hospital-symbol, + "pager": $fa-var-pager, + "address-book": $fa-var-address-book, + "contact-book": $fa-var-contact-book, + "strikethrough": $fa-var-strikethrough, + "k": $fa-var-k, + "landmark-flag": $fa-var-landmark-flag, + "pencil": $fa-var-pencil, + "pencil-alt": $fa-var-pencil-alt, + "backward": $fa-var-backward, + "caret-right": $fa-var-caret-right, + "comments": $fa-var-comments, + "paste": $fa-var-paste, + "file-clipboard": $fa-var-file-clipboard, + "code-pull-request": $fa-var-code-pull-request, + "clipboard-list": $fa-var-clipboard-list, + "truck-ramp-box": $fa-var-truck-ramp-box, + "truck-loading": $fa-var-truck-loading, + "user-check": $fa-var-user-check, + "vial-virus": $fa-var-vial-virus, + "sheet-plastic": $fa-var-sheet-plastic, + "blog": $fa-var-blog, + "user-ninja": $fa-var-user-ninja, + "person-arrow-up-from-line": $fa-var-person-arrow-up-from-line, + "scroll-torah": $fa-var-scroll-torah, + "torah": $fa-var-torah, + "broom-ball": $fa-var-broom-ball, + "quidditch": $fa-var-quidditch, + "quidditch-broom-ball": $fa-var-quidditch-broom-ball, + "toggle-off": $fa-var-toggle-off, + "box-archive": $fa-var-box-archive, + "archive": $fa-var-archive, + "person-drowning": $fa-var-person-drowning, + "arrow-down-9-1": $fa-var-arrow-down-9-1, + "sort-numeric-desc": $fa-var-sort-numeric-desc, + "sort-numeric-down-alt": $fa-var-sort-numeric-down-alt, + "face-grin-tongue-squint": $fa-var-face-grin-tongue-squint, + "grin-tongue-squint": $fa-var-grin-tongue-squint, + "spray-can": $fa-var-spray-can, + "truck-monster": $fa-var-truck-monster, + "w": $fa-var-w, + "earth-africa": $fa-var-earth-africa, + "globe-africa": $fa-var-globe-africa, + "rainbow": $fa-var-rainbow, + "circle-notch": $fa-var-circle-notch, + "tablet-screen-button": $fa-var-tablet-screen-button, + "tablet-alt": $fa-var-tablet-alt, + "paw": $fa-var-paw, + "cloud": $fa-var-cloud, + "trowel-bricks": $fa-var-trowel-bricks, + "face-flushed": $fa-var-face-flushed, + "flushed": $fa-var-flushed, + "hospital-user": $fa-var-hospital-user, + "tent-arrow-left-right": $fa-var-tent-arrow-left-right, + "gavel": $fa-var-gavel, + "legal": $fa-var-legal, + "binoculars": $fa-var-binoculars, + "microphone-slash": $fa-var-microphone-slash, + "box-tissue": $fa-var-box-tissue, + "motorcycle": $fa-var-motorcycle, + "bell-concierge": $fa-var-bell-concierge, + "concierge-bell": $fa-var-concierge-bell, + "pen-ruler": $fa-var-pen-ruler, + "pencil-ruler": $fa-var-pencil-ruler, + "people-arrows": $fa-var-people-arrows, + "people-arrows-left-right": $fa-var-people-arrows-left-right, + "mars-and-venus-burst": $fa-var-mars-and-venus-burst, + "square-caret-right": $fa-var-square-caret-right, + "caret-square-right": $fa-var-caret-square-right, + "scissors": $fa-var-scissors, + "cut": $fa-var-cut, + "sun-plant-wilt": $fa-var-sun-plant-wilt, + "toilets-portable": $fa-var-toilets-portable, + "hockey-puck": $fa-var-hockey-puck, + "table": $fa-var-table, + "magnifying-glass-arrow-right": $fa-var-magnifying-glass-arrow-right, + "tachograph-digital": $fa-var-tachograph-digital, + "digital-tachograph": $fa-var-digital-tachograph, + "users-slash": $fa-var-users-slash, + "clover": $fa-var-clover, + "reply": $fa-var-reply, + "mail-reply": $fa-var-mail-reply, + "star-and-crescent": $fa-var-star-and-crescent, + "house-fire": $fa-var-house-fire, + "square-minus": $fa-var-square-minus, + "minus-square": $fa-var-minus-square, + "helicopter": $fa-var-helicopter, + "compass": $fa-var-compass, + "square-caret-down": $fa-var-square-caret-down, + "caret-square-down": $fa-var-caret-square-down, + "file-circle-question": $fa-var-file-circle-question, + "laptop-code": $fa-var-laptop-code, + "swatchbook": $fa-var-swatchbook, + "prescription-bottle": $fa-var-prescription-bottle, + "bars": $fa-var-bars, + "navicon": $fa-var-navicon, + "people-group": $fa-var-people-group, + "hourglass-end": $fa-var-hourglass-end, + "hourglass-3": $fa-var-hourglass-3, + "heart-crack": $fa-var-heart-crack, + "heart-broken": $fa-var-heart-broken, + "square-up-right": $fa-var-square-up-right, + "external-link-square-alt": $fa-var-external-link-square-alt, + "face-kiss-beam": $fa-var-face-kiss-beam, + "kiss-beam": $fa-var-kiss-beam, + "film": $fa-var-film, + "ruler-horizontal": $fa-var-ruler-horizontal, + "people-robbery": $fa-var-people-robbery, + "lightbulb": $fa-var-lightbulb, + "caret-left": $fa-var-caret-left, + "circle-exclamation": $fa-var-circle-exclamation, + "exclamation-circle": $fa-var-exclamation-circle, + "school-circle-xmark": $fa-var-school-circle-xmark, + "arrow-right-from-bracket": $fa-var-arrow-right-from-bracket, + "sign-out": $fa-var-sign-out, + "circle-chevron-down": $fa-var-circle-chevron-down, + "chevron-circle-down": $fa-var-chevron-circle-down, + "unlock-keyhole": $fa-var-unlock-keyhole, + "unlock-alt": $fa-var-unlock-alt, + "cloud-showers-heavy": $fa-var-cloud-showers-heavy, + "headphones-simple": $fa-var-headphones-simple, + "headphones-alt": $fa-var-headphones-alt, + "sitemap": $fa-var-sitemap, + "circle-dollar-to-slot": $fa-var-circle-dollar-to-slot, + "donate": $fa-var-donate, + "memory": $fa-var-memory, + "road-spikes": $fa-var-road-spikes, + "fire-burner": $fa-var-fire-burner, + "flag": $fa-var-flag, + "hanukiah": $fa-var-hanukiah, + "feather": $fa-var-feather, + "volume-low": $fa-var-volume-low, + "volume-down": $fa-var-volume-down, + "comment-slash": $fa-var-comment-slash, + "cloud-sun-rain": $fa-var-cloud-sun-rain, + "compress": $fa-var-compress, + "wheat-awn": $fa-var-wheat-awn, + "wheat-alt": $fa-var-wheat-alt, + "ankh": $fa-var-ankh, + "hands-holding-child": $fa-var-hands-holding-child, + "asterisk": $fa-var-asterisk, + "square-check": $fa-var-square-check, + "check-square": $fa-var-check-square, + "peseta-sign": $fa-var-peseta-sign, + "heading": $fa-var-heading, + "header": $fa-var-header, + "ghost": $fa-var-ghost, + "list": $fa-var-list, + "list-squares": $fa-var-list-squares, + "square-phone-flip": $fa-var-square-phone-flip, + "phone-square-alt": $fa-var-phone-square-alt, + "cart-plus": $fa-var-cart-plus, + "gamepad": $fa-var-gamepad, + "circle-dot": $fa-var-circle-dot, + "dot-circle": $fa-var-dot-circle, + "face-dizzy": $fa-var-face-dizzy, + "dizzy": $fa-var-dizzy, + "egg": $fa-var-egg, + "house-medical-circle-xmark": $fa-var-house-medical-circle-xmark, + "campground": $fa-var-campground, + "folder-plus": $fa-var-folder-plus, + "futbol": $fa-var-futbol, + "futbol-ball": $fa-var-futbol-ball, + "soccer-ball": $fa-var-soccer-ball, + "paintbrush": $fa-var-paintbrush, + "paint-brush": $fa-var-paint-brush, + "lock": $fa-var-lock, + "gas-pump": $fa-var-gas-pump, + "hot-tub-person": $fa-var-hot-tub-person, + "hot-tub": $fa-var-hot-tub, + "map-location": $fa-var-map-location, + "map-marked": $fa-var-map-marked, + "house-flood-water": $fa-var-house-flood-water, + "tree": $fa-var-tree, + "bridge-lock": $fa-var-bridge-lock, + "sack-dollar": $fa-var-sack-dollar, + "pen-to-square": $fa-var-pen-to-square, + "edit": $fa-var-edit, + "car-side": $fa-var-car-side, + "share-nodes": $fa-var-share-nodes, + "share-alt": $fa-var-share-alt, + "heart-circle-minus": $fa-var-heart-circle-minus, + "hourglass-half": $fa-var-hourglass-half, + "hourglass-2": $fa-var-hourglass-2, + "microscope": $fa-var-microscope, + "sink": $fa-var-sink, + "bag-shopping": $fa-var-bag-shopping, + "shopping-bag": $fa-var-shopping-bag, + "arrow-down-z-a": $fa-var-arrow-down-z-a, + "sort-alpha-desc": $fa-var-sort-alpha-desc, + "sort-alpha-down-alt": $fa-var-sort-alpha-down-alt, + "mitten": $fa-var-mitten, + "person-rays": $fa-var-person-rays, + "users": $fa-var-users, + "eye-slash": $fa-var-eye-slash, + "flask-vial": $fa-var-flask-vial, + "hand": $fa-var-hand, + "hand-paper": $fa-var-hand-paper, + "om": $fa-var-om, + "worm": $fa-var-worm, + "house-circle-xmark": $fa-var-house-circle-xmark, + "plug": $fa-var-plug, + "chevron-up": $fa-var-chevron-up, + "hand-spock": $fa-var-hand-spock, + "stopwatch": $fa-var-stopwatch, + "face-kiss": $fa-var-face-kiss, + "kiss": $fa-var-kiss, + "bridge-circle-xmark": $fa-var-bridge-circle-xmark, + "face-grin-tongue": $fa-var-face-grin-tongue, + "grin-tongue": $fa-var-grin-tongue, + "chess-bishop": $fa-var-chess-bishop, + "face-grin-wink": $fa-var-face-grin-wink, + "grin-wink": $fa-var-grin-wink, + "ear-deaf": $fa-var-ear-deaf, + "deaf": $fa-var-deaf, + "deafness": $fa-var-deafness, + "hard-of-hearing": $fa-var-hard-of-hearing, + "road-circle-check": $fa-var-road-circle-check, + "dice-five": $fa-var-dice-five, + "square-rss": $fa-var-square-rss, + "rss-square": $fa-var-rss-square, + "land-mine-on": $fa-var-land-mine-on, + "i-cursor": $fa-var-i-cursor, + "stamp": $fa-var-stamp, + "stairs": $fa-var-stairs, + "i": $fa-var-i, + "hryvnia-sign": $fa-var-hryvnia-sign, + "hryvnia": $fa-var-hryvnia, + "pills": $fa-var-pills, + "face-grin-wide": $fa-var-face-grin-wide, + "grin-alt": $fa-var-grin-alt, + "tooth": $fa-var-tooth, + "v": $fa-var-v, + "bangladeshi-taka-sign": $fa-var-bangladeshi-taka-sign, + "bicycle": $fa-var-bicycle, + "staff-snake": $fa-var-staff-snake, + "rod-asclepius": $fa-var-rod-asclepius, + "rod-snake": $fa-var-rod-snake, + "staff-aesculapius": $fa-var-staff-aesculapius, + "head-side-cough-slash": $fa-var-head-side-cough-slash, + "truck-medical": $fa-var-truck-medical, + "ambulance": $fa-var-ambulance, + "wheat-awn-circle-exclamation": $fa-var-wheat-awn-circle-exclamation, + "snowman": $fa-var-snowman, + "mortar-pestle": $fa-var-mortar-pestle, + "road-barrier": $fa-var-road-barrier, + "school": $fa-var-school, + "igloo": $fa-var-igloo, + "joint": $fa-var-joint, + "angle-right": $fa-var-angle-right, + "horse": $fa-var-horse, + "q": $fa-var-q, + "g": $fa-var-g, + "notes-medical": $fa-var-notes-medical, + "temperature-half": $fa-var-temperature-half, + "temperature-2": $fa-var-temperature-2, + "thermometer-2": $fa-var-thermometer-2, + "thermometer-half": $fa-var-thermometer-half, + "dong-sign": $fa-var-dong-sign, + "capsules": $fa-var-capsules, + "poo-storm": $fa-var-poo-storm, + "poo-bolt": $fa-var-poo-bolt, + "face-frown-open": $fa-var-face-frown-open, + "frown-open": $fa-var-frown-open, + "hand-point-up": $fa-var-hand-point-up, + "money-bill": $fa-var-money-bill, + "bookmark": $fa-var-bookmark, + "align-justify": $fa-var-align-justify, + "umbrella-beach": $fa-var-umbrella-beach, + "helmet-un": $fa-var-helmet-un, + "bullseye": $fa-var-bullseye, + "bacon": $fa-var-bacon, + "hand-point-down": $fa-var-hand-point-down, + "arrow-up-from-bracket": $fa-var-arrow-up-from-bracket, + "folder": $fa-var-folder, + "folder-blank": $fa-var-folder-blank, + "file-waveform": $fa-var-file-waveform, + "file-medical-alt": $fa-var-file-medical-alt, + "radiation": $fa-var-radiation, + "chart-simple": $fa-var-chart-simple, + "mars-stroke": $fa-var-mars-stroke, + "vial": $fa-var-vial, + "gauge": $fa-var-gauge, + "dashboard": $fa-var-dashboard, + "gauge-med": $fa-var-gauge-med, + "tachometer-alt-average": $fa-var-tachometer-alt-average, + "wand-magic-sparkles": $fa-var-wand-magic-sparkles, + "magic-wand-sparkles": $fa-var-magic-wand-sparkles, + "e": $fa-var-e, + "pen-clip": $fa-var-pen-clip, + "pen-alt": $fa-var-pen-alt, + "bridge-circle-exclamation": $fa-var-bridge-circle-exclamation, + "user": $fa-var-user, + "school-circle-check": $fa-var-school-circle-check, + "dumpster": $fa-var-dumpster, + "van-shuttle": $fa-var-van-shuttle, + "shuttle-van": $fa-var-shuttle-van, + "building-user": $fa-var-building-user, + "square-caret-left": $fa-var-square-caret-left, + "caret-square-left": $fa-var-caret-square-left, + "highlighter": $fa-var-highlighter, + "key": $fa-var-key, + "bullhorn": $fa-var-bullhorn, + "globe": $fa-var-globe, + "synagogue": $fa-var-synagogue, + "person-half-dress": $fa-var-person-half-dress, + "road-bridge": $fa-var-road-bridge, + "location-arrow": $fa-var-location-arrow, + "c": $fa-var-c, + "tablet-button": $fa-var-tablet-button, + "building-lock": $fa-var-building-lock, + "pizza-slice": $fa-var-pizza-slice, + "money-bill-wave": $fa-var-money-bill-wave, + "chart-area": $fa-var-chart-area, + "area-chart": $fa-var-area-chart, + "house-flag": $fa-var-house-flag, + "person-circle-minus": $fa-var-person-circle-minus, + "ban": $fa-var-ban, + "cancel": $fa-var-cancel, + "camera-rotate": $fa-var-camera-rotate, + "spray-can-sparkles": $fa-var-spray-can-sparkles, + "air-freshener": $fa-var-air-freshener, + "star": $fa-var-star, + "repeat": $fa-var-repeat, + "cross": $fa-var-cross, + "box": $fa-var-box, + "venus-mars": $fa-var-venus-mars, + "arrow-pointer": $fa-var-arrow-pointer, + "mouse-pointer": $fa-var-mouse-pointer, + "maximize": $fa-var-maximize, + "expand-arrows-alt": $fa-var-expand-arrows-alt, + "charging-station": $fa-var-charging-station, + "shapes": $fa-var-shapes, + "triangle-circle-square": $fa-var-triangle-circle-square, + "shuffle": $fa-var-shuffle, + "random": $fa-var-random, + "person-running": $fa-var-person-running, + "running": $fa-var-running, + "mobile-retro": $fa-var-mobile-retro, + "grip-lines-vertical": $fa-var-grip-lines-vertical, + "spider": $fa-var-spider, + "hands-bound": $fa-var-hands-bound, + "file-invoice-dollar": $fa-var-file-invoice-dollar, + "plane-circle-exclamation": $fa-var-plane-circle-exclamation, + "x-ray": $fa-var-x-ray, + "spell-check": $fa-var-spell-check, + "slash": $fa-var-slash, + "computer-mouse": $fa-var-computer-mouse, + "mouse": $fa-var-mouse, + "arrow-right-to-bracket": $fa-var-arrow-right-to-bracket, + "sign-in": $fa-var-sign-in, + "shop-slash": $fa-var-shop-slash, + "store-alt-slash": $fa-var-store-alt-slash, + "server": $fa-var-server, + "virus-covid-slash": $fa-var-virus-covid-slash, + "shop-lock": $fa-var-shop-lock, + "hourglass-start": $fa-var-hourglass-start, + "hourglass-1": $fa-var-hourglass-1, + "blender-phone": $fa-var-blender-phone, + "building-wheat": $fa-var-building-wheat, + "person-breastfeeding": $fa-var-person-breastfeeding, + "right-to-bracket": $fa-var-right-to-bracket, + "sign-in-alt": $fa-var-sign-in-alt, + "venus": $fa-var-venus, + "passport": $fa-var-passport, + "thumbtack-slash": $fa-var-thumbtack-slash, + "thumb-tack-slash": $fa-var-thumb-tack-slash, + "heart-pulse": $fa-var-heart-pulse, + "heartbeat": $fa-var-heartbeat, + "people-carry-box": $fa-var-people-carry-box, + "people-carry": $fa-var-people-carry, + "temperature-high": $fa-var-temperature-high, + "microchip": $fa-var-microchip, + "crown": $fa-var-crown, + "weight-hanging": $fa-var-weight-hanging, + "xmarks-lines": $fa-var-xmarks-lines, + "file-prescription": $fa-var-file-prescription, + "weight-scale": $fa-var-weight-scale, + "weight": $fa-var-weight, + "user-group": $fa-var-user-group, + "user-friends": $fa-var-user-friends, + "arrow-up-a-z": $fa-var-arrow-up-a-z, + "sort-alpha-up": $fa-var-sort-alpha-up, + "chess-knight": $fa-var-chess-knight, + "face-laugh-squint": $fa-var-face-laugh-squint, + "laugh-squint": $fa-var-laugh-squint, + "wheelchair": $fa-var-wheelchair, + "circle-arrow-up": $fa-var-circle-arrow-up, + "arrow-circle-up": $fa-var-arrow-circle-up, + "toggle-on": $fa-var-toggle-on, + "person-walking": $fa-var-person-walking, + "walking": $fa-var-walking, + "l": $fa-var-l, + "fire": $fa-var-fire, + "bed-pulse": $fa-var-bed-pulse, + "procedures": $fa-var-procedures, + "shuttle-space": $fa-var-shuttle-space, + "space-shuttle": $fa-var-space-shuttle, + "face-laugh": $fa-var-face-laugh, + "laugh": $fa-var-laugh, + "folder-open": $fa-var-folder-open, + "heart-circle-plus": $fa-var-heart-circle-plus, + "code-fork": $fa-var-code-fork, + "city": $fa-var-city, + "microphone-lines": $fa-var-microphone-lines, + "microphone-alt": $fa-var-microphone-alt, + "pepper-hot": $fa-var-pepper-hot, + "unlock": $fa-var-unlock, + "colon-sign": $fa-var-colon-sign, + "headset": $fa-var-headset, + "store-slash": $fa-var-store-slash, + "road-circle-xmark": $fa-var-road-circle-xmark, + "user-minus": $fa-var-user-minus, + "mars-stroke-up": $fa-var-mars-stroke-up, + "mars-stroke-v": $fa-var-mars-stroke-v, + "champagne-glasses": $fa-var-champagne-glasses, + "glass-cheers": $fa-var-glass-cheers, + "clipboard": $fa-var-clipboard, + "house-circle-exclamation": $fa-var-house-circle-exclamation, + "file-arrow-up": $fa-var-file-arrow-up, + "file-upload": $fa-var-file-upload, + "wifi": $fa-var-wifi, + "wifi-3": $fa-var-wifi-3, + "wifi-strong": $fa-var-wifi-strong, + "bath": $fa-var-bath, + "bathtub": $fa-var-bathtub, + "underline": $fa-var-underline, + "user-pen": $fa-var-user-pen, + "user-edit": $fa-var-user-edit, + "signature": $fa-var-signature, + "stroopwafel": $fa-var-stroopwafel, + "bold": $fa-var-bold, + "anchor-lock": $fa-var-anchor-lock, + "building-ngo": $fa-var-building-ngo, + "manat-sign": $fa-var-manat-sign, + "not-equal": $fa-var-not-equal, + "border-top-left": $fa-var-border-top-left, + "border-style": $fa-var-border-style, + "map-location-dot": $fa-var-map-location-dot, + "map-marked-alt": $fa-var-map-marked-alt, + "jedi": $fa-var-jedi, + "square-poll-vertical": $fa-var-square-poll-vertical, + "poll": $fa-var-poll, + "mug-hot": $fa-var-mug-hot, + "car-battery": $fa-var-car-battery, + "battery-car": $fa-var-battery-car, + "gift": $fa-var-gift, + "dice-two": $fa-var-dice-two, + "chess-queen": $fa-var-chess-queen, + "glasses": $fa-var-glasses, + "chess-board": $fa-var-chess-board, + "building-circle-check": $fa-var-building-circle-check, + "person-chalkboard": $fa-var-person-chalkboard, + "mars-stroke-right": $fa-var-mars-stroke-right, + "mars-stroke-h": $fa-var-mars-stroke-h, + "hand-back-fist": $fa-var-hand-back-fist, + "hand-rock": $fa-var-hand-rock, + "square-caret-up": $fa-var-square-caret-up, + "caret-square-up": $fa-var-caret-square-up, + "cloud-showers-water": $fa-var-cloud-showers-water, + "chart-bar": $fa-var-chart-bar, + "bar-chart": $fa-var-bar-chart, + "hands-bubbles": $fa-var-hands-bubbles, + "hands-wash": $fa-var-hands-wash, + "less-than-equal": $fa-var-less-than-equal, + "train": $fa-var-train, + "eye-low-vision": $fa-var-eye-low-vision, + "low-vision": $fa-var-low-vision, + "crow": $fa-var-crow, + "sailboat": $fa-var-sailboat, + "window-restore": $fa-var-window-restore, + "square-plus": $fa-var-square-plus, + "plus-square": $fa-var-plus-square, + "torii-gate": $fa-var-torii-gate, + "frog": $fa-var-frog, + "bucket": $fa-var-bucket, + "image": $fa-var-image, + "microphone": $fa-var-microphone, + "cow": $fa-var-cow, + "caret-up": $fa-var-caret-up, + "screwdriver": $fa-var-screwdriver, + "folder-closed": $fa-var-folder-closed, + "house-tsunami": $fa-var-house-tsunami, + "square-nfi": $fa-var-square-nfi, + "arrow-up-from-ground-water": $fa-var-arrow-up-from-ground-water, + "martini-glass": $fa-var-martini-glass, + "glass-martini-alt": $fa-var-glass-martini-alt, + "square-binary": $fa-var-square-binary, + "rotate-left": $fa-var-rotate-left, + "rotate-back": $fa-var-rotate-back, + "rotate-backward": $fa-var-rotate-backward, + "undo-alt": $fa-var-undo-alt, + "table-columns": $fa-var-table-columns, + "columns": $fa-var-columns, + "lemon": $fa-var-lemon, + "head-side-mask": $fa-var-head-side-mask, + "handshake": $fa-var-handshake, + "gem": $fa-var-gem, + "dolly": $fa-var-dolly, + "dolly-box": $fa-var-dolly-box, + "smoking": $fa-var-smoking, + "minimize": $fa-var-minimize, + "compress-arrows-alt": $fa-var-compress-arrows-alt, + "monument": $fa-var-monument, + "snowplow": $fa-var-snowplow, + "angles-right": $fa-var-angles-right, + "angle-double-right": $fa-var-angle-double-right, + "cannabis": $fa-var-cannabis, + "circle-play": $fa-var-circle-play, + "play-circle": $fa-var-play-circle, + "tablets": $fa-var-tablets, + "ethernet": $fa-var-ethernet, + "euro-sign": $fa-var-euro-sign, + "eur": $fa-var-eur, + "euro": $fa-var-euro, + "chair": $fa-var-chair, + "circle-check": $fa-var-circle-check, + "check-circle": $fa-var-check-circle, + "circle-stop": $fa-var-circle-stop, + "stop-circle": $fa-var-stop-circle, + "compass-drafting": $fa-var-compass-drafting, + "drafting-compass": $fa-var-drafting-compass, + "plate-wheat": $fa-var-plate-wheat, + "icicles": $fa-var-icicles, + "person-shelter": $fa-var-person-shelter, + "neuter": $fa-var-neuter, + "id-badge": $fa-var-id-badge, + "marker": $fa-var-marker, + "face-laugh-beam": $fa-var-face-laugh-beam, + "laugh-beam": $fa-var-laugh-beam, + "helicopter-symbol": $fa-var-helicopter-symbol, + "universal-access": $fa-var-universal-access, + "circle-chevron-up": $fa-var-circle-chevron-up, + "chevron-circle-up": $fa-var-chevron-circle-up, + "lari-sign": $fa-var-lari-sign, + "volcano": $fa-var-volcano, + "person-walking-dashed-line-arrow-right": $fa-var-person-walking-dashed-line-arrow-right, + "sterling-sign": $fa-var-sterling-sign, + "gbp": $fa-var-gbp, + "pound-sign": $fa-var-pound-sign, + "viruses": $fa-var-viruses, + "square-person-confined": $fa-var-square-person-confined, + "user-tie": $fa-var-user-tie, + "arrow-down-long": $fa-var-arrow-down-long, + "long-arrow-down": $fa-var-long-arrow-down, + "tent-arrow-down-to-line": $fa-var-tent-arrow-down-to-line, + "certificate": $fa-var-certificate, + "reply-all": $fa-var-reply-all, + "mail-reply-all": $fa-var-mail-reply-all, + "suitcase": $fa-var-suitcase, + "person-skating": $fa-var-person-skating, + "skating": $fa-var-skating, + "filter-circle-dollar": $fa-var-filter-circle-dollar, + "funnel-dollar": $fa-var-funnel-dollar, + "camera-retro": $fa-var-camera-retro, + "circle-arrow-down": $fa-var-circle-arrow-down, + "arrow-circle-down": $fa-var-arrow-circle-down, + "file-import": $fa-var-file-import, + "arrow-right-to-file": $fa-var-arrow-right-to-file, + "square-arrow-up-right": $fa-var-square-arrow-up-right, + "external-link-square": $fa-var-external-link-square, + "box-open": $fa-var-box-open, + "scroll": $fa-var-scroll, + "spa": $fa-var-spa, + "location-pin-lock": $fa-var-location-pin-lock, + "pause": $fa-var-pause, + "hill-avalanche": $fa-var-hill-avalanche, + "temperature-empty": $fa-var-temperature-empty, + "temperature-0": $fa-var-temperature-0, + "thermometer-0": $fa-var-thermometer-0, + "thermometer-empty": $fa-var-thermometer-empty, + "bomb": $fa-var-bomb, + "registered": $fa-var-registered, + "address-card": $fa-var-address-card, + "contact-card": $fa-var-contact-card, + "vcard": $fa-var-vcard, + "scale-unbalanced-flip": $fa-var-scale-unbalanced-flip, + "balance-scale-right": $fa-var-balance-scale-right, + "subscript": $fa-var-subscript, + "diamond-turn-right": $fa-var-diamond-turn-right, + "directions": $fa-var-directions, + "burst": $fa-var-burst, + "house-laptop": $fa-var-house-laptop, + "laptop-house": $fa-var-laptop-house, + "face-tired": $fa-var-face-tired, + "tired": $fa-var-tired, + "money-bills": $fa-var-money-bills, + "smog": $fa-var-smog, + "crutch": $fa-var-crutch, + "cloud-arrow-up": $fa-var-cloud-arrow-up, + "cloud-upload": $fa-var-cloud-upload, + "cloud-upload-alt": $fa-var-cloud-upload-alt, + "palette": $fa-var-palette, + "arrows-turn-right": $fa-var-arrows-turn-right, + "vest": $fa-var-vest, + "ferry": $fa-var-ferry, + "arrows-down-to-people": $fa-var-arrows-down-to-people, + "seedling": $fa-var-seedling, + "sprout": $fa-var-sprout, + "left-right": $fa-var-left-right, + "arrows-alt-h": $fa-var-arrows-alt-h, + "boxes-packing": $fa-var-boxes-packing, + "circle-arrow-left": $fa-var-circle-arrow-left, + "arrow-circle-left": $fa-var-arrow-circle-left, + "group-arrows-rotate": $fa-var-group-arrows-rotate, + "bowl-food": $fa-var-bowl-food, + "candy-cane": $fa-var-candy-cane, + "arrow-down-wide-short": $fa-var-arrow-down-wide-short, + "sort-amount-asc": $fa-var-sort-amount-asc, + "sort-amount-down": $fa-var-sort-amount-down, + "cloud-bolt": $fa-var-cloud-bolt, + "thunderstorm": $fa-var-thunderstorm, + "text-slash": $fa-var-text-slash, + "remove-format": $fa-var-remove-format, + "face-smile-wink": $fa-var-face-smile-wink, + "smile-wink": $fa-var-smile-wink, + "file-word": $fa-var-file-word, + "file-powerpoint": $fa-var-file-powerpoint, + "arrows-left-right": $fa-var-arrows-left-right, + "arrows-h": $fa-var-arrows-h, + "house-lock": $fa-var-house-lock, + "cloud-arrow-down": $fa-var-cloud-arrow-down, + "cloud-download": $fa-var-cloud-download, + "cloud-download-alt": $fa-var-cloud-download-alt, + "children": $fa-var-children, + "chalkboard": $fa-var-chalkboard, + "blackboard": $fa-var-blackboard, + "user-large-slash": $fa-var-user-large-slash, + "user-alt-slash": $fa-var-user-alt-slash, + "envelope-open": $fa-var-envelope-open, + "handshake-simple-slash": $fa-var-handshake-simple-slash, + "handshake-alt-slash": $fa-var-handshake-alt-slash, + "mattress-pillow": $fa-var-mattress-pillow, + "guarani-sign": $fa-var-guarani-sign, + "arrows-rotate": $fa-var-arrows-rotate, + "refresh": $fa-var-refresh, + "sync": $fa-var-sync, + "fire-extinguisher": $fa-var-fire-extinguisher, + "cruzeiro-sign": $fa-var-cruzeiro-sign, + "greater-than-equal": $fa-var-greater-than-equal, + "shield-halved": $fa-var-shield-halved, + "shield-alt": $fa-var-shield-alt, + "book-atlas": $fa-var-book-atlas, + "atlas": $fa-var-atlas, + "virus": $fa-var-virus, + "envelope-circle-check": $fa-var-envelope-circle-check, + "layer-group": $fa-var-layer-group, + "arrows-to-dot": $fa-var-arrows-to-dot, + "archway": $fa-var-archway, + "heart-circle-check": $fa-var-heart-circle-check, + "house-chimney-crack": $fa-var-house-chimney-crack, + "house-damage": $fa-var-house-damage, + "file-zipper": $fa-var-file-zipper, + "file-archive": $fa-var-file-archive, + "square": $fa-var-square, + "martini-glass-empty": $fa-var-martini-glass-empty, + "glass-martini": $fa-var-glass-martini, + "couch": $fa-var-couch, + "cedi-sign": $fa-var-cedi-sign, + "italic": $fa-var-italic, + "table-cells-column-lock": $fa-var-table-cells-column-lock, + "church": $fa-var-church, + "comments-dollar": $fa-var-comments-dollar, + "democrat": $fa-var-democrat, + "z": $fa-var-z, + "person-skiing": $fa-var-person-skiing, + "skiing": $fa-var-skiing, + "road-lock": $fa-var-road-lock, + "a": $fa-var-a, + "temperature-arrow-down": $fa-var-temperature-arrow-down, + "temperature-down": $fa-var-temperature-down, + "feather-pointed": $fa-var-feather-pointed, + "feather-alt": $fa-var-feather-alt, + "p": $fa-var-p, + "snowflake": $fa-var-snowflake, + "newspaper": $fa-var-newspaper, + "rectangle-ad": $fa-var-rectangle-ad, + "ad": $fa-var-ad, + "circle-arrow-right": $fa-var-circle-arrow-right, + "arrow-circle-right": $fa-var-arrow-circle-right, + "filter-circle-xmark": $fa-var-filter-circle-xmark, + "locust": $fa-var-locust, + "sort": $fa-var-sort, + "unsorted": $fa-var-unsorted, + "list-ol": $fa-var-list-ol, + "list-1-2": $fa-var-list-1-2, + "list-numeric": $fa-var-list-numeric, + "person-dress-burst": $fa-var-person-dress-burst, + "money-check-dollar": $fa-var-money-check-dollar, + "money-check-alt": $fa-var-money-check-alt, + "vector-square": $fa-var-vector-square, + "bread-slice": $fa-var-bread-slice, + "language": $fa-var-language, + "face-kiss-wink-heart": $fa-var-face-kiss-wink-heart, + "kiss-wink-heart": $fa-var-kiss-wink-heart, + "filter": $fa-var-filter, + "question": $fa-var-question, + "file-signature": $fa-var-file-signature, + "up-down-left-right": $fa-var-up-down-left-right, + "arrows-alt": $fa-var-arrows-alt, + "house-chimney-user": $fa-var-house-chimney-user, + "hand-holding-heart": $fa-var-hand-holding-heart, + "puzzle-piece": $fa-var-puzzle-piece, + "money-check": $fa-var-money-check, + "star-half-stroke": $fa-var-star-half-stroke, + "star-half-alt": $fa-var-star-half-alt, + "code": $fa-var-code, + "whiskey-glass": $fa-var-whiskey-glass, + "glass-whiskey": $fa-var-glass-whiskey, + "building-circle-exclamation": $fa-var-building-circle-exclamation, + "magnifying-glass-chart": $fa-var-magnifying-glass-chart, + "arrow-up-right-from-square": $fa-var-arrow-up-right-from-square, + "external-link": $fa-var-external-link, + "cubes-stacked": $fa-var-cubes-stacked, + "won-sign": $fa-var-won-sign, + "krw": $fa-var-krw, + "won": $fa-var-won, + "virus-covid": $fa-var-virus-covid, + "austral-sign": $fa-var-austral-sign, + "f": $fa-var-f, + "leaf": $fa-var-leaf, + "road": $fa-var-road, + "taxi": $fa-var-taxi, + "cab": $fa-var-cab, + "person-circle-plus": $fa-var-person-circle-plus, + "chart-pie": $fa-var-chart-pie, + "pie-chart": $fa-var-pie-chart, + "bolt-lightning": $fa-var-bolt-lightning, + "sack-xmark": $fa-var-sack-xmark, + "file-excel": $fa-var-file-excel, + "file-contract": $fa-var-file-contract, + "fish-fins": $fa-var-fish-fins, + "building-flag": $fa-var-building-flag, + "face-grin-beam": $fa-var-face-grin-beam, + "grin-beam": $fa-var-grin-beam, + "object-ungroup": $fa-var-object-ungroup, + "poop": $fa-var-poop, + "location-pin": $fa-var-location-pin, + "map-marker": $fa-var-map-marker, + "kaaba": $fa-var-kaaba, + "toilet-paper": $fa-var-toilet-paper, + "helmet-safety": $fa-var-helmet-safety, + "hard-hat": $fa-var-hard-hat, + "hat-hard": $fa-var-hat-hard, + "eject": $fa-var-eject, + "circle-right": $fa-var-circle-right, + "arrow-alt-circle-right": $fa-var-arrow-alt-circle-right, + "plane-circle-check": $fa-var-plane-circle-check, + "face-rolling-eyes": $fa-var-face-rolling-eyes, + "meh-rolling-eyes": $fa-var-meh-rolling-eyes, + "object-group": $fa-var-object-group, + "chart-line": $fa-var-chart-line, + "line-chart": $fa-var-line-chart, + "mask-ventilator": $fa-var-mask-ventilator, + "arrow-right": $fa-var-arrow-right, + "signs-post": $fa-var-signs-post, + "map-signs": $fa-var-map-signs, + "cash-register": $fa-var-cash-register, + "person-circle-question": $fa-var-person-circle-question, + "h": $fa-var-h, + "tarp": $fa-var-tarp, + "screwdriver-wrench": $fa-var-screwdriver-wrench, + "tools": $fa-var-tools, + "arrows-to-eye": $fa-var-arrows-to-eye, + "plug-circle-bolt": $fa-var-plug-circle-bolt, + "heart": $fa-var-heart, + "mars-and-venus": $fa-var-mars-and-venus, + "house-user": $fa-var-house-user, + "home-user": $fa-var-home-user, + "dumpster-fire": $fa-var-dumpster-fire, + "house-crack": $fa-var-house-crack, + "martini-glass-citrus": $fa-var-martini-glass-citrus, + "cocktail": $fa-var-cocktail, + "face-surprise": $fa-var-face-surprise, + "surprise": $fa-var-surprise, + "bottle-water": $fa-var-bottle-water, + "circle-pause": $fa-var-circle-pause, + "pause-circle": $fa-var-pause-circle, + "toilet-paper-slash": $fa-var-toilet-paper-slash, + "apple-whole": $fa-var-apple-whole, + "apple-alt": $fa-var-apple-alt, + "kitchen-set": $fa-var-kitchen-set, + "r": $fa-var-r, + "temperature-quarter": $fa-var-temperature-quarter, + "temperature-1": $fa-var-temperature-1, + "thermometer-1": $fa-var-thermometer-1, + "thermometer-quarter": $fa-var-thermometer-quarter, + "cube": $fa-var-cube, + "bitcoin-sign": $fa-var-bitcoin-sign, + "shield-dog": $fa-var-shield-dog, + "solar-panel": $fa-var-solar-panel, + "lock-open": $fa-var-lock-open, + "elevator": $fa-var-elevator, + "money-bill-transfer": $fa-var-money-bill-transfer, + "money-bill-trend-up": $fa-var-money-bill-trend-up, + "house-flood-water-circle-arrow-right": $fa-var-house-flood-water-circle-arrow-right, + "square-poll-horizontal": $fa-var-square-poll-horizontal, + "poll-h": $fa-var-poll-h, + "circle": $fa-var-circle, + "backward-fast": $fa-var-backward-fast, + "fast-backward": $fa-var-fast-backward, + "recycle": $fa-var-recycle, + "user-astronaut": $fa-var-user-astronaut, + "plane-slash": $fa-var-plane-slash, + "trademark": $fa-var-trademark, + "basketball": $fa-var-basketball, + "basketball-ball": $fa-var-basketball-ball, + "satellite-dish": $fa-var-satellite-dish, + "circle-up": $fa-var-circle-up, + "arrow-alt-circle-up": $fa-var-arrow-alt-circle-up, + "mobile-screen-button": $fa-var-mobile-screen-button, + "mobile-alt": $fa-var-mobile-alt, + "volume-high": $fa-var-volume-high, + "volume-up": $fa-var-volume-up, + "users-rays": $fa-var-users-rays, + "wallet": $fa-var-wallet, + "clipboard-check": $fa-var-clipboard-check, + "file-audio": $fa-var-file-audio, + "burger": $fa-var-burger, + "hamburger": $fa-var-hamburger, + "wrench": $fa-var-wrench, + "bugs": $fa-var-bugs, + "rupee-sign": $fa-var-rupee-sign, + "rupee": $fa-var-rupee, + "file-image": $fa-var-file-image, + "circle-question": $fa-var-circle-question, + "question-circle": $fa-var-question-circle, + "plane-departure": $fa-var-plane-departure, + "handshake-slash": $fa-var-handshake-slash, + "book-bookmark": $fa-var-book-bookmark, + "code-branch": $fa-var-code-branch, + "hat-cowboy": $fa-var-hat-cowboy, + "bridge": $fa-var-bridge, + "phone-flip": $fa-var-phone-flip, + "phone-alt": $fa-var-phone-alt, + "truck-front": $fa-var-truck-front, + "cat": $fa-var-cat, + "anchor-circle-exclamation": $fa-var-anchor-circle-exclamation, + "truck-field": $fa-var-truck-field, + "route": $fa-var-route, + "clipboard-question": $fa-var-clipboard-question, + "panorama": $fa-var-panorama, + "comment-medical": $fa-var-comment-medical, + "teeth-open": $fa-var-teeth-open, + "file-circle-minus": $fa-var-file-circle-minus, + "tags": $fa-var-tags, + "wine-glass": $fa-var-wine-glass, + "forward-fast": $fa-var-forward-fast, + "fast-forward": $fa-var-fast-forward, + "face-meh-blank": $fa-var-face-meh-blank, + "meh-blank": $fa-var-meh-blank, + "square-parking": $fa-var-square-parking, + "parking": $fa-var-parking, + "house-signal": $fa-var-house-signal, + "bars-progress": $fa-var-bars-progress, + "tasks-alt": $fa-var-tasks-alt, + "faucet-drip": $fa-var-faucet-drip, + "cart-flatbed": $fa-var-cart-flatbed, + "dolly-flatbed": $fa-var-dolly-flatbed, + "ban-smoking": $fa-var-ban-smoking, + "smoking-ban": $fa-var-smoking-ban, + "terminal": $fa-var-terminal, + "mobile-button": $fa-var-mobile-button, + "house-medical-flag": $fa-var-house-medical-flag, + "basket-shopping": $fa-var-basket-shopping, + "shopping-basket": $fa-var-shopping-basket, + "tape": $fa-var-tape, + "bus-simple": $fa-var-bus-simple, + "bus-alt": $fa-var-bus-alt, + "eye": $fa-var-eye, + "face-sad-cry": $fa-var-face-sad-cry, + "sad-cry": $fa-var-sad-cry, + "audio-description": $fa-var-audio-description, + "person-military-to-person": $fa-var-person-military-to-person, + "file-shield": $fa-var-file-shield, + "user-slash": $fa-var-user-slash, + "pen": $fa-var-pen, + "tower-observation": $fa-var-tower-observation, + "file-code": $fa-var-file-code, + "signal": $fa-var-signal, + "signal-5": $fa-var-signal-5, + "signal-perfect": $fa-var-signal-perfect, + "bus": $fa-var-bus, + "heart-circle-xmark": $fa-var-heart-circle-xmark, + "house-chimney": $fa-var-house-chimney, + "home-lg": $fa-var-home-lg, + "window-maximize": $fa-var-window-maximize, + "face-frown": $fa-var-face-frown, + "frown": $fa-var-frown, + "prescription": $fa-var-prescription, + "shop": $fa-var-shop, + "store-alt": $fa-var-store-alt, + "floppy-disk": $fa-var-floppy-disk, + "save": $fa-var-save, + "vihara": $fa-var-vihara, + "scale-unbalanced": $fa-var-scale-unbalanced, + "balance-scale-left": $fa-var-balance-scale-left, + "sort-up": $fa-var-sort-up, + "sort-asc": $fa-var-sort-asc, + "comment-dots": $fa-var-comment-dots, + "commenting": $fa-var-commenting, + "plant-wilt": $fa-var-plant-wilt, + "diamond": $fa-var-diamond, + "face-grin-squint": $fa-var-face-grin-squint, + "grin-squint": $fa-var-grin-squint, + "hand-holding-dollar": $fa-var-hand-holding-dollar, + "hand-holding-usd": $fa-var-hand-holding-usd, + "chart-diagram": $fa-var-chart-diagram, + "bacterium": $fa-var-bacterium, + "hand-pointer": $fa-var-hand-pointer, + "drum-steelpan": $fa-var-drum-steelpan, + "hand-scissors": $fa-var-hand-scissors, + "hands-praying": $fa-var-hands-praying, + "praying-hands": $fa-var-praying-hands, + "arrow-rotate-right": $fa-var-arrow-rotate-right, + "arrow-right-rotate": $fa-var-arrow-right-rotate, + "arrow-rotate-forward": $fa-var-arrow-rotate-forward, + "redo": $fa-var-redo, + "biohazard": $fa-var-biohazard, + "location-crosshairs": $fa-var-location-crosshairs, + "location": $fa-var-location, + "mars-double": $fa-var-mars-double, + "child-dress": $fa-var-child-dress, + "users-between-lines": $fa-var-users-between-lines, + "lungs-virus": $fa-var-lungs-virus, + "face-grin-tears": $fa-var-face-grin-tears, + "grin-tears": $fa-var-grin-tears, + "phone": $fa-var-phone, + "calendar-xmark": $fa-var-calendar-xmark, + "calendar-times": $fa-var-calendar-times, + "child-reaching": $fa-var-child-reaching, + "head-side-virus": $fa-var-head-side-virus, + "user-gear": $fa-var-user-gear, + "user-cog": $fa-var-user-cog, + "arrow-up-1-9": $fa-var-arrow-up-1-9, + "sort-numeric-up": $fa-var-sort-numeric-up, + "door-closed": $fa-var-door-closed, + "shield-virus": $fa-var-shield-virus, + "dice-six": $fa-var-dice-six, + "mosquito-net": $fa-var-mosquito-net, + "file-fragment": $fa-var-file-fragment, + "bridge-water": $fa-var-bridge-water, + "person-booth": $fa-var-person-booth, + "text-width": $fa-var-text-width, + "hat-wizard": $fa-var-hat-wizard, + "pen-fancy": $fa-var-pen-fancy, + "person-digging": $fa-var-person-digging, + "digging": $fa-var-digging, + "trash": $fa-var-trash, + "gauge-simple": $fa-var-gauge-simple, + "gauge-simple-med": $fa-var-gauge-simple-med, + "tachometer-average": $fa-var-tachometer-average, + "book-medical": $fa-var-book-medical, + "poo": $fa-var-poo, + "quote-right": $fa-var-quote-right, + "quote-right-alt": $fa-var-quote-right-alt, + "shirt": $fa-var-shirt, + "t-shirt": $fa-var-t-shirt, + "tshirt": $fa-var-tshirt, + "cubes": $fa-var-cubes, + "divide": $fa-var-divide, + "tenge-sign": $fa-var-tenge-sign, + "tenge": $fa-var-tenge, + "headphones": $fa-var-headphones, + "hands-holding": $fa-var-hands-holding, + "hands-clapping": $fa-var-hands-clapping, + "republican": $fa-var-republican, + "arrow-left": $fa-var-arrow-left, + "person-circle-xmark": $fa-var-person-circle-xmark, + "ruler": $fa-var-ruler, + "align-left": $fa-var-align-left, + "dice-d6": $fa-var-dice-d6, + "restroom": $fa-var-restroom, + "j": $fa-var-j, + "users-viewfinder": $fa-var-users-viewfinder, + "file-video": $fa-var-file-video, + "up-right-from-square": $fa-var-up-right-from-square, + "external-link-alt": $fa-var-external-link-alt, + "table-cells": $fa-var-table-cells, + "th": $fa-var-th, + "file-pdf": $fa-var-file-pdf, + "book-bible": $fa-var-book-bible, + "bible": $fa-var-bible, + "o": $fa-var-o, + "suitcase-medical": $fa-var-suitcase-medical, + "medkit": $fa-var-medkit, + "user-secret": $fa-var-user-secret, + "otter": $fa-var-otter, + "person-dress": $fa-var-person-dress, + "female": $fa-var-female, + "comment-dollar": $fa-var-comment-dollar, + "business-time": $fa-var-business-time, + "briefcase-clock": $fa-var-briefcase-clock, + "table-cells-large": $fa-var-table-cells-large, + "th-large": $fa-var-th-large, + "book-tanakh": $fa-var-book-tanakh, + "tanakh": $fa-var-tanakh, + "phone-volume": $fa-var-phone-volume, + "volume-control-phone": $fa-var-volume-control-phone, + "hat-cowboy-side": $fa-var-hat-cowboy-side, + "clipboard-user": $fa-var-clipboard-user, + "child": $fa-var-child, + "lira-sign": $fa-var-lira-sign, + "satellite": $fa-var-satellite, + "plane-lock": $fa-var-plane-lock, + "tag": $fa-var-tag, + "comment": $fa-var-comment, + "cake-candles": $fa-var-cake-candles, + "birthday-cake": $fa-var-birthday-cake, + "cake": $fa-var-cake, + "envelope": $fa-var-envelope, + "angles-up": $fa-var-angles-up, + "angle-double-up": $fa-var-angle-double-up, + "paperclip": $fa-var-paperclip, + "arrow-right-to-city": $fa-var-arrow-right-to-city, + "ribbon": $fa-var-ribbon, + "lungs": $fa-var-lungs, + "arrow-up-9-1": $fa-var-arrow-up-9-1, + "sort-numeric-up-alt": $fa-var-sort-numeric-up-alt, + "litecoin-sign": $fa-var-litecoin-sign, + "border-none": $fa-var-border-none, + "circle-nodes": $fa-var-circle-nodes, + "parachute-box": $fa-var-parachute-box, + "indent": $fa-var-indent, + "truck-field-un": $fa-var-truck-field-un, + "hourglass": $fa-var-hourglass, + "hourglass-empty": $fa-var-hourglass-empty, + "mountain": $fa-var-mountain, + "user-doctor": $fa-var-user-doctor, + "user-md": $fa-var-user-md, + "circle-info": $fa-var-circle-info, + "info-circle": $fa-var-info-circle, + "cloud-meatball": $fa-var-cloud-meatball, + "camera": $fa-var-camera, + "camera-alt": $fa-var-camera-alt, + "square-virus": $fa-var-square-virus, + "meteor": $fa-var-meteor, + "car-on": $fa-var-car-on, + "sleigh": $fa-var-sleigh, + "arrow-down-1-9": $fa-var-arrow-down-1-9, + "sort-numeric-asc": $fa-var-sort-numeric-asc, + "sort-numeric-down": $fa-var-sort-numeric-down, + "hand-holding-droplet": $fa-var-hand-holding-droplet, + "hand-holding-water": $fa-var-hand-holding-water, + "water": $fa-var-water, + "calendar-check": $fa-var-calendar-check, + "braille": $fa-var-braille, + "prescription-bottle-medical": $fa-var-prescription-bottle-medical, + "prescription-bottle-alt": $fa-var-prescription-bottle-alt, + "landmark": $fa-var-landmark, + "truck": $fa-var-truck, + "crosshairs": $fa-var-crosshairs, + "person-cane": $fa-var-person-cane, + "tent": $fa-var-tent, + "vest-patches": $fa-var-vest-patches, + "check-double": $fa-var-check-double, + "arrow-down-a-z": $fa-var-arrow-down-a-z, + "sort-alpha-asc": $fa-var-sort-alpha-asc, + "sort-alpha-down": $fa-var-sort-alpha-down, + "money-bill-wheat": $fa-var-money-bill-wheat, + "cookie": $fa-var-cookie, + "arrow-rotate-left": $fa-var-arrow-rotate-left, + "arrow-left-rotate": $fa-var-arrow-left-rotate, + "arrow-rotate-back": $fa-var-arrow-rotate-back, + "arrow-rotate-backward": $fa-var-arrow-rotate-backward, + "undo": $fa-var-undo, + "hard-drive": $fa-var-hard-drive, + "hdd": $fa-var-hdd, + "face-grin-squint-tears": $fa-var-face-grin-squint-tears, + "grin-squint-tears": $fa-var-grin-squint-tears, + "dumbbell": $fa-var-dumbbell, + "rectangle-list": $fa-var-rectangle-list, + "list-alt": $fa-var-list-alt, + "tarp-droplet": $fa-var-tarp-droplet, + "house-medical-circle-check": $fa-var-house-medical-circle-check, + "person-skiing-nordic": $fa-var-person-skiing-nordic, + "skiing-nordic": $fa-var-skiing-nordic, + "calendar-plus": $fa-var-calendar-plus, + "plane-arrival": $fa-var-plane-arrival, + "circle-left": $fa-var-circle-left, + "arrow-alt-circle-left": $fa-var-arrow-alt-circle-left, + "train-subway": $fa-var-train-subway, + "subway": $fa-var-subway, + "chart-gantt": $fa-var-chart-gantt, + "indian-rupee-sign": $fa-var-indian-rupee-sign, + "indian-rupee": $fa-var-indian-rupee, + "inr": $fa-var-inr, + "crop-simple": $fa-var-crop-simple, + "crop-alt": $fa-var-crop-alt, + "money-bill-1": $fa-var-money-bill-1, + "money-bill-alt": $fa-var-money-bill-alt, + "left-long": $fa-var-left-long, + "long-arrow-alt-left": $fa-var-long-arrow-alt-left, + "dna": $fa-var-dna, + "virus-slash": $fa-var-virus-slash, + "minus": $fa-var-minus, + "subtract": $fa-var-subtract, + "chess": $fa-var-chess, + "arrow-left-long": $fa-var-arrow-left-long, + "long-arrow-left": $fa-var-long-arrow-left, + "plug-circle-check": $fa-var-plug-circle-check, + "street-view": $fa-var-street-view, + "franc-sign": $fa-var-franc-sign, + "volume-off": $fa-var-volume-off, + "hands-asl-interpreting": $fa-var-hands-asl-interpreting, + "american-sign-language-interpreting": $fa-var-american-sign-language-interpreting, + "asl-interpreting": $fa-var-asl-interpreting, + "hands-american-sign-language-interpreting": $fa-var-hands-american-sign-language-interpreting, + "gear": $fa-var-gear, + "cog": $fa-var-cog, + "droplet-slash": $fa-var-droplet-slash, + "tint-slash": $fa-var-tint-slash, + "mosque": $fa-var-mosque, + "mosquito": $fa-var-mosquito, + "star-of-david": $fa-var-star-of-david, + "person-military-rifle": $fa-var-person-military-rifle, + "cart-shopping": $fa-var-cart-shopping, + "shopping-cart": $fa-var-shopping-cart, + "vials": $fa-var-vials, + "plug-circle-plus": $fa-var-plug-circle-plus, + "place-of-worship": $fa-var-place-of-worship, + "grip-vertical": $fa-var-grip-vertical, + "hexagon-nodes": $fa-var-hexagon-nodes, + "arrow-turn-up": $fa-var-arrow-turn-up, + "level-up": $fa-var-level-up, + "u": $fa-var-u, + "square-root-variable": $fa-var-square-root-variable, + "square-root-alt": $fa-var-square-root-alt, + "clock": $fa-var-clock, + "clock-four": $fa-var-clock-four, + "backward-step": $fa-var-backward-step, + "step-backward": $fa-var-step-backward, + "pallet": $fa-var-pallet, + "faucet": $fa-var-faucet, + "baseball-bat-ball": $fa-var-baseball-bat-ball, + "s": $fa-var-s, + "timeline": $fa-var-timeline, + "keyboard": $fa-var-keyboard, + "caret-down": $fa-var-caret-down, + "house-chimney-medical": $fa-var-house-chimney-medical, + "clinic-medical": $fa-var-clinic-medical, + "temperature-three-quarters": $fa-var-temperature-three-quarters, + "temperature-3": $fa-var-temperature-3, + "thermometer-3": $fa-var-thermometer-3, + "thermometer-three-quarters": $fa-var-thermometer-three-quarters, + "mobile-screen": $fa-var-mobile-screen, + "mobile-android-alt": $fa-var-mobile-android-alt, + "plane-up": $fa-var-plane-up, + "piggy-bank": $fa-var-piggy-bank, + "battery-half": $fa-var-battery-half, + "battery-3": $fa-var-battery-3, + "mountain-city": $fa-var-mountain-city, + "coins": $fa-var-coins, + "khanda": $fa-var-khanda, + "sliders": $fa-var-sliders, + "sliders-h": $fa-var-sliders-h, + "folder-tree": $fa-var-folder-tree, + "network-wired": $fa-var-network-wired, + "map-pin": $fa-var-map-pin, + "hamsa": $fa-var-hamsa, + "cent-sign": $fa-var-cent-sign, + "flask": $fa-var-flask, + "person-pregnant": $fa-var-person-pregnant, + "wand-sparkles": $fa-var-wand-sparkles, + "ellipsis-vertical": $fa-var-ellipsis-vertical, + "ellipsis-v": $fa-var-ellipsis-v, + "ticket": $fa-var-ticket, + "power-off": $fa-var-power-off, + "right-long": $fa-var-right-long, + "long-arrow-alt-right": $fa-var-long-arrow-alt-right, + "flag-usa": $fa-var-flag-usa, + "laptop-file": $fa-var-laptop-file, + "tty": $fa-var-tty, + "teletype": $fa-var-teletype, + "diagram-next": $fa-var-diagram-next, + "person-rifle": $fa-var-person-rifle, + "house-medical-circle-exclamation": $fa-var-house-medical-circle-exclamation, + "closed-captioning": $fa-var-closed-captioning, + "person-hiking": $fa-var-person-hiking, + "hiking": $fa-var-hiking, + "venus-double": $fa-var-venus-double, + "images": $fa-var-images, + "calculator": $fa-var-calculator, + "people-pulling": $fa-var-people-pulling, + "n": $fa-var-n, + "cable-car": $fa-var-cable-car, + "tram": $fa-var-tram, + "cloud-rain": $fa-var-cloud-rain, + "building-circle-xmark": $fa-var-building-circle-xmark, + "ship": $fa-var-ship, + "arrows-down-to-line": $fa-var-arrows-down-to-line, + "download": $fa-var-download, + "face-grin": $fa-var-face-grin, + "grin": $fa-var-grin, + "delete-left": $fa-var-delete-left, + "backspace": $fa-var-backspace, + "eye-dropper": $fa-var-eye-dropper, + "eye-dropper-empty": $fa-var-eye-dropper-empty, + "eyedropper": $fa-var-eyedropper, + "file-circle-check": $fa-var-file-circle-check, + "forward": $fa-var-forward, + "mobile": $fa-var-mobile, + "mobile-android": $fa-var-mobile-android, + "mobile-phone": $fa-var-mobile-phone, + "face-meh": $fa-var-face-meh, + "meh": $fa-var-meh, + "align-center": $fa-var-align-center, + "book-skull": $fa-var-book-skull, + "book-dead": $fa-var-book-dead, + "id-card": $fa-var-id-card, + "drivers-license": $fa-var-drivers-license, + "outdent": $fa-var-outdent, + "dedent": $fa-var-dedent, + "heart-circle-exclamation": $fa-var-heart-circle-exclamation, + "house": $fa-var-house, + "home": $fa-var-home, + "home-alt": $fa-var-home-alt, + "home-lg-alt": $fa-var-home-lg-alt, + "calendar-week": $fa-var-calendar-week, + "laptop-medical": $fa-var-laptop-medical, + "b": $fa-var-b, + "file-medical": $fa-var-file-medical, + "dice-one": $fa-var-dice-one, + "kiwi-bird": $fa-var-kiwi-bird, + "arrow-right-arrow-left": $fa-var-arrow-right-arrow-left, + "exchange": $fa-var-exchange, + "rotate-right": $fa-var-rotate-right, + "redo-alt": $fa-var-redo-alt, + "rotate-forward": $fa-var-rotate-forward, + "utensils": $fa-var-utensils, + "cutlery": $fa-var-cutlery, + "arrow-up-wide-short": $fa-var-arrow-up-wide-short, + "sort-amount-up": $fa-var-sort-amount-up, + "mill-sign": $fa-var-mill-sign, + "bowl-rice": $fa-var-bowl-rice, + "skull": $fa-var-skull, + "tower-broadcast": $fa-var-tower-broadcast, + "broadcast-tower": $fa-var-broadcast-tower, + "truck-pickup": $fa-var-truck-pickup, + "up-long": $fa-var-up-long, + "long-arrow-alt-up": $fa-var-long-arrow-alt-up, + "stop": $fa-var-stop, + "code-merge": $fa-var-code-merge, + "upload": $fa-var-upload, + "hurricane": $fa-var-hurricane, + "mound": $fa-var-mound, + "toilet-portable": $fa-var-toilet-portable, + "compact-disc": $fa-var-compact-disc, + "file-arrow-down": $fa-var-file-arrow-down, + "file-download": $fa-var-file-download, + "caravan": $fa-var-caravan, + "shield-cat": $fa-var-shield-cat, + "bolt": $fa-var-bolt, + "zap": $fa-var-zap, + "glass-water": $fa-var-glass-water, + "oil-well": $fa-var-oil-well, + "vault": $fa-var-vault, + "mars": $fa-var-mars, + "toilet": $fa-var-toilet, + "plane-circle-xmark": $fa-var-plane-circle-xmark, + "yen-sign": $fa-var-yen-sign, + "cny": $fa-var-cny, + "jpy": $fa-var-jpy, + "rmb": $fa-var-rmb, + "yen": $fa-var-yen, + "ruble-sign": $fa-var-ruble-sign, + "rouble": $fa-var-rouble, + "rub": $fa-var-rub, + "ruble": $fa-var-ruble, + "sun": $fa-var-sun, + "guitar": $fa-var-guitar, + "face-laugh-wink": $fa-var-face-laugh-wink, + "laugh-wink": $fa-var-laugh-wink, + "horse-head": $fa-var-horse-head, + "bore-hole": $fa-var-bore-hole, + "industry": $fa-var-industry, + "circle-down": $fa-var-circle-down, + "arrow-alt-circle-down": $fa-var-arrow-alt-circle-down, + "arrows-turn-to-dots": $fa-var-arrows-turn-to-dots, + "florin-sign": $fa-var-florin-sign, + "arrow-down-short-wide": $fa-var-arrow-down-short-wide, + "sort-amount-desc": $fa-var-sort-amount-desc, + "sort-amount-down-alt": $fa-var-sort-amount-down-alt, + "less-than": $fa-var-less-than, + "angle-down": $fa-var-angle-down, + "car-tunnel": $fa-var-car-tunnel, + "head-side-cough": $fa-var-head-side-cough, + "grip-lines": $fa-var-grip-lines, + "thumbs-down": $fa-var-thumbs-down, + "user-lock": $fa-var-user-lock, + "arrow-right-long": $fa-var-arrow-right-long, + "long-arrow-right": $fa-var-long-arrow-right, + "anchor-circle-xmark": $fa-var-anchor-circle-xmark, + "ellipsis": $fa-var-ellipsis, + "ellipsis-h": $fa-var-ellipsis-h, + "chess-pawn": $fa-var-chess-pawn, + "kit-medical": $fa-var-kit-medical, + "first-aid": $fa-var-first-aid, + "person-through-window": $fa-var-person-through-window, + "toolbox": $fa-var-toolbox, + "hands-holding-circle": $fa-var-hands-holding-circle, + "bug": $fa-var-bug, + "credit-card": $fa-var-credit-card, + "credit-card-alt": $fa-var-credit-card-alt, + "car": $fa-var-car, + "automobile": $fa-var-automobile, + "hand-holding-hand": $fa-var-hand-holding-hand, + "book-open-reader": $fa-var-book-open-reader, + "book-reader": $fa-var-book-reader, + "mountain-sun": $fa-var-mountain-sun, + "arrows-left-right-to-line": $fa-var-arrows-left-right-to-line, + "dice-d20": $fa-var-dice-d20, + "truck-droplet": $fa-var-truck-droplet, + "file-circle-xmark": $fa-var-file-circle-xmark, + "temperature-arrow-up": $fa-var-temperature-arrow-up, + "temperature-up": $fa-var-temperature-up, + "medal": $fa-var-medal, + "bed": $fa-var-bed, + "square-h": $fa-var-square-h, + "h-square": $fa-var-h-square, + "podcast": $fa-var-podcast, + "temperature-full": $fa-var-temperature-full, + "temperature-4": $fa-var-temperature-4, + "thermometer-4": $fa-var-thermometer-4, + "thermometer-full": $fa-var-thermometer-full, + "bell": $fa-var-bell, + "superscript": $fa-var-superscript, + "plug-circle-xmark": $fa-var-plug-circle-xmark, + "star-of-life": $fa-var-star-of-life, + "phone-slash": $fa-var-phone-slash, + "paint-roller": $fa-var-paint-roller, + "handshake-angle": $fa-var-handshake-angle, + "hands-helping": $fa-var-hands-helping, + "location-dot": $fa-var-location-dot, + "map-marker-alt": $fa-var-map-marker-alt, + "file": $fa-var-file, + "greater-than": $fa-var-greater-than, + "person-swimming": $fa-var-person-swimming, + "swimmer": $fa-var-swimmer, + "arrow-down": $fa-var-arrow-down, + "droplet": $fa-var-droplet, + "tint": $fa-var-tint, + "eraser": $fa-var-eraser, + "earth-americas": $fa-var-earth-americas, + "earth": $fa-var-earth, + "earth-america": $fa-var-earth-america, + "globe-americas": $fa-var-globe-americas, + "person-burst": $fa-var-person-burst, + "dove": $fa-var-dove, + "battery-empty": $fa-var-battery-empty, + "battery-0": $fa-var-battery-0, + "socks": $fa-var-socks, + "inbox": $fa-var-inbox, + "section": $fa-var-section, + "gauge-high": $fa-var-gauge-high, + "tachometer-alt": $fa-var-tachometer-alt, + "tachometer-alt-fast": $fa-var-tachometer-alt-fast, + "envelope-open-text": $fa-var-envelope-open-text, + "hospital": $fa-var-hospital, + "hospital-alt": $fa-var-hospital-alt, + "hospital-wide": $fa-var-hospital-wide, + "wine-bottle": $fa-var-wine-bottle, + "chess-rook": $fa-var-chess-rook, + "bars-staggered": $fa-var-bars-staggered, + "reorder": $fa-var-reorder, + "stream": $fa-var-stream, + "dharmachakra": $fa-var-dharmachakra, + "hotdog": $fa-var-hotdog, + "person-walking-with-cane": $fa-var-person-walking-with-cane, + "blind": $fa-var-blind, + "drum": $fa-var-drum, + "ice-cream": $fa-var-ice-cream, + "heart-circle-bolt": $fa-var-heart-circle-bolt, + "fax": $fa-var-fax, + "paragraph": $fa-var-paragraph, + "check-to-slot": $fa-var-check-to-slot, + "vote-yea": $fa-var-vote-yea, + "star-half": $fa-var-star-half, + "boxes-stacked": $fa-var-boxes-stacked, + "boxes": $fa-var-boxes, + "boxes-alt": $fa-var-boxes-alt, + "link": $fa-var-link, + "chain": $fa-var-chain, + "ear-listen": $fa-var-ear-listen, + "assistive-listening-systems": $fa-var-assistive-listening-systems, + "tree-city": $fa-var-tree-city, + "play": $fa-var-play, + "font": $fa-var-font, + "table-cells-row-lock": $fa-var-table-cells-row-lock, + "rupiah-sign": $fa-var-rupiah-sign, + "magnifying-glass": $fa-var-magnifying-glass, + "search": $fa-var-search, + "table-tennis-paddle-ball": $fa-var-table-tennis-paddle-ball, + "ping-pong-paddle-ball": $fa-var-ping-pong-paddle-ball, + "table-tennis": $fa-var-table-tennis, + "person-dots-from-line": $fa-var-person-dots-from-line, + "diagnoses": $fa-var-diagnoses, + "trash-can-arrow-up": $fa-var-trash-can-arrow-up, + "trash-restore-alt": $fa-var-trash-restore-alt, + "naira-sign": $fa-var-naira-sign, + "cart-arrow-down": $fa-var-cart-arrow-down, + "walkie-talkie": $fa-var-walkie-talkie, + "file-pen": $fa-var-file-pen, + "file-edit": $fa-var-file-edit, + "receipt": $fa-var-receipt, + "square-pen": $fa-var-square-pen, + "pen-square": $fa-var-pen-square, + "pencil-square": $fa-var-pencil-square, + "suitcase-rolling": $fa-var-suitcase-rolling, + "person-circle-exclamation": $fa-var-person-circle-exclamation, + "chevron-down": $fa-var-chevron-down, + "battery-full": $fa-var-battery-full, + "battery": $fa-var-battery, + "battery-5": $fa-var-battery-5, + "skull-crossbones": $fa-var-skull-crossbones, + "code-compare": $fa-var-code-compare, + "list-ul": $fa-var-list-ul, + "list-dots": $fa-var-list-dots, + "school-lock": $fa-var-school-lock, + "tower-cell": $fa-var-tower-cell, + "down-long": $fa-var-down-long, + "long-arrow-alt-down": $fa-var-long-arrow-alt-down, + "ranking-star": $fa-var-ranking-star, + "chess-king": $fa-var-chess-king, + "person-harassing": $fa-var-person-harassing, + "brazilian-real-sign": $fa-var-brazilian-real-sign, + "landmark-dome": $fa-var-landmark-dome, + "landmark-alt": $fa-var-landmark-alt, + "arrow-up": $fa-var-arrow-up, + "tv": $fa-var-tv, + "television": $fa-var-television, + "tv-alt": $fa-var-tv-alt, + "shrimp": $fa-var-shrimp, + "list-check": $fa-var-list-check, + "tasks": $fa-var-tasks, + "jug-detergent": $fa-var-jug-detergent, + "circle-user": $fa-var-circle-user, + "user-circle": $fa-var-user-circle, + "user-shield": $fa-var-user-shield, + "wind": $fa-var-wind, + "car-burst": $fa-var-car-burst, + "car-crash": $fa-var-car-crash, + "y": $fa-var-y, + "person-snowboarding": $fa-var-person-snowboarding, + "snowboarding": $fa-var-snowboarding, + "truck-fast": $fa-var-truck-fast, + "shipping-fast": $fa-var-shipping-fast, + "fish": $fa-var-fish, + "user-graduate": $fa-var-user-graduate, + "circle-half-stroke": $fa-var-circle-half-stroke, + "adjust": $fa-var-adjust, + "clapperboard": $fa-var-clapperboard, + "circle-radiation": $fa-var-circle-radiation, + "radiation-alt": $fa-var-radiation-alt, + "baseball": $fa-var-baseball, + "baseball-ball": $fa-var-baseball-ball, + "jet-fighter-up": $fa-var-jet-fighter-up, + "diagram-project": $fa-var-diagram-project, + "project-diagram": $fa-var-project-diagram, + "copy": $fa-var-copy, + "volume-xmark": $fa-var-volume-xmark, + "volume-mute": $fa-var-volume-mute, + "volume-times": $fa-var-volume-times, + "hand-sparkles": $fa-var-hand-sparkles, + "grip": $fa-var-grip, + "grip-horizontal": $fa-var-grip-horizontal, + "share-from-square": $fa-var-share-from-square, + "share-square": $fa-var-share-square, + "child-combatant": $fa-var-child-combatant, + "child-rifle": $fa-var-child-rifle, + "gun": $fa-var-gun, + "square-phone": $fa-var-square-phone, + "phone-square": $fa-var-phone-square, + "plus": $fa-var-plus, + "add": $fa-var-add, + "expand": $fa-var-expand, + "computer": $fa-var-computer, + "xmark": $fa-var-xmark, + "close": $fa-var-close, + "multiply": $fa-var-multiply, + "remove": $fa-var-remove, + "times": $fa-var-times, + "arrows-up-down-left-right": $fa-var-arrows-up-down-left-right, + "arrows": $fa-var-arrows, + "chalkboard-user": $fa-var-chalkboard-user, + "chalkboard-teacher": $fa-var-chalkboard-teacher, + "peso-sign": $fa-var-peso-sign, + "building-shield": $fa-var-building-shield, + "baby": $fa-var-baby, + "users-line": $fa-var-users-line, + "quote-left": $fa-var-quote-left, + "quote-left-alt": $fa-var-quote-left-alt, + "tractor": $fa-var-tractor, + "trash-arrow-up": $fa-var-trash-arrow-up, + "trash-restore": $fa-var-trash-restore, + "arrow-down-up-lock": $fa-var-arrow-down-up-lock, + "lines-leaning": $fa-var-lines-leaning, + "ruler-combined": $fa-var-ruler-combined, + "copyright": $fa-var-copyright, + "equals": $fa-var-equals, + "blender": $fa-var-blender, + "teeth": $fa-var-teeth, + "shekel-sign": $fa-var-shekel-sign, + "ils": $fa-var-ils, + "shekel": $fa-var-shekel, + "sheqel": $fa-var-sheqel, + "sheqel-sign": $fa-var-sheqel-sign, + "map": $fa-var-map, + "rocket": $fa-var-rocket, + "photo-film": $fa-var-photo-film, + "photo-video": $fa-var-photo-video, + "folder-minus": $fa-var-folder-minus, + "hexagon-nodes-bolt": $fa-var-hexagon-nodes-bolt, + "store": $fa-var-store, + "arrow-trend-up": $fa-var-arrow-trend-up, + "plug-circle-minus": $fa-var-plug-circle-minus, + "sign-hanging": $fa-var-sign-hanging, + "sign": $fa-var-sign, + "bezier-curve": $fa-var-bezier-curve, + "bell-slash": $fa-var-bell-slash, + "tablet": $fa-var-tablet, + "tablet-android": $fa-var-tablet-android, + "school-flag": $fa-var-school-flag, + "fill": $fa-var-fill, + "angle-up": $fa-var-angle-up, + "drumstick-bite": $fa-var-drumstick-bite, + "holly-berry": $fa-var-holly-berry, + "chevron-left": $fa-var-chevron-left, + "bacteria": $fa-var-bacteria, + "hand-lizard": $fa-var-hand-lizard, + "notdef": $fa-var-notdef, + "disease": $fa-var-disease, + "briefcase-medical": $fa-var-briefcase-medical, + "genderless": $fa-var-genderless, + "chevron-right": $fa-var-chevron-right, + "retweet": $fa-var-retweet, + "car-rear": $fa-var-car-rear, + "car-alt": $fa-var-car-alt, + "pump-soap": $fa-var-pump-soap, + "video-slash": $fa-var-video-slash, + "battery-quarter": $fa-var-battery-quarter, + "battery-2": $fa-var-battery-2, + "radio": $fa-var-radio, + "baby-carriage": $fa-var-baby-carriage, + "carriage-baby": $fa-var-carriage-baby, + "traffic-light": $fa-var-traffic-light, + "thermometer": $fa-var-thermometer, + "vr-cardboard": $fa-var-vr-cardboard, + "hand-middle-finger": $fa-var-hand-middle-finger, + "percent": $fa-var-percent, + "percentage": $fa-var-percentage, + "truck-moving": $fa-var-truck-moving, + "glass-water-droplet": $fa-var-glass-water-droplet, + "display": $fa-var-display, + "face-smile": $fa-var-face-smile, + "smile": $fa-var-smile, + "thumbtack": $fa-var-thumbtack, + "thumb-tack": $fa-var-thumb-tack, + "trophy": $fa-var-trophy, + "person-praying": $fa-var-person-praying, + "pray": $fa-var-pray, + "hammer": $fa-var-hammer, + "hand-peace": $fa-var-hand-peace, + "rotate": $fa-var-rotate, + "sync-alt": $fa-var-sync-alt, + "spinner": $fa-var-spinner, + "robot": $fa-var-robot, + "peace": $fa-var-peace, + "gears": $fa-var-gears, + "cogs": $fa-var-cogs, + "warehouse": $fa-var-warehouse, + "arrow-up-right-dots": $fa-var-arrow-up-right-dots, + "splotch": $fa-var-splotch, + "face-grin-hearts": $fa-var-face-grin-hearts, + "grin-hearts": $fa-var-grin-hearts, + "dice-four": $fa-var-dice-four, + "sim-card": $fa-var-sim-card, + "transgender": $fa-var-transgender, + "transgender-alt": $fa-var-transgender-alt, + "mercury": $fa-var-mercury, + "arrow-turn-down": $fa-var-arrow-turn-down, + "level-down": $fa-var-level-down, + "person-falling-burst": $fa-var-person-falling-burst, + "award": $fa-var-award, + "ticket-simple": $fa-var-ticket-simple, + "ticket-alt": $fa-var-ticket-alt, + "building": $fa-var-building, + "angles-left": $fa-var-angles-left, + "angle-double-left": $fa-var-angle-double-left, + "qrcode": $fa-var-qrcode, + "clock-rotate-left": $fa-var-clock-rotate-left, + "history": $fa-var-history, + "face-grin-beam-sweat": $fa-var-face-grin-beam-sweat, + "grin-beam-sweat": $fa-var-grin-beam-sweat, + "file-export": $fa-var-file-export, + "arrow-right-from-file": $fa-var-arrow-right-from-file, + "shield": $fa-var-shield, + "shield-blank": $fa-var-shield-blank, + "arrow-up-short-wide": $fa-var-arrow-up-short-wide, + "sort-amount-up-alt": $fa-var-sort-amount-up-alt, + "comment-nodes": $fa-var-comment-nodes, + "house-medical": $fa-var-house-medical, + "golf-ball-tee": $fa-var-golf-ball-tee, + "golf-ball": $fa-var-golf-ball, + "circle-chevron-left": $fa-var-circle-chevron-left, + "chevron-circle-left": $fa-var-chevron-circle-left, + "house-chimney-window": $fa-var-house-chimney-window, + "pen-nib": $fa-var-pen-nib, + "tent-arrow-turn-left": $fa-var-tent-arrow-turn-left, + "tents": $fa-var-tents, + "wand-magic": $fa-var-wand-magic, + "magic": $fa-var-magic, + "dog": $fa-var-dog, + "carrot": $fa-var-carrot, + "moon": $fa-var-moon, + "wine-glass-empty": $fa-var-wine-glass-empty, + "wine-glass-alt": $fa-var-wine-glass-alt, + "cheese": $fa-var-cheese, + "yin-yang": $fa-var-yin-yang, + "music": $fa-var-music, + "code-commit": $fa-var-code-commit, + "temperature-low": $fa-var-temperature-low, + "person-biking": $fa-var-person-biking, + "biking": $fa-var-biking, + "broom": $fa-var-broom, + "shield-heart": $fa-var-shield-heart, + "gopuram": $fa-var-gopuram, + "earth-oceania": $fa-var-earth-oceania, + "globe-oceania": $fa-var-globe-oceania, + "square-xmark": $fa-var-square-xmark, + "times-square": $fa-var-times-square, + "xmark-square": $fa-var-xmark-square, + "hashtag": $fa-var-hashtag, + "up-right-and-down-left-from-center": $fa-var-up-right-and-down-left-from-center, + "expand-alt": $fa-var-expand-alt, + "oil-can": $fa-var-oil-can, + "t": $fa-var-t, + "hippo": $fa-var-hippo, + "chart-column": $fa-var-chart-column, + "infinity": $fa-var-infinity, + "vial-circle-check": $fa-var-vial-circle-check, + "person-arrow-down-to-line": $fa-var-person-arrow-down-to-line, + "voicemail": $fa-var-voicemail, + "fan": $fa-var-fan, + "person-walking-luggage": $fa-var-person-walking-luggage, + "up-down": $fa-var-up-down, + "arrows-alt-v": $fa-var-arrows-alt-v, + "cloud-moon-rain": $fa-var-cloud-moon-rain, + "calendar": $fa-var-calendar, + "trailer": $fa-var-trailer, + "bahai": $fa-var-bahai, + "haykal": $fa-var-haykal, + "sd-card": $fa-var-sd-card, + "dragon": $fa-var-dragon, + "shoe-prints": $fa-var-shoe-prints, + "circle-plus": $fa-var-circle-plus, + "plus-circle": $fa-var-plus-circle, + "face-grin-tongue-wink": $fa-var-face-grin-tongue-wink, + "grin-tongue-wink": $fa-var-grin-tongue-wink, + "hand-holding": $fa-var-hand-holding, + "plug-circle-exclamation": $fa-var-plug-circle-exclamation, + "link-slash": $fa-var-link-slash, + "chain-broken": $fa-var-chain-broken, + "chain-slash": $fa-var-chain-slash, + "unlink": $fa-var-unlink, + "clone": $fa-var-clone, + "person-walking-arrow-loop-left": $fa-var-person-walking-arrow-loop-left, + "arrow-up-z-a": $fa-var-arrow-up-z-a, + "sort-alpha-up-alt": $fa-var-sort-alpha-up-alt, + "fire-flame-curved": $fa-var-fire-flame-curved, + "fire-alt": $fa-var-fire-alt, + "tornado": $fa-var-tornado, + "file-circle-plus": $fa-var-file-circle-plus, + "book-quran": $fa-var-book-quran, + "quran": $fa-var-quran, + "anchor": $fa-var-anchor, + "border-all": $fa-var-border-all, + "face-angry": $fa-var-face-angry, + "angry": $fa-var-angry, + "cookie-bite": $fa-var-cookie-bite, + "arrow-trend-down": $fa-var-arrow-trend-down, + "rss": $fa-var-rss, + "feed": $fa-var-feed, + "draw-polygon": $fa-var-draw-polygon, + "scale-balanced": $fa-var-scale-balanced, + "balance-scale": $fa-var-balance-scale, + "gauge-simple-high": $fa-var-gauge-simple-high, + "tachometer": $fa-var-tachometer, + "tachometer-fast": $fa-var-tachometer-fast, + "shower": $fa-var-shower, + "desktop": $fa-var-desktop, + "desktop-alt": $fa-var-desktop-alt, + "m": $fa-var-m, + "table-list": $fa-var-table-list, + "th-list": $fa-var-th-list, + "comment-sms": $fa-var-comment-sms, + "sms": $fa-var-sms, + "book": $fa-var-book, + "user-plus": $fa-var-user-plus, + "check": $fa-var-check, + "battery-three-quarters": $fa-var-battery-three-quarters, + "battery-4": $fa-var-battery-4, + "house-circle-check": $fa-var-house-circle-check, + "angle-left": $fa-var-angle-left, + "diagram-successor": $fa-var-diagram-successor, + "truck-arrow-right": $fa-var-truck-arrow-right, + "arrows-split-up-and-left": $fa-var-arrows-split-up-and-left, + "hand-fist": $fa-var-hand-fist, + "fist-raised": $fa-var-fist-raised, + "cloud-moon": $fa-var-cloud-moon, + "briefcase": $fa-var-briefcase, + "person-falling": $fa-var-person-falling, + "image-portrait": $fa-var-image-portrait, + "portrait": $fa-var-portrait, + "user-tag": $fa-var-user-tag, + "rug": $fa-var-rug, + "earth-europe": $fa-var-earth-europe, + "globe-europe": $fa-var-globe-europe, + "cart-flatbed-suitcase": $fa-var-cart-flatbed-suitcase, + "luggage-cart": $fa-var-luggage-cart, + "rectangle-xmark": $fa-var-rectangle-xmark, + "rectangle-times": $fa-var-rectangle-times, + "times-rectangle": $fa-var-times-rectangle, + "window-close": $fa-var-window-close, + "baht-sign": $fa-var-baht-sign, + "book-open": $fa-var-book-open, + "book-journal-whills": $fa-var-book-journal-whills, + "journal-whills": $fa-var-journal-whills, + "handcuffs": $fa-var-handcuffs, + "triangle-exclamation": $fa-var-triangle-exclamation, + "exclamation-triangle": $fa-var-exclamation-triangle, + "warning": $fa-var-warning, + "database": $fa-var-database, + "share": $fa-var-share, + "mail-forward": $fa-var-mail-forward, + "bottle-droplet": $fa-var-bottle-droplet, + "mask-face": $fa-var-mask-face, + "hill-rockslide": $fa-var-hill-rockslide, + "right-left": $fa-var-right-left, + "exchange-alt": $fa-var-exchange-alt, + "paper-plane": $fa-var-paper-plane, + "road-circle-exclamation": $fa-var-road-circle-exclamation, + "dungeon": $fa-var-dungeon, + "align-right": $fa-var-align-right, + "money-bill-1-wave": $fa-var-money-bill-1-wave, + "money-bill-wave-alt": $fa-var-money-bill-wave-alt, + "life-ring": $fa-var-life-ring, + "hands": $fa-var-hands, + "sign-language": $fa-var-sign-language, + "signing": $fa-var-signing, + "calendar-day": $fa-var-calendar-day, + "water-ladder": $fa-var-water-ladder, + "ladder-water": $fa-var-ladder-water, + "swimming-pool": $fa-var-swimming-pool, + "arrows-up-down": $fa-var-arrows-up-down, + "arrows-v": $fa-var-arrows-v, + "face-grimace": $fa-var-face-grimace, + "grimace": $fa-var-grimace, + "wheelchair-move": $fa-var-wheelchair-move, + "wheelchair-alt": $fa-var-wheelchair-alt, + "turn-down": $fa-var-turn-down, + "level-down-alt": $fa-var-level-down-alt, + "person-walking-arrow-right": $fa-var-person-walking-arrow-right, + "square-envelope": $fa-var-square-envelope, + "envelope-square": $fa-var-envelope-square, + "dice": $fa-var-dice, + "bowling-ball": $fa-var-bowling-ball, + "brain": $fa-var-brain, + "bandage": $fa-var-bandage, + "band-aid": $fa-var-band-aid, + "calendar-minus": $fa-var-calendar-minus, + "circle-xmark": $fa-var-circle-xmark, + "times-circle": $fa-var-times-circle, + "xmark-circle": $fa-var-xmark-circle, + "gifts": $fa-var-gifts, + "hotel": $fa-var-hotel, + "earth-asia": $fa-var-earth-asia, + "globe-asia": $fa-var-globe-asia, + "id-card-clip": $fa-var-id-card-clip, + "id-card-alt": $fa-var-id-card-alt, + "magnifying-glass-plus": $fa-var-magnifying-glass-plus, + "search-plus": $fa-var-search-plus, + "thumbs-up": $fa-var-thumbs-up, + "user-clock": $fa-var-user-clock, + "hand-dots": $fa-var-hand-dots, + "allergies": $fa-var-allergies, + "file-invoice": $fa-var-file-invoice, + "window-minimize": $fa-var-window-minimize, + "mug-saucer": $fa-var-mug-saucer, + "coffee": $fa-var-coffee, + "brush": $fa-var-brush, + "file-half-dashed": $fa-var-file-half-dashed, + "mask": $fa-var-mask, + "magnifying-glass-minus": $fa-var-magnifying-glass-minus, + "search-minus": $fa-var-search-minus, + "ruler-vertical": $fa-var-ruler-vertical, + "user-large": $fa-var-user-large, + "user-alt": $fa-var-user-alt, + "train-tram": $fa-var-train-tram, + "user-nurse": $fa-var-user-nurse, + "syringe": $fa-var-syringe, + "cloud-sun": $fa-var-cloud-sun, + "stopwatch-20": $fa-var-stopwatch-20, + "square-full": $fa-var-square-full, + "magnet": $fa-var-magnet, + "jar": $fa-var-jar, + "note-sticky": $fa-var-note-sticky, + "sticky-note": $fa-var-sticky-note, + "bug-slash": $fa-var-bug-slash, + "arrow-up-from-water-pump": $fa-var-arrow-up-from-water-pump, + "bone": $fa-var-bone, + "table-cells-row-unlock": $fa-var-table-cells-row-unlock, + "user-injured": $fa-var-user-injured, + "face-sad-tear": $fa-var-face-sad-tear, + "sad-tear": $fa-var-sad-tear, + "plane": $fa-var-plane, + "tent-arrows-down": $fa-var-tent-arrows-down, + "exclamation": $fa-var-exclamation, + "arrows-spin": $fa-var-arrows-spin, + "print": $fa-var-print, + "turkish-lira-sign": $fa-var-turkish-lira-sign, + "try": $fa-var-try, + "turkish-lira": $fa-var-turkish-lira, + "dollar-sign": $fa-var-dollar-sign, + "dollar": $fa-var-dollar, + "usd": $fa-var-usd, + "x": $fa-var-x, + "magnifying-glass-dollar": $fa-var-magnifying-glass-dollar, + "search-dollar": $fa-var-search-dollar, + "users-gear": $fa-var-users-gear, + "users-cog": $fa-var-users-cog, + "person-military-pointing": $fa-var-person-military-pointing, + "building-columns": $fa-var-building-columns, + "bank": $fa-var-bank, + "institution": $fa-var-institution, + "museum": $fa-var-museum, + "university": $fa-var-university, + "umbrella": $fa-var-umbrella, + "trowel": $fa-var-trowel, + "d": $fa-var-d, + "stapler": $fa-var-stapler, + "masks-theater": $fa-var-masks-theater, + "theater-masks": $fa-var-theater-masks, + "kip-sign": $fa-var-kip-sign, + "hand-point-left": $fa-var-hand-point-left, + "handshake-simple": $fa-var-handshake-simple, + "handshake-alt": $fa-var-handshake-alt, + "jet-fighter": $fa-var-jet-fighter, + "fighter-jet": $fa-var-fighter-jet, + "square-share-nodes": $fa-var-square-share-nodes, + "share-alt-square": $fa-var-share-alt-square, + "barcode": $fa-var-barcode, + "plus-minus": $fa-var-plus-minus, + "video": $fa-var-video, + "video-camera": $fa-var-video-camera, + "graduation-cap": $fa-var-graduation-cap, + "mortar-board": $fa-var-mortar-board, + "hand-holding-medical": $fa-var-hand-holding-medical, + "person-circle-check": $fa-var-person-circle-check, + "turn-up": $fa-var-turn-up, + "level-up-alt": $fa-var-level-up-alt, +); + +$fa-brand-icons: ( + "monero": $fa-var-monero, + "hooli": $fa-var-hooli, + "yelp": $fa-var-yelp, + "cc-visa": $fa-var-cc-visa, + "lastfm": $fa-var-lastfm, + "shopware": $fa-var-shopware, + "creative-commons-nc": $fa-var-creative-commons-nc, + "aws": $fa-var-aws, + "redhat": $fa-var-redhat, + "yoast": $fa-var-yoast, + "cloudflare": $fa-var-cloudflare, + "ups": $fa-var-ups, + "pixiv": $fa-var-pixiv, + "wpexplorer": $fa-var-wpexplorer, + "dyalog": $fa-var-dyalog, + "bity": $fa-var-bity, + "stackpath": $fa-var-stackpath, + "buysellads": $fa-var-buysellads, + "first-order": $fa-var-first-order, + "modx": $fa-var-modx, + "guilded": $fa-var-guilded, + "vnv": $fa-var-vnv, + "square-js": $fa-var-square-js, + "js-square": $fa-var-js-square, + "microsoft": $fa-var-microsoft, + "qq": $fa-var-qq, + "orcid": $fa-var-orcid, + "java": $fa-var-java, + "invision": $fa-var-invision, + "creative-commons-pd-alt": $fa-var-creative-commons-pd-alt, + "centercode": $fa-var-centercode, + "glide-g": $fa-var-glide-g, + "drupal": $fa-var-drupal, + "jxl": $fa-var-jxl, + "dart-lang": $fa-var-dart-lang, + "hire-a-helper": $fa-var-hire-a-helper, + "creative-commons-by": $fa-var-creative-commons-by, + "unity": $fa-var-unity, + "whmcs": $fa-var-whmcs, + "rocketchat": $fa-var-rocketchat, + "vk": $fa-var-vk, + "untappd": $fa-var-untappd, + "mailchimp": $fa-var-mailchimp, + "css3-alt": $fa-var-css3-alt, + "square-reddit": $fa-var-square-reddit, + "reddit-square": $fa-var-reddit-square, + "vimeo-v": $fa-var-vimeo-v, + "contao": $fa-var-contao, + "square-font-awesome": $fa-var-square-font-awesome, + "deskpro": $fa-var-deskpro, + "brave": $fa-var-brave, + "sistrix": $fa-var-sistrix, + "square-instagram": $fa-var-square-instagram, + "instagram-square": $fa-var-instagram-square, + "battle-net": $fa-var-battle-net, + "the-red-yeti": $fa-var-the-red-yeti, + "square-hacker-news": $fa-var-square-hacker-news, + "hacker-news-square": $fa-var-hacker-news-square, + "edge": $fa-var-edge, + "threads": $fa-var-threads, + "napster": $fa-var-napster, + "square-snapchat": $fa-var-square-snapchat, + "snapchat-square": $fa-var-snapchat-square, + "google-plus-g": $fa-var-google-plus-g, + "artstation": $fa-var-artstation, + "markdown": $fa-var-markdown, + "sourcetree": $fa-var-sourcetree, + "google-plus": $fa-var-google-plus, + "diaspora": $fa-var-diaspora, + "foursquare": $fa-var-foursquare, + "stack-overflow": $fa-var-stack-overflow, + "github-alt": $fa-var-github-alt, + "phoenix-squadron": $fa-var-phoenix-squadron, + "pagelines": $fa-var-pagelines, + "algolia": $fa-var-algolia, + "red-river": $fa-var-red-river, + "creative-commons-sa": $fa-var-creative-commons-sa, + "safari": $fa-var-safari, + "google": $fa-var-google, + "square-font-awesome-stroke": $fa-var-square-font-awesome-stroke, + "font-awesome-alt": $fa-var-font-awesome-alt, + "atlassian": $fa-var-atlassian, + "linkedin-in": $fa-var-linkedin-in, + "digital-ocean": $fa-var-digital-ocean, + "nimblr": $fa-var-nimblr, + "chromecast": $fa-var-chromecast, + "evernote": $fa-var-evernote, + "hacker-news": $fa-var-hacker-news, + "creative-commons-sampling": $fa-var-creative-commons-sampling, + "adversal": $fa-var-adversal, + "creative-commons": $fa-var-creative-commons, + "watchman-monitoring": $fa-var-watchman-monitoring, + "fonticons": $fa-var-fonticons, + "weixin": $fa-var-weixin, + "shirtsinbulk": $fa-var-shirtsinbulk, + "codepen": $fa-var-codepen, + "git-alt": $fa-var-git-alt, + "lyft": $fa-var-lyft, + "rev": $fa-var-rev, + "windows": $fa-var-windows, + "wizards-of-the-coast": $fa-var-wizards-of-the-coast, + "square-viadeo": $fa-var-square-viadeo, + "viadeo-square": $fa-var-viadeo-square, + "meetup": $fa-var-meetup, + "centos": $fa-var-centos, + "adn": $fa-var-adn, + "cloudsmith": $fa-var-cloudsmith, + "opensuse": $fa-var-opensuse, + "pied-piper-alt": $fa-var-pied-piper-alt, + "square-dribbble": $fa-var-square-dribbble, + "dribbble-square": $fa-var-dribbble-square, + "codiepie": $fa-var-codiepie, + "node": $fa-var-node, + "mix": $fa-var-mix, + "steam": $fa-var-steam, + "cc-apple-pay": $fa-var-cc-apple-pay, + "scribd": $fa-var-scribd, + "debian": $fa-var-debian, + "openid": $fa-var-openid, + "instalod": $fa-var-instalod, + "files-pinwheel": $fa-var-files-pinwheel, + "expeditedssl": $fa-var-expeditedssl, + "sellcast": $fa-var-sellcast, + "square-twitter": $fa-var-square-twitter, + "twitter-square": $fa-var-twitter-square, + "r-project": $fa-var-r-project, + "delicious": $fa-var-delicious, + "freebsd": $fa-var-freebsd, + "vuejs": $fa-var-vuejs, + "accusoft": $fa-var-accusoft, + "ioxhost": $fa-var-ioxhost, + "fonticons-fi": $fa-var-fonticons-fi, + "app-store": $fa-var-app-store, + "cc-mastercard": $fa-var-cc-mastercard, + "itunes-note": $fa-var-itunes-note, + "golang": $fa-var-golang, + "kickstarter": $fa-var-kickstarter, + "square-kickstarter": $fa-var-square-kickstarter, + "grav": $fa-var-grav, + "weibo": $fa-var-weibo, + "uncharted": $fa-var-uncharted, + "firstdraft": $fa-var-firstdraft, + "square-youtube": $fa-var-square-youtube, + "youtube-square": $fa-var-youtube-square, + "wikipedia-w": $fa-var-wikipedia-w, + "wpressr": $fa-var-wpressr, + "rendact": $fa-var-rendact, + "angellist": $fa-var-angellist, + "galactic-republic": $fa-var-galactic-republic, + "nfc-directional": $fa-var-nfc-directional, + "skype": $fa-var-skype, + "joget": $fa-var-joget, + "fedora": $fa-var-fedora, + "stripe-s": $fa-var-stripe-s, + "meta": $fa-var-meta, + "laravel": $fa-var-laravel, + "hotjar": $fa-var-hotjar, + "bluetooth-b": $fa-var-bluetooth-b, + "square-letterboxd": $fa-var-square-letterboxd, + "sticker-mule": $fa-var-sticker-mule, + "creative-commons-zero": $fa-var-creative-commons-zero, + "hips": $fa-var-hips, + "css": $fa-var-css, + "behance": $fa-var-behance, + "reddit": $fa-var-reddit, + "discord": $fa-var-discord, + "chrome": $fa-var-chrome, + "app-store-ios": $fa-var-app-store-ios, + "cc-discover": $fa-var-cc-discover, + "wpbeginner": $fa-var-wpbeginner, + "confluence": $fa-var-confluence, + "shoelace": $fa-var-shoelace, + "mdb": $fa-var-mdb, + "dochub": $fa-var-dochub, + "accessible-icon": $fa-var-accessible-icon, + "ebay": $fa-var-ebay, + "amazon": $fa-var-amazon, + "unsplash": $fa-var-unsplash, + "yarn": $fa-var-yarn, + "square-steam": $fa-var-square-steam, + "steam-square": $fa-var-steam-square, + "500px": $fa-var-500px, + "square-vimeo": $fa-var-square-vimeo, + "vimeo-square": $fa-var-vimeo-square, + "asymmetrik": $fa-var-asymmetrik, + "font-awesome": $fa-var-font-awesome, + "font-awesome-flag": $fa-var-font-awesome-flag, + "font-awesome-logo-full": $fa-var-font-awesome-logo-full, + "gratipay": $fa-var-gratipay, + "apple": $fa-var-apple, + "hive": $fa-var-hive, + "gitkraken": $fa-var-gitkraken, + "keybase": $fa-var-keybase, + "apple-pay": $fa-var-apple-pay, + "padlet": $fa-var-padlet, + "amazon-pay": $fa-var-amazon-pay, + "square-github": $fa-var-square-github, + "github-square": $fa-var-github-square, + "stumbleupon": $fa-var-stumbleupon, + "fedex": $fa-var-fedex, + "phoenix-framework": $fa-var-phoenix-framework, + "shopify": $fa-var-shopify, + "neos": $fa-var-neos, + "square-threads": $fa-var-square-threads, + "hackerrank": $fa-var-hackerrank, + "researchgate": $fa-var-researchgate, + "swift": $fa-var-swift, + "angular": $fa-var-angular, + "speakap": $fa-var-speakap, + "angrycreative": $fa-var-angrycreative, + "y-combinator": $fa-var-y-combinator, + "empire": $fa-var-empire, + "envira": $fa-var-envira, + "google-scholar": $fa-var-google-scholar, + "square-gitlab": $fa-var-square-gitlab, + "gitlab-square": $fa-var-gitlab-square, + "studiovinari": $fa-var-studiovinari, + "pied-piper": $fa-var-pied-piper, + "wordpress": $fa-var-wordpress, + "product-hunt": $fa-var-product-hunt, + "firefox": $fa-var-firefox, + "linode": $fa-var-linode, + "goodreads": $fa-var-goodreads, + "square-odnoklassniki": $fa-var-square-odnoklassniki, + "odnoklassniki-square": $fa-var-odnoklassniki-square, + "jsfiddle": $fa-var-jsfiddle, + "sith": $fa-var-sith, + "themeisle": $fa-var-themeisle, + "page4": $fa-var-page4, + "hashnode": $fa-var-hashnode, + "react": $fa-var-react, + "cc-paypal": $fa-var-cc-paypal, + "squarespace": $fa-var-squarespace, + "cc-stripe": $fa-var-cc-stripe, + "creative-commons-share": $fa-var-creative-commons-share, + "bitcoin": $fa-var-bitcoin, + "keycdn": $fa-var-keycdn, + "opera": $fa-var-opera, + "itch-io": $fa-var-itch-io, + "umbraco": $fa-var-umbraco, + "galactic-senate": $fa-var-galactic-senate, + "ubuntu": $fa-var-ubuntu, + "draft2digital": $fa-var-draft2digital, + "stripe": $fa-var-stripe, + "houzz": $fa-var-houzz, + "gg": $fa-var-gg, + "dhl": $fa-var-dhl, + "square-pinterest": $fa-var-square-pinterest, + "pinterest-square": $fa-var-pinterest-square, + "xing": $fa-var-xing, + "blackberry": $fa-var-blackberry, + "creative-commons-pd": $fa-var-creative-commons-pd, + "playstation": $fa-var-playstation, + "quinscape": $fa-var-quinscape, + "less": $fa-var-less, + "blogger-b": $fa-var-blogger-b, + "opencart": $fa-var-opencart, + "vine": $fa-var-vine, + "signal-messenger": $fa-var-signal-messenger, + "paypal": $fa-var-paypal, + "gitlab": $fa-var-gitlab, + "typo3": $fa-var-typo3, + "reddit-alien": $fa-var-reddit-alien, + "yahoo": $fa-var-yahoo, + "dailymotion": $fa-var-dailymotion, + "affiliatetheme": $fa-var-affiliatetheme, + "pied-piper-pp": $fa-var-pied-piper-pp, + "bootstrap": $fa-var-bootstrap, + "odnoklassniki": $fa-var-odnoklassniki, + "nfc-symbol": $fa-var-nfc-symbol, + "mintbit": $fa-var-mintbit, + "ethereum": $fa-var-ethereum, + "speaker-deck": $fa-var-speaker-deck, + "creative-commons-nc-eu": $fa-var-creative-commons-nc-eu, + "patreon": $fa-var-patreon, + "avianex": $fa-var-avianex, + "ello": $fa-var-ello, + "gofore": $fa-var-gofore, + "bimobject": $fa-var-bimobject, + "brave-reverse": $fa-var-brave-reverse, + "facebook-f": $fa-var-facebook-f, + "square-google-plus": $fa-var-square-google-plus, + "google-plus-square": $fa-var-google-plus-square, + "web-awesome": $fa-var-web-awesome, + "mandalorian": $fa-var-mandalorian, + "first-order-alt": $fa-var-first-order-alt, + "osi": $fa-var-osi, + "google-wallet": $fa-var-google-wallet, + "d-and-d-beyond": $fa-var-d-and-d-beyond, + "periscope": $fa-var-periscope, + "fulcrum": $fa-var-fulcrum, + "cloudscale": $fa-var-cloudscale, + "forumbee": $fa-var-forumbee, + "mizuni": $fa-var-mizuni, + "schlix": $fa-var-schlix, + "square-xing": $fa-var-square-xing, + "xing-square": $fa-var-xing-square, + "bandcamp": $fa-var-bandcamp, + "wpforms": $fa-var-wpforms, + "cloudversify": $fa-var-cloudversify, + "usps": $fa-var-usps, + "megaport": $fa-var-megaport, + "magento": $fa-var-magento, + "spotify": $fa-var-spotify, + "optin-monster": $fa-var-optin-monster, + "fly": $fa-var-fly, + "square-bluesky": $fa-var-square-bluesky, + "aviato": $fa-var-aviato, + "itunes": $fa-var-itunes, + "cuttlefish": $fa-var-cuttlefish, + "blogger": $fa-var-blogger, + "flickr": $fa-var-flickr, + "viber": $fa-var-viber, + "soundcloud": $fa-var-soundcloud, + "digg": $fa-var-digg, + "tencent-weibo": $fa-var-tencent-weibo, + "letterboxd": $fa-var-letterboxd, + "symfony": $fa-var-symfony, + "maxcdn": $fa-var-maxcdn, + "etsy": $fa-var-etsy, + "facebook-messenger": $fa-var-facebook-messenger, + "audible": $fa-var-audible, + "think-peaks": $fa-var-think-peaks, + "bilibili": $fa-var-bilibili, + "erlang": $fa-var-erlang, + "x-twitter": $fa-var-x-twitter, + "cotton-bureau": $fa-var-cotton-bureau, + "dashcube": $fa-var-dashcube, + "42-group": $fa-var-42-group, + "innosoft": $fa-var-innosoft, + "stack-exchange": $fa-var-stack-exchange, + "elementor": $fa-var-elementor, + "square-pied-piper": $fa-var-square-pied-piper, + "pied-piper-square": $fa-var-pied-piper-square, + "creative-commons-nd": $fa-var-creative-commons-nd, + "palfed": $fa-var-palfed, + "superpowers": $fa-var-superpowers, + "resolving": $fa-var-resolving, + "xbox": $fa-var-xbox, + "square-web-awesome-stroke": $fa-var-square-web-awesome-stroke, + "searchengin": $fa-var-searchengin, + "tiktok": $fa-var-tiktok, + "square-facebook": $fa-var-square-facebook, + "facebook-square": $fa-var-facebook-square, + "renren": $fa-var-renren, + "linux": $fa-var-linux, + "glide": $fa-var-glide, + "linkedin": $fa-var-linkedin, + "hubspot": $fa-var-hubspot, + "deploydog": $fa-var-deploydog, + "twitch": $fa-var-twitch, + "flutter": $fa-var-flutter, + "ravelry": $fa-var-ravelry, + "mixer": $fa-var-mixer, + "square-lastfm": $fa-var-square-lastfm, + "lastfm-square": $fa-var-lastfm-square, + "vimeo": $fa-var-vimeo, + "mendeley": $fa-var-mendeley, + "uniregistry": $fa-var-uniregistry, + "figma": $fa-var-figma, + "creative-commons-remix": $fa-var-creative-commons-remix, + "cc-amazon-pay": $fa-var-cc-amazon-pay, + "dropbox": $fa-var-dropbox, + "instagram": $fa-var-instagram, + "cmplid": $fa-var-cmplid, + "upwork": $fa-var-upwork, + "facebook": $fa-var-facebook, + "gripfire": $fa-var-gripfire, + "jedi-order": $fa-var-jedi-order, + "uikit": $fa-var-uikit, + "fort-awesome-alt": $fa-var-fort-awesome-alt, + "phabricator": $fa-var-phabricator, + "ussunnah": $fa-var-ussunnah, + "earlybirds": $fa-var-earlybirds, + "trade-federation": $fa-var-trade-federation, + "autoprefixer": $fa-var-autoprefixer, + "whatsapp": $fa-var-whatsapp, + "square-upwork": $fa-var-square-upwork, + "slideshare": $fa-var-slideshare, + "google-play": $fa-var-google-play, + "viadeo": $fa-var-viadeo, + "line": $fa-var-line, + "google-drive": $fa-var-google-drive, + "servicestack": $fa-var-servicestack, + "simplybuilt": $fa-var-simplybuilt, + "bitbucket": $fa-var-bitbucket, + "imdb": $fa-var-imdb, + "deezer": $fa-var-deezer, + "raspberry-pi": $fa-var-raspberry-pi, + "jira": $fa-var-jira, + "docker": $fa-var-docker, + "screenpal": $fa-var-screenpal, + "bluetooth": $fa-var-bluetooth, + "gitter": $fa-var-gitter, + "d-and-d": $fa-var-d-and-d, + "microblog": $fa-var-microblog, + "cc-diners-club": $fa-var-cc-diners-club, + "gg-circle": $fa-var-gg-circle, + "pied-piper-hat": $fa-var-pied-piper-hat, + "kickstarter-k": $fa-var-kickstarter-k, + "yandex": $fa-var-yandex, + "readme": $fa-var-readme, + "html5": $fa-var-html5, + "sellsy": $fa-var-sellsy, + "square-web-awesome": $fa-var-square-web-awesome, + "sass": $fa-var-sass, + "wirsindhandwerk": $fa-var-wirsindhandwerk, + "wsh": $fa-var-wsh, + "buromobelexperte": $fa-var-buromobelexperte, + "salesforce": $fa-var-salesforce, + "octopus-deploy": $fa-var-octopus-deploy, + "medapps": $fa-var-medapps, + "ns8": $fa-var-ns8, + "pinterest-p": $fa-var-pinterest-p, + "apper": $fa-var-apper, + "fort-awesome": $fa-var-fort-awesome, + "waze": $fa-var-waze, + "bluesky": $fa-var-bluesky, + "cc-jcb": $fa-var-cc-jcb, + "snapchat": $fa-var-snapchat, + "snapchat-ghost": $fa-var-snapchat-ghost, + "fantasy-flight-games": $fa-var-fantasy-flight-games, + "rust": $fa-var-rust, + "wix": $fa-var-wix, + "square-behance": $fa-var-square-behance, + "behance-square": $fa-var-behance-square, + "supple": $fa-var-supple, + "webflow": $fa-var-webflow, + "rebel": $fa-var-rebel, + "css3": $fa-var-css3, + "staylinked": $fa-var-staylinked, + "kaggle": $fa-var-kaggle, + "space-awesome": $fa-var-space-awesome, + "deviantart": $fa-var-deviantart, + "cpanel": $fa-var-cpanel, + "goodreads-g": $fa-var-goodreads-g, + "square-git": $fa-var-square-git, + "git-square": $fa-var-git-square, + "square-tumblr": $fa-var-square-tumblr, + "tumblr-square": $fa-var-tumblr-square, + "trello": $fa-var-trello, + "creative-commons-nc-jp": $fa-var-creative-commons-nc-jp, + "get-pocket": $fa-var-get-pocket, + "perbyte": $fa-var-perbyte, + "grunt": $fa-var-grunt, + "weebly": $fa-var-weebly, + "connectdevelop": $fa-var-connectdevelop, + "leanpub": $fa-var-leanpub, + "black-tie": $fa-var-black-tie, + "themeco": $fa-var-themeco, + "python": $fa-var-python, + "android": $fa-var-android, + "bots": $fa-var-bots, + "free-code-camp": $fa-var-free-code-camp, + "hornbill": $fa-var-hornbill, + "js": $fa-var-js, + "ideal": $fa-var-ideal, + "git": $fa-var-git, + "dev": $fa-var-dev, + "sketch": $fa-var-sketch, + "yandex-international": $fa-var-yandex-international, + "cc-amex": $fa-var-cc-amex, + "uber": $fa-var-uber, + "github": $fa-var-github, + "php": $fa-var-php, + "alipay": $fa-var-alipay, + "youtube": $fa-var-youtube, + "skyatlas": $fa-var-skyatlas, + "firefox-browser": $fa-var-firefox-browser, + "replyd": $fa-var-replyd, + "suse": $fa-var-suse, + "jenkins": $fa-var-jenkins, + "twitter": $fa-var-twitter, + "rockrms": $fa-var-rockrms, + "pinterest": $fa-var-pinterest, + "buffer": $fa-var-buffer, + "npm": $fa-var-npm, + "yammer": $fa-var-yammer, + "btc": $fa-var-btc, + "dribbble": $fa-var-dribbble, + "stumbleupon-circle": $fa-var-stumbleupon-circle, + "internet-explorer": $fa-var-internet-explorer, + "stubber": $fa-var-stubber, + "telegram": $fa-var-telegram, + "telegram-plane": $fa-var-telegram-plane, + "old-republic": $fa-var-old-republic, + "odysee": $fa-var-odysee, + "square-whatsapp": $fa-var-square-whatsapp, + "whatsapp-square": $fa-var-whatsapp-square, + "node-js": $fa-var-node-js, + "edge-legacy": $fa-var-edge-legacy, + "slack": $fa-var-slack, + "slack-hash": $fa-var-slack-hash, + "medrt": $fa-var-medrt, + "usb": $fa-var-usb, + "tumblr": $fa-var-tumblr, + "vaadin": $fa-var-vaadin, + "quora": $fa-var-quora, + "square-x-twitter": $fa-var-square-x-twitter, + "reacteurope": $fa-var-reacteurope, + "medium": $fa-var-medium, + "medium-m": $fa-var-medium-m, + "amilia": $fa-var-amilia, + "mixcloud": $fa-var-mixcloud, + "flipboard": $fa-var-flipboard, + "viacoin": $fa-var-viacoin, + "critical-role": $fa-var-critical-role, + "sitrox": $fa-var-sitrox, + "discourse": $fa-var-discourse, + "joomla": $fa-var-joomla, + "mastodon": $fa-var-mastodon, + "airbnb": $fa-var-airbnb, + "wolf-pack-battalion": $fa-var-wolf-pack-battalion, + "buy-n-large": $fa-var-buy-n-large, + "gulp": $fa-var-gulp, + "creative-commons-sampling-plus": $fa-var-creative-commons-sampling-plus, + "strava": $fa-var-strava, + "ember": $fa-var-ember, + "canadian-maple-leaf": $fa-var-canadian-maple-leaf, + "teamspeak": $fa-var-teamspeak, + "pushed": $fa-var-pushed, + "wordpress-simple": $fa-var-wordpress-simple, + "nutritionix": $fa-var-nutritionix, + "wodu": $fa-var-wodu, + "google-pay": $fa-var-google-pay, + "intercom": $fa-var-intercom, + "zhihu": $fa-var-zhihu, + "korvue": $fa-var-korvue, + "pix": $fa-var-pix, + "steam-symbol": $fa-var-steam-symbol, +); diff --git a/src/sass/fontawesome/brands.scss b/src/sass/fontawesome/brands.scss new file mode 100644 index 0000000..f05b3fc --- /dev/null +++ b/src/sass/fontawesome/brands.scss @@ -0,0 +1,30 @@ +/*! + * Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-brands: 'Font Awesome 6 Brands'; + --#{$fa-css-prefix}-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; +} + +@font-face { + font-family: 'Font Awesome 6 Brands'; + font-style: normal; + font-weight: 400; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-brands-400.woff2') format('woff2'), + url('#{$fa-font-path}/fa-brands-400.ttf') format('truetype'); +} + +.fab, +.#{$fa-css-prefix}-brands { + font-weight: 400; +} + +@each $name, $icon in $fa-brand-icons { + .#{$fa-css-prefix}-#{$name} { #{$fa-icon-property}: unquote("\"#{ $icon }\""); } +} diff --git a/src/sass/fontawesome/fontawesome.scss b/src/sass/fontawesome/fontawesome.scss new file mode 100644 index 0000000..2a5b40a --- /dev/null +++ b/src/sass/fontawesome/fontawesome.scss @@ -0,0 +1,21 @@ +/*! + * Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. + */ +// Font Awesome core compile (Web Fonts-based) +// ------------------------- + +@import 'functions'; +@import 'variables'; +@import 'mixins'; +@import 'core'; +@import 'sizing'; +@import 'fixed-width'; +@import 'list'; +@import 'bordered-pulled'; +@import 'animated'; +@import 'rotated-flipped'; +@import 'stacked'; +@import 'icons'; +@import 'screen-reader'; diff --git a/src/sass/fontawesome/regular.scss b/src/sass/fontawesome/regular.scss new file mode 100644 index 0000000..4a15596 --- /dev/null +++ b/src/sass/fontawesome/regular.scss @@ -0,0 +1,27 @@ +/*! + * Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-classic: '#{ $fa-style-family }'; + --#{$fa-css-prefix}-font-regular: normal 400 1em/1 '#{ $fa-style-family }'; +} + + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 400; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-regular-400.woff2') format('woff2'), + url('#{$fa-font-path}/fa-regular-400.ttf') format('truetype'); +} + +.far, +.#{$fa-css-prefix}-regular { + font-weight: 400; +} diff --git a/src/sass/fontawesome/solid.scss b/src/sass/fontawesome/solid.scss new file mode 100644 index 0000000..f673892 --- /dev/null +++ b/src/sass/fontawesome/solid.scss @@ -0,0 +1,27 @@ +/*! + * Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. + */ +@import 'functions'; +@import 'variables'; + +:root, :host { + --#{$fa-css-prefix}-style-family-classic: '#{ $fa-style-family }'; + --#{$fa-css-prefix}-font-solid: normal 900 1em/1 '#{ $fa-style-family }'; +} + + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 900; + font-display: $fa-font-display; + src: url('#{$fa-font-path}/fa-solid-900.woff2') format('woff2'), + url('#{$fa-font-path}/fa-solid-900.ttf') format('truetype'); +} + +.fas, +.#{$fa-css-prefix}-solid { + font-weight: 900; +} diff --git a/src/sass/fontawesome/v4-shims.scss b/src/sass/fontawesome/v4-shims.scss new file mode 100644 index 0000000..448e25a --- /dev/null +++ b/src/sass/fontawesome/v4-shims.scss @@ -0,0 +1,11 @@ +/*! + * Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. + */ +// V4 shims compile (Web Fonts-based) +// ------------------------- + +@import 'functions'; +@import 'variables'; +@import 'shims'; diff --git a/src/sass/fonts/Inter-Regular.woff2 b/src/sass/fonts/Inter-Regular.woff2 deleted file mode 100644 index 6c2b689..0000000 Binary files a/src/sass/fonts/Inter-Regular.woff2 and /dev/null differ diff --git a/src/sass/fonts/Inter-SemiBold.woff2 b/src/sass/fonts/Inter-SemiBold.woff2 deleted file mode 100644 index 611e90c..0000000 Binary files a/src/sass/fonts/Inter-SemiBold.woff2 and /dev/null differ diff --git a/src/sass/magicuserjs.scss b/src/sass/magicuserjs.scss index 326e3de..ec6651f 100644 --- a/src/sass/magicuserjs.scss +++ b/src/sass/magicuserjs.scss @@ -1,3 +1,3 @@ @charset 'UTF-8'; -@import 'main'; +@use 'main'; diff --git a/src/sass/web-ext.scss b/src/sass/web-ext.scss index cae6385..0dbb6da 100644 --- a/src/sass/web-ext.scss +++ b/src/sass/web-ext.scss @@ -1,13 +1,22 @@ +// importing core styling file +@use './fontawesome/fontawesome.scss'; + +// our project needs Solid + Brands +@use './fontawesome/solid.scss'; +@use './fontawesome/brands.scss'; + body.webext-page { - // overflow-x: hidden; + overflow: hidden; padding: 0; margin: 0; - background: #495060; - color: #ffffff; + background: var(--mujs-background-color, hsl(222, 14%, 33%)); + color: var(--mujs-txt-color, hsl(0, 0%, 100%)); border: 1px solid rgba(0, 0, 0, 0); border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; font-size: 14px; + min-width: 720px; + .mujs-cfg { display: flex; gap: 10px; @@ -20,11 +29,7 @@ body.webext-page { } } mujs-main { - // max-width: 100%; - min-width: 480px; - // max-height: 100%; - // min-height: 200px; - // min-width: 50em; - min-height: 40em; + min-width: 25em; + height: 492px; } -} \ No newline at end of file +} diff --git a/src/svg/bug-svgrepo-com.svg b/src/svg/bug-svgrepo-com.svg deleted file mode 100644 index 32b7852..0000000 --- a/src/svg/bug-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/svg/checkmark-seal-svgrepo-com.svg b/src/svg/checkmark-seal-svgrepo-com.svg deleted file mode 100644 index 8a5b4a3..0000000 --- a/src/svg/checkmark-seal-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/svg/close-svgrepo-com.svg b/src/svg/close-svgrepo-com.svg deleted file mode 100644 index a4a1e54..0000000 --- a/src/svg/close-svgrepo-com.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/src/svg/download-svgrepo-com.svg b/src/svg/download-svgrepo-com.svg deleted file mode 100644 index 9b8cf09..0000000 --- a/src/svg/download-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/svg/expand-full-fullscreen-svgrepo-com.svg b/src/svg/expand-full-fullscreen-svgrepo-com.svg deleted file mode 100644 index 53218ff..0000000 --- a/src/svg/expand-full-fullscreen-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/svg/filter-svgrepo-com.svg b/src/svg/filter-svgrepo-com.svg deleted file mode 100644 index 6bedf72..0000000 --- a/src/svg/filter-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/svg/fork-svgrepo-com.svg b/src/svg/fork-svgrepo-com.svg deleted file mode 100644 index 8087aa8..0000000 --- a/src/svg/fork-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/svg/fullscreen-svgrepo-com.svg b/src/svg/fullscreen-svgrepo-com.svg deleted file mode 100644 index 15379af..0000000 --- a/src/svg/fullscreen-svgrepo-com.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/svg/github-svgrepo-com.svg b/src/svg/github-svgrepo-com.svg deleted file mode 100644 index eaac757..0000000 --- a/src/svg/github-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/svg/minus-svgrepo-com.svg b/src/svg/minus-svgrepo-com.svg deleted file mode 100644 index 4f655c0..0000000 --- a/src/svg/minus-svgrepo-com.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/svg/navigation-svgrepo-com.svg b/src/svg/navigation-svgrepo-com.svg deleted file mode 100644 index 92569ad..0000000 --- a/src/svg/navigation-svgrepo-com.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/svg/plus-svgrepo-com.svg b/src/svg/plus-svgrepo-com.svg deleted file mode 100644 index 8410ef7..0000000 --- a/src/svg/plus-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/svg/search-svgrepo-com.svg b/src/svg/search-svgrepo-com.svg deleted file mode 100644 index 8369115..0000000 --- a/src/svg/search-svgrepo-com.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/svg/settings-svgrepo-com.svg b/src/svg/settings-svgrepo-com.svg deleted file mode 100644 index eaeef67..0000000 --- a/src/svg/settings-svgrepo-com.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/typings/UserJS.d.ts b/src/typings/UserJS.d.ts index 3868ff4..a3a0cc9 100644 --- a/src/typings/UserJS.d.ts +++ b/src/typings/UserJS.d.ts @@ -1,24 +1,123 @@ // import GM from '@types/greasemonkey'; // import '@types/tampermonkey'; import '@violentmonkey/types'; -import { type GSForkQuery } from './types'; +import { config, FilterLayout, UserJSEngine, type GSForkQuery } from './types'; +import './scheduler'; -/** - * Some sites will alter or remove document functions - * To get around this we bind them to the `userjs` object - * - * This method is based on uBlock Origin `scriptlets.js` file - * - * [Source Code](https://github.com/gorhill/uBlock/blob/master/assets/resources/scriptlets.js) - */ -export declare function safeSelf(): { +/** [i18n directory](https://github.com/magicoflolis/Userscript-Plus/tree/master/src/_locales) */ +export const translations: { + [i18n: string]: { + [key: string]: string; + }; +}; +/** [source code](https://github.com/magicoflolis/Userscript-Plus/blob/master/src/sass/_main.scss) */ +export const main_css: string; + +export interface safeHandles { XMLHttpRequest: typeof XMLHttpRequest; + CustomEvent: typeof CustomEvent; + customElements: typeof customElements; createElement: typeof document.createElement; createElementNS: typeof document.createElementNS; createTextNode: typeof document.createTextNode; setTimeout: typeof setTimeout; clearTimeout: typeof clearTimeout; -}; + navigator: typeof navigator; + // customElements: typeof customElements; + /** Taken from [scheduler-polyfill](https://github.com/GoogleChromeLabs/scheduler-polyfill) */ + scheduler: typeof scheduler; + /** + * Groups members of an iterable according to the return value of the passed callback. + * @param items An iterable. + * @param keySelector A callback which will be invoked for each item in items. + */ + groupBy( + items: Iterable, + keySelector: (item: T, index: number) => K + ): Partial>; +} + +export class Safe { + public constructor(); + public _self: safeHandles | null; +} + +export interface Translations { + createdby: string; + name: string; + daily_installs: string; + close: string; + filterA: string; + max: string; + min: string; + search: string; + search_placeholder: string; + install: string; + issue: string; + version_number: string; + updated: string; + total_installs: string; + ratings: string; + good: string; + ok: string; + bad: string; + created_date: string; + redirect: string; + filter: string; + dtime: string; + save: string; + reset: string; + preview_code: string; + saveFile: string; + newTab: string; + applies_to: string; + license: string; + no_license: string; + antifeatures: string; + userjs_fullscreen: string; + listing_none: string; + export_config: string; + export_theme: string; + import_config: string; + import_theme: string; + code_size: string; + prmpt_css: string; + userjs_inject: string; + userjs_close: string; + userjs_sync: string; + userjs_autoinject: string; + auto_fetch: string; +} + +export interface LanguageTranslations { + ar: Translations; + de: Translations; + en: Translations; + en_GB: Translations; + es: Translations; + fr: Translations; + ja: Translations; + nl: Translations; + pl: Translations; + ru: Translations; + zh: Translations; + zh_CN: Translations; + zh_TW: Translations; +} + +// export declare const translations: LanguageTranslations; + +export declare function i18n$( + key: string +): Translations[K] | 'Unknown'; + +/** + * Some sites will alter or remove document functions + * To get around this we bind them to the `userjs` object + * + * This method is based on uBlock Origin [scriptlets.js](https://github.com/gorhill/uBlock/blob/master/assets/resources/scriptlets.js) + */ +export declare function safeSelf(): safeHandles; export declare function loadCSS(css: string, name: string): HTMLStyleElement | undefined; @@ -40,15 +139,9 @@ export declare function observe( */ export declare function openTab(url: string | URL): WindowProxy | null; -/** - * Get information about the current userscript. - * - * [ViolentMonkey Reference](https://violentmonkey.github.io/api/gm/#gm_info) - */ -export declare function getGMInfo(): typeof GM_info; - export interface StorageSystem { prefix: string; + events: Set; /** * Alias of `window.localStorage.getItem` */ @@ -66,6 +159,10 @@ export interface StorageSystem { */ remove(key: K): void; + addListener(name: string, callback: VMScriptGMValueChangeCallback): number | void; + + attach(): void; + /** * Set value - Saves key to either GM managed storage or `window.localStorage` * @@ -111,35 +208,163 @@ export interface Network { ): Promise>; bscStr(str: S, lowerCase: boolean): S; } - -declare class Timeout { +//#region Testing crap +export class Timeout { + public constructor(); public ids: number[]; - public set(delay: number, reason: any): Promise; + public set(delay: number, reason?: R): Promise; public clear(...ids: number[]): void; } +export class Tabs { + public constructor(root: HTMLElement); + public pool: Set; + public blank: 'about:blank'; + public protocal: 'mujs:'; + public protoReg: RegExp; + public el: { + add: HTMLElement; + head: HTMLElement; + root: HTMLElement; + }; + public custom: () => void; + public getTab(hostname: S): HTMLElement | undefined; + public getActive(): HTMLElement | undefined; + public intFN(hostname: S): void; + public active(tab: T, build?: boolean): void; + public close(tab: T): void; + public create(hostname?: string): HTMLElement | undefined; +} -export interface Container { - webpage: URL; - host: string; - domain: string; - ready: boolean; - injected: boolean; - shadowRoot?: ShadowRoot | HTMLElement; - supported: boolean; - frame: HTMLElement | HTMLIFrameElement; - cache: Map; - userjsCache: Map; - root: HTMLElement; - unsaved: boolean; - isBlacklisted: boolean; - rebuild: boolean; - counters: { - total: number; - [engine: string]: number; +export interface mujsName extends HTMLTableCellElement { + _mujs: { + fmore: HTMLElement; + fBtns: HTMLElement; + codeArea: HTMLTextAreaElement; + } +} + +export interface cfgpage extends HTMLElement { + _mujs: { + base: { + text: string; + tag: string; + value: string; + type: HTMLInputElement['type']; + attrs: object; + default: string | boolean | number | UserJSEngine | FilterLayout; + cache: string | boolean | number | UserJSEngine | FilterLayout; + elem: HTMLInputElement | HTMLSelectElement; + elemUrl?: HTMLInputElement; + elemToken?: HTMLInputElement; + }[]; + sections: Set; + } +} + +export class Container { + public static prompts: HTMLElement[]; + public webpage: URL; + public host: string; + public domain: string; + public ready: boolean; + public injected: boolean; + public shadowRoot?: ShadowRoot; + public shadowSupport: boolean; + public frame: HTMLElement; + public hostCache?: Map; + public userjsCache: Map; + public root: HTMLElement; + public unsaved: boolean; + public isBlacklisted: boolean; + public rebuild: boolean; + public opacityMin: string; + public opacityMax: string; + public elementsReady?: boolean; + public timeouts?: { + frame: Timeout; + mouse: Timeout; + }; + public Tabs?: Tabs; + public mainframe?: HTMLElement; + public countframe?: HTMLElement; + public mainbtn?: HTMLElement; + public urlBar?: HTMLInputElement; + public rateContainer?: HTMLElement; + public footer?: HTMLElement; + public tabbody?: HTMLElement; + public promptElem?: HTMLElement; + public toolbar?: HTMLElement; + public table?: HTMLTableElement; + public tabhead?: HTMLTableSectionElement; + public header?: HTMLElement; + public tbody?: HTMLTableSectionElement; + public cfgpage: cfgpage; + public main?: HTMLElement; + public urlContainer?: HTMLElement; + public btnframe?: HTMLElement; + public btnHandles?: HTMLElement; + public btnHide?: HTMLElement; + public btnfullscreen?: HTMLElement; + public closebtn?: HTMLElement; + public btncfg?: HTMLElement; + public btnhome?: HTMLElement; + public btnissue?: HTMLElement; + public btngreasy?: HTMLElement; + public btnnav?: HTMLElement; + public injFN?: () => void; + public inject(callback: (this: this, shadowRoot: this['shadowRoot']) => any, doc?: Document): void; + public initFn(): void; + public init(): boolean; + public remove(): void; + public save(): Promise; + /** + * @param css - CSS to inject + * @param name - Name of stylesheet + * @return Style element + */ + public loadCSS(css: C, name?: N): HTMLStyleElement | undefined; + public checkBlacklist(str: S): boolean; + public setTheme(): void; + public makePrompt(txt: S, dataset?: {}, usePrompt?: boolean): HTMLElement; + public showError(...ex: E[]): void; + public refresh(): void; + /** + * Redirects sleazyfork userscripts from greasyfork.org to sleazyfork.org + * + * Taken from: https://greasyfork.org/scripts/23840 + */ + public redirect(): void; + public timeoutFrame(time?: N): Promise; + public toElem(): HTMLElement[]; + public [Symbol.iterator](): Generator; +} + +export class List { + public constructor(hostname?: string); + private intEngines: UserJSEngine[]; + private intHost: string; + public dispatch(ujs: GSForkQuery): void; + public get engines(): UserJSEngine[]; + public get host(): string; + public setEngines(engines?: E[]): E[]; + public setHost(hostname: S): S; + public getDomain(str?: S): S; + public build(): void; + public sortRecords(): void; + public groupBy(): Record; + public [Symbol.iterator](): Generator; +} +//#endregion + +declare global { + let translations: { + [i18n: string]: string; + }; + let userjs: { + /** + * this should always be `true` otherwise the script won't execute + */ + UserJS: boolean; + isMobile?: boolean; }; - opacityMin: string; - opacityMax: string; - Timeout: new () => Timeout; } diff --git a/src/typings/scheduler.d.ts b/src/typings/scheduler.d.ts new file mode 100644 index 0000000..8c4148e --- /dev/null +++ b/src/typings/scheduler.d.ts @@ -0,0 +1,131 @@ +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Task priorities that determine the order in which tasks run. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Prioritized_Task_Scheduling_API#task_priorities) + */ +export type TaskPriority = 'user-blocking' | 'user-visible' | 'background'; + +/** + * {@link Scheduler.postTask} options. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Scheduler/postTask#options) + */ +export type SchedulerPostTaskOptions = { + /** The immutable {@link TaskPriority} of the task. One of `"user-blocking"`, `"user-visible"`, or `"background"`. If set, this priority is used for the lifetime of the task and priority set on the `signal` is ignored. */ + priority?: TaskPriority; + /** An {@link AbortSignal} or {@link TaskSignal} that can be used to abort or re-prioritize the task (from its associated controller). The signal's priority is ignored if `priority` is set. */ + signal?: AbortSignal | TaskSignal; + /** The minimum amount of time after which the task will be added to the scheduler queue, in whole milliseconds. The actual delay may be higher than specified, but will not be less. The default delay is 0. */ + delay?: number; +}; + +/** + * {@link TaskController} options. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TaskController/TaskController#options) + */ +type TaskControllerOptions = { + /** The {@link TaskPriority} of the signal associated with this {@link TaskController}. One of `"user-blocking"`, `"user-visible"`, or `"background"`. The default is `"user-visible"`. */ + priority?: TaskPriority; +} + +/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/TaskPriorityChangeEvent/TaskPriorityChangeEvent#options) */ +interface TaskPriorityChangeEventInit extends EventInit { + /** A string indicating the previous priority of the task. One of `"user-blocking"`, `"user-visible"`, or `"background"`. */ + previousPriority: TaskPriority; +} + +declare global { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Scheduler) */ + interface Scheduler { + /** + * Adds a task to the scheduler as a callback, optionally specifying a priority, delay, and/or a signal for aborting the task. + * @param callback A callback function that implements the task. The return value of the callback is used to resolve the promise returned by this function. + * @param options {@link SchedulerPostTaskOptions} options. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Scheduler/postTask) + */ + postTask(callback: () => T, options?: SchedulerPostTaskOptions): Promise; + /** + * Returns a promise that yields to the event loop when awaited, allowing continuation in a new task. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Scheduler/yield) + */ + yield(): Promise; + } + + /** + * A controller object that can be used to both abort and change the priority of one or more prioritized tasks. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TaskController) + */ + class TaskController extends AbortController { + constructor(options?: TaskControllerOptions); + /** Returns a {@link TaskSignal} instance. The signal is passed to tasks so that they can be aborted or re-prioritized by the controller. */ + readonly signal: TaskSignal; + /** + * Sets the priority of the controller's signal, and hence the priority of any tasks with which it is associated. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TaskController/setPriority) + */ + setPriority(priority: TaskPriority): void; + } + + /** + * The `prioritychange` event, sent to a {@link TaskSignal} if its priority is changed. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TaskPriorityChangeEvent) + */ + class TaskPriorityChangeEvent extends Event { + constructor(type: string, init: TaskPriorityChangeEventInit); + /** + * The priority of the corresponding {@link TaskSignal} _before_ this prioritychange event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TaskPriorityChangeEvent/previousPriority) + */ + readonly previousPriority: TaskPriority; + } + + /** + * A signal object that allows you to communicate with a prioritized task, aborting it or changing the priority (if required) via a {@link TaskController} object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TaskSignal) + */ + class TaskSignal extends AbortSignal { + /** + * The priority of the signal. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TaskSignal/priority) + */ + readonly priority: TaskPriority; + /** + * Fired when the priority is changed. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TaskSignal/prioritychange_event) + */ + onprioritychange: (event: TaskPriorityChangeEvent) => void; + } + + /** + * The global {@link Scheduler} entry point for using the Prioritized Task Scheduling API. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/scheduler) + */ + const scheduler: Scheduler; +} diff --git a/src/typings/types.d.ts b/src/typings/types.d.ts index 9f6c0d0..2ff990c 100644 --- a/src/typings/types.d.ts +++ b/src/typings/types.d.ts @@ -48,6 +48,10 @@ export type GSForkQuery = { description: string; url: string; code_url: string; + code_urls: { + name: string; + code_url: string; + }[]; license: string; version: string; locale: string; @@ -58,14 +62,22 @@ export type GSForkQuery = { host?: string; }; code: { - antifeatures: string[]; - data?: string; + meta: UserJSMeta | null; + request(translate: boolean, code_url?: string): Promise; + code_size?: string[]; translated?: boolean; - grant: string[]; - match: string[]; - meta: UserJSMeta | null; - request(): Promise; + code?: string; + data_meta_block?: string; + data_code_block?: string; + data_meta?: { + [meta: string]: string | string[] | { [resource: string]: string }; + }; + data_names?: { + text: string; + domain: boolean; + tld_extra: boolean; + }[]; }; root?: HTMLTableRowElement; }; @@ -94,22 +106,54 @@ export type GSFork = { export type UserJSEngine = { enabled: boolean; name: string; - url: string; + url?: string; token?: string; - query?: string; + query: string; }; +export interface FilterLayout { + enabled: boolean; + name: string; + flag?: string; + regExp: string; +} + +export interface Filters extends FilterLayout { + reg: RegExp; + keyReg: RegExp; + valueReg: RegExp; +} + export type config = { /** - * Sync config with UserScript manager + * List sorting on load + */ + autoSort: string; + /** + * Fetch from engines on load + */ + autofetch: boolean; + /** + * Inject list on load + */ + autoinject: boolean; + /** + * Clear cache on tab close + */ + clearTabCache: boolean; + /** + * `UserScript:` Sync config with UserScript manager */ cache?: boolean; /** * Fetch all UserScript code on load */ - codePreview: boolean; + preview: { + code: boolean; + metadata: boolean; + }; /** - * Fullscreen list on load + * `UserScript:` Fullscreen list on load */ autoexpand?: boolean; /** @@ -121,7 +165,7 @@ export type config = { */ sleazyredirect: boolean; /** - * Miliseconds before list closes + * `UserScript:` Miliseconds before list closes */ time?: number; /** @@ -166,6 +210,7 @@ export type config = { 'position-left': string; 'position-right': string; 'font-family': string; + // [key: string ]: string; }; /** * Highlight UserScripts recommended by the author or UserScripts created by the author @@ -174,56 +219,68 @@ export type config = { author: boolean; others: boolean; }; + /** + * Taken from https://greasyfork.org/scripts/12179 + */ + filters: { + ASCII: FilterLayout; + Latin: FilterLayout; + Games: FilterLayout; + SocialNetworks: FilterLayout; + Clutter: FilterLayout; + }; }; -export declare function hasOwn(o: object, p: PropertyKey): boolean; - -export declare function objToStr(obj: O): string; - -export declare function strToURL(str: S): URL; - +/** + * Object to `[object *]` + */ +export declare function objToStr(obj: any): string; /** * Object is typeof `RegExp` */ -export declare function isRegExp(obj: O): boolean; - +export declare function isRegExp(obj: any): obj is RegExp; +/** + * Object is typeof `HTMLElement` + */ +export declare function isHTML(obj: any): obj is HTMLElement; /** * Object is typeof `Element` */ -export declare function isElem(obj: O): boolean; +export declare function isElem(obj: any): obj is Element; /** * Object is typeof `object` / JSON Object */ -export declare function isObj(obj: O): boolean; +export declare function isObj(obj: any): obj is Object; /** * Object is typeof `Function` */ -export declare function isFN(obj: O): boolean; +export declare function isFN(obj: any): obj is Function; /** * Object is `null` or `undefined` */ -export declare function isNull(obj: O): boolean; +export declare function isNull(obj: any): obj is null; +export declare function isNull(obj: any): obj is undefined; /** - * Object is Blank + * Object is blank */ -export declare function isBlank(obj: O): boolean; +export declare function isBlank(obj: O): boolean; /** - * Object is Empty + * Object is empty */ -export declare function isEmpty(obj: O): boolean; +export declare function isEmpty(obj: O): boolean; /** * Type is not 100% accurate */ export declare function normalizeTarget( target: T, - toQuery: boolean, - root: Element | Document + toQuery?: boolean, + root?: Document | Element ): T[]; export declare function halt(evt: Event): void; @@ -263,26 +320,12 @@ declare global { } /** - * Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. - * - * The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. - * - * When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. - * - * When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. - * - * When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. - * - * If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted. - * - * The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. - * * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) */ export declare function ael( el: E, type: K, - listener: (this: E, ev: HTMLElementEventMap[K]) => any, + listener: (this: E, ev: HTMLElementEventMap[K]) => any | EventListenerOrEventListenerObject, options?: AddEventListenerOptions | boolean ): void; @@ -305,8 +348,10 @@ export declare function qsA( /** * Set attributes for an element. + * @param elem HTML element + * @param attr Set attributes for the element */ -export declare function formAttrs(elem: E, attr: keyof E): E; +export declare function formAttrs(elem: E, attr?: E[keyof E]): E; /** * Creates an instance of the element for the specified tag. @@ -321,9 +366,14 @@ export declare function make< A extends keyof HTMLElementTagNameMap[K] >( tagName: K, - cname?: string, + cname?: + | string + | string[] + | { + [key in A]: Record; + }, attrs?: { - [key in A]: unknown; + [key in A]: Record; } ): HTMLElementTagNameMap[K]; @@ -344,9 +394,10 @@ export interface dom { value?: V ): V | undefined; text(target: T, text?: V): string | null | undefined; + remove(target: T): void; cl: { - add(target: T, token: string | string[]): void; - remove(target: T, token: string | string[]): void; + add(target: T, token: string | string[]): boolean; + remove(target: T, token: string | string[]): boolean; toggle(target: T, token: string | string[], force?: boolean): boolean; has(target: T, token: string | string[]): boolean; }; diff --git a/src/webfonts/fa-brands-400.ttf b/src/webfonts/fa-brands-400.ttf new file mode 100644 index 0000000..0f82a83 Binary files /dev/null and b/src/webfonts/fa-brands-400.ttf differ diff --git a/src/webfonts/fa-brands-400.woff2 b/src/webfonts/fa-brands-400.woff2 new file mode 100644 index 0000000..3c5cf97 Binary files /dev/null and b/src/webfonts/fa-brands-400.woff2 differ diff --git a/src/webfonts/fa-regular-400.ttf b/src/webfonts/fa-regular-400.ttf new file mode 100644 index 0000000..9ee1919 Binary files /dev/null and b/src/webfonts/fa-regular-400.ttf differ diff --git a/src/webfonts/fa-regular-400.woff2 b/src/webfonts/fa-regular-400.woff2 new file mode 100644 index 0000000..57d9179 Binary files /dev/null and b/src/webfonts/fa-regular-400.woff2 differ diff --git a/src/webfonts/fa-solid-900.ttf b/src/webfonts/fa-solid-900.ttf new file mode 100644 index 0000000..1c10972 Binary files /dev/null and b/src/webfonts/fa-solid-900.ttf differ diff --git a/src/webfonts/fa-solid-900.woff2 b/src/webfonts/fa-solid-900.woff2 new file mode 100644 index 0000000..1672102 Binary files /dev/null and b/src/webfonts/fa-solid-900.woff2 differ diff --git a/src/webfonts/fa-v4compatibility.ttf b/src/webfonts/fa-v4compatibility.ttf new file mode 100644 index 0000000..3bcb67f Binary files /dev/null and b/src/webfonts/fa-v4compatibility.ttf differ diff --git a/src/webfonts/fa-v4compatibility.woff2 b/src/webfonts/fa-v4compatibility.woff2 new file mode 100644 index 0000000..fbafb22 Binary files /dev/null and b/src/webfonts/fa-v4compatibility.woff2 differ diff --git a/tools/languageLoader.js b/tools/languageLoader.js deleted file mode 100644 index 50e8861..0000000 --- a/tools/languageLoader.js +++ /dev/null @@ -1,54 +0,0 @@ -import { readdir, readFile, stat } from 'node:fs/promises'; -import { URL, fileURLToPath } from 'node:url'; - -/** - * Loads all the structures in the provided directory. - * - * @template T - * @param {import('node:fs').PathLike} dir - The directory to load the structures from - * @param {boolean} recursive - Whether to recursively load the structures in the directory - * @returns {Promise} - */ -export async function loadLanguages(dir, recursive = true) { - // Get the stats of the directory - const statDir = await stat(dir); - - // If the provided directory path is not a directory, throw an error - if (!statDir.isDirectory()) { - throw new Error(`The directory '${dir}' is not a directory.`); - } - - // Get all the files in the directory - const files = await readdir(dir); - - // Create an empty array to store the structures - /** @type {T[]} */ - const structures = []; - - // Loop through all the files in the directory - for (const file of files) { - // Get the stats of the file - const statFile = await stat(new URL(`${dir}/${file}`)); - - // If the file is a directory and recursive is true, recursively load the structures in the directory - if (statFile.isDirectory() && recursive) { - structures.push(...(await loadLanguages(new URL(`${dir}/${file}`), recursive))); - continue; - } - - if (!file.endsWith('.json')) { - continue; - } - - const filePath = fileURLToPath(`${dir}/${file}`); - const reg = /_locales\\(.*?)\\messages\.json/g.exec(filePath); - if (reg) { - const structure = await readFile(filePath, 'utf-8'); - structures.push({ - [reg[1]]: JSON.parse(structure.toString('utf-8')) - }); - } - } - - return structures; -} diff --git a/tools/userscript.js b/tools/userscript.js index 2bb3a73..c7c6d0d 100644 --- a/tools/userscript.js +++ b/tools/userscript.js @@ -1,398 +1,4 @@ -import path from 'path'; -import { URL } from 'node:url'; -import fs from 'node:fs'; -import dotenv from 'dotenv'; -import Watchpack from 'watchpack'; -import { loadLanguages } from './languageLoader.js'; +'use strict'; +import build from 'user.js'; -/** - * @typedef { import('../package.json') } CFG - */ - -const replaceTemplate = /\[\[(.*?)\]\]/g; -const metaStr = '[[metadata]]'; - -const log = (...msg) => { - console.log('[LOG]', ...msg); -}; -const err = (...msg) => { - console.error('[ERROR]', ...msg); -}; -/** - * Object is typeof `object` / JSON Object - * @template O - * @param { O } obj - * @returns { boolean } - */ -const isObj = (obj) => { - /** @type { string } */ - const s = Object.prototype.toString.call(obj); - return s.includes('Object'); -}; -/** - * Object is `null` or `undefined` - * @template O - * @param { O } obj - * @returns { boolean } - */ -const isNull = (obj) => { - return Object.is(obj, null) || Object.is(obj, undefined); -}; -/** - * Object is Blank - * @template O - * @param { O } obj - * @returns { boolean } - */ -const isBlank = (obj) => { - return ( - (typeof obj === 'string' && Object.is(obj.trim(), '')) || - ((obj instanceof Set || obj instanceof Map) && Object.is(obj.size, 0)) || - (Array.isArray(obj) && Object.is(obj.length, 0)) || - (isObj(obj) && Object.is(Object.keys(obj).length, 0)) - ); -}; -/** - * Object is Empty - * @template O - * @param { O } obj - * @returns { boolean } - */ -const isEmpty = (obj) => { - return isNull(obj) || isBlank(obj); -}; -/** - * @template { string | { msg: string; } } T - * @template D - * @param { T } template - * @param { D } data - */ -const nano = (template, data) => { - if (typeof template === 'string') { - return template.replace(replaceTemplate, (_match, key) => { - const keys = key.split('.'); - let v = data[keys.shift()]; - for (const i in keys.length) v = v[keys[i]]; - return isEmpty(v) ? '' : v; - }); - } - return ''; -}; -/** - * @param {import('node:fs').PathLike} filePath - * @param {string} encoding - */ -const canAccess = async (filePath, encoding = 'utf-8') => { - const testAccess = await fs.promises.access( - filePath, - fs.promises.constants.R_OK | fs.promises.constants.W_OK - ); - if (isNull(testAccess)) { - const data = await fs.promises.readFile(filePath, encoding); - return data.toString(encoding); - } - return { - msg: `Cannot access provided filePath: ${filePath}` - }; -}; -/** - * @param {import('node:fs').PathLike} filePath - * @param {string} encoding - */ -const fileToJSON = async (filePath, encoding = 'utf-8') => { - const testAccess = await canAccess(filePath, encoding); - if (isObj(testAccess)) { - throw new Error(testAccess.msg); - } - return JSON.parse(testAccess); -}; -/** - * @param {import('node:fs').PathLike} destinationFilePath - * @param data - */ -const writeUserJS = async (destinationFilePath, data) => { - return await fs.promises.writeFile(destinationFilePath, data); -}; -const toTime = () => { - return new Intl.DateTimeFormat('default', { - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - fractionalSecondDigits: 3 - }).format(new Date()); -}; -/** - * @type { Map } - */ -const dataMap = new Map(); -const compareArrays = (a, b) => - a.length === b.length && a.every((element, index) => element === b[index]); -/** - * @template {string} K - * @template V - * @param {K} key - * @param {...V} values - */ -const addTo = (key, ...values) => { - if (values.length === 0) { - return ''; - } - if (dataMap.has(key)) { - if (compareArrays(dataMap.get(key), values)) { - return dataMap.get(key); - } - } - dataMap.set(key, values); - return dataMap.get(key); -}; -const filterData = [ - 'name', - 'description', - 'author', - 'icon', - 'version', - 'url', - 'homepage', - 'bugs', - 'license' -]; - -(async () => { - /** @type { dotenv.DotenvConfigOutput } */ - let result = {}; - try { - /** - * @type { CFG } - */ - const jsonData = await fileToJSON('./package.json'); - if (!isObj(jsonData)) { - throw new Error('"jsonData" must be an object.'); - } - if (!jsonData.userJS) { - throw new Error('Missing "userJS" key'); - } - const userJS = jsonData.userJS; - const { build } = userJS; - - result = dotenv.config({ - path: isEmpty(process.env.JS_ENV) ? build.paths.dev.env : build.paths.public.env - }); - if (result.error) { - throw result.error; - } - if (isNull(result.parsed.JS_ENV)) { - dotenv.populate( - result.parsed, - { - JS_ENV: 'development' - }, - { override: true, debug: true } - ); - } - const env = result.parsed; - const js_env = env.JS_ENV === 'development'; - const dp = js_env ? 'dev' : 'public'; - const compileMetadata = () => { - const metaData = []; - for (const [key, value] of Object.entries(userJS.metadata)) { - if (Array.isArray(value)) { - for (const v of value) { - metaData.push(`// @${key} ${v}`); - } - } else if (isObj(value)) { - for (const [k, v] of Object.entries(value)) { - metaData.push(`// @${key} ${k} ${v}`); - } - } else if (typeof value === 'boolean') { - if (value === true) { - metaData.push(`// @${key}`); - } - } else { - metaData.push(`// @${key} ${value}`); - } - } - return metaData.join('\n'); - }; - const buildUserJS = async () => { - try { - const lngList = await loadLanguages(new URL('../src/_locales', import.meta.url)); - const transformLanguages = () => { - const resp = {}; - for (const obj of lngList) { - for (const [k, v] of Object.entries(obj)) { - const o = {}; - for (const [key, value] of Object.entries(v)) { - if (key.startsWith('ext')) { - continue; - } - if (/userjs_(name|description)/i.test(key)) { - continue; - } - if (isEmpty(value.message)) { - continue; - } - o[key] = value.message; - } - resp[k] = o; - } - } - return JSON.stringify(resp, null, ' '); - }; - const compileLanguage = (type = 'userjs_name') => { - const resp = []; - for (const obj of lngList) { - for (const [k, v] of Object.entries(obj)) { - if (v[type]) { - if (isEmpty(v[type].message)) { - continue; - } - if (k.startsWith('en')) { - continue; - } - const t = type.toLowerCase().replace('userjs_', ''); - if (type === 'userjs_name') { - resp.push( - `// @${t}:${k.replace('_', '-')} ${js_env ? '[Dev] ' : ''}${v[type].message}` - ); - } else { - resp.push(`// @${t}:${k.replace('_', '-')} ${v[type].message}`); - } - } - } - } - return resp; - }; - const getData = () => { - for (const [k, v] of Object.entries(userJS)) { - if (typeof v !== 'string') { - continue; - } - if (k === 'name') { - addTo( - k, - `// @${k} ${js_env ? '[Dev] ' : ''}${v}`, - ...compileLanguage('userjs_name') - ); - } else if (k === 'description') { - addTo(k, `// @${k} ${v}`, ...compileLanguage('userjs_description')); - } else if (k === 'author') { - addTo(k, `// @${k} ${v}`); - } else if (k === 'icon') { - const buff = new Buffer.from(fs.readFileSync(v)); - const base64data = buff.toString('base64'); - if (v.endsWith('.png')) { - addTo(k, `// @${k} data:image/png;base64,${base64data}`); - } else if (v.endsWith('.svg')) { - addTo(k, `// @${k} data:image/svg+xml;base64,${base64data}`); - } - } else if (k === 'downloadURL') { - addTo(k, `// @downloadURL ${v}`); - } else if (k === 'updateURL') { - addTo(k, `// @updateURL ${v}`); - } else if (k === 'url') { - addTo(k, `// @downloadURL ${v}`, `// @updateURL ${v}`); - } else if (k === 'version') { - addTo(k, `// @${k} ${js_env ? +new Date() : v}`); - } else if (k === 'homepage') { - addTo(k, `// @namespace ${v}`, `// @homepageURL ${v}`); - } else if (k === 'bugs') { - addTo(k, `// @supportURL ${v}`); - } else if (k === 'license') { - addTo(k, `// @${k} ${v}`); - } else { - addTo(k, v); - } - } - for (const [k, v] of Object.entries(jsonData)) { - if (typeof v !== 'string') { - continue; - } - if (!filterData.includes(k)) { - continue; - } - if (dataMap.has(k)) { - continue; - } - if (k === 'license') { - addTo(k, `// @${k} ${v}`); - } else if (k === 'author') { - addTo(k, `// @${k} ${v}`); - } - } - if (userJS.metadata) { - addTo('metaData', compileMetadata()); - } - return [...dataMap.values()].flat().join('\n'); - }; - const userJSHeader = `// ==UserScript==\n${getData()}\n// ==/UserScript==`; - const headerFile = await canAccess(build.source.head); - const mainFile = await canAccess(build.source.body); - const nanoCFG = { - metadata: userJSHeader, - languageList: transformLanguages(), - code: mainFile - }; - if (build.source.extras) { - for (const [k, v] of Object.entries(build.source.extras)) { - const extraFile = await canAccess(v); - if (typeof extraFile === 'string') { - nanoCFG[k] = extraFile; - } - } - } - const outFile = `${build.paths[dp].dir}/${build.paths[dp].fileName}.user.js`; - await writeUserJS(outFile, nano(headerFile, nanoCFG)); - log('UserJS Build:', { - path: outFile, - time: toTime() - }); - if (!js_env) { - const outMeta = `${build.paths[dp].dir}/${build.paths[dp].fileName}.meta.js`; - await writeUserJS(outMeta, nano(metaStr, { metadata: userJSHeader })); - log('UserJS Metadata:', { - path: outMeta, - time: toTime() - }); - } - } catch (ex) { - err(ex); - } - }; - //#region Start Process - log(`Node ENV: ${env.JS_ENV}`); - - if (js_env) { - const wp = new Watchpack(); - let changed = new Set(); - wp.watch(build.watch.files, build.watch.dirs); - wp.on('change', (changedFile, mtime) => { - if (mtime === null) { - changed.delete(changedFile); - } else { - changed.add(changedFile); - } - }); - wp.on('aggregated', async () => { - // Filter out files that start with a dot from detected changes - // (as they are hidden files or temp files created by an editor). - const changes = Array.from(changed).filter((filePath) => { - return !path.basename(filePath).startsWith('.'); - }); - changed = new Set(); - - if (changes.length === 0) { - return; - } - - await buildUserJS(); - }); - return; - } - await buildUserJS(); - - process.exit(0); - //#endregion - } catch (ex) { - err(ex); - } -})(); +build(); diff --git a/tools/web-ext.js b/tools/web-ext.mjs similarity index 57% rename from tools/web-ext.js rename to tools/web-ext.mjs index a36f36e..8d38668 100644 --- a/tools/web-ext.js +++ b/tools/web-ext.mjs @@ -1,20 +1,21 @@ -module.exports = { +export default { + verbose: false, artifactsDir: './build', sourceDir: './build/firefox', - verbose: true, + ignoreFiles: [ + '*.web-extension-id', + 'web-ext-artifacts' + ], build: { asNeeded: false, overwriteDest: true, }, run: { - args: ['-devtools'], - firefox: 'firefox', - firefoxProfile: 'debug', + devtools: true, + firefox: 'deved', + firefoxProfile: 'UserJS', + target: ['firefox-desktop'], startUrl: ['https://www.google.com'], - watchFile: ['./build/firefox/*'] - }, - ignoreFiles: [ - '*.web-extension-id', - 'web-ext-artifacts' - ], + watchFile: ['./build/firefox'] + } }; diff --git a/tools/webpack.config.js b/tools/webpack.config.js index c88799a..b4e49a6 100644 --- a/tools/webpack.config.js +++ b/tools/webpack.config.js @@ -85,6 +85,11 @@ export default async (env, args) => { to: file(`${webExtDir}/img`), globOptions }, + { + from: file(`${webExtSrc}/webfonts`), + to: file(`${webExtDir}/webfonts`), + globOptions + }, // { // from: file(`${webExtSrc}/web_accessible_resources`), // to: file(`${webExtDir}/web_accessible_resources`), @@ -105,7 +110,6 @@ export default async (env, args) => { const Config = { context: file(webExtSrc), entry: { - // start: './js/start.js' mu: './js/mu.js' }, output: { diff --git a/utils/builder/README.md b/utils/builder/README.md new file mode 100644 index 0000000..b69b879 --- /dev/null +++ b/utils/builder/README.md @@ -0,0 +1 @@ +# UserScript Builder diff --git a/utils/builder/package.json b/utils/builder/package.json new file mode 100644 index 0000000..e5c81dc --- /dev/null +++ b/utils/builder/package.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "name": "@userjs/builder", + "description": "Create a UserScript", + "version": "1.0.0", + "license": "MIT", + "type": "module", + "main": "./src/index.js", + "types": "./typings/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./typings/index.d.ts", + "default": "./src/index.js" + } + } + }, + "directories": { + "lib": "src" + }, + "files": [ + "src", + "typings" + ], + "dependencies": { + "@userjs/i18n": "workspace:^", + "minimist": "^1.2.8", + "sass-embedded": "^1.85.1", + "watchpack": "^2.4.2" + } +} diff --git a/utils/builder/src/index.d.ts b/utils/builder/src/index.d.ts new file mode 100644 index 0000000..1ca7e43 --- /dev/null +++ b/utils/builder/src/index.d.ts @@ -0,0 +1,56 @@ +import { PathLike } from 'node:fs'; + +/** + * Loads all the structures in the provided directory. + * + * @param dir - The directory to load the structures from + * @param recursive - Whether to recursively load the structures in the directory + */ +export declare function loadLanguages(dir: PathLike, recursive?: boolean): Promise; +export declare function build(): Promise; +export interface UserJS { + name: string; + description: string; + version: string; + license?: string; + bugs?: URL; + homepage?: URL; + icon?: PathLike; + downloadURL?: URL; + updateURL?: URL; + url_source?: URL; + url?: URL; + build: { + source: { + languageList: string; + [source: string]: PathLike; + }; + watch: { + files: string[]; + dirs: PathLike[]; + }; + paths: { + fileName: string; + dir: PathLike; + dev?: { + fileName?: string; + dir?: PathLike; + }; + }; + }; + metadata: { + compatible: string[]; + connect: string[]; + grant: string[]; + exclude: string[]; + include: string[]; + 'exclude-match': string[]; + match: string[]; + noframes: boolean; + resource: { + [name: string]: string; + }; + require: string[]; + 'run-at': 'document-start' | 'document-body' | 'document-end' | 'document-idle'; + }; +} diff --git a/utils/builder/src/index.js b/utils/builder/src/index.js new file mode 100644 index 0000000..07195d3 --- /dev/null +++ b/utils/builder/src/index.js @@ -0,0 +1,400 @@ +'use strict'; +import path from 'path'; +import { URL } from 'node:url'; +import fs from 'node:fs'; +import Watchpack from 'watchpack'; +import minimist from 'minimist'; +import { loadLanguages } from '@userjs/i18n'; +import { compile } from 'sass-embedded'; + +/** + * @typedef { import('../typings/index.d.ts').UserJS } CFG + */ + +const replaceTemplate = /\[\[(.*?)\]\]/g; +const metaStr = '[[metadata]]'; + +const log = (...msg) => { + console.log('[LOG]', ...msg); +}; +const err = (...msg) => { + console.error('[ERROR]', ...msg); +}; +/** + * Object is typeof `object` / JSON Object + * @template O + * @param { O } obj + * @returns { boolean } + */ +const isObj = (obj) => { + /** @type { string } */ + const s = Object.prototype.toString.call(obj); + return s.includes('Object'); +}; +/** + * Object is `null` or `undefined` + * @template O + * @param { O } obj + * @returns { boolean } + */ +const isNull = (obj) => { + return Object.is(obj, null) || Object.is(obj, undefined); +}; +/** + * Object is Blank + * @template O + * @param { O } obj + * @returns { boolean } + */ +const isBlank = (obj) => { + return ( + (typeof obj === 'string' && Object.is(obj.trim(), '')) || + ((obj instanceof Set || obj instanceof Map) && Object.is(obj.size, 0)) || + (Array.isArray(obj) && Object.is(obj.length, 0)) || + (isObj(obj) && Object.is(Object.keys(obj).length, 0)) + ); +}; +/** + * Object is Empty + * @template O + * @param { O } obj + * @returns { boolean } + */ +const isEmpty = (obj) => { + return isNull(obj) || isBlank(obj); +}; +/** + * @template { string | { msg: string; } } T + * @template D + * @param { T } template + * @param { D } data + */ +const nano = (template, data) => { + if (typeof template === 'string') { + return template.replace(replaceTemplate, (_match, key) => { + const keys = key.split('.'); + let v = data[keys.shift()]; + for (const i in keys.length) v = v[keys[i]]; + return isEmpty(v) ? '' : v; + }); + } + return ''; +}; +/** + * @param {import('node:fs').PathLike} filePath + * @param {string} encoding + */ +const canAccess = async (filePath, encoding = 'utf-8') => { + const testAccess = await fs.promises.access( + filePath, + fs.promises.constants.R_OK | fs.promises.constants.W_OK + ); + if (isNull(testAccess)) { + const data = await fs.promises.readFile(filePath, encoding); + return data.toString(encoding); + } + return { + msg: `Cannot access provided filePath: ${filePath}` + }; +}; +/** + * @param {import('node:fs').PathLike} filePath + * @param {string} encoding + */ +const fileToJSON = async (filePath, encoding = 'utf-8') => { + const testAccess = await canAccess(filePath, encoding); + if (isObj(testAccess)) { + throw new Error(testAccess.msg); + } + return JSON.parse(testAccess); +}; +/** + * @param {import('node:fs').PathLike} destinationFilePath + * @param data + */ +const writeUserJS = async (destinationFilePath, data) => { + return await fs.promises.writeFile(destinationFilePath, data); +}; +const toTime = () => { + return new Intl.DateTimeFormat('default', { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + fractionalSecondDigits: 3 + }).format(new Date()); +}; +/** + * @type { Map } + */ +const dataMap = new Map(); +const compareArrays = (a, b) => + a.length === b.length && a.every((element, index) => element === b[index]); +/** + * @template {string} K + * @template V + * @param {K} key + * @param {...V} values + */ +const addTo = (key, ...values) => { + if (values.length === 0) { + return ''; + } + if (dataMap.has(key)) { + if (compareArrays(dataMap.get(key), values)) { + return dataMap.get(key); + } + } + dataMap.set(key, values); + return dataMap.get(key); +}; + +async function build() { + try { + const getEnv = () => { + const argv = minimist(process.argv.slice(2)); + if (argv.JS_ROOT && argv.JS_ENV) { + return { + JS_ROOT: argv.JS_ROOT, + JS_ENV: argv.JS_ENV, + JS_i18n: `${argv.JS_ROOT}src/_locales` + }; + } + return process.env; + }; + const { JS_ENV, JS_i18n } = getEnv(); + const jsonRecords = await Promise.all([ + // fileToJSON('./package.json').then(({ userJS }) => { + // return userJS ?? {}; + // }), + fileToJSON('./UserJS.json') + .then((userJS) => { + return userJS ?? {}; + }) + .catch(() => { + return {}; + }) + ]); + if (isEmpty(jsonRecords)) + throw new Error('"UserJS.json" not found + no "userJS" key in package.json'); + /** + * @type { CFG } + */ + const userJS = {}; + for (const r of jsonRecords) Object.assign(userJS, r); + const isDev = isEmpty(JS_ENV) || JS_ENV === 'development'; + const buildUserJS = async () => { + try { + const i18n = userJS.build.paths.i18n; + const i18nList = await loadLanguages(new URL(i18n.dir ?? JS_i18n, import.meta.url)); + const compileLanguage = (type = 'userjs_name') => { + const resp = []; + for (const [key, obj] of i18nList.entries()) { + const value = obj[type]; + if (!value || isEmpty(value) || key.startsWith(i18n.default)) { + continue; + } + const t = type.toLowerCase().replace('userjs_', ''); + if (type === 'userjs_name') { + resp.push( + `// @${t}:${key.replace('_', '-')} ${isDev ? '[Dev] ' : ''}${value}` + ); + } else { + resp.push(`// @${t}:${key.replace('_', '-')} ${value}`); + } + } + return resp; + }; + const compileData = () => { + if (isDev && !isEmpty(dataMap)) { + addTo('version', `// @version ${+new Date()}`); + return `// ==UserScript==\n${[...dataMap.values()].flat().join('\n')}\n// ==/UserScript==`; + } + for (const [k, v] of Object.entries(userJS)) { + if (k === 'metadata') { + const metaData = []; + for (const [key, value] of Object.entries(v)) { + if (Array.isArray(value)) { + for (const v of value) { + metaData.push(`// @${key} ${v}`); + } + } else if (isObj(value)) { + for (const [k, v] of Object.entries(value)) { + metaData.push(`// @${key} ${k} ${v}`); + } + } else if (typeof value === 'boolean') { + if (value === true) { + metaData.push(`// @${key}`); + } + } else { + metaData.push(`// @${key} ${value}`); + } + } + addTo('metaData', metaData.join('\n')); + } + if (typeof v !== 'string') { + continue; + } + if (k === 'name') { + addTo( + k, + `// @${k} ${isDev ? '[Dev] ' : ''}${v}`, + ...compileLanguage('userjs_name') + ); + } else if (k === 'description') { + addTo(k, `// @${k} ${v}`, ...compileLanguage('userjs_description')); + } else if (k === 'author') { + addTo(k, `// @${k} ${v}`); + } else if (k === 'icon') { + if (v.startsWith('.') || v.startsWith('/')) { + const buff = new Buffer.from(fs.readFileSync(v)); + const base64data = buff.toString('base64'); + if (v.endsWith('.png')) { + addTo(k, `// @${k} data:image/png;base64,${base64data}`); + } else if (v.endsWith('.svg')) { + addTo(k, `// @${k} data:image/svg+xml;base64,${base64data}`); + } + } else { + addTo(k, `// @${k} ${v}`); + } + } else if (k === 'downloadURL') { + addTo(k, `// @downloadURL ${v}`); + } else if (k === 'updateURL') { + addTo(k, `// @updateURL ${v}`); + } else if (k === 'url_source') { + addTo(k, `// @url_source ${v}`); + } else if (k === 'url') { + addTo(k, `// @downloadURL ${v}`, `// @updateURL ${v}`); + } else if (k === 'version') { + addTo(k, `// @${k} ${isDev ? +new Date() : v}`); + } else if (k === 'homepage') { + addTo(k, `// @namespace ${v}`, `// @homepageURL ${v}`); + } else if (k === 'bugs') { + addTo(k, `// @supportURL ${v}`); + } else if (k === 'license') { + addTo(k, `// @${k} ${v}`); + } else { + addTo(k, v); + } + } + return `// ==UserScript==\n${[...dataMap.values()].flat().join('\n')}\n// ==/UserScript==`; + }; + const cfg = { + nano: { + languageList: {}, + metadata: compileData() + }, + path: { + fileName: 'main-userjs', + dir: './dist' + }, + file: '', + meta: '', + metaPath: '' + }; + for (const [k, v] of Object.entries(userJS.build.source)) { + if (/\.s[ac]ss$/i.test(v)) { + cfg.nano[k] = compile(v, { + sourceMap: false, + style: 'expanded' + }).css; + continue; + } + const f = await canAccess(v); + if (typeof f !== 'string') continue; + if (k === 'metadata') { + cfg.meta = f; + } else { + cfg.nano[k] = f; + } + } + if (!isEmpty(i18nList)) { + for (const [k, obj] of i18nList.entries()) { + const o = {}; + for (const [key, value] of Object.entries(obj)) { + if (isEmpty(value)) { + continue; + } + if (/^ext[A-Z_]|^userjs_(name|description)/.test(key)) { + continue; + } + o[key] = value; + } + cfg.nano.languageList[k] = o; + } + } + for (const [k, v] of Object.entries(userJS.build.paths)) { + if (isEmpty(v)) continue; + if (isDev && /dev/i.test(k)) { + for (const [key, value] of Object.entries(v)) { + cfg.path[key] = value; + } + } else { + cfg.path[k] = v; + } + } + cfg.file = `${cfg.path.dir}/${cfg.path.fileName}.user.js`; + cfg.metaPath = `${cfg.path.dir}/${cfg.path.fileName}.meta.js`; + cfg.nano.languageList = JSON.stringify(cfg.nano.languageList, null, ' '); + + await writeUserJS(cfg.file, nano(cfg.meta, cfg.nano)); + log('UserJS File:', { + path: cfg.file, + time: toTime() + }); + if (!isDev) { + await writeUserJS( + cfg.metaPath, + nano(metaStr, { + metadata: cfg.nano.metadata + }) + ); + log('UserJS Metadata:', { + path: cfg.metaPath, + time: toTime() + }); + } + } catch (ex) { + err(ex); + } + }; + log(`Node ENV: ${JS_ENV}`); + await buildUserJS(); + if (isDev) { + const wp = new Watchpack(); + let changed = new Set(); + if (isEmpty(userJS.build.watch.files)) { + wp.watch(userJS.build.watch.dirs); + } else { + wp.watch(userJS.build.watch.files, userJS.build.watch.dirs); + } + wp.on('change', (changedFile, mtime) => { + if (mtime === null) { + changed.delete(changedFile); + } else { + changed.add(changedFile); + } + }); + wp.on('aggregated', async () => { + // Filter out files that start with a dot from detected changes + // (as they are hidden files or temp files created by an editor). + const changes = Array.from(changed).filter((filePath) => { + return !path.basename(filePath).startsWith('.'); + }); + changed = new Set(); + + if (changes.length === 0) { + return; + } + + await buildUserJS(); + }); + } else { + process.exit(0); + } + } catch (ex) { + err(ex); + } +} + +export { build }; +export default build; diff --git a/utils/builder/typings/index.d.ts b/utils/builder/typings/index.d.ts new file mode 100644 index 0000000..b60fd30 --- /dev/null +++ b/utils/builder/typings/index.d.ts @@ -0,0 +1,54 @@ +import { PathLike } from 'node:fs'; + +export interface UserJS { + name: string; + description: string; + version: string; + license?: string; + bugs?: URL; + homepage?: URL; + icon?: PathLike; + downloadURL?: URL; + updateURL?: URL; + url_source?: URL; + url?: URL; + build: { + source: { + languageList: string; + [source: string]: PathLike; + }; + watch: { + files: string[]; + dirs: PathLike[]; + }; + paths: { + fileName: string; + dir: PathLike; + i18n?: { + default: string; + dir: PathLike; + }; + dev?: { + fileName?: string; + dir?: PathLike; + }; + }; + }; + metadata: { + compatible: string[]; + connect: string[]; + grant: string[]; + exclude: string[]; + include: string[]; + 'exclude-match': string[]; + match: string[]; + noframes: boolean; + resource: { + [name: string]: string; + }; + require: string[]; + 'run-at': 'document-start' | 'document-body' | 'document-end' | 'document-idle'; + }; +} + +export declare function build(): Promise; diff --git a/utils/i18n/README.md b/utils/i18n/README.md new file mode 100644 index 0000000..90c84e3 --- /dev/null +++ b/utils/i18n/README.md @@ -0,0 +1 @@ +# UserScript i18n diff --git a/utils/i18n/package.json b/utils/i18n/package.json new file mode 100644 index 0000000..65555d5 --- /dev/null +++ b/utils/i18n/package.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "name": "@userjs/i18n", + "description": "Loads languages", + "version": "1.0.0", + "license": "MIT", + "type": "module", + "main": "./src/index.js", + "types": "./typings/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./typings/index.d.ts", + "default": "./src/index.js" + } + } + }, + "directories": { + "lib": "src" + }, + "files": [ + "src", + "typings" + ] +} diff --git a/utils/i18n/src/index.js b/utils/i18n/src/index.js new file mode 100644 index 0000000..ba664f6 --- /dev/null +++ b/utils/i18n/src/index.js @@ -0,0 +1,54 @@ +'use strict'; +import { readdir, readFile, stat } from 'node:fs/promises'; +import { URL, fileURLToPath } from 'node:url'; + +/** + * + * @type { import('../typings/index.d.ts').loadLanguages } + */ +async function loadLanguages(dir, recursive = true, mapper = new Map()) { + try { + // Get the stats of the directory + const statDir = await stat(dir); + + // If the provided directory path is not a directory, throw an error + if (!statDir.isDirectory()) { + throw new Error(`The directory '${dir}' is not a directory.`); + } + + // Get all the files in the directory + const files = await readdir(dir); + + // Loop through all the files in the directory + for (const file of files) { + // Get the stats of the file + const statFile = await stat(new URL(`${dir}/${file}`)); + + // If the file is a directory and recursive is true, recursively load the structures in the directory + if (statFile.isDirectory() && recursive) { + await loadLanguages(new URL(`${dir}/${file}`), recursive, mapper); + continue; + } + + if (!file.endsWith('.json')) { + continue; + } + const filePath = fileURLToPath(`${dir}/${file}`); + const reg = /_locales\\(.*?)\\messages\.json/g.exec(filePath); + if (reg) { + const structure = await readFile(filePath, 'utf-8'); + const obj = {}; + for (const [k, v] of Object.entries(JSON.parse(structure.toString('utf-8')))) + obj[k] = v.message ?? ''; + mapper.set(reg[1], obj); + } + } + } catch (ex) { + console.error(ex); + } + + return mapper; +} + +export { loadLanguages }; +export default loadLanguages; diff --git a/utils/i18n/typings/index.d.ts b/utils/i18n/typings/index.d.ts new file mode 100644 index 0000000..046f1f2 --- /dev/null +++ b/utils/i18n/typings/index.d.ts @@ -0,0 +1,9 @@ +import { PathLike } from 'node:fs'; + +/** + * Loads all the structures in the provided directory. + * + * @param dir - The directory to load the structures from + */ +export declare function loadLanguages(dir: PathLike, recursive?: boolean): Promise>; +// export declare function loadLanguages(dir: PathLike, recursive?: boolean): Promise; diff --git a/utils/user.js/README.md b/utils/user.js/README.md new file mode 100644 index 0000000..b69b879 --- /dev/null +++ b/utils/user.js/README.md @@ -0,0 +1 @@ +# UserScript Builder diff --git a/utils/user.js/package.json b/utils/user.js/package.json new file mode 100644 index 0000000..47fb61a --- /dev/null +++ b/utils/user.js/package.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "name": "user.js", + "description": "Create a UserScript", + "version": "1.0.0", + "license": "MIT", + "type": "module", + "main": "./src/index.js", + "types": "./typings/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./typings/index.d.ts", + "default": "./src/index.js" + } + } + }, + "directories": { + "lib": "src" + }, + "files": [ + "src", + "typings" + ], + "dependencies": { + "@userjs/builder": "workspace:^", + "@userjs/i18n": "workspace:^" + } +} diff --git a/utils/user.js/src/index.js b/utils/user.js/src/index.js new file mode 100644 index 0000000..f8f2a95 --- /dev/null +++ b/utils/user.js/src/index.js @@ -0,0 +1,6 @@ +'use strict'; +import { build } from '@userjs/builder'; +import { loadLanguages } from '@userjs/i18n'; + +export { build, loadLanguages }; +export default build; diff --git a/utils/user.js/typings/index.d.ts b/utils/user.js/typings/index.d.ts new file mode 100644 index 0000000..f8f2a95 --- /dev/null +++ b/utils/user.js/typings/index.d.ts @@ -0,0 +1,6 @@ +'use strict'; +import { build } from '@userjs/builder'; +import { loadLanguages } from '@userjs/i18n'; + +export { build, loadLanguages }; +export default build; diff --git a/wiki/Build.md b/wiki/Build.md new file mode 100644 index 0000000..4d1ef16 --- /dev/null +++ b/wiki/Build.md @@ -0,0 +1,84 @@ +# Build + +Install [Node.js](https://nodejs.org/) and [pnpm](https://pnpm.io/) - *the version of Node.js should match or be greater than the `"node"` key in `package.json`.* + +```sh +# Install dependencies +$ pnpm i +``` + +**UserScript:** + +Before you begin, rename `.env.example` to `.env` + +**Recommened Testing Environments:** + +- **Desktop:** any browser with [ViolentMonkey](https://violentmonkey.github.io/), see [How to edit scripts with your favorite editor?](https://violentmonkey.github.io/posts/how-to-edit-scripts-with-your-favorite-editor/) +- **Mobile:** + - Desktop: use "Responsive Design Mode (Ctrl+Shift+M)" on [FireFox Developer Edition](https://www.mozilla.org/firefox/developer/) or [FireFox](https://www.mozilla.org/firefox/) + - Android: any browser with UserScript support or support for installing a UserScript manager, I use [Cromite](https://github.com/uazo/cromite) + - IOS: any browser with UserScript support or support for installing a UserScript manager + +```sh +# Watch and build to local HTTP server +$ pnpm run dev:UserJS + +# Build UserScript to "./dist" +$ pnpm run pub:UserJS +``` + +--- + +**WebExtension:** + +```sh +# Build WebExtension (developer version) for "Chromium" to "./build/chrome" +$ pnpm run dev:Cr +# Build WebExtension (developer version) for "Firefox" to "./build/firefox" +$ pnpm run dev:FF +# Build WebExtension (public version) for "Chromium" to "./build/chrome" +$ pnpm run webpack:Cr +# Build WebExtension (public version) for "Firefox" to "./build/firefox" +$ pnpm run webpack:FF + +# [ Testing ] +# Browsers can be launched via "Run and Debug" in VSCode. +# Edit runtimeExecutable in ".vscode/launch.json" to your Chrome executable. +# Recommended to create an additional profile "about:profiles" for Firefox. +``` + +## Contribution Rules + +**General:** + +Follow [Semantic Versioning](https://semver.org/) standards, if your unsure you don't need to update the version + +--- + +**Translations:** + +Translations follow [Web Extension Internationalization](https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/Internationalization) standards + +--- + +**UserScript:** + +Do not edit the UserScript found in the `./dist` directory, the file is auto generated from `dev:UserJS` command. Instead edit it from `./src/UserJS` + +## Contribution Guide + +Fork this repo, make your changes, and open a [pull request](https://github.com/magicoflolis/Userscript-Plus/pulls) + +**Translations:** + +Translations are located in `./src/_locales` + +For versioning translations are a PATCH + +**Adding:** + +Recommend using `./src/_locales/en/messages.json` as a template + +**Updating:** + +Update any key from `messages.json` file diff --git a/wiki/README.md b/wiki/README.md new file mode 100644 index 0000000..daf8727 --- /dev/null +++ b/wiki/README.md @@ -0,0 +1,146 @@ +# Wiki Page + +- [Wiki Page](#wiki-page) + - [GitHub Detection](#github-detection) + - [Shortcuts](#shortcuts) + +## GitHub Detection + +For this Web Extension / UserScript to detect your UserScript or UserStyle: + +- Your repository must have the following: + - UserScripts **must** be in `.user.js` format + - UserStyles **must** be in `.user.css` format + - In the Topics section of your repository: + - `domain` or `hostname`: + - example: you create a userscript for `google.com` then put `google` in the Topics section + - if you match all sites: `*` or `http*://*` then put `all-sites` in the Topics section + - put `userscript` or `userstyle` in the Topics section + +_if you need to create a build environment, you can use my workspace and tools as a template._ + +## Shortcuts + +**New Tab:** + +Syntax: `*` - Shortcut for userscipts that apply to `All sites` + +Syntax: `` + +```txt +# Example +google.com +``` + +--- + +Syntax: `mujs:` + +```txt +# Available +settings + +# Example +mujs:settings +``` + +--- + +**Search Bar:** + +By default it will search by name and description, you can refine your searches using the examples listed below. + +Syntax: `filter:` + +```txt +# Available +ascii +latin +games +socialnetworks +clutter + +# Example +filter:ascii +``` + +--- + +Syntax: `code_url:` or `url:` + +```txt +# Example +code_url:google.com +``` + +--- + +Syntax: `author:` or `users:` + +```txt +# Example +author:magicoflolis +``` + +--- + +Syntax: `locale:` or `i18n:` + +```txt +# Example +locale:en +``` + +--- + +Syntax: `id:` + +```txt +# Example +id:421603 +``` + +--- + +Syntax: `license:` + +```txt +# Example +license:MIT +``` + +--- + +Syntax: `name:` + +```txt +# Example +name:Userscript+ +``` + +--- + +Syntax: `description:` + +```txt +# Example +description:Finds available userscripts for the current webpage. +``` + +--- + +Syntax: `search_engine:` or `engine:` + +```txt +# Example +search_engine:greasyfork +``` + +--- + +Syntax: `recommend:` + +```txt +# Example +recommend: +```