diff --git a/.oxlintrc.json b/.oxlintrc.json
new file mode 100644
index 0000000..8f01c51
--- /dev/null
+++ b/.oxlintrc.json
@@ -0,0 +1,377 @@
+{
+ "plugins": [
+ "unicorn",
+ "typescript",
+ "oxc",
+ "jsdoc",
+ "promise",
+ "eslint"
+ ],
+ "categories": {},
+ "rules": {
+ /*
+ "ruleName": "severity"
+ OR
+ "ruleName": ["severity", { "ruleOption": "value" (boolean and numeric values are unquoted) }]
+ where "severity" is one of 'off'/0/'allow' or 'error'/2/'deny' or 'warn'/1
+ */
+ "accessor-pairs": "warn",
+// "array-callback-return": [ "warn", {"checkForEach": true } ],
+ "array-callback-return": "warn",
+ "block-scoped-var": "warn",
+ "complexity": "warn",
+ "for-direction": "warn",
+ "getter-return": [ "warn", { "allowImplicit": true } ],
+ "no-async-promise-executor": "warn",
+ "no-await-in-loop": "warn",
+ "no-caller": "warn",
+ "no-class-assign": "warn",
+ "no-compare-neg-zero": "warn",
+ "no-cond-assign": "warn",
+ "no-const-assign": "warn",
+ "no-constant-binary-expression": "warn",
+ "no-constant-condition": "warn",
+ "no-control-regex": "warn",
+ "no-debugger": "warn",
+ "no-delete-var": "warn",
+ "no-dupe-class-members": "warn",
+ "no-dupe-else-if": "warn",
+ "no-dupe-keys": "warn",
+ "no-duplicate-case": "warn",
+ "no-empty-character-class": "warn",
+ "no-empty-pattern": "warn",
+ "no-empty-static-block": "warn",
+ "no-eval": "warn",
+ "no-ex-assign": "warn",
+// "no-extend-native": "warn",
+ "no-extra-bind": "warn",
+ "no-extra-boolean-cast": "warn",
+ "no-fallthrough": "warn",
+ "no-func-assign": "warn",
+ "no-global-assign": "warn",
+ "no-import-assign": "warn",
+// "no-implicit-coercion": "warn",
+ "no-invalid-regexp": "warn",
+ "no-irregular-whitespace": "warn",
+ "no-loop-func": "error",
+ "no-loss-of-precision": "warn",
+ "no-new": "warn",
+ "no-new-native-nonconstructor": "warn",
+ "no-nonoctal-decimal-escape": "warn",
+ "no-obj-calls": "warn",
+ "no-promise-executor-return": "warn",
+ "no-regex-spaces": "warn",
+ "no-return-assign": "warn",
+ "no-self-assign": "warn",
+ "no-sequences": [ "warn", { "allowInParentheses": false } ],
+ "no-setter-return": "warn",
+ "no-shadow": "warn",
+ "no-shadow-restricted-names": "warn",
+ "no-sparse-arrays": "warn",
+ "no-this-before-super": "warn",
+ "no-unassigned-vars": "warn",
+ "no-unexpected-multiline": "warn",
+ "no-unneeded-ternary": "warn",
+ "no-unsafe-finally": "warn",
+ "no-unsafe-negation": "warn",
+ "no-unsafe-optional-chaining": "warn",
+ "no-unused-expressions": "warn",
+ "no-unused-labels": "warn",
+ "no-unused-private-class-members": "warn",
+ "no-unused-vars": "warn",
+ "no-useless-backreference": "warn",
+ "no-useless-call": "warn",
+ "no-useless-catch": "warn",
+ "no-useless-computed-key": "warn",
+ "no-useless-concat": "warn",
+ "no-useless-constructor": "warn",
+ "no-useless-escape": "warn",
+ "no-useless-rename": "warn",
+ "no-with": "warn",
+// "prefer-const": "off",
+ "preserve-caught-error": "warn",
+ "require-await": "warn", // Under consideration
+ "require-yield": "warn",
+ "use-isnan": "warn",
+ "valid-typeof": "warn",
+ "import/no-nodejs-modules": "warn",
+ "import/no-relative-parent-imports": "warn",
+ "jsdoc/check-property-names": "warn",
+ "jsdoc/check-tag-names": "warn",
+ "jsdoc/implements-on-classes": "warn",
+ "jsdoc/no-defaults": "off",
+ "jsdoc/require-property": "warn",
+ "jsdoc/require-property-description": "warn",
+ "jsdoc/require-property-name": "warn",
+ "jsdoc/require-property-type": "warn",
+ "jsdoc/require-yields": "warn",
+ "oxc/approx-constant": "warn",
+ "oxc/bad-array-method-on-arguments": "error",
+ "oxc/bad-bitwise-operator": "error",
+ "oxc/bad-char-at-comparison": "error",
+ "oxc/bad-comparison-sequence": "warn",
+ "oxc/bad-min-max-func": "warn",
+ "oxc/bad-object-literal-comparison": "error",
+ "oxc/bad-replace-all-arg": "warn",
+ "oxc/branches-sharing-code": "warn",
+ "oxc/const-comparisons": "error",
+ "oxc/double-comparisons": "error",
+ "oxc/erasing-op": "warn",
+ "oxc/misrefactored-assign-op": "warn",
+ "oxc/missing-throw": "warn",
+ "oxc/no-accumulating-spread": "warn",
+ "oxc/no-async-endpoint-handlers": "warn",
+ "oxc/no-barrel-file": [ "warn", { "threshold": 100 } ],
+ "oxc/no-const-enum": "error",
+ "oxc/no-map-spread": "error",
+ "oxc/no-this-in-exported-function": "warn",
+ "oxc/number-arg-out-of-range": "warn",
+ "oxc/only-used-in-recursion": "error",
+ "oxc/uninvoked-array-callback": "error",
+ "promise/always-return": "warn",
+ "promise/no-callback-in-promise": "warn",
+ "promise/no-multiple-resolved": "warn",
+ "promise/no-new-statics": "warn",
+ "promise/no-promise-in-callback": "warn",
+// "promise/prefer-await-to-then": "off", // I kind of wish I could create a custom rule category to toggle rules like this...
+ "promise/prefer-catch": "warn",
+ "promise/spec-only": "warn",
+ "promise/valid-params": "warn",
+ "typescript/await-thenable": "warn",
+ "typescript/no-array-delete": "warn",
+ "typescript/no-base-to-string": "warn",
+// "typescript/no-confusing-non-null-assertion": "warn",
+ "typescript/no-duplicate-enum-values": "warn",
+ "typescript/no-duplicate-type-constituents": "warn",
+ "typescript/no-extra-non-null-assertion": "warn",
+// "typescript/no-extraneous-class": "warn",
+ "typescript/no-floating-promises": "warn",
+ "typescript/no-for-in-array": "warn",
+ "typescript/no-implied-eval": "warn",
+ "typescript/no-meaningless-void-operator": "warn",
+ "typescript/no-misused-new": "warn",
+ "typescript/no-misused-spread": "warn",
+ "typescript/no-non-null-asserted-optional-chain": "warn",
+ "typescript/no-redundant-type-constituents": "warn",
+ "typescript/no-this-alias": "warn",
+// "typescript/no-unnecessary-boolean-literal-compare": "warn",
+ "typescript/no-unnecessary-parameter-property-assignment": "warn",
+// "typescript/no-unnecessary-template-expression": "warn",
+// "typescript/no-unnecessary-type-arguments": "warn",
+// "typescript/no-unnecessary-type-assertion": "warn",
+// "typescript/no-unnecessary-type-constraint": "warn",
+ "typescript/no-unsafe-declaration-merging": "warn",
+// "typescript/no-unsafe-enum-comparison": "warn",
+// "typescript/no-unsafe-type-assertion": "warn",
+ "typescript/no-unsafe-unary-minus": "warn",
+ "typescript/no-useless-empty-export": "warn",
+ "typescript/no-wrapper-object-types": "warn",
+ "typescript/prefer-as-const": "warn",
+ "typescript/require-array-sort-compare": "warn",
+ "typescript/require-await": "warn",
+ "typescript/restrict-template-expressions": "warn",
+ "typescript/triple-slash-reference": "warn",
+ "typescript/unbound-method": "warn",
+ "unicorn/catch-error-name": [ "warn", { "ignore": ["e", "err", "error"] } ],
+ // "unicorn/consistent-assert": "warn", // NodeJS
+ "unicorn/consistent-date-clone": "warn",
+ "unicorn/consistent-empty-array-spread": "warn",
+ "unicorn/consistent-existence-index-check": "warn",
+ "unicorn/consistent-function-scoping": "warn", // Ignores IIFE!!!
+ "unicorn/custom-error-definition": "warn",
+ "unicorn/empty-brace-spaces": "error",
+ "unicorn/error-message": "warn",
+ "unicorn/escape-case": "error",
+ "unicorn/explicit-length-check": [ "error", { "non-zero": "not-equal" } ], // May change back to default option of "greater-than"; will have to see which I use more often.
+// "unicorn/filename-case": [ "warn", { "case": "snakeCase"} ],
+ "unicorn/new-for-builtins": "error",
+ "unicorn/no-abusive-eslint-disable": "error",
+ "unicorn/no-accessor-recursion": "warn",
+ "unicorn/no-anonymous-default-export": "warn",
+// "unicorn/no-array-callback-reference": "warn", // Under consideration
+// "unicorn/no-array-for-each": "warn", // Another situation where I'd love a custom rule category.
+ "unicorn/no-array-method-this-argument": "error",
+ "unicorn/no-array-reverse": [ "warn", { "allowExpressionStatement": true } ],
+// "unicorn/no-array-reduce": "warn", // Under consideration
+ "unicorn/no-array-sort": [ "warn", { "allowExpressionStatement": true } ],
+ "unicorn/no-await-in-expression-member": "error",
+ "unicorn/no-await-in-promise-methods": "warn",
+ "unicorn/no-console-spaces": "warn",
+ "unicorn/no-document-cookie": "warn",
+ "unicorn/no-empty-file": "warn",
+ "unicorn/no-hex-escape": "off", // I want this to support checking only for consecutive hex escapes; '\x1b[32m' is perfectly fine by me, but '\x68\x65\x78' is not, especially when it might represent a multi-byte character.
+ "unicorn/no-immediate-mutation": "warn",
+ "unicorn/no-instanceof-array": "error",
+ "unicorn/no-instanceof-builtins": "warn",
+ "unicorn/no-invalid-fetch-options": "warn",
+ "unicorn/no-invalid-remove-event-listener": "warn",
+ "unicorn/no-length-as-slice-end": "warn",
+ "unicorn/no-lonely-if": "warn",
+ "unicorn/no-negation-in-equality-check": "warn",
+ "unicorn/no-nested-ternary": "warn",
+ "unicorn/no-new-array": "warn",
+// "unicorn/no-new-buffer": "error", // NodeJS
+ "unicorn/no-null": "off", // undefined means "idfk", null means "I know, it's nothing"
+ "unicorn/no-magic-array-flat-depth": "warn",
+ "unicorn/no-object-as-default-parameter": "warn",
+// "unicorn/no-process-exit": "error", // NodeJS
+ "unicorn/no-single-promise-in-promise-methods": "warn",
+ "unicorn/no-static-only-class": "warn",
+ "unicorn/no-thenable": "warn",
+ "unicorn/no-this-assignment": "warn",
+ "unicorn/no-typeof-undefined": "warn",
+ "unicorn/no-unnecessary-array-flat-depth": "warn",
+ "unicorn/no-unnecessary-array-splice-count": "warn",
+ "unicorn/no-unnecessary-await": "warn",
+ "unicorn/no-unnecessary-slice-end": "warn",
+ "unicorn/no-unreadable-array-destructuring": "off",
+ "unicorn/no-unreadable-iife": "warn",
+ "unicorn/no-useless-collection-argument": "warn",
+ "unicorn/no-useless-error-capture-stack-trace": "warn",
+ "unicorn/no-useless-fallback-in-spread": "warn",
+ "unicorn/no-useless-length-check": "warn",
+ "unicorn/no-useless-promise-resolve-reject": "warn",
+ "unicorn/no-useless-spread": "warn",
+ "unicorn/no-useless-switch-case": "warn",
+// "unicorn/no-useless-undefined": "warn",
+ "unicorn/no-zero-fractions": "warn",
+ "unicorn/number-literal-case": "warn",
+ "unicorn/numeric-separators-style": [ "warn", {
+ "onlyIfContainsSeparator": false,
+ "binary": { "groupLength": 4, "minimumDigits": 8 },
+ "number": { "groupLength": 3, "minimumDigits": 5 },
+ }
+ ],
+ "unicorn/prefer-add-event-listener": "warn",
+ "unicorn/prefer-array-find": "warn",
+ "unicorn/prefer-array-flat": "warn",
+ "unicorn/prefer-array-flat-map": "warn",
+ "unicorn/prefer-array-index-of": "warn",
+ "unicorn/prefer-array-some": "warn",
+ "unicorn/prefer-at": "warn",
+// "unicorn/prefer-bigint-literals": "warn",
+ "unicorn/prefer-blob-reading-methods": "warn",
+ "unicorn/prefer-classlist-toggle": "off",
+ "unicorn/prefer-code-point": "warn",
+ "unicorn/prefer-date-now": "warn",
+ "unicorn/prefer-default-parameters": "warn",
+ "unicorn/prefer-dom-node-append": "warn",
+ "unicorn/prefer-dom-node-dataset": "off",
+ "unicorn/prefer-dom-node-remove": "warn",
+ "unicorn/prefer-dom-node-text-content": "warn",
+ "unicorn/prefer-event-target": "warn",
+// "unicorn/prefer-globalThis": "warn",
+ "unicorn/prefer-includes": "warn",
+ "unicorn/prefer-keyboard-event-key": "warn",
+ "unicorn/prefer-logical-operator-over-ternary": "warn",
+ "unicorn/prefer-math-min-max": "warn",
+ "unicorn/prefer-math-trunc": "warn",
+// "unicorn/prefer-modern-dom-apis": "warn", // Eventually
+ "unicorn/prefer-modern-math-apis": "warn",
+ "unicorn/prefer-module": "warn",
+ "unicorn/prefer-native-coercion-functions": "warn",
+ "unicorn/prefer-negative-index": "warn",
+// "unicorn/prefer-node-protocol": "error", // NodeJS
+// "unicorn/prefer-number-properties": "warn", // Eventually
+ "unicorn/prefer-object-from-entries": "warn",
+ "unicorn/prefer-optional-catch-binding": "warn",
+ "unicorn/prefer-prototype-methods": "error",
+ "unicorn/prefer-query-selector": "warn",
+ "unicorn/prefer-reflect-apply": "warn",
+ "unicorn/prefer-regexp-test": "warn",
+ "unicorn/prefer-response-static-json": "warn",
+ "unicorn/prefer-set-has": "warn",
+ "unicorn/prefer-set-size": "warn",
+// "unicorn/prefer-spread": "warn", // Under consideration
+ "unicorn/prefer-string-raw": "warn", // What the heck function call syntax is "functionName`argument`"???
+ "unicorn/prefer-string-replace-all": "warn",
+ "unicorn/prefer-string-slice": "warn",
+ "unicorn/prefer-string-starts-ends-with": "warn",
+ "unicorn/prefer-string-trim-start-end": "error",
+ "unicorn/prefer-structured-clone": "warn",
+// "unicorn/prefer-ternary": "warn",
+// "unicorn/prefer-top-level-await": "warn",
+ "unicorn/prefer-type-error": "error",
+ "unicorn/relative-url-style": "warn",
+ "unicorn/require-array-join-separator": "error",
+ "unicorn/require-module-attributes": "warn",
+ "unicorn/require-module-specifiers": "warn",
+ "unicorn/require-number-to-fixed-digits-argument": "error",
+ "unicorn/require-post-message-target-origin": "warn",
+// "unicorn/switch-case-braces": [ "warn", "always" ],
+ "unicorn/text-encoding-identifier-case": "warn",
+ "unicorn/throw-new-error": "error",
+ },
+ "settings": {
+ "jsx-a11y": {
+ "polymorphicPropName": null,
+ "components": {},
+ "attributes": {}
+ },
+ "next": {
+ "rootDir": []
+ },
+ "react": {
+ "formComponents": [],
+ "linkComponents": []
+ },
+ "jsdoc": {
+ "ignorePrivate": false,
+ "ignoreInternal": false,
+ "ignoreReplacesDocs": true,
+ "overrideReplacesDocs": true,
+ "augmentsExtendsReplacesDocs": false,
+ "implementsReplacesDocs": false,
+ "exemptDestructuredRootsFromChecks": false,
+ "tagNamePreference": {}
+ },
+ "vitest": {
+ "typecheck": false
+ }
+ },
+ "env": {
+ "builtin": true
+ },
+ "globals": {},
+ "ignorePatterns": [],
+ "extends": [
+// "localrules.json"
+ ],
+ "overrides": [
+ // REFERENCE: https://eslint.org/docs/v8.x/use/configure/language-options
+ // REFERENCE: https://oxc.rs/docs/guide/usage/linter/config-file-reference
+ { // userscripts
+ "files": ["**/*.user.js"],
+ "rules": {
+ // Always allow console api in userscripts
+ "no-console": "off",
+ // Doesn't understand that immediately-executed-functions are only ever called once, and just whines...
+ "unicorn/consistent-function-scoping": "off",
+ // As of March 9th, 2026, this assumes that the IIFE traditionally wrapping the userscript is a module,
+ // and complains about "use strict", which is redundant because modules are always strict.
+ "unicorn/prefer-module": "off",
+ // Portability
+ "unicorn/prefer-global-this": "warn",
+ // Too new
+ "oxc/no-optional-chaining": [ "error", { "message": "es2018 target does not support optional chaining operator"} ],
+ "typescript/prefer-nullish-coalescing": "off",
+ "typescript/prefer-optional-chain": "off",
+ "unicorn/prefer-at": "off",
+ "unicorn/prefer-negative-index": "off",
+ "unicorn/prefer-object-from-entries": "off",
+ "unicorn/prefer-string-replace-all": "off",
+ "unicorn/prefer-structured-clone": "off",
+ },
+ "env": {
+ "browser": true,
+ "greasemonkey": true,
+ "es2018": true,
+ "es2019": false
+ },
+ "globals": {
+ "GM": "writable",
+ "unsafeWindow": "writable"
+ }
+ }
+ ]
+}
diff --git a/README.md b/README.md
index d66bccd..e70307e 100644
--- a/README.md
+++ b/README.md
@@ -2,19 +2,17 @@
[](http://hits.dwyl.com/StaticPH/UserScripts)
Unless otherwise specified in their description, all userscripts have been tested with ViolentMonkey on Chromium 72 or later and on Vivaldi 3.6 or later.
They may also work with GreaseMonkey, TamperMonkey, or on other browsers.
+I try to specifically write code that will run on these targets, _regardless of the extent to which the page(s) they run on will remain functional under those conditions._
### Get ViolentMonkey for your browser
* [Firefox][ViolentMonkey_Firefox]
* [Google Chrome and (most) Chromium-based browsers][ViolentMonkey_Chrome]
* [Microsoft Edge (which people apparently use)][ViolentMonkey_Edge]
* Alternatively, install ViolentMonkey from its [source][ViolentMonkey_src]
-
+
### Other
-* If you use [Pale Moon](https://www.palemoon.org), [Basilisk](https://www.basilisk-browser.org), or [K-Meleon](http://kmeleonbrowser.org), try [this fork of GreaseMonkey v3][GreaseMonkey_v3_Moonchild].
+* If you use [Pale Moon](https://www.palemoon.org), [Basilisk](https://www.basilisk-browser.org), [K-Meleon](http://kmeleonbrowser.org), [Arctic Fox](https://github.com/rmottola/Arctic-Fox), [Serpent, New Moon](https://rtfreesoft.blogspot.com), or another browser using the Goanna browser engine, try [this fork of GreaseMonkey v3][GreaseMonkey_v3_Moonchild].
+* The [Hyperbola-fork of Iceweasel][Hyperbola_Iceweasel] has its own [fork of GreaseMonkey v3][GreaseMonkey_v3_Hyperbola_Iceweasel]
Page only available in Spanish as of November 16th, 2025
Some scripts will likely require modifications for this to work; if you do this yourself, please submit a pull request so that others may also benefit.
## Important note:
@@ -24,14 +22,11 @@ If your browsing habit doesn't involve much opening things in new tabs, you'll l
One example of such a scenario could be opening a Twitch.tv livestream from the directory view.
-
---
+
To add a script:
* Install a script directly from GitHub by clicking on the "install" link in the table below.
-
+
| Userscript
Description | Direct
Install | Sites | Supports
Auto-Update | License | Created | Updated |
|------------------------------------------------|:--------------------:|:-------------------:|:-----------------------:|:-------:|:----------:|:----------:|
@@ -41,11 +36,11 @@ To add a script:
| [Twitch Hide Content Disclosure](#THCD) | [install][raw-THCD] | N/A | :heavy_check_mark: | MIT | Jun 29, 2023 | Dec 12, 2024 |
| [Twitch Hide Channel Leaderboard](#THCL) | [install][raw-THCL] | N/A | :heavy_check_mark: | MIT | Jun 19, 2020 | Dec 12, 2024 |
| [Twitch Hide Overlay Ads](#THOA) | [install][raw-THOA] | N/A | :heavy_check_mark: | MIT | Dec 14, 2023 | Dec 12, 2024 |
-| [Twitch Transparent Video Stats](#TTVS) | [install][raw-TTVS] | N/A | :heavy_check_mark: | MIT | May 19, 2021 | Dec 12, 2024 |
-| [GitHub Repo Network Tab](#GRNT) | [install][raw-GRNT] | N/A | :heavy_check_mark: | MIT | Apr 6, 2020 | Feb 1, 2024 |
+| [Twitch Transparent Video Stats](#TTVS) | [install][raw-TTVS] | N/A | :heavy_check_mark: | MIT | May 19, 2021 | Feb 9, 2026 |
+| [GitHub Repo Network Tab](#GRNT) | [install][raw-GRNT] | N/A | :heavy_check_mark: | MIT | Apr 6, 2020 | Feb 27, 2026 |
| [Bigger GitHub Network Graph](#BGNG) | [install][raw-BGNG] | N/A | :heavy_check_mark: | MIT | Apr 12, 2020 | Oct 28, 2021 |
| [GitHub Notification Page Tweaks](#GNPT) | [install][raw-GNPT] | N/A | :heavy_check_mark: | MIT | Oct 22, 2020 | Aug 3, 2021 |
-| [GitHub Sticky Editor Header](#GSEH) | [install][raw-GSEH] | N/A | :heavy_check_mark: | MIT | Nov 24, 2021 | Nov 24, 2021 |
+| [GitHub Sticky Editor Header](#GSEH) | [install][raw-GSEH] | N/A | :heavy_check_mark: | MIT | Nov 24, 2021 | Apr 5, 2026 |
| [GitLab Description In Title](#GDIT) | [install][raw-GDIT] | N/A | :heavy_check_mark: | MIT | May 22, 2021 | Aug 3, 2021 |
| [Prettier Lib.rs Preformatted Code](#PLPC) | [install][raw-PLPC] | N/A | :heavy_check_mark: | MIT | Jul 5, 2020 | Mar 30, 2021 |
| [Lib.rs Description In Title](#LDIT) | [install][raw-LDIT] | N/A | :heavy_check_mark: | MIT | Apr 28, 2021 | May 11, 2021 |
@@ -54,41 +49,35 @@ To add a script:
| [Google Meet Ignore Hardware Disabled](#GMIHD) | [install][raw-GMIHD] | N/A | :heavy_check_mark: | MIT | Mar 3, 2023 | Mar 3, 2023 |
| [Wider Google Form Fields](#WGFF) | [install][raw-WGFF] | N/A | :heavy_check_mark: | MIT | Sep 30, 2021 | Aug 19, 2022 |
| [Correct Google Form Correctness](#GFCC) | [install][raw-GFCC] | N/A | :heavy_check_mark: | MIT | Nov 9, 2021 | Nov 9, 2021 |
-| [Google Search Lean Query Updates](#GSLQU) | [install][raw-GSLQU] | N/A | :heavy_check_mark: | MIT | Jul 12, 2023 | Sep 28, 2024 |
+| [Google Search Lean Query Updates](#GSLQU) | [install][raw-GSLQU] | N/A | :heavy_check_mark: | MIT | Jul 12, 2023 | Apr 5, 2026 |
+| [Google Search Footer Privacy](#GSFP) | [install][raw-GSFP] | N/A | :heavy_check_mark: | MIT | Dec 30, 2023 | Dec 16, 2025 |
| [Roll20 Nonscrolling Number Fields](#RNNF) | [install][raw-RNNF] | N/A | :heavy_check_mark: | MIT | Jan 23, 2021 | Apr 5, 2021 |
| [Bypass Blogspot's Blogger IFrame](#BBBI) | [install][raw-BBBI] | N/A | :heavy_check_mark: | MIT | Jun 2, 2021 | May 1, 2022 |
| [Foxaholic Fixes](#FoxF) | [install][raw-FoxF] | N/A | :heavy_check_mark: | MIT | Jun 2, 2021 | Aug 27, 2021 |
| [Mitigate Target \_blank Risk](#MTBR) | [install][raw-MTBR] | N/A | :heavy_check_mark: | MIT | Aug 27, 2021 | Nov 23, 2021 |
-| [MSYS2 Package Description In Title](#MDIT) | [install][raw-MDIT] | N/A | :heavy_check_mark: | MIT | Apr 28, 2021 | Sep 28, 2024 |
-| [Minecraft CurseForge Title Tweaks](#MCTT) | [install][raw-MCTT] | N/A | :heavy_check_mark: | MIT | Apr 20, 2022 | Jun 18, 2022 |
-| [Another "Open In Steam" Button](#OISB) | [install][raw-OISB] | N/A | :heavy_check_mark: | MIT | Nov 25, 2022 | Nov 25, 2022 |
+| [MSYS2 Package Description In Title](#MDIT) | [install][raw-MDIT] | N/A | :heavy_check_mark: | MIT | Apr 28, 2021 | Feb 9, 2026 |
+| [Minecraft CurseForge Title Tweaks](#MCTT) | [install][raw-MCTT] | N/A | :heavy_check_mark: | MIT | Apr 20, 2022 | Apr 5, 2026 |
+| [Another "Open In Steam" Button](#OISB) | [install][raw-OISB] | N/A | :heavy_check_mark: | MIT | Nov 25, 2022 | Apr 5, 2026 |
| [Ubuntu Packages Description In Title](#UPDIT) | [install][raw-UPDIT] | N/A | :heavy_check_mark: | MIT | May 11, 2023 | May 11, 2023 |
| [Quietly Reject StackExchange Cookies](#QRSC) | [install][raw-QRSC] | N/A | :heavy_check_mark: | MIT | May 14, 2023 | May 14, 2023 |
| [PyPI Description In Title](#PDIT) | [install][raw-PDIT] | N/A | :heavy_check_mark: | MIT | May 31, 2023 | Aug 15, 2024 |
-| [Simple URL Tracker Cleaner](#SUTC) | [install][raw-SUTC] | N/A | :heavy_check_mark: | MIT | Aug 10, 2021 | Jan 28, 2024 |
+| [Simple URL Tracker Cleaner](#SUTC) | [install][raw-SUTC] | N/A | :heavy_check_mark: | MIT | Aug 10, 2021 | Apr 5, 2026 |
| [Old Reddit Hide Posts By Sub](#ORHS) | [install][raw-ORHS] | N/A | :heavy_check_mark: | MIT | Apr 8, 2022 | Jul 2, 2023 |
| [ScribbleHub Reading List Upgrades](#SRLU) | [install][raw-SRLU] | N/A | :heavy_check_mark: | MIT | Oct 7, 2022 | Jan 19, 2024 |
| [NovelUpdates Reading List Upgrades](#NRLU) | [install][raw-NRLU] | N/A | :heavy_check_mark: | MIT | Jul 8, 2022 | Nov 16, 2022 |
| [Softpedia Improvements](#SFTPD) | [install][raw-SFTPD] | N/A | :heavy_check_mark: | MIT | Jan 4, 2024 | Jan 4, 2024 |
| [Sublime Package Control Description in Title](#SPCDIT) | [install][raw-SPCDIT] | N/A | :heavy_check_mark: | MIT | Jan 23, 2024 | Jan 23, 2024 |
| [YouTrack Use Standard Scrollbar](#YUSS) | [install][raw-YUSS] | N/A | :heavy_check_mark: | MIT | Aug 22, 2024 | Aug 22, 2024 |
+| [StackExchange Wide Mode](#SEWM) | [install][raw-SEWM] | N/A | :heavy_check_mark: | MIT | Jun 20, 2024 | Jun 20, 2024 |
+| [Better IzzyOnDroid App Titles](#BIAT) | [install][raw-BIAT] | N/A | :heavy_check_mark: | MIT | May 2, 2024 | May 22, 2025 |
+| [Better F-Droid App Titles](#BFAT) | [install][raw-BFAT] | N/A | :heavy_check_mark: | MIT | Aug 15, 2025 | Aug 15, 2025 |
+| [Better Greasyfork Page Titles](#BGPT) | [install][raw-BGPT] | N/A | :heavy_check_mark: | MIT | Nov 15, 2025 | Nov 15, 2025 |
+
-
-
---
-
-
---
@@ -294,6 +283,15 @@ Proof-of-concept: Prevent modifications to the Google search query in the on-pag
---
+
+### Google Search Footer Privacy
+
+Hide the "Location" part of the footer on Google Search results, and don't show the email address of the current user.
+
+[[Install]][raw-GSFP]
+
+---
+
### Roll20 Nonscrolling Number Fields
@@ -337,7 +335,7 @@ e.g. Setting `customAllowedOrigins` to `'http://wordpress.com https://stackexcha
### MSYS2 Package Description In Title
-Include the package description on the tab title for a package's page on packages.msys2.org/packages
+Include the package description on the tab title for a package's page on packages.msys2.org
[[Install]][raw-MDIT]
@@ -461,6 +459,42 @@ Use the browser's regular scrollbar style on JetBrains' YouTrack pages
[[Install]][raw-YUSS]
+---
+
+
+### StackExchange Wide Mode
+
+StackExchange sites should take better advantage of the horizontal screen space, particularly on question pages. This removes the width restrictions on the main content part of those pages.
+
+[[Install]][raw-SEWM]
+
+---
+
+
+### Better IzzyOnDroid App Titles
+
+Adds app description to page titles where possible.
+
+[[Install]][raw-BIAT]
+
+---
+
+
+### Better F-Droid App Titles
+
+Adds app description to page titles where possible.
+
+[[Install]][raw-BFAT]
+
+---
+
+
+### Better Greasyfork Page Titles
+
+Include userscript descriptions in page titles on Greasy Fork (and Sleazy Fork)
+
+[[Install]][raw-BGPT]
+
---
## Legacy Workaround Scripts
@@ -517,6 +551,15 @@ Multiple fixes related to user-downloadable asset files on GitHub for users of l
---
+
+### GitHub Show Issue Comments Workaround
+
+Manually display comments on Github issues using the JSON that's already on the page, which totally doesn't need React to accomplish.
+
+[[Install]][raw-GSICW]
+
+---
+
### StackExchange Legacy Comments Expander
@@ -534,8 +577,9 @@ Additionally, I do occasionally take requests for simple scripts, so feel free t
---
## Some of the awesome scripts I use from other authors
+I make no guarantees regarding the availability, maintenance, or browser version support of these scripts
- [Wide GitHub](https://github.com/xthexder/wide-github)
-- [GitHub Code Folding](https://openuserjs.org/scripts/Mottie/GitHub_Code_Folding)
+- [GitHub Code Folding](https://openuserjs.org/scripts/Mottie/GitHub_Code_Folding) (Note: last working version for Chrome 72 was v1.1.2, although subsequent changes to GitHub's UI have broken parts of that, too)
- [GitHub Gist Search Box](https://greasyfork.org/en/scripts/395318-github-gist-search-box)
- [GitHub Search Autocomplete](https://github.com/Mottie/GitHub-userscripts/wiki/GitHub-search-autocomplete)
- [GitHub - Add Path Search](https://gist.github.com/splintor/8d3f12b86962efe5dcacb28ca15aa87d)
@@ -546,18 +590,7 @@ Additionally, I do occasionally take requests for simple scripts, so feel free t
-
+
[raw-HYOA]: /hide_youtube_overlay_ads.user.js?raw=1
[raw-FYPBG]: /fix_youtube_player_bottom_gradient.user.js?raw=1
[raw-YCKP]: /youtube_channel_keyboard_protector.user.js?raw=1
@@ -578,6 +611,7 @@ Format END -->
[raw-WGFF]: /wider_google_form_fields.user.js?raw=1
[raw-GFCC]: /correct_google_form_correctness.user.js?raw=1
[raw-GSLQU]: /google_search_lean_query_updates.user.js?raw=1
+[raw-GSFP]: /google_search_footer_privacy.user.js?raw=1
[raw-RNNF]: /roll20_character_sheet_no_scrolling_number_fields.user.js?raw=1
[raw-BBBI]: /bypass_blogspots_blogger_iframe.user.js?raw=1
[raw-FoxF]: /foxaholic_fixes.user.js?raw=1
@@ -595,6 +629,10 @@ Format END -->
[raw-SFTPD]: /softpedia_improvements.user.js?raw=1
[raw-SPCDIT]: /sublime_package_control_description_in_title.user.js?raw=1
[raw-YUSS]: /youtrack_use_standard_scrollbar.user.js?raw=1
+[raw-SEWM]: /stackexchange_wide_mode.user.js?raw=1
+[raw-BIAT]: /izzyondroid_description_in_title.user.js?raw=1
+[raw-BFAT]: /fdroid_app_description_in_title.user.js?raw=1
+[raw-BGPT]: /greasyfork_better_page_titles.user.js?raw=1
[raw-DFSF]: /legacy_browser_workarounds/discourse_forum_scroll_fixer.user.js?raw=1
@@ -602,6 +640,7 @@ Format END -->
[raw-GNAW]: /legacy_browser_workarounds/github_notifications_archive_workaround.user.js?raw=1
[raw-GCDW]: /legacy_browser_workarounds/github_collapsed_details_workaround.user.js?raw=1
[raw-GLRAW]: /legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js?raw=1
+[raw-GSICW]: /legacy_browser_workarounds/github_show_issue_comments_workaround.user.js?raw=1
[raw-SELCE]: /legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js?raw=1
@@ -610,3 +649,5 @@ Format END -->
[ViolentMonkey_Chrome]: https://chrome.google.com/webstore/detail/violent-monkey/jinjaccalgkegednnccohejagnlnfdag
[ViolentMonkey_Edge]: https://microsoftedge.microsoft.com/addons/detail/violentmonkey/eeagobfjdenkkddmbclomhiblgggliao
[GreaseMonkey_v3_Moonchild]: https://github.com/janekptacijarabaci/greasemonkey/releases
+[GreaseMonkey_v3_Hyperbola_Iceweasel]: https://wiki.hyperbola.info/doku.php?id=es:system:userspace:application:uxp:iceweasel-uxp_addons
+[Hyperbola_Iceweasel]: https://wiki.hyperbola.info/doku.php?id=es:system:userspace:application:uxp:iceweasel-uxp
diff --git a/build/ABOUT.md b/build/ABOUT.md
new file mode 100644
index 0000000..3a7fec1
--- /dev/null
+++ b/build/ABOUT.md
@@ -0,0 +1,91 @@
+This file contains instructions for using the readme automation in this directory, and also a todo list.
+
+# Instructions
+## Prerequisites
+The following tools must be available on the `PATH`:
+ * Python (minimum version: 3.7)
+ * [MiniJinja CLI](https://github.com/mitsuhiko/minijinja)
+ * [jq](https://github.com/stedolan/jq)
+ * Bash/Zsh/Ksh/other-sh-interpreter
+ * git
+
+# What the hell do I do with any of this?
+Run `./run.sh --help` for instructions on what needs to be done manually
+when adding a new userscript.
+
+Otherwise, run `./run.sh`, then manually compare:
+ * `./data_files/main_script_manifest.json` <-> `./new_main_manifest.json`
+ * `./data_files/legacy_scripts.json` <-> `./new_legacy_manifest.json`
+ * `../README.md` <-> `./output.md`
+If satisfied, move/copy the right-hand files to the (version-controlled)
+left-hand files.
+
+Then add the changes and commit.
+
+# TODO list
+1. [X] The updater script, templates, and data files should reside under a directory named something like `build` or `deploy`
+
+2. [X] Clear fucking instructions on how to run this shit.
+
+ 1. [X] How should the updater know which userscript files to check for updates?
+ Currently it only checks files specified in the data files.
+ 2. [X] I don't believe the script currently understands how to read from multiple separate data files; should it?
+ Script now reads and updates both the main and legacy script manifests separately.
+ Support has been added for alternate keys to access the list of scripts in a file,
+ as the legacy manifest uses 'legacy_scripts', while the main manifest uses 'scripts'.
+
+ Running `./update_script_manifest.py` with Python3.7 or later and the
+ non-empty environment variable `WRITE_FILES` will produce updated
+ `new_main_manifest.json` and `new_legacy_manifest.json` from
+ `data_files/main_script_manifest.json` and `data_files/legacy_scripts.json`.
+ The updated readme can then be written to `output.md` by running:
+```bash
+jq -rs 'reduce .[] as $item ({}; . * $item)' 'data_files/general_url_references.json' new_legacy_manifest.json new_main_manifest.json |
+minijinja-cli --format json templates/primary_template.md.j2 - > output.md
+```
+
+**For convenience, simply run `./run.sh`**
+
+3. [ ] Automatically display (?git-?)diff of data file after running updater script.
+
+ 1. [-] Do I want to ultimately want to write back to the original data file, or should I write to a temporary file, then compare them,
+ and prompt for confirmation before overwriting the original? What if I want only part of the changes?
+ Currently all outputs go to temporary files that must be manually checked, copied (if good), and cleaned up.
+
+4. [X] If the metadata block of a script doesn't contain the non-standard `@createdAt` property,
+ and the script doesn't already have a `created` value in the data file,
+
+ _EITHER_
+ 1. Attempt to check the creation and modified date of the script file according to the file system. Keep the older of the two.
+ 2. Call out to git to determine when the script was first added to the repository.
+ 3. Set the earlier of the two as the `created` value in the data file.
+
+ _OR_
+
+ set the `created` value in the data file to the current date.
+
+ The first scenario seems preferable, but in the interest of speed and
+ convenience, the second option is employed.
+
+5. [ ] Support automatic detection of version-controlled scripts not yet in a data file, and automatically add them to the appropriate one.
+ This should also handle adding the `created` date if necessary.
+
+6. [ ] Decide on some process to automatically assign an `anchorString`.
+ Consider ditching custom anchor strings entirely in favor of just using the name of the script file, sans extension.
+ `pandoc` might be of use here, depending on the exact behavior of [extension: `auto_identifiers`](https://pandoc.org/MANUAL.html#extension-auto_identifiers)
+
+7. [ ] Figure out what part of `getUpdatedScriptData` can be replaced with a call to `dict.update()`.
+
+8. [ ] Investigate converting some part of `findScriptData` into a generator.
+
+9. [ ] Vendor icons into a repo (Projects/third_party/asset_archive) (Projects/Userscripts/notes/icon_index.txt)
+ 1. [ ] Add some kind of disclaimer section mentioning that site names and logos are property of their original owners.
+
+10. [ ] Group scripts under collapsible `section`/`detail` tags according to the sites they affect.
+ 1. [ ] Support a *manually added* `group` property in the data files to denote a named group; scripts without (a non-empty, non-null value for) this property should be under an "ungrouped" group, and appear only after all other groups.
+
+11. [X] Temporary local branch in which to test repository layout with new automation files included.
+
+12. [X] Finish/fix date parsing and conversion for the non-standard `createdAt` property in script metadata, so that it can be stored *sensibly* in the manifest.
+
+13. [ ] Although I'm not using `pandoc` at this time, apparently it's default markdown variant expects a blank line before headings (other than at the start of the document). Might want to try changing the format of script details in the readme template, which currently has a named anchor on the line before each script's heading. See [`pandoc` markdown extension: `blank_before_header`](https://pandoc.org/MANUAL.html#extension-blank_before_header)
diff --git a/build/data_files/general_url_references.json b/build/data_files/general_url_references.json
index 985c27b..9ea7a24 100644
--- a/build/data_files/general_url_references.json
+++ b/build/data_files/general_url_references.json
@@ -3,7 +3,7 @@
"title": "Some of the awesome scripts I use from other authors",
"scripts": [
"[Wide GitHub](https://github.com/xthexder/wide-github)",
- "[GitHub Code Folding](https://openuserjs.org/scripts/Mottie/GitHub_Code_Folding)",
+ "[GitHub Code Folding](https://openuserjs.org/scripts/Mottie/GitHub_Code_Folding) (Note: last working version for Chrome 72 was v1.1.2, although subsequent changes to GitHub's UI have broken parts of that, too)",
"[GitHub Gist Search Box](https://greasyfork.org/en/scripts/395318-github-gist-search-box)",
"[GitHub Search Autocomplete](https://github.com/Mottie/GitHub-userscripts/wiki/GitHub-search-autocomplete)",
"[GitHub - Add Path Search](https://gist.github.com/splintor/8d3f12b86962efe5dcacb28ca15aa87d)",
@@ -13,11 +13,22 @@
"[GitHub Network Ninja](https://github.com/maliayas/github-network-ninja/blob/master/main.user.js)"
]
},
+ "goanna_browsers_inline_refs": [
+ "[Pale Moon](https://www.palemoon.org)",
+ "[Basilisk](https://www.basilisk-browser.org)",
+ "[K-Meleon](http://kmeleonbrowser.org)",
+ "[Arctic Fox](https://github.com/rmottola/Arctic-Fox)",
+ "[Serpent, New Moon](https://rtfreesoft.blogspot.com)"
+ ],
"script_manager_refs": [
"[ViolentMonkey_src]: https://github.com/violentmonkey/violentmonkey/releases",
"[ViolentMonkey_Firefox]: https://addons.mozilla.org/firefox/addon/violentmonkey/",
"[ViolentMonkey_Chrome]: https://chrome.google.com/webstore/detail/violent-monkey/jinjaccalgkegednnccohejagnlnfdag",
"[ViolentMonkey_Edge]: https://microsoftedge.microsoft.com/addons/detail/violentmonkey/eeagobfjdenkkddmbclomhiblgggliao",
- "[GreaseMonkey_v3_Moonchild]: https://github.com/janekptacijarabaci/greasemonkey/releases"
+ "[GreaseMonkey_v3_Moonchild]: https://github.com/janekptacijarabaci/greasemonkey/releases",
+ "[GreaseMonkey_v3_Hyperbola_Iceweasel]: https://wiki.hyperbola.info/doku.php?id=es:system:userspace:application:uxp:iceweasel-uxp_addons"
+ ],
+ "other_url_refs": [
+ "[Hyperbola_Iceweasel]: https://wiki.hyperbola.info/doku.php?id=es:system:userspace:application:uxp:iceweasel-uxp"
]
-}
\ No newline at end of file
+}
diff --git a/build/data_files/legacy_scripts.json b/build/data_files/legacy_scripts.json
index e7441f4..adbef6d 100644
--- a/build/data_files/legacy_scripts.json
+++ b/build/data_files/legacy_scripts.json
@@ -5,60 +5,77 @@
"anchorString": "DFSF",
"path": "/legacy_browser_workarounds/discourse_forum_scroll_fixer.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Jun 29, 2024",
- "updated": "Mar 23, 2025",
- "desc": "Fix for Discourse forums breaking the ability to scroll on browsers that are too old or are blocking some script or other."
+ "updated": "Apr 5, 2026",
+ "desc": "Fix for Discourse forums breaking the ability to scroll on browsers that are too old or are blocking some script or other.",
+ "version": "1.0.5"
},
{
"name": "GitHub Line Hyperlink Workaround",
"anchorString": "GLHW",
"path": "/legacy_browser_workarounds/github_line_hyperlink_workaround.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Aug 10, 2022",
"updated": "Nov 18, 2022",
- "desc": "Add simple onclick handlers to the line numbers when viewing files on GitHub, as the normal behavior of linking directly to a clicked line number seems to have broken on legacy browsers as a result of some change to the implementation."
+ "desc": "Add simple onclick handlers to the line numbers when viewing files on GitHub, as the normal behavior of linking directly to a clicked line number seems to have broken on legacy browsers as a result of some change to the implementation.",
+ "version": "1.1.0"
},
{
"name": "GitHub Notifications Archive Workaround",
"anchorString": "GNAW",
"path": "/legacy_browser_workarounds/github_notifications_archive_workaround.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Jul 10, 2022",
"updated": "Mar 12, 2023",
- "desc": "Quick and simple redirect to work around strange behavior of being sent to github.com/notifications/beta/archive when marking notifications as done."
+ "desc": "Quick and simple redirect to work around strange behavior of being sent to github.com/notifications/beta/archive when marking notifications as done.",
+ "version": "1.1.0"
},
{
"name": "GitHub Collapsed Details Workaround",
"anchorString": "GCDW",
"path": "/legacy_browser_workarounds/github_collapsed_details_workaround.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Sep 30, 2022",
"updated": "Jun 10, 2023",
- "desc": "Add simple onclick handlers to the collapsed details of commits on GitHub, as the normal behavior of expanding the ellipses to the full commit message when clicked seems to have broken on legacy browsers as a result of some change to the implementation."
+ "desc": "Add simple onclick handlers to the collapsed details of commits on GitHub, as the normal behavior of expanding the ellipses to the full commit message when clicked seems to have broken on legacy browsers as a result of some change to the implementation.",
+ "version": "1.2.1"
},
{
"name": "GitHub Lazy Release Asset Workaround",
"anchorString": "GLRAW",
"path": "/legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Oct 8, 2022",
- "updated": "Mar 12, 2023",
- "desc": "Multiple fixes related to user-downloadable asset files on GitHub for users of legacy browsers.\n- Fetch asset list for releases, as the code that should already have been responsible for that is too modern, and is thus never even attempted on legacy browsers.\n- Fix the timestamps on the release page(s), most of which are within asset lists.\n- Slightly changes normal behavior by automatically showing __all__ assets for the first release on the page, whether that's two assets or fourty assets."
+ "updated": "Apr 5, 2026",
+ "desc": "Multiple fixes related to user-downloadable asset files on GitHub for users of legacy browsers.\n- Fetch asset list for releases, as the code that should already have been responsible for that is too modern, and is thus never even attempted on legacy browsers.\n- Fix the timestamps on the release page(s), most of which are within asset lists.\n- Slightly changes normal behavior by automatically showing __all__ assets for the first release on the page, whether that's two assets or fourty assets.",
+ "version": "1.1.2"
+ },
+ {
+ "name": "GitHub Show Issue Comments Workaround",
+ "anchorString": "GSICW",
+ "path": "/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js",
+ "license": "MIT",
+ "autoUpdates": true,
+ "created": "Nov 10, 2025",
+ "updated": "Apr 5, 2026",
+ "desc": "Manually display comments on Github issues using the JSON that's already on the page, which totally doesn't need React to accomplish.",
+ "version": "1.1.2"
},
{
"name": "StackExchange Legacy Comments Expander",
"anchorString": "SELCE",
"path": "/legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Nov 6, 2022",
- "updated": "Nov 2, 2023",
- "desc": "Replace 'Show X more comments' handler for StackExchange sites to better support older browsers; in particular, this enables showing all comments when using Chromium 72."
+ "updated": "Apr 5, 2026",
+ "desc": "Replace 'Show X more comments' handler for StackExchange sites to better support older browsers; in particular, this enables showing all comments when using Chromium 72.",
+ "version": "1.2.0"
}
]
-}
+}
\ No newline at end of file
diff --git a/build/data_files/main_script_manifest.json b/build/data_files/main_script_manifest.json
index c2c69f6..d4c3539 100644
--- a/build/data_files/main_script_manifest.json
+++ b/build/data_files/main_script_manifest.json
@@ -9,7 +9,7 @@
"created": "Apr 4, 2020",
"updated": "Oct 27, 2020",
"desc": "**This script should no longer be needed after Google's removal of overlay ads on April 6th, 2023.**
\n
\nYou know those little overlay advertisements that pop up on the bottom center of YouTube videos? If those really annoy you, this simple userscript (really just a userstyle wrapped into a userscript) will help by simply preventing them from rendering.
\nNote that this _does not_ affect other ads.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Fix YouTube Player Bottom Gradient",
@@ -20,7 +20,7 @@
"created": "Feb 26, 2021",
"updated": "Mar 30, 2021",
"desc": "This \"fixes\" the excessively large bottom gradient area that sometimes appears on the YouTube video player when the mouse cursor is within the player frame.
\n**So far, I've only seen the phenomenon that led me to write this while using Vivaldi.**",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "YouTube Channel Keyboard Protection",
@@ -31,7 +31,7 @@
"created": "Nov 13, 2021",
"updated": "May 1, 2022",
"desc": "Prevents YouTube from hijacking the Up/Down arrow keys on channel pages, as it likes to do sometimes (Left and Right arrow keys are okay though, because those don't control page scrolling).",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "Twitch Hide Content Disclosure",
@@ -73,9 +73,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "May 19, 2021",
- "updated": "Dec 12, 2024",
+ "updated": "Feb 9, 2026",
"desc": "Makes the video stats overlay on Twitch.tv video player partially transparent, so as to avoid obscuring the stream so much.",
- "version": "1.1.1"
+ "version": "1.1.2"
},
{
"name": "GitHub Repo Network Tab",
@@ -84,9 +84,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "Apr 6, 2020",
- "updated": "Feb 1, 2024",
+ "updated": "Feb 27, 2026",
"desc": "Adds a navigation tab for faster access to the 'Network' page of a repository.
\n
\nKnown bugs:\n- Occasionally the tab fails to be added, with no clear explanation or pattern. If this occurs, simply reload the page.\n- When switching between repository tabs, the network tab sometimes disappears, and something about the way GitHub does page navigation within a repository doesn't cause this script to be re-injected. Short of constantly checking state on a sub-second timer, or using a mutation observer, I don't know how else to solve this.",
- "version": "1.7.2"
+ "version": "1.8.0"
},
{
"name": "Bigger GitHub Network Graph",
@@ -97,7 +97,7 @@
"created": "Apr 12, 2020",
"updated": "Oct 28, 2021",
"desc": "Makes the timeline on the Network page of GitHub repositories utilize more of that available whitespace on the sides.
\nStill can't seem to make it use all the space on the right side though...
\n
\nEssentially a subset of [Wide GitHub](https://github.com/xthexder/wide-github) which, of course, I only realized after I'd written this.
\nOh well, someone will probably find this useful.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "GitHub Notification Page Tweaks",
@@ -108,7 +108,7 @@
"created": "Oct 22, 2020",
"updated": "Aug 3, 2021",
"desc": "Why does GitHub's beta notifications inbox use a \"More\" dropdown when there's more than enough space for the 2 elements within?
\nI don't know, and I dislike having to open a dropdown just to mark something as \"read\", so I did something about it.",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "GitHub Sticky Editor Header",
@@ -117,9 +117,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "Nov 24, 2021",
- "updated": "Nov 24, 2021",
+ "updated": "Apr 5, 2026",
"desc": "Makes the header of the (text) file editor on GitHub sticky.
\nWritten because I got sick and tired of having to move up and down the page to change to and from the preview while editing this README.",
- "version": "1.0"
+ "version": "1.0.1"
},
{
"name": "GitLab Description In Title",
@@ -130,7 +130,7 @@
"created": "May 22, 2021",
"updated": "Aug 3, 2021",
"desc": "Attempts to improve the page titles on GitLab by including the contents of the page's description, if one is provided.
\nThis also replaces instances of Unicode character 0x00B7, \"Middle Dot\", in the title, as I've found that particular character\nhas strangely led some editors to erroneously read and write the text in undesired encodings, such as GB2312, instead of UTF-8.",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "Prettier Lib.rs Preformatted Code",
@@ -141,7 +141,7 @@
"created": "Jul 5, 2020",
"updated": "Mar 30, 2021",
"desc": "Makes `
` blocks on lib.rs look more like they do on crates.io; lib.rs is so much faster thanks to reduced JS use, but it's not as pretty.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Lib.rs Description In Title",
@@ -152,7 +152,7 @@
"created": "Apr 28, 2021",
"updated": "May 11, 2021",
"desc": "Replace the unhelpful part of the tab title on a lib.rs crate's page with the short description of the crate, if one is provided.
\nConvenient for bookmarking and tab-saving extensions, as pages are typically stored according to their titles.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Crates.io Description In Title",
@@ -174,7 +174,7 @@
"created": "Jun 19, 2020",
"updated": "Apr 5, 2021",
"desc": "Do you hate that Gmail shows a toast notification that blocks functional regions of the UI after you do something to any email? Me too!
\nThis little change should help mitigate the problem by moving the toast notification to the bottom center of the screen.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Google Meet Ignore Hardware Disabled",
@@ -185,7 +185,7 @@
"created": "Mar 3, 2023",
"updated": "Mar 3, 2023",
"desc": "A.K.A \"I know my hardware is disabled, Google\"
\nThanks Google, but I'm well aware that my browser hasn't given you permission to access my hardware; I don't need you showing a prompt that can't be closed with a keypress.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Wider Google Form Fields",
@@ -196,7 +196,7 @@
"created": "Sep 30, 2021",
"updated": "Aug 19, 2022",
"desc": "Widens the input fields in google forms from 50% to 100% of the question element (minus padding).",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "Correct Google Form Correctness",
@@ -207,7 +207,7 @@
"created": "Nov 9, 2021",
"updated": "Nov 9, 2021",
"desc": "Make fields that have been manually marked as correct take on the same styling as fields that exactly matched the preset correct answer.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Google Search Lean Query Updates",
@@ -216,9 +216,20 @@
"license": "MIT",
"autoUpdates": true,
"created": "Jul 12, 2023",
- "updated": "Sep 28, 2024",
+ "updated": "Apr 5, 2026",
"desc": "Proof-of-concept: Prevent modifications to the Google search query in the on-page search bar from inserting a bunch of unwanted parameters into the resulting URL.",
- "version": "1.3.0"
+ "version": "1.3.1"
+ },
+ {
+ "name": "Google Search Footer Privacy",
+ "anchorString": "GSFP",
+ "path": "/google_search_footer_privacy.user.js",
+ "license": "MIT",
+ "autoUpdates": true,
+ "created": "Dec 30, 2023",
+ "updated": "Dec 16, 2025",
+ "desc": "Hide the \"Location\" part of the footer on Google Search results, and don't show the email address of the current user.",
+ "version": "1.1.1"
},
{
"name": "Roll20 Nonscrolling Number Fields",
@@ -229,7 +240,7 @@
"created": "Jan 23, 2021",
"updated": "Apr 5, 2021",
"desc": "This should disable changing the value of any numeric fields on Roll20 character sheets by scrolling.
\nTODO: Replace the use of setTimeout with a MutationObserver.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Bypass Blogspot's Blogger IFrame",
@@ -240,7 +251,7 @@
"created": "Jun 2, 2021",
"updated": "May 1, 2022",
"desc": "Unhide the page body and hide obstructive injected iframes on some Blogspot pages, which use those methods for reasons like discouraging ad blocking.",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "Foxaholic Fixes",
@@ -251,7 +262,7 @@
"created": "Jun 2, 2021",
"updated": "Aug 27, 2021",
"desc": "Fix Foxaholic's deliberate breaking of context menus, keypresses, and text selection.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Mitigate Target \\_blank Risk",
@@ -262,7 +273,7 @@
"created": "Aug 27, 2021",
"updated": "Nov 23, 2021",
"desc": "Appends `rel=\"noopener noreferrer\"` to every link (HTMLAnchorElement, not to be confused with HTMLLinkElement) that has `target=\"_blank\"`, preventing a possible security risk.
\nThis **_will_** break links to some sites, likely any links that would otherwise have opened in a new tab by default.
\nUsers may choose to ignore links from additional url origins, by setting the `customAllowedOrigins` key in the script's value storage to a list of origins, delimited by a single space character, `' '`.
\ne.g. Setting `customAllowedOrigins` to `'http://wordpress.com https://stackexchange.com https://novelupdates.com'` will prevent this script from modifying links to those origins.",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "MSYS2 Package Description In Title",
@@ -271,9 +282,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "Apr 28, 2021",
- "updated": "Sep 28, 2024",
- "desc": "Include the package description on the tab title for a package's page on packages.msys2.org/packages",
- "version": "1.2.0"
+ "updated": "Feb 9, 2026",
+ "desc": "Include the package description on the tab title for a package's page on packages.msys2.org",
+ "version": "1.2.1"
},
{
"name": "Minecraft CurseForge Title Tweaks",
@@ -282,9 +293,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "Apr 20, 2022",
- "updated": "Jun 18, 2022",
+ "updated": "Apr 5, 2026",
"desc": "Modifies the format of the page title for some of CurseForge's Minecraft pages.",
- "version": "1.1"
+ "version": "1.1.1"
},
{
"name": "Another \"Open In Steam\" Button",
@@ -293,9 +304,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "Nov 25, 2022",
- "updated": "Nov 25, 2022",
+ "updated": "Apr 5, 2026",
"desc": "As the name should imply, this is my own version of a script which adds a new button on Steam's steampowered and steamcommunity sites to open the current page in the Steam app.
\nSome of the CSS used was borrowed from https://greasyfork.org/en/scripts/454372-open-steam-url after I spent well over an hour fiddling with my own CSS in the pre-dawn hours, and decided I wasn't going to manage much better.",
- "version": "1.0"
+ "version": "1.0.1"
},
{
"name": "Ubuntu Packages Description In Title",
@@ -306,7 +317,7 @@
"created": "May 11, 2023",
"updated": "May 11, 2023",
"desc": "Try to provide a minimal, yet meaningful, page title that includes the package description on Ubuntu's package search/archive website.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Quietly Reject StackExchange Cookies",
@@ -317,7 +328,7 @@
"created": "May 14, 2023",
"updated": "May 14, 2023",
"desc": "Hide the pesky cookie permission requests on StackExchange sites, which don't actually appear to set even \"necessary\" cookies until the user responds to the permission prompt.
\nAlso hides a few other little things that just don't warrant another tiny script.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "PyPI Description In Title",
@@ -328,7 +339,7 @@
"created": "May 31, 2023",
"updated": "Aug 15, 2024",
"desc": "Rewrite the page title for a PyPI package to include a brief summary, when available.
\n
\nAlso doesn't use that centered dot character as a separator.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Simple URL Tracker Cleaner",
@@ -337,9 +348,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "Aug 10, 2021",
- "updated": "Jan 28, 2024",
+ "updated": "Apr 5, 2026",
"desc": "Scrub various common tracker parameters from URLs. Even if you don't mind being tracked, parameters like these often make URLs rather long; why copy, share, or save a 200 character URL when you could get the exact same content by removing 160 of those characters, without routing traffic through some URL shortening service?
\n
\nPrimarily targets parameters related to Google, Amazon, and Facebook.
\n
\n\nIcon from: Fly Swatter free icon created by Freepik - Flaticon\n",
- "version": "1.6.2"
+ "version": "1.6.4"
},
{
"name": "Old Reddit Hide Posts By Sub",
@@ -350,7 +361,7 @@
"created": "Apr 8, 2022",
"updated": "Jul 2, 2023",
"desc": "Hide posts from arbitrary subreddits (unless specifically looking at them, of course).
\n
\nOnly works for old.reddit.com, not www.reddit.com, because not only does the latter use a DOM structure that makes it unsuitable for applying styles to entire post-elements by the CSS selector of the child element holding the subreddit, it _also_ commits the desktop (and frankly, even mobile) user-experience war-crime of infinite pagination (endless scrolling). **TLDR: Modern Reddit UI sucks, and supporting it would take more effort than I'm willing to put in to this for my own use.**",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "ScribbleHub Reading List Upgrades",
@@ -361,7 +372,7 @@
"created": "Oct 7, 2022",
"updated": "Jan 19, 2024",
"desc": "Allows hiding novels the user is caught up on from their reading lists, adds the current reading list name to the page title, and more planned.",
- "version": "1.2"
+ "version": "1.2.0"
},
{
"name": "NovelUpdates Reading List Upgrades",
@@ -372,7 +383,7 @@
"created": "Jul 8, 2022",
"updated": "Nov 16, 2022",
"desc": "Allows hiding novels the user is caught up on from their reading lists, adds the current reading list name to the page title, and more planned.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Softpedia Improvements",
@@ -406,6 +417,50 @@
"updated": "Aug 22, 2024",
"desc": "Use the browser's regular scrollbar style on JetBrains' YouTrack pages",
"version": "1.0.0"
+ },
+ {
+ "name": "StackExchange Wide Mode",
+ "anchorString": "SEWM",
+ "path": "/stackexchange_wide_mode.user.js",
+ "license": "MIT",
+ "autoUpdates": true,
+ "created": "Jun 20, 2024",
+ "updated": "Jun 20, 2024",
+ "desc": "StackExchange sites should take better advantage of the horizontal screen space, particularly on question pages. This removes the width restrictions on the main content part of those pages.",
+ "version": "1.0.0"
+ },
+ {
+ "name": "Better IzzyOnDroid App Titles",
+ "anchorString": "BIAT",
+ "path": "/izzyondroid_description_in_title.user.js",
+ "license": "MIT",
+ "autoUpdates": true,
+ "created": "May 2, 2024",
+ "updated": "May 22, 2025",
+ "desc": "Adds app description to page titles where possible.",
+ "version": "1.1.0"
+ },
+ {
+ "name": "Better F-Droid App Titles",
+ "anchorString": "BFAT",
+ "path": "/fdroid_app_description_in_title.user.js",
+ "license": "MIT",
+ "autoUpdates": true,
+ "created": "Aug 15, 2025",
+ "updated": "Aug 15, 2025",
+ "desc": "Adds app description to page titles where possible.",
+ "version": "1.0.0"
+ },
+ {
+ "name": "Better Greasyfork Page Titles",
+ "anchorString": "BGPT",
+ "path": "/greasyfork_better_page_titles.user.js",
+ "license": "MIT",
+ "autoUpdates": true,
+ "created": "Nov 15, 2025",
+ "updated": "Nov 15, 2025",
+ "desc": "Include userscript descriptions in page titles on Greasy Fork (and Sleazy Fork)",
+ "version": "1.0.0"
}
]
}
\ No newline at end of file
diff --git a/build/run.sh b/build/run.sh
index 62dba70..b98c442 100644
--- a/build/run.sh
+++ b/build/run.sh
@@ -1,13 +1,52 @@
#! /bin/sh
+# To "register" a new userscript, add a new object with `path` and `anchorString` values to the `scripts` array in `main_script_manifest.json`; the updater python script will do the rest.
+
+usage(){
+ # shellcheck disable=SC2016
+ printf '%s\n' \
+ 'This script is used to generate updated versions of readme and manifest files.' \
+ 'To "register" a new userscript for inclusion in the readme, add a new' \
+ 'object with both `path` and `anchorString` values to the' \ '`scripts` array (or other such array where relevant)' \
+ 'in `main_script_manifest.json` (and/or `legacy_scripts.json`)' \
+ 'before running this script.' \
+ 'Updated manifest and readme files will be created in the `build` directory,' \
+ 'and named `new_main_manifest.json`/`new_legacy_manifest.json`' \
+ 'and `output.md`, respectively.' \
+ 'They can be manually copied over the originals.'
+ return 0
+}
+
+## If this script ever comes to require any arguments, uncomment this.
+# if [ "$#" -eq 0 ]; then
+# usage
+# exit 1
+# fi
+
+case "$1" in
+ -h|--help)
+ usage && exit
+ ;;
+ -*)
+ printf 'ERROR: unrecognized flag: "%s"\n' "$1"
+ usage
+ exit 1
+ ;;
+esac
+
+## POSIX way to determine the location of an executed shell script https://stackoverflow.com/a/29835459
+thisScriptLocation="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)"
+## Ensure this script is run in the context of it's parent directory, which is within the git repository.
+if [ "$PWD" != "$thisScriptLocation" ]; then
+ exec env -C "$thisScriptLocation" "./${0##*/}" "$@"
+fi
+
repoRoot="$(git rev-parse --show-toplevel)"
dataDir="${repoRoot}/build/data_files"
templateDir="${repoRoot}/build/templates"
-# To "register" a new userscript, add a new object with `path` and `anchorString` values to the `scripts` array in `main_script_manifest.json`; the updater python script will do the rest.
-
-TEST_READER=1 python3 "${repoRoot}/build/update_script_manifest.py"
-jq -rs 'reduce .[] as $item ({}; . * $item)' "${dataDir}/general_url_references.json" "${dataDir}/legacy_scripts.json" newdata.json | \
+WRITE_FILES=1 python3 "${repoRoot}/build/update_script_manifest.py"
+jq -rs 'reduce .[] as $item ({}; . * $item)' "${dataDir}/general_url_references.json" new_legacy_manifest.json new_main_manifest.json | \
minijinja-cli --format json "${templateDir}/primary_template.md.j2" - > output.md
################
diff --git a/build/templates/link_refs.md.j2 b/build/templates/link_refs.md.j2
index 38fe08a..ed52407 100644
--- a/build/templates/link_refs.md.j2
+++ b/build/templates/link_refs.md.j2
@@ -1,5 +1,5 @@
-
+Format END --> #}
{%- for script in scripts %}
[raw-{{ script.anchorString}}]: {{ script.path }}?raw=1
{%- endfor %}
@@ -24,3 +24,6 @@ Format END -->
{%- for item in script_manager_refs %}
{{ item }}
{%- endfor %}
+{%- for item in other_url_refs %}
+{{ item }}
+{%- endfor %}
diff --git a/build/templates/primary_template.md.j2 b/build/templates/primary_template.md.j2
index 848048e..4b1370c 100644
--- a/build/templates/primary_template.md.j2
+++ b/build/templates/primary_template.md.j2
@@ -2,6 +2,7 @@
[](http://hits.dwyl.com/StaticPH/UserScripts)
Unless otherwise specified in their description, all userscripts have been tested with ViolentMonkey on Chromium 72 or later and on Vivaldi 3.6 or later.
They may also work with GreaseMonkey, TamperMonkey, or on other browsers.
+I try to specifically write code that will run on these targets, _regardless of the extent to which the page(s) they run on will remain functional under those conditions._
{% include "userscript_manager.md.j2" ignore missing %}
@@ -12,14 +13,15 @@ If your browsing habit doesn't involve much opening things in new tabs, you'll l
One example of such a scenario could be opening a Twitch.tv livestream from the directory view.
-
---
+
To add a script:
* Install a script directly from GitHub by clicking on the "install" link in the table below.
-
+--> #}
| Userscript
Description | Direct
Install | Sites | Supports
Auto-Update | License | Created | Updated |
|------------------------------------------------|:--------------------:|:-------------------:|:-----------------------:|:-------:|:----------:|:----------:|
@@ -29,11 +31,14 @@ To add a script:
| [{{ script.name }}](#{{ script.anchorString }}) | [install][raw-{{ script.anchorString }}] | N/A | {{ ":heavy_check_mark:" if script.autoUpdates == true else script.autoUpdates }} | {{ script.license }} | {{ script.created }} | {{ script.updated|default(script.created) }} |
{%- endfor %}
+{#
+#}
---
+{#
+#}
---
{#- Script Details go here #}
@@ -87,6 +93,7 @@ Additionally, I do occasionally take requests for simple scripts, so feel free t
---
## {{ recommendations.title }}
+I make no guarantees regarding the availability, maintenance, or browser version support of these scripts
{%- for script in recommendations.scripts %}
- {{ script }}
{%- endfor %}
diff --git a/build/templates/userscript_manager.md.j2 b/build/templates/userscript_manager.md.j2
index 7392522..d8725ed 100644
--- a/build/templates/userscript_manager.md.j2
+++ b/build/templates/userscript_manager.md.j2
@@ -3,11 +3,8 @@
* [Google Chrome and (most) Chromium-based browsers][ViolentMonkey_Chrome]
* [Microsoft Edge (which people apparently use)][ViolentMonkey_Edge]
* Alternatively, install ViolentMonkey from its [source][ViolentMonkey_src]
-
+
### Other
-* If you use [Pale Moon](https://www.palemoon.org), [Basilisk](https://www.basilisk-browser.org), or [K-Meleon](http://kmeleonbrowser.org), try [this fork of GreaseMonkey v3][GreaseMonkey_v3_Moonchild].
-Some scripts will likely require modifications for this to work; if you do this yourself, please submit a pull request so that others may also benefit.
\ No newline at end of file
+* If you use {{ goanna_browsers_inline_refs|join(', ') }}, or another browser using the Goanna browser engine, try [this fork of GreaseMonkey v3][GreaseMonkey_v3_Moonchild].
+* The [Hyperbola-fork of Iceweasel][Hyperbola_Iceweasel] has its own [fork of GreaseMonkey v3][GreaseMonkey_v3_Hyperbola_Iceweasel]
Page only available in Spanish as of November 16th, 2025
+Some scripts will likely require modifications for this to work; if you do this yourself, please submit a pull request so that others may also benefit.
diff --git a/build/update_script_manifest.py b/build/update_script_manifest.py
index 5cd69ba..be6cde1 100644
--- a/build/update_script_manifest.py
+++ b/build/update_script_manifest.py
@@ -29,7 +29,7 @@ def mayPrint(*args, **kwargs) -> None: #noqa: ARG001,ANN002,ANN003, RUF100 # pyr
defaultFileEncoding = getpreferredencoding(False) or sys.getdefaultencoding()
__dateOutputFmtStr = '%b %d, %Y' # ex: Sep 08, 2024
-scriptStartTime = datetime.datetime.utcnow()
+scriptStartTime = datetime.datetime.now(datetime.timezone.utc)
def fmtDateForDisplay(date: datetime.datetime) -> str:
dateStr = date.astimezone(datetime.timezone.utc).strftime(__dateOutputFmtStr)
# Use a space to left-pad single-digit days, rather than a zero
@@ -83,7 +83,7 @@ def __init__(self, value: str):
self.minor: int = int(minor, 10)
self.patch: int = int(patch, 10) or 0
self.originalValue: str = value
- self.value: str = value + '.0' if patch == '' else value
+ self.value: str = f'{value}.{self.patch}' if value.count('.') < 2 else value
def __str__(self) -> str:
return self.value
@@ -125,7 +125,7 @@ def __gt__(self, value: Union[str, VersionString]) -> bool:
return (self.major, self.minor, self.patch) > (operand.major, operand.minor, operand.patch)
def __le__(self, value: Union[str, VersionString]) -> bool:
- return not self < value
+ return not self > value
def __ge__(self, value: Union[str, VersionString]) -> bool:
return not self < value
@@ -143,7 +143,7 @@ class DataUpdater:
_missingLicenseValue: str = 'No license found'
_missingVersionValue: str = '0.1.0'
- def __init__(self): #noqa: ARG002, RUF100
+ def __init__(self) -> None: #noqa: ARG002, RUF100
self.dataFileContents: Dict[str, JSON_Value_Type] = {}
## Steps to use:
@@ -228,25 +228,25 @@ def getUpdatedScriptData(self, scriptFileMeta: Dict[str, Union[str, bool]], scri
scriptItem['updated'] = scriptItem['created']
elif ('version' in scriptItem) and VersionString(scriptItem['version']) < scriptFileMeta['version']:
# The file's version info was increased since the last time the data file was updated by this script;
- # record the new "updated" date as the current date, and update the "version" to match the script's meta block
+ # record the new "updated" date as the current date
scriptItem['updated'] = self._missingCreatedValue
if self.__needsAttrForDataFile(scriptItem, 'license'):
# Get license field from script file's metadata
scriptItem['license'] = scriptFileMeta['license'] or self._missingLicenseValue
- # Unconditionally update the version field
+ # Unconditionally update the "version" field to match the script's meta block
scriptItem['version'] = str(VersionString(scriptFileMeta['version'])) or self._missingVersionValue
# Unconditionally update the autoUpdates field
scriptItem['autoUpdates'] = scriptFileMeta['autoUpdates'] or 'false'
return self.sortScriptMeta(scriptItem)
- def findScriptData(self) -> None:
+ def findScriptData(self, scriptArrayKey: str = 'scripts') -> None:
# Must be called after either `readJSONFile`/`readJSONStr`/`readJSONDict`
# FIXME: With the large blocks of data this function will eventually be producing and returning,
# it would probably make sense to turn this into a Generator method, or at least make the
# `for` loop into its own inner-function and make _that_ a Generator.
adjusted: List[Dict[str, Union[str, bool]]] = []
- for scriptItem in self.dataFileContents['scripts']:
+ for scriptItem in self.dataFileContents[scriptArrayKey]:
scriptItem: Dict[str, str] # Shut up linter wrongly complaining that scriptItem is a string.
if 'path' not in scriptItem:
# TODO: support automatic detection of version-controlled scripts not in a data file,
@@ -270,29 +270,35 @@ def findScriptData(self) -> None:
adjusted.append(self.getUpdatedScriptData(scriptFileMeta, scriptItem))
print(adjusted) # FIXME: Do something with this value other than just print it to stdout.
- self.dataFileContents['scripts'] = adjusted # TEMP
+ self.dataFileContents[scriptArrayKey] = adjusted # TEMP
- def writeTest(self) -> None: # TEMP
- with open('newdata.json', 'w', encoding=defaultFileEncoding) as outputFile:
+ def writeNew(self, toFile: str='new_main_manifest.json') -> None:
+ with open(toFile, 'w', encoding=defaultFileEncoding) as outputFile:
json.dump(self.dataFileContents, outputFile, indent="\t")
- print('Go diff main_script_manifest.json and newdata.json')
# TODO: ?Decide on some process to automatically assign an anchorString?
if __name__ == '__main__':
- from os import getenv
from functools import partial
- hasDebugVar: bool = bool(getenv('DEBUG'))
+ hasDebugVar: bool = bool(os.getenv('DEBUG'))
if hasDebugVar:
mayPrint = partial(print, file=sys.stderr) #noqa: F811, RUF100
- if bool(getenv('TEST_READER')):
- updater = DataUpdater()
- # TODO: utilize __file__ to make these paths relative to the script, rather than the working directory
- updater.readJSONFile('./data_files/main_script_manifest.json')
- here = os.getcwd()
- os.chdir('..')
- updater.findScriptData()
- os.chdir(here)
- updater.writeTest()
+ updater = DataUpdater()
+ # Get the parent directory of this script.
+ here = os.path.dirname(__file__)
+
+ updater.readJSONFile(here + '/data_files/main_script_manifest.json')
+ os.chdir(here + '/..')
+ updater.findScriptData()
+ if bool(os.getenv('WRITE_FILES')):
+ updater.writeNew(here + '/new_main_manifest.json')
+ print('Go diff main_script_manifest.json and new_main_manifest.json')
+
+ updater.readJSONFile(here + '/data_files/legacy_scripts.json')
+ os.chdir(here + '/..')
+ updater.findScriptData('legacy_scripts')
+ if bool(os.getenv('WRITE_FILES')):
+ updater.writeNew(here + '/new_legacy_manifest.json')
+ print('Go diff legacy_scripts.json and new_legacy_manifest.json')
diff --git a/fdroid_app_description_in_title.user.js b/fdroid_app_description_in_title.user.js
new file mode 100644
index 0000000..c53a209
--- /dev/null
+++ b/fdroid_app_description_in_title.user.js
@@ -0,0 +1,37 @@
+// ==UserScript==
+// @name Better F-Droid App Titles
+// @namespace https://github.com/StaticPH
+// @match https://f-droid.org/en/packages/*
+// @version 1.0.0
+// @createdAt 8/15/2025, 11:47:05 PM
+// @author StaticPH
+// @description Adds app description to page titles where possible.
+// @license MIT
+// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/fdroid_app_description_in_title.user.js
+// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/fdroid_app_description_in_title.user.js
+// @homepageURL https://github.com/StaticPH/UserScripts
+// @supportURL https://github.com/StaticPH/UserScripts/issues
+// @icon https://f-droid.org/assets/favicon-32x32_Dk3aeG3k_A20gYe5zAlPSBx_CEcpJaCI89K2X2z6CFY=.png
+// @grant none
+// @noframes
+// @run-at document-end
+// ==/UserScript==
+
+(function(){
+ "use strict";
+
+ const trailer = ' | F-Droid - Free and Open Source Android App Repository';
+ const pkgNameEle = document.querySelector('.package-name');
+ const appID = document.location.pathname.replace(/.+\/packages\//, '').replace(/\/.*/,'');
+ const pkgName = pkgNameEle && pkgNameEle.textContent ? pkgNameEle.textContent.trim() : appID;
+ const pkgSummaryEle = document.querySelector('.package-summary');
+ let pkgSummary;
+ if (pkgSummaryEle && pkgSummaryEle.textContent){
+ pkgSummary = pkgSummaryEle.textContent.trim();
+ }
+ else {
+ const pkgDescEle = document.querySelector('.package-description');
+ pkgSummary = pkgDescEle && pkgDescEle.firstChild && pkgDescEle.firstChild.textContent && pkgDescEle.firstChild.textContent.split(/[.!]\W/)[0].trim() || 'ERROR';
+ }
+ document.title = `${pkgName} | ${pkgSummary}${trailer}`;
+})();
diff --git a/fix_youtube_player_bottom_gradient.user.js b/fix_youtube_player_bottom_gradient.user.js
index aa99c45..52111ec 100644
--- a/fix_youtube_player_bottom_gradient.user.js
+++ b/fix_youtube_player_bottom_gradient.user.js
@@ -26,7 +26,7 @@
// `);
setTimeout(function wait(){
- const playerBottomGradient = document.getElementById('movie_player').querySelector('.ytp-gradient-bottom');
+ const playerBottomGradient = document.querySelector('#movie_player .ytp-gradient-bottom');
if (playerBottomGradient){
console.log('Fixing bottom player gradient height');
playerBottomGradient.style.removeProperty('height');
diff --git a/github_repo_network_tab.user.js b/github_repo_network_tab.user.js
index b4e93df..a230395 100644
--- a/github_repo_network_tab.user.js
+++ b/github_repo_network_tab.user.js
@@ -9,6 +9,7 @@
// @exclude-match https://github.com/enterprise*
// @exclude-match https://github.com/explore*
// @exclude-match https://github.com/features*
+// @exclude-match https://github.com/github-copilot/*
// @exclude-match https://github.com/login/*
// @exclude-match https://github.com/marketplace*
// @exclude-match https://github.com/new*
@@ -25,7 +26,7 @@
// @exclude-match https://github.com/topics*
// @exclude-match https://github.com/trending*
// @exclude-match https://github.com/users/*/projects/*
-// @version 1.7.2
+// @version 1.8.0
// @createdAt 4/06/2020
// @author StaticPH
// @description Adds a navigation tab for faster access to the 'Network' page of a repository.
@@ -48,16 +49,16 @@
return location.pathname.split('/', 3).slice(1).join('/');
})();
+ const networkIconSvgHTML = '';
+
/* Honestly, I feel like creating the HTML directly is less of a hassle than creating all the elements with JavaScript */
function createBigNetworkTabHTML(){
// Exclude analytical "data-ga-click" and "data-selected-links" attributes
return '\n' +
- ' \n' +
+ '\t' + networkIconSvgHTML + '\n' +
' \n' +
' Network\n' +
- ' \n' +
+ ' \n' +
'\n';
//TODO: Intelligently determine if the link element should have 'style="visibility:hidden;"' to start with?
}
@@ -81,6 +82,53 @@
'';
}
+ function maybeFixHighlightedTab(){
+ if (location.pathname.endsWith(here + '/network') || location.pathname.endsWith(here + '/network/')){
+ let networkTab = document.querySelector('[data-tab-item="i2_1network-tab"]');
+ let insightsTab = document.querySelector('[data-tab-item="i7insights-tab"]');
+
+ if (insightsTab /*&& insightsTab.hasAttribute('aria-current')*/){
+ insightsTab.removeAttribute('aria-current');
+ insightsTab.classList.remove('selected');
+ }
+ if (networkTab){
+ networkTab.setAttribute('aria-current', 'page');
+ networkTab.classList.add('selected');
+ }
+ }
+ }
+
+ function doesNeedModernTabVariant(){
+ // Thanks for making it more annoying to find or work with meaningful elements using CSS selectors, GitHub. /s
+ return document.querySelector('nav[class*="prc-components-UnderlineWrapper"]') !== null;
+ }
+
+ function modernUIAddNetworkTab(){
+ const prTabLink = document.querySelector(`li.prc-UnderlineNav-UnderlineNavItem-syRjR > a[href="/${here}/pulls"]`);
+ if (!prTabLink){ return false; } // Not ready yet... or GitHub changed shit again.
+
+ const dataAttrs = 'data-turbo-frame="repo-content-turbo-frame" data-discover="true"';
+ const isActiveTab = (location.pathname.endsWith('/network') || location.pathname.endsWith('/network/'));
+ const tabHTML = ' \n' +
+ `\n` +
+ ` ${networkIconSvgHTML}\n` +
+ // May want to skip adding actual text manually due to css attr magic setting value from data-content
+ ' Network\n' +
+ '\n' +
+ ' ';
+ prTabLink.parentElement.insertAdjacentHTML('afterend', tabHTML);
+
+ const networkTab = document.querySelector('#bigNetworkTab');
+ if (isActiveTab){
+ const falseActiveTab = document.querySelector('li.prc-UnderlineNav-UnderlineNavItem-syRjR > a[aria-current]');
+ if (falseActiveTab){
+ falseActiveTab.removeAttribute('aria-current');
+ }
+ networkTab.setAttribute('aria-current', 'page');
+ }
+ return networkTab;
+ }
+
//TODO: Consider insertion at Nth element position, rather than relative to PR tab.
setTimeout(function wait(){
/* Find the 'Pull Requests' tab; inserting the new Network tab immediately after it ensures consistent placement. */
@@ -91,11 +139,13 @@
// Wait until the page loads in enough to have the 'Pull Requests' tab in the repository header, so that it can be used as a point of reference for element insertion
if (repoPullsTab.length !== 0){
repoPullsTab[0].insertAdjacentHTML('afterend', createBigNetworkTabHTML());
- document.getElementById('bigNetworkTab') && console.debug('Added big Network tab.');
+ /* oxlint-disable no-unused-expressions */
+ document.querySelector('#bigNetworkTab') && console.debug('Added big Network tab.');
if (repoPullsTab.length > 1){
repoPullsTab[1].insertAdjacentHTML('afterend', createSmallNetworkTabHTML());
- document.getElementById('smallNetworkTab') && console.debug('Added small Network tab.');
+ /* oxlint-disable no-unused-expressions */
+ document.querySelector('#smallNetworkTab') && console.debug('Added small Network tab.');
}
// setTimeout(function foo(){
@@ -112,34 +162,45 @@
const pullsDropdownItem = document.querySelector('details-menu li[data-menu-item="i2pull-requests-tab"]');
if (pullsDropdownItem){
pullsDropdownItem.insertAdjacentHTML('afterend', createNetworkTabInDropdownHTML());
- document.getElementById('networkTabDropdown') && console.debug('Added Network tab item to dropdown.');
+ /* oxlint-disable no-unused-expressions */
+ document.querySelector('#networkTabDropdown') && console.debug('Added Network tab item to dropdown.');
}
else if (dropdownRetries >= dropdownRetryLimit){
console.log(`Number of attempts at adding Network tab to dropdown have exceeded the limit of ${dropdownRetryLimit} attempts. Giving up.`);
}
- else{
+ else {
console.log(`Waiting ${(dropdownRetries * 500) + 500}ms for page to load further before attempting insertion of dropdown-item.`);
dropdownRetries++;
setTimeout(waitmore, (dropdownRetries * 500) + 500);
}
});
- if (location.pathname.endsWith(here + '/network') || location.pathname.endsWith(here + '/network/')){
- let networkTab = document.querySelector('[data-tab-item="i2_1network-tab"]');
- let insightsTab = document.querySelector('[data-tab-item="i7insights-tab"]');
-
- if (insightsTab /*&& insightsTab.hasAttribute('aria-current')*/){
- insightsTab.removeAttribute('aria-current');
- insightsTab.classList.remove('selected');
- }
- if (networkTab){
- networkTab.classList.add('selected');
- }
+ maybeFixHighlightedTab();
+ }
+ else if (doesNeedModernTabVariant()){
+ if (modernUIAddNetworkTab()){
+ console.log('Added Network tab to modern nagivation menu.');
+ (async function(){
+ return await setInterval(function babysit(){
+ // Compensate for React's DOM fuckery often regenerating the navigation tabs (and who knows what else) shortly after document-idle;
+ // check back every 5 seconds.
+ if (!document.querySelector('#bigNetworkTab')){
+ console.log('Compensating for React DOM fuckery regenerating the navigation tabs. Re-adding Network tab');
+ if (modernUIAddNetworkTab()){
+ console.log('Network tab re-added to modern navigation menu.');
+ };
+ }
+ }, 5000);
+ })();
+ }
+ else {
+ console.log('Modern UI required; waiting 300ms for page to load further.');
+ return setTimeout(wait, 300);
}
}
- else{
+ else {
console.log('Waiting 300ms for page to load further.');
- setTimeout(wait, 300);
+ return setTimeout(wait, 300);
}
});
})();
diff --git a/github_sticky_editor_header.user.js b/github_sticky_editor_header.user.js
index 2df3b4c..bbff278 100644
--- a/github_sticky_editor_header.user.js
+++ b/github_sticky_editor_header.user.js
@@ -3,7 +3,7 @@
// @namespace https://github.com/StaticPH
// @match http*://github.com/*/*/edit/*
// include /https?:\/\/github\.com\/[^\/]+\/[^\/]+\/edit\/.+\.(md|MD|Md|adoc|asciidoc|rst)/
-// @version 1.0
+// @version 1.0.1
// @createdAt 11/24/2021, 1:50:21 AM
// @author StaticPH
// @description Makes the header of the (text) file editor on GitHub sticky.
@@ -22,11 +22,20 @@
(function(){
"use strict";
- if (!(GM && GM.addStyle)){
- console.log('GM.addStyle is not defined. Falling back to GM_addStyle.');
- GM = GM ? GM : {};
- GM.addStyle = GM.addStyle ? GM.addStyle : GM_addStyle;
+ // Prefer asychronous Greasemonkey4-API GM.addStyle, but allow use of GM_addStyle as a fallback if necessary.
+ if (typeof GM === 'undefined'){
+ this.GM = {};
}
+ if (GM['addStyle'] === undefined){
+ console.log('GM.addStyle is not defined. Falling back to GM_addStyle Promise.');
+ GM['addStyle'] = function(...args){
+ return new Promise((onResolve, onReject) => {
+ try{ onResolve(GM_addStyle.apply(this, args)); }
+ catch(err){ onReject(err); }
+ });
+ }
+ }
+
GM.addStyle(`
.js-code-editor.container-preview > .file-header {
top: 0%;
diff --git a/gitlab_description_in_title.user.js b/gitlab_description_in_title.user.js
index ad06af8..6eee28c 100644
--- a/gitlab_description_in_title.user.js
+++ b/gitlab_description_in_title.user.js
@@ -30,8 +30,8 @@
let title = document.title;
let description = document.querySelector('meta[name="description"]').content;
- //I've found that Unicode character 0x00B7, "Middle Dot", has led some editors to erroneously interpret the text in encodings other than UTF-8, so let's replace it.
- title = title.replace(/\u00b7/g, '|');
+ // I've found that Unicode character 0x00B7, "Middle Dot", has led some editors to erroneously interpret the text in encodings other than UTF-8, so let's replace it.
+ title = title.replace(/\u{00B7}/g, '|');
if (description){
title = `${title.trim()} — ${description.trim()}`;
diff --git a/google_search_footer_privacy.user.js b/google_search_footer_privacy.user.js
new file mode 100644
index 0000000..f2d9098
--- /dev/null
+++ b/google_search_footer_privacy.user.js
@@ -0,0 +1,64 @@
+// ==UserScript==
+// @name Google Search Footer Privacy
+// @namespace https://github.com/StaticPH
+// @match *://google.com/search
+// @match *://*.google.com/search
+// @version 1.1.1
+// @createdAt 12/30/2023, 6:16:41 PM
+// @author StaticPH
+// @description Hide the "Location" part of the footer on Google Search results, and don't show the email address of the current user.
+// @license MIT
+// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/google_search_footer_privacy.user.js
+// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/google_search_footer_privacy.user.js
+// @homepageURL https://github.com/StaticPH/UserScripts
+// @supportURL https://github.com/StaticPH/UserScripts/issues
+// @icon https://www.gstatic.com/images/branding/googleg/1x/googleg_standard_color_48dp.png
+// @grant GM_addStyle
+// @grant GM.addStyle
+// @noframes
+// @run-at document-end
+// ==/UserScript==
+
+(function(){
+ "use strict";
+
+ // Prefer asychronous Greasemonkey4-API GM.addStyle, but allow use of GM_addStyle as a fallback if necessary.
+ if (typeof GM === 'undefined'){
+ this.GM = {};
+ }
+ if (GM['addStyle'] === undefined){
+ console.log('GM.addStyle is not defined. Falling back to GM_addStyle Promise.');
+ GM['addStyle'] = function(...args){
+ return new Promise((onResolve, onReject) => {
+ try{ onResolve(GM_addStyle.apply(this, args)); }
+ catch(err){ onReject(err); }
+ });
+ }
+ }
+
+ GM.addStyle(`
+ /* Hide email address of currently logged in user, if applicable */
+ /* #EOlPnc > :nth-last-child(2):not(:first-child) > :first-child:not(:last-child), */
+ /* Hide Location */
+ #EOlPnc > .Srfpq, .dfB0uf, .HddGcc > .VYM29 {
+ display: none !important;
+ }
+
+ /* Hide email address of currently logged in user, AND hide "Sign Out" link. Also hides "Sign In" link if not logged in. */
+ #EOlPnc > :nth-last-child(2):not(:first-child) > span {
+ font-size: 0;
+ }
+ /* Unhide JUST the "Sign In" or "Sign Out" link.
+ * Instead of "visibility: hidden" or "opacity: 0" above, which would keep the layout intact, using "font-size: 0" will
+ * allow child elements with a non-zero "font-size" to realign, which re-centers the "Sign Out" link that is normally offset
+ * due to the presence of the email. Furthermore, this method allows hiding text nodes, such as the " - " that would
+ * otherwise be placed between the email and the "Sign Out" link.
+ * The caveat to this is that simply setting "font-size" to initial, unset, or inherit is insufficient to show specific elements
+ * again with their previous size, if that size is different from "font-size: initial"; in such a case, the size needs to be
+ * hard-coded.
+ */
+ #EOlPnc > :nth-last-child(2):not(:first-child) > span > a {
+ font-size: 14px;
+ }
+ `);
+})();
diff --git a/google_search_lean_query_updates.user.js b/google_search_lean_query_updates.user.js
index 0b11264..619401e 100644
--- a/google_search_lean_query_updates.user.js
+++ b/google_search_lean_query_updates.user.js
@@ -2,7 +2,7 @@
// @name Google Search Lean Query Updates
// @namespace https://github.com/StaticPH
// @match https://www.google.com/search
-// @version 1.3.0
+// @version 1.3.1
// @createdAt 7/12/2023, 2:08:47 PM
// @author StaticPH
// @description Proof-of-concept: Prevent modifications to the Google search query in the on-page search bar from inserting a bunch of unwanted parameters into the resulting URL.
@@ -24,7 +24,11 @@
Object.defineProperty(String.prototype, 'replaceAll', {
writable: true, enumerable: false, configurable: true,
value: function replaceAll(searchValue, newValue){
- return this.replace( RegExp(searchValue, 'g'), newValue );
+ let flags = searchValue.flags || 'g';
+ if (!flags.includes('g')){
+ flags += 'g';
+ }
+ return this.replace( new RegExp(searchValue, flags), newValue );
}
});
}
diff --git a/greasyfork_better_page_titles.user.js b/greasyfork_better_page_titles.user.js
new file mode 100644
index 0000000..79345c8
--- /dev/null
+++ b/greasyfork_better_page_titles.user.js
@@ -0,0 +1,50 @@
+// ==UserScript==
+// @name Better Greasy Fork Page Titles
+// @namespace https://github.com/StaticPH
+// @match https://greasyfork.org/*/scripts/*
+// @match https://sleazyfork.org/*/scripts/*
+// @version 1.0.0
+// @createdAt 11/15/2025, 3:33:58 PM
+// @author StaticPH
+// @description Include userscript descriptions in page titles on Greasy Fork and Sleazy Fork
+// @license MIT
+// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/greasyfork_better_page_titles.user.js
+// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/greasyfork_better_page_titles.user.js
+// @homepageURL https://github.com/StaticPH/UserScripts
+// @supportURL https://github.com/StaticPH/UserScripts/issues
+// @icon https://greasyfork.org/vite/assets/blacklogo16-DftkYuVe.png
+// @grant none
+// @noframes
+// @run-at document-end
+// ==/UserScript==
+
+(function(){
+ "use strict";
+
+ // Get the description from the visible element, rather than from
+ // the meta tag in document.head; the latter changes based on
+ // which tab (Info/Code/History/Feedback/Stats) is active.
+ const descEle = document.querySelector('#script-description');
+ // Guard against a missing description element (which should never occur).
+ const descValue = descEle !== null ? descEle.textContent.trim() : 'No description';
+ // Guard against the description element's text being only whitespace or an empty string.
+ const desc = descValue !== '' ? descValue : 'No description';
+
+ // Get the script title from the visible element, rather than from
+ // the existing document title; the latter changes based on
+ // which tab (Info/Code/History/Feedback/Stats) is active.
+ const scriptTitleEle = document.querySelector('#script-info > header > h2');
+ // Fallback to actual page title only if necessary, which is less desirable because it makes extraKeyword seem out of place on all but the Info tab.
+ const scriptTitle = scriptTitleEle !== null ? scriptTitleEle.textContent.trim() : document.title;
+
+ // For the purposes of searching through bookmarks and the like,
+ // ensure that the keyword "userscript" is always present in the title
+ const extraKeyword = scriptTitle.toLowerCase().includes('userscript') ? '' : ' userscript';
+
+ // If not viewing the first tab (Info), include the name of the tab in the updated title.
+ const tabLabelEle = document.querySelector('#script-links > .current:not(:first-of-type)');
+ const tabLabel = tabLabelEle !== null ? ` (${tabLabelEle.textContent.trim().split(' ')[0]})` : '';
+
+ document.title = `${scriptTitle}${extraKeyword}${tabLabel} — ${desc}`;
+
+})();
diff --git a/izzyondroid_description_in_title.user.js b/izzyondroid_description_in_title.user.js
new file mode 100644
index 0000000..7b22e9b
--- /dev/null
+++ b/izzyondroid_description_in_title.user.js
@@ -0,0 +1,34 @@
+// ==UserScript==
+// @name Better IzzyOnDroid App Titles
+// @namespace https://github.com/StaticPH
+// @match *://apt.izzysoft.de/fdroid/index/apk/*
+// @match *://android.izzysoft.de/repo/apk/*
+// @version 1.1.0
+// @createdAt 5/2/2024, 5:33:04 PM
+// @author StaticPH
+// @description Adds app description to page titles where possible.
+// @license MIT
+// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/izzyondroid_description_in_title.user.js
+// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/izzyondroid_description_in_title.user.js
+// @homepageURL https://github.com/StaticPH/UserScripts
+// @supportURL https://github.com/StaticPH/UserScripts/issues
+// @icon https://apt.izzysoft.de/fdroid/izzy-on-droid.png
+// @grant none
+// @run-at document-end
+// @noframes
+// ==/UserScript==
+
+(function(){
+ "use strict";
+
+ const parts = document.title.split('- IzzyOnDroid');
+ const tail = parts.pop();
+ let summary = document.querySelector('#summary').textContent;
+ summary = summary[0].toLocaleUpperCase() + summary.slice(1);
+ parts.push('- ' + summary + ' | IzzyOnDroid' + tail);
+ document.title = parts.join('');
+
+ // Add convenient link to alternate between the two places to view app details.
+ const anchorUrl = 'https://' + (document.location.host.startsWith('apt') ? 'android.izzysoft.de/repo' : 'apt.izzysoft.de/fdroid/index') + '/apk/' + document.location.pathname.split('/apk/').pop();
+ document.querySelector('#appdetails > h2').insertAdjacentHTML('beforeBegin', '(Switch Site View)
');
+})();
diff --git a/legacy_browser_workarounds/discourse_forum_scroll_fixer.user.js b/legacy_browser_workarounds/discourse_forum_scroll_fixer.user.js
index 66488a7..c4501c3 100644
--- a/legacy_browser_workarounds/discourse_forum_scroll_fixer.user.js
+++ b/legacy_browser_workarounds/discourse_forum_scroll_fixer.user.js
@@ -2,9 +2,14 @@
// @name Legacy Discussion Forum Scroll Fixer
// @namespace https://github.com/StaticPH
// @match *://community.blokada.org/*
+// @match *://community.brave.app/*
// @match *://community.cloudflare.com/*
+// @match *://community.latenode.com/*
+// @match *://community.osr.com/*
+// @match *://community.retool.com/*
// @match *://community.syncromsp.com/*
// @match *://discourse.cmake.org/*
+// @match *://discuss.dev.twitch.com/*
// @match *://discuss.gradle.org/*
// @match *://discuss.python.org/*
// @match *://discussions.unity.com/*
@@ -13,10 +18,13 @@
// @match *://forum.graphviz.org/*
// @match *://forum.kee.pm/*
// @match *://forum.manjaro.org/*
+// @match *://forum.obsidian.md/*
// @match *://forums.comodo.com/*
// @match *://forums.spongepowered.org/*
+// @match *://forum.freecodecamp.org/*
+// @match *://meta.discourse.org/*
// @match *://talk.restarters.net/*
-// @version 1.0.3
+// @version 1.0.5
// @createdAt 6/29/2024, 6:24:11 PM
// @author StaticPH
// @description Fix for Discourse forums breaking the ability to scroll on browsers that are too old or are blocking some script or other.
@@ -36,10 +44,10 @@
'use strict';
// Prefer asychronous Greasemonkey4-API GM.addStyle, but allow use of GM_addStyle as a fallback if necessary.
- if (typeof GM == 'undefined'){
+ if (typeof GM === 'undefined'){
this.GM = {};
}
- if (typeof GM['addStyle'] == 'undefined'){
+ if (GM['addStyle'] === undefined){
console.log('GM.addStyle is not defined. Falling back to GM_addStyle Promise.');
GM['addStyle'] = function(...args){
return new Promise((onResolve, onReject) => {
diff --git a/legacy_browser_workarounds/github_collapsed_details_workaround.user.js b/legacy_browser_workarounds/github_collapsed_details_workaround.user.js
index ba3a2ff..2521d49 100644
--- a/legacy_browser_workarounds/github_collapsed_details_workaround.user.js
+++ b/legacy_browser_workarounds/github_collapsed_details_workaround.user.js
@@ -9,6 +9,7 @@
// @exclude-match https://github.com/enterprise*
// @exclude-match https://github.com/explore*
// @exclude-match https://github.com/features*
+// @exclude-match https://github.com/github-copilot/*
// @exclude-match https://github.com/login/*
// @exclude-match https://github.com/marketplace*
// @exclude-match https://github.com/new*
@@ -25,7 +26,7 @@
// @exclude-match https://github.com/topics*
// @exclude-match https://github.com/trending*
// @exclude-match https://github.com/users/*/projects/*
-// @version 1.2
+// @version 1.2.1
// @createdAt 9/30/2022, 9:32:58 PM
// @author StaticPH
// @description Add simple onclick handlers to the collapsed details of commits on GitHub, as the normal behavior of expanding the ellipses to the full commit message when clicked seems to have broken on legacy browsers as a result of some change to the implementation. Also fixes some other instances of non-functioning collapsing elements.
@@ -64,10 +65,13 @@
// let commitRow = evnt.target.closest('.Details').querySelector('p+*');
let commitRow = evnt.target.closest('.Details').querySelector('.Details-content--hidden, .Details-content--on') || evnt.target.closest('.Details').querySelector('.text-small').parentElement;
commitRow.classList.toggle('Details-content--hidden');
- hasRefinedGithub && commitRow.closest('.rgh-dim-bot').classList.toggle('Details--on');
+ if (hasRefinedGithub){
+ commitRow.closest('.rgh-dim-bot').classList.toggle('Details--on');
+ }
}
- document.querySelectorAll('.hidden-text-expander > button.ellipsis-expander.js-details-target').forEach(expander => expander.onclick = toggleShowDetails);
+ /* oxlint-disable-next-line unicorn/prefer-add-event-listener */
+ document.querySelectorAll('.hidden-text-expander > button.ellipsis-expander.js-details-target').forEach(expander => (expander.onclick = toggleShowDetails));
if (urlRelativeToRepoDefault.startsWith('/wiki')){
// Fix collapsed categories on wiki page sidebar.
@@ -110,9 +114,9 @@
evnt.target.parentElement.querySelector('[data-target="annotation-message.showLessButton"]').toggleAttribute('hidden');
}
else { // Normally implies that classList contains 'annotation--expanded'
- // replace 'annotation--expanded' with 'annotation--contracted' if the former exists, otherwise just add the latter;
- // the second case is a fallback in case the class was previously just removed entirely, rather than replaced.
- annotationBody.classList.replace('annotation--expanded', 'annotation--contracted') || annotationBody.classList.add('annotation--contracted');
+ // Always attempt to remove the `annotation--expanded` class from annotationBody, and ensure it always has the `annotation--contracted` class.
+ annotationBody.classList.remove('annotation--expanded');
+ annotationBody.classList.add('annotation--contracted');
evnt.target.toggleAttribute('hidden');
evnt.target.parentElement.querySelector('[data-target="annotation-message.showMoreButton"]').toggleAttribute('hidden');
}
diff --git a/legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js b/legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js
index e23c952..28aafc8 100644
--- a/legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js
+++ b/legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js
@@ -2,7 +2,7 @@
// @name GitHub Lazy Release Assets Legacy Workaround
// @namespace https://github.com/StaticPH
// @match https://github.com/*/*/releases*
-// @version 1.1
+// @version 1.1.2
// @createdAt 10/8/2022, 4:40:25 PM
// @author StaticPH
// @description Fixes a number of things related to user-downloadable asset files on GitHub for users of legacy browsers.
@@ -40,11 +40,16 @@
'method': 'GET',
'mode': 'cors'
};
- const timestampPreset = { month: 'short', year: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' };
+ const timestampPreset = new Intl.DateTimeFormat(navigator.language, {
+ numberingSystem: 'ltn', calendar: 'gregory',
+ year: 'numeric', month: 'short', day: 'numeric',
+ hour: 'numeric', hour12: true, minute: 'numeric', second: 'numeric'
+ });
async function localizeTimestamp(dateObj){
- return await dateObj.toLocaleString(navigator.language, timestampPreset);
+ return await timestampPreset.format(dateObj);
}
+ /*
// While on the topic of fixing things, fix all the timestamps on the page that don't seem to want to display.
// Use the same format as GitHub would be, by utilizing the attributes of the local-time elements.
async function fixDate(ele){
@@ -56,25 +61,29 @@
});
}
document.querySelectorAll('section local-time:first-of-type').forEach(fixDate);
+ */
// Similar to fixDate, but tailored for relative-time rather than local-time
// FIXME: the format for relative times should probably be different. Check and confirm.
async function fixTime(ele){
- ele.textContent = await new Date(ele.getAttribute('datetime')).toLocaleString(navigator.language, {
- month: ele.getAttribute('month') || undefined,
- day: ele.getAttribute('day') || undefined,
- year: ele.getAttribute('year') || undefined
- });
+ // ele.textContent = await new Date(ele.getAttribute('datetime')).toLocaleString(navigator.language, {
+ // month: ele.getAttribute('month') || undefined,
+ // day: ele.getAttribute('day') || undefined,
+ // year: ele.getAttribute('year') || undefined
+ // });
+ //// Screw it, just use absolute time for the locale.
+ ele.textContent = await localizeTimestamp(new Date(ele.getAttribute('datetime')));
}
+ document.querySelectorAll('section relative-time:first-of-type').forEach(fixTime);
- document.querySelectorAll('[data-view-component] include-fragment[loading="lazy"][src]:not([src=""])').forEach(async function(lazyPageFrag){
+ document.querySelectorAll('[data-view-component] include-fragment[loading="lazy"][src]:not([src=""]):not([data-target="select-panel.includeFragment"])').forEach(async function(lazyPageFrag){
try{
await fetch(lazyPageFrag.getAttribute('src'), requestTemplate).then(
resp => resp.text().then(
async function(html){
await lazyPageFrag.replaceChildren(...parser.parseFromString(html, 'text/html').querySelectorAll('body > [data-view-component]'));
- await lazyPageFrag.querySelectorAll('local-time').forEach(await fixDate);
- await lazyPageFrag.querySelectorAll('relative-time').forEach(await fixTime); // FIXME: the format for relative times should probably be different.
+ // await lazyPageFrag.querySelectorAll('local-time').forEach(await fixDate);
+ await lazyPageFrag.querySelectorAll('relative-time').forEach(await fixTime);
},
err => console.warn('There was a problem retrieving the response text', err)
),
@@ -91,8 +100,8 @@
resp => resp.text().then(
async function(html){
await deferredPageFrag.replaceChildren(...parser.parseFromString(html, 'text/html').querySelectorAll('body > [data-view-component]'));
- await deferredPageFrag.querySelectorAll('local-time').forEach(await fixDate);
- await deferredPageFrag.querySelectorAll('relative-time').forEach(await fixTime); // FIXME: the format for relative times should probably be different.
+ // await deferredPageFrag.querySelectorAll('local-time').forEach(await fixDate);
+ await deferredPageFrag.querySelectorAll('relative-time').forEach(await fixTime);
},
err => console.warn('There was a problem retrieving the response text', err)
),
diff --git a/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js b/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js
new file mode 100644
index 0000000..f3afa5c
--- /dev/null
+++ b/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js
@@ -0,0 +1,272 @@
+// ==UserScript==
+// @name GitHub Issue Comments Legacy Workaround
+// @namespace https://github.com/StaticPH
+// @match https://github.com/*/*/issues/*
+// @version 1.1.2
+// @createdAt 11/10/2025, 6:17:25 PM
+// @author StaticPH
+// @description Manually display comments on Github issues using the JSON that's already on the page, which totally doesn't need React to accomplish.
+// @license MIT
+// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js
+// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js
+// @homepageURL https://github.com/StaticPH/UserScripts
+// @supportURL https://github.com/StaticPH/UserScripts/issues
+// @icon https://github.githubassets.com/pinned-octocat.svg
+// @grant none
+// @noframes
+// @run-at document-idle
+// ==/UserScript==
+
+(function(){
+ "use strict";
+
+ const reactionIconHTML = '';
+ const reactionToolbarHTML = ``;
+
+ const fixedStyles = `
+ .dShPvE {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ border-radius: 6px;
+ background-color: var(--bgColor-default,var(--color-canvas-default,#ffffff));
+ transition: all 0.2s ease 0s;
+ border-width: 1px;
+ border-style: solid;
+ border-image: initial;
+ border-color: var(--borderColor-default,var(--color-border-default,#d0d7de));
+ padding-top: 0px;
+ padding-bottom: 0px;
+ }
+ .bkHoaI { opacity: 0; }
+ .PphTR {
+ background-color: var(--bgColor-muted,var(--color-canvas-subtle,#f6f8fa));
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
+ border-bottom-style: solid;
+ border-color: var(--borderColor-default,var(--color-border-default,#d0d7de));
+ border-bottom-width: 1px;
+ padding: 4px 4px 4px 16px;
+ }
+ [class*="IssueBodyHeader-module__titleSection__"],
+ [class*="ActivityHeader-module__TitleContainer__"] {
+ padding-right: .45ch;
+ }
+
+ /* FIXME: Honestly, these need to be their own script */
+ /* Fix bad padding creating weird-looking empty space on the right side */
+ /*body:not(.wgh-disabled)*/ .application-main div[data-target="react-app.reactRoot"] div[class^='ContentWrapper-module__contentContainer__'] {
+ padding-left: 0px;
+ padding-right: 0px;
+ }
+ /* Fix repository navigation tabs being clipped if the window is too narrow */
+ .UnderlineNav {
+ overflow: auto hidden !important;
+ }`;
+
+ function mapResponseToNode(response){
+ const node = response.node;
+ // Known __typename values include: [
+ // 'IssueComment', 'LabeledEvent', 'ClosedEvent', 'CrossReferencedEvent'
+ // ]
+ if (node.__typename === 'IssueComment' && node.author === null){
+ // Each *COMMENT* item in a response node has the following properties
+ // (non-exhaustive): {
+ // author:{avatarUrl, id, login, name, profileUrl} || null,
+ // lastUserContentEdit:{editor:{id, login, url, __typename} || null, id},
+ // bodyHTML, bodyVersion, createdAt, databaseId, id, lastEditedAt,
+ // reactionGroups, url,
+ // __typename: 'User'
+ // }
+ // Because comments authored by deleted users have a null value for `author`, author properties for such comments require a little fudging.
+ node.author = {
+ login: 'ghost',
+ profileUrl: '/ghost',
+ avatarUrl: 'https://avatars.githubusercontent.com/u/10137', // 'https://github.com/ghost.png',
+ __typename: 'User',
+ }
+ }
+ /*
+ else if (node.__typename === 'LabeledEvent){
+ // These items in a response node has the following properties
+ // (non-exhaustive): {
+ // actor: {} || null,
+ // label: {id, nameHTML, name, color, description}
+ // createdAt, databaseId, id,
+ // __typename: 'LabeledEvent'
+ // }
+ }
+ else if (node.__typename === 'ClosedEvent'){
+ // These items in a response node has the following properties
+ // (non-exhaustive): {
+ // actor: ???? || null,
+ // closer: ???? || null,
+ // closingProjectItemStatus: ???? || null,
+ // duplicateOf: ???? || null,
+ // createdAt, databaseId, id, stateReason,
+ // __typename: 'ClosedEvent'
+ // }
+ }
+ else if (node.__typename === 'CrossReferencedEvent'){
+ // These items in a response node has the following properties
+ // (non-exhaustive): {
+ // actor: {avatarUrl, id, login, name:????||null, profileResourcePath},
+ // innerSource: {__typename, __isReferencedSubject, id, issueTitleHTML, number, repository: {id, name, owner:{login, id}}, stateReason, url},
+ // source: {__typename, id},
+ // createdAt, databaseId, id, referencedAt, willCloseTarget,
+ // __typename: 'CrossReferencedEvent'
+ // }
+ }
+ */
+ return node;
+ }
+
+ const dataEle = document.querySelector('[data-target="react-app.embeddedData"]');
+ const embeddedData = JSON.parse(dataEle.textContent);
+ const responses = embeddedData.payload.preloadedQueries[0].result.data.repository.issue.frontTimelineItems.edges;
+ // For some reason I've yet to figure out, there seems to cases where
+ // some comments aren't included in ...frontTimelineItems.edges,
+ // and thus cant be inserted by this script. I suspect it's some kind of
+ // limit to an API, but I'm not sure how to go about compensating.
+ const responseNodesData = responses.map(mapResponseToNode);
+
+ const friendlyTimeFormatter = new Intl.DateTimeFormat(navigator.language, {
+ numberingSystem: 'ltn', calendar: 'gregory',
+ year: 'numeric', month: 'long', day: 'numeric',
+ hour: 'numeric', hour12: true, minute: 'numeric', second: 'numeric'
+ });
+
+ function buildTimeElement(timestamp){
+ // Until I come up with a way to make relative-time-element load
+ // (instead of being skipped due to earlier parsing errors),
+ // just use the absolute time in a friendly format
+ return `${
+ friendlyTimeFormatter.format(new Date(timestamp))
+ } `;
+ }
+
+ function buildCommentNode(commentNode){
+ return `\t`;
+ }
+
+ function buildTimelineNodes(){
+ // TODO: handle nodes that aren't actually comments, i.e. those where __typename != 'IssueComment'
+ // Will probably want to use a delegating method to determine whether a given node will be a
+ // comment or what-have-you, which impacts the properties available on each edge.node object.
+ // For the time being, just handle the actual comments.
+ return Array.from(
+ responseNodesData.filter(e => e.__typename === 'IssueComment'),
+ buildCommentNode
+ ).join('\n');
+ }
+
+ function fixIssueTimeline(){
+ const commentContainer = document.querySelector('div.react-comments-container > div[class*="IssueViewer-module__commentsContainer"]');
+ const frag = document.createDocumentFragment();
+ const substContainer = commentContainer.cloneNode();
+
+ // fix borked styles
+ substContainer.insertAdjacentHTML('beforeEnd', ``);
+
+ substContainer.insertAdjacentHTML('beforeEnd', `Activity
\n\n${buildTimelineNodes()}\n`);
+ // TODO: figure out if replacing the above with something using generators is more efficient
+ frag.append(substContainer);
+ commentContainer.replaceWith(frag);
+ }
+
+ function redoIssueLabelTooltips(){
+ // Add tooltips for long descriptions to Issue labels.
+ /*
+ document.querySelectorAll('[aria-describedby*="-tooltip"]').forEach(function(ele){
+ const tooltipIDSelector = ele.getAttribute('aria-describedby').split(' ').map(s => `[id="${s}"]`).join(',');
+ // Even if there may be multiple IDs, as the spec permits, take whatever is found first.
+ const descriptor = document.querySelector(tooltipIDSelector);
+
+ if(!descriptor){
+ console.warn(`ERROR: unable to find an element matching the selector "${tooltipIDSelector}"`);
+ return;
+ }
+ ele.title = descriptor.textContent.trim();
+ descriptor.remove();
+ ele.removeAttribute('aria-describedby');
+ });
+ */
+
+ // Has a similar overall effect, but less explicit DOM manipulation, I guess?
+ document.head.insertAdjacentHTML('beforeEnd', `
+ `);
+ // The attribute `role="tooltip"` isn't already added by GitHub, even though aria-describedby is.
+ // Not sure what was expected to happen like that.
+ document.querySelectorAll('.sr-only[id*="-tooltip"]').forEach(e => e.setAttribute('role', 'tooltip'));
+ }
+
+ fixIssueTimeline();
+ redoIssueLabelTooltips();
+
+ /*
+ document.querySelectorAll('[aria-label="Reactions"]').forEach(function(ele){
+ const btn = ele.querySelector('button[aria-labelledby=":rr:"]');
+ if (!btn || btn.title){ return; } // No need to substitute a tooltip
+ if (btn.ariaDescribedBy === ':rq:-loading-announcement){
+ btn.removeAttribute('aria-describedby');
+ }
+ const oldLabelEle = ele.querySelector('[id=":rr:"]');
+ if (!oldLabelEle){ return; } // No need to substitute a tooltip
+ btn.title = oldLabelEle.textContent;
+ oldLabelEle.remove(); // Will likely break the reaction menu popover, if I ever got around to fixing it in a way that resembles the modern standard behavior.
+ });
+ */ // Hardcoded simplification, since the dummy reactions buttons are just constant raw HTML being applied by this script anyways.
+})();
diff --git a/legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js b/legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js
index e86bdbf..1f40961 100644
--- a/legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js
+++ b/legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js
@@ -8,7 +8,7 @@
// @match https://stackexchange.com/*
// @match https://*.stackoverflow.com/*
// @match https://*.stackexchange.com/*
-// @version 1.1
+// @version 1.2.0
// @createdAt 11/6/2022, 1:17:14 AM
// @author StaticPH
// @description Replace 'Show X more comments' handler for StackExchange sites to better support older browsers; in particular, this enables showing all comments when using Chromium 72.
@@ -20,7 +20,7 @@
// @icon https://cdn.sstatic.net/Sites/stackoverflow/Img/favicon.ico
// @grant none
// @noframes
-// @run-at document-load
+// @run-at document-idle
// ==/UserScript==
(function(){
@@ -28,7 +28,7 @@
function replaceChildrenWithNodes(parentNode, ...newChildren){
while (parentNode.lastChild){
- parentNode.removeChild(parentNode.lastChild);
+ parentNode.lastChild.remove();
}
if (newChildren !== undefined){
const replacements = (newChildren.length === 1 && Array.isArray(newChildren[0])) ? newChildren[0] : newChildren;
@@ -38,12 +38,94 @@
return false;
}
- async function replaceChildrenWithFetched(parentEle, rawHtml){
+ function replaceChildrenWithFetched(parentEle, rawHtml){
const parser = new DOMParser();
const responseHtml = parser.parseFromString(rawHtml, 'text/html');
return replaceChildrenWithNodes(parentEle, ...responseHtml.querySelectorAll('li'));
}
+ function replaceCommentsListFromFetched(insertInto, data){
+ const parser = new DOMParser();
+ const responseHtml = parser.parseFromString(`${data}
`, 'text/html');
+ return replaceChildrenWithNodes(insertInto, responseHtml.body.firstElementChild);
+ }
+
+ const replySVG = '';
+ const commentUpvoteSVG = '';
+ const ellipsesSVG = '';
+
+ // oxlint-disable no-unused-vars
+ function buildReplyTemplate(reply, postID){
+ return `
+
+
+
+
+
+
+
+
+
+
+
+ ${reply.user.displayName}
+
+
+
+
+
+
+
+
+
+
+
+ ${reply.htmlBody}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+
function onExpandComments(evnt){
if (evnt.target.matches('.answer a.js-show-link, .question a.js-show-link')){
evnt.stopImmediatePropagation();
@@ -52,14 +134,14 @@
const postID = answer.getAttribute('data-answerid') || answer.getAttribute('data-questionid');
const insertInto = answer.querySelector('ul.comments-list');
fetch(document.location.origin + '/posts/' + postID + '/comments', {
- 'headers': {
- 'accept': 'text/html, */*; q=0.01',
- },
- 'referrerPolicy': 'strict-origin-when-cross-origin',
- 'body': null,
- 'method': 'GET',
- 'mode': 'cors',
- 'credentials': 'include'
+ 'headers': {
+ 'accept': 'text/html, */*; q=0.01',
+ },
+ 'referrerPolicy': 'strict-origin-when-cross-origin',
+ 'body': null,
+ 'method': 'GET',
+ 'mode': 'cors',
+ 'credentials': 'include'
}).then(resp => resp.text())
/*
.then(data => replaceChildrenWithFetched(insertInto, data))
@@ -68,9 +150,43 @@
insertInto.setAttribute('data-remaining-comments-count', '0');
});
*/
- .then(data => replaceChildrenWithFetched(insertInto, data) && evnt.target.parentElement.remove(), insertInto.setAttribute('data-remaining-comments-count', '0'));
+ .then(data => replaceChildrenWithFetched(insertInto, data) && evnt.target.parentElement.remove())
+ .catch(insertInto.setAttribute('data-remaining-comments-count', '0'));
+ }
+ else if (evnt.target.matches('.answer button.comments-link:not([data-so-test*="parent-answer"])')){
+ evnt.stopImmediatePropagation();
+ evnt.preventDefault();
+ const answer = evnt.target.closest('.answer');
+ const postID = answer.getAttribute('data-answerid') || answer.getAttribute('data-questionid');
+ const followupScriptEle = answer.querySelector('script[type="application/json"]');
+ const followupData = JSON.parse(followupScriptEle.textContent);
+ const insertInto = answer.querySelector(`#${followupData.containerElementId} [role="list"]`);
+ // for (reply of followupData.replies){
+ // insertInto.insertAdjacentHTML('beforeEnd', buildReplyTemplate(reply, postID)); // Doesn't fix the order to be chronological, and that seems like a hassle.
+ //}
+
+ fetch(`${document.location.origin}/posts/${postID}/comments`, {
+ 'headers': {
+ 'accept': 'text/html, */*; q=0.01',
+ },
+ 'referrerPolicy': 'strict-origin-when-cross-origin',
+ 'body': null,
+ 'method': 'GET',
+ 'mode': 'cors',
+ 'credentials': 'include'
+ }).then(resp => resp.text())
+ .then(data => requestAnimationFrame(function(){
+ if (replaceCommentsListFromFetched(insertInto, data)){
+ evnt.target.parentElement.remove();
+ }
+ insertInto.setAttribute('data-remaining-comments-count', '0');
+ }));
}
}
+
+ // Relevant if using buildReplyTemplate
+ // document.head.insertAdjacentHTML('beforeEnd', '');
+
document.addEventListener('click', onExpandComments);
})();
diff --git a/minecraft_curseforge_title_tweaks.user.js b/minecraft_curseforge_title_tweaks.user.js
index b18ac9b..ae798b9 100644
--- a/minecraft_curseforge_title_tweaks.user.js
+++ b/minecraft_curseforge_title_tweaks.user.js
@@ -3,7 +3,7 @@
// @namespace https://github.com/StaticPH
// @match https://www.curseforge.com/minecraft/mc-mods/*
// @match https://legacy.curseforge.com/minecraft/mc-mods/*
-// @version 1.1
+// @version 1.1.1
// @createdAt 4/20/2022, 5:46:34 PM
// @author StaticPH
// @description Modifies the format of the page title for some of CurseForge's Minecraft pages.
@@ -19,7 +19,7 @@
(function(){
"use strict";
- if (document.location.href.match(/search\?/)){
+ if (document.location.pathname.endsWith('search')){
let queryMatches = document.location.search.match(/search=([^&?#]+)(?:[&?#].+)?$/);
if (queryMatches && queryMatches[1] !== undefined) {
document.title = 'CurseForge Minecraft Mod Search - ' + queryMatches[1];
@@ -48,10 +48,10 @@
}
document.title += ' - CurseForge Minecraft Mods';
}
- if (document.title.match('Mods - Projects - CurseForge')){
+ if (document.title.includes('Mods - Projects - CurseForge')){
document.title = document.title.replace('Mods - Projects - CurseForge', '') + 'CurseForge Minecraft Projects';
}
- else if (document.title.match('Mods - Minecraft - CurseForge')){
+ else if (document.title.includes('Mods - Minecraft - CurseForge')){
document.title = document.title.replace('Mods - Minecraft - CurseForge', '') + 'CurseForge Minecraft Mods';
}
}
diff --git a/mitigate_target_blank_risk.user.js b/mitigate_target_blank_risk.user.js
index 2923f66..be0b920 100644
--- a/mitigate_target_blank_risk.user.js
+++ b/mitigate_target_blank_risk.user.js
@@ -47,7 +47,7 @@
let splitRel;
function cleanse(){
// Unsure whether to prefer getElementByTagName('a')+if target=='_blank' OR querySeletorAll('a[target="_blank"]')
- Array.from(document.getElementsByTagName('a')).forEach( (link) => {
+ Array.from(document.querySelectorAll('a')).forEach( (link) => {
if (link.target == '_blank'){
if (allowedOrigins.includes(link.origin)){
console.log('Found link with allowed origin:"' + link.origin + '" and target="_blank". The link\'s "rel" attribute will not be modified.');
diff --git a/msys2_package_description_in_title.user.js b/msys2_package_description_in_title.user.js
index 2072e8e..28db64e 100644
--- a/msys2_package_description_in_title.user.js
+++ b/msys2_package_description_in_title.user.js
@@ -1,12 +1,13 @@
// ==UserScript==
// @name MSYS Packages Description In Title
// @namespace https://github.com/StaticPH
+// @include http*://packages.msys2.org/base/*
// @include http*://packages.msys2.org/package/*
// @include http*://packages.msys2.org/packages/*
-// @version 1.2.0
+// @version 1.2.1
// @createdAt 4/28/2021
// @author StaticPH
-// @description Include the package description on the tab title for a package's page on packages.msys2.org/packages.
+// @description Include the package description on the tab title for a package's page on packages.msys2.org.
// @license MIT
// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/msys2_package_description_in_title.user.js
// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/msys2_package_description_in_title.user.js
@@ -22,12 +23,16 @@
setTimeout(function wait(){
const pkgName = document.querySelector('h4.card-title');
- const pkgDesc = document.querySelector('h6.card-subtitle, .card-body > dl > dd:nth-child(6)');
-
- //TODO: Modify script to also indicate which pkg subpage is currently loaded (if it isnt the readme subpage)
+ let pkgDesc = null;
+ if (document.location.pathname.startsWith('/base/')){
+ pkgDesc = document.querySelector('.card-body > dl > dd:nth-child(2)');
+ }
+ else{
+ pkgDesc = document.querySelector('h6.card-subtitle, .card-body > dl > dd:nth-child(6)');
+ }
if (pkgName && pkgDesc){
- document.title=`${pkgName.textContent.trim()} — ${pkgDesc.textContent.trim()}`;
+ document.title = `${pkgName.textContent.trim()} — ${pkgDesc.textContent.trim()}`;
}
else{
setTimeout(wait, 100); // Continue trying every 100ms until success
diff --git a/novelupdates_reading_list_upgrades.user.js b/novelupdates_reading_list_upgrades.user.js
index 0841a1a..8332f24 100644
--- a/novelupdates_reading_list_upgrades.user.js
+++ b/novelupdates_reading_list_upgrades.user.js
@@ -120,6 +120,7 @@
// document.querySelectorAll('.bmhide > :not(.nu_editnotes):first-child') // get not caught up
// document.querySelectorAll('.bmhide > span[class*="bm_hide_me"]:first-child') // alternative to get not caught up
const caughtUpHelper = {
+ /* oxlint-disable unicorn/prefer-query-selector */
mainTbl: document.getElementById('myTable read'),
hiddenTbl: (function(){
const tbl = document.createElement('table');
@@ -216,7 +217,9 @@
// Fuck tracking and analytics
document.querySelectorAll('script').forEach(s => (s.textContent.includes('urchinTracker') || s.src.includes('analytic')) && s.remove());
- settings.improvePageTitle && improveTitle();
+ if (settings.improvePageTitle){
+ improveTitle();
+ }
caughtUpHelper.init();
})();
\ No newline at end of file
diff --git a/quietly_reject_stackexchange_cookies.user.js b/quietly_reject_stackexchange_cookies.user.js
index edd9ae9..b736228 100644
--- a/quietly_reject_stackexchange_cookies.user.js
+++ b/quietly_reject_stackexchange_cookies.user.js
@@ -29,10 +29,10 @@
'use strict';
// Prefer asychronous Greasemonkey4-API GM.addStyle, but allow use of GM_addStyle as a fallback if necessary.
- if (typeof GM == 'undefined'){
+ if (typeof GM === 'undefined'){
this.GM = {};
}
- if (typeof GM['addStyle'] == 'undefined'){
+ if (GM['addStyle'] === undefined){
console.log('GM.addStyle is not defined. Falling back to GM_addStyle Promise.');
GM['addStyle'] = function(...args){
return new Promise((onResolve, onReject) => {
diff --git a/reddit_hide_by_subreddit.user.js b/reddit_hide_by_subreddit.user.js
index 597dfd6..bed7013 100644
--- a/reddit_hide_by_subreddit.user.js
+++ b/reddit_hide_by_subreddit.user.js
@@ -69,7 +69,7 @@
readSettings();
saveSettings();
- if (settings.hideSubredditsNamed.length < 1){
+ if (settings.hideSubredditsNamed.length === 0){
console.log('No subreddits to hide.');
return;
}
diff --git a/scribblehub_reading_list_upgrades.user.js b/scribblehub_reading_list_upgrades.user.js
index 40166d4..dcea3f4 100644
--- a/scribblehub_reading_list_upgrades.user.js
+++ b/scribblehub_reading_list_upgrades.user.js
@@ -132,7 +132,7 @@
}
const caughtUpHelper = {
- mainTbl: document.getElementsByClassName('rl_table')[0],
+ mainTbl: document.querySelector('.rl_table'),
hiddenTbl: (function(){
const tbl = document.createElement('table');
tbl.id = 'hiddenTbl';
@@ -225,7 +225,9 @@
// Fuck tracking and analytics
document.querySelectorAll('script').forEach(s => (s.textContent.includes('urchinTracker') || s.src.includes('analytic')) && s.remove());
- settings.improvePageTitle && improveTitle();
+ if (settings.improvePageTitle){
+ improveTitle();
+ }
if (settings.ctrlEnterSavesNotes){
document.addEventListener('keyup', saveNotesKeybindHandler);
setTitleForFakeButton(document.querySelector('.rlnotes_btn.savenotes'), true, '', ' (Ctrl+Enter)');
diff --git a/simple_url_tracker_cleaner.user.js b/simple_url_tracker_cleaner.user.js
index 39aa86a..e8d7c41 100644
--- a/simple_url_tracker_cleaner.user.js
+++ b/simple_url_tracker_cleaner.user.js
@@ -2,7 +2,7 @@
// @name Simple URL Tracker Cleaner
// @namespace https://github.com/StaticPH
// @match *://*/*
-// @version 1.6.2
+// @version 1.6.4
// @createdAt 8/10/2021
// @author StaticPH
// @description Scrub various common tracker parameters from URLs.
@@ -24,21 +24,41 @@
// Put the damn query parameter first so it's predictable
const args = href.split('&');
const queryIndex = args.findIndex(s => s.startsWith('q='));
- args[0] = args[0].replace('search?', `search?${args[queryIndex]}&`);
+ if (queryIndex === -1){
+ return href;
+ }
+ const argStart = href.indexOf('?');
+ const pathTail = href.slice(href.lastIndexOf('/', argStart), argStart);
+ args[0] = args[0].replace(`${pathTail}?`, `${pathTail}?${args[queryIndex]}&`);
delete args[queryIndex];
- return args.filter(s => s).join('&');
+ return args.filter(Boolean).join('&');
}
- async function cleanURLs(){
- Array.from(document.links).forEach(async function(link){
+ function fixDanglingQuery(href){
+ return href.replace(/[?&]*#/, '#').replace(/[?&]*$/, '');
+ }
+
+ const commonRegex = {
+ stripAll: /[&?].+$/,
+ stripSearch: /[&?][^#]+/,
+ findUniversal: /(?:[?&])(amp;)?(utm_(source|medium|campaign|term|content)|(fb|g)clid)=[^&?#]*[&?#]?/,
+ stripUniversal: /(?:[?&])(amp;)?(utm_(source|medium|campaign|term|content)|(fb|g)clid)=[^&?#]*/g,
+ findGoogleUrlRedir: /google\.[^/]+\/url\?.*/,
+ findGSearchClutter: /google\.[^/]+\/search.*?[&?](ei|sa|ved|bi[wh]|spell|oq|gs_lcp|sclient|uact)=[^&?#]*/,
+ stripGSearchClutter: /&(ei|sa|ved|bi[wh]|spell|oq|gs_lcp|sclient|uact)=[^&?#]*/g,
+ findAmazonProduct: /amazon\.[^/]+\/[^/]+\/dp\/[^/]+\/[^?&]+[?&]/,
+ };
+
+ function cleanURLs(){
+ for (const link of document.links){
let fixed = null;
- if (link.href.match(/google\.[^/]+\/url\?.*/)){
+ if (commonRegex.findGoogleUrlRedir.test(link.href)){
const url = new URL(link.href);
if (url.searchParams.has('url')){
fixed = url.searchParams.get('url');
if (!fixed || fixed.length === 0){ console.warn('Found empty "url" parameter in link: ' + link.href); }
else{
- console.log( link.href + ' --> ' + fixed );
+ console.log(`${link.href} --> ${fixed}`);
link.href = fixed;
}
}
@@ -49,7 +69,7 @@
if (url.searchParams.has('tbs')){ // This is one of the parameters actually worth keeping, as it controls some of the "advanced" filtering
fixed = fixed + '&' + url.searchParams.get('tbs');
}
- console.log( link.href + ' --> ' + fixed );
+ console.log(`${link.href} --> ${fixed}`);
link.href = fixed;
}
else{
@@ -60,34 +80,32 @@
console.warn('Could not find expected "url" or "q" parameter for link: ' + link.href);
}
}
- if (link.href.match(/(?:[?&])(amp;)?(utm_(source|medium|campaign|term|content)|(fb|g)clid)=[^&?#]*[&?#]?/)){
- fixed = link.href.replace(/(?:[?&])(amp;)?(utm_(source|medium|campaign|term|content)|(fb|g)clid)=[^&?#]*/g, '').replace(/[?&]*#/, '#').replace(/[?&]*$/, '');
- console.log( link.href + ' --> ' + fixed );
+ if (commonRegex.findUniversal.test(link.href)){
+ fixed = fixDanglingQuery(link.href.replace(commonRegex.stripUniversal, ''));
+ console.log(`${link.href} --> ${fixed}`);
link.href = fixed;
}
- // if (link.href.match(/google\.[^/]+\/search\?q=.+/)){
- if (link.href.match(/google\.[^/]+\/search.*?[&?](ei|sa|ved|bi[wh]|spell|oq|gs_lcp|sclient|uact)=[^&?#]*/)){
+ if (commonRegex.findGSearchClutter.test(link.href)){
// fixed = link.href.replace(/&(ei|sa|ved|bi[wh]|spell|oq|gs_lcp|sclient|uact)=[^&?#]*/g, '');
// fixed = link.href.replace(/\?([^=]+=[^]*((&[^=]+=[^]*)+)?&)(q=[^&?#]*)(.*)/, '?$4&$1$5').replace('&&','&'); // This *CAN'T* be the optimal way to handle scenarios where 'q' isn't the first search parameter...
fixed = queryParamFirst(link.href);
- fixed = fixed.replace(/&(ei|sa|ved|bi[wh]|spell|oq|gs_lcp|sclient|uact)=[^&?#]*/g, '');
+ fixed = fixed.replace(commonRegex.stripGSearchClutter, '');
/* Compare performance against:
const oldUrl = new URL(link.href);
['ei','sa','ved','biw','bih','spell','oq','gs_lcp','sclient','uact'].forEach(oldUrl.searchParams.delete);
fixed = oldUrl.href;
*/
- console.log( link.href + ' --> ' + fixed );
+ console.log(`${link.href} --> ${fixed}`);
link.href = fixed;
}
- // TODO: AMAZON URLS
+ // TODO: More AMAZON URLS
// On Amazon product pages in particular, you can remove keywords, ref, dchild, pd*, pf*, qid, and sr
- if (link.href.match(/amazon\.[^/]+\/[^/]+\/dp\/[^/]+\/[^?&]+[?&]/)){
- fixed = link.href.replace(/[&?].+$/, '');
- console.log( link.href + ' --> ' + fixed );
+ if (commonRegex.findAmazonProduct.test(link.href)){
+ fixed = link.href.replace(commonRegex.stripAll, '');
+ console.log(`${link.href} --> ${fixed}`);
link.href = fixed;
}
- });
- return;
+ }
}
cleanURLs();
diff --git a/softpedia_improvements.user.js b/softpedia_improvements.user.js
index 8a3a597..b32da3b 100644
--- a/softpedia_improvements.user.js
+++ b/softpedia_improvements.user.js
@@ -22,7 +22,7 @@
"use strict";
// Replace "outlink"s with their actual link
- document.querySelectorAll('[data-href]').forEach(e => e.href = e.getAttribute('data-href'));
+ document.querySelectorAll('[data-href]').forEach(e => (e.href = e.getAttribute('data-href')));
// Hide ads and other clutter
let noClutter = document.createElement('style');
diff --git a/stackexchange_wide_mode.user.js b/stackexchange_wide_mode.user.js
new file mode 100644
index 0000000..73b1541
--- /dev/null
+++ b/stackexchange_wide_mode.user.js
@@ -0,0 +1,52 @@
+// ==UserScript==
+// @name StackExchange Wide Mode
+// @namespace https://github.com/StaticPH
+// @match https://*.stackexchange.com/*
+// @match https://askubuntu.com/*
+// @match https://mathoverflow.com/*
+// @match https://serverfault.com/*
+// @match https://stackapps.com/*
+// @match https://stackexchange.com/*
+// @match https://stackoverflow.com/*
+// @match https://superuser.com/*
+// @version 1.0.0
+// @createdAt 6/20/2024
+// @author StaticPH
+// @description StackExchange sites should take better advantage of the horizontal screen space, particularly on question pages. This removes the width restrictions on the main content part of those pages.
+// @license MIT
+// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/stackexchange_wide_mode.user.js
+// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/stackexchange_wide_mode.user.js
+// @homepageURL https://github.com/StaticPH/UserScripts
+// @supportURL https://github.com/StaticPH/UserScripts/issues
+// @icon https://cdn.sstatic.net/sites/stackexchange/Img/favicon.ico
+// @grant GM.addStyle
+// @grant GM_addStyle
+// @noframes
+// @run-at document-end
+// ==/UserScript==
+
+(function(){
+ 'use strict';
+
+ // Prefer asychronous Greasemonkey4-API GM.addStyle, but allow use of GM_addStyle as a fallback if necessary.
+ if (typeof GM === 'undefined'){
+ this.GM = {};
+ }
+ if (GM['addStyle'] === undefined){
+ console.log('GM.addStyle is not defined. Falling back to GM_addStyle Promise.');
+ GM['addStyle'] = function(...args){
+ return new Promise((onResolve, onReject) => {
+ try{ onResolve(GM_addStyle.apply(this, args)); }
+ catch(err){ onReject(err); }
+ });
+ }
+ }
+
+ GM.addStyle(`
+ /* Page main content element */
+ body > .container:not(.sillyPriorityBump){ max-width: unset; }
+
+ /* body > .container > #content is the child element that actually contains all the questions and answers, without either sidebar. */
+ body > .container > #content:not(.sillyPriorityBump){ max-width: unset; }
+ `);
+})();
\ No newline at end of file
diff --git a/steam_app_from_webpage.user.js b/steam_app_from_webpage.user.js
index 8d0d8c8..6aaf24a 100644
--- a/steam_app_from_webpage.user.js
+++ b/steam_app_from_webpage.user.js
@@ -3,7 +3,7 @@
// @namespace https://github.com/StaticPH
// @match http*://steamcommunity.com/*
// @match http*://*.steampowered.com/*
-// @version 1.0
+// @version 1.0.1
// @createdAt 11/25/2022, 11:25:51 PM
// @author StaticPH
// @description Another of many scripts attempting to add "Open this in the Steam application" functionality to Steam's webpages. Some CSS borrowed from https://greasyfork.org/en/scripts/454372-open-steam-url
@@ -73,5 +73,5 @@
`);
}
- window.addEventListener('DOMContentLoaded', inject, {once:true});
+ globalThis.addEventListener('DOMContentLoaded', inject, {once:true});
})();
diff --git a/twitch_hide_channel_leaderboard.user.js b/twitch_hide_channel_leaderboard.user.js
index 443b430..348f3e8 100644
--- a/twitch_hide_channel_leaderboard.user.js
+++ b/twitch_hide_channel_leaderboard.user.js
@@ -17,12 +17,12 @@
// @createdAt 6/19/2020
// @author StaticPH
// @description Hides the stupid channel leaderboard on Twitch.tv
-// @icon https://brand.twitch.tv/assets/logos/svg/glitch/purple.svg
// @license MIT
// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/twitch_hide_channel_leaderboard.user.js
// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/twitch_hide_channel_leaderboard.user.js
// @homepageURL https://github.com/StaticPH/UserScripts
// @supportURL https://github.com/StaticPH/UserScripts/issues
+// @icon https://brand.twitch.tv/assets/logos/svg/glitch/purple.svg
// @grant GM.addStyle
// @grant GM_addStyle
// @run-at document-start
@@ -32,10 +32,10 @@
'use strict';
// Prefer asychronous Greasemonkey4-API GM.addStyle, but allow use of GM_addStyle as a fallback if necessary.
- if (typeof GM == 'undefined'){
+ if (typeof GM === 'undefined'){
this.GM = {};
}
- if (typeof GM['addStyle'] == 'undefined'){
+ if (GM['addStyle'] === undefined){
console.log('GM.addStyle is not defined. Falling back to GM_addStyle Promise.');
GM['addStyle'] = function(...args){
return new Promise((onResolve, onReject) => {
@@ -45,10 +45,9 @@
}
}
- GM.addStyle ( `
+ GM.addStyle (`
div.channel-leaderboard.tw-z-default {
display: none;
}
- `
- );
+ `);
})();
diff --git a/twitch_hide_content_disclosure.user.js b/twitch_hide_content_disclosure.user.js
index ca8d0eb..76c25ae 100644
--- a/twitch_hide_content_disclosure.user.js
+++ b/twitch_hide_content_disclosure.user.js
@@ -17,12 +17,12 @@
// @createdAt 6/29/2023
// @author StaticPH
// @description Hides the stupid content disclosure overlay from the video player on Twitch.tv
-// @icon https://brand.twitch.tv/assets/logos/svg/glitch/purple.svg
// @license MIT
// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/twitch_hide_content_disclosure.user.js
// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/twitch_hide_content_disclosure.user.js
// @homepageURL https://github.com/StaticPH/UserScripts
// @supportURL https://github.com/StaticPH/UserScripts/issues
+// @icon https://brand.twitch.tv/assets/logos/svg/glitch/purple.svg
// @grant GM.addStyle
// @grant GM_addStyle
// @run-at document-start
@@ -35,7 +35,7 @@
if (typeof GM === 'undefined'){
this.GM = {};
}
- if (typeof GM['addStyle'] === 'undefined'){
+ if (GM['addStyle'] === undefined){
console.log('GM.addStyle is not defined. Falling back to GM_addStyle Promise.');
GM['addStyle'] = function(...args){
return new Promise((onResolve, onReject) => {
@@ -45,10 +45,9 @@
}
}
- GM.addStyle ( `
+ GM.addStyle (`
#channel-player-disclosures {
display: none;
}
- `
- );
+ `);
})();
diff --git a/twitch_hide_overlay_ads.user.js b/twitch_hide_overlay_ads.user.js
index f8a45ff..a54fab4 100644
--- a/twitch_hide_overlay_ads.user.js
+++ b/twitch_hide_overlay_ads.user.js
@@ -17,12 +17,12 @@
// @createdAt 12/14/2023, 7:34:48 PM
// @author StaticPH
// @description Hides the unacceptable overlay ads on Twitch.tv
-// @icon https://brand.twitch.tv/assets/logos/svg/glitch/purple.svg
// @license MIT
// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/twitch_hide_overlay_ads.user.js
// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/twitch_hide_overlay_ads.user.js
// @homepageURL https://github.com/StaticPH/UserScripts
// @supportURL https://github.com/StaticPH/UserScripts/issues
+// @icon https://brand.twitch.tv/assets/logos/svg/glitch/purple.svg
// @grant GM.addStyle
// @grant GM_addStyle
// @run-at document-start
@@ -32,10 +32,10 @@
'use strict';
// Prefer asychronous Greasemonkey4-API GM.addStyle, but allow use of GM_addStyle as a fallback if necessary.
- if (typeof GM == 'undefined'){
+ if (typeof GM === 'undefined'){
this.GM = {};
}
- if (typeof GM['addStyle'] == 'undefined'){
+ if (GM['addStyle'] === undefined){
console.log('GM.addStyle is not defined. Falling back to GM_addStyle Promise.');
GM['addStyle'] = function(...args){
return new Promise((onResolve, onReject) => {
@@ -45,10 +45,9 @@
}
}
- GM.addStyle ( `
+ GM.addStyle (`
.stream-display-ad__wrapper {
display: none !important;
}
- `
- );
+ `);
})();
diff --git a/twitch_transparent_video_stats.user.js b/twitch_transparent_video_stats.user.js
index 6090678..a7c5e16 100644
--- a/twitch_transparent_video_stats.user.js
+++ b/twitch_transparent_video_stats.user.js
@@ -13,7 +13,7 @@
// @exclude-match https://www.twitch.tv/settings*
// @exclude-match https://www.twitch.tv/turbo*
// @exclude-match https://www.twitch.tv/annual-recap
-// @version 1.1.1
+// @version 1.1.2
// @createdAt 5/19/2021
// @author StaticPH
// @description Makes the video stats overlay 50% transparent
@@ -25,17 +25,17 @@
// @icon https://brand.twitch.tv/assets/logos/svg/glitch/purple.svg
// @grant GM.addStyle
// @grant GM_addStyle
-// @run-at document-start
+// @run-at document-idle
// ==/UserScript==
(function(){
'use strict';
// Prefer asychronous Greasemonkey4-API GM.addStyle, but allow use of GM_addStyle as a fallback if necessary.
- if (typeof GM == 'undefined'){
+ if (typeof GM === 'undefined'){
this.GM = {};
}
- if (typeof GM['addStyle'] == 'undefined'){
+ if (GM['addStyle'] === undefined){
console.log('GM.addStyle is not defined. Falling back to GM_addStyle Promise.');
GM['addStyle'] = function(...args){
return new Promise((onResolve, onReject) => {
@@ -46,7 +46,8 @@
}
GM.addStyle(`
- div.video-player__overlay div.simplebar-scroll-content > div.simplebar-content > div {
+ div.video-player__overlay div.simplebar-scroll-content > div.simplebar-content > div,
+ [data-a-target="player-overlay-video-stats"] {
opacity: 0.5;
}
`);
diff --git a/ubuntu_packages_description_in_title.user.js b/ubuntu_packages_description_in_title.user.js
index 5e1128b..eec846a 100644
--- a/ubuntu_packages_description_in_title.user.js
+++ b/ubuntu_packages_description_in_title.user.js
@@ -20,6 +20,7 @@
(function(){
'use strict';
+ /* oxlint-disable unicorn/no-array-reverse */
const [pkg, repo, sourceSegment, /*lang*/] = document.location.pathname.split('/').reverse();
setTimeout(function wait(){
diff --git a/wider_google_form_fields.user.js b/wider_google_form_fields.user.js
index cef6ba0..c058702 100644
--- a/wider_google_form_fields.user.js
+++ b/wider_google_form_fields.user.js
@@ -22,10 +22,10 @@
"use strict";
// Prefer asychronous Greasemonkey4-API GM.addStyle, but allow use of GM_addStyle as a fallback if necessary.
- if (typeof GM == 'undefined'){
+ if (typeof GM === 'undefined'){
this.GM = {};
}
- if (typeof GM['addStyle'] == 'undefined'){
+ if (GM['addStyle'] === undefined){
console.log('GM.addStyle is not defined. Falling back to GM_addStyle Promise.');
GM['addStyle'] = function(...args){
return new Promise((onResolve, onReject) => {
${commentNode.author.login} commented ${buildTimeElement(commentNode.createdAt)}
+