|
| 1 | +// ==UserScript== |
| 2 | +// @name #Example |
| 3 | +// @namespace https://github.com/#REPLACE:User/Repo#readme |
| 4 | +// @version 1.0.0 |
| 5 | +// @description #REPLACE:Userscript Description |
| 6 | +// @homepageURL https://github.com/#REPLACE:User/Repo#readme#readme |
| 7 | +// @supportURL https://github.com/#REPLACE:User/Repo/issues |
| 8 | +// @license MIT |
| 9 | +// @author #REPLACE:Author name |
| 10 | +// @copyright #REPLACE:Author name (#REPLACE:Author URL) |
| 11 | +// @icon https://raw.githubusercontent.com/#REPLACE:User/Repo/develop/assets/images/logo_48.png?b=3f6dd30 |
| 12 | +// @match #REPLACE:Match URL(s) - i.e. *://*.example.com/* |
| 13 | +// @run-at document-start |
| 14 | +// @downloadURL https://raw.githubusercontent.com/#REPLACE:User/Repo/develop/dist/#Example.user.js |
| 15 | +// @updateURL https://raw.githubusercontent.com/#REPLACE:User/Repo/develop/dist/#Example.user.js |
| 16 | +// @connect github.com |
| 17 | +// @connect raw.githubusercontent.com |
| 18 | +// @grant GM.getValue |
| 19 | +// @grant GM.setValue |
| 20 | +// @grant GM.deleteValue |
| 21 | +// @grant GM.getResourceUrl |
| 22 | +// @grant GM.xmlHttpRequest |
| 23 | +// @grant GM.openInTab |
| 24 | +// @noframes |
| 25 | +// @resource img-icon https://raw.githubusercontent.com/#REPLACE:User/Repo/develop/assets/images/icon.png?b=3f6dd30 |
| 26 | +// @resource doc-changelog https://raw.githubusercontent.com/#REPLACE:User/Repo/develop/CHANGELOG.md?b=3f6dd30 |
| 27 | +// @require https://cdn.jsdelivr.net/npm/@sv443-network/[email protected]/dist/index.global.js |
| 28 | +// @grant GM.registerMenuCommand |
| 29 | +// @grant GM.listValues |
| 30 | +// ==/UserScript== |
| 31 | + |
| 32 | +(function(userutils){'use strict';/****************************************************************************** |
| 33 | +Copyright (c) Microsoft Corporation. |
| 34 | +
|
| 35 | +Permission to use, copy, modify, and/or distribute this software for any |
| 36 | +purpose with or without fee is hereby granted. |
| 37 | +
|
| 38 | +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH |
| 39 | +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
| 40 | +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, |
| 41 | +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM |
| 42 | +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR |
| 43 | +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| 44 | +PERFORMANCE OF THIS SOFTWARE. |
| 45 | +***************************************************************************** */ |
| 46 | +/* global Reflect, Promise, SuppressedError, Symbol */ |
| 47 | + |
| 48 | + |
| 49 | +function __awaiter(thisArg, _arguments, P, generator) { |
| 50 | + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } |
| 51 | + return new (P || (P = Promise))(function (resolve, reject) { |
| 52 | + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } |
| 53 | + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } |
| 54 | + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } |
| 55 | + step((generator = generator.apply(thisArg, _arguments || [])).next()); |
| 56 | + }); |
| 57 | +} |
| 58 | + |
| 59 | +typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { |
| 60 | + var e = new Error(message); |
| 61 | + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; |
| 62 | +};/** Default compression format used throughout the entire script */ |
| 63 | +const compressionFormat = "deflate-raw"; |
| 64 | +/** Whether sessionStorage is available and working */ |
| 65 | +typeof (sessionStorage === null || sessionStorage === void 0 ? void 0 : sessionStorage.setItem) !== "undefined" |
| 66 | + && (() => { |
| 67 | + try { |
| 68 | + const key = `_ses_test_${userutils.randomId(4)}`; |
| 69 | + sessionStorage.setItem(key, "test"); |
| 70 | + sessionStorage.removeItem(key); |
| 71 | + return true; |
| 72 | + } |
| 73 | + catch (_a) { |
| 74 | + return false; |
| 75 | + } |
| 76 | + })(); |
| 77 | +/** Info about the userscript, parsed from the userscript header (tools/post-build.js) */ |
| 78 | +({ |
| 79 | + name: GM.info.script.name, |
| 80 | + version: GM.info.script.version, |
| 81 | + namespace: GM.info.script.namespace, |
| 82 | +});let canCompress; |
| 83 | +const config = new userutils.DataStore({ |
| 84 | + id: "script-config", |
| 85 | + defaultData: { |
| 86 | + // add data here |
| 87 | + }, |
| 88 | + // increment this value if the data format changes: |
| 89 | + formatVersion: 1, |
| 90 | + // functions that migrate data from older versions to newer ones: |
| 91 | + migrations: { |
| 92 | + // migrate from v1 to v2: |
| 93 | + // 2: (oldData) => { |
| 94 | + // return { ...oldData, newProp: "foo" }; |
| 95 | + // }, |
| 96 | + }, |
| 97 | + encodeData: (data) => canCompress ? userutils.compress(data, compressionFormat, "string") : data, |
| 98 | + decodeData: (data) => canCompress ? userutils.decompress(data, compressionFormat, "string") : data, |
| 99 | +}); |
| 100 | +function initConfig() { |
| 101 | + return __awaiter(this, void 0, void 0, function* () { |
| 102 | + canCompress = yield compressionSupported(); |
| 103 | + yield config.loadData(); |
| 104 | + }); |
| 105 | +} |
| 106 | +function compressionSupported() { |
| 107 | + return __awaiter(this, void 0, void 0, function* () { |
| 108 | + if (typeof canCompress === "boolean") |
| 109 | + return canCompress; |
| 110 | + try { |
| 111 | + yield userutils.compress(".", compressionFormat, "string"); |
| 112 | + return canCompress = true; |
| 113 | + } |
| 114 | + catch (e) { |
| 115 | + return canCompress = false; |
| 116 | + } |
| 117 | + }); |
| 118 | +}//| "foo" |
| 119 | +//| "bar"; |
| 120 | +/** Options that are applied to every SelectorObserver instance */ |
| 121 | +const defaultObserverOptions = { |
| 122 | + defaultDebounce: 100, |
| 123 | + defaultDebounceEdge: "rising", |
| 124 | + subtree: false, |
| 125 | +}; |
| 126 | +/** Global SelectorObserver instances usable throughout the script for improved performance */ |
| 127 | +const globservers = {}; |
| 128 | +/** Call after DOM load to initialize all SelectorObserver instances */ |
| 129 | +function initObservers() { |
| 130 | + try { |
| 131 | + //#region body |
| 132 | + // -> the entire <body> element - use sparingly due to performance impacts! |
| 133 | + globservers.body = new userutils.SelectorObserver(document.body, Object.assign(Object.assign({}, defaultObserverOptions), { defaultDebounce: 150 })); |
| 134 | + globservers.body.enable(); |
| 135 | + //#region foo |
| 136 | + // -> some other subdivision of the <body> element - the selector can't start higher or on the same level as `body`! |
| 137 | + // const fooSelector = "#foo"; |
| 138 | + // globservers.foo = new SelectorObserver(fooSelector, { |
| 139 | + // ...defaultObserverOptions, |
| 140 | + // }); |
| 141 | + // globservers.body.addListener(fooSelector, { |
| 142 | + // listener: () => globservers.foo.enable(), |
| 143 | + // }); |
| 144 | + } |
| 145 | + catch (err) { |
| 146 | + console.error("Failed to initialize observers:", err); |
| 147 | + } |
| 148 | +}//#region DOM utils |
| 149 | +let domLoaded = document.readyState === "complete" || document.readyState === "interactive"; |
| 150 | +document.addEventListener("DOMContentLoaded", () => domLoaded = true); |
| 151 | +/** |
| 152 | + * Adds a style element to the DOM at runtime. |
| 153 | + * @param css The CSS stylesheet to add |
| 154 | + * @param ref A reference string to identify the style element - defaults to a random 5-character string |
| 155 | + */ |
| 156 | +function addStyle(css, ref) { |
| 157 | + if (!domLoaded) |
| 158 | + throw new Error("DOM has not finished loading yet"); |
| 159 | + const elem = userutils.addGlobalStyle(css); |
| 160 | + elem.id = `global-style-${ref !== null && ref !== void 0 ? ref : userutils.randomId(5, 36)}`; |
| 161 | + return elem; |
| 162 | +}/** Runs when the userscript is loaded initially */ |
| 163 | +function init() { |
| 164 | + return __awaiter(this, void 0, void 0, function* () { |
| 165 | + yield initConfig(); |
| 166 | + if (domLoaded) |
| 167 | + run(); |
| 168 | + else |
| 169 | + document.addEventListener("DOMContentLoaded", run); |
| 170 | + }); |
| 171 | +} |
| 172 | +/** Runs after the DOM is available */ |
| 173 | +function run() { |
| 174 | + return __awaiter(this, void 0, void 0, function* () { |
| 175 | + try { |
| 176 | + // post-build these double quotes are replaced by backticks (because if backticks are used here, the bundler converts them to double quotes) |
| 177 | + addStyle("#{{GLOBAL_STYLE}}", "global"); |
| 178 | + initObservers(); |
| 179 | + } |
| 180 | + catch (err) { |
| 181 | + console.error("Fatal error:", err); |
| 182 | + return; |
| 183 | + } |
| 184 | + }); |
| 185 | +} |
| 186 | +init();})(UserUtils);//# sourceMappingURL=http://localhost:8710/#Example.user.js.map |
0 commit comments