You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dev-sidecar/tampermonkey.js

389 lines
17 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 篡改猴Tampermonkey| 油猴Greasemonkey浏览器脚本扩展
*
* @author 由 WangLiang王良仿照的
* @authorHomePage https://wangliang1024.cn
* @description 篡改猴 (Tampermonkey) 是拥有 超过 1000 万用户 的最流行的浏览器扩展之一。 它适用于 Chrome、Microsoft Edge、Safari、Opera Next 和 Firefox。
* 有些人也会把篡改猴(Tampermonkey)称作油猴(Greasemonkey),尽管后者只是一款仅适用于 Firefox 浏览器的浏览器扩展程序。
* 它允许用户自定义并增强您最喜爱的网页的功能。用户脚本是小型 JavaScript 程序,可用于向网页添加新功能或修改现有功能。使用 篡改猴,您可以轻松在任何网站上创建、管理和运行这些用户脚本。
* 例如,使用 篡改猴,您可以向网页添加一个新按钮,可以快速在社交媒体上分享链接,或自动填写带有个人信息的表格。在数字化时代,这特别有用,因为网页常常被用作访问广泛的服务和应用程序的用户界面。
* 此外,篡改猴 使您轻松找到并安装其他用户创建的用户脚本。这意味着您可以快速轻松地访问为您喜爱的网页定制的广泛库,而无需花费数小时编写自己的代码。
* 无论您是希望为您的站点添加新功能的 Web 开发人员,还是只是希望 改善在线体验的普通用户,篡改猴 都是您的工具箱中的一个很好的工具。
* @homepageUrl https://www.tampermonkey.net
*/
'use strict';
(function () {
const PRE = "DS-Tampermonkey:"; // 前缀
const MENU_ID_PRE = PRE + "menu-";
const context = {
initialized: false, // 是否已经初始化
defaultPluginOptions: {}, // 默认插件选项
pluginElement: null, // 插件div
menusElement: null, // 菜单列表div
menus: {}, // 菜单集合
menuIndex: 0, // 菜单索引用于生成menuCmdId
lastNotification: null // 最后一次通知
/*{
obj: null, // 通知对象
options: null, // 通知选项
timeout: null // 通知定时器
}*/
};
// 创建插件样式
function createPluginStyle (options) {
options = options || {};
// 创建一个新的<style>元素
const styleElement = document.createElement('style');
// 设置<style>元素的type属性
styleElement.type = 'text/css';
// 设置<style>元素的内容
let cssContent = `
.____ds-icon____{
position: fixed;
right: 5px;
top: 30%;
z-index: 9999;
width: 36px;
height: 36px;
border-radius: 8px;
background-color: #DDD;
background-repeat: no-repeat;
background-size: cover;
background-image: url("data:image/png;base64,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");
}
.____ds-menus____ {
display: none;
position: absolute;
right: 36px;
top: 0;
z-index: 10000;
min-width: 200px;
min-height: 35px;
border-radius: 8px;
background-color: #323231;
border: 1px solid #52525E;
overflow: hidden;
}
.____ds-icon____:hover .____ds-menus____ {
display: block;
}
.____ds-menu____ {
height: 35px;
line-height: 35px;
padding: 0 10px;
white-space: nowrap;
color: #FFF;
cursor: pointer;
margin-left: 26px;
}
.____ds-menu____:hover {
background-color: #855F16;
}
.____ds-menu0____ {
margin-left: 0;
font-size: 16px;
font-weight: bold;
}
.____ds-menu0____ img{
width: 23px;
height: 23px;
vertical-align: middle;
margin: 0 8px 3px 8px;
}
`;
// 如果有自定义样式,则添加到 CSS 内容中
if (options.style) {
cssContent += options.style;
}
// 添加 CSS 内容到<style>元素中
if (styleElement.styleSheet) {
// 兼容 IE
styleElement.styleSheet.cssText = cssContent;
} else {
styleElement.appendChild(document.createTextNode(cssContent));
}
// 将<style>元素添加到<head>中
document.head.append(styleElement);
}
// 创建插件div
function createPluginDiv (options) {
options = {
...{ name: "油猴脚本" },
...options
}
// 创建插件div
context.pluginElement = document.createElement('div');
context.pluginElement.title = "油猴脚本" + (options.name ? "" + options.name : "");
context.pluginElement.className = "____ds-icon____";
// 创建菜单列表div
context.menusElement = document.createElement('div');
context.menusElement.className = "____ds-menus____";
if (options.width > 0) {
context.menusElement.style['min-width'] = options.width + "px";
}
// 将菜单列表div添加到插件div中
context.pluginElement.append(context.menusElement);
// 创建开关菜单
const enabled = window.__ds_global__.GM_getValue("ds_enabled", true)
const switchMenuElement = document.createElement('div');
const icon = (options.icon ? `<img alt="icon" src="${options.icon}"/>` : " ");
switchMenuElement.id = PRE + "menu-0";
switchMenuElement.className = "____ds-menu____ ____ds-menu0____";
switchMenuElement.innerHTML = (enabled ? "✅" : "❌") + icon + options.name;
switchMenuElement.title = `点击${enabled ? "关闭" : "开启"}此脚本功能`;
switchMenuElement.onclick = function () {
let enabled = window.__ds_global__.GM_getValue("ds_enabled", true)
if (enabled) {
hideMenus();
enabled = false;
} else {
showMenus();
enabled = true;
}
switchMenuElement.innerHTML = (enabled ? "✅" : "❌") + icon + options.name;
switchMenuElement.title = `点击${enabled ? "关闭" : "开启"}此脚本功能`;
window.__ds_global__.GM_setValue("ds_enabled", enabled)
window.__ds_global__.GM_notification({
text: `${enabled ? "开启" : "关闭"}${options.name}」 功能\n(刷新网页后生效)`,
timeout: 3500
});
};
// 将开关菜单添加到菜单列表div中
context.menusElement.append(switchMenuElement);
// 获取body元素
const body = document.getElementsByTagName('body')[0];
// 将插件div添加到body中
body.prepend(context.pluginElement);
}
function showMenus () {
for (const menuCmdId in context.menus) {
const menuElement = context.menus[menuCmdId].element;
menuElement.style.display = "block";
}
}
function hideMenus () {
for (const menuCmdId in context.menus) {
const menuElement = context.menus[menuCmdId].element;
menuElement.style.display = "none";
}
}
window.__ds_global__ = {
// 获取上下文
getContext: () => {
return context;
},
// 初始化
DS_init: (options) => {
try {
if (context.initialized) return;
// 合并默认参数
options = {
...context.defaultPluginOptions,
...options
};
createPluginStyle(options);
createPluginDiv(options);
context.initialized = true;
console.log("ds_tampermonkey: initialization completed篡改猴插件初始化完成篡改猴图标已显示在页面右侧鼠标移到上面可展示功能列表")
} catch (e) {
console.error("ds_tampermonkey: initialization failed篡改猴插件初始化失败:", e);
}
},
// 注册菜单
GM_registerMenuCommand: (name, callback, options_or_accessKey) => {
const options = typeof options_or_accessKey === "string" ? { accessKey: options_or_accessKey } : options_or_accessKey;
// 生成菜单ID
let menuCmdId;
if (options.id) {
if (options.id.indexOf(MENU_ID_PRE) === 0) {
menuCmdId = options.id;
} else {
menuCmdId = MENU_ID_PRE + options.id;
}
} else {
menuCmdId = MENU_ID_PRE + (++context.menuIndex);
}
// 创建菜单元素
const menuElement = document.createElement('div');
menuElement.id = menuCmdId;
menuElement.className = "____ds-menu____";
menuElement.innerHTML = name;
if (options.title) {
menuElement.title = typeof options.title === "function" ? options.title() : options.title;
}
if (callback) {
menuElement.onclick = callback;
}
if (options.accessKey) {
// TODO: 快捷键功能待开发篡改猴官方文档https://www.tampermonkey.net/documentation.php#api:GM_registerMenuCommand
}
// 将菜单元素添加到菜单列表div中
context.menusElement.append(menuElement);
// 将菜单添加到菜单集合中
context.menus[menuCmdId] = {
name: name,
callback: callback,
options: options,
element: menuElement
};
// 返回菜单ID
return menuCmdId;
},
// 删除菜单
GM_unregisterMenuCommand: (menuCmdId) => {
if (menuCmdId == null) {
return;
}
if (menuCmdId.indexOf(MENU_ID_PRE) !== 0) {
menuCmdId = MENU_ID_PRE + menuCmdId;
}
const menuElement = document.getElementById(menuCmdId)
if (menuElement) {
menuElement.remove();
}
delete context.menus[menuCmdId];
},
// 打开新标签
GM_openInTab: (url, options_or_loadInBackground) => {
// const options = typeof options_or_loadInBackground === "boolean"
// ? { loadInBackground: options_or_loadInBackground }
// : (options_or_loadInBackground || {});
window.open(url)
},
// 获取配置
GM_getValue: (key, defaultValue) => {
key = PRE + key;
const valueStr = localStorage.getItem(key);
if (valueStr == null || valueStr === '') {
return defaultValue;
}
try {
return JSON.parse(valueStr).v;
} catch (e) {
}
return valueStr;
},
// 设置配置
GM_setValue: (key, value) => {
key = PRE + key;
localStorage.setItem(key, JSON.stringify({ v: value }));
},
// 删除设置
GM_deleteValue: (key) => {
key = PRE + key;
localStorage.removeItem(key);
},
// 通知
GM_notification: (details_or_text, ondone_or_title, image, onclick) => {
// param1
let options = typeof details_or_text === "string" ? { text: details_or_text } : details_or_text;
if (typeof options !== "object") {
console.error("GM_notification: 无效的参数值details_or_text = " + details_or_text);
return;
}
// param2
if (typeof ondone_or_title === "string") {
options.title = ondone_or_title;
} else if (typeof ondone_or_title === "function") {
options.ondone = ondone_or_title;
} else if (ondone_or_title != null) {
console.warn("GM_notification: 无效的参数值ondone_or_title = " + ondone_or_title);
}
// param3
if (typeof image === "string") {
options.image = image;
} else if (onclick != null) {
console.warn("GM_notification: 无效的参数值image = " + image);
}
// param4
if (typeof onclick === "function") {
options.onclick = onclick;
} else if (onclick != null) {
console.warn("GM_notification: 无效的参数值onclick = " + onclick);
}
let text = options.text;
if (options.title) {
text = options.title + ": " + text;
}
// 显示通知方法
const showNotification = () => {
// 先关闭上一个通知
let lastNotification = context.lastNotification;
if (lastNotification) {
if (lastNotification.timeout) {
clearTimeout(lastNotification.timeout);
}
lastNotification.obj.close();
if (lastNotification.options && typeof lastNotification.options.ondone === "function") lastNotification.options.ondone();
context.lastNotification = null;
}
const notification = new Notification(text);
lastNotification = {
obj: notification,
options: options,
timeout: null
}
context.lastNotification = lastNotification;
if (options.timeout) {
lastNotification.timeout = setTimeout(function () {
context.lastNotification = null // 清除最后一个通知
notification.close();
if (options.ondone) options.ondone(); // 回调
}, options.timeout)
}
return notification;
};
const showAlert = () => {
alert(text);
if (options.ondone) options.ondone(); // 回调
};
// 检查浏览器是否支持Notification API
if (!("Notification" in window)) {
showAlert(); // 不支持直接使用alert显示通知
}
// 检查用户是否已授予权限
else if (Notification.permission === "granted") {
// 如果用户已授予权限,我们可以显示通知
showNotification();
}
// 否则,先请求权限
else if (Notification.permission !== 'denied') {
Notification.requestPermission(function (permission) {
if (permission === "granted") {
showNotification(); // 用户接受权限,我们可以显示通知
} else {
showAlert(); // 用户驳回了权限直接使用alert显示通知
}
});
}
}
}
})();
console.log("ds_tampermonkey: completed")