您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
确保不同用户脚本管理器之间兼容性的库
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/519877/1497523/UserScript%20Compatibility%20Library.js
- // ==UserScript==
- // @name UserScript Compatibility Library
- // @name:en UserScript Compatibility Library
- // @name:zh-CN UserScript 兼容库
- // @name:ru Библиотека совместимости для пользовательских скриптов
- // @name:vi Thư viện tương thích cho userscript
- // @namespace https://greasyfork.org/vi/users/1195312-renji-yuusei
- // @version 1.5.0
- // @description A library to ensure compatibility between different userscript managers
- // @description:en A library to ensure compatibility between different userscript managers
- // @description:zh-CN 确保不同用户脚本管理器之间兼容性的库
- // @description:vi Thư viện đảm bảo tương thích giữa các trình quản lý userscript khác nhau
- // @description:ru Библиотека для обеспечения совместимости между различными менеджерами пользовательских скриптов
- // @author Yuusei
- // @license GPL-3.0-only
- // @grant unsafeWindow
- // @grant GM_info
- // @grant GM.info
- // @grant GM_getValue
- // @grant GM.getValue
- // @grant GM_setValue
- // @grant GM.setValue
- // @grant GM_deleteValue
- // @grant GM.deleteValue
- // @grant GM_listValues
- // @grant GM.listValues
- // @grant GM_xmlhttpRequest
- // @grant GM.xmlHttpRequest
- // @grant GM_download
- // @grant GM.download
- // @grant GM_notification
- // @grant GM.notification
- // @grant GM_addStyle
- // @grant GM.addStyle
- // @grant GM_registerMenuCommand
- // @grant GM.registerMenuCommand
- // @grant GM_unregisterMenuCommand
- // @grant GM.unregisterMenuCommand
- // @grant GM_setClipboard
- // @grant GM.setClipboard
- // @grant GM_getResourceText
- // @grant GM.getResourceText
- // @grant GM_getResourceURL
- // @grant GM.getResourceURL
- // @grant GM_openInTab
- // @grant GM.openInTab
- // @grant GM_addElement
- // @grant GM.addElement
- // @grant GM_addValueChangeListener
- // @grant GM.addValueChangeListener
- // @grant GM_removeValueChangeListener
- // @grant GM.removeValueChangeListener
- // @grant GM_log
- // @grant GM.log
- // @grant GM_getTab
- // @grant GM.getTab
- // @grant GM_saveTab
- // @grant GM.saveTab
- // @grant GM_getTabs
- // @grant GM.getTabs
- // @grant GM_cookie
- // @grant GM.cookie
- // @grant GM_webRequest
- // @grant GM.webRequest
- // @grant GM_fetch
- // @grant GM.fetch
- // @grant window.close
- // @grant window.focus
- // @grant window.onurlchange
- // @grant GM_addValueChangeListener
- // @grant GM_removeValueChangeListener
- // @grant GM_getResourceURL
- // @grant GM_notification
- // @grant GM_xmlhttpRequest
- // @grant GM_openInTab
- // @grant GM_registerMenuCommand
- // @grant GM_unregisterMenuCommand
- // @grant GM_setClipboard
- // @grant GM_getResourceText
- // @grant GM_addStyle
- // @grant GM_download
- // @grant GM_cookie.get
- // @grant GM_cookie.set
- // @grant GM_cookie.delete
- // @grant GM_webRequest.listen
- // @grant GM_webRequest.onBeforeRequest
- // @grant GM_addElement.byTag
- // @grant GM_addElement.byId
- // @grant GM_addElement.byClass
- // @grant GM_addElement.byXPath
- // @grant GM_addElement.bySelector
- // @grant GM_removeElement
- // @grant GM_removeElements
- // @grant GM_getElement
- // @grant GM_getElements
- // @grant GM_addScript
- // @grant GM_removeScript
- // @grant GM_addLink
- // @grant GM_removeLink
- // @grant GM_addMeta
- // @grant GM_removeMeta
- // @grant GM_addIframe
- // @grant GM_removeIframe
- // @grant GM_addImage
- // @grant GM_removeImage
- // @grant GM_addVideo
- // @grant GM_removeVideo
- // @grant GM_addAudio
- // @grant GM_removeAudio
- // @grant GM_addCanvas
- // @grant GM_removeCanvas
- // @grant GM_addSvg
- // @grant GM_removeSvg
- // @grant GM_addObject
- // @grant GM_removeObject
- // @grant GM_addEmbed
- // @grant GM_removeEmbed
- // @grant GM_addApplet
- // @grant GM_removeApplet
- // @run-at document-start
- // @license GPL-3.0-only
- // ==/UserScript==
- (function () {
- 'use strict';
- const utils = {
- isFunction: function (fn) {
- return typeof fn === 'function';
- },
- isUndefined: function (value) {
- return typeof value === 'undefined';
- },
- isObject: function (value) {
- return value !== null && typeof value === 'object';
- },
- sleep: function (ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- },
- retry: async function (fn, attempts = 3, delay = 1000) {
- let lastError;
- for (let i = 0; i < attempts; i++) {
- try {
- return await fn();
- } catch (error) {
- lastError = error;
- if (i === attempts - 1) break;
- await this.sleep(delay * Math.pow(2, i));
- }
- }
- throw lastError;
- },
- debounce: function (fn, wait) {
- let timeout;
- return function (...args) {
- clearTimeout(timeout);
- timeout = setTimeout(() => fn.apply(this, args), wait);
- };
- },
- throttle: function (fn, limit) {
- let timeout;
- let inThrottle;
- return function (...args) {
- if (!inThrottle) {
- fn.apply(this, args);
- inThrottle = true;
- clearTimeout(timeout);
- timeout = setTimeout(() => (inThrottle = false), limit);
- }
- };
- },
- // Thêm các tiện ích mới
- isArray: function (arr) {
- return Array.isArray(arr);
- },
- isString: function (str) {
- return typeof str === 'string';
- },
- isNumber: function (num) {
- return typeof num === 'number' && !isNaN(num);
- },
- isBoolean: function (bool) {
- return typeof bool === 'boolean';
- },
- isNull: function (value) {
- return value === null;
- },
- isEmpty: function (value) {
- if (this.isArray(value)) return value.length === 0;
- if (this.isObject(value)) return Object.keys(value).length === 0;
- if (this.isString(value)) return value.trim().length === 0;
- return false;
- },
- };
- const GMCompat = {
- info: (function () {
- if (!utils.isUndefined(GM_info)) return GM_info;
- if (!utils.isUndefined(GM) && GM.info) return GM.info;
- return {};
- })(),
- storageCache: new Map(),
- cacheTimestamps: new Map(),
- cacheExpiry: 3600000, // 1 hour
- getValue: async function (key, defaultValue) {
- try {
- if (this.storageCache.has(key)) {
- const timestamp = this.cacheTimestamps.get(key);
- if (Date.now() - timestamp < this.cacheExpiry) {
- return this.storageCache.get(key);
- }
- }
- let value;
- if (!utils.isUndefined(GM_getValue)) {
- value = GM_getValue(key, defaultValue);
- } else if (!utils.isUndefined(GM) && GM.getValue) {
- value = await GM.getValue(key, defaultValue);
- } else {
- value = defaultValue;
- }
- this.storageCache.set(key, value);
- this.cacheTimestamps.set(key, Date.now());
- return value;
- } catch (error) {
- console.error('getValue error:', error);
- return defaultValue;
- }
- },
- setValue: async function (key, value) {
- try {
- this.storageCache.set(key, value);
- this.cacheTimestamps.set(key, Date.now());
- if (!utils.isUndefined(GM_setValue)) {
- return GM_setValue(key, value);
- }
- if (!utils.isUndefined(GM) && GM.setValue) {
- return await GM.setValue(key, value);
- }
- } catch (error) {
- this.storageCache.delete(key);
- this.cacheTimestamps.delete(key);
- throw new Error('Failed to set value: ' + error.message);
- }
- },
- deleteValue: async function (key) {
- try {
- this.storageCache.delete(key);
- this.cacheTimestamps.delete(key);
- if (!utils.isUndefined(GM_deleteValue)) {
- return GM_deleteValue(key);
- }
- if (!utils.isUndefined(GM) && GM.deleteValue) {
- return await GM.deleteValue(key);
- }
- } catch (error) {
- throw new Error('Failed to delete value: ' + error.message);
- }
- },
- requestQueue: [],
- processingRequest: false,
- maxRetries: 3,
- retryDelay: 1000,
- xmlHttpRequest: async function (details) {
- const makeRequest = () => {
- return new Promise((resolve, reject) => {
- try {
- const callbacks = {
- onload: resolve,
- onerror: reject,
- ontimeout: reject,
- onprogress: details.onprogress,
- onreadystatechange: details.onreadystatechange,
- };
- const finalDetails = {
- timeout: 30000,
- ...details,
- ...callbacks,
- };
- if (!utils.isUndefined(GM_xmlhttpRequest)) {
- GM_xmlhttpRequest(finalDetails);
- } else if (!utils.isUndefined(GM) && GM.xmlHttpRequest) {
- GM.xmlHttpRequest(finalDetails);
- } else if (!utils.isUndefined(GM_fetch)) {
- GM_fetch(finalDetails.url, finalDetails);
- } else if (!utils.isUndefined(GM) && GM.fetch) {
- GM.fetch(finalDetails.url, finalDetails);
- } else {
- reject(new Error('XMLHttpRequest API not available'));
- }
- } catch (error) {
- reject(error);
- }
- });
- };
- return utils.retry(makeRequest, this.maxRetries, this.retryDelay);
- },
- download: async function (details) {
- try {
- const downloadWithProgress = {
- ...details,
- onprogress: details.onprogress,
- onerror: details.onerror,
- onload: details.onload,
- };
- if (!utils.isUndefined(GM_download)) {
- return new Promise((resolve, reject) => {
- GM_download({
- ...downloadWithProgress,
- onload: resolve,
- onerror: reject,
- });
- });
- }
- if (!utils.isUndefined(GM) && GM.download) {
- return await GM.download(downloadWithProgress);
- }
- throw new Error('Download API not available');
- } catch (error) {
- throw new Error('Download failed: ' + error.message);
- }
- },
- notification: function (details) {
- return new Promise((resolve, reject) => {
- try {
- const defaultOptions = {
- timeout: 5000,
- highlight: false,
- silent: false,
- requireInteraction: false,
- priority: 0,
- };
- const callbacks = {
- onclick: utils.debounce((...args) => {
- if (details.onclick) details.onclick(...args);
- resolve('clicked');
- }, 300),
- ondone: (...args) => {
- if (details.ondone) details.ondone(...args);
- resolve('closed');
- },
- onerror: (...args) => {
- if (details.onerror) details.onerror(...args);
- reject('error');
- },
- };
- const finalDetails = { ...defaultOptions, ...details, ...callbacks };
- if (!utils.isUndefined(GM_notification)) {
- GM_notification(finalDetails);
- } else if (!utils.isUndefined(GM) && GM.notification) {
- GM.notification(finalDetails);
- } else {
- if ('Notification' in window) {
- Notification.requestPermission().then(permission => {
- if (permission === 'granted') {
- const notification = new Notification(finalDetails.title, {
- body: finalDetails.text,
- silent: finalDetails.silent,
- icon: finalDetails.image,
- tag: finalDetails.tag,
- requireInteraction: finalDetails.requireInteraction,
- badge: finalDetails.badge,
- vibrate: finalDetails.vibrate,
- });
- notification.onclick = callbacks.onclick;
- notification.onerror = callbacks.onerror;
- if (finalDetails.timeout > 0) {
- setTimeout(() => {
- notification.close();
- callbacks.ondone();
- }, finalDetails.timeout);
- }
- } else {
- reject(new Error('Notification permission denied'));
- }
- });
- } else {
- reject(new Error('Notification API not available'));
- }
- }
- } catch (error) {
- reject(error);
- }
- });
- },
- addStyle: function (css) {
- try {
- const testStyle = document.createElement('style');
- testStyle.textContent = css;
- if (testStyle.sheet === null) {
- throw new Error('Invalid CSS');
- }
- if (!utils.isUndefined(GM_addStyle)) {
- return GM_addStyle(css);
- }
- if (!utils.isUndefined(GM) && GM.addStyle) {
- return GM.addStyle(css);
- }
- const style = document.createElement('style');
- style.textContent = css;
- style.type = 'text/css';
- document.head.appendChild(style);
- return style;
- } catch (error) {
- throw new Error('Failed to add style: ' + error.message);
- }
- },
- registerMenuCommand: function (name, fn, accessKey) {
- try {
- if (!utils.isFunction(fn)) {
- throw new Error('Command callback must be a function');
- }
- if (!utils.isUndefined(GM_registerMenuCommand)) {
- return GM_registerMenuCommand(name, fn, accessKey);
- }
- if (!utils.isUndefined(GM) && GM.registerMenuCommand) {
- return GM.registerMenuCommand(name, fn, accessKey);
- }
- } catch (error) {
- throw new Error('Failed to register menu command: ' + error.message);
- }
- },
- setClipboard: function (text, info) {
- try {
- if (!utils.isUndefined(GM_setClipboard)) {
- return GM_setClipboard(text, info);
- }
- if (!utils.isUndefined(GM) && GM.setClipboard) {
- return GM.setClipboard(text, info);
- }
- return navigator.clipboard.writeText(text);
- } catch (error) {
- throw new Error('Failed to set clipboard: ' + error.message);
- }
- },
- getResourceText: async function (name) {
- try {
- if (!utils.isUndefined(GM_getResourceText)) {
- return GM_getResourceText(name);
- }
- if (!utils.isUndefined(GM) && GM.getResourceText) {
- return await GM.getResourceText(name);
- }
- throw new Error('Resource API not available');
- } catch (error) {
- throw new Error('Failed to get resource text: ' + error.message);
- }
- },
- getResourceURL: async function (name) {
- try {
- if (!utils.isUndefined(GM_getResourceURL)) {
- return GM_getResourceURL(name);
- }
- if (!utils.isUndefined(GM) && GM.getResourceURL) {
- return await GM.getResourceURL(name);
- }
- throw new Error('Resource URL API not available');
- } catch (error) {
- throw new Error('Failed to get resource URL: ' + error.message);
- }
- },
- openInTab: function (url, options = {}) {
- try {
- const defaultOptions = {
- active: true,
- insert: true,
- setParent: true,
- };
- const finalOptions = { ...defaultOptions, ...options };
- if (!utils.isUndefined(GM_openInTab)) {
- return GM_openInTab(url, finalOptions);
- }
- if (!utils.isUndefined(GM) && GM.openInTab) {
- return GM.openInTab(url, finalOptions);
- }
- return window.open(url, '_blank');
- } catch (error) {
- throw new Error('Failed to open tab: ' + error.message);
- }
- },
- cookie: {
- get: async function (details) {
- try {
- if (!utils.isUndefined(GM_cookie) && GM_cookie.get) {
- return await GM_cookie.get(details);
- }
- if (!utils.isUndefined(GM) && GM.cookie && GM.cookie.get) {
- return await GM.cookie.get(details);
- }
- return document.cookie;
- } catch (error) {
- throw new Error('Failed to get cookie: ' + error.message);
- }
- },
- set: async function (details) {
- try {
- if (!utils.isUndefined(GM_cookie) && GM_cookie.set) {
- return await GM_cookie.set(details);
- }
- if (!utils.isUndefined(GM) && GM.cookie && GM.cookie.set) {
- return await GM.cookie.set(details);
- }
- document.cookie = details;
- } catch (error) {
- throw new Error('Failed to set cookie: ' + error.message);
- }
- },
- delete: async function (details) {
- try {
- if (!utils.isUndefined(GM_cookie) && GM_cookie.delete) {
- return await GM_cookie.delete(details);
- }
- if (!utils.isUndefined(GM) && GM.cookie && GM.cookie.delete) {
- return await GM.cookie.delete(details);
- }
- } catch (error) {
- throw new Error('Failed to delete cookie: ' + error.message);
- }
- },
- },
- webRequest: {
- listen: function (filter, callback) {
- try {
- if (!utils.isUndefined(GM_webRequest) && GM_webRequest.listen) {
- return GM_webRequest.listen(filter, callback);
- }
- if (!utils.isUndefined(GM) && GM.webRequest && GM.webRequest.listen) {
- return GM.webRequest.listen(filter, callback);
- }
- } catch (error) {
- throw new Error('Failed to listen to web request: ' + error.message);
- }
- },
- onBeforeRequest: function (filter, callback) {
- try {
- if (!utils.isUndefined(GM_webRequest) && GM_webRequest.onBeforeRequest) {
- return GM_webRequest.onBeforeRequest(filter, callback);
- }
- if (!utils.isUndefined(GM) && GM.webRequest && GM.webRequest.onBeforeRequest) {
- return GM.webRequest.onBeforeRequest(filter, callback);
- }
- } catch (error) {
- throw new Error('Failed to handle onBeforeRequest: ' + error.message);
- }
- },
- },
- dom: {
- addElement: function (tag, attributes = {}, parent = document.body) {
- try {
- const element = document.createElement(tag);
- Object.entries(attributes).forEach(([key, value]) => {
- element.setAttribute(key, value);
- });
- parent.appendChild(element);
- return element;
- } catch (error) {
- throw new Error('Failed to add element: ' + error.message);
- }
- },
- removeElement: function (element) {
- try {
- if (element && element.parentNode) {
- element.parentNode.removeChild(element);
- }
- } catch (error) {
- throw new Error('Failed to remove element: ' + error.message);
- }
- },
- getElement: function (selector) {
- try {
- return document.querySelector(selector);
- } catch (error) {
- throw new Error('Failed to get element: ' + error.message);
- }
- },
- getElements: function (selector) {
- try {
- return Array.from(document.querySelectorAll(selector));
- } catch (error) {
- throw new Error('Failed to get elements: ' + error.message);
- }
- },
- },
- };
- const exportGMCompat = function () {
- try {
- const target = !utils.isUndefined(unsafeWindow) ? unsafeWindow : window;
- Object.defineProperty(target, 'GMCompat', {
- value: GMCompat,
- writable: false,
- configurable: false,
- enumerable: true,
- });
- if (window.onurlchange !== undefined) {
- window.addEventListener('urlchange', () => {});
- }
- } catch (error) {
- console.error('Failed to export GMCompat:', error);
- }
- };
- exportGMCompat();
- })();