// ==UserScript==
// @name GreasyFork优化
// @name:en-US GreasyFork Optimization
// @namespace https://github.com/WhiteSevs/TamperMonkeyScript
// @version 2024.11.11
// @author WhiteSevs
// @description 自动登录账号、快捷寻找自己库被其他脚本引用、更新自己的脚本列表、库、优化图片浏览、美化页面、Markdown复制按钮
// @description:en-US Automatically log in to the account, quickly find your own library referenced by other scripts, update your own script list, library, optimize image browsing, beautify the page, Markdown copy button
// @license GPL-3.0-only
// @icon data:image/png;base64,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
// @supportURL https://github.com/WhiteSevs/TamperMonkeyScript/issues
// @match *://greasyfork.org/*
// @require https://update.greasyfork.org/scripts/494167/1413255/CoverUMD.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/[email protected]/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/[email protected]/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/[email protected]/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/[email protected]/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/[email protected]/dist/viewer.min.js
// @require https://fastly.jsdelivr.net/npm/[email protected]/i18next.min.js
// @resource ViewerCSS https://fastly.jsdelivr.net/npm/[email protected]/dist/viewer.min.css
// @connect greasyfork.org
// @grant GM_addStyle
// @grant GM_deleteValue
// @grant GM_getResourceText
// @grant GM_getValue
// @grant GM_info
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_unregisterMenuCommand
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @run-at document-start
// ==/UserScript==
(t=>{function d(n){if(typeof n!="string")throw new TypeError("cssText must be a string");let e=document.createElement("style");return e.setAttribute("type","text/css"),e.innerHTML=n,document.head?document.head.appendChild(e):document.body?document.body.appendChild(e):document.documentElement.childNodes.length===0?document.documentElement.appendChild(e):document.documentElement.insertBefore(e,document.documentElement.childNodes[0]),e}if(typeof GM_addStyle=="function"){GM_addStyle(t);return}d(t)})(" .whitesev-hide{display:none}.whitesev-hide-important{display:none!important} ");
(function (Qmsg, DOMUtils, Utils, i18next, pops, Viewer) {
'use strict';
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var _a;
var _GM_addStyle = /* @__PURE__ */ (() => typeof GM_addStyle != "undefined" ? GM_addStyle : void 0)();
var _GM_deleteValue = /* @__PURE__ */ (() => typeof GM_deleteValue != "undefined" ? GM_deleteValue : void 0)();
var _GM_getResourceText = /* @__PURE__ */ (() => typeof GM_getResourceText != "undefined" ? GM_getResourceText : void 0)();
var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
var _GM_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : void 0)();
var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
var _GM_unregisterMenuCommand = /* @__PURE__ */ (() => typeof GM_unregisterMenuCommand != "undefined" ? GM_unregisterMenuCommand : void 0)();
var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
var _monkeyWindow = /* @__PURE__ */ (() => window)();
const zh_CN_language = {
GreasyFork优化: "GreasyFork优化",
请求取消: "请求取消",
请求超时: "请求超时",
请求异常: "请求异常",
通用: "通用",
账号: "账号",
密码: "密码",
语言: "语言",
"账号/密码": "账号/密码",
请输入账号: "请输入账号",
请输入密码: "请输入密码",
自动登录: "自动登录",
自动登录当前保存的账号: "自动登录当前保存的账号",
"清空账号/密码": "清空账号/密码",
点击清空: "点击清空",
"确定清空账号和密码?": "确定清空账号和密码?",
"已清空账号/密码": "已清空账号/密码",
"源代码同步【脚本列表】": "源代码同步【脚本列表】",
一键同步: "一键同步",
前往用户主页: "前往用户主页",
获取当前已登录的用户主页失败: "获取当前已登录的用户主页失败",
"源代码同步【未上架的脚本】": "源代码同步【未上架的脚本】",
"源代码同步【库】": "源代码同步【库】",
论坛: "论坛",
功能: "功能",
过滤重复的评论: "过滤重复的评论",
"过滤掉重复的评论数量(≥2)": "过滤掉重复的评论数量(≥2)",
"过滤脚本(id)": "过滤脚本(id)",
"请输入脚本id,每行一个": "请输入脚本id,每行一个",
"过滤发布的用户(id)": "过滤发布的用户(id)",
"请输入用户id,每行一个": "请输入用户id,每行一个",
"过滤回复的用户(id)": "过滤回复的用户(id)",
优化: "优化",
固定当前语言: "固定当前语言",
无: "无",
"如button、input、textarea": "如button、input、textarea",
更直观的查看版本迭代: "更直观的查看版本迭代",
美化上传图片按钮: "美化上传图片按钮",
放大上传区域: "放大上传区域",
优化图片浏览: "优化图片浏览",
使用Viewer浏览图片: "使用Viewer浏览图片",
覆盖图床图片跳转: "覆盖图床图片跳转",
"配合上面的【优化图片浏览】更优雅浏览图片": "配合上面的【优化图片浏览】更优雅浏览图片",
'需安装Greasyfork Beautify脚本,<a href="https://greasyfork.org/zh-CN/scripts/446849-greasyfork-beautify" target="_blank">🖐点我安装</a>': '需安装Greasyfork Beautify脚本,<a href="https://greasyfork.org/zh-CN/scripts/446849-greasyfork-beautify" target="_blank">🖐点我安装</a>',
代码: "代码",
添加复制代码按钮: "添加复制代码按钮",
更优雅的复制: "更优雅的复制",
快捷键: "快捷键",
"【F】键全屏、【Alt+Shift+F】键宽屏": "【F】键全屏、【Alt+Shift+F】键宽屏",
库: "库",
脚本列表: "脚本列表",
"请输入屏蔽规则,每行一个": "请输入屏蔽规则,每行一个",
请求admin内容失败: "请求admin内容失败",
解析admin的源代码同步表单失败: "解析admin的源代码同步表单失败",
源代码同步失败: "源代码同步失败",
获取用户信息失败: "获取用户信息失败",
获取用户的收藏集失败: "获取用户的收藏集失败",
"解析Script Sets失败": "解析Script Sets失败",
"获取收藏集{{setsId}}失败": "获取收藏集{{setsId}}失败",
"获取表单元素#edit_script_set失败": "获取表单元素#edit_script_set失败",
更新收藏集表单请求失败: "更新收藏集表单请求失败",
请先在菜单中录入账号: "请先在菜单中录入账号",
请先在菜单中录入密码: "请先在菜单中录入密码",
"获取csrf-token失败": "获取csrf-token失败",
"正在登录中...": "正在登录中...",
"登录失败,请在控制台查看原因": "登录失败,请在控制台查看原因",
"登录成功,1s后自动跳转": "登录成功,1s后自动跳转",
"登录失败,可能是账号/密码错误,请在控制台查看原因": "登录失败,可能是账号/密码错误,请在控制台查看原因",
"美化 历史版本 页面": "美化 历史版本 页面",
未找到history_versions元素列表: "未找到history_versions元素列表",
"yyyy年MM月dd日 HH:mm:ss": "yyyy-MM-dd HH:mm:ss",
"美化 Greasyfork Beautify脚本": "美化 Greasyfork Beautify脚本",
"❌ 最多同时长传5张图": "❌ 最多同时长传5张图片",
"❌ 图片:{{name}} 大小:{{size}}": "❌ 图片:{{name}} 大小:{{size}}",
"已过滤:{{oldCount}}": "已过滤:{{oldCount}}",
寻找引用: "寻找引用",
获取脚本id失败: "获取脚本id失败",
收藏: "收藏",
请先登录账号: "请先登录账号",
获取用户id失败: "获取用户id失败",
"获取收藏夹中...": "获取收藏夹中...",
收藏集: "收藏集",
"添加中...": "添加中...",
"添加失败,{{selector}}元素不存在": "添加失败,{{selector}}元素不存在",
"未找到{{selector}}元素": "未找到{{selector}}元素",
添加失败: "添加失败",
添加成功: "添加成功",
"删除中...": "删除中...",
删除成功: "删除成功",
添加: "添加",
刪除: "刪除",
"拦截跳转:": "拦截跳转:",
今日检查: "今日检查",
复制代码: "复制代码",
"加载文件中...": "加载文件中...",
复制成功: "复制成功",
"✅ 复制成功!": "✅ 复制成功!",
"当前语言:{{currentLocaleLanguage}},,3秒后切换至:{{localeLanguage}}": "当前语言:{{currentLocaleLanguage}},,3秒后切换至:{{localeLanguage}}",
"导航至:": "导航至:",
"请先登录账号!": "请先登录账号!",
"获取信息中,请稍后...": "获取信息中,请稍后...",
"获取成功,共 {{count}} 个": "获取成功,共 {{count}} 个",
"评分:": "评分:",
"语言:": "语言:",
"版本:": "版本:",
"更新:": "更新:",
同步代码: "同步代码",
"同步中...": "同步中...",
手动: "手动",
自动: "自动",
"同步方式:{{syncMode}}": "同步方式:{{syncMode}}",
同步成功: "同步成功",
同步失败: "同步失败",
该脚本未设置同步信息: "该脚本未设置同步信息",
"上次重载时间 {{time}},{{timeout}}秒内拒绝反复重载": "上次重载时间 {{time}},{{timeout}}秒内拒绝反复重载",
"名称:": "名称:",
"进度:": "进度:",
"未获取到【脚本列表】": "未获取到【脚本列表】",
"源代码同步成功,3秒后更新下一个": "源代码同步成功,3秒后更新下一个",
全部更新失败: "全部更新失败",
"全部更新完毕<br >成功:{{successNums}}<br >失败:{{failedNums}}<br >总计:{{scriptUrlListLength}}": "全部更新完毕<br >成功:{{successNums}}<br >失败:{{failedNums}}<br >总计:{{scriptUrlListLength}}",
"⚙ 设置": "⚙ 设置",
"{{SCRIPT_NAME}}-设置": "{{SCRIPT_NAME}}-设置",
美化页面元素: "美化页面元素",
美化历史版本页面: "美化历史版本页面",
"美化Greasyfork Beautify脚本": "美化Greasyfork Beautify脚本",
获取表单csrfToken失败: "获取表单csrfToken失败",
Toast配置: "Toast配置",
Toast位置: "Toast位置",
左上角: "左上角",
顶部: "顶部",
右上角: "右上角",
左边: "左边",
中间: "中间",
右边: "右边",
左下角: "左下角",
底部: "底部",
右下角: "右下角",
Toast显示在页面九宫格的位置: "Toast显示在页面九宫格的位置",
最多显示的数量: "最多显示的数量",
限制Toast显示的数量: "限制Toast显示的数量",
逆序弹出: "逆序弹出",
修改Toast弹出的顺序: "修改Toast弹出的顺序",
该脚本已经在该收藏集中: "该脚本已经在该收藏集中",
其它错误: "其它错误",
启用: "启用",
开启后下面的过滤功能才会生效: "开启后下面的功能才会生效",
屏蔽脚本: "屏蔽脚本",
点击查看规则: "点击查看规则",
过滤: "过滤",
代码同步: "代码同步",
美化: "美化",
修复代码行号显示: "修复代码行号显示",
修复代码行数超过1k行号显示不全问题: "修复代码行数超过1k行号显示不全问题",
"添加【寻找引用】按钮": "添加【寻找引用】按钮",
"在脚本栏添加按钮,一般用于搜索引用该库的相关脚本": "在脚本栏添加按钮,一般用于搜索引用该库的相关脚本",
"添加【收藏】按钮": "添加【收藏】按钮",
"在脚本栏添加按钮,一般用于快捷收藏该脚本/库": "在脚本栏添加按钮,一般用于快捷收藏该脚本/库",
修复图片宽度显示问题: "修复图片宽度显示问题",
修复图片在移动端宽度超出浏览器宽度问题: "修复图片在移动端宽度超出浏览器宽度问题",
"添加【今日检查】信息块": "添加【今日检查】信息块",
"在脚本信息栏添加【今日检查】信息块": "在脚本信息栏添加【今日检查】信息块",
"给Markdown添加【复制】按钮": "给Markdown添加【复制】按钮",
"在Markdown内容右上角添加【复制】按钮,点击一键复制Markdown内容": "在Markdown内容右上角添加【复制】按钮,点击一键复制Markdown内容",
开启后下面的功能才会生效: "开启后下面的功能才会生效",
检测页面加载: "检测页面加载",
"检测Greasyfork页面是否正常加载,如加载失败则自动刷新页面": "检测Greasyfork页面是否正常加载,如加载失败则自动刷新页面",
检测间隔: "检测间隔",
"设置检测上次刷新页面的间隔时间,当距离上次刷新页面的时间超过设置的值,将不再刷新页面": "设置检测上次刷新页面的间隔时间,当距离上次刷新页面的时间超过设置的值,将不再刷新页面",
美化顶部导航栏: "美化顶部导航栏",
"可能会跟Greasyfork Beautify脚本有冲突": "可能会跟Greasyfork Beautify脚本有冲突",
美化脚本列表: "美化脚本列表",
"双列显示且添加脚本卡片操作项(安装、收藏)": "双列显示且添加脚本卡片操作项(安装、收藏)",
操作面板: "操作面板",
"添加【操作面板】按钮": "添加【操作面板】按钮",
"在脚本列表页面时为顶部导航栏添加【操作面板】按钮": "在脚本列表页面时为顶部导航栏添加【操作面板】按钮",
操作: "操作",
安装此脚本: "安装此脚本",
脚本: "脚本",
历史版本: "历史版本",
自定义已读颜色: "自定义已读颜色",
在讨论内生效: "在讨论内生效",
用户: "用户",
控制台: "控制台",
"迁移【控制台】到顶部导航栏": "迁移【控制台】到顶部导航栏",
"将【控制台】按钮移动到顶部导航栏,节省空间": "将【控制台】按钮移动到顶部导航栏,节省空间",
"在版本下面添加【安装】、【查看代码】按钮": "在版本下面添加【安装】、【查看代码】按钮",
查看代码: "查看代码",
添加快捷操作按钮: "添加快捷操作按钮",
"在每一行讨论的最后面添加【过滤】按钮,需开启过滤功能才会生效": "在每一行讨论的最后面添加【过滤】按钮,需开启过滤功能才会生效",
选择需要过滤的选项: "选择需要过滤的选项",
"确定{{type}}:{{filterId}}?": "确定{{type}}:{{filterId}}?",
"该收藏集未包含:{{scriptId}}": "该收藏集未包含:{{scriptId}}",
帮助文档: "帮助文档",
"请输入规则,每行一个": "请输入规则,每行一个",
选择过滤的选项: "选择过滤的选项",
"脚本id:{{text}}": "脚本id:{{text}}",
"脚本名:{{text}}": "脚本名:{{text}}",
"作者id:{{text}}": "作者id:{{text}}",
"作者名:{{text}}": "作者名:{{text}}",
"作用域:脚本、脚本搜索、用户主页": "作用域:脚本、脚本搜索、用户主页",
"更新到 {{version}} 版本": "更新到 {{version}} 版本",
"降级到 {{version}} 版本": "降级到 {{version}} 版本",
"重新安装 {{version}} 版本": "重新安装 {{version}} 版本",
"发布的用户id:{{text}}": "发布的用户id:{{text}}",
自定义快捷键: "自定义快捷键",
点击录入快捷键: "点击录入快捷键",
快捷键发表回复: "快捷键发表回复",
"在输入框内按下快捷发表回复,例如:{{key}}": "在输入框内按下快捷发表回复,例如:{{key}}",
请先执行当前的录入操作: "请先执行当前的录入操作",
清空快捷键: "清空快捷键",
"请按下快捷键...": "请按下快捷键...",
成功录入: "成功录入",
"快捷键 {{key}} 已被 {{isUsedKey}} 占用": "快捷键 {{key}} 已被 {{isUsedKey}} 占用",
私聊: "私聊",
美化私信页面: "美化私信页面",
美化为左右对话模式: "美化为左右对话模式",
"最后回复:": "最后回复:",
进入: "进入",
记住回复内容: "记住回复内容",
"监听表单内的textarea内容改变并存储到indexDB中,提交表单将清除保存的数据,误刷新页面时可动态恢复": "监听表单内的textarea内容改变并存储到indexDB中,提交表单将清除保存的数据,误刷新页面时可动态恢复",
表单: "表单",
自动清理空间: "自动清理空间",
不清理: "不清理",
"{{value}} 天": "{{value}} 天",
"{{value}} 周": "{{value}} 周",
"{{value}} 个月": "{{value}} 个月",
半年: "半年",
计算中: "计算中",
根据设置的间隔时间自动清理保存的回复内容: "根据设置的间隔时间自动清理保存的回复内容",
"数据占用空间:{{size}}": "数据占用空间:{{size}}",
当前存储的数据所占用的空间大小: "当前存储的数据所占用的空间大小",
清空: "清空",
清理成功: "清理成功",
清理失败: "清理失败",
"Url To WebhookUrl": "Url 转 WebhookUrl",
关闭: "关闭",
"例如:": "例如:",
"结果:": "结果:",
转换前: "转换前",
转换后: "转换后",
使用namespace查询脚本信息: "使用namespace查询脚本信息",
脚本管理: "脚本管理",
"开启后检测已安装的脚本信息更准确,但是速度会更慢": "开启后检测已安装的脚本信息更准确,但是速度会更慢",
美化私信列表: "美化私信列表",
搜索: "搜索"
};
const en_US_language = {
GreasyFork优化: "GreasyFork Optimization",
请求取消: "http request cancel",
请求超时: "http request timeout",
请求异常: "http request error",
通用: "General",
账号: "Account",
密码: "Password",
语言: "Language",
"账号/密码": "Account/Password",
请输入账号: "Please enter your account number",
请输入密码: "Please enter password",
自动登录: "Auto Login",
自动登录当前保存的账号: "Automatically log in to the currently saved account",
"清空账号/密码": "Clear account/password",
点击清空: "Clear",
"确定清空账号和密码?": "Are you sure to clear your account and password?",
"已清空账号/密码": "Account/password cleared",
"源代码同步【脚本列表】": "Source Code Synchronization [Script List]",
一键同步: "Sync All",
前往用户主页: "Go to the user's homepage",
获取当前已登录的用户主页失败: "Failed to retrieve the currently logged in user's homepage",
"源代码同步【未上架的脚本】": "Source code synchronization [Script not listed]",
"源代码同步【库】": "Source code synchronization [Library]",
论坛: "Forum",
功能: "Features",
过滤重复的评论: "Filter duplicate comments",
"过滤掉重复的评论数量(≥2)": "Filter out duplicate comments (≥ 2)",
"过滤脚本(id)": "Filter script (id)",
"请输入脚本id,每行一个": "Please enter the script ID, one per line",
"过滤发布的用户(id)": "Filter published users (id)",
"请输入用户id,每行一个": "Please enter the user ID, one per line",
"过滤回复的用户(id)": "User (ID) who filters replies",
优化: "Optimization",
固定当前语言: "Fix current language",
无: "nothing",
"如button、input、textarea": "For example button、input、textarea",
更直观的查看版本迭代: "More intuitive viewing of version iterations",
美化上传图片按钮: "Beautify upload image button",
放大上传区域: "Enlarge the upload area",
优化图片浏览: "Optimize image browsing",
使用Viewer浏览图片: "Using Viewer to browse images",
覆盖图床图片跳转: "Overlay bed image jump",
"配合上面的【优化图片浏览】更优雅浏览图片": "Collaborate with the optimization of image browsing above to browse images more elegantly",
'需安装Greasyfork Beautify脚本,<a href="https://greasyfork.org/zh-CN/scripts/446849-greasyfork-beautify" target="_blank">🖐点我安装</a>': 'Greasyfork Beauty script needs to be installed,<a href="https://greasyfork.org/zh-CN/scripts/446849-greasyfork-beautify" target="_blank">🖐 Click me to install</a>',
代码: "Code",
添加复制代码按钮: "Add Copy Code Button",
更优雅的复制: "More elegant replication",
快捷键: "Shortcut keys",
"【F】键全屏、【Alt+Shift+F】键宽屏": "【F】 Key full screen, [Alt+Shift+F] key wide screen",
库: "Library",
脚本列表: "Script List",
"请输入屏蔽规则,每行一个": "Please enter a blocking rule, one per line",
请求admin内容失败: "Request for admin content failed",
解析admin的源代码同步表单失败: "Failed to parse the source code of admin and synchronize the form",
源代码同步失败: "Source code synchronization failed",
获取用户信息失败: "Failed to obtain user information",
获取用户的收藏集失败: "Failed to retrieve user's collection",
"解析Script Sets失败": "Parsing Script Sets failed",
"获取收藏集{{setsId}}失败": "Failed to retrieve collection {{setsId}}",
"获取表单元素#edit_script_set失败": "Failed to retrieve form element #edit_script_set",
更新收藏集表单请求失败: "Update collection form request failed",
请先在菜单中录入账号: "Please enter your account in the menu first",
请先在菜单中录入密码: "Please enter your password in the menu first",
"获取csrf-token失败": "Failed to obtain csrf token",
"正在登录中...": "Logging in...",
"登录失败,请在控制台查看原因": "Login failed, please check the reason in the console",
"登录成功,1s后自动跳转": "Login successful, automatically redirect after 1 second",
"登录失败,可能是账号/密码错误,请在控制台查看原因": "Login failed, possibly due to incorrect account/password. Please check the reason in the console",
"美化 历史版本 页面": "Beautify the historical version page",
未找到history_versions元素列表: "History_versions element list not found",
"yyyy年MM月dd日 HH:mm:ss": "yyyy-MM-dd HH:mm:ss",
"美化 Greasyfork Beautify脚本": "Beautify Greasyfork Beauty Script",
"❌ 最多同时长传5张图": "❌ Upload up to 5 images simultaneously",
"❌ 图片:{{name}} 大小:{{size}}": "❌ Image:{{name}} Size:{{size}}",
"已过滤:{{oldCount}}": "Filtered:{{oldCount}}",
寻找引用: "Find references",
获取脚本id失败: "Failed to obtain script ID",
收藏: "Collection",
请先登录账号: "Please log in to your account first",
获取用户id失败: "Failed to obtain user ID",
"获取收藏夹中...": "Get in favorites...",
收藏集: "Collection",
"添加中...": "Adding...",
"添加失败,{{selector}}元素不存在": "Add failed, {{selector}} element does not exist",
"未找到{{selector}}元素": "{{selector}} element not found",
添加失败: "Add failed",
添加成功: "Successfully added",
"删除中...": "Deleting...",
删除成功: "Delete successful",
添加: "Add in deletion",
刪除: "Delete",
"拦截跳转:": "Intercept jump:",
今日检查: "Today's inspection",
复制代码: "Copy Code",
"加载文件中...": "Loading files...",
复制成功: "Copy successful",
"✅ 复制成功!": "✅ Copy successful!",
"当前语言:{{currentLocaleLanguage}},,3秒后切换至:{{localeLanguage}}": "Current language: {{currentLocaleLanguage}}, switch to {{localeLanguage}} in 3 seconds",
"导航至:": "Navigation to:",
"请先登录账号!": "Please log in to your account first!",
"获取信息中,请稍后...": "Obtaining information, please wait...",
"获取成功,共 {{count}} 个": "Successfully obtained, a total of {{count}}",
"评分:": "Rating:",
"语言:": "Language:",
"版本:": "Version:",
"更新:": "Update:",
同步代码: "Synchronize Code",
"同步中...": "Synchronizing...",
手动: "Manual",
自动: "Automatic",
"同步方式:{{syncMode}}": "Synchronization method: {{syncMode}}",
同步成功: "Sync successful",
同步失败: "Sync failed",
该脚本未设置同步信息: "The script has not set synchronization information",
"上次重载时间 {{time}},{{timeout}}秒内拒绝反复重载": "Last reload time {{time}}, rejected repeated reloads within {{timeout}} seconds",
"名称:": "Name:",
"进度:": "Progress:",
"未获取到【脚本列表】": "Unable to obtain [Script List]",
"源代码同步成功,3秒后更新下一个": "Source code synchronization successful, update next one in 3 seconds",
全部更新失败: "All updates failed",
"全部更新完毕<br >成功:{{successNums}}<br >失败:{{failedNums}}<br >总计:{{scriptUrlListLength}}": "All updates completed<br>Success: {{successNums}}<br>Failure: {{failed Nums}}<br>Total: {{scriptUrlListLength}}",
"⚙ 设置": "⚙ Setting",
"{{SCRIPT_NAME}}-设置": "{{SCRIPT_NAME}}-Setting",
美化页面元素: "Beautify page elements",
美化历史版本页面: "Beautify the historical version page",
"美化Greasyfork Beautify脚本": "Beautify Greasyfork Beauty Script",
获取表单csrfToken失败: "Failed to obtain form csrfToken",
Toast配置: "Toast Config",
Toast位置: "Toast position",
左上角: "Top left",
顶部: "Top",
右上角: "Top right",
左边: "Left",
中间: "Center",
右边: "Right",
左下角: "Bottom left",
底部: "Bottom",
右下角: "Bottom right",
Toast显示在页面九宫格的位置: "Toast is displayed in the nine grid position on the page",
最多显示的数量: "Maximum number of displays",
限制Toast显示的数量: "Limit the number of Toast displays",
逆序弹出: "Reverse pop-up",
修改Toast弹出的顺序: "Modify the order in which Toast pops up",
该脚本已经在该收藏集中: "The script is already in this collection",
其它错误: "Ohter Error",
启用: "Enable",
开启后下面的过滤功能才会生效: "The following filtering features will only take effect after it is enabled",
屏蔽脚本: "Block script",
点击查看规则: "Click to view rules",
过滤: "Filter",
代码同步: "Code synchronization",
美化: "Beautify",
修复代码行号显示: "Fix code line number display",
修复代码行数超过1k行号显示不全问题: "Fix the problem that the code line number display is not complete when the number of lines exceeds 1k",
"添加【寻找引用】按钮": "Add the button to find references",
"在脚本栏添加按钮,一般用于搜索引用该库的相关脚本": "Add a button to the script bar, generally used to search for scripts that reference this library",
"添加【收藏】按钮": "Add the button to collect",
"在脚本栏添加按钮,一般用于快捷收藏该脚本/库": "Add a button to the script bar, generally used to quickly collect this script / library",
修复图片宽度显示问题: " Fix the problem that the picture width display is not complete",
修复图片在移动端宽度超出浏览器宽度问题: "Fix the problem that the picture width exceeds the browser width on mobile",
"添加【今日检查】信息块": "Add the block of information of today's inspection",
"在脚本信息栏添加【今日检查】信息块": "Add the block of information of today's inspection to the script information bar",
"给Markdown添加【复制】按钮": "Add the button to copy to Markdown",
"在Markdown内容右上角添加【复制】按钮,点击一键复制Markdown内容": "Add the button to copy to the top right corner of the Markdown content, click to copy the Markdown content in one click",
开启后下面的功能才会生效: "The following features will only take effect after it is enabled",
检测页面加载: "Detect page loading",
"检测Greasyfork页面是否正常加载,如加载失败则自动刷新页面": "Detect whether the Greasyfork page is loaded normally. If the loading fails, the page will be automatically refreshed",
检测间隔: "Detection interval",
"设置检测上次刷新页面的间隔时间,当距离上次刷新页面的时间超过设置的值,将不再刷新页面": "Set the interval time for detecting the last refresh page. If the time since the last refresh page exceeds the set value, the page will no longer be refreshed",
美化顶部导航栏: "Beautify the top navigation bar",
"可能会跟Greasyfork Beautify脚本有冲突": "Possible conflict with Greasymfork Beautify script",
美化脚本列表: "Beautify Script List",
"双列显示且添加脚本卡片操作项(安装、收藏)": "Double column display and add script card operation items (installation, bookmarking)",
操作面板: "Operation Panel",
"添加【操作面板】按钮": "Add [Operation Panel] button",
"在脚本列表页面时为顶部导航栏添加【操作面板】按钮": "Add an 'Operation Panel' button to the top navigation bar on the script list page",
操作: "Operation",
安装此脚本: "Install this script",
脚本: "Scripts",
历史版本: "Historical version",
自定义已读颜色: "Customize read colors",
在讨论内生效: "Effective within the discussion",
用户: "Users",
控制台: "Console",
"迁移【控制台】到顶部导航栏": "Migration of Console to Top Navigation Bar",
"将【控制台】按钮移动到顶部导航栏,节省空间": "Move the 'Console' button to the top navigation bar to save space",
添加额外的标签按钮: "Add additional label button",
"在版本下面添加【安装】、【查看代码】按钮": "Add 【 Install 】 and 【 View Code 】 buttons under the version",
查看代码: "View Code",
"添加【过滤】按钮": "Add [Filter] button",
"添加【举报】按钮": "Add [Report] button",
"在每一行讨论的最后面添加【过滤】按钮,需开启过滤功能才会生效": "Add a 'Filter' button at the end of each discussion line. The filtering features needs to be enabled for it to take effect",
"在每一行讨论的最后面添加【举报】按钮": "Add a Report button at the end of each line of discussion",
选择需要过滤的选项: "Select the options that need to be filtered",
"确定{{type}}:{{filterId}}?": "Are you sure {{type}}:{{filterId}}?",
"该收藏集未包含:{{scriptId}}": "This collection does not include:{{scriptId}}",
帮助文档: "Help document",
"请输入规则,每行一个": "Please enter a rule, one per line",
选择过滤的选项: "Select filtering options",
"脚本id:{{text}}": "Script Id: {{text}}",
"脚本名:{{text}}": "Script Name: {{text}}",
"作者id:{{text}}": "Author Id: {{text}}",
"作者名:{{text}}": "Author Name: {{text}}",
"作用域:脚本、脚本搜索、用户主页": "Scope: Script, Script Search, User Homepage",
"更新到 {{version}} 版本": "Update To {{version}} Version",
"降级到 {{version}} 版本": "Downgrade to {{version}} Version",
"重新安装 {{version}} 版本": "Reinstall {{version}} Version",
"发布的用户id:{{text}}": "Published user ID: {{text}}",
自定义快捷键: "Customize shortcut keys",
点击录入快捷键: "Click on the input shortcut key",
快捷键发表回复: "Shortcut key to post reply",
"在输入框内按下快捷发表回复,例如:{{key}}": "Press the shortcut to post a reply in the input box, for example: {{key}}",
请先执行当前的录入操作: "Please perform the current input operation first",
清空快捷键: "Clear shortcut keys",
"请按下快捷键...": "Please press the shortcut key...",
成功录入: "Successful entry",
"快捷键 {{key}} 已被 {{isUsedKey}} 占用": "The shortcut key {{key}} is already used by {{isUsedKey}}",
私聊: "Private Chat",
美化私信页面: "Beautify the private message page",
美化为左右对话模式: "Beautify as a left-right dialogue mode",
"最后回复:": "Final response:",
进入: "Enter",
记住回复内容: "Remember the reply content",
"监听表单内的textarea内容改变并存储到indexDB中,提交表单将清除保存的数据,误刷新页面时可动态恢复": "Monitor changes to the textarea content in the form and store it in the index database. Submitting the form will clear the saved data, and dynamic recovery can be achieved when the page is accidentally refreshed",
表单: "Forms",
自动清理空间: "Automatically clear space",
不清理: "Not cleaning",
"{{value}} 天": "{{value}} day",
"{{value}} 周": "{{value}} weeks",
"{{value}} 个月": "{{value}} months",
半年: "half a year",
计算中: "In the process of calculation",
根据设置的间隔时间自动清理保存的回复内容: "Automatically clean up saved reply content according to the set interval time",
"数据占用空间:{{size}}": "Data occupancy space: {{size}}",
当前存储的数据所占用的空间大小: "The size of the space occupied by the currently stored data",
清空: "Clear",
清理成功: "Cleanup successful",
清理失败: "Cleaning failed",
"Url To WebhookUrl": "Url To WebhookUrl",
关闭: "Clsoe",
"例如:": "Example: ",
"结果:": "Result: ",
转换前: "Before Parse",
转换后: "Parse Result",
使用namespace查询脚本信息: "Use a namespace to query script information",
脚本管理: "Script management",
"开启后检测已安装的脚本信息更准确,但是速度会更慢": "Detecting the installed script information is more accurate, but slower",
美化私信列表: "Beautify the private message list",
搜索: "Search",
"新增【{{buttonText}}】按钮": "Added [{{buttonText}}] button",
"该Checkbox按钮开启后,自动过滤出包含搜索关键词的脚本": "When the Checkbox button is turned on, it automatically filters out scripts that contain search terms",
"名称-全词匹配": "Name - Full word match",
"描述-全词匹配": "Description - Full word match",
"作者名称-全词匹配": "Author name - Full word match",
获取举报表单信息失败: "Failed to obtain report form information. Procedure",
发送举报表单失败: "Failed to send the report form. Procedure",
举报: "Report",
"举报讨论:": "Report discussion:",
"举报脚本:": "Report script:",
"举报用户:": "Report user:"
};
const KEY = "GM_Panel";
const ATTRIBUTE_INIT = "data-init";
const ATTRIBUTE_KEY = "data-key";
const ATTRIBUTE_DEFAULT_VALUE = "data-default-value";
const ATTRIBUTE_INIT_MORE_VALUE = "data-init-more-value";
const PROPS_STORAGE_API = "data-storage-api";
const LanguageInit = function() {
let settingPanel = _GM_getValue(KEY, {});
let lng = settingPanel["setting-language"] || "zh-CN";
i18next.init({
lng,
// lng: "zh-CN",
fallbackLng: "zh-CN",
resources: {
"zh-CN": {
translation: { ...zh_CN_language }
},
"en-US": {
translation: { ...en_US_language }
}
}
});
};
const CommonUtil = {
/**
* 添加屏蔽CSS
* @param args
* @example
* addBlockCSS("")
* addBlockCSS("","")
* addBlockCSS(["",""])
*/
addBlockCSS(...args) {
let selectorList = [];
if (args.length === 0) {
return;
}
if (args.length === 1 && typeof args[0] === "string" && args[0].trim() === "") {
return;
}
args.forEach((selector) => {
if (Array.isArray(selector)) {
selectorList = selectorList.concat(selector);
} else {
selectorList.push(selector);
}
});
return addStyle(`${selectorList.join(",\n")}{display: none !important;}`);
},
/**
* 设置GM_getResourceText的style内容
* @param resourceMapData 资源数据
* @example
* setGMResourceCSS({
* keyName: "ViewerCSS",
* url: "https://example.com/example.css",
* })
*/
setGMResourceCSS(resourceMapData) {
let cssText = typeof _GM_getResourceText === "function" ? _GM_getResourceText(resourceMapData.keyName) : "";
if (typeof cssText === "string" && cssText) {
addStyle(cssText);
} else {
CommonUtil.loadStyleLink(resourceMapData.url);
}
},
/**
* 添加<link>标签
* @param url
* @example
* loadStyleLink("https://example.com/example.css")
*/
async loadStyleLink(url2) {
let $link = document.createElement("link");
$link.rel = "stylesheet";
$link.type = "text/css";
$link.href = url2;
domUtils.ready(() => {
document.head.appendChild($link);
});
},
/**
* 添加<script>标签
* @param url
* @example
* loadStyleLink("https://example.com/example.js")
*/
async loadScript(url2) {
let $script = document.createElement("script");
$script.src = url2;
return new Promise((resolve) => {
$script.onload = () => {
resolve(null);
};
(document.head || document.documentElement).appendChild($script);
});
},
/**
* 将url修复,例如只有search的链接修复为完整的链接
*
* 注意:不包括http转https
* @param url 需要修复的链接
* @example
* 修复前:`/xxx/xxx?ss=ssss`
* 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
* @example
* 修复前:`//xxx/xxx?ss=ssss`
* 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
* @example
* 修复前:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
* 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
* @example
* 修复前:`xxx/xxx?ss=ssss`
* 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
*/
fixUrl(url2) {
url2 = url2.trim();
if (url2.match(/^http(s|):\/\//i)) {
return url2;
} else {
if (!url2.startsWith("/")) {
url2 += "/";
}
url2 = window.location.origin + url2;
return url2;
}
},
/**
* http转https
* @param url 需要修复的链接
* @example
* 修复前:
* 修复后:
* @example
* 修复前:
* 修复后:
*/
fixHttps(url2) {
if (url2.startsWith("https://")) {
return url2;
}
if (!url2.startsWith("http://")) {
return url2;
}
let urlObj2 = new URL(url2);
urlObj2.protocol = "https:";
return urlObj2.toString();
}
};
LanguageInit();
_GM_getValue(KEY, {});
const _SCRIPT_NAME_ = i18next.t("GreasyFork优化");
const utils = Utils.noConflict();
const domUtils = DOMUtils.noConflict();
const __pops = pops;
const log = new utils.Log(
_GM_info,
_unsafeWindow.console || _monkeyWindow.console
);
const SCRIPT_NAME = ((_a = _GM_info == null ? void 0 : _GM_info.script) == null ? void 0 : _a.name) || _SCRIPT_NAME_;
const DEBUG = false;
log.config({
debug: DEBUG,
logMaxCount: 1e3,
autoClearConsole: true,
tag: true
});
Qmsg.config(
Object.defineProperties(
{
html: true,
autoClose: true,
showClose: false
},
{
position: {
get() {
return PopsPanel.getValue("qmsg-config-position", "bottom");
}
},
maxNums: {
get() {
return PopsPanel.getValue("qmsg-config-maxnums", 5);
}
},
showReverse: {
get() {
return PopsPanel.getValue("qmsg-config-showreverse", true);
}
},
zIndex: {
get() {
let maxZIndex = Utils.getMaxZIndex();
let popsMaxZIndex = pops.config.InstanceUtils.getPopsMaxZIndex().zIndex;
return Utils.getMaxValue(maxZIndex, popsMaxZIndex) + 100;
}
}
}
)
);
const GM_Menu = new utils.GM_Menu({
GM_getValue: _GM_getValue,
GM_setValue: _GM_setValue,
GM_registerMenuCommand: _GM_registerMenuCommand,
GM_unregisterMenuCommand: _GM_unregisterMenuCommand
});
const httpx = new utils.Httpx(_GM_xmlhttpRequest);
httpx.interceptors.response.use(void 0, (data) => {
log.error("拦截器-请求错误", data);
if (data.type === "onabort") {
Qmsg.warning(i18next.t("请求取消"));
} else if (data.type === "onerror") {
Qmsg.error(i18next.t("请求异常"));
} else if (data.type === "ontimeout") {
Qmsg.error(i18next.t("请求超时"));
} else {
Qmsg.error(i18next.t("其它错误"));
}
return data;
});
httpx.config({
logDetails: DEBUG
});
({
Object: {
defineProperty: _unsafeWindow.Object.defineProperty
},
Function: {
apply: _unsafeWindow.Function.prototype.apply,
call: _unsafeWindow.Function.prototype.call
},
Element: {
appendChild: _unsafeWindow.Element.prototype.appendChild
},
setTimeout: _unsafeWindow.setTimeout
});
const addStyle = utils.addStyle.bind(utils);
document.querySelector.bind(document);
document.querySelectorAll.bind(document);
const UIButton = function(text, description, buttonText, buttonIcon, buttonIsRightIcon, buttonIconIsLoading, buttonType, clickCallBack, afterAddToUListCallBack) {
let result = {
text,
type: "button",
description,
buttonIcon,
buttonIsRightIcon,
buttonIconIsLoading,
buttonType,
buttonText,
callback(event) {
if (typeof clickCallBack === "function") {
clickCallBack(event);
}
},
afterAddToUListCallBack
};
return result;
};
const UIInput = function(text, key, defaultValue, description, changeCallBack, placeholder = "", isNumber, isPassword) {
let result = {
text,
type: "input",
isNumber: Boolean(isNumber),
isPassword: Boolean(isPassword),
props: {},
attributes: {},
description,
getValue() {
return this.props[PROPS_STORAGE_API].get(key, defaultValue);
},
callback(event, value) {
this.props[PROPS_STORAGE_API].set(key, value);
},
placeholder
};
Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
Reflect.set(result.props, PROPS_STORAGE_API, {
get(key2, defaultValue2) {
return PopsPanel.getValue(key2, defaultValue2);
},
set(key2, value) {
PopsPanel.setValue(key2, value);
}
});
return result;
};
const UISwitch = function(text, key, defaultValue, clickCallBack, description, afterAddToUListCallBack) {
let result = {
text,
type: "switch",
description,
attributes: {},
props: {},
getValue() {
return Boolean(
this.props[PROPS_STORAGE_API].get(key, defaultValue)
);
},
callback(event, __value) {
let value = Boolean(__value);
log.success(`${value ? "开启" : "关闭"} ${text}`);
this.props[PROPS_STORAGE_API].set(key, value);
},
afterAddToUListCallBack
};
Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
Reflect.set(result.props, PROPS_STORAGE_API, {
get(key2, defaultValue2) {
return PopsPanel.getValue(key2, defaultValue2);
},
set(key2, value) {
PopsPanel.setValue(key2, value);
}
});
return result;
};
const GreasyforkApi = {
/**
* 获取脚本统计数据
* @param scriptId
*/
async getScriptStats(scriptId) {
let response = await httpx.get(`/scripts/${scriptId}/stats.json`, {
fetch: true,
allowInterceptConfig: false
});
log.info(response);
if (!response.status) {
log.error(i18next.t("获取脚本统计数据失败"));
return;
}
let scriptStatsJSON = utils.toJSON(response.data.responseText);
return scriptStatsJSON;
},
/**
* 解析并获取admin内的源代码同步的配置表单
* @param scriptId
*/
async getSourceCodeSyncFormData(scriptId) {
let response = await httpx.get(`/scripts/${scriptId}/admin`, {
fetch: true,
allowInterceptConfig: false
});
log.info(response);
if (!response.status) {
Qmsg.error(i18next.t("请求admin内容失败"));
return;
}
let adminHTML = response.data.responseText;
let adminHTMLElement = domUtils.parseHTML(adminHTML, false, true);
let formElement = adminHTMLElement.querySelector("form.edit_script");
if (!formElement) {
Qmsg.error(i18next.t("解析admin的源代码同步表单失败"));
return;
}
let formData = new FormData(formElement);
return formData;
},
/**
* 进行源代码同步,要求先getSourceCodeSyncFormData
* @param scriptId
* @param data
*/
async sourceCodeSync(scriptId, data) {
let response = await httpx.post(`/scripts/${scriptId}/sync_update`, {
fetch: true,
data,
allowInterceptConfig: false
});
log.info(response);
if (!response.status) {
Qmsg.error(i18next.t("源代码同步失败"));
return;
}
return response;
},
/**
* 获取用户的信息,包括脚本列表、未上架的脚本、库
*/
async getUserInfo(userId) {
let response = await httpx.get(`/users/${userId}.json`, {
fetch: true,
allowInterceptConfig: false
});
log.success(response);
if (!response.status) {
Qmsg.error(i18next.t("获取用户信息失败"));
return;
}
let data = utils.toJSON(response.data.responseText);
data["scriptList"] = [];
data["scriptLibraryList"] = [];
data["scripts"].forEach((scriptInfo) => {
if (scriptInfo["code_url"].endsWith(".user.js")) {
data["scriptList"].push(scriptInfo);
} else {
data["scriptLibraryList"].push(scriptInfo);
}
});
return data;
},
/**
* 获取用户的收藏集
* @param userId
*/
async getUserCollection(userId) {
let response = await httpx.get(`/users/${userId}`, {
fetch: true,
allowInterceptConfig: false
});
log.info("获取用户的收藏集", response);
if (!response.status) {
Qmsg.error(i18next.t("获取用户的收藏集失败"));
return;
}
let respText = response.data.responseText;
let respDocument = domUtils.parseHTML(respText, true, true);
let userScriptSets = respDocument.querySelector("#user-script-sets");
if (!userScriptSets) {
log.error("解析Script Sets失败");
return;
}
let scriptSetsIdList = [];
userScriptSets.querySelectorAll("li").forEach((liElement) => {
var _a2;
let $ele = liElement.querySelector("a:last-child");
if (!$ele) {
return;
}
let setsUrl = $ele.href;
if (setsUrl.includes("?fav=1")) {
return;
}
let setsName = liElement.querySelector("a").innerText;
let setsId = (_a2 = setsUrl.match(/\/sets\/([\d]+)\//)) == null ? void 0 : _a2[1];
scriptSetsIdList.push({
id: setsId,
name: setsName
});
});
return scriptSetsIdList;
},
/**
* 获取某个收藏集的信息
* @param userId 用户id
* @param setsId 收藏集id
*/
async getUserCollectionInfo(userId, setsId) {
let response = await httpx.get(`/users/${userId}/sets/${setsId}/edit`, {
fetch: true,
allowInterceptConfig: false
});
log.info(response);
if (!response.status) {
Qmsg.error(i18next.t("获取收藏集{{setsId}}失败", { setsId }));
return;
}
let respText = response.data.responseText;
let respDocument = domUtils.parseHTML(respText, true, true);
let $edit_script_set_form = respDocument.querySelector(
'form[id^="edit_script_set"]'
);
if (!$edit_script_set_form) {
Qmsg.error(i18next.t("获取表单元素#edit_script_set失败"));
return;
}
let formData = new FormData($edit_script_set_form);
let csrfToken = respDocument.querySelector(
'meta[name="csrf-token"]'
);
if (!csrfToken) {
Qmsg.error(i18next.t("获取表单csrfToken失败"));
return;
}
if (csrfToken.hasAttribute("content")) {
let authenticity_token = csrfToken.getAttribute("content");
if (authenticity_token) {
formData.set("authenticity_token", authenticity_token);
}
}
return formData;
},
/**
* 更新用户的某个收藏集的表单信息
* @param userId 用户id
* @param setsId 收藏集id
* @param data
*/
async updateUserSetsInfo(userId, setsId, data) {
let response = await httpx.post(`/users/${userId}/sets/${setsId}`, {
fetch: true,
allowInterceptConfig: false,
headers: {
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Cache-Control": "no-cache",
"Content-Type": "application/x-www-form-urlencoded",
Pragma: "no-cache"
},
fetchInit: {
referrerPolicy: "strict-origin-when-cross-origin"
},
data
});
log.info(response);
if (!response.status) {
Qmsg.error(i18next.t("更新收藏集表单请求失败"));
return;
}
let respText = response.data.responseText;
let respDocument = domUtils.parseHTML(respText, true, true);
return respDocument;
},
/**
* 切换语言
* @param url
*/
async switchLanguage(url2) {
let response = await httpx.get(url2, {
fetch: true,
headers: {
"Upgrade-Insecure-Requests": "1"
}
});
log.info(response);
if (!response.status) {
return;
}
}
};
const url = globalThis.location.href;
const urlObj = new URL(url);
const pathname = urlObj.pathname;
const searchParams = urlObj.searchParams;
const GreasyforkRouter = {
/**
* 代码页面
*
* + /code...
*/
isCode() {
var _a2;
return Boolean((_a2 = pathname.split("/")) == null ? void 0 : _a2.includes("code"));
},
/**
* 代码页面
*
* (严格比较)
*
* + /code
* + /code/
*/
isCodeStrict() {
return Boolean(pathname.match(/\/code(\/|)$/));
},
/**
* 版本页面
*
* (严格比较)
*
* + /version
* + /version/
*/
isVersion() {
return Boolean(pathname.match(/\/versions(\/|)$/));
},
/**
* 用户
*
* + /users/...
*/
isUsers() {
return Boolean(pathname.match(/\/.+\/users\/.+/gi));
},
/**
* 私聊用户页面,可能是全部私信页面,也可能是某个用户的私信页面
*
* + /conversations...
*/
isUsersConversations() {
return this.isUsers() && Boolean(pathname.includes("/conversations"));
},
/**
* 私聊xxx用户页面
*
* + /conversations/111...
*/
isUsersConversationsWithSomeUser() {
return this.isUsersConversations() && Boolean(pathname.match(/\/conversations\/[\d]+/));
},
/**
* 脚本页面(单个脚本的页面)
*
* + /scripts/111...
*/
isScript() {
return Boolean(pathname.match(/\/scripts\/[\d+]/));
},
/**
* 脚本列表页面
*
* (严格比较)
*
* + /scripts
* + /scripts/
*/
isScriptList() {
return Boolean(pathname.match(/\/scripts(\/|)$/));
},
/**
* 脚本列表-按域名
*
* + /scripts/by-site...
*/
isScriptsBySite() {
return Boolean(pathname.match("/scripts/by-site"));
},
/**
* 库列表页面
*
* (严格比较)
*
* + /libraries
* + /libraries/
*/
isScriptLibraryList() {
return Boolean(pathname.match(/\/libraries(\/|)$/));
},
/**
* 脚本搜索结果页面
*
* + /scripts?q=
*/
isScriptSearch() {
return this.isScriptList() && searchParams.has("q");
},
/**
* 脚本代码搜索页面
*
* (严格比较)
*
* + /code-search
* + /code-search/
*/
isScriptCodeSearch() {
return Boolean(pathname.match(/\/code-search(\/|)$/));
},
/**
* 讨论页面
*
* (严格比较)
*
* + /discussions
* + /discussions/
*/
isDiscuessions() {
return Boolean(pathname.match(/\/discussions(\/|)$/));
}
};
const GreasyforkUrlUtils = {
/**
* 获取脚本安装的链接
* @param scriptId
* @param scriptVersion
* @param scriptName
* @returns
*/
getInstallUrl(scriptId, scriptVersion, scriptName) {
if (utils.isNotNull(scriptName)) {
scriptName = "/" + scriptName;
} else {
scriptName = "";
}
return `https://update.greasyfork.org/scripts/${scriptId}/${scriptVersion}${scriptName}.user.js`;
},
/**
* 获取脚本的代码页面链接
* @param scriptId
* @param scriptVersion
* @returns
*/
getCodeUrl(scriptId, scriptVersion) {
if (utils.isNull(scriptVersion)) {
scriptVersion = "";
}
return `https://greasyfork.org/scripts/${scriptId}/code?version=${scriptVersion}`;
},
/**
* 获取代码搜索地址
* @param url
*/
getCodeSearchUrl(url2) {
return "https://greasyfork.org/zh-CN/scripts/code-search?c=" + url2;
},
/**
* 获取脚本的信息
* @param scriptId 脚本id
*/
getScriptInfoUrl(scriptId) {
return `https://greasyfork.org/scripts/${scriptId}.json`;
},
/**
* 获取管理地址
* @param url
*/
getAdminUrl(url2) {
return url2 + "/admin";
},
/**
* 从字符串中提取Id
* @param text
* @default window.location.pathname
*/
getScriptId(text) {
var _a2, _b;
return (_b = (_a2 = text || window.location.pathname) == null ? void 0 : _a2.match(
/\/scripts\/([\d]+)/i
)) == null ? void 0 : _b[1];
},
/**
* 从字符串中提取用户id
* @param text
* @default window.location.pathname
*/
getUserId(text) {
var _a2;
return (_a2 = (text || window.location.pathname).match(/\/users\/([\d]+)/i)) == null ? void 0 : _a2[1];
},
/**
* 获取举报地址
*/
getReportUrl(item_class, item_id) {
return `${window.location.origin}/reports/new?item_class=${item_class}&item_id=${item_id}`;
},
/**
* 从字符串中提取脚本名
* @param text
*/
getScriptName(text) {
let pathname2 = window.location.pathname;
if (text != null) {
pathname2 = new URL(text).pathname;
}
pathname2 = decodeURIComponent(pathname2);
let pathnameSplit = pathname2.split("/");
for (const name of pathnameSplit) {
let nameMatch = name.match(/[\d]+/);
if (nameMatch && nameMatch.length) {
return nameMatch[1];
}
}
},
/**
* 获取需要切换语言的Url
* @default "zh-CN"
*/
getSwitchLanguageUrl(localeLanguage = "zh-CN") {
let url2 = window.location.origin;
let urlSplit = window.location.pathname.split("/");
urlSplit[1] = localeLanguage;
url2 = url2 + urlSplit.join("/");
url2 += window.location.search;
if (window.location.search === "") {
url2 += "?locale_override=1";
} else if (!window.location.search.includes("locale_override=1")) {
url2 += "&locale_override=1";
}
return url2;
}
};
const GreasyforkMenu = {
/**
* @class
*/
menu: GM_Menu,
/**
* 当前是否已登录
*/
isLogin: false,
/**
* 初始化环境变量
*/
initEnv() {
let userLinkElement = this.getUserLinkElement();
this.isLogin = Boolean(userLinkElement);
},
/**
* 获取当前登录用户的a标签元素
*/
getUserLinkElement() {
return document.querySelector(
"#nav-user-info span.user-profile-link a"
);
},
/**
* 更新脚本
* @param scriptUrlList
*/
async updateScript(scriptUrlList) {
let getLoadingHTML = function(scriptName, progress = 1) {
return `
<div style="display: flex;flex-direction: column;align-items: flex-start;">
<div style="height: 30px;line-height: 30px;">${i18next.t(
"名称:"
)}${scriptName}</div>
<div style="height: 30px;line-height: 30px;">${i18next.t(
"进度:"
)}${progress}/${scriptUrlList.length}</div>
</div>`;
};
if (utils.isNull(scriptUrlList)) {
Qmsg.error(i18next.t("未获取到【脚本列表】"));
} else {
let loading = Qmsg.loading(
getLoadingHTML(
GreasyforkUrlUtils.getScriptName(scriptUrlList[0])
),
{
html: true
}
);
let successNums = 0;
let failedNums = 0;
for (let index = 0; index < scriptUrlList.length; index++) {
let scriptUrl = scriptUrlList[index];
let scriptId = GreasyforkUrlUtils.getScriptId(scriptUrl);
log.success("更新:" + scriptUrl);
let scriptName = GreasyforkUrlUtils.getScriptName(scriptUrl);
loading.setHTML(getLoadingHTML(scriptName, index + 1));
let codeSyncFormData = await GreasyforkApi.getSourceCodeSyncFormData(
scriptId
);
if (codeSyncFormData) {
let syncUpdateStatus = await GreasyforkApi.sourceCodeSync(
scriptId,
codeSyncFormData
);
if (syncUpdateStatus) {
Qmsg.success(i18next.t("源代码同步成功,3秒后更新下一个"));
await utils.sleep(3e3);
successNums++;
} else {
Qmsg.error(i18next.t("源代码同步失败"));
failedNums++;
}
} else {
Qmsg.error(i18next.t("源代码同步失败"));
failedNums++;
}
}
loading.close();
if (successNums === 0) {
Qmsg.error(i18next.t("全部更新失败"));
} else {
Qmsg.success(
i18next.t(
"全部更新完毕<br >成功:{{successNums}}<br >失败:{{failedNums}}<br >总计:{{scriptUrlListLength}}",
{
successNums,
failedNums,
scriptUrlListLength: scriptUrlList.length
}
),
{
html: true
}
);
}
}
},
/**
* 处理本地的goto事件
*/
handleLocalGotoCallBack() {
if (PopsPanel.getValue("goto_updateSettingsAndSynchronize_scriptList")) {
PopsPanel.deleteValue("goto_updateSettingsAndSynchronize_scriptList");
if (!GreasyforkRouter.isUsers()) {
PopsPanel.setValue(
"goto_updateSettingsAndSynchronize_scriptList",
true
);
if (GreasyforkMenu.getUserLinkElement()) {
Qmsg.success(i18next.t("前往用户主页"));
window.location.href = GreasyforkMenu.getUserLinkElement().href;
} else {
Qmsg.error(i18next.t("获取当前已登录的用户主页失败"));
}
return;
}
let scriptUrlList = [];
document.querySelectorAll(
"#user-script-list-section li a.script-link"
).forEach((item) => {
scriptUrlList = scriptUrlList.concat(
GreasyforkUrlUtils.getAdminUrl(item.href)
);
});
GreasyforkMenu.updateScript(scriptUrlList);
} else if (PopsPanel.getValue("goto_updateSettingsAndSynchronize_unlistedScriptList")) {
PopsPanel.deleteValue(
"goto_updateSettingsAndSynchronize_unlistedScriptList"
);
if (!GreasyforkRouter.isUsers()) {
PopsPanel.setValue(
"goto_updateSettingsAndSynchronize_unlistedScriptList",
true
);
if (GreasyforkMenu.getUserLinkElement()) {
Qmsg.success(i18next.t("前往用户主页"));
window.location.href = GreasyforkMenu.getUserLinkElement().href;
} else {
Qmsg.error(i18next.t("获取当前已登录的用户主页失败"));
}
return;
}
let scriptUrlList = [];
document.querySelectorAll(
"#user-unlisted-script-list li a.script-link"
).forEach((item) => {
scriptUrlList = scriptUrlList.concat(
GreasyforkUrlUtils.getAdminUrl(item.href)
);
});
GreasyforkMenu.updateScript(scriptUrlList);
} else if (PopsPanel.getValue("goto_updateSettingsAndSynchronize_libraryScriptList")) {
PopsPanel.deleteValue(
"goto_updateSettingsAndSynchronize_libraryScriptList"
);
if (!GreasyforkRouter.isUsers()) {
PopsPanel.setValue(
"goto_updateSettingsAndSynchronize_libraryScriptList",
true
);
if (GreasyforkMenu.getUserLinkElement()) {
Qmsg.success(i18next.t("前往用户主页"));
window.location.href = GreasyforkMenu.getUserLinkElement().href;
} else {
Qmsg.error(i18next.t("获取当前已登录的用户主页失败"));
}
return;
}
let scriptUrlList = [];
document.querySelectorAll(
"#user-library-script-list li a.script-link"
).forEach((item) => {
scriptUrlList = scriptUrlList.concat(
GreasyforkUrlUtils.getAdminUrl(item.href)
);
});
GreasyforkMenu.updateScript(scriptUrlList);
}
}
};
const UISelect = function(text, key, defaultValue, data, callback, description) {
let selectData = [];
if (typeof data === "function") {
selectData = data();
} else {
selectData = data;
}
let result = {
text,
type: "select",
description,
attributes: {},
props: {},
getValue() {
return this.props[PROPS_STORAGE_API].get(key, defaultValue);
},
callback(event, isSelectedValue, isSelectedText) {
let value = isSelectedValue;
log.info(`选择:${isSelectedText}`);
this.props[PROPS_STORAGE_API].set(key, value);
if (typeof callback === "function") {
callback(event, value, isSelectedText);
}
},
data: selectData
};
Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
Reflect.set(result.props, PROPS_STORAGE_API, {
get(key2, defaultValue2) {
return PopsPanel.getValue(key2, defaultValue2);
},
set(key2, value) {
PopsPanel.setValue(key2, value);
}
});
return result;
};
const UIButtonShortCut = function(text, description, key, defaultValue, defaultButtonText, buttonType = "default", shortCut) {
let __defaultButtonText = typeof defaultButtonText === "function" ? defaultButtonText() : defaultButtonText;
if (typeof defaultValue === "object") {
shortCut.initConfig(key, defaultValue);
}
let getButtonText = () => {
return shortCut.getShowText(key, __defaultButtonText);
};
let result = UIButton(
text,
description,
getButtonText,
"keyboard",
false,
false,
buttonType,
async (event) => {
var _a2;
let $click = event.target;
let $btn = (_a2 = $click.closest(".pops-panel-button")) == null ? void 0 : _a2.querySelector("span");
if (shortCut.isWaitPress) {
Qmsg.warning("请先执行当前的录入操作");
return;
}
if (shortCut.hasOptionValue(key)) {
shortCut.emptyOption(key);
Qmsg.success("清空快捷键");
} else {
let loadingQmsg = Qmsg.loading("请按下快捷键...", {
showClose: true
});
let {
status,
option,
key: isUsedKey
} = await shortCut.enterShortcutKeys(key);
loadingQmsg.close();
if (status) {
log.success(["成功录入快捷键", option]);
Qmsg.success("成功录入");
} else {
Qmsg.error(
`快捷键 ${shortCut.translateKeyboardValueToButtonText(
option
)} 已被 ${isUsedKey} 占用`
);
}
}
$btn.innerHTML = getButtonText();
}
);
result.attributes = {};
Reflect.set(result.attributes, ATTRIBUTE_INIT, () => {
return false;
});
return result;
};
class ShortCut {
constructor(key) {
/** 存储的键 */
__publicField(this, "key", "short-cut");
/** 是否存在等待按下的按键 */
__publicField(this, "isWaitPress", false);
if (typeof key === "string") {
this.key = key;
}
}
/**
* 初始化配置默认值
*/
initConfig(key, option) {
if (this.hasOption(key)) ;
else {
this.setOption(key, option);
}
}
/** 获取存储的键 */
getStorageKey() {
return this.key;
}
/**
* 获取本地存储的所有值
*/
getLocalAllOptions() {
return _GM_getValue(this.key, []);
}
/**
* 判断是否存在该配置
* @param key 键
*/
hasOption(key) {
let localOptions = this.getLocalAllOptions();
let findOption = localOptions.find((item) => item.key === key);
return !!findOption;
}
/**
* 判断是否存在该配置的value值
* @param key 键
*/
hasOptionValue(key) {
if (this.hasOption(key)) {
let option = this.getOption(key);
return !((option == null ? void 0 : option.value) == null);
} else {
return false;
}
}
/**
* 获取配置
* @param key 键
* @param defaultValue 默认值
*/
getOption(key, defaultValue) {
let localOptions = this.getLocalAllOptions();
let findOption = localOptions.find((item) => item.key === key);
return findOption ?? defaultValue;
}
/**
* 设置配置
* @param key 键
* @param value 配置
*/
setOption(key, value) {
let localOptions = this.getLocalAllOptions();
let findIndex = localOptions.findIndex((item) => item.key === key);
if (findIndex == -1) {
localOptions.push({
key,
value
});
} else {
Reflect.set(localOptions[findIndex], "value", value);
}
_GM_setValue(this.key, localOptions);
}
/**
* 清空当前已有配置录入的值
* @param key
*/
emptyOption(key) {
let result = false;
let localOptions = this.getLocalAllOptions();
let findIndex = localOptions.findIndex((item) => item.key === key);
if (findIndex !== -1) {
localOptions[findIndex].value = null;
result = true;
}
_GM_setValue(this.key, localOptions);
return result;
}
/**
* 删除配置
* @param key 键
*/
deleteOption(key) {
let result = false;
let localValue = this.getLocalAllOptions();
let findValueIndex = localValue.findIndex((item) => item.key === key);
if (findValueIndex !== -1) {
localValue.splice(findValueIndex, 1);
result = true;
}
_GM_setValue(this.key, localValue);
return result;
}
/**
* 把配置的快捷键转成文字
* @param keyboardValue
* @returns
*/
translateKeyboardValueToButtonText(keyboardValue) {
let result = "";
keyboardValue.ohterCodeList.forEach((ohterCodeKey) => {
result += utils.stringTitleToUpperCase(ohterCodeKey, true) + " + ";
});
result += utils.stringTitleToUpperCase(keyboardValue.keyName);
return result;
}
/**
* 获取快捷键显示的文字
* @param key 本地存储的快捷键键名
* @param defaultShowText 默认显示的文字
*/
getShowText(key, defaultShowText) {
if (this.hasOption(key)) {
let localOption = this.getOption(key);
if (localOption.value == null) {
return defaultShowText;
} else {
return this.translateKeyboardValueToButtonText(localOption.value);
}
} else {
return defaultShowText;
}
}
/**
* 录入快捷键
* @param key 本地存储的快捷键键名
*/
async enterShortcutKeys(key) {
return new Promise((resolve) => {
this.isWaitPress = true;
let keyboardListener = domUtils.listenKeyboard(
window,
"keyup",
(keyName, keyValue, ohterCodeList) => {
const currentOption = {
keyName,
keyValue,
ohterCodeList
};
const shortcutJSONString = JSON.stringify(currentOption);
const allOptions = this.getLocalAllOptions();
for (let index = 0; index < allOptions.length; index++) {
let localValue = allOptions[index];
if (localValue.key === key) {
continue;
}
const localShortCutJSONString = JSON.stringify(localValue.value);
let isUsedByOtherOption = false;
if (localValue.value != null && shortcutJSONString === localShortCutJSONString) {
isUsedByOtherOption = true;
}
if (isUsedByOtherOption) {
this.isWaitPress = false;
keyboardListener.removeListen();
resolve({
status: false,
key: localValue.key,
option: currentOption
});
return;
}
}
this.setOption(key, currentOption);
this.isWaitPress = false;
keyboardListener.removeListen();
resolve({
status: true,
key,
option: currentOption
});
}
);
});
}
/**
* 初始化全局键盘监听
* @param shortCutOption 快捷键配置 一般是{ "键名": { callback: ()=>{}}},键名是本地存储的自定义快捷键的键名
*/
initGlobalKeyboardListener(shortCutOption) {
let localOptions = this.getLocalAllOptions();
if (!localOptions.length) {
log.warn("没有设置快捷键");
return;
}
let that = this;
function setListenKeyboard($ele, option) {
domUtils.listenKeyboard(
$ele,
"keydown",
(keyName, keyValue, ohterCodeList) => {
if (that.isWaitPress) {
return;
}
localOptions = that.getLocalAllOptions();
let findShortcutIndex = localOptions.findIndex((item) => {
let option2 = item.value;
let tempOption = {
keyName,
keyValue,
ohterCodeList
};
if (JSON.stringify(option2) === JSON.stringify(tempOption)) {
return item;
}
});
if (findShortcutIndex != -1) {
let findShortcut = localOptions[findShortcutIndex];
log.info(["调用快捷键", findShortcut]);
if (findShortcut.key in option) {
option[findShortcut.key].callback();
}
}
}
);
}
let WindowShortCutOption = {};
let ElementShortCutOption = {};
Object.keys(shortCutOption).forEach((localKey) => {
let option = shortCutOption[localKey];
if (option.target == null || typeof option.target === "string" && option.target === "") {
option.target = "window";
}
if (option.target === "window") {
Reflect.set(WindowShortCutOption, localKey, option);
} else {
Reflect.set(ElementShortCutOption, localKey, option);
}
});
setListenKeyboard(window, WindowShortCutOption);
domUtils.ready(() => {
Object.keys(ElementShortCutOption).forEach(async (localKey) => {
let option = ElementShortCutOption[localKey];
if (typeof option.target === "string") {
utils.waitNode(option.target, 1e4).then(($ele) => {
if (!$ele) {
return;
}
let __option = {};
Reflect.set(__option, localKey, option);
setListenKeyboard($ele, __option);
});
} else if (typeof option.target === "function") {
let target = await option.target();
if (target == null) {
return;
}
let __option = {};
Reflect.set(__option, localKey, option);
setListenKeyboard(target, __option);
} else {
let __option = {};
Reflect.set(__option, localKey, option);
setListenKeyboard(option.target, __option);
}
});
});
}
}
const GreasyforkShortCut = {
shortCut: new ShortCut(),
shortOption: {
"gf-quickReply": {
target: () => {
let $commentText = document.querySelector("form textarea");
let $replyBtn = document.querySelector(
'input[name="commit"][type="submit"]'
);
if (!$commentText) {
log.error("页面不存在输入框");
return;
} else if (!$replyBtn) {
log.error("页面不存在【发表回复】按钮");
return;
}
log.success("监听快捷键:gf-quickReply");
return $commentText;
},
callback() {
if (document.activeElement) {
let $parentForm = document.activeElement.closest("form");
if (!$parentForm) {
log.error("当前activeElement不在表单内,无法触发快捷键");
return;
}
let $replyBtnList = $parentForm.querySelectorAll(
'input[name="commit"][type="submit"]'
);
if (!$replyBtnList.length) {
log.error("表单内不存在【发表回复】按钮");
return;
}
if ($replyBtnList.length > 1) {
log.warn("表单内存在多个【发表回复】按钮,只触发第一个");
}
$replyBtnList[0].click();
} else {
log.error("当前页面没有激活元素,无法触发快捷键");
}
}
}
},
init() {
this.shortCut.initGlobalKeyboardListener(this.shortOption);
}
};
const GreasyforkRememberFormTextArea = {
$key: {
DB_KEY: "data"
},
$data: {
db: null
},
init() {
this.$data.db = this.getDB();
PopsPanel.execMenuOnce("rememberReplyContent", () => {
this.rememberReplyContent();
});
PopsPanel.execMenu("gf-autoClearRememberReplayContent", (value) => {
this.autoClearRememberReplayContent(value);
});
},
/**
* 获取数据库连接对象
*/
getDB() {
const dbName = "reply_record";
const storeName = "textarea_text";
const dbVersion = 2;
return new utils.indexedDB(dbName, storeName, dbVersion);
},
/**
* 记住回复内容
*/
async rememberReplyContent() {
const TAG = "记住回复内容 -- ";
let $formList = document.querySelectorAll("form");
if (!$formList.length) {
log.warn(TAG + "不存在表单");
return;
}
try {
await this.clearRelayHistoryRememberContentText();
} catch (error) {
log.error(error);
}
$formList.forEach(async ($form) => {
let $textarea = $form.querySelector("textarea");
let $replySubmit = $form.querySelector(`input[type="submit"]`);
if (!$textarea) {
return;
}
if (!$replySubmit) {
return;
}
log.success([`开始监听form --- 记住回复内容`, $form]);
this.$data.db.get(this.$key.DB_KEY).then((result) => {
if (!result.success) {
return;
}
let localDataIndex = result.data.findIndex((item) => {
return this.checkUrlIsSame(window.location.href, item.url);
});
if (localDataIndex == -1) {
return;
}
let historyInputText = result.data[localDataIndex].text;
log.success("填入历史输入内容:" + historyInputText);
$textarea.value = historyInputText;
});
domUtils.on(
$textarea,
["propertychange", "input"],
utils.debounce((event) => {
let data = {
url: window.location.href,
text: $textarea.value,
time: Date.now()
};
this.$data.db.get(this.$key.DB_KEY).then((result) => {
if (!result.success && result.event && result.event.type !== "success") {
log.warn(result);
return;
}
if (result.data == null) {
result.data = [];
}
let localDataIndex = result.data.findIndex((item) => {
return this.checkUrlIsSame(window.location.href, item.url);
});
if (localDataIndex !== -1) {
if (utils.isNull(data.text)) {
result.data.splice(localDataIndex, 1);
} else {
result.data[localDataIndex] = utils.assign(
result.data[localDataIndex],
data
);
}
} else {
result.data = result.data.concat(data);
}
this.$data.db.save(this.$key.DB_KEY, result.data).then((result2) => {
if (result2.success) ;
else {
log.error(["保存失败", result2]);
}
});
});
}, 25)
);
domUtils.on(
$form,
"submit",
(event) => {
log.info(`表单提交,刷新页面后清理内容:` + window.location.href);
_GM_setValue(
"delyClear_rememberReplyContent_url",
window.location.href
);
},
{
capture: true
}
);
});
},
/**
* 清理历史记住的回复内容
*/
async clearRelayHistoryRememberContentText() {
const KEY2 = "delyClear_rememberReplyContent_url";
let delyClear_rememberReplyContent_url = _GM_getValue(KEY2);
if (delyClear_rememberReplyContent_url) {
let result = await this.$data.db.get(
this.$key.DB_KEY
);
if (!result.success) {
log.info("表单记录:数据库是空的");
return;
}
let localDataIndex = result.data.findIndex((item) => {
return this.checkUrlIsSame(
delyClear_rememberReplyContent_url,
item.url
);
});
if (localDataIndex == -1) {
log.info("表单记录:已不存在该数据");
_GM_deleteValue(KEY2);
return;
}
result.data.splice(localDataIndex, 1);
let saveResult = await this.$data.db.save(this.$key.DB_KEY, result.data);
if (saveResult.success) {
log.success("表单记录:成功清除");
_GM_deleteValue(KEY2);
} else {
log.error(["表单记录:清除失败", result]);
}
}
},
/**
* 检测两个url是否相同(不包括hash值)
* @param url1
* @param url2
*/
checkUrlIsSame(url1, url2) {
let url1Obj = new URL(url1);
let url2Obj = new URL(url2);
return url1Obj.origin === url2Obj.origin && url1Obj.pathname === url2Obj.pathname;
},
/**
* 自动清理空间
* @param intervalDay 间隔天数
*/
autoClearRememberReplayContent(intervalDay) {
const KEY2 = "gf-last-time-autoClearRememberReplayContent";
let lastClearTime = _GM_getValue(KEY2);
let intervalTime = intervalDay * 24 * 60 * 60 * 1e3;
if (lastClearTime) {
if (Date.now() - lastClearTime > intervalTime) {
_GM_setValue(KEY2, Date.now());
} else {
return;
}
}
_GM_setValue(KEY2, Date.now());
},
/**
* 获取所有的记住的回复内容
*/
async getAllRememberReplyContent() {
let result = await this.$data.db.get(
this.$key.DB_KEY
);
return result.data ?? [];
},
/**
* 清空所有的记住的回复内容
*/
async clearAllRememberReplyContent() {
let result = await this.$data.db.delete(this.$key.DB_KEY);
return result.success;
}
};
const PopsPanelUISetting = {
/**
* 面板-脚本列表|库
* @param type
* @param event
* @param rightHeaderElement
* @param rightContainerElement
* @returns
*/
async UIScriptList(type, rightContainerElement) {
var _a2, _b, _c;
if (!GreasyforkMenu.isLogin) {
Qmsg.error(i18next.t("请先登录账号!"));
return;
}
let userLinkElement = GreasyforkMenu.getUserLinkElement();
let userLink = userLinkElement.href;
let userId = (_c = (_b = (_a2 = userLink == null ? void 0 : userLink.split("/")) == null ? void 0 : _a2.pop()) == null ? void 0 : _b.match(/([0-9]+)/)) == null ? void 0 : _c[0];
let loading = __pops.loading({
mask: {
enable: true
},
parent: rightContainerElement,
content: {
text: i18next.t("获取信息中,请稍后...")
},
addIndexCSS: false
});
let userInfo = await GreasyforkApi.getUserInfo(userId);
loading.close();
if (!userInfo) {
return;
}
log.info(userInfo);
let scriptList = type === "script-list" ? userInfo["scriptList"] : userInfo["scriptLibraryList"];
Qmsg.success(
i18next.t("获取成功,共 {{count}} 个", {
count: scriptList.length
})
);
for (const scriptInfo of scriptList) {
let liElement = domUtils.createElement("li", {
className: "w-script-list-item",
innerHTML: (
/*html*/
`
<div class="w-script-info">
<div class="w-script-name">
<a href="${scriptInfo["url"]}" target="_blank">${scriptInfo["name"]}</a>
</div>
<div class="w-script-fan-score">
<p>${i18next.t("评分:")}${scriptInfo["fan_score"]}</p>
</div>
<div class="w-script-locale">
<p>${i18next.t("语言:")}${scriptInfo["locale"]}</p>
</div>
<div class="w-script-version">
<p>${i18next.t("版本:")}${scriptInfo["version"]}</p>
</div>
<div class="w-script-update-time">
<p>${i18next.t("更新:")}${utils.getDaysDifference(
new Date(scriptInfo["code_updated_at"]).getTime(),
void 0,
"auto"
)}前</p>
</div>
</div>
`
)
});
let scriptInfoElement = liElement.querySelector(
".w-script-info"
);
let buttonElement = domUtils.createElement("div", {
className: "pops-panel-button",
innerHTML: (
/*html*/
`
<button type="primary" data-icon="" data-righticon="false">
<span>${i18next.t("同步代码")}</span>
</button>
`
)
});
if (scriptInfo["deleted"]) {
liElement.classList.add("w-script-deleted");
buttonElement.querySelector("button").setAttribute("disabled", "true");
}
domUtils.on(buttonElement, "click", void 0, async function() {
log.success(["同步", scriptInfo]);
let btn = buttonElement.querySelector("button");
let span = buttonElement.querySelector(
"button span"
);
let iconElement = domUtils.createElement(
"i",
{
className: "pops-bottom-icon",
innerHTML: __pops.config.iconSVG.loading
},
{
"is-loading": true
}
);
btn.setAttribute("disabled", "true");
btn.setAttribute("data-icon", "true");
span.innerText = i18next.t("同步中...");
domUtils.before(span, iconElement);
let scriptId = scriptInfo == null ? void 0 : scriptInfo["id"];
let codeSyncFormData = await GreasyforkApi.getSourceCodeSyncFormData(
scriptId.toString()
);
if (codeSyncFormData) {
const SCRIPT_SYNC_TYPE_ID_FORMDATA_KEY = "script[script_sync_type_id]";
if (codeSyncFormData.has(SCRIPT_SYNC_TYPE_ID_FORMDATA_KEY)) {
let syncTypeId = codeSyncFormData.get(
SCRIPT_SYNC_TYPE_ID_FORMDATA_KEY
);
let syncMode = "";
if (syncTypeId.toString() === "1") {
syncMode = i18next.t("手动");
} else if (syncTypeId.toString() === "2") {
syncMode = i18next.t("自动");
} else if (syncTypeId.toString() === "3") {
syncMode = "webhook";
}
let oldSyncTypeElement = liElement.querySelector(
".w-script-sync-type"
);
if (oldSyncTypeElement) {
oldSyncTypeElement.querySelector("p").innerText = i18next.t(
"同步方式:{{syncMode}}",
{ syncMode }
);
} else {
domUtils.append(
scriptInfoElement,
/*html*/
`
<div class="w-script-sync-type">
<p>${i18next.t("同步方式:{{syncMode}}", {
syncMode
})}
</p>
</div>`
);
}
let syncUpdateResponse = await GreasyforkApi.sourceCodeSync(
scriptInfo["id"].toString(),
codeSyncFormData
);
if (syncUpdateResponse) {
Qmsg.success(i18next.t("同步成功"));
} else {
Qmsg.error(i18next.t("同步失败"));
}
} else {
Qmsg.error(i18next.t("该脚本未设置同步信息"));
}
}
btn.removeAttribute("disabled");
btn.removeAttribute("data-icon");
span.innerText = i18next.t("同步代码");
iconElement.remove();
});
liElement.appendChild(buttonElement);
rightContainerElement.appendChild(liElement);
}
}
};
const GreasyforkScriptsCode = {
init() {
PopsPanel.execMenuOnce("code-repairCodeLineNumber", () => {
this.repairCodeLineNumber();
});
},
/**
* 修复代码的行号显示不够问题
* 超过1w行不会高亮代码
*/
repairCodeLineNumber() {
log.info("修复代码的行号显示不够问题");
PopsPanel.execMenuOnce("beautifyGreasyforkBeautify", () => {
_GM_addStyle(
/*css*/
`
.code-container pre code .marker{
padding-left: 6px;
}
`
);
});
utils.waitNode(
"#script-content div.code-container pre.prettyprint ol"
).then(($prettyPrintOL) => {
if ($prettyPrintOL.childElementCount >= 1e3) {
log.success(
`当前代码行数${$prettyPrintOL.childElementCount}行,超过1000行,优化行号显示问题`
);
_GM_addStyle(
/*css*/
`
pre.prettyprint{
padding-left: 26px;
}
`
);
}
});
}
};
const beautifyVersionsPageCSS = 'ul.history_versions,\r\nul.history_versions li {\r\n width: 100%;\r\n}\r\nul.history_versions li {\r\n display: flex;\r\n flex-direction: column;\r\n margin: 25px 0px;\r\n}\r\n.diff-controls input[type="radio"]:nth-child(2) {\r\n margin-left: 5px;\r\n}\r\n.flex-align-item-center {\r\n display: flex;\r\n align-items: center;\r\n}\r\n.script-tag {\r\n margin-bottom: 8px;\r\n}\r\n.script-tag-version a {\r\n color: #656d76;\r\n fill: #656d76;\r\n text-decoration: none;\r\n width: fit-content;\r\n width: -moz-fit-content;\r\n}\r\n.script-tag-version a:hover svg {\r\n color: #00a3f5;\r\n fill: #00a3f5;\r\n}\r\n.script-tag-version a > span {\r\n margin-left: 0.25rem;\r\n}\r\n.script-note-box-body {\r\n border-radius: 0.375rem;\r\n border-style: solid;\r\n border-width: max(1px, 0.0625rem);\r\n border-color: #d0d7de;\r\n color: #1f2328;\r\n padding: 16px;\r\n overflow-wrap: anywhere;\r\n}\r\n.script-note-box-body p {\r\n margin-bottom: unset;\r\n}\r\n';
const GreasyforkVersions = {
init() {
PopsPanel.execMenuOnce("beautifyHistoryVersionPage", () => {
return this.beautifyHistoryVersionPage();
});
PopsPanel.execMenuOnce("scripts-versions-addExtraTagButton", () => {
this.addExtraTagButton();
});
},
/**
* 美化 历史版本 页面
*/
beautifyHistoryVersionPage() {
log.info("美化 历史版本 页面");
let result = [];
result.push(_GM_addStyle(beautifyVersionsPageCSS));
result.push(
CommonUtil.addBlockCSS(
".version-number",
".version-date",
".version-changelog"
)
);
domUtils.ready(function() {
let $historyVersion = document.querySelector(
"ul.history_versions"
);
if (!$historyVersion) {
Qmsg.error(i18next.t("未找到history_versions元素列表"));
return;
}
Array.from($historyVersion.children).forEach((liElement) => {
var _a2, _b;
let versionUrl = liElement.querySelector(".version-number a").href;
let versionNumber = liElement.querySelector(
".version-number a"
).innerText;
let versionDate = (_a2 = liElement.querySelector(".version-date")) == null ? void 0 : _a2.getAttribute("datetime");
let updateNote = ((_b = liElement.querySelector(".version-changelog")) == null ? void 0 : _b.innerHTML) || "";
let versionDateElement = domUtils.createElement("span", {
className: "script-version-date",
innerHTML: utils.formatTime(
versionDate,
i18next.t("yyyy年MM月dd日 HH:mm:ss")
)
});
let tagElement = domUtils.createElement("div", {
className: "script-tag",
innerHTML: (
/*html*/
`
<div class="script-tag-version">
<a href="${versionUrl}" class="flex-align-item-center">
<svg aria-label="Tag" role="img" height="16" viewBox="0 0 16 16" version="1.1" width="16">
<path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path>
</svg>
<span>${versionNumber}</span>
</a>
</div>`
)
});
let boxBodyElement = domUtils.createElement("div", {
className: "script-note-box-body",
innerHTML: updateNote
});
liElement.appendChild(versionDateElement);
liElement.appendChild(tagElement);
liElement.appendChild(boxBodyElement);
});
});
return result;
},
/**
* 添加额外的标签按钮
*/
addExtraTagButton() {
log.info("添加额外的标签按钮");
domUtils.ready(() => {
document.querySelectorAll(".script-tag-version").forEach(($tagVersion) => {
var _a2, _b;
let $anchor = $tagVersion.querySelector("a");
if (!$anchor) {
return;
}
let urlObj2 = new URL($anchor.href);
let scriptId = (_a2 = urlObj2.pathname.match(/\/scripts\/([\d]+)/)) == null ? void 0 : _a2[1];
let scriptVersion = urlObj2.searchParams.get("version");
let scriptName = (_b = urlObj2.pathname.match(/\/scripts\/[\d]+-(.+)/)) == null ? void 0 : _b[1];
let installUrl = GreasyforkUrlUtils.getInstallUrl(
scriptId,
scriptVersion,
scriptName
);
let codeUrl = GreasyforkUrlUtils.getCodeUrl(scriptId, scriptVersion);
let $buttonTag = domUtils.createElement("div", {
className: "scripts-tag-install",
innerHTML: (
/*html*/
`
<a class="script-btn-install install-link" data-install-format="js" target="_blank" href="${installUrl}">${i18next.t(
"安装此脚本"
)}</a>
<a class="script-btn-see-code" target="_blank" href="${codeUrl}">${i18next.t(
"查看代码"
)}</a>
`
)
});
domUtils.after($tagVersion, $buttonTag);
});
});
}
};
const PanelUISize = {
/**
* 一般设置界面的尺寸
*/
setting: {
get width() {
return window.innerWidth < 550 ? "88vw" : "550px";
},
get height() {
return window.innerHeight < 450 ? "70vh" : "450px";
}
},
/**
* 功能丰富,aside铺满了的设置界面,要稍微大一点
*/
settingBig: {
get width() {
return window.innerWidth < 800 ? "92vw" : "800px";
},
get height() {
return window.innerHeight < 600 ? "80vh" : "600px";
}
},
/**
* 信息界面,一般用于提示信息之类
*/
info: {
get width() {
return window.innerWidth < 350 ? "350px" : "350px";
},
get height() {
return window.innerHeight < 250 ? "250px" : "250px";
}
}
};
let userCollection = [];
const GreasyforkScriptsCollectEvent = async function(scriptId) {
log.info("当前脚本id:" + scriptId);
if (!GreasyforkMenu.isLogin) {
log.error("请先登录账号");
Qmsg.error(i18next.t("请先登录账号"));
return;
}
let userId = GreasyforkUrlUtils.getUserId(
GreasyforkMenu.getUserLinkElement().href
);
if (userId == null) {
log.error("获取用户id失败");
Qmsg.error(i18next.t("获取用户id失败"));
return;
}
if (!userCollection.length) {
let loading = Qmsg.loading(i18next.t("获取收藏夹中..."));
userCollection = await GreasyforkApi.getUserCollection(userId) || [];
loading.close();
if (!userCollection.length) {
return;
}
}
let alertHTML = "";
userCollection.forEach((userCollectInfo) => {
alertHTML += /*html*/
`
<li class="user-collect-item" data-id="${userCollectInfo.id}" data-name="${userCollectInfo.name}">
<div class="user-collect-name">${userCollectInfo.name}</div>
<div class="user-collect-btn-container">
<div class="pops-panel-button collect-add-script-id">
<button type="primary" data-icon="" data-righticon="">
<span>${i18next.t("添加")}</span>
</button>
</div>
<div class="pops-panel-button collect-delete-script-id">
<button type="danger" data-icon="" data-righticon="">
<span>${i18next.t("刪除")}</span>
</button>
</div>
</div>
</li>
`;
});
let collectionDialog = __pops.alert({
title: {
text: i18next.t("收藏集"),
position: "center"
},
content: {
html: true,
text: (
/*html*/
`<ul>${alertHTML}</ul>`
)
},
mask: {
enable: true,
clickEvent: {
toClose: true
}
},
btn: {
ok: {
enable: false
}
},
width: __pops.isPhone() ? "92vw" : "500px",
height: "auto",
drag: true,
only: true,
style: (
/*css*/
`
.pops{
--content-max-height: 400px;
max-height: var(--content-max-height);
}
.pops[type-value=alert] .pops-alert-content {
max-height: calc(var(--content-max-height) - var(--container-title-height) - var(--container-bottom-btn-height));
}
.user-collect-item{
-webkit-user-select: none;
user-select: none;
padding: 5px 10px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px dotted #c9c9c9;
}
.user-collect-name{
}
.user-collect-item:hover{
}
.user-collect-btn-container{
margin-left: 10px;
display: flex;
}
`
)
});
domUtils.on(
collectionDialog.$shadowRoot,
"click",
".collect-add-script-id",
async function(event) {
let $userCollectItem = event.target.closest(
".user-collect-item"
);
let setsId = $userCollectItem.dataset.id;
$userCollectItem.dataset.name;
let loading = Qmsg.loading(i18next.t("添加中..."));
let formData = await GreasyforkApi.getUserCollectionInfo(userId, setsId);
if (!formData) {
loading.close();
return;
}
let editForm = utils.cloneFormData(formData);
let saveEditForm = utils.cloneFormData(formData);
let isCollect = false;
for (const [key, value] of formData.entries()) {
if (key === "scripts-included[]" && String(value).trim() === String(scriptId).trim()) {
isCollect = true;
break;
} else {
saveEditForm.append(key, value);
editForm.append(key, value);
}
}
if (isCollect) {
Qmsg.warning(i18next.t("该脚本已经在该收藏集中"));
loading.close();
return;
}
editForm.set("add-script", scriptId.toString());
editForm.set("script-action", "i");
saveEditForm.append("scripts-included[]", scriptId.toString());
saveEditForm.set("save", "1");
let addFormDataSearchParams = new URLSearchParams(editForm);
let saveFormDataSearchParams = new URLSearchParams(saveEditForm);
let addData = Array.from(addFormDataSearchParams).map(
// @ts-ignore
([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
).join("&");
let saveData = Array.from(saveFormDataSearchParams).map(
// @ts-ignore
([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
).join("&");
log.info(["添加的数据", addData]);
log.info(["保存的数据", saveData]);
let addResult = await GreasyforkApi.updateUserSetsInfo(
userId,
setsId,
addData
);
if (!addResult) {
loading.close();
return;
}
let changeScriptSet = addResult.querySelector(".change-script-set");
if (!changeScriptSet) {
Qmsg.error(
i18next.t("添加失败,{{selector}}元素不存在", {
selector: ".change-script-set"
})
);
loading.close();
return;
}
let section = changeScriptSet.querySelector("section");
if (!section) {
Qmsg.error(
i18next.t("添加失败,{{selector}}元素不存在", {
selector: "section"
})
);
loading.close();
return;
}
let alertElement = section.querySelector(".alert");
if (alertElement) {
__pops.alert({
title: {
text: i18next.t("添加失败"),
position: "center"
},
content: {
text: alertElement.innerHTML,
html: true
},
mask: {
enable: true,
clickEvent: {
toClose: true
}
},
style: (
/*css*/
`
.pops-alert-content{
font-style: italic;
background-color: #ffc;
border: none;
border-left: 6px solid #FFEB3B;
padding: .5em;
}
`
),
drag: true,
dragLimit: true,
width: PanelUISize.info.width,
height: PanelUISize.info.height
});
} else {
await GreasyforkApi.updateUserSetsInfo(userId, setsId, saveData);
Qmsg.success(i18next.t("添加成功"));
}
loading.close();
}
);
domUtils.on(
collectionDialog.$shadowRoot,
"click",
".collect-delete-script-id",
async function(event) {
let $collectItem = event.target.closest(
".user-collect-item"
);
let setsId = $collectItem.dataset.id;
$collectItem.dataset.name;
let loading = Qmsg.loading(i18next.t("删除中..."));
let formData = await GreasyforkApi.getUserCollectionInfo(userId, setsId);
if (!formData) {
loading.close();
return;
}
let editForm = new FormData();
let saveEditForm = new FormData();
let isCollect = false;
for (const [key, value] of formData.entries()) {
if (String(key).trim() === "scripts-included[]" && String(value).trim() === String(scriptId).trim()) {
isCollect = true;
continue;
}
saveEditForm.append(key, value);
editForm.append(key, value);
}
if (!isCollect) {
Qmsg.warning(
i18next.t("该收藏集未包含:{{scriptId}}", {
scriptId
})
);
loading.close();
return;
}
editForm.set("remove-scripts-included[]", scriptId.toString());
editForm.set("remove-selected-scripts", "i");
editForm.delete("script-action");
saveEditForm.set("save", "1");
let deleteFormDataSearchParams = new URLSearchParams(editForm);
let saveFormDataSearchParams = new URLSearchParams(saveEditForm);
let removeData = Array.from(deleteFormDataSearchParams).map(
// @ts-ignore
([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
).join("&");
let saveData = Array.from(saveFormDataSearchParams).map(
// @ts-ignore
([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
).join("&");
log.info(["删除的数据", removeData]);
log.info(["保存的数据", saveData]);
let removeResult = await GreasyforkApi.updateUserSetsInfo(
userId,
setsId,
removeData
);
if (!removeResult) {
loading.close();
return;
}
await GreasyforkApi.updateUserSetsInfo(userId, setsId, saveData);
Qmsg.success(i18next.t("删除成功"));
loading.close();
}
);
};
const GreasyforkScripts = {
init() {
if (GreasyforkRouter.isCode()) {
GreasyforkScriptsCode.init();
} else if (GreasyforkRouter.isVersion()) {
GreasyforkVersions.init();
}
if (GreasyforkRouter.isCodeStrict()) {
PopsPanel.execMenuOnce("fullScreenOptimization", () => {
this.fullScreenOptimization();
});
PopsPanel.execMenuOnce("addCopyCodeButton", () => {
this.addCopyCodeButton();
});
}
PopsPanel.execMenuOnce("addCollectionButton", () => {
this.addCollectionButton();
});
PopsPanel.execMenuOnce("addFindReferenceButton", () => {
this.setFindCodeSearchBtn();
});
PopsPanel.execMenuOnce("scriptHomepageAddedTodaySUpdate", () => {
this.scriptHomepageAddedTodaySUpdate();
});
},
/**
* 添加【收藏】按钮
*/
addCollectionButton() {
log.info("添加收藏按钮");
utils.waitNode("ul#script-links li.current span").then(() => {
let $collectBtn = domUtils.createElement("li", {
innerHTML: `
<a href="javascript:;">
<span>${i18next.t("收藏")}</span>
</a>`
});
domUtils.append(
document.querySelector("ul#script-links"),
$collectBtn
);
domUtils.on($collectBtn, "click", () => {
let scriptIdMatch = window.location.pathname.match(/scripts\/([\d]+)/i);
if (!scriptIdMatch) {
log.error([scriptIdMatch, window.location.pathname]);
Qmsg.error(i18next.t("获取脚本id失败"));
return;
}
let scriptId = scriptIdMatch[scriptIdMatch.length - 1];
GreasyforkScriptsCollectEvent(scriptId);
});
});
},
/**
* F11全屏,F键代码全屏
*/
fullScreenOptimization() {
log.info("F11全屏,F键代码全屏");
_GM_addStyle(
/*css*/
`
.code-wide-screen{
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0;
padding: 0;
width: 100%;
height: 100%;
min-width: 100%;
min-height: 100%;
max-width: 100%;
max-height: 100%;
z-index: 10000;
}
`
);
let isFullScreen = false;
domUtils.keydown(
_unsafeWindow,
function(event) {
if (event.key.toLowerCase() === "f") {
let codeElement = document.querySelector(
"#script-content div.code-container code"
);
if (event.altKey && event.shiftKey) {
utils.preventEvent(event);
if (codeElement.classList.contains("code-wide-screen")) {
codeElement.classList.remove("code-wide-screen");
} else {
codeElement.classList.add("code-wide-screen");
}
} else if (!event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {
utils.preventEvent(event);
if (isFullScreen) {
utils.exitFullScreen(codeElement);
isFullScreen = false;
} else {
utils.enterFullScreen(codeElement);
isFullScreen = true;
}
}
}
},
{
capture: true
}
);
},
/**
* 设置代码搜索按钮(对于库)
*/
setFindCodeSearchBtn() {
log.info("设置代码搜索按钮(对于库)");
utils.waitNode("ul#script-links li.current span").then(() => {
let searchBtn = domUtils.createElement("li", {
innerHTML: `
<a href="javascript:;">
<span>${i18next.t("寻找引用")}</span>
</a>`
});
domUtils.append(
document.querySelector(
"ul#script-links"
),
searchBtn
);
domUtils.on(searchBtn, "click", async function() {
let scriptIdMatch = window.location.pathname.match(/scripts\/([\d]+)/i);
if (!scriptIdMatch) {
log.error([scriptIdMatch, window.location.pathname]);
Qmsg.error(i18next.t("获取脚本id失败"));
return;
}
let scriptId = scriptIdMatch[scriptIdMatch.length - 1];
window.location.href = GreasyforkUrlUtils.getCodeSearchUrl(
`greasyfork.org/scripts/${scriptId}`
);
});
});
},
/**
* 脚本首页新增【今日检查】
*/
async scriptHomepageAddedTodaySUpdate() {
if (!document.querySelector("#install-area")) {
return;
}
log.info("脚本首页新增【今日检查】");
let scriptStatsJSON = await GreasyforkApi.getScriptStats(
GreasyforkUrlUtils.getScriptId()
);
if (!scriptStatsJSON) {
return;
}
log.info("统计信息", scriptStatsJSON);
let todayStatsJSON = scriptStatsJSON[utils.formatTime(void 0, "yyyy-MM-dd")];
if (!todayStatsJSON) {
log.error("今日份的统计信息不存在");
return;
}
let update_checks = todayStatsJSON["update_checks"];
log.info(["今日统计信息", todayStatsJSON]);
domUtils.after(
"dd.script-show-daily-installs",
domUtils.createElement("dt", {
className: "script-show-daily-update_checks",
innerHTML: `<span>${i18next.t("今日检查")}</span>`
})
);
domUtils.after(
"dt.script-show-daily-update_checks",
domUtils.createElement("dd", {
className: "script-show-daily-update_checks",
innerHTML: "<span>" + update_checks + "</span>"
})
);
},
/**
* 添加复制代码按钮
*/
addCopyCodeButton() {
log.info("添加复制代码按钮");
utils.waitNode("div#script-content div.code-container").then(($codeContainer) => {
let copyButton = domUtils.createElement(
"button",
{
textContent: i18next.t("复制代码")
},
{
style: "margin-bottom: 1em;"
}
);
domUtils.on(copyButton, "click", async function() {
let loading = Qmsg.loading(i18next.t("加载文件中..."));
let getResp = await httpx.get(
`https://greasyfork.org/zh-CN/scripts/${GreasyforkUrlUtils.getScriptId()}.json`,
{
fetch: true,
responseType: "json"
}
);
if (!getResp.status) {
loading.close();
return;
}
let respJSON = utils.toJSON(getResp.data.responseText);
let code_url = respJSON["code_url"];
log.success(["代码地址:", code_url]);
let scriptJS = await httpx.get(code_url);
if (!scriptJS.status) {
loading.close();
return;
}
loading.close();
utils.setClip(scriptJS.data.responseText);
Qmsg.success(i18next.t("复制成功"));
});
domUtils.before($codeContainer, copyButton);
});
}
};
const beautifyCenterContentCSS = '.sidebarred-main-content {\r\n max-width: unset;\r\n flex: unset;\r\n}\r\nol.script-list {\r\n display: flex;\r\n flex-wrap: wrap;\r\n border: none;\r\n gap: 20px;\r\n background: transparent;\r\n box-shadow: none;\r\n}\r\nol.script-list .script-description {\r\n overflow-wrap: anywhere;\r\n}\r\nol.script-list li {\r\n border: 1px solid rgb(221, 221, 221);\r\n border-radius: 5px;\r\n flex: 1 1 45%;\r\n box-shadow: rgb(221, 221, 221) 0px 0px 5px 2px;\r\n}\r\n/* 收藏按钮 */\r\n.script-collect-btn {\r\n color: #ffffff;\r\n border-color: #409eff;\r\n background-color: #409eff;\r\n}\r\n/* 评分按钮 */\r\n.script-list-rating-score[data-position="right"] {\r\n display: inline-block;\r\n min-width: 1em;\r\n text-align: center;\r\n padding: 0 0.25em;\r\n border: 1px solid #dddddd;\r\n border-radius: 10px;\r\n width: fit-content;\r\n}\r\n/* 安装按钮 */\r\n.install-link {\r\n border-radius: 0.25rem 0.25rem 0.25rem 0.25rem;\r\n}\r\n.install-link:has(+ .install-help-link) {\r\n border-radius: 0.25rem 0 0 0.25rem;\r\n}\r\n/* 加载圆圈动画 */\r\n.install-link.lum-lightbox-loader {\r\n position: relative;\r\n min-width: 4rem;\r\n min-height: 1rem;\r\n}\r\n.install-link.lum-lightbox-loader::before {\r\n margin-left: 1rem;\r\n}\r\n.install-link.lum-lightbox-loader::after {\r\n margin-right: 1rem;\r\n}\r\n.install-link.lum-lightbox-loader::before,\r\n.install-link.lum-lightbox-loader::after {\r\n width: 1em;\r\n height: 1em;\r\n margin-top: -0.5em;\r\n border-radius: 1em;\r\n background: hsla(0, 0%, 100%, 0.5);\r\n}\r\n';
const GreasyforkCheckVersion = {
/** 获取 TamperMonkey 暴露在window下的函数 */
getTampermonkey: () => {
var _a2;
return (_a2 = _unsafeWindow.external) == null ? void 0 : _a2.Tampermonkey;
},
/** 获取 Violentmonkey 暴露在window下的函数 */
getViolentmonkey: () => {
var _a2;
return (_a2 = _unsafeWindow.external) == null ? void 0 : _a2.Violentmonkey;
},
/** 获取 ScriptCat 暴露在window下的函数 */
getScriptCat: () => {
var _a2;
return (_a2 = _unsafeWindow.external) == null ? void 0 : _a2.Scriptcat;
},
/**
* 获取脚本容器启用状态
*/
getScriptContainerStatus() {
var _a2, _b, _c;
let containerStatus = {
Tampermonkey: false,
Violentmonkey: false,
ScriptCat: false
};
if ((_a2 = _unsafeWindow.external) == null ? void 0 : _a2.Tampermonkey) {
containerStatus.Tampermonkey = true;
}
if ((_b = _unsafeWindow.external) == null ? void 0 : _b.Violentmonkey) {
containerStatus.Violentmonkey = true;
}
if ((_c = _unsafeWindow.external) == null ? void 0 : _c.Scriptcat) {
containerStatus.ScriptCat = true;
}
return containerStatus;
},
/**
* 获取脚本安装的版本号
* @param name 脚本名
* @param namespace 脚本命名空间
*/
getInstalledVersion(name, namespace) {
return new Promise((resolve, reject) => {
const tm = this.getTampermonkey();
if (tm) {
tm.isInstalled(name, namespace, function(data) {
if (data.installed) {
resolve(data.version);
} else {
resolve(null);
}
});
return;
}
const vm = this.getViolentmonkey();
if (vm) {
vm.isInstalled(name, namespace).then(resolve);
return;
}
const scriptCat = this.getScriptCat();
if (scriptCat) {
scriptCat.isInstalled(name, namespace, function(data) {
if (data.installed) {
resolve(data.version);
} else {
resolve(null);
}
});
return;
}
reject(new TypeError("获取脚本容器暴露的external信息失败"));
});
},
/**
* https://developer.mozilla.org/en/docs/Toolkit_version_format
*
* 比较版本号
* @param a 版本号
* @param b 版本号
* @returns
* + -1 该版本号低
* + 0 该版本号和比较的版本号相同
* + 1 该版本号高
*/
compareVersions(a, b) {
if (a === b) {
return 0;
}
const aParts = a.split(".");
const bParts = b.split(".");
for (let i = 0; i < aParts.length; i++) {
const result = this.compareVersionPart(aParts[i], bParts[i]);
if (result !== 0) {
return result;
}
}
return 0;
},
compareVersionPart(partA, partB) {
const partAParts = this.parseVersionPart(partA);
const partBParts = this.parseVersionPart(partB);
for (let i = 0; i < partAParts.length; i++) {
if (partAParts[i].length > 0 && partBParts[i].length === 0) {
return -1;
}
if (partAParts[i].length === 0 && partBParts[i].length > 0) {
return 1;
}
if (partAParts[i] > partBParts[i]) {
return 1;
}
if (partAParts[i] < partBParts[i]) {
return -1;
}
}
return 0;
},
// It goes number, string, number, string. If it doesn't exist, then
// 0 for numbers, empty string for strings.
parseVersionPart(part) {
if (!part) {
return [0, "", 0, ""];
}
const partParts = /([0-9]*)([^0-9]*)([0-9]*)([^0-9]*)/.exec(part);
return [
partParts[1] ? parseInt(partParts[1]) : 0,
partParts[2],
partParts[3] ? parseInt(partParts[3]) : 0,
partParts[4]
];
},
/**
*
* @param installButton 安装按钮
* 必须属性
* + data-update-label 按钮升级的文字
* + data-downgrade-label 按钮降级的文字
* + data-reinstall-label 按钮重装的文字
* @param installedVersion 安装版本
* @param version 版本号
* @returns
*/
handleInstallResult(installButton, installedVersion, version) {
if (installedVersion == null) {
return;
}
installButton.removeAttribute("data-ping-url");
switch (this.compareVersions(installedVersion, version)) {
case -1:
installButton.textContent = installButton.getAttribute("data-update-label");
break;
case 1:
installButton.textContent = installButton.getAttribute(
"data-downgrade-label"
);
break;
case 0:
installButton.textContent = installButton.getAttribute(
"data-reinstall-label"
);
break;
}
},
/**
* 检测js脚本的更新
* @param installButton 安装按钮
* 必须属性
* + data-script-name 脚本名
* + data-script-namespace 脚本命名空间
* + data-script-version 脚本当前版本号
* @param retry 重试
*/
async checkForUpdatesJS(installButton, retry) {
const name = installButton.getAttribute("data-script-name");
const namespace = installButton.getAttribute("data-script-namespace");
const version = installButton.getAttribute("data-script-version");
try {
let installedVersion = await this.getInstalledVersion(name, namespace);
if (installedVersion == null) {
return false;
}
this.handleInstallResult(installButton, installedVersion, version);
return true;
} catch (error) {
if (retry) {
await utils.sleep(1e3);
try {
return await this.checkForUpdatesJS(installButton, false);
} catch (error2) {
}
}
return false;
}
},
/**
* 检测css脚本的更新
* @param installButton 安装按钮
* 必须属性
* + data-script-name 脚本名
* + data-script-namespace 脚本命名空间
*/
checkForUpdatesCSS(installButton) {
const name = installButton.getAttribute("data-script-name");
const namespace = installButton.getAttribute("data-script-namespace");
postMessage(
{ type: "style-version-query", name, namespace, url: location.href },
location.origin
);
}
};
const parseScriptListInfo = ($scriptList) => {
let dataset = $scriptList.dataset;
const info = {
scriptId: parseInt(dataset.scriptId),
scriptName: dataset.scriptName,
scriptAuthors: [],
scriptDailyInstalls: parseInt(dataset.scriptDailyInstalls),
scriptTotalInstalls: parseInt(dataset.scriptTotalInstalls),
scriptRatingScore: parseFloat(dataset.scriptRatingScore),
scriptCreatedDate: new Date(dataset.scriptCreatedDate),
scriptUpdatedDate: new Date(dataset.scriptUpdatedDate),
scriptType: dataset.scriptType,
scriptVersion: dataset.scriptVersion,
sensitive: dataset.sensitive === "true",
scriptLanguage: dataset.scriptLanguage,
cssAvailableAsJs: dataset.cssAvailableAsJs === "true",
codeUrl: dataset.codeUrl,
scriptDescription: dataset.scriptDescription,
scriptAuthorId: parseInt(dataset.scriptAuthorId),
scriptAuthorName: dataset.scriptAuthorName
};
let scriptAuthorsObj = utils.toJSON(dataset.scriptAuthors);
Object.keys(scriptAuthorsObj).forEach((authorId) => {
let authorName = scriptAuthorsObj[authorId];
info.scriptAuthors.push({
authorId: parseInt(authorId),
authorName
});
});
if ((info.scriptAuthorName == null || isNaN(info.scriptAuthorId)) && info.scriptAuthors.length) {
info.scriptAuthorName = info.scriptAuthors[0].authorName;
info.scriptAuthorId = info.scriptAuthors[0].authorId;
}
if (info.scriptDescription == null) {
let $description = $scriptList.querySelector(".script-description") || $scriptList.querySelector(".description");
if ($description) {
info.scriptDescription = $description.innerText || $description.textContent;
}
}
return info;
};
const GreasyforkScriptsList = {
init() {
PopsPanel.execMenuOnce("gf-scripts-filter-enable", () => {
GreasyforkScriptsFilter.init();
});
PopsPanel.execMenuOnce("beautifyCenterContent", () => {
return this.beautifyCenterContent();
});
},
/**
* 美化脚本列表
*/
beautifyCenterContent() {
log.info("美化脚本列表-双列");
let result = [];
result.push(addStyle(beautifyCenterContentCSS));
const lodingClassName = "lum-lightbox-loader";
const noInstallBtnText = i18next.t("安装此脚本");
DOMUtils.ready(async () => {
let allScriptsList = GreasyforkScriptsFilter.getElementList();
allScriptsList.forEach(($scriptList) => {
if ($scriptList.querySelector(".script-list-operation")) {
return;
}
let scriptInfo = parseScriptListInfo($scriptList);
let $inlineStats = $scriptList.querySelector(
".inline-script-stats"
);
if (!$inlineStats) {
log.error("美化脚本列表失败,未获取到.inline-script-stats");
return;
}
let code_url = scriptInfo.codeUrl;
let $ratingScoreLeft = DOMUtils.createElement("dt", {
className: "script-list-rating-score",
innerHTML: `<span>${i18next.t("评分")}</span>`
});
let $ratingScoreRight = DOMUtils.createElement(
"dd",
{
className: "script-list-rating-score",
innerHTML: `<span>${scriptInfo.scriptRatingScore}</span>`
},
{
"data-position": "right"
}
);
let $goodRatingCount = $scriptList.querySelector(
"dd.script-list-ratings .good-rating-count"
);
let $okRatingCount = $scriptList.querySelector(
"dd.script-list-ratings .ok-rating-count"
);
let $badRatingCount = $scriptList.querySelector(
"dd.script-list-ratings .bad-rating-count"
);
if ($goodRatingCount && $okRatingCount && $badRatingCount) {
let goodRatingCount = parseInt($goodRatingCount.innerText);
let okRatingCount = parseInt($okRatingCount.innerText);
let badRatingCount = parseInt($badRatingCount.innerText);
let totalRatingCount = goodRatingCount + okRatingCount + badRatingCount;
if (totalRatingCount >= 10) {
if (goodRatingCount / totalRatingCount >= 0.6) {
$ratingScoreRight.classList.add("good-rating-count");
} else {
$ratingScoreRight.classList.add("bad-rating-count");
}
} else if (totalRatingCount == 0) {
$ratingScoreRight.classList.add("good-rating-count");
} else {
if (goodRatingCount > okRatingCount + badRatingCount) {
$ratingScoreRight.classList.add("good-rating-count");
} else {
$ratingScoreRight.classList.add("bad-rating-count");
}
}
}
let $versionLeft = DOMUtils.createElement("dt", {
className: "script-list-version",
innerHTML: (
/*html*/
`<span>${i18next.t("版本")}</span>`
)
});
let $versionRight = DOMUtils.createElement(
"dd",
{
className: "script-list-version",
innerHTML: (
/*html*/
`<span>${scriptInfo.scriptVersion}</span>`
)
},
{
"data-position": "right"
}
);
let $operationLeft = DOMUtils.createElement("dt", {
className: "script-list-operation",
innerHTML: `<span>${i18next.t("操作")}</span>`
});
let $operationRight = DOMUtils.createElement(
"dd",
{
className: "script-list-operation",
innerHTML: (
/*html*/
`
<a
target="_blank"
class="install-link"
data-install-format="js"
data-script-name="${scriptInfo.scriptName}"
data-script-namespace=""
data-script-version="${scriptInfo.scriptVersion}"
data-update-label="${i18next.t("更新到 {{version}} 版本", {
version: scriptInfo.scriptVersion
})}"
data-downgrade-label="${i18next.t("降级到 {{version}} 版本", {
version: scriptInfo.scriptVersion
})}"
data-reinstall-label="${i18next.t("重新安装 {{version}} 版本", {
version: scriptInfo.scriptVersion
})}"
href="${code_url}"></a>
<button class="script-collect-btn">${i18next.t("收藏")}</button>
`
)
},
{
"data-position": "right",
style: "gap:10px;display: flex;flex-wrap: wrap;align-items: center;"
}
);
let $collect = $operationRight.querySelector(
".script-collect-btn"
);
let $installLink = $operationRight.querySelector(".install-link");
$installLink["data-script-info"] = scriptInfo;
DOMUtils.addClass($installLink, lodingClassName);
if (scriptInfo.scriptType === "library") {
$installLink.remove();
}
DOMUtils.on($collect, "click", (event) => {
utils.preventEvent(event);
GreasyforkScriptsCollectEvent(scriptInfo.scriptId);
});
if (PopsPanel.getValue("gf-scripts-filter-enable")) {
let $filter = DOMUtils.createElement("button", {
className: "script-filter-btn",
innerHTML: i18next.t("过滤")
});
let attr_filter_key = "data-filter-key";
let attr_filter_value = "data-filter-value";
DOMUtils.on($filter, "click", (event) => {
utils.preventEvent(event);
let $dialog = __pops.alert({
title: {
text: i18next.t("选择需要过滤的选项"),
position: "center"
},
content: {
text: (
/*html*/
`
<button ${attr_filter_key}="scriptId" ${attr_filter_value}="^${scriptInfo.scriptId}$">${i18next.t("脚本id:{{text}}", {
text: scriptInfo.scriptId
})}</button>
<button ${attr_filter_key}="scriptName" ${attr_filter_value}="^${utils.parseStringToRegExpString(
scriptInfo.scriptName
)}$">${i18next.t("脚本名:{{text}}", {
text: scriptInfo.scriptName
})}</button>
`
),
html: true
},
mask: {
enable: true,
clickEvent: {
toClose: true
}
},
width: "350px",
height: "300px",
drag: true,
dragLimit: true,
style: (
/*css*/
`
.pops-alert-content{
display: flex;
flex-direction: column;
gap: 20px;
}
.pops-alert-content button{
text-wrap: wrap;
padding: 8px;
height: auto;
text-align: left;
}
`
)
});
let $content = $dialog.$shadowRoot.querySelector(
".pops-alert-content"
);
scriptInfo.scriptAuthors.forEach((scriptAuthorInfo) => {
let $authorIdButton = DOMUtils.createElement("button", {
innerHTML: i18next.t("作者id:{{text}}", {
text: scriptAuthorInfo.authorId
})
});
$authorIdButton.setAttribute(attr_filter_key, "scriptAuthorId");
$authorIdButton.setAttribute(
attr_filter_value,
"^" + scriptAuthorInfo.authorId + "$"
);
let $authorNameButton = DOMUtils.createElement("button", {
innerHTML: i18next.t("作者名:{{text}}", {
text: scriptAuthorInfo.authorName
})
});
$authorNameButton.setAttribute(
attr_filter_key,
"scriptAuthorName"
);
$authorNameButton.setAttribute(
attr_filter_value,
"^" + utils.parseStringToRegExpString(scriptAuthorInfo.authorName) + "$"
);
$content.appendChild($authorIdButton);
$content.appendChild($authorNameButton);
});
DOMUtils.on(
$dialog.$shadowRoot,
"click",
`button[${attr_filter_key}]`,
(event2) => {
utils.preventEvent(event2);
let $click = event2.target;
let key = $click.getAttribute(
attr_filter_key
);
let value = $click.getAttribute(attr_filter_value);
GreasyforkScriptsFilter.addValue(key, value);
$dialog.close();
GreasyforkScriptsFilter.filter();
Qmsg.success(i18next.t("添加成功"));
}
);
});
$operationRight.appendChild($filter);
}
$inlineStats.appendChild($ratingScoreLeft);
$inlineStats.appendChild($ratingScoreRight);
$inlineStats.appendChild($versionLeft);
$inlineStats.appendChild($versionRight);
$inlineStats.appendChild($operationLeft);
$inlineStats.appendChild($operationRight);
});
let $installLinkList = Array.from(
document.querySelectorAll(
".install-link[data-install-format=js]"
)
);
let allScriptContainerStatus = GreasyforkCheckVersion.getScriptContainerStatus();
let hasScriptContainer = Object.values(allScriptContainerStatus).find(
(item) => item
);
let isForceUseNameSpace = PopsPanel.getValue(
"beautifyCenterContent-queryNameSpace"
);
if (!hasScriptContainer) {
log.error("脚本容器未暴露external信息", window.external);
} else {
log.info(
"当前暴露的external信息:" + Object.keys(allScriptContainerStatus).map((item) => `【${item}】`).join("、")
);
}
for (let index = 0; index < $installLinkList.length; index++) {
const $installLink = $installLinkList[index];
let scriptInfo = Reflect.get(
$installLink,
"data-script-info"
);
if (hasScriptContainer) {
if (isForceUseNameSpace) {
let response = await httpx.get(
GreasyforkUrlUtils.getScriptInfoUrl(scriptInfo.scriptId),
{
fetch: true
}
);
if (response.status) {
let data = utils.toJSON(
response.data.responseText
);
$installLink.setAttribute(
"data-script-namespace",
data.namespace
);
}
}
GreasyforkCheckVersion.checkForUpdatesJS($installLink, true).then(
(checkResult) => {
DOMUtils.removeClass($installLink, lodingClassName);
if (!checkResult) {
DOMUtils.text($installLink, noInstallBtnText);
}
}
);
} else {
DOMUtils.removeClass($installLink, lodingClassName);
DOMUtils.text($installLink, noInstallBtnText);
}
}
});
return result;
}
};
const GreasyforkElementUtils = {
/**
* 获取当前登录用户id
*/
getCurrentLoginUserId() {
let $anchor = document.querySelector(
"#nav-user-info .user-profile-link a"
);
if (!$anchor) {
return;
}
let userId = GreasyforkUrlUtils.getUserId($anchor.href);
if (userId == null) {
return;
}
return userId;
}
};
const GreasyforkUtils = {
/**
* 判断是否是当前已登录账户的主页
*/
isCurrentLoginUserHome() {
let currentLoginUserId = GreasyforkElementUtils.getCurrentLoginUserId();
if (currentLoginUserId != null && GreasyforkRouter.isUsers() && window.location.pathname.includes("/" + currentLoginUserId)) {
return true;
} else {
return false;
}
}
};
const GreasyforkScriptsFilter = {
/** 存储的键 */
key: "gf-shield-rule",
init() {
log.info("脚本过滤");
let lockFunction = new utils.LockFunction(() => {
this.filter();
}, 50);
domUtils.ready(() => {
if (GreasyforkUtils.isCurrentLoginUserHome()) {
log.warn("当前在已登录的账户主页下,禁用脚本过滤");
return;
}
utils.mutationObserver(document.body, {
config: {
subtree: true,
childList: true
},
callback: () => {
lockFunction.run();
}
});
lockFunction.run();
});
},
/**
* 获取脚本列表元素
*/
getElementList() {
let scriptList = [];
scriptList = scriptList.concat(
Array.from(document.querySelectorAll("ol.script-list li"))
);
return scriptList;
},
/**
* 对页面进行过滤
*/
filter() {
this.getElementList().forEach(($scriptList) => {
let data = parseScriptListInfo($scriptList);
let localValueSplit = this.getValue().split("\n");
for (let index = 0; index < localValueSplit.length; index++) {
let localRule = localValueSplit[index];
let ruleSplit = localRule.split("##");
let ruleName = ruleSplit[0];
let ruleValue = ruleSplit[1];
if (ruleName === "scriptRatingScore") {
let userRatingScoreValue = parseFloat(ruleValue.slice(1));
if (ruleValue.startsWith(">")) {
if (data.scriptRatingScore > userRatingScoreValue) {
log.info(["触发脚本过滤规则", [localRule, data]]);
$scriptList.remove();
break;
}
} else if (ruleValue.startsWith("<")) {
if (data.scriptRatingScore < userRatingScoreValue) {
log.info(["触发脚本过滤规则", [localRule, data]]);
$scriptList.remove();
break;
}
}
} else if (ruleName in data || ruleName === "scriptDescription") {
if (typeof ruleValue !== "string") {
continue;
}
let ruleValueRegExp = new RegExp(ruleValue, "ig");
let scriptInfoString = String(data[ruleName]);
if (scriptInfoString.match(ruleValueRegExp)) {
log.info(["触发脚本过滤规则", localRule, data]);
$scriptList.remove();
break;
}
}
}
});
},
setValue(value) {
PopsPanel.setValue(this.key, value);
},
addValue(key, value) {
let localValue = this.getValue();
if (localValue.trim() !== "") {
localValue += "\n";
}
if (utils.isNull(key)) {
return;
}
key = key.toString().trim();
let rule = key + "##" + value;
localValue += rule;
this.setValue(localValue);
},
getValue() {
return PopsPanel.getValue(this.key, "");
}
};
const SettingUIGeneral = {
id: "greasy-fork-panel-config-account",
title: i18next.t("通用"),
forms: [
{
text: "",
type: "forms",
forms: [
{
text: i18next.t("Toast配置"),
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISelect(
i18next.t("Toast位置"),
"qmsg-config-position",
"bottom",
[
{
value: "topleft",
text: i18next.t("左上角")
},
{
value: "top",
text: i18next.t("顶部")
},
{
value: "topright",
text: i18next.t("右上角")
},
{
value: "left",
text: i18next.t("左边")
},
{
value: "center",
text: i18next.t("中间")
},
{
value: "right",
text: i18next.t("右边")
},
{
value: "bottomleft",
text: i18next.t("左下角")
},
{
value: "bottom",
text: i18next.t("底部")
},
{
value: "bottomright",
text: i18next.t("右下角")
}
],
(event, isSelectValue, isSelectText) => {
log.info("设置当前Qmsg弹出位置" + isSelectText);
},
i18next.t("Toast显示在页面九宫格的位置")
),
UISelect(
i18next.t("最多显示的数量"),
"qmsg-config-maxnums",
3,
[
{
value: 1,
text: "1"
},
{
value: 2,
text: "2"
},
{
value: 3,
text: "3"
},
{
value: 4,
text: "4"
},
{
value: 5,
text: "5"
}
],
void 0,
i18next.t("限制Toast显示的数量")
),
UISwitch(
i18next.t("逆序弹出"),
"qmsg-config-showreverse",
false,
void 0,
i18next.t("修改Toast弹出的顺序")
)
]
}
]
},
UISelect(
i18next.t("语言"),
"setting-language",
"zh-CN",
[
{
value: "zh-CN",
text: "中文"
},
{
value: "en-US",
text: "English"
}
],
(event, isSelectValue, isSelectText) => {
log.info("改变语言:" + isSelectText);
i18next.changeLanguage(isSelectValue);
}
)
]
},
{
text: "",
type: "forms",
forms: [
{
text: i18next.t("账号/密码"),
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UIInput(
i18next.t("账号"),
"user",
"",
void 0,
void 0,
i18next.t("请输入账号")
),
UIInput(
i18next.t("密码"),
"pwd",
"",
void 0,
void 0,
i18next.t("请输入密码"),
false,
true
)
]
},
{
text: "",
type: "forms",
forms: [
UISwitch(
i18next.t("自动登录"),
"autoLogin",
true,
void 0,
i18next.t("自动登录当前保存的账号")
),
UIButton(
i18next.t("清空账号/密码"),
void 0,
i18next.t("点击清空"),
void 0,
void 0,
false,
"default",
(event) => {
if (confirm(i18next.t("确定清空账号和密码?"))) {
PopsPanel.deleteValue("user");
PopsPanel.deleteValue("pwd");
Qmsg.success(i18next.t("已清空账号/密码"));
let $shadowRoot = event.target.getRootNode();
$shadowRoot.querySelector(
`li[data-key="user"] .pops-panel-input input`
).value = "";
$shadowRoot.querySelector(
`li[data-key="pwd"] .pops-panel-input input`
).value = "";
}
}
)
]
}
]
},
{
text: i18next.t("功能"),
type: "deepMenu",
forms: [
{
text: i18next.t("功能"),
type: "forms",
forms: [
UISelect(
i18next.t("固定当前语言"),
"language-selector-locale",
"",
function() {
let result = [
{
value: "",
text: i18next.t("无")
}
];
document.querySelectorAll(
"select#language-selector-locale option"
).forEach((element) => {
let value = element.getAttribute("value");
if (value === "help") {
return;
}
let text = (element.innerText || element.textContent).trim();
result.push({
value,
text
});
});
return result;
}()
),
UISwitch(
i18next.t("修复图片宽度显示问题"),
"fixImageWidth",
true,
void 0,
i18next.t("修复图片在移动端宽度超出浏览器宽度问题")
),
UISwitch(
i18next.t("优化图片浏览"),
"optimizeImageBrowsing",
true,
void 0,
i18next.t("使用Viewer浏览图片")
),
UISwitch(
i18next.t("覆盖图床图片跳转"),
"overlayBedImageClickEvent",
true,
void 0,
i18next.t("配合上面的【优化图片浏览】更优雅浏览图片")
),
UISwitch(
i18next.t("添加【操作面板】按钮"),
"scripts-addOperationPanelBtnWithNavigator",
true,
void 0,
i18next.t("在脚本列表页面时为顶部导航栏添加【操作面板】按钮")
),
UISwitch(
i18next.t("给Markdown添加【复制】按钮"),
"addMarkdownCopyButton",
true,
void 0,
i18next.t(
"在Markdown内容右上角添加【复制】按钮,点击一键复制Markdown内容"
)
)
]
},
{
text: i18next.t("检测页面加载"),
type: "forms",
forms: [
UISwitch(
i18next.t("启用"),
"checkPage",
true,
void 0,
i18next.t(
"检测Greasyfork页面是否正常加载,如加载失败则自动刷新页面"
)
),
UISelect(
i18next.t("检测间隔"),
"greasyfork-check-page-timeout",
5,
(() => {
let result = [];
for (let index = 0; index < 5; index++) {
result.push({
value: index + 1,
text: index + 1 + "s"
});
}
return result;
})(),
void 0,
i18next.t(
"设置检测上次刷新页面的间隔时间,当距离上次刷新页面的时间超过设置的值,将不再刷新页面"
)
)
]
}
]
},
{
type: "deepMenu",
text: i18next.t("表单"),
forms: [
{
type: "forms",
text: "",
forms: [
UISwitch(
i18next.t("记住回复内容"),
"rememberReplyContent",
true,
void 0,
i18next.t(
"监听表单内的textarea内容改变并存储到indexDB中,提交表单将清除保存的数据,误刷新页面时可动态恢复"
)
),
UISelect(
i18next.t("自动清理空间"),
"gf-autoClearRememberReplayContent",
7,
[
{
text: i18next.t("不清理"),
value: -1
},
{
text: i18next.t("{{value}} 天", {
value: 1
}),
value: 1
},
{
text: i18next.t("{{value}} 周", {
value: 1
}),
value: 7
},
{
text: i18next.t("{{value}} 个月", {
value: 1
}),
value: 30
},
{
text: i18next.t("{{value}} 个月", {
value: 2
}),
value: 60
},
{
text: i18next.t("{{value}} 个月", {
value: 3
}),
value: 90
},
{
text: i18next.t("半年"),
value: 180
}
],
void 0,
i18next.t("根据设置的间隔时间自动清理保存的回复内容")
),
UIButton(
i18next.t(`数据占用空间:{{size}}`, {
size: i18next.t("计算中")
}),
i18next.t("当前存储的数据所占用的空间大小"),
i18next.t("清空"),
void 0,
void 0,
void 0,
"default",
async () => {
let isClear = await GreasyforkRememberFormTextArea.clearAllRememberReplyContent();
if (isClear) {
Qmsg.success(i18next.t("清理成功"));
} else {
Qmsg.error(i18next.t("清理失败"));
}
},
async (formConfig, container) => {
let $leftTopText = container.ulElement.querySelector(
'li[data-key="gf-autoClearRememberReplayContent"]+li .pops-panel-item-left-main-text'
);
let allText = await GreasyforkRememberFormTextArea.getAllRememberReplyContent();
let showSize = "";
if (allText.length) {
showSize = utils.getTextStorageSize(
JSON.stringify(allText)
);
} else {
showSize = utils.getTextStorageSize("");
}
$leftTopText.innerText = i18next.t(
`数据占用空间:{{size}}`,
{
size: showSize
}
);
}
)
]
}
]
},
{
text: i18next.t("美化"),
type: "deepMenu",
forms: [
{
text: i18next.t("全局"),
type: "forms",
forms: [
UISwitch(
i18next.t("美化页面元素"),
"beautifyPage",
true,
void 0,
i18next.t("如button、input、textarea")
),
UISwitch(
i18next.t("美化上传图片按钮"),
"beautifyUploadImage",
true,
void 0,
i18next.t("放大上传区域")
),
UISwitch(
i18next.t("美化顶部导航栏"),
"beautifyTopNavigationBar",
true,
void 0,
i18next.t("可能会跟Greasyfork Beautify脚本有冲突")
),
UISwitch(
i18next.t("美化Greasyfork Beautify脚本"),
"beautifyGreasyforkBeautify",
true,
void 0,
i18next.t(
'需安装Greasyfork Beautify脚本,<a href="https://greasyfork.org/zh-CN/scripts/446849-greasyfork-beautify" target="_blank">🖐点我安装</a>'
)
)
]
},
{
type: "forms",
text: i18next.t("脚本列表"),
forms: [
UISwitch(
i18next.t("美化脚本列表"),
"beautifyCenterContent",
true,
void 0,
i18next.t("双列显示且添加脚本卡片操作项(安装、收藏)")
),
UISwitch(
"↑" + i18next.t("使用namespace查询脚本信息"),
"beautifyCenterContent-queryNameSpace",
true,
void 0,
i18next.t("开启后检测已安装的脚本信息更准确,但是速度会更慢")
)
]
}
]
},
{
type: "deepMenu",
text: i18next.t("自定义快捷键"),
forms: [
{
type: "forms",
text: "",
forms: [
UIButtonShortCut(
i18next.t("快捷键发表回复"),
i18next.t("在输入框内按下快捷发表回复,例如:{{key}}", {
key: "Ctrl + Enter"
}),
"gf-quickReply",
{
keyName: "Enter",
keyValue: "13",
ohterCodeList: ["ctrl"]
},
i18next.t("点击录入快捷键"),
void 0,
GreasyforkShortCut.shortCut
)
]
}
]
},
{
text: i18next.t("过滤"),
type: "deepMenu",
forms: [
{
text: `<a target="_blank" href="https://greasyfork.org/zh-CN/scripts/475722-greasyfork%E4%BC%98%E5%8C%96#:~:text=%E8%84%9A%E6%9C%AC%E8%BF%87%E6%BB%A4%E8%A7%84%E5%88%99">${i18next.t(
"帮助文档"
)}</a>`,
type: "forms",
forms: [
UISwitch(
i18next.t("启用"),
"gf-scripts-filter-enable",
true,
void 0,
i18next.t("作用域:脚本、脚本搜索、用户主页")
),
{
type: "own",
getLiElementCallBack(liElement) {
let textareaDiv = domUtils.createElement(
"div",
{
className: "pops-panel-textarea",
innerHTML: `
<textarea placeholder="${i18next.t(
"请输入规则,每行一个"
)}" style="height:200px;"></textarea>`
},
{
style: "width: 100%;"
}
);
let $textarea = textareaDiv.querySelector(
"textarea"
);
$textarea.value = GreasyforkScriptsFilter.getValue();
domUtils.on(
$textarea,
["input", "propertychange"],
void 0,
utils.debounce(function(event) {
GreasyforkScriptsFilter.setValue($textarea.value);
}, 200)
);
liElement.appendChild(textareaDiv);
return liElement;
}
}
]
}
]
}
]
},
{
type: "forms",
text: i18next.t("脚本管理"),
forms: [
{
type: "deepMenu",
text: i18next.t("代码同步"),
forms: [
{
text: i18next.t("代码同步"),
type: "forms",
forms: [
UIButton(
i18next.t("源代码同步【脚本列表】"),
void 0,
i18next.t("一键同步"),
void 0,
void 0,
false,
"primary",
(event) => {
if (!GreasyforkRouter.isUsers()) {
PopsPanel.setValue(
"goto_updateSettingsAndSynchronize_scriptList",
true
);
if (GreasyforkMenu.getUserLinkElement()) {
Qmsg.success(i18next.t("前往用户主页"));
window.location.href = GreasyforkMenu.getUserLinkElement().href;
} else {
Qmsg.error(i18next.t("获取当前已登录的用户主页失败"));
}
return;
}
let scriptUrlList = [];
document.querySelectorAll(
"#user-script-list-section li a.script-link"
).forEach((item) => {
scriptUrlList = scriptUrlList.concat(
GreasyforkUrlUtils.getAdminUrl(item.href)
);
});
GreasyforkMenu.updateScript(scriptUrlList);
}
),
UIButton(
i18next.t("源代码同步【未上架的脚本】"),
void 0,
i18next.t("一键同步"),
void 0,
void 0,
false,
"primary",
(event) => {
if (!GreasyforkRouter.isUsers()) {
PopsPanel.setValue(
"goto_updateSettingsAndSynchronize_unlistedScriptList",
true
);
if (GreasyforkMenu.getUserLinkElement()) {
Qmsg.success(i18next.t("前往用户主页"));
window.location.href = GreasyforkMenu.getUserLinkElement().href;
} else {
Qmsg.error(i18next.t("获取当前已登录的用户主页失败"));
}
return;
}
let scriptUrlList = [];
document.querySelectorAll(
"#user-unlisted-script-list li a.script-link"
).forEach((item) => {
scriptUrlList = scriptUrlList.concat(
GreasyforkUrlUtils.getAdminUrl(item.href)
);
});
GreasyforkMenu.updateScript(scriptUrlList);
}
),
UIButton(
i18next.t("源代码同步【库】"),
void 0,
i18next.t("一键同步"),
void 0,
void 0,
false,
"primary",
(event) => {
if (!GreasyforkRouter.isUsers()) {
PopsPanel.setValue(
"goto_updateSettingsAndSynchronize_libraryScriptList",
true
);
if (GreasyforkMenu.getUserLinkElement()) {
Qmsg.success(i18next.t("前往用户主页"));
window.location.href = GreasyforkMenu.getUserLinkElement().href;
} else {
Qmsg.error(i18next.t("获取当前已登录的用户主页失败"));
}
return;
}
let scriptUrlList = [];
document.querySelectorAll(
"#user-library-script-list li a.script-link"
).forEach((item) => {
scriptUrlList = scriptUrlList.concat(
GreasyforkUrlUtils.getAdminUrl(item.href)
);
});
GreasyforkMenu.updateScript(scriptUrlList);
}
)
]
}
]
},
{
type: "deepMenu",
text: i18next.t("脚本列表"),
forms: [],
afterEnterDeepMenuCallBack(formConfig, container) {
PopsPanelUISetting.UIScriptList(
"script-list",
container.sectionBodyContainer
);
}
},
{
type: "deepMenu",
text: i18next.t("库"),
forms: [],
afterEnterDeepMenuCallBack(formConfig, container) {
PopsPanelUISetting.UIScriptList(
"script-library",
container.sectionBodyContainer
);
}
}
]
}
]
};
const SettingUIScripts = {
id: "greasy-fork-panel-config-scripts",
title: i18next.t("脚本"),
forms: [
{
text: "",
type: "forms",
forms: [
{
text: i18next.t("代码"),
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
i18next.t("添加复制代码按钮"),
"addCopyCodeButton",
true,
void 0,
i18next.t("更优雅的复制")
),
UISwitch(
i18next.t("快捷键"),
"fullScreenOptimization",
true,
void 0,
i18next.t("【F】键全屏、【Alt+Shift+F】键宽屏")
),
UISwitch(
i18next.t("修复代码行号显示"),
"code-repairCodeLineNumber",
true,
void 0,
i18next.t("修复代码行数超过1k行号显示不全问题")
)
]
}
]
},
{
text: i18next.t("历史版本"),
type: "deepMenu",
forms: [
{
text: i18next.t("功能"),
type: "forms",
forms: [
UISwitch(
i18next.t("添加额外的标签按钮"),
"scripts-versions-addExtraTagButton",
true,
void 0,
i18next.t("在版本下面添加【安装】、【查看代码】按钮")
)
]
},
{
text: i18next.t("美化"),
type: "forms",
forms: [
UISwitch(
i18next.t("美化历史版本页面"),
"beautifyHistoryVersionPage",
true,
void 0,
i18next.t("更直观的查看版本迭代")
)
]
}
]
}
]
},
{
text: "",
type: "forms",
forms: [
{
text: i18next.t("功能"),
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
i18next.t("添加【寻找引用】按钮"),
"addFindReferenceButton",
true,
void 0,
i18next.t("在脚本栏添加按钮,一般用于搜索引用该库的相关脚本")
),
UISwitch(
i18next.t("添加【收藏】按钮"),
"addCollectionButton",
true,
void 0,
i18next.t("在脚本栏添加按钮,一般用于快捷收藏该脚本/库")
),
UISwitch(
i18next.t("添加【今日检查】信息块"),
"scriptHomepageAddedTodaySUpdate",
true,
void 0,
i18next.t("在脚本信息栏添加【今日检查】信息块")
)
]
}
]
}
]
}
]
};
const GreasyforkDiscussionsFilter = {
/** 存储的键 */
key: "gf-discuessions-filter-rule",
$data: {
/** 脚本 */
FILTER_SCRIPT_KEY: "greasyfork-discussions-filter-script",
/** 发布用户 */
FILTER_POST_USER_KEY: "greasyfork-discussions-filter-post-user",
/** 回复用户 */
FILTER_REPLY_USER_KEY: "greasyfork-discussions-filter-reply-user"
},
init() {
log.info("论坛-过滤");
_GM_addStyle(
/*css*/
`
.discussion-list-container {
--discusstion-repeat-color: #ffa700;
}
.discussion-list-container a.discussion-title[data-repeat-tip-show]::before {
content: attr(data-repeat-tip-show);
color: var(--discusstion-repeat-color);
border-radius: 5px;
border: 2px solid var(--discusstion-repeat-color);
padding: 2px 5px;
font-weight: 800;
font-size: 14px;
}
`
);
let lockFunction = new utils.LockFunction(() => {
this.filter();
}, 50);
utils.mutationObserver(document.body, {
config: {
subtree: true,
childList: true
},
callback: () => {
lockFunction.run();
}
});
lockFunction.run();
},
/**
* 获取反馈列表元素
*/
getElementList() {
let discussionsListContainer = [];
discussionsListContainer = discussionsListContainer.concat(
Array.from(
document.querySelectorAll(".discussion-list-container")
)
);
return discussionsListContainer;
},
/**
* 论坛-过滤
*/
filter() {
this.transformOldRule();
const SNIPPET_MAP = /* @__PURE__ */ new Map();
this.getElementList().forEach(($listContainer, index) => {
const discussionInfo = this.parseDiscuessionListContainerInfo($listContainer);
let localValueSplit = this.getValue().split("\n");
if (SNIPPET_MAP.has(discussionInfo.snippet) && PopsPanel.getValue("greasyfork-discussions-filter-duplicate-comments")) {
let discussionTitleElement = SNIPPET_MAP.get(
discussionInfo.snippet
).querySelector("a.discussion-title");
discussionTitleElement.setAttribute("data-repeat-tip-show", "true");
let oldCount = 0;
if (discussionTitleElement.hasAttribute("data-repeat-count")) {
oldCount = parseInt(
discussionTitleElement.getAttribute("data-repeat-count")
);
}
oldCount++;
discussionTitleElement.setAttribute(
"data-repeat-count",
oldCount.toString()
);
discussionTitleElement.setAttribute(
"data-repeat-tip-show",
i18next.t("已过滤:{{oldCount}}", { oldCount })
);
log.success([
`过滤重复内容:${discussionInfo.snippet}`,
discussionInfo
]);
$listContainer.remove();
return;
}
SNIPPET_MAP.set(discussionInfo.snippet, $listContainer);
for (let index2 = 0; index2 < localValueSplit.length; index2++) {
let localRule = localValueSplit[index2];
let ruleSplit = localRule.split("##");
let ruleName = ruleSplit[0];
let ruleValue = ruleSplit[1];
if (ruleName in discussionInfo) {
let ruleValueRegExp = new RegExp(ruleValue, "ig");
if (discussionInfo[ruleName] != null) {
let scriptInfoString = String(discussionInfo[ruleName]);
if (scriptInfoString.match(ruleValueRegExp)) {
log.info(["触发论坛过滤规则", localRule, discussionInfo]);
$listContainer.remove();
return;
}
}
}
}
});
},
/**
* 解析出元素上的属性
*/
parseDiscuessionListContainerInfo($listContainer) {
var _a2, _b, _c, _d;
let discussionUrl = $listContainer.querySelector("a.discussion-title").href;
let discuessionIdMatch = discussionUrl.match(
/\/discussions(|\/greasyfork)\/([\d]+)/
);
let discuessionId = discuessionIdMatch[discuessionIdMatch.length - 1];
const info = {
/** 脚本名 */
scriptName: $listContainer.querySelector(
".discussion-meta-item-script-name"
).innerText,
/** 脚本主页地址 */
scriptUrl: (_a2 = $listContainer.querySelector(
".discussion-meta-item-script-name a"
)) == null ? void 0 : _a2.href,
/** 脚本id */
scriptId: GreasyforkUrlUtils.getScriptId(
(_b = $listContainer.querySelector(
".discussion-meta-item-script-name a"
)) == null ? void 0 : _b.href
),
/** 发布的用户名 */
postUserName: $listContainer.querySelector("a.user-link").innerText,
/** 发布的用户主页地址 */
postUserHomeUrl: $listContainer.querySelector("a.user-link").href,
/** 发布的用户id */
postUserId: GreasyforkUrlUtils.getUserId(
$listContainer.querySelector("a.user-link").href
),
/** 发布的时间 */
postTimeStamp: new Date(
$listContainer.querySelector("relative-time").getAttribute("datetime")
),
/** 发布的id */
snippetId: discuessionId,
/** 发布的地址*/
snippetUrl: discussionUrl,
/** 发布的内容片段*/
snippet: ((_c = $listContainer.querySelector("span.discussion-snippet")) == null ? void 0 : _c.innerText) || "",
/** (如果有)回复的用户名*/
replyUserName: void 0,
/** (如果有)回复的用户主页地址*/
replyUserHomeUrl: void 0,
/** (如果有)回复的用户id*/
replyUserId: void 0,
/** (如果有)回复的时间 */
replyTimeStamp: void 0
};
if ($listContainer.querySelector(
".discussion-meta-item .discussion-meta-item"
)) {
info.replyUserName = $listContainer.querySelector(
".discussion-meta-item .discussion-meta-item a.user-link"
).innerText;
info.replyUserHomeUrl = $listContainer.querySelector(
".discussion-meta-item .discussion-meta-item a.user-link"
).href;
info.replyUserId = GreasyforkUrlUtils.getUserId(info.replyUserHomeUrl);
info.replyTimeStamp = new Date(
(_d = $listContainer.querySelector(
".discussion-meta-item .discussion-meta-item relative-time"
)) == null ? void 0 : _d.getAttribute("datetime")
);
}
return info;
},
/** 转换旧规则 @deprecated */
transformOldRule() {
if (Date.now() > (/* @__PURE__ */ new Date("2024-8-19")).getTime()) {
return;
}
const FILTER_SCRIPT_KEY = "greasyfork-discussions-filter-script";
const FILTER_POST_USER_KEY = "greasyfork-discussions-filter-post-user";
const FILTER_REPLY_USER_KEY = "greasyfork-discussions-filter-reply-user";
const filterScript = PopsPanel.getValue(FILTER_SCRIPT_KEY, "");
const filterPostUser = PopsPanel.getValue(FILTER_POST_USER_KEY, "");
const filterReplyUser = PopsPanel.getValue(FILTER_REPLY_USER_KEY, "");
const filterScriptList = filterScript.trim() === "" ? [] : filterScript.split("\n");
const filterPostUserList = filterPostUser.trim() === "" ? [] : filterPostUser.split("\n");
const filterReplyUserList = filterReplyUser.trim() === "" ? [] : filterReplyUser.split("\n");
filterScriptList.forEach((ruleValue) => {
this.addValue(
"scriptId",
utils.parseStringToRegExpString("^" + ruleValue + "$")
);
});
filterPostUserList.forEach((ruleValue) => {
this.addValue(
"postUserId",
utils.parseStringToRegExpString("^" + ruleValue + "$")
);
});
filterReplyUserList.forEach((ruleValue) => {
this.addValue(
"replyUserId",
utils.parseStringToRegExpString("^" + ruleValue + "$")
);
});
PopsPanel.deleteValue(FILTER_SCRIPT_KEY);
PopsPanel.deleteValue(FILTER_POST_USER_KEY);
PopsPanel.deleteValue(FILTER_REPLY_USER_KEY);
},
setValue(value) {
PopsPanel.setValue(this.key, value);
},
addValue(key, value) {
let localValue = this.getValue();
if (localValue.trim() !== "") {
localValue += "\n";
}
if (utils.isNull(key)) {
return;
}
key = key.toString().trim();
let rule = key + "##" + value;
localValue += rule;
this.setValue(localValue);
},
getValue() {
return PopsPanel.getValue(this.key, "");
}
};
const SettingUIDiscuessions = {
id: "greasy-fork-panel-config-discussions",
title: i18next.t("论坛"),
forms: [
{
text: "",
type: "forms",
forms: [
{
text: i18next.t("功能"),
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
{
type: "own",
attributes: {
"data-key": "discussions-readBgColor",
"data-default-value": "#e5e5e5"
},
getLiElementCallBack(liElement) {
let key = "discussions-readBgColor";
let $left = domUtils.createElement("div", {
className: "pops-panel-item-left-text",
innerHTML: `
<p class="pops-panel-item-left-main-text">${i18next.t("自定义已读颜色")}</p>
<p class="pops-panel-item-left-desc-text">${i18next.t("在讨论内生效")}</p>
`
});
let $right = domUtils.createElement("div", {
className: "pops-panel-item-right",
innerHTML: `
<input type="color" class="pops-color-choose" />
`
});
let $color = $right.querySelector(
".pops-color-choose"
);
$color.value = PopsPanel.getValue(key);
let $style = domUtils.createElement("style");
domUtils.append(document.head, $style);
domUtils.on(
$color,
["input", "propertychange"],
(event) => {
log.info("选择颜色:" + $color.value);
$style.innerHTML = `
.discussion-read{
background: ${$color.value} !important;
}
`;
PopsPanel.setValue(key, $color.value);
}
);
liElement.appendChild($left);
liElement.appendChild($right);
return liElement;
}
},
UISwitch(
i18next.t("添加【过滤】按钮"),
"discussions-addShortcutOperationButton",
true,
void 0,
i18next.t(
"在每一行讨论的最后面添加【过滤】按钮,需开启过滤功能才会生效"
)
),
UISwitch(
i18next.t("添加【举报】按钮"),
"discussions-addReportButton",
true,
void 0,
i18next.t("在每一行讨论的最后面添加【举报】按钮")
)
]
}
]
},
{
text: i18next.t("过滤"),
type: "deepMenu",
forms: [
{
text: `<a target="_blank" href="https://greasyfork.org/zh-CN/scripts/475722-greasyfork%E4%BC%98%E5%8C%96#:~:text=%E8%AE%BA%E5%9D%9B%E8%BF%87%E6%BB%A4%E8%A7%84%E5%88%99">${i18next.t(
"帮助文档"
)}</a>`,
type: "forms",
forms: [
UISwitch(
i18next.t("启用"),
"greasyfork-discussions-filter-enable",
true,
void 0,
i18next.t("开启后下面的过滤功能才会生效")
),
UISwitch(
i18next.t("过滤重复的评论"),
"greasyfork-discussions-filter-duplicate-comments",
false,
void 0,
i18next.t("过滤掉重复的评论数量(≥2)")
),
{
type: "own",
getLiElementCallBack(liElement) {
let textareaDiv = domUtils.createElement(
"div",
{
className: "pops-panel-textarea",
innerHTML: `
<textarea placeholder="${i18next.t(
"请输入规则,每行一个"
)}" style="height:200px;"></textarea>`
},
{
style: "width: 100%;"
}
);
let $textarea = textareaDiv.querySelector(
"textarea"
);
$textarea.value = GreasyforkDiscussionsFilter.getValue();
domUtils.on(
$textarea,
["input", "propertychange"],
void 0,
utils.debounce(function(event) {
GreasyforkDiscussionsFilter.setValue($textarea.value);
}, 200)
);
liElement.appendChild(textareaDiv);
return liElement;
}
}
]
}
]
}
]
}
]
};
const UIScriptListCSS = '.w-script-list-item {\r\n padding: 10px;\r\n border-bottom: 1px solid #e5e5e5;\r\n font-size: 16px;\r\n text-align: left;\r\n background: var(--aside-bg-color);\r\n border-radius: 8px;\r\n --pops-panel-forms-margin-left-right: 10px;\r\n}\r\n.w-script-version,\r\n.w-script-fan-score,\r\n.w-script-create-time,\r\n.w-script-update-time,\r\n.w-script-locale,\r\n.w-script-sync-type {\r\n font-size: 14px;\r\n color: #7c7c7c;\r\n}\r\n.w-script-fan-score {\r\n margin-left: unset !important;\r\n text-align: unset !important;\r\n max-width: unset !important;\r\n}\r\n.w-script-deleted {\r\n text-decoration: line-through;\r\n font-style: italic;\r\n color: red;\r\n}\r\n.w-script-deleted .w-script-name::before {\r\n content: "【删除】";\r\n}\r\n\r\nli[data-key="user"] .pops-panel-input,\r\nli[data-key="pwd"] .pops-panel-input {\r\n max-width: 200px;\r\n}\r\n';
const SettingUIUsers = {
id: "greasy-fork-panel-config-account",
title: i18next.t("用户"),
forms: [
{
text: "",
type: "forms",
forms: [
{
text: i18next.t("功能"),
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
i18next.t("迁移【控制台】到顶部导航栏"),
"users-changeConsoleToTopNavigator",
true,
void 0,
i18next.t("将【控制台】按钮移动到顶部导航栏,节省空间")
)
]
}
]
},
{
text: i18next.t("美化"),
type: "deepMenu",
forms: [
{
text: "",
type: "forms",
forms: [
UISwitch(
i18next.t("美化私信页面"),
"conversations-beautifyDialogBox",
true,
void 0,
i18next.t("美化为左右对话模式")
),
UISwitch(
i18next.t("美化私信列表"),
"conversations-beautifyPrivateMessageList",
true
)
]
}
]
}
]
}
]
};
const GithubUrl2WebhookUrl = {
init() {
},
/**
* 显示视图
*/
showView() {
let $alert = __pops.alert({
title: {
text: i18next.t("Url To WebhookUrl"),
position: "center"
},
content: {
text: (
/*html*/
`
<div class="github-2-webhook-container">
<div class="url-container">
<h4>Github Url</h4>
<div class="url-parse url-parse-link">
<label>${i18next.t("转换前")}</label>
<textarea id="github" placeholder="${i18next.t("例如:") + "https://github.com/WhiteSevs/TamperMonkeyScript/blob/master/README.md"}"></textarea>
</div>
<div class="url-parse url-parse-result">
<label>${i18next.t("转换后")}</label>
<textarea id="webhook" placeholder="${i18next.t("结果:") + "https://raw.githubusercontent.com/WhiteSevs/TamperMonkeyScript/master/README.md"}" readonly></textarea>
</div>
</div>
</div>
`
),
html: true
},
btn: {
ok: {
type: "default",
text: i18next.t("关闭")
}
},
mask: {
enable: true,
clickEvent: {
toClose: false,
toHide: false
}
},
drag: true,
width: window.innerWidth > 500 ? "500px" : "88vw",
height: window.innerHeight > 500 ? "500px" : "80vh",
style: (
/*css*/
`
.github-2-webhook-container{
display: flex;
flex-direction: column;
height: 100%;
}
.url-container{
display: flex;
flex-direction: column;
gap: 10px;
padding: 20px;
flex: 1;
}
.url-parse{
display: flex;
flex-direction: column;
flex: 1;
}
.url-container textarea{
height: 100%;
width: 100%;
position: relative;
display: block;
resize: none;
padding: 5px 11px;
box-sizing: border-box;
font-size: inherit;
font-family: inherit;
background-color: rgb(255, 255, 255, var(--pops-bg-opacity));
background-image: none;
-webkit-appearance: none;
appearance: none;
box-shadow: 0 0 0 1px #dcdfe6 inset;
border-radius: 0;
transition: box-shadow 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
border: none;
}
.url-container textarea:hover,
.url-container textarea:focus{
outline: 0;
box-shadow: 0 0 0 1px #409eff inset;
}
`
)
});
let $githubUrlInput = $alert.$shadowRoot.querySelector("#github");
let $webhookUrlInput = $alert.$shadowRoot.querySelector("#webhook");
domUtils.on($githubUrlInput, ["input", "propertychange"], (event) => {
let githubUrl = $githubUrlInput.value.trim();
let webhookUrlList = [];
githubUrl.split("\n").forEach((urlStr) => {
try {
urlStr = urlStr.trim();
if (utils.isNull(urlStr)) {
return;
}
let urlObj2 = new URL(urlStr);
let urlPathNameSplit = urlObj2.pathname.split("/");
let {
1: userName,
2: repoName,
3: blobStr,
4: branchName
} = urlPathNameSplit;
let filePath = "";
if (urlPathNameSplit.length >= 6 && blobStr === "blob") {
filePath = urlPathNameSplit.slice(5, urlPathNameSplit.length).join("/");
} else if (urlPathNameSplit.length >= 8 && blobStr === "raw" && branchName === "refs") {
branchName = urlPathNameSplit[6];
filePath = urlPathNameSplit.slice(7, urlPathNameSplit.length).join("/");
} else {
return;
}
if (filePath === "") {
return;
}
webhookUrlList.push(
`https://raw.githubusercontent.com/${userName}/${repoName}/${branchName}/${filePath}`
);
} catch (error) {
}
});
$webhookUrlInput.value = webhookUrlList.join("\n");
});
}
};
const SettingUIScriptSearch = {
id: "greasy-fork-panel-config-script-search",
title: i18next.t("搜索"),
forms: [
{
type: "forms",
text: "",
forms: [
UISwitch(
i18next.t("新增【{{buttonText}}】按钮", {
buttonText: i18next.t("名称-全词匹配")
}),
"gf-script-search-filterScriptTitleWholeWordMatching",
true,
void 0,
i18next.t("该Checkbox按钮开启后,自动过滤出包含搜索关键词的脚本")
),
UISwitch(
i18next.t("新增【{{buttonText}}】按钮", {
buttonText: i18next.t("描述-全词匹配")
}),
"gf-script-search-filterScriptDescWholeWordMatching",
true,
void 0,
i18next.t("该Checkbox按钮开启后,自动过滤出包含搜索关键词的脚本")
),
UISwitch(
i18next.t("新增【{{buttonText}}】按钮", {
buttonText: i18next.t("作者名称-全词匹配")
}),
"gf-script-search-filterScriptAuthorNameWholeWordMatching",
true,
void 0,
i18next.t("该Checkbox按钮开启后,自动过滤出包含搜索关键词的脚本")
)
]
}
]
};
const PopsPanel = {
/** 数据 */
$data: {
__data: null,
__oneSuccessExecMenu: null,
__onceExec: null,
__listenData: null,
/**
* 菜单项的默认值
*/
get data() {
if (PopsPanel.$data.__data == null) {
PopsPanel.$data.__data = new utils.Dictionary();
}
return PopsPanel.$data.__data;
},
/**
* 成功只执行了一次的项
*/
get oneSuccessExecMenu() {
if (PopsPanel.$data.__oneSuccessExecMenu == null) {
PopsPanel.$data.__oneSuccessExecMenu = new utils.Dictionary();
}
return PopsPanel.$data.__oneSuccessExecMenu;
},
/**
* 成功只执行了一次的项
*/
get onceExec() {
if (PopsPanel.$data.__onceExec == null) {
PopsPanel.$data.__onceExec = new utils.Dictionary();
}
return PopsPanel.$data.__onceExec;
},
/** 脚本名,一般用在设置的标题上 */
get scriptName() {
return SCRIPT_NAME;
},
/** 菜单项的总值在本地数据配置的键名 */
key: KEY,
/** 菜单项在attributes上配置的菜单键 */
attributeKeyName: ATTRIBUTE_KEY,
/** 菜单项在attributes上配置的菜单默认值 */
attributeDefaultValueName: ATTRIBUTE_DEFAULT_VALUE
},
/** 监听器 */
$listener: {
/**
* 值改变的监听器
*/
get listenData() {
if (PopsPanel.$data.__listenData == null) {
PopsPanel.$data.__listenData = new utils.Dictionary();
}
return PopsPanel.$data.__listenData;
}
},
init() {
this.initPanelDefaultValue();
this.initExtensionsMenu();
},
/** 判断是否是顶层窗口 */
isTopWindow() {
return _unsafeWindow.top === _unsafeWindow.self;
},
initExtensionsMenu() {
if (!this.isTopWindow()) {
return;
}
GM_Menu.add([
{
key: "show_pops_panel_setting",
text: i18next.t("⚙ 设置"),
autoReload: false,
isStoreValue: false,
showText(text) {
return text;
},
callback: () => {
this.showPanel();
}
},
{
key: "githubUrl2webhookUrl",
text: "⚙ " + i18next.t("Url To WebhookUrl"),
autoReload: false,
isStoreValue: false,
showText(text) {
return text;
},
callback: () => {
GithubUrl2WebhookUrl.showView();
}
}
]);
},
/** 初始化菜单项的默认值保存到本地数据中 */
initPanelDefaultValue() {
let that = this;
function initDefaultValue(config) {
if (!config.attributes) {
return;
}
let needInitConfig = {};
let key = config.attributes[ATTRIBUTE_KEY];
if (key != null) {
needInitConfig[key] = config.attributes[ATTRIBUTE_DEFAULT_VALUE];
}
let __attr_init__ = config.attributes[ATTRIBUTE_INIT];
if (typeof __attr_init__ === "function") {
let __attr_result__ = __attr_init__();
if (typeof __attr_result__ === "boolean" && !__attr_result__) {
return;
}
}
let initMoreValue = config.attributes[ATTRIBUTE_INIT_MORE_VALUE];
if (initMoreValue && typeof initMoreValue === "object") {
Object.assign(needInitConfig, initMoreValue);
}
let needInitConfigList = Object.keys(needInitConfig);
if (!needInitConfigList.length) {
log.warn(["请先配置键", config]);
return;
}
needInitConfigList.forEach((__key) => {
let __defaultValue = needInitConfig[__key];
if (that.$data.data.has(__key)) {
log.warn("请检查该key(已存在): " + __key);
}
that.$data.data.set(__key, __defaultValue);
});
}
function loopInitDefaultValue(configList) {
for (let index = 0; index < configList.length; index++) {
let configItem = configList[index];
initDefaultValue(configItem);
let childForms = configItem.forms;
if (childForms && Array.isArray(childForms)) {
loopInitDefaultValue(childForms);
}
}
}
let contentConfigList = this.getPanelContentConfig();
for (let index = 0; index < contentConfigList.length; index++) {
let leftContentConfigItem = contentConfigList[index];
if (!leftContentConfigItem.forms) {
continue;
}
let rightContentConfigList = leftContentConfigItem.forms;
if (rightContentConfigList && Array.isArray(rightContentConfigList)) {
loopInitDefaultValue(rightContentConfigList);
}
}
},
/**
* 设置值
* @param key 键
* @param value 值
*/
setValue(key, value) {
let locaData = _GM_getValue(KEY, {});
let oldValue = locaData[key];
locaData[key] = value;
_GM_setValue(KEY, locaData);
if (this.$listener.listenData.has(key)) {
this.$listener.listenData.get(key).callback(key, oldValue, value);
}
},
/**
* 获取值
* @param key 键
* @param defaultValue 默认值
*/
getValue(key, defaultValue) {
let locaData = _GM_getValue(KEY, {});
let localValue = locaData[key];
if (localValue == null) {
if (this.$data.data.has(key)) {
return this.$data.data.get(key);
}
return defaultValue;
}
return localValue;
},
/**
* 删除值
* @param key 键
*/
deleteValue(key) {
let locaData = _GM_getValue(KEY, {});
let oldValue = locaData[key];
Reflect.deleteProperty(locaData, key);
_GM_setValue(KEY, locaData);
if (this.$listener.listenData.has(key)) {
this.$listener.listenData.get(key).callback(key, oldValue, void 0);
}
},
/**
* 监听调用setValue、deleteValue
* @param key 需要监听的键
* @param callback
*/
addValueChangeListener(key, callback, option) {
let listenerId = Math.random();
this.$listener.listenData.set(key, {
id: listenerId,
key,
callback
});
if (option) {
if (option.immediate) {
callback(key, this.getValue(key), this.getValue(key));
}
}
return listenerId;
},
/**
* 移除监听
* @param listenerId 监听的id
*/
removeValueChangeListener(listenerId) {
let deleteKey = null;
for (const [key, value] of this.$listener.listenData.entries()) {
if (value.id === listenerId) {
deleteKey = key;
break;
}
}
if (typeof deleteKey === "string") {
this.$listener.listenData.delete(deleteKey);
} else {
console.warn("没有找到对应的监听器");
}
},
/**
* 主动触发菜单值改变的回调
* @param key 菜单键
* @param newValue 想要触发的新值,默认使用当前值
* @param oldValue 想要触发的旧值,默认使用当前值
*/
triggerMenuValueChange(key, newValue, oldValue) {
if (this.$listener.listenData.has(key)) {
let listenData = this.$listener.listenData.get(key);
if (typeof listenData.callback === "function") {
let value = this.getValue(key);
let __newValue = value;
let __oldValue = value;
if (typeof newValue !== "undefined" && arguments.length > 1) {
__newValue = newValue;
}
if (typeof oldValue !== "undefined" && arguments.length > 2) {
__oldValue = oldValue;
}
listenData.callback(key, __oldValue, __newValue);
}
}
},
/**
* 判断该键是否存在
* @param key 键
*/
hasKey(key) {
let locaData = _GM_getValue(KEY, {});
return key in locaData;
},
/**
* 自动判断菜单是否启用,然后执行回调
* @param key
* @param callback 回调
* @param [isReverse=false] 逆反判断菜单启用
*/
execMenu(key, callback, isReverse = false) {
if (!(typeof key === "string" || typeof key === "object" && Array.isArray(key))) {
throw new TypeError("key 必须是字符串或者字符串数组");
}
let runKeyList = [];
if (typeof key === "object" && Array.isArray(key)) {
runKeyList = [...key];
} else {
runKeyList.push(key);
}
let value = void 0;
for (let index = 0; index < runKeyList.length; index++) {
const runKey = runKeyList[index];
if (!this.$data.data.has(runKey)) {
log.warn(`${key} 键不存在`);
return;
}
let runValue = PopsPanel.getValue(runKey);
if (isReverse) {
runValue = !runValue;
}
if (!runValue) {
break;
}
value = runValue;
}
if (value) {
callback(value);
}
},
/**
* 自动判断菜单是否启用,然后执行回调,只会执行一次
* @param key
* @param callback 回调
* @param getValueFn 自定义处理获取当前值,值true是启用并执行回调,值false是不执行回调
* @param handleValueChangeFn 自定义处理值改变时的回调,值true是启用并执行回调,值false是不执行回调
*/
execMenuOnce(key, callback, getValueFn, handleValueChangeFn) {
if (typeof key !== "string") {
throw new TypeError("key 必须是字符串");
}
if (!this.$data.data.has(key)) {
log.warn(`${key} 键不存在`);
return;
}
if (this.$data.oneSuccessExecMenu.has(key)) {
return;
}
this.$data.oneSuccessExecMenu.set(key, 1);
let __getValue = () => {
let localValue = PopsPanel.getValue(key);
return typeof getValueFn === "function" ? getValueFn(key, localValue) : localValue;
};
let resultStyleList = [];
let dynamicPushStyleNode = ($style) => {
let __value = __getValue();
let dynamicResultList = [];
if ($style instanceof HTMLStyleElement) {
dynamicResultList = [$style];
} else if (Array.isArray($style)) {
dynamicResultList = [
...$style.filter(
(item) => item != null && item instanceof HTMLStyleElement
)
];
}
if (__value) {
resultStyleList = resultStyleList.concat(dynamicResultList);
} else {
for (let index = 0; index < dynamicResultList.length; index++) {
let $css = dynamicResultList[index];
$css.remove();
dynamicResultList.splice(index, 1);
index--;
}
}
};
let changeCallBack = (currentValue) => {
let resultList = [];
if (currentValue) {
let result = callback(currentValue, dynamicPushStyleNode);
if (result instanceof HTMLStyleElement) {
resultList = [result];
} else if (Array.isArray(result)) {
resultList = [
...result.filter(
(item) => item != null && item instanceof HTMLStyleElement
)
];
}
}
for (let index = 0; index < resultStyleList.length; index++) {
let $css = resultStyleList[index];
$css.remove();
resultStyleList.splice(index, 1);
index--;
}
resultStyleList = [...resultList];
};
this.addValueChangeListener(
key,
(__key, oldValue, newValue) => {
let __newValue = newValue;
if (typeof handleValueChangeFn === "function") {
__newValue = handleValueChangeFn(__key, newValue, oldValue);
}
changeCallBack(__newValue);
}
);
let value = __getValue();
if (value) {
changeCallBack(value);
}
},
/**
* 父子菜单联动,自动判断菜单是否启用,然后执行回调,只会执行一次
* @param key 菜单键
* @param childKey 子菜单键
* @param callback 回调
* @param replaceValueFn 用于修改mainValue,返回undefined则不做处理
*/
execInheritMenuOnce(key, childKey, callback, replaceValueFn) {
let that = this;
const handleInheritValue = (key2, childKey2) => {
let mainValue = that.getValue(key2);
let childValue = that.getValue(childKey2);
if (typeof replaceValueFn === "function") {
let changedMainValue = replaceValueFn(mainValue, childValue);
if (changedMainValue !== void 0) {
return changedMainValue;
}
}
return mainValue;
};
this.execMenuOnce(
key,
callback,
() => {
return handleInheritValue(key, childKey);
},
() => {
return handleInheritValue(key, childKey);
}
);
this.execMenuOnce(
childKey,
() => {
},
() => false,
() => {
this.triggerMenuValueChange(key);
return false;
}
);
},
/**
* 根据key执行一次
* @param key
*/
onceExec(key, callback) {
if (typeof key !== "string") {
throw new TypeError("key 必须是字符串");
}
if (this.$data.onceExec.has(key)) {
return;
}
callback();
this.$data.onceExec.set(key, 1);
},
/**
* 显示设置面板
*/
showPanel() {
__pops.panel({
title: {
text: i18next.t("{{SCRIPT_NAME}}-设置", { SCRIPT_NAME }),
position: "center",
html: false,
style: ""
},
content: this.getPanelContentConfig(),
mask: {
enable: true,
clickEvent: {
toClose: true,
toHide: false
}
},
zIndex() {
let maxZIndex = Utils.getMaxZIndex();
let popsMaxZIndex = __pops.config.InstanceUtils.getPopsMaxZIndex().zIndex;
return Utils.getMaxValue(maxZIndex, popsMaxZIndex) + 100;
},
width: PanelUISize.setting.width,
height: PanelUISize.setting.height,
drag: true,
only: true,
style: `
${UIScriptListCSS}
`
});
},
/**
* 获取配置内容
*/
getPanelContentConfig() {
let configList = [
SettingUIGeneral,
SettingUIScripts,
SettingUIScriptSearch,
SettingUIDiscuessions,
SettingUIUsers
];
return configList;
}
};
const beautifyMarkdownCSS = 'code {\r\n font-family: Menlo, Monaco, Consolas, "Courier New", monospace;\r\n font-size: 0.85em;\r\n color: #000;\r\n background-color: #f0f0f0;\r\n border-radius: 3px;\r\n padding: 0.2em 0;\r\n}\r\ntable {\r\n text-indent: initial;\r\n}\r\ntable {\r\n margin: 10px 0 15px 0;\r\n border-collapse: collapse;\r\n border-spacing: 0;\r\n display: block;\r\n width: 100%;\r\n overflow: auto;\r\n word-break: normal;\r\n word-break: keep-all;\r\n}\r\ncode,\r\npre {\r\n color: #333;\r\n background: 0 0;\r\n font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;\r\n text-align: left;\r\n white-space: pre;\r\n word-spacing: normal;\r\n word-break: normal;\r\n word-wrap: normal;\r\n line-height: 1.4;\r\n -moz-tab-size: 8;\r\n -o-tab-size: 8;\r\n tab-size: 8;\r\n -webkit-hyphens: none;\r\n -moz-hyphens: none;\r\n -ms-hyphens: none;\r\n hyphens: none;\r\n}\r\npre {\r\n padding: 0.8em;\r\n overflow: auto;\r\n border-radius: 3px;\r\n background: #f5f5f5;\r\n}\r\n:not(pre) > code {\r\n padding: 0.1em;\r\n border-radius: 0.3em;\r\n white-space: normal;\r\n background: #f5f5f5;\r\n}\r\nhtml body {\r\n font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans,\r\n sans-serif;\r\n font-size: 16px;\r\n line-height: 1.6;\r\n color: #333;\r\n background-color: #fff;\r\n overflow: initial;\r\n box-sizing: border-box;\r\n word-wrap: break-word;\r\n}\r\nhtml body > :first-child {\r\n margin-top: 0;\r\n}\r\nhtml body h1,\r\nhtml body h2,\r\nhtml body h3,\r\nhtml body h4,\r\nhtml body h5,\r\nhtml body h6 {\r\n line-height: 1.2;\r\n margin-top: 1em;\r\n margin-bottom: 16px;\r\n color: #000;\r\n}\r\nhtml body h1 {\r\n font-size: 2.25em;\r\n font-weight: 300;\r\n padding-bottom: 0.3em;\r\n}\r\nhtml body h2 {\r\n font-size: 1.75em;\r\n font-weight: 400;\r\n padding-bottom: 0.3em;\r\n}\r\nhtml body h3 {\r\n font-size: 1.5em;\r\n font-weight: 500;\r\n}\r\nhtml body h4 {\r\n font-size: 1.25em;\r\n font-weight: 600;\r\n}\r\nhtml body h5 {\r\n font-size: 1.1em;\r\n font-weight: 600;\r\n}\r\nhtml body h6 {\r\n font-size: 1em;\r\n font-weight: 600;\r\n}\r\nhtml body h1,\r\nhtml body h2,\r\nhtml body h3,\r\nhtml body h4,\r\nhtml body h5 {\r\n font-weight: 600;\r\n}\r\nhtml body h5 {\r\n font-size: 1em;\r\n}\r\nhtml body h6 {\r\n color: #5c5c5c;\r\n}\r\nhtml body strong {\r\n color: #000;\r\n}\r\nhtml body del {\r\n color: #5c5c5c;\r\n}\r\nhtml body a:not([href]) {\r\n color: inherit;\r\n}\r\nhtml body a {\r\n text-decoration: underline;\r\n text-underline-offset: 0.2rem;\r\n}\r\nhtml body a:hover {\r\n color: #00a3f5;\r\n}\r\nhtml body img {\r\n max-width: 100%;\r\n}\r\nhtml body > p {\r\n margin-top: 0;\r\n margin-bottom: 16px;\r\n word-wrap: break-word;\r\n}\r\nhtml body > ol,\r\nhtml body > ul {\r\n margin-bottom: 16px;\r\n}\r\nhtml body ol,\r\nhtml body ul {\r\n padding-left: 2em;\r\n}\r\nhtml body ol.no-list,\r\nhtml body ul.no-list {\r\n padding: 0;\r\n list-style-type: none;\r\n}\r\nhtml body ol ol,\r\nhtml body ol ul,\r\nhtml body ul ol,\r\nhtml body ul ul {\r\n margin-top: 0;\r\n margin-bottom: 0;\r\n}\r\nhtml body li {\r\n margin-bottom: 0;\r\n}\r\nhtml body li.task-list-item {\r\n list-style: none;\r\n}\r\nhtml body li > p {\r\n margin-top: 0;\r\n margin-bottom: 0;\r\n}\r\nhtml body .task-list-item-checkbox {\r\n margin: 0 0.2em 0.25em -1.8em;\r\n vertical-align: middle;\r\n}\r\nhtml body .task-list-item-checkbox:hover {\r\n cursor: pointer;\r\n}\r\nhtml body blockquote {\r\n margin: 16px 0;\r\n font-size: inherit;\r\n padding: 0 15px;\r\n color: #5c5c5c;\r\n background-color: #f0f0f0;\r\n border-left: 4px solid #d6d6d6 !important;\r\n}\r\nhtml body blockquote > :first-child {\r\n margin-top: 0;\r\n}\r\nhtml body blockquote > :last-child {\r\n margin-bottom: 0;\r\n}\r\nhtml body hr {\r\n height: 4px;\r\n margin: 32px 0;\r\n background-color: #d6d6d6;\r\n border: 0 none;\r\n}\r\nhtml body table {\r\n margin: 10px 0 15px 0;\r\n border-collapse: collapse;\r\n border-spacing: 0;\r\n display: block;\r\n width: 100%;\r\n overflow: auto;\r\n word-break: normal;\r\n word-break: keep-all;\r\n}\r\nhtml body table th {\r\n font-weight: 700;\r\n color: #000;\r\n}\r\nhtml body table td,\r\nhtml body table th {\r\n border: 1px solid #d6d6d6;\r\n padding: 6px 13px;\r\n}\r\nhtml body dl {\r\n padding: 0;\r\n}\r\nhtml body dl dt {\r\n padding: 0;\r\n margin-top: 16px;\r\n font-size: 1em;\r\n font-style: italic;\r\n font-weight: 700;\r\n}\r\nhtml body dl dd {\r\n padding: 0 16px;\r\n margin-bottom: 16px;\r\n}\r\nhtml body code {\r\n font-family: Menlo, Monaco, Consolas, "Courier New", monospace;\r\n font-size: 0.85em;\r\n color: #000;\r\n background-color: #f0f0f0;\r\n border-radius: 3px;\r\n padding: 0.2em 0;\r\n}\r\nhtml body code::after,\r\nhtml body code::before {\r\n letter-spacing: -0.2em;\r\n content: "\\00a0";\r\n}\r\nhtml body pre > code {\r\n padding: 0;\r\n margin: 0;\r\n word-break: normal;\r\n white-space: pre;\r\n background: 0 0;\r\n border: 0;\r\n}\r\nhtml body .highlight {\r\n margin-bottom: 16px;\r\n}\r\nhtml body .highlight pre,\r\nhtml body pre {\r\n padding: 1em;\r\n overflow: auto;\r\n line-height: 1.45;\r\n border: #d6d6d6;\r\n border-radius: 3px;\r\n}\r\nhtml body .highlight pre {\r\n margin-bottom: 0;\r\n word-break: normal;\r\n}\r\nhtml body pre code,\r\nhtml body pre tt {\r\n display: inline;\r\n max-width: initial;\r\n padding: 0;\r\n margin: 0;\r\n overflow: initial;\r\n line-height: inherit;\r\n word-wrap: normal;\r\n background-color: transparent;\r\n border: 0;\r\n}\r\nhtml body pre code:after,\r\nhtml body pre code:before,\r\nhtml body pre tt:after,\r\nhtml body pre tt:before {\r\n content: normal;\r\n}\r\nhtml body blockquote,\r\nhtml body dl,\r\nhtml body ol,\r\nhtml body p,\r\nhtml body pre,\r\nhtml body ul {\r\n margin-top: 0;\r\n margin-bottom: 16px;\r\n}\r\nhtml body kbd {\r\n color: #000;\r\n border: 1px solid #d6d6d6;\r\n border-bottom: 2px solid #c7c7c7;\r\n padding: 2px 4px;\r\n background-color: #f0f0f0;\r\n border-radius: 3px;\r\n}\r\n@media print {\r\n html body {\r\n background-color: #fff;\r\n }\r\n html body h1,\r\n html body h2,\r\n html body h3,\r\n html body h4,\r\n html body h5,\r\n html body h6 {\r\n color: #000;\r\n page-break-after: avoid;\r\n }\r\n html body blockquote {\r\n color: #5c5c5c;\r\n }\r\n html body pre {\r\n page-break-inside: avoid;\r\n }\r\n html body table {\r\n display: table;\r\n }\r\n html body img {\r\n display: block;\r\n max-width: 100%;\r\n max-height: 100%;\r\n }\r\n html body code,\r\n html body pre {\r\n word-wrap: break-word;\r\n white-space: pre;\r\n }\r\n}\r\n/* 强制换行 */\r\ncode {\r\n text-wrap: wrap !important;\r\n}\r\n\r\n.scrollbar-style::-webkit-scrollbar {\r\n width: 8px;\r\n}\r\n.scrollbar-style::-webkit-scrollbar-track {\r\n border-radius: 10px;\r\n background-color: transparent;\r\n}\r\n.scrollbar-style::-webkit-scrollbar-thumb {\r\n border-radius: 5px;\r\n background-color: rgba(150, 150, 150, 0.66);\r\n border: 4px solid rgba(150, 150, 150, 0.66);\r\n background-clip: content-box;\r\n}\r\n';
const beautifyButtonCSS = '/* 美化按钮 */\r\ninput[type="submit"],\r\nbutton {\r\n display: inline-flex;\r\n justify-content: center;\r\n align-items: center;\r\n line-height: 1;\r\n height: 32px;\r\n white-space: nowrap;\r\n cursor: pointer;\r\n /* color: #606266; */\r\n text-align: center;\r\n box-sizing: border-box;\r\n outline: none;\r\n transition: 0.1s;\r\n font-weight: 500;\r\n user-select: none;\r\n vertical-align: middle;\r\n appearance: none;\r\n -webkit-appearance: none;\r\n background-color: #ffffff;\r\n border: 1px solid #dcdfe6;\r\n border-color: #dcdfe6;\r\n padding: 8px 15px;\r\n font-size: 14px;\r\n border-radius: 4px;\r\n}\r\n\r\ninput[type="submit"]:hover,\r\ninput[type="submit"]:focus,\r\nbutton:hover,\r\nbutton:focus {\r\n color: #409eff;\r\n border-color: #c6e2ff;\r\n background-color: #ecf5ff;\r\n outline: none;\r\n}\r\n\r\ninput[type="url"] {\r\n position: relative;\r\n font-size: 14px;\r\n display: inline-flex;\r\n line-height: 32px;\r\n box-sizing: border-box;\r\n vertical-align: middle;\r\n appearance: none;\r\n -webkit-appearance: none;\r\n /* color: #606266; */\r\n padding: 0;\r\n outline: none;\r\n border: none;\r\n background: none;\r\n flex-grow: 1;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 1px 11px;\r\n background-color: #ffffff;\r\n background-image: none;\r\n border-radius: 4px;\r\n cursor: text;\r\n transition: box-shadow 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);\r\n transform: translateZ(0);\r\n box-shadow: 0 0 0 1px #dcdfe6 inset;\r\n\r\n width: 100%;\r\n width: -moz-available;\r\n width: -webkit-fill-available;\r\n width: fill-available;\r\n}\r\n\r\ninput[type="url"]::placeholder {\r\n color: #a8abb2;\r\n}\r\n\r\ninput[type="url"]:hover {\r\n box-shadow: 0 0 0 1px #c0c4cc inset;\r\n}\r\n\r\ninput[type="url"]:focus {\r\n box-shadow: 0 0 0 1px #409eff inset;\r\n}\r\n';
const beautifyRadioCSS = 'label.radio-label {\r\n font-weight: 500;\r\n position: relative;\r\n cursor: pointer;\r\n display: inline-flex;\r\n align-items: center;\r\n white-space: normal;\r\n outline: none;\r\n font-size: 14px;\r\n user-select: none;\r\n margin-right: 32px;\r\n height: 32px;\r\n padding: 4px;\r\n border-radius: 4px;\r\n box-sizing: border-box;\r\n}\r\nlabel:has(input[type="radio"]:checked),\r\nlabel:has(input[type="radio"]:checked) a {\r\n color: #409eff;\r\n}\r\nlabel.radio-label input[type="radio"] {\r\n margin-right: 4px;\r\n width: 14px;\r\n height: 14px;\r\n}\r\nlabel.radio-label input[type="radio"]:checked {\r\n -webkit-appearance: none;\r\n -moz-appearance: none;\r\n appearance: none;\r\n border-radius: 50%;\r\n width: 14px;\r\n height: 14px;\r\n outline: none;\r\n border: 4px solid #409eff;\r\n cursor: pointer;\r\n}\r\nlabel.radio-label input[type="radio"]:checked + span {\r\n color: #409eff;\r\n}\r\n';
const beautifyInputCSS = 'input[type="search"],\r\ninput[type="text"],\r\ninput[type="password"] {\r\n justify-content: center;\r\n align-items: center;\r\n /* line-height: 1; */\r\n /* height: 32px; */\r\n white-space: nowrap;\r\n cursor: text;\r\n text-align: center;\r\n box-sizing: border-box;\r\n outline: 0;\r\n transition: 0.1s;\r\n /* font-weight: 500; */\r\n user-select: none;\r\n -webkit-user-select: none;\r\n -moz-user-select: none;\r\n -ms-user-select: none;\r\n vertical-align: middle;\r\n -webkit-appearance: none;\r\n appearance: none;\r\n background-color: transparent;\r\n border: 0;\r\n padding: 8px 8px;\r\n /* font-size: 14px; */\r\n text-align: start;\r\n /* width: 100%; */\r\n flex: 1;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n}\r\ninput[type="search"]:hover,\r\ninput[type="text"]:hover,\r\ninput[type="password"]:hover {\r\n box-shadow: 0 0 0 1px #c0c4cc;\r\n}\r\ninput[type="search"]:focus,\r\ninput[type="text"]:focus,\r\ninput[type="password"]:focus {\r\n outline: 0;\r\n border: 1px solid #409eff;\r\n border-radius: 4px;\r\n box-shadow: none;\r\n}\r\n';
const beautifyTextAreaCSS = "textarea {\r\n display: block;\r\n position: relative;\r\n /*vertical-align: bottom;*/\r\n position: relative;\r\n resize: vertical;\r\n padding: 5px 11px;\r\n line-height: 1.5;\r\n box-sizing: border-box;\r\n width: 100%;\r\n font-size: inherit;\r\n font-family: inherit;\r\n /* color: #606266; */\r\n background-color: #ffffff;\r\n background-image: none;\r\n appearance: none;\r\n -webkit-appearance: none;\r\n box-shadow: 0 0 0 1px #dcdfe6 inset;\r\n border-radius: 4px;\r\n transition: box-shadow 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);\r\n border: none;\r\n}\r\ntextarea:focus {\r\n outline: none;\r\n box-shadow: 0 0 0 1px #409eff inset;\r\n}\r\n";
const beautifyUploadImageCSS = '/* 隐藏 添加: */\r\nlabel[for="discussion_comments_attributes_0_attachments"],\r\nlabel[for="comment_attachments"] {\r\n display: none;\r\n}\r\ninput[type="file"] {\r\n width: 100%;\r\n font-size: 20px;\r\n background: #e2e2e2;\r\n padding: 40px 0px;\r\n border-radius: 10px;\r\n text-align-last: center;\r\n}\r\n';
const compatibleBeautifyCSS = "#main-header {\r\n background-color: #670000 !important;\r\n background-image: linear-gradient(#670000, #990000) !important;\r\n}\r\n#site-nav-vue {\r\n flex-wrap: wrap;\r\n justify-content: flex-end;\r\n}\r\n.open-sidebar {\r\n border-width: 1px;\r\n border-radius: 3px;\r\n margin-right: 0;\r\n}\r\ninput.search-submit {\r\n transform: translateY(-5%) !important;\r\n margin-left: 10px;\r\n}\r\n#script-content code {\r\n word-wrap: break-word;\r\n}\r\n.code-container ::selection {\r\n background-color: #3d4556 !important;\r\n}\r\n";
const beautifyTopNavigationBarCSS = "#language-selector {\r\n display: none;\r\n}\r\n@media screen and (min-width: 600px) {\r\n body {\r\n --header-height: 50px;\r\n --el-gap: 20px;\r\n }\r\n\r\n header#main-header {\r\n height: var(--header-height);\r\n position: fixed;\r\n top: 0;\r\n width: 100%;\r\n z-index: 55555;\r\n padding: unset;\r\n display: flex;\r\n justify-content: space-around;\r\n }\r\n\r\n body > .width-constraint {\r\n margin-top: calc(var(--header-height) + 35px);\r\n }\r\n\r\n header#main-header .width-constraint {\r\n display: flex;\r\n align-items: center;\r\n gap: var(--el-gap);\r\n padding: unset;\r\n margin: unset;\r\n max-width: unset;\r\n }\r\n\r\n header#main-header a {\r\n text-decoration: none;\r\n text-wrap: nowrap;\r\n }\r\n\r\n header#main-header .sign-out-link a {\r\n text-decoration: underline;\r\n }\r\n\r\n header#main-header #site-name {\r\n display: flex;\r\n align-items: center;\r\n }\r\n\r\n header#main-header #site-name img {\r\n width: calc(var(--header-height) - 5px);\r\n height: calc(var(--header-height) - 5px);\r\n }\r\n\r\n /* 隐藏Greasyfork文字 */\r\n header#main-header #site-name-text {\r\n display: none;\r\n }\r\n\r\n header#main-header #site-nav {\r\n display: flex;\r\n flex-direction: row-reverse;\r\n align-items: center;\r\n flex: 1;\r\n justify-content: space-between;\r\n height: 100%;\r\n gap: var(--el-gap);\r\n }\r\n\r\n header#main-header #site-nav nav li {\r\n padding: 0 0.5em;\r\n display: flex;\r\n align-items: center;\r\n height: var(--header-height);\r\n min-width: 30px;\r\n justify-content: center;\r\n }\r\n\r\n header#main-header #site-nav nav li:hover {\r\n background: #5f0101;\r\n }\r\n\r\n header#main-header #nav-user-info {\r\n max-width: 150px;\r\n }\r\n\r\n header#main-header #nav-user-info > span {\r\n /*flex: 1;*/\r\n flex: 1 0 auto;\r\n }\r\n\r\n header#main-header #nav-user-info,\r\n header#main-header #nav-user-info + nav {\r\n position: unset;\r\n width: unset;\r\n display: flex;\r\n flex-wrap: nowrap;\r\n align-items: center;\r\n }\r\n}\r\n";
const GreasyforkBeautify = {
init() {
PopsPanel.execMenuOnce("beautifyPage", () => {
return this.beautifyPageElement();
});
PopsPanel.execMenuOnce("beautifyGreasyforkBeautify", () => {
return this.beautifyGreasyforkBeautify();
});
PopsPanel.execMenuOnce("beautifyUploadImage", () => {
return this.beautifyUploadImage();
});
PopsPanel.execMenuOnce("beautifyTopNavigationBar", () => {
return this.beautifyTopNavigationBar();
});
},
/**
* 美化页面元素
*/
beautifyPageElement() {
log.info("美化页面元素");
let result = [];
result.push(_GM_addStyle(beautifyMarkdownCSS));
result.push(_GM_addStyle(beautifyButtonCSS));
result.push(_GM_addStyle(beautifyRadioCSS));
result.push(_GM_addStyle(beautifyInputCSS));
result.push(_GM_addStyle(beautifyTextAreaCSS));
result.push(
_GM_addStyle(
/*css*/
`
p:has(input[type="submit"][name="update-and-sync"]){
margin-top: 10px;
}
`
)
);
domUtils.ready(function() {
let markupChoiceELement = document.querySelector(
'a[target="markup_choice"][href*="daringfireball.net"]'
);
if (markupChoiceELement) {
markupChoiceELement.parentElement.replaceChild(
domUtils.createElement("span", {
textContent: "Markdown"
}),
markupChoiceELement
);
}
if (globalThis.location.pathname.endsWith("/admin") && !document.querySelector('input[type="submit"][name="update-only"]')) {
result.push(
_GM_addStyle(
/*css*/
`
.indented{
padding-left: unset;
}
`
)
);
}
});
return result;
},
/**
* 美化 Greasyfork Beautify脚本
*/
beautifyGreasyforkBeautify() {
log.info("美化 Greasyfork Beautify脚本");
let result = [];
result.push(_GM_addStyle(compatibleBeautifyCSS));
if (utils.isPhone()) {
result.push(
_GM_addStyle(
/*css*/
`
section#script-info,
section.text-content,
div.width-constraint table.text-content.log-table{
margin-top: 80px;
}
div.width-constraint div.sidebarred{
padding-top: 80px;
}
div.width-constraint div.sidebarred .sidebar{
top: 80px;
}`
)
);
} else {
result.push(
_GM_addStyle(
/*css*/
`
section#script-info{
margin-top: 10px;
}`
)
);
}
return result;
},
/**
* 美化上传图片
*/
beautifyUploadImage() {
log.info("美化上传图片");
let result = [];
result.push(_GM_addStyle(beautifyUploadImageCSS));
domUtils.ready(() => {
function clearErrorTip(element) {
while (element.nextElementSibling) {
element.parentElement.removeChild(element.nextElementSibling);
}
}
let $fileInputList = document.querySelectorAll('input[type="file"]');
$fileInputList.forEach(($input) => {
if ($input.getAttribute("name") === "code_upload") {
return;
}
if ($input.hasAttribute("accept") && $input.getAttribute("accept").includes("javascript")) {
return;
}
domUtils.on($input, ["propertychange", "input"], function(event) {
clearErrorTip(event.target);
let chooseImageFiles = event.currentTarget.files;
if (!chooseImageFiles) {
return;
}
if (chooseImageFiles.length === 0) {
return;
}
log.info(["选择的图片", chooseImageFiles]);
if (chooseImageFiles.length > 5) {
domUtils.after(
$input,
domUtils.createElement("p", {
textContent: i18next.t(`❌ 最多同时长传5张图片`)
})
);
}
let notAllowImage = [];
Array.from(chooseImageFiles).forEach((imageFile) => {
if (imageFile.size > 204800 || !imageFile.type.match(/png|jpg|jpeg|gif|apng|webp/i)) {
notAllowImage.push(imageFile);
}
});
if (notAllowImage.length === 0) {
return;
}
notAllowImage.forEach((imageFile) => {
domUtils.after(
$input,
domUtils.createElement("p", {
textContent: i18next.t("❌ 图片:{{name}} 大小:{{size}}", {
name: imageFile.name,
size: imageFile.size
})
})
);
});
});
});
let $textAreaSelectorString = [
/* 某条反馈内的回复框 */
"textarea#comment_text",
/* 反馈页面的回复框 */
"textarea.comment-entry"
];
$textAreaSelectorString.forEach((selector) => {
domUtils.on(selector, "paste", (event) => {
log.info(["触发粘贴事件", event]);
setTimeout(() => {
domUtils.trigger($fileInputList, "input");
}, 100);
});
});
});
return result;
},
/**
* 美化顶部导航栏
*/
beautifyTopNavigationBar() {
log.info("美化顶部导航栏");
let result = [];
result.push(_GM_addStyle(beautifyTopNavigationBarCSS));
if (window.outerWidth > 550) {
result.push(CommonUtil.addBlockCSS(".with-submenu"));
domUtils.ready(() => {
let $siteNav = document.querySelector("#site-nav");
let $siteNavNav = $siteNav.querySelector("nav");
document.querySelectorAll(".with-submenu nav li").forEach(($ele) => {
$siteNavNav.appendChild($ele);
});
});
}
return result;
}
};
const GreasyforkAccount = {
init() {
PopsPanel.execMenu("autoLogin", () => {
this.autoLogin();
});
},
/**
* 自动登录
*/
autoLogin() {
utils.waitNode("span.sign-in-link a[rel=nofollow]").then(async () => {
let user = PopsPanel.getValue("user");
let pwd = PopsPanel.getValue("pwd");
if (utils.isNull(user)) {
Qmsg.error(i18next.t("请先在菜单中录入账号"));
return;
}
if (utils.isNull(pwd)) {
Qmsg.error(i18next.t("请先在菜单中录入密码"));
return;
}
let csrfToken = document.querySelector("meta[name='csrf-token']");
if (!csrfToken) {
Qmsg.error(i18next.t("获取csrf-token失败"));
return;
}
let loginTip = Qmsg.loading(i18next.t("正在登录中..."));
let postResp = await httpx.post(
"https://greasyfork.org/zh-CN/users/sign_in",
{
fetch: true,
data: encodeURI(
`authenticity_token=${csrfToken.getAttribute(
"content"
)}&user[email]=${user}&user[password]=${pwd}&user[remember_me]=1&commit=登录`
),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
}
);
loginTip.destroy();
if (!postResp.status) {
log.error(postResp);
Qmsg.error(i18next.t("登录失败,请在控制台查看原因"));
return;
}
let respText = postResp.data.responseText;
let parseLoginHTMLNode = domUtils.parseHTML(respText, true, true);
if (parseLoginHTMLNode.querySelectorAll(
".sign-out-link a[rel=nofollow][data-method='delete']"
).length) {
Qmsg.success(i18next.t("登录成功,1s后自动跳转"));
setTimeout(() => {
window.location.reload();
}, 1e3);
} else {
log.error(postResp);
log.error(`当前账号:${user}`);
log.error(`当前密码:${pwd}`);
Qmsg.error(
i18next.t("登录失败,可能是账号/密码错误,请在控制台查看原因")
);
}
});
}
};
const GreasyforkForum = {
init() {
this.readBgColor();
domUtils.ready(() => {
PopsPanel.execMenuOnce("greasyfork-discussions-filter-enable", () => {
this.filterEnable();
});
PopsPanel.execMenuOnce("discussions-addShortcutOperationButton", () => {
this.addShortcutOperationButton();
});
PopsPanel.execMenuOnce("discussions-addReportButton", () => {
this.addReportButton();
});
});
},
/**
* 启用Greasyfork论坛过滤器
*/
filterEnable() {
log.info("启用Greasyfork论坛过滤器");
GreasyforkDiscussionsFilter.init();
},
/**
* 设置已读背景颜色
*/
readBgColor() {
log.info("设置已读背景颜色");
let color = PopsPanel.getValue("discussions-readBgColor");
_GM_addStyle(
/*css*/
`
.discussion-read{
background: ${color} !important;
}
`
);
},
/**
* 添加【过滤】按钮
*/
addShortcutOperationButton() {
log.info("添加【过滤】按钮");
GreasyforkDiscussionsFilter.getElementList().forEach(($listContainer) => {
if ($listContainer.querySelector(
".discussion-filter-button"
)) {
return;
}
let $listItem = $listContainer.querySelector(
".discussion-list-item"
);
let $meta = $listItem.querySelector(".discussion-meta");
let $ownMetaItem = domUtils.createElement(
"div",
{
className: "discussion-meta-item",
innerHTML: `
<button class="discussion-filter-button">${i18next.t("过滤")}</button>
`
},
{
style: "flex: 0;"
}
);
let $button = $ownMetaItem.querySelector(
".discussion-filter-button"
);
$meta.appendChild($ownMetaItem);
domUtils.on($button, "click", (event) => {
var _a2, _b, _c;
utils.preventEvent(event);
const discussionInfo = GreasyforkDiscussionsFilter.parseDiscuessionListContainerInfo(
$listContainer
);
let attr_filter_key = "data-filter-key";
let attr_filter_value = "data-filter-value";
let $dialog = __pops.alert({
title: {
text: i18next.t("选择需要过滤的选项"),
position: "center",
html: false
},
content: {
text: (
/*html*/
`
<button ${attr_filter_key}="scriptId" ${attr_filter_value}="^${discussionInfo.scriptId}$">${i18next.t("脚本id:{{text}}", {
text: discussionInfo.scriptId
})}</button>
<button ${attr_filter_key}="scriptName" ${attr_filter_value}="^${utils.parseStringToRegExpString(
discussionInfo.scriptName
)}$">${i18next.t("脚本名:{{text}}", {
text: discussionInfo.scriptName
})}</button>
<button ${attr_filter_key}="postUserId" ${attr_filter_value}="^${utils.parseStringToRegExpString(
discussionInfo.postUserId
)}$">${i18next.t("发布的用户id:{{text}}", {
text: discussionInfo.postUserId
})}</button>
`
),
html: true
},
mask: {
enable: true,
clickEvent: {
toClose: true
}
},
btn: {
ok: {
enable: false
}
},
drag: true,
dragLimit: true,
width: "350px",
height: "300px",
style: (
/*css*/
`
.pops-alert-content{
display: flex;
flex-direction: column;
gap: 20px;
}
.pops-alert-content button{
text-wrap: wrap;
padding: 8px;
height: auto;
text-align: left;
}
`
)
});
let $content = $dialog.$shadowRoot.querySelector(
".pops-alert-content"
);
if (discussionInfo.scriptId == null) {
(_a2 = $content.querySelector(`button[${attr_filter_key}="scriptId"]`)) == null ? void 0 : _a2.remove();
}
if (discussionInfo.scriptName == null) {
(_b = $content.querySelector(`button[${attr_filter_key}="scriptName"]`)) == null ? void 0 : _b.remove();
}
if (discussionInfo.postUserId == null) {
(_c = $content.querySelector(`button[${attr_filter_key}="postUserId"]`)) == null ? void 0 : _c.remove();
}
if (discussionInfo.replyUserId != null) {
let $replyUserIdButton = domUtils.createElement("button", {
innerHTML: i18next.t("作者id:{{text}}", {
text: discussionInfo.replyUserId
})
});
$replyUserIdButton.setAttribute(attr_filter_key, "scriptAuthorId");
$replyUserIdButton.setAttribute(
attr_filter_value,
"^" + discussionInfo.replyUserId + "$"
);
$content.appendChild($replyUserIdButton);
}
domUtils.on(
$dialog.$shadowRoot,
"click",
`button[${attr_filter_key}]`,
(event2) => {
utils.preventEvent(event2);
let $click = event2.target;
let key = $click.getAttribute(
attr_filter_key
);
let value = $click.getAttribute(attr_filter_value);
GreasyforkDiscussionsFilter.addValue(key, value);
$dialog.close();
GreasyforkDiscussionsFilter.filter();
Qmsg.success(i18next.t("添加成功"));
}
);
});
});
},
/**
* 添加【举报】按钮
*/
addReportButton() {
log.info(`添加【举报】按钮`);
GreasyforkDiscussionsFilter.getElementList().forEach(($listContainer) => {
if ($listContainer.querySelector(".discussion-report-button")) {
return;
}
let $listItem = $listContainer.querySelector(
".discussion-list-item"
);
let $meta = $listItem.querySelector(".discussion-meta");
let $ownMetaItem = domUtils.createElement(
"div",
{
className: "discussion-meta-item",
innerHTML: `
<button class="discussion-report-button" style="border-color: #ff4d4d;background-color: #ffe6e6;color: red;">${i18next.t(
"举报"
)}</button>
`
},
{
style: "flex: 0;"
}
);
let $button = $ownMetaItem.querySelector(
".discussion-report-button"
);
$meta.appendChild($ownMetaItem);
domUtils.on($button, "click", (event) => {
utils.preventEvent(event);
const discussionInfo = GreasyforkDiscussionsFilter.parseDiscuessionListContainerInfo(
$listContainer
);
__pops.alert({
title: {
text: i18next.t("举报"),
position: "center",
html: false
},
content: {
text: (
/*html*/
`
<div class="report-item">
${i18next.t("举报讨论:")}
<a href="${GreasyforkUrlUtils.getReportUrl(
"discussion",
discussionInfo.snippetId
)}" target="_blank">${discussionInfo.snippet}</a>
</div>
${discussionInfo.scriptId ? (
/*html*/
`
<div class="report-item">
${i18next.t("举报脚本:")}
<a href="${GreasyforkUrlUtils.getReportUrl(
"script",
discussionInfo.scriptId
)}" target="_blank">${discussionInfo.scriptName}</a>
</div>
`
) : ""}
<div class="report-item">
${i18next.t("举报用户:")}
<a href="${GreasyforkUrlUtils.getReportUrl(
"user",
discussionInfo.postUserId
)}" target="_blank">${discussionInfo.postUserName}</a>
</div>
${discussionInfo.replyUserId && discussionInfo.replyUserId != discussionInfo.postUserId ? (
/*html*/
`
<div class="report-item">
${i18next.t("举报用户:")}
<a href="${GreasyforkUrlUtils.getReportUrl(
"user",
discussionInfo.replyUserId
)}" target="_blank">${discussionInfo.replyUserName}</a>
</div>
`
) : ""}
`
),
html: true
},
btn: {
ok: {
enable: false
}
},
mask: {
enable: true,
clickEvent: {
toClose: true
}
},
drag: true,
dragLimit: true,
width: "350px",
height: "300px",
style: (
/*css*/
`
.pops-alert-content{
display: flex;
flex-direction: column;
gap: 20px;
}
.pops-alert-content .report-item{
text-wrap: wrap;
padding: 8px;
height: auto;
text-align: left;
margin: var(--button-margin-top) var(--button-margin-right)
var(--button-margin-bottom) var(--button-margin-left);
border-radius: var(--button-radius);
color: var(--button-color);
border-color: var(--button-bd-color);
background-color: var(--button-bg-color);
}
`
)
});
});
});
}
};
const GreasyforkUsers = {
init() {
PopsPanel.execMenuOnce("users-changeConsoleToTopNavigator", () => {
this.changeConsoleToTopNavigator();
});
PopsPanel.execMenuOnce("gf-scripts-filter-enable", () => {
GreasyforkScriptsFilter.init();
});
PopsPanel.execMenuOnce("beautifyCenterContent", () => {
return GreasyforkScriptsList.beautifyCenterContent();
});
},
/**
* 迁移【控制台】到顶部导航栏
*/
changeConsoleToTopNavigator() {
log.info("迁移【控制台】到顶部导航栏");
CommonUtil.addBlockCSS("#about-user");
domUtils.ready(() => {
let $aboutUser = document.querySelector("#about-user");
let $siteNav = document.querySelector("#site-nav nav");
if (!$aboutUser) {
log.error("#about-user元素不存在");
return;
}
if (!$siteNav) {
log.error("#site-nav nav元素不存在");
return;
}
$aboutUser = $aboutUser.cloneNode(true);
let $consoleNav = domUtils.createElement("li", {
className: "scripts-console",
innerHTML: `<a href="javascript:;">${i18next.t("控制台")}</a>`
});
domUtils.on($consoleNav, "click", (event) => {
utils.preventEvent(event);
let $drawer = __pops.drawer({
title: {
enable: false
},
content: {
text: "",
html: true
},
size: "auto",
direction: "top",
zIndex: utils.getMaxZIndex(100),
style: (
/*css*/
`
.text-content{
list-style-type: none;
box-shadow: rgb(221, 221, 221) 0px 0px 5px;
background-color: rgb(255, 255, 255);
box-sizing: border-box;
border-width: 1px;
border-style: solid;
border-color: rgb(187, 187, 187);
border-image: initial;
border-radius: 5px;
margin: 14px 0px;
padding: 10px 40px;
}
a.report-link{
position: absolute;
right: 0px;
font-size: smaller;
margin-right: 16px;
margin-top: 8px;
}
`
)
});
let $drawerContent = $drawer.$shadowRoot.querySelector(
".pops-drawer-content"
);
$drawerContent.appendChild($aboutUser);
});
$siteNav.appendChild($consoleNav);
});
}
};
const beautifyContentCSS = "section.text-content {\r\n /*height: calc(100vh - 100px);*/\r\n /*overflow-y: auto;\r\n overflow-x: hidden;*/\r\n background: #eaf0ff;\r\n}\r\n\r\n.comment .user-content {\r\n border: 1px solid transparent;\r\n background: #fff;\r\n max-width: 70%;\r\n border-radius: 10px;\r\n width: fit-content;\r\n}\r\n\r\n.comment .comment-meta-spacer {\r\n flex: unset;\r\n margin-left: 15px;\r\n}\r\n.comment:not(:has(.report-link)) .comment-meta-spacer {\r\n flex: unset;\r\n margin-left: unset;\r\n margin-right: 10px;\r\n}\r\n.comment:not(:has(.report-link)) {\r\n display: flex;\r\n align-items: flex-end;\r\n flex-direction: column;\r\n}\r\n\r\n.comment:not(:has(.report-link)) .comment-meta {\r\n display: flex;\r\n flex-direction: row-reverse;\r\n}\r\n.comment:not(:has(.report-link)) .comment-meta-item {\r\n margin-left: 0px;\r\n margin-right: 15px;\r\n}";
const GreasyforkConversations = {
init() {
PopsPanel.execMenuOnce("conversations-beautifyDialogBox", () => {
return this.beautifyDialogBox();
});
domUtils.ready(() => {
PopsPanel.execMenuOnce("conversations-beautifyPrivateMessageList", () => {
this.beautifyPrivateMessageList();
});
});
},
/**
* 美化对话框
*/
beautifyDialogBox() {
log.info("美化对话框");
let result = [];
result.push(addStyle(beautifyContentCSS));
},
/**
* 美化私信列表
*/
beautifyPrivateMessageList() {
log.info(`美化私信列表`);
addStyle(
/*css*/
`
.user-conversations-item{
list-style: none;
border: 1px solid #bfbfbf;
border-radius: 4px;
display: flex;
gap: 10px;
flex-direction: column;
padding: 4px 20px;
margin: 10px 0px;
}
.user-conversations-item .user-link-container{
}
.user-conversations-item .user-link-container .user-latest-send-time{
float: right;
}
.user-conversations-item .enter-coversations{
float: right;
}
`
);
document.querySelectorAll("section.text-content ul li").forEach(($li) => {
var _a2;
let $user = $li.querySelector(
'a[href*="conversations"]'
);
let chatUrl = $user.href;
let userName = (_a2 = $user.textContent) == null ? void 0 : _a2.split(" ")[1];
let $latestMsgUser = $li.querySelector("a.user-link");
let latestSendMsgUser = null;
let latestSendMsgUserHomeUrl = null;
let latestSendMsgTimeText = null;
if ($latestMsgUser) {
latestSendMsgUser = $latestMsgUser.textContent;
latestSendMsgUserHomeUrl = $latestMsgUser.href;
let $relativeTime = $li.querySelector("relative-time");
new Date($relativeTime.getAttribute("datetime"));
latestSendMsgTimeText = $relativeTime.shadowRoot.lastChild.textContent;
}
$li.classList.add("user-conversations-item");
GreasyforkUrlUtils.getUserId();
$li.innerHTML = /*html*/
`
<div class="user-link-container">
<a class="user-link" href="${chatUrl}">${userName}</a>
<span class="user-latest-send-time">${latestSendMsgTimeText}</span>
</div>
<div class="latest-send-user-container">
${i18next.t("最后回复:")}
<a href="${latestSendMsgUserHomeUrl}">${latestSendMsgUser}</a>
<a class="enter-coversations" href="${chatUrl}">${i18next.t("进入")}</a>
</div>
`;
});
}
};
const GreasyforkScriptsSearchElement = {
/**
* 等待脚本列表元素
*/
waitScritList() {
return utils.waitNode("#browse-script-list", 1e4);
},
/**
* 添加控制区域
*/
addFilterControls($scriptList) {
function getControls() {
var _a2;
let $el = document.querySelector(
"#gm-script-filter-controls"
);
return (_a2 = $el == null ? void 0 : $el.shadowRoot) == null ? void 0 : _a2.querySelector(".pops");
}
let $controls = getControls();
if ($controls) {
return $controls;
}
let $controlsContainer = domUtils.createElement("div", {
id: "gm-script-filter-controls"
});
let shadowRoot = $controlsContainer.attachShadow({ mode: "open" });
shadowRoot.appendChild(
domUtils.createElement("style", {
innerHTML: (
/*css*/
`
${__pops.config.cssText.index}
${__pops.config.cssText.common}
${__pops.config.cssText.panelCSS}
`
)
})
);
shadowRoot.appendChild(
domUtils.createElement("style", {
innerHTML: (
/*css*/
`
.pops{
display: flex;
align-items: center;
flex-direction: row;
gap: 10px;
padding: 10px;
}
.pops .gm-script-control-item{
display: flex;
align-items: center;
gap: 10px;
}
.pops .pops-panel-item-left-main-text{
display: flex;
align-items: center;
margin: 0px;
padding: 0px;
}
`
)
})
);
let $pops = domUtils.createElement("div", {
className: "pops"
});
shadowRoot.appendChild($pops);
domUtils.before($scriptList, $controlsContainer);
return $pops;
}
};
const GreasyforkScriptsSearch = {
init() {
domUtils.ready(() => {
GreasyforkScriptsSearchElement.waitScritList().then(($scriptList) => {
if (!$scriptList) {
log.error("未找到脚本列表节点,无法继续执行");
return;
}
let $filterControlsContainer = GreasyforkScriptsSearchElement.addFilterControls($scriptList);
this.addFilterControlsItem($filterControlsContainer);
});
});
},
/**
* 添加过滤项
*/
addFilterControlsItem($filterControlsContainer) {
let controlsConfig = [
{
name: i18next.t("名称-全词匹配"),
ENABLE_KEY: "gf-script-search-filterScriptTitleWholeWordMatching",
STORAGE_KEY: "gf-script-search-filterScriptTitleWholeWordMatching-enable",
callback: (searchText, scriptInfo) => {
return !scriptInfo.scriptName.includes(searchText);
}
},
{
name: i18next.t("描述-全词匹配"),
ENABLE_KEY: "gf-script-search-filterScriptDescWholeWordMatching",
STORAGE_KEY: "gf-script-search-filterScriptDescWholeWordMatching-enable",
callback: (searchText, scriptInfo) => {
return !scriptInfo.scriptDescription.includes(searchText);
}
},
{
name: i18next.t("作者名称-全词匹配"),
ENABLE_KEY: "gf-script-search-filterScriptAuthorNameWholeWordMatching",
STORAGE_KEY: "gf-script-search-filterScriptAuthorNameWholeWordMatching-enable",
callback: (searchText, scriptInfo) => {
return !scriptInfo.scriptAuthorName.includes(searchText);
}
}
];
function callback() {
let searchParams2 = new URLSearchParams(window.location.search);
let searchText = searchParams2.get("q").trim();
if (searchText == "") {
return;
}
let allScriptsList = GreasyforkScriptsFilter.getElementList();
allScriptsList.forEach(($scriptList) => {
let scriptInfo = parseScriptListInfo($scriptList);
let fitlerFlagList = controlsConfig.map((controlsConfig2) => {
let enable = _GM_getValue(controlsConfig2.STORAGE_KEY);
if (!enable) {
return;
}
return controlsConfig2.callback(searchText, scriptInfo);
}).filter((item) => typeof item === "boolean");
if (fitlerFlagList.length !== 0) {
let flag = false;
fitlerFlagList.forEach((enable) => {
flag = flag || enable;
});
if (flag) {
domUtils.hide($scriptList, false);
} else {
domUtils.show($scriptList, false);
}
} else {
domUtils.show($scriptList, false);
}
});
}
controlsConfig.forEach((controlConfig) => {
if (!PopsPanel.getValue(controlConfig.ENABLE_KEY)) {
return;
}
log.info(`添加按钮${controlConfig.name}`);
let panelHandleContentUtils = __pops.config.panelHandleContentUtils();
let $controlContainer = panelHandleContentUtils.createSectionContainerItem_switch({
type: "switch",
className: "gm-script-control-item",
text: controlConfig.name,
getValue() {
let value = _GM_getValue(controlConfig.STORAGE_KEY, false);
callback();
return value;
},
callback(event, value) {
_GM_setValue(controlConfig.STORAGE_KEY, value);
callback();
}
});
domUtils.append($filterControlsContainer, $controlContainer);
});
}
};
const Greasyfork = {
init() {
PopsPanel.execMenu("checkPage", () => {
this.checkPage();
});
GreasyforkBeautify.init();
GreasyforkShortCut.init();
if (GreasyforkRouter.isScript()) {
GreasyforkScripts.init();
}
if (GreasyforkRouter.isScriptList() || GreasyforkRouter.isScriptLibraryList() || GreasyforkRouter.isScriptCodeSearch() || GreasyforkRouter.isScriptsBySite()) {
GreasyforkScriptsList.init();
}
if (GreasyforkRouter.isDiscuessions()) {
log.info(`Router: 讨论页面`);
GreasyforkForum.init();
} else if (GreasyforkRouter.isUsers()) {
log.info(`Router: 用户页面`);
GreasyforkUsers.init();
if (GreasyforkRouter.isUsersConversations()) {
log.info(`Router-next: 私聊用户页面`);
GreasyforkConversations.init();
}
} else if (GreasyforkRouter.isScriptSearch()) {
log.info(`Router: 脚本搜索页面`);
GreasyforkScriptsSearch.init();
}
PopsPanel.execMenuOnce("scripts-addOperationPanelBtnWithNavigator", () => {
this.addOperationPanelBtnWithNavigator();
});
domUtils.ready(() => {
GreasyforkMenu.initEnv();
GreasyforkAccount.init();
GreasyforkRememberFormTextArea.init();
GreasyforkMenu.handleLocalGotoCallBack();
PopsPanel.execMenuOnce("fixImageWidth", () => {
Greasyfork.fixImageWidth();
});
Greasyfork.languageSelectorLocale();
PopsPanel.execMenuOnce("optimizeImageBrowsing", () => {
Greasyfork.optimizeImageBrowsing();
});
PopsPanel.execMenuOnce("overlayBedImageClickEvent", () => {
Greasyfork.overlayBedImageClickEvent();
});
if (!GreasyforkRouter.isCodeStrict()) {
PopsPanel.execMenuOnce("addMarkdownCopyButton", () => {
Greasyfork.addMarkdownCopyButton();
});
}
});
},
/**
* 修复图片宽度显示问题
*/
fixImageWidth() {
if (window.innerWidth < window.innerHeight) {
log.info("修复图片显示问题");
_GM_addStyle(
/*css*/
`
img.lum-img{
width: 100% !important;
height: 100% !important;
}
`
);
}
},
/**
* 优化图片浏览
*/
optimizeImageBrowsing() {
log.info("优化图片浏览");
{
_GM_addStyle(_GM_getResourceText("ViewerCSS"));
}
_GM_addStyle(
/*css*/
`
@media (max-width: 460px) {
.lum-lightbox-image-wrapper {
display:flex;
overflow: auto;
-webkit-overflow-scrolling: touch
}
.lum-lightbox-caption {
width: 100%;
position: absolute;
bottom: 0
}
.lum-lightbox-position-helper {
margin: auto
}
.lum-lightbox-inner img {
max-width:100%;
max-height:100%;
}
}
`
);
function viewIMG(imgList = [], _index_ = 0) {
let viewerULNodeHTML = "";
imgList.forEach((item) => {
viewerULNodeHTML += `<li><img data-src="${item}" loading="lazy"></li>`;
});
let viewerULNode = domUtils.createElement("ul", {
innerHTML: viewerULNodeHTML
});
let viewer = new Viewer(viewerULNode, {
inline: false,
url: "data-src",
zIndex: utils.getMaxZIndex() + 100,
hidden: () => {
viewer.destroy();
}
});
_index_ = _index_ < 0 ? 0 : _index_;
viewer.view(_index_);
viewer.zoomTo(1);
viewer.show();
}
function getImgElementSrc(element) {
return element.getAttribute("data-src") || element.getAttribute("src") || element.getAttribute("alt");
}
domUtils.on(
document,
"click",
"img",
function(event) {
var _a2;
let imgElement = event.target;
if (((_a2 = imgElement.parentElement) == null ? void 0 : _a2.localName) === "a" && imgElement.hasAttribute("data-screenshots")) {
return;
}
if (imgElement.closest(".viewer-container")) {
return;
}
if (imgElement.closest(".lum-lightbox-position-helper")) {
return;
}
let userContentElement = imgElement.closest(".user-content");
let imgList = [];
let imgIndex = 0;
let imgElementList = [];
let currentImgSrc = getImgElementSrc(imgElement);
if (currentImgSrc == null ? void 0 : currentImgSrc.startsWith("https://img.shields.io")) {
return;
}
if (userContentElement) {
userContentElement.querySelectorAll("img").forEach((childImgElement) => {
imgElementList.push(childImgElement);
let imgSrc = getImgElementSrc(childImgElement);
let $parent = childImgElement.parentElement;
if (($parent == null ? void 0 : $parent.localName) === "a") {
imgSrc = $parent.getAttribute("data-href") || $parent.href;
}
imgList.push(imgSrc);
});
imgIndex = imgElementList.indexOf(imgElement);
if (imgIndex === -1) {
imgIndex = 0;
}
} else {
imgList.push(currentImgSrc);
imgIndex = 0;
}
log.success(["点击浏览图片👉", imgList, imgIndex]);
viewIMG(imgList, imgIndex);
}
);
document.querySelectorAll(".user-screenshots").forEach((element) => {
let linkElement = element.querySelector("a");
if (!linkElement) {
return;
}
let imgSrc = linkElement.getAttribute("data-href") || linkElement.getAttribute("href");
let imgElement = element.querySelector("img");
if (!imgElement) {
return;
}
imgElement.setAttribute("data-screenshots", "true");
imgElement.setAttribute("data-src", imgSrc);
linkElement.setAttribute("href", "javascript:;");
domUtils.after(linkElement, imgElement);
linkElement.remove();
});
},
/**
* 覆盖图床图片的parentElement的a标签
*/
overlayBedImageClickEvent() {
log.info("覆盖图床图片的parentElement的a标签");
document.querySelectorAll(".user-content a>img").forEach((imgElement) => {
let $link = imgElement.parentElement;
let url2 = $link.getAttribute("href");
$link.setAttribute("data-href", url2);
$link.removeAttribute("href");
if (url2.startsWith("/rails/active_storage/blobs/redirect")) {
log.info(`该图片是上传到Greasyfork的图片,拦截默认行为,不做提示`);
return;
}
domUtils.on($link, "click", () => {
Qmsg.warning(
/*html*/
`<div style="overflow-wrap: anywhere;">${i18next.t(
"拦截跳转:"
)}<a href="${url2}" target="_blank">${url2}</a></div>`,
{
html: true,
zIndex: utils.getMaxZIndex() + 105
}
);
});
});
},
/**
* 在Markdown右上角添加复制按钮
*/
addMarkdownCopyButton() {
log.info("在Markdown右上角添加复制按钮");
_GM_addStyle(
/*css*/
`
pre{
position: relative;
margin-bottom: 0px !important;
width: 100%;
}
`
);
_GM_addStyle(
/*css*/
`
.snippet-clipboard-content{
display: flex;
justify-content: space-between;
background: rgb(246, 248, 250);
margin-bottom: 16px;
}
.zeroclipboard-container {
/* right: 0;
top: 0;
position: absolute; */
box-sizing: border-box;
display: flex;
font-size: 16px;
line-height: 24px;
text-size-adjust: 100%;
overflow-wrap: break-word;
width: fit-content;
height: fit-content;
}
.zeroclipboard-container svg{
vertical-align: text-bottom;
display: inline-block;
overflow: visible;
fill: currentColor;
margin: 8px;
}
.zeroclipboard-container svg[aria-hidden="true"]{
display: none;
}
clipboard-copy.js-clipboard-copy {
position: relative;
padding: 0px;
color: rgb(36, 41, 47);
background-color: rgb(246, 248, 250);
transition: 80ms cubic-bezier(0.33, 1, 0.68, 1);
transition-property: color,background-color,box-shadow,border-color;
display: inline-block;
font-size: 14px;
line-height: 20px;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
-webkit-user-select: none;
user-select: none;
border: 1px solid rgba(31, 35, 40, 0.15);
-webkit-appearance: none;
appearance: none;
box-shadow: rgba(31, 35, 40, 0.04) 0px 1px 0px 0px, rgba(255, 255, 255, 0.25) 0px 1px 0px 0px inset;
margin: 8px;
overflow-wrap: break-word;
text-wrap: nowrap;
border-radius: 6px;
}
clipboard-copy.js-clipboard-copy[success]{
border-color: rgb(31, 136, 61);
box-shadow: 0 0 0 0.2em rgba(52,208,88,.4);
}
clipboard-copy.js-clipboard-copy:hover{
background-color: rgb(243, 244, 246);
border-color: rgba(31, 35, 40, 0.15);
transition-duration: .1s;
}
clipboard-copy.js-clipboard-copy:active{
background-color: rgb(235, 236, 240);
border-color: rgba(31, 35, 40, 0.15);
transition: none;
}
`
);
_GM_addStyle(
/*css*/
`
.pops-tip.github-tooltip {
border-radius: 6px;
padding: 6px 8px;
}
.pops-tip.github-tooltip, .pops-tip.github-tooltip .pops-tip-arrow::after {
background: rgb(36, 41, 47);
color: #fff;
}
.pops-tip.github-tooltip .pops-tip-arrow::after {
width: 8px;
height: 8px;
}
`
);
function getCopyElement() {
let copyElement = domUtils.createElement("div", {
className: "zeroclipboard-container",
innerHTML: (
/*html*/
`
<clipboard-copy class="js-clipboard-copy">
<svg height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon-copy">
<path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path>
</svg>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon-check-copy">
<path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path>
</svg>
</clipboard-copy>
`
)
});
let clipboardCopyElement = copyElement.querySelector(
".js-clipboard-copy"
);
let octiconCopyElement = copyElement.querySelector(
".octicon-copy"
);
let octiconCheckCopyElement = copyElement.querySelector(
".octicon-check-copy"
);
domUtils.on(copyElement, "click", function() {
let codeElement = copyElement.parentElement.querySelector("code");
if (!codeElement && copyElement.parentElement.className.includes("prettyprinted")) {
codeElement = copyElement.parentElement;
}
if (!codeElement) {
Qmsg.error(i18next.t("未找到{{selector}}元素", { selector: "code" }));
return;
}
utils.setClip(codeElement.innerText || codeElement.textContent);
clipboardCopyElement.setAttribute("success", "true");
octiconCopyElement.setAttribute("aria-hidden", "true");
octiconCheckCopyElement.removeAttribute("aria-hidden");
let tooltip = __pops.tooltip({
target: clipboardCopyElement,
content: i18next.t("✅ 复制成功!"),
position: "left",
className: "github-tooltip",
alwaysShow: true
});
tooltip.toolTip.onToolTipAnimationFinishEvent();
setTimeout(() => {
clipboardCopyElement.removeAttribute("success");
octiconCheckCopyElement.setAttribute("aria-hidden", "true");
octiconCopyElement.removeAttribute("aria-hidden");
tooltip.toolTip.close();
}, 2e3);
});
return copyElement;
}
document.querySelectorAll("pre").forEach((preElement) => {
let zeroclipboardElement = preElement.querySelector(
"div.zeroclipboard-container"
);
if (zeroclipboardElement) {
return;
}
let copyElement = getCopyElement();
let snippetClipboardContentElement = domUtils.createElement("div", {
className: "snippet-clipboard-content"
});
domUtils.before(preElement, snippetClipboardContentElement);
snippetClipboardContentElement.appendChild(preElement);
snippetClipboardContentElement.appendChild(copyElement);
});
},
/**
* 固定当前语言
*/
languageSelectorLocale() {
let localeLanguage = PopsPanel.getValue("language-selector-locale");
let currentLocaleLanguage = window.location.pathname.split("/").filter((item) => Boolean(item))[0];
log.success("选择语言:" + localeLanguage);
log.success("当前语言:" + currentLocaleLanguage);
if (utils.isNull(localeLanguage)) {
return;
}
if (localeLanguage === currentLocaleLanguage) {
return;
} else {
let timer = null;
let url2 = GreasyforkUrlUtils.getSwitchLanguageUrl(localeLanguage);
GreasyforkApi.switchLanguage(url2);
log.success("新Url:" + url2);
Qmsg.loading(
i18next.t(
"当前语言:{{currentLocaleLanguage}},,3秒后切换至:{{localeLanguage}}",
{
currentLocaleLanguage,
localeLanguage
}
),
{
timeout: 3e3,
showClose: true,
onClose() {
clearTimeout(timer);
}
}
);
Qmsg.info(i18next.t("导航至:") + url2, {
timeout: 3e3
});
timer = setTimeout(() => {
window.location.href = url2;
}, 3e3);
}
},
/**
* 检测gf页面是否正确加载,有时候会出现
* We're down for maintenance. Check back again soon.
*/
checkPage() {
log.info("检测gf页面是否正确加载,有时候会出现");
domUtils.ready(() => {
if (document.body.firstElementChild && document.body.firstElementChild.localName === "p" && document.body.firstElementChild.innerText.includes(
"We're down for maintenance. Check back again soon."
)) {
let latestRefreshPageTime = parseInt(
_GM_getValue(
"greasyfork-check-page-time",
0
)
);
let checkPageTimeout = PopsPanel.getValue(
"greasyfork-check-page-timeout",
5
);
let checkPageTimeoutStamp = checkPageTimeout * 1e3;
if (latestRefreshPageTime && Date.now() - latestRefreshPageTime < checkPageTimeoutStamp) {
Qmsg.warning(
i18next.t("上次重载时间 {{time}},{{timeout}}秒内拒绝反复重载", {
time: utils.formatTime(
latestRefreshPageTime,
"yyyy-MM-dd HH:mm:ss"
),
timeout: checkPageTimeout
})
);
return;
}
_GM_setValue("greasyfork-check-page-time", Date.now());
window.location.reload();
}
});
},
/**
* 在顶部导航栏添加【操作面板】按钮
*/
addOperationPanelBtnWithNavigator() {
log.info("添加【操作面板】按钮");
CommonUtil.addBlockCSS(
".sidebarred .sidebar",
".sidebarred-main-content .open-sidebar"
);
_GM_addStyle(
/*css*/
`
.sidebarred .sidebarred-main-content{
max-width: 100%;
}
`
);
domUtils.ready(() => {
let $nav = document.querySelector("#site-nav nav");
let $subNav = document.querySelector(
"#site-nav .with-submenu nav"
);
let $scriptsOptionGroups = document.querySelector("#script-list-option-groups") || document.querySelector(".list-option-groups");
if (!$scriptsOptionGroups) {
log.warn("不存在右侧面板元素#script-list-option-groups");
return;
}
$scriptsOptionGroups = $scriptsOptionGroups.cloneNode(
true
);
$scriptsOptionGroups.classList.add("option-panel-groups");
if (!$nav) {
log.error("元素#site-nav nav不存在");
return;
}
let $filterBtn = domUtils.createElement("li", {
className: "filter-scripts",
innerHTML: `
<a href="javascript:;">${i18next.t("操作面板")}</a>
`
});
domUtils.on($filterBtn, "click", (event) => {
utils.preventEvent(event);
let $drawer = __pops.drawer({
title: {
enable: false
},
content: {
text: "",
html: true
},
direction: "top",
size: "80%",
zIndex: utils.getMaxZIndex(100),
style: (
/*css*/
`
.pops-drawer-content div:first-child{
margin: 20px;
}
.option-panel-groups > div{
}
.option-panel-groups ul{
margin: .5em 0 0;
list-style-type: none;
padding: 1em 0;
box-shadow: 0 0 5px #ddd;
border: 1px solid #BBBBBB;
border-radius: 5px;
background-color: #fff;
}
.option-panel-groups ul li{
}
li.list-current{
border-left: 7px solid #800;
box-shadow: inset 0 1px #0000001a, inset 0 -1px #0000001a;
margin: 0 0 0 -4px;
padding: .4em 1em .4em calc(1em - 3px);
background: linear-gradient(#fff, #eee);
}
.list-option-group a {
padding: .35em 1em;
display: block;
}
.list-option-group {
margin-bottom: 1em;
}
form.sidebar-search{
display: flex;
align-items: center;
gap: 10px;
}
form.sidebar-search input[type="search"]{
display: inline-flex;
justify-content: center;
align-items: center;
line-height: 1;
height: 32px;
white-space: nowrap;
cursor: text;
text-align: center;
box-sizing: border-box;
outline: 0;
transition: 0.1s;
font-weight: 500;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
vertical-align: middle;
-webkit-appearance: none;
appearance: none;
background-color: transparent;
border: 0;
padding: 8px 8px;
font-size: 14px;
text-align: start;
/* width: 100%; */
// flex: 1;
display: flex;
align-items: center;
border: 1px solid #dcdfe6;
border-radius: 4px;
background-color: #ffffff;
}
form.sidebar-search input[type="submit"]{
width: 32px;
height: 32px;
}
`
)
});
let $drawerContent = $drawer.$shadowRoot.querySelector(
".pops-drawer-content"
);
$drawerContent.appendChild($scriptsOptionGroups);
});
if ($subNav && $subNav.children.length) {
$subNav.appendChild($filterBtn);
} else {
$nav.appendChild($filterBtn);
}
});
}
};
PopsPanel.init();
Greasyfork.init();
})(Qmsg, DOMUtils, Utils, i18next, pops, Viewer);