// ==UserScript==
// @name 简书优化
// @namespace https://github.com/WhiteSevs/TamperMonkeyScript
// @version 2026.5.11
// @author WhiteSevs
// @description 支持手机端和PC端、屏蔽广告、优化浏览体验、重定向链接、全文居中、自动展开全文、允许复制文字、劫持唤醒/跳转App、自定义屏蔽元素等
// @license GPL-3.0-only
// @icon data:image/png;base64,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
// @supportURL https://github.com/WhiteSevs/TamperMonkeyScript/issues
// @match *://*.jianshu.com/*
// @match *://*.jianshu.io/*
// @require https://fastly.jsdelivr.net/gh/WhiteSevs/TamperMonkeyScript@86be74b83fca4fa47521cded28377b35e1d7d2ac/lib/CoverUMD/index.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/utils@2.12.2/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/domutils@2.0.8/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/@whitesev/pops@4.2.8/dist/index.umd.js
// @require https://fastly.jsdelivr.net/npm/qmsg@1.7.2/dist/index.umd.js
// @connect *
// @grant GM_addValueChangeListener
// @grant GM_deleteValue
// @grant GM_getResourceText
// @grant GM_getValue
// @grant GM_info
// @grant GM_listValues
// @grant GM_registerMenuCommand
// @grant GM_removeValueChangeListener
// @grant GM_setValue
// @grant GM_setValues
// @grant GM_unregisterMenuCommand
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @run-at document-start
// ==/UserScript==
(function (_whitesev_domutils, _whitesev_pops, _whitesev_utils, qmsg) {
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if ((from && typeof from === "object") || typeof from === "function")
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (
(target = mod != null ? __create(__getProtoOf(mod)) : {}),
__copyProps(
isNodeMode || !mod || !mod.__esModule
? __defProp(target, "default", {
value: mod,
enumerable: true,
})
: target,
mod
)
);
_whitesev_domutils = __toESM(_whitesev_domutils);
_whitesev_pops = __toESM(_whitesev_pops);
_whitesev_utils = __toESM(_whitesev_utils);
qmsg = __toESM(qmsg);
var block_default =
'.download-app-guidance,\n.call-app-btn,\n.collapse-tips,\n.note-graceful-button,\n.app-open,\n.header-wrap,\n.recommend-wrap.recommend-ad,\n.call-app-Ad-bottom,\n#recommended-notes p.top-title span.more,\n#homepage .modal,\nbutton.index_call-app-btn,\nspan.note__flow__download,\n.download-guide,\n#footer,\n.comment-open-app-btn-wrap,\n.nav.navbar-nav + div,\n.self-flow-ad,\n#free-reward-panel,\ndiv[id*=\'AdFive\'],\n#index-aside-download-qrbox,\n.baidu-app-download-2eIkf_1,\n/* 底部的"小礼物走一走,来简书关注我"、赞赏支持和更多精彩内容,就在简书APP */\ndiv[role="main"] > div > section:first-child > div:nth-last-child(2),\n/* 它的内部是script标签,可能影响部分评论之间的高度问题 */\ndiv.adad_container ,\n/* 顶部导航栏的【下载App】 */\n#__next nav a[href*="navbar-app"] {\n display: none !important;\n}\nbody.reader-day-mode.normal-size {\n overflow: auto !important;\n}\n.collapse-free-content {\n height: auto !important;\n}\n.copyright {\n color: #000 !important;\n}\n#note-show .content .show-content-free .collapse-free-content:after {\n background-image: none !important;\n}\nfooter > div > div {\n justify-content: center;\n}\n/* 修复底部最后编辑于:。。。在某些套壳浏览器上的错位问题 */\n#note-show .content .show-content-free .note-meta-time {\n margin-top: 0px !important;\n}\n';
var _GM_addValueChangeListener = typeof GM_addValueChangeListener != "undefined" ? GM_addValueChangeListener : void 0;
var _GM_deleteValue = typeof GM_deleteValue != "undefined" ? GM_deleteValue : void 0;
var _GM_getResourceText = typeof GM_getResourceText != "undefined" ? GM_getResourceText : void 0;
var _GM_getValue = typeof GM_getValue != "undefined" ? GM_getValue : void 0;
var _GM_info = typeof GM_info != "undefined" ? GM_info : void 0;
var _GM_listValues = typeof GM_listValues != "undefined" ? GM_listValues : void 0;
var _GM_registerMenuCommand = typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0;
var _GM_removeValueChangeListener =
typeof GM_removeValueChangeListener != "undefined" ? GM_removeValueChangeListener : void 0;
var _GM_setValue = typeof GM_setValue != "undefined" ? GM_setValue : void 0;
var _GM_setValues = typeof GM_setValues != "undefined" ? GM_setValues : void 0;
var _GM_unregisterMenuCommand = typeof GM_unregisterMenuCommand != "undefined" ? GM_unregisterMenuCommand : void 0;
var _GM_xmlhttpRequest = typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0;
var _unsafeWindow = typeof unsafeWindow != "undefined" ? unsafeWindow : void 0;
var _monkeyWindow = window;
var CommonUtil = {
waitRemove(...args) {
args.forEach((selector) => {
if (typeof selector !== "string") return;
_whitesev_domutils.default.waitNodeList(selector).then((nodeList) => {
nodeList.forEach(($el) => $el.remove());
});
});
},
createBlockCSSNode(...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 _whitesev_domutils.default.createElement("style", {
type: "text/css",
innerHTML: `${selectorList.join(",\n")}{display: none !important;}`,
});
},
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);
});
selectorList = selectorList.map((it) => it.trim()).filter((it) => it !== "");
if (selectorList.length) return addStyle(`${selectorList.join(",\n")}{display: none !important;}`);
},
setGMResourceCSS(resourceMapData) {
const cssText = typeof _GM_getResourceText === "function" ? _GM_getResourceText(resourceMapData.keyName) : null;
if (typeof cssText === "string" && cssText) return addStyle(cssText);
else return CommonUtil.loadStyleLink(resourceMapData.url);
},
async loadStyleLink(url) {
let $link = document.createElement("link");
$link.rel = "stylesheet";
$link.type = "text/css";
$link.href = url;
return new Promise((resolve) => {
_whitesev_domutils.default.onReady(() => {
document.head.appendChild($link);
resolve($link);
});
});
},
async loadScript(url) {
let $script = document.createElement("script");
$script.src = url;
return new Promise((resolve) => {
$script.onload = () => {
resolve(null);
};
(document.head || document.documentElement).appendChild($script);
});
},
fixUrl(url) {
url = url.trim();
if (url.startsWith("data:")) return url;
if (url.match(/^http(s|):\/\//i)) return url;
else if (url.startsWith("//")) {
if (url.startsWith("///")) {
} else url = window.location.protocol + url;
return url;
} else {
if (!url.startsWith("/")) url += "/";
url = window.location.origin + url;
return url;
}
},
fixHttps(url) {
if (url.startsWith("https://")) return url;
if (!url.startsWith("http://")) return url;
try {
let urlInstance = new URL(url);
urlInstance.protocol = "https:";
return urlInstance.toString();
} catch {
return url;
}
},
lockScroll(...args) {
let $hidden = document.createElement("style");
$hidden.innerHTML = `
.pops-overflow-hidden-important {
overflow: hidden !important;
}
`;
let $elList = [document.documentElement, document.body].concat(...(args || []));
$elList.forEach(($el) => {
$el.classList.add("pops-overflow-hidden-important");
});
(document.head || document.documentElement).appendChild($hidden);
return {
recovery() {
$elList.forEach(($el) => {
$el.classList.remove("pops-overflow-hidden-important");
});
$hidden.remove();
},
};
},
async getClipboardText() {
function readClipboardText(resolve) {
navigator.clipboard
.readText()
.then((clipboardText) => {
resolve(clipboardText);
})
.catch((error) => {
log.error("读取剪贴板内容失败👉", error);
resolve("");
});
}
function requestPermissionsWithClipboard(resolve) {
navigator.permissions
.query({ name: "clipboard-read" })
.then(() => {
readClipboardText(resolve);
})
.catch((error) => {
log.error("申请剪贴板权限失败,尝试直接读取👉", error.message ?? error.name ?? error.stack);
readClipboardText(resolve);
});
}
function checkClipboardApi() {
if (typeof navigator?.clipboard?.readText !== "function") return false;
if (typeof navigator?.permissions?.query !== "function") return false;
return true;
}
return new Promise((resolve) => {
if (!checkClipboardApi()) {
resolve("");
return;
}
if (document.hasFocus()) requestPermissionsWithClipboard(resolve);
else
window.addEventListener(
"focus",
() => {
requestPermissionsWithClipboard(resolve);
},
{ once: true }
);
});
},
escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'")
.replace(/©/g, "©")
.replace(/®/g, "®")
.replace(/™/g, "™")
.replace(/→/g, "→")
.replace(/←/g, "←")
.replace(/↑/g, "↑")
.replace(/↓/g, "↓")
.replace(/—/g, "—")
.replace(/–/g, "–")
.replace(/…/g, "…")
.replace(/ /g, " ")
.replace(/\r\n/g, "
")
.replace(/\r/g, "
")
.replace(/\n/g, "
")
.replace(/\t/g, " ");
},
interval(fn, intervalTime, timeout = 5e3) {
let timeId;
let maxTimeout = timeout - intervalTime;
let intervalTimeCount = intervalTime;
let loop = async (isTimeout) => {
const result = await fn(isTimeout);
if ((typeof result === "boolean" && result) || isTimeout) {
utils.workerClearTimeout(timeId);
return;
}
intervalTimeCount += intervalTime;
if (intervalTimeCount > maxTimeout) {
loop(true);
return;
}
timeId = utils.workerSetTimeout(() => {
loop(false);
}, intervalTime);
};
loop(false);
},
findParentNode($el, selector, parentSelector) {
if (parentSelector) {
let $parent = _whitesev_domutils.default.closest($el, parentSelector);
if ($parent) return $parent.querySelector(selector);
} else {
if (_whitesev_domutils.default.matches($el, selector)) return $el;
return _whitesev_domutils.default.closest($el, selector);
}
},
toStr(data, space = 2) {
const undefinedReplacedStr = `__undefined__placeholder__replaced__str__` + performance.now();
return JSON.stringify(
data,
(key, value) => {
return value === void 0 ? undefinedReplacedStr : value;
},
space
).replace(new RegExp(`"${undefinedReplacedStr}"`, "g"), "undefined");
},
isVerticalScreen() {
return !globalThis.screen.orientation.type.includes("landscape");
},
isMobileDevice(size = 768) {
if (this.isVerticalScreen()) return globalThis.innerWidth < size;
else return globalThis.innerHeight < size;
},
isTopWindow() {
const win = typeof _unsafeWindow === "object" && _unsafeWindow != null ? _unsafeWindow : window;
return win.top === win.self;
},
formatVideoDuration(duration) {
if (typeof duration !== "number") duration = parseInt(duration);
if (isNaN(duration)) return duration.toString();
const zeroPadding = function (num) {
if (num < 10) return `0${num}`;
else return num;
};
if (duration < 60) return `0:${zeroPadding(duration)}`;
else if (duration >= 60 && duration < 3600) return `${Math.floor(duration / 60)}:${zeroPadding(duration % 60)}`;
else {
const hours = Math.floor(duration / 3600);
const minutes = Math.floor(duration / 60) % 60;
const seconds = duration % 60;
return `${hours}:${zeroPadding(minutes)}:${zeroPadding(seconds)}`;
}
},
formatTimeStamp(time, endTime) {
if (typeof time === "number") {
if (time < 0xe8d4a51000) {
const padZeroLength = String(Date.now()).length - String(time).length;
time = time * Math.pow(10, padZeroLength);
}
}
let result = time;
let oldTime = new Date(typeof time === "string" ? time.replace(/-/g, "/") : time);
let timeDifference = new Date(endTime ?? Date.now()).getTime() - oldTime.getTime();
let days = Math.floor(timeDifference / (24 * 3600 * 1e3));
if (days > 0)
if (days > 7) result = utils.formatTime(oldTime.getTime());
else result = days + "天前";
else {
let leave1 = timeDifference % (24 * 3600 * 1e3);
let hours = Math.floor(leave1 / (3600 * 1e3));
if (hours > 0) result = hours + "小时前";
else {
let leave2 = leave1 % (3600 * 1e3);
let minutes = Math.floor(leave2 / (60 * 1e3));
if (minutes > 0) result = minutes + "分钟前";
else {
let leave3 = leave2 % (60 * 1e3);
result = Math.round(leave3 / 1e3) + "秒前";
}
}
}
return result;
},
};
var KEY = "GM_Panel";
var ATTRIBUTE_INIT = "data-init";
var ATTRIBUTE_KEY = "data-key";
var ATTRIBUTE_DEFAULT_VALUE = "data-default-value";
var ATTRIBUTE_INIT_MORE_VALUE = "data-init-more-value";
var ATTRIBUTE_PLUGIN_SEARCH_CONFIG = "data-plugin-search-config";
var PROPS_STORAGE_API = "data-storage-api";
var PanelSizeUtil = {
followBrowserSize: false,
get width() {
return PanelSizeUtil.followBrowserSize ? globalThis.outerWidth : globalThis.innerWidth;
},
get height() {
return PanelSizeUtil.followBrowserSize ? globalThis.outerHeight : globalThis.innerHeight;
},
};
var PanelUISize = {
setting: {
get width() {
if (PanelSizeUtil.width < 550) return "88vw";
else if (PanelSizeUtil.width < 700) return "550px";
else return "700px";
},
get height() {
if (PanelSizeUtil.height < 450) return "70vh";
else if (PanelSizeUtil.height < 550) return "450px";
else return "550px";
},
},
settingMiddle: {
get width() {
return PanelSizeUtil.width < 350 ? "88vw" : "350px";
},
get height() {
return PanelSizeUtil.height < 450 ? "88vh" : "450px";
},
},
settingBig: {
get width() {
return PanelSizeUtil.width < 800 ? "92vw" : "800px";
},
get height() {
return PanelSizeUtil.height < 600 ? "80vh" : "600px";
},
},
info: {
get width() {
return PanelSizeUtil.width < 350 ? "88vw" : "350px";
},
get height() {
return PanelSizeUtil.height < 250 ? "88vh" : "250px";
},
},
};
var PanelContent = {
$data: {
__contentConfig: null,
get contentConfig() {
if (this.__contentConfig == null) this.__contentConfig = new utils.Dictionary();
return this.__contentConfig;
},
__defaultBottomContentConfig: [],
},
addContentConfig(configList) {
if (!Array.isArray(configList)) configList = [configList];
let index = this.$data.contentConfig.keys().length;
this.$data.contentConfig.set(index, configList);
},
getAllContentConfig() {
return this.$data.contentConfig.values().flat();
},
getConfig(index = 0) {
return this.$data.contentConfig.get(index) ?? [];
},
getDefaultBottomContentConfig(config) {
if (this.$data.__defaultBottomContentConfig.length) return this.$data.__defaultBottomContentConfig;
let isDoubleClick = false;
let timer = void 0;
const translateCallback = (text, translateMap) => {
if (config && typeof config.translateCallback === "function")
return config.translateCallback(text, translateMap);
else {
if (typeof translateMap === "object" && translateMap)
for (const key in translateMap) text = text.replaceAll(`{{${key}}}`, translateMap[key]);
return text;
}
};
const exportToFile = (fileName, fileData) => {
if (typeof fileData !== "string") fileData = CommonUtil.toStr(fileData);
const blob = new Blob([fileData]);
const blobUrl = globalThis.URL.createObjectURL(blob);
domUtils
.createElement("a", {
href: blobUrl,
download: fileName,
})
.click();
utils.workerSetTimeout(() => {
globalThis.URL.revokeObjectURL(blobUrl);
}, 500);
};
const dbclick_callback = () => {
const importConfig = (importEndCallBack) => {
const $alert = __pops__.alert({
title: {
text: translateCallback("请选择导入方式"),
position: "center",
},
content: {
text: `
${translateCallback("本地导入")}
${translateCallback("网络导入")}
${translateCallback("剪贴板导入")}
`,
html: true,
},
btn: {
ok: { enable: false },
close: {
enable: true,
callback(details) {
details.close();
},
},
},
drag: true,
mask: { enable: true },
width: PanelUISize.info.width,
height: PanelUISize.info.height,
style: `
.btn-control{
display: inline-block;
margin: 10px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
cursor: pointer;
}
.btn-control:hover{
color: #409eff;
border-color: #c6e2ff;
background-color: #ecf5ff;
}`,
});
const $local = $alert.$shadowRoot.querySelector(".btn-control[data-mode='local']");
const $network = $alert.$shadowRoot.querySelector(".btn-control[data-mode='network']");
const $clipboard = $alert.$shadowRoot.querySelector(".btn-control[data-mode='clipboard']");
const updateConfigToStorage = async (data) => {
if (confirm(translateCallback("是否清空脚本存储的配置?(如果点击取消按钮,则仅做配置覆盖处理)")))
if (typeof _GM_listValues === "function")
if (typeof _GM_deleteValue === "function") {
_GM_listValues().forEach((key) => {
_GM_deleteValue(key);
});
qmsg.default.success(translateCallback("已清空脚本存储的配置"));
} else qmsg.default.error(translateCallback("不支持GM_deleteValue函数,无法执行删除脚本配置"));
else qmsg.default.error(translateCallback("不支持GM_listValues函数,无法清空脚本存储的配置"));
if (typeof _GM_setValues === "function") _GM_setValues(data);
else
Object.keys(data).forEach((key) => {
const value = data[key];
_GM_setValue(key, value);
});
qmsg.default.success(translateCallback("配置导入完毕"));
importEndCallBack?.();
};
const importFile = (configText) => {
return new Promise(async (resolve) => {
const data = utils.toJSON(configText);
if (Object.keys(data).length === 0) qmsg.default.warning(translateCallback("解析为空配置,不导入"));
else await updateConfigToStorage(data);
resolve(true);
});
};
domUtils.on($local, "click", (event) => {
domUtils.preventEvent(event);
$alert.close();
const $input = domUtils.createElement("input", {
type: "file",
accept: ".json",
});
domUtils.on($input, ["propertychange", "input"], () => {
if (!$input.files?.length) return;
const uploadFile = $input.files[0];
const fileReader = new FileReader();
fileReader.onload = () => {
importFile(fileReader.result);
};
fileReader.readAsText(uploadFile, "UTF-8");
});
$input.click();
});
domUtils.on($network, "click", (event) => {
domUtils.preventEvent(event);
$alert.close();
const $prompt = __pops__.prompt({
title: {
text: translateCallback("网络导入"),
position: "center",
},
content: {
text: "",
placeholder: translateCallback("请填写URL"),
focus: true,
},
btn: {
close: {
enable: true,
callback(details) {
details.close();
},
},
ok: {
text: translateCallback("导入"),
callback: async (details) => {
const url = details.text;
if (utils.isNull(url)) {
qmsg.default.error(translateCallback("请填入完整的url"));
return;
}
const $loading = qmsg.default.loading(translateCallback("正在获取配置..."));
const response = await httpx.get(url, { allowInterceptConfig: false });
$loading.close();
if (!response.status) {
log.error(response);
qmsg.default.error(translateCallback("获取配置失败"), { consoleLogContent: true });
return;
}
if (!(await importFile(response.data.responseText))) return;
details.close();
},
},
cancel: { enable: false },
},
drag: true,
mask: { enable: true },
width: PanelUISize.info.width,
height: "auto",
});
const $promptInput = $prompt.$shadowRoot.querySelector("input");
const $promptOk = $prompt.$shadowRoot.querySelector(".pops-prompt-btn-ok");
domUtils.on($promptInput, ["input", "propertychange"], () => {
if (domUtils.val($promptInput) === "") domUtils.attr($promptOk, "disabled", "true");
else domUtils.removeAttr($promptOk, "disabled");
});
domUtils.onKeyboard($promptInput, "keydown", (keyName, keyValue, otherCodeList) => {
if (keyName === "Enter" && otherCodeList.length === 0) {
if (domUtils.val($promptInput) !== "") domUtils.emit($promptOk, "click");
}
});
domUtils.emit($promptInput, "input");
});
domUtils.on($clipboard, "click", async (event) => {
domUtils.preventEvent(event);
$alert.close();
let clipboardText = await CommonUtil.getClipboardText();
if (clipboardText.trim() === "") {
qmsg.default.warning(translateCallback("获取到的剪贴板内容为空"));
return;
}
if (!(await importFile(clipboardText))) return;
});
};
const exportConfig = (
fileName = `${SCRIPT_NAME}_panel-setting-${utils.formatTime(Date.now(), "yyyy_MM_dd_HH_mm_ss")}.json`,
fileData
) => {
const $alert = __pops__.alert({
title: {
text: translateCallback("请选择导出方式"),
position: "center",
},
content: {
text: `
${translateCallback("导出至文件")}
${translateCallback("导出至剪贴板")}
`,
html: true,
},
btn: {
ok: { enable: false },
close: {
enable: true,
callback(details) {
details.close();
},
},
},
drag: true,
mask: { enable: true },
width: PanelUISize.info.width,
height: PanelUISize.info.height,
style: `
.btn-control{
display: inline-block;
margin: 10px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
cursor: pointer;
}
.btn-control:hover{
color: #409eff;
border-color: #c6e2ff;
background-color: #ecf5ff;
}`,
});
const $exportToFile = $alert.$shadowRoot.querySelector(".btn-control[data-mode='export-to-file']");
const $exportToClipboard = $alert.$shadowRoot.querySelector(".btn-control[data-mode='export-to-clipboard']");
domUtils.on($exportToFile, "click", (event) => {
domUtils.preventEvent(event);
try {
exportToFile(fileName, fileData);
$alert.close();
} catch (error) {
qmsg.default.error(error.toString(), { consoleLogContent: true });
}
});
domUtils.on($exportToClipboard, "click", async () => {
if (await utils.copy(fileData)) {
qmsg.default.success(translateCallback("复制成功"));
$alert.close();
} else qmsg.default.error(translateCallback("复制失败"));
});
};
const $textarea = __pops__
.confirm({
title: {
text: translateCallback("配置"),
position: "center",
},
content: {
text: ``,
html: true,
},
btn: {
ok: {
enable: true,
type: "primary",
text: translateCallback("导入"),
callback() {
importConfig();
},
},
cancel: {
enable: true,
text: translateCallback("导出"),
callback() {
exportConfig(void 0, configDataStr);
},
},
},
width: PanelSizeUtil.width < 450 ? "90vw" : "450px",
height: "auto",
style: `
.pops-content textarea {
--textarea-bd-color: #dcdfe6;
display: inline-block;
resize: vertical;
padding: 5px 15px;
margin: 0;
line-height: normal;
box-sizing: border-box;
color: #606266;
border: 0;
border-radius: 0;
outline: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: none;
width: 100%;
height: 100%;
appearance: none;
resize: none;
}
.pops-content textarea{
height: 500px;
}
.pops-content textarea:focus {
--textarea-bd-color: #3677f0;
}
.pops-content textarea:hover {
--textarea-bd-color: #c0c4cc;
}
`,
})
.$shadowRoot.querySelector("textarea");
const configData = {};
if (typeof _GM_listValues === "function")
_GM_listValues().forEach((key) => {
const value = _GM_getValue(key);
Reflect.set(configData, key, value);
});
else {
qmsg.default.warning(translateCallback("不支持函数GM_listValues,仅导出菜单配置"));
const panelLocalValue = _GM_getValue(KEY);
Reflect.set(configData, KEY, panelLocalValue);
}
const configDataStr = CommonUtil.toStr(configData);
$textarea.value = configDataStr;
};
const click_callback = () => {
let supportURL = _GM_info?.script?.supportURL || _GM_info?.script?.namespace;
if (typeof supportURL === "string" && utils.isNotNull(supportURL)) window.open(supportURL, "_blank");
};
return [
{
id: "script-version",
title: translateCallback(`版本:{{version}}`, {
version: _GM_info?.script?.version || translateCallback("未知"),
}),
isBottom: true,
views: [],
clickFirstCallback() {
return false;
},
afterRender(config) {
new AnyTouch(config.$asideLiElement).on("tap", function () {
clearTimeout(timer);
timer = void 0;
if (isDoubleClick) {
isDoubleClick = false;
dbclick_callback();
} else {
timer = setTimeout(() => {
isDoubleClick = false;
click_callback();
}, 200);
isDoubleClick = true;
}
});
},
},
];
},
setDefaultBottomContentConfig(config) {
this.$data.__defaultBottomContentConfig = config;
},
};
var PanelMenu = {
$data: {
__menuOption: [
{
key: "show_pops_panel_setting",
text: "⚙ 设置",
autoReload: false,
isStoreValue: false,
showText(text) {
return text;
},
callback: () => {
Panel.showPanel(PanelContent.getConfig(0));
},
},
],
get menuOption() {
return this.__menuOption;
},
},
init() {
this.initExtensionsMenu();
},
initExtensionsMenu() {
if (!CommonUtil.isTopWindow()) return;
MenuRegister.add(this.$data.menuOption);
},
addMenuOption(option) {
if (!Array.isArray(option)) option = [option];
this.$data.menuOption.push(...option);
},
updateMenuOption(option) {
if (!Array.isArray(option)) option = [option];
option.forEach((optionItem) => {
let findIndex = this.$data.menuOption.findIndex((it) => {
return it.key === optionItem.key;
});
if (findIndex !== -1) this.$data.menuOption[findIndex] = optionItem;
});
},
getMenuOption(index = 0) {
return this.$data.menuOption[index];
},
deleteMenuOption(index = 0) {
this.$data.menuOption.splice(index, 1);
},
};
var PanelMenuResultsHandler = class {
data = {
storeNodeList: [],
destoryFnList: [],
};
option = {};
constructor(option) {
this.option = option;
}
handlerResult(enableValue, args) {
const dynamicMenuStoreNodeList = [];
const dynamicDestoryFnList = [];
let resultValueList = [];
if (Array.isArray(args)) resultValueList = resultValueList.concat(args);
else {
const handleArgs = (obj) => {
if (typeof obj === "object" && obj != null)
if (obj instanceof Element) resultValueList.push(obj);
else if (Array.isArray(obj)) handleArgs(obj);
else {
const { $css, destory } = obj;
if ($css != null) {
if (Array.isArray($css)) resultValueList = resultValueList.concat($css);
else if ($css instanceof Element) resultValueList.push($css);
}
if (typeof destory === "function") resultValueList.push(destory);
}
else resultValueList.push(obj);
};
handleArgs(args);
}
const handleResult = (it) => {
if (it == null) return;
if (it instanceof Element) {
dynamicMenuStoreNodeList.push(it);
return;
}
if (typeof it === "function") {
dynamicDestoryFnList.push(it);
return;
}
};
for (const it of resultValueList) {
const flag = handleResult(it);
if (typeof flag === "boolean" && !flag) break;
if (Array.isArray(it))
for (const it2 of it) {
const flag2 = handleResult(it2);
if (typeof flag2 === "boolean" && !flag2) break;
}
}
this.clearStoreNodeList();
this.execDestoryFnAndClear();
if (enableValue) {
this.data.storeNodeList = this.data.storeNodeList.concat(dynamicMenuStoreNodeList);
this.data.destoryFnList = this.data.destoryFnList.concat(dynamicDestoryFnList);
}
}
getEnableStatus(key) {
const value = this.option.getValue(key);
return Boolean(value);
}
clearStoreNodeList = () => {
for (let index = this.data.storeNodeList.length - 1; index >= 0; index--) {
this.data.storeNodeList[index]?.remove();
this.data.storeNodeList.splice(index, 1);
}
};
execDestoryFnAndClear = () => {
for (let index = this.data.destoryFnList.length - 1; index >= 0; index--) {
const destoryFnItem = this.data.destoryFnList[index];
destoryFnItem();
this.data.destoryFnList.splice(index, 1);
}
};
checkMenuExec() {
let flag = false;
if (typeof this.option.checkExec === "function") flag = this.option.checkExec(this.option.keyList);
else flag = this.option.keyList.every((key) => this.getEnableStatus(key));
return flag;
}
};
var StorageUtils = class {
storageKey;
listenerData;
cacheData;
callbacks = [];
constructor(key) {
if (typeof key === "string") {
const trimKey = key.trim();
if (trimKey == "") throw new Error("key can not be empty string");
this.storageKey = trimKey;
} else throw new TypeError("key must be a string");
this.listenerData = new _whitesev_utils.default.Dictionary();
this.getLocalValue = this.getLocalValue.bind(this);
this.setLocalValue = this.setLocalValue.bind(this);
this.destory = this.destory.bind(this);
this.set = this.set.bind(this);
this.get = this.get.bind(this);
this.getAll = this.getAll.bind(this);
this.delete = this.delete.bind(this);
this.has = this.has.bind(this);
this.keys = this.keys.bind(this);
this.values = this.values.bind(this);
this.clear = this.clear.bind(this);
this.addValueChangeListener = this.addValueChangeListener.bind(this);
this.removeValueChangeListener = this.removeValueChangeListener.bind(this);
this.emitValueChangeListener = this.emitValueChangeListener.bind(this);
}
[Symbol.dispose]() {
this.destory();
}
async [Symbol.asyncDispose]() {
this.destory();
}
destory() {
this.cacheData = null;
for (let index = this.callbacks.length - 1; index >= 0; index--) {
const cb = this.callbacks[index];
cb();
this.callbacks.splice(index, 1);
}
}
getLocalValue() {
if (this.cacheData == null) {
let localValue = _GM_getValue(this.storageKey);
if (localValue == null) {
localValue = {};
this.setLocalValue(localValue);
}
this.destory();
this.cacheData = localValue;
const listenerId = _GM_addValueChangeListener(this.storageKey, (name, oldValue, newValue) => {
this.cacheData = null;
this.cacheData = newValue;
});
this.callbacks.push(() => {
_GM_removeValueChangeListener(listenerId);
});
return localValue;
} else return this.cacheData;
}
setLocalValue(value) {
this.cacheData = null;
this.cacheData = value;
_GM_setValue(this.storageKey, value);
}
set(key, value) {
const oldValue = this.get(key);
const localValue = this.getLocalValue();
Reflect.set(localValue, key, value);
this.setLocalValue(localValue);
this.emitValueChangeListener(key, value, oldValue);
}
get(key, defaultValue) {
const localValue = this.getLocalValue();
return Reflect.get(localValue, key) ?? defaultValue;
}
getAll() {
return this.getLocalValue();
}
delete(key) {
const oldValue = this.get(key);
const localValue = this.getLocalValue();
Reflect.deleteProperty(localValue, key);
this.setLocalValue(localValue);
this.emitValueChangeListener(key, void 0, oldValue);
}
has(key) {
const localValue = this.getLocalValue();
return Reflect.has(localValue, key);
}
keys() {
const localValue = this.getLocalValue();
return Reflect.ownKeys(localValue);
}
values() {
const localValue = this.getLocalValue();
return Reflect.ownKeys(localValue).map((key) => Reflect.get(localValue, key));
}
clear() {
this.destory();
_GM_deleteValue(this.storageKey);
}
addValueChangeListener(key, callback) {
const listenerId = Math.random();
const listenerData = this.listenerData.get(key) || [];
listenerData.push({
id: listenerId,
key,
callback,
});
this.listenerData.set(key, listenerData);
return listenerId;
}
removeValueChangeListener(listenerId) {
let flag = false;
for (const [key, listenerData] of this.listenerData.entries()) {
for (let index = 0; index < listenerData.length; index++) {
const value = listenerData[index];
if (
(typeof listenerId === "string" && value.key === listenerId) ||
(typeof listenerId === "number" && value.id === listenerId)
) {
listenerData.splice(index, 1);
index--;
flag = true;
}
}
this.listenerData.set(key, listenerData);
}
return flag;
}
async emitValueChangeListener(...args) {
const [key, newValue, oldValue] = args;
if (!this.listenerData.has(key)) return;
const listenerData = this.listenerData.get(key);
for (let index = 0; index < listenerData.length; index++) {
const data = listenerData[index];
if (typeof data.callback === "function") {
let __newValue;
let __oldValue;
if (args.length === 1) {
} else if (args.length === 2) __newValue = newValue;
else if (args.length === 3) {
__newValue = newValue;
__oldValue = oldValue;
}
await data.callback(key, __newValue, __oldValue);
}
}
}
};
var PopsPanelStorageApi = new StorageUtils(KEY);
var Panel = {
$data: {
__contentConfigInitDefaultValue: null,
__onceExecMenuData: null,
__urlChangeReloadMenuExecOnce: null,
__onceExecData: null,
__panelConfig: {},
$panel: null,
panelContent: [],
get contentConfigInitDefaultValue() {
if (this.__contentConfigInitDefaultValue == null) this.__contentConfigInitDefaultValue = new utils.Dictionary();
return this.__contentConfigInitDefaultValue;
},
contentConfigInitDisabledKeys: [],
get onceExecMenuData() {
if (this.__onceExecMenuData == null) this.__onceExecMenuData = new utils.Dictionary();
return this.__onceExecMenuData;
},
get urlChangeReloadMenuExecOnce() {
if (this.__urlChangeReloadMenuExecOnce == null) this.__urlChangeReloadMenuExecOnce = new utils.Dictionary();
return this.__urlChangeReloadMenuExecOnce;
},
get onceExecData() {
if (this.__onceExecData == null) this.__onceExecData = new utils.Dictionary();
return this.__onceExecData;
},
get scriptName() {
return SCRIPT_NAME;
},
get panelConfig() {
return this.__panelConfig;
},
set panelConfig(value) {
this.__panelConfig = value;
},
key: KEY,
attributeKeyName: ATTRIBUTE_KEY,
attributeDefaultValueName: ATTRIBUTE_DEFAULT_VALUE,
},
init() {
this.initContentDefaultValue();
PanelMenu.init();
},
initContentDefaultValue() {
const initDefaultValue = (config) => {
if (!config.attributes) return;
if (config.type === "button" || config.type === "container" || config.type === "deepMenu") return;
const attributes = config.attributes;
const __attr_init__ = attributes[ATTRIBUTE_INIT];
if (typeof __attr_init__ === "function") {
const __attr_result__ = __attr_init__();
if (typeof __attr_result__ === "boolean" && !__attr_result__) return;
}
const menuDefaultConfig = new Map();
const key = attributes[ATTRIBUTE_KEY];
if (key != null) {
const defaultValue = attributes[ATTRIBUTE_DEFAULT_VALUE];
menuDefaultConfig.set(key, defaultValue);
}
const moreMenuDefaultConfig = attributes[ATTRIBUTE_INIT_MORE_VALUE];
if (typeof moreMenuDefaultConfig === "object" && moreMenuDefaultConfig)
Object.keys(moreMenuDefaultConfig).forEach((key) => {
const defaultValue = moreMenuDefaultConfig[key];
menuDefaultConfig.set(key, defaultValue);
});
if (!menuDefaultConfig.size) {
log.warn("请先配置键", config);
return;
}
if (config.type === "switch") {
const disabled = typeof config.disabled === "function" ? config.disabled() : config.disabled;
if (typeof disabled === "boolean" && disabled)
this.$data.contentConfigInitDisabledKeys.push(...menuDefaultConfig.keys());
}
for (const [__key, __defaultValue] of menuDefaultConfig.entries()) this.setDefaultValue(__key, __defaultValue);
};
const loopInitDefaultValue = (configList) => {
for (let index = 0; index < configList.length; index++) {
const configItem = configList[index];
initDefaultValue(configItem);
const childViews = configItem.views;
if (childViews && Array.isArray(childViews)) loopInitDefaultValue(childViews);
}
};
const contentConfigList = [...PanelContent.getAllContentConfig()];
for (let index = 0; index < contentConfigList.length; index++) {
const leftContentConfigItem = contentConfigList[index];
if (!leftContentConfigItem.views) continue;
const rightContentConfigList = leftContentConfigItem.views;
if (rightContentConfigList && Array.isArray(rightContentConfigList))
loopInitDefaultValue(rightContentConfigList);
}
this.$data.contentConfigInitDisabledKeys = [...new Set(this.$data.contentConfigInitDisabledKeys)];
},
setDefaultValue(key, defaultValue) {
if (this.$data.contentConfigInitDefaultValue.has(key))
log.warn("该key已存在,初始化默认值失败: ", {
key,
initValue: this.$data.contentConfigInitDefaultValue.get(key),
});
this.$data.contentConfigInitDefaultValue.set(key, defaultValue);
},
getDefaultValue(key) {
return this.$data.contentConfigInitDefaultValue.get(key);
},
setValue(key, value) {
PopsPanelStorageApi.set(key, value);
},
getValue(key, defaultValue) {
const localValue = PopsPanelStorageApi.get(key);
if (localValue == null) {
if (this.$data.contentConfigInitDefaultValue.has(key)) return this.$data.contentConfigInitDefaultValue.get(key);
return defaultValue;
}
return localValue;
},
deleteValue(key) {
PopsPanelStorageApi.delete(key);
},
hasKey(key) {
return PopsPanelStorageApi.has(key);
},
addValueChangeListener(key, callback, option) {
const listenerId = PopsPanelStorageApi.addValueChangeListener(key, callback);
if (option?.immediate || option?.immediateAll) {
const value = this.getValue(key);
if (option?.immediate) callback(key, value, value);
else if (option?.immediateAll) Panel.emitMenuValueChange(key, value, value);
}
return listenerId;
},
removeValueChangeListener(listenerId) {
PopsPanelStorageApi.removeValueChangeListener(listenerId);
},
emitMenuValueChange(key, newValue, oldValue) {
PopsPanelStorageApi.emitValueChangeListener(key, newValue, oldValue);
},
async exec(queryKey, callback, checkExec, once = true) {
let queryKeyFn;
if (typeof queryKey === "string" || Array.isArray(queryKey)) queryKeyFn = () => queryKey;
else queryKeyFn = queryKey;
let isArrayKey = false;
const queryKeyResult = queryKeyFn();
let keyList = [];
if (Array.isArray(queryKeyResult)) {
isArrayKey = true;
keyList = queryKeyResult;
} else keyList.push(queryKeyResult);
const findNotInDataKey = keyList.find((it) => !this.$data.contentConfigInitDefaultValue.has(it));
if (findNotInDataKey) {
log.warn(`${findNotInDataKey} 键不存在`);
return;
}
const storageKey = JSON.stringify(keyList);
if (once) {
if (this.$data.onceExecMenuData.has(storageKey)) return this.$data.onceExecMenuData.get(storageKey);
}
const listenerIdList = [];
const panelMenuResultsHandler = new PanelMenuResultsHandler({
keyList,
getValue: (key) => {
const value = this.getValue(key);
return Boolean(value);
},
checkExec(keyList) {
let flag = false;
if (typeof checkExec === "function") flag = checkExec(keyList);
else flag = keyList.every((key) => this.getValue(key));
return flag;
},
});
const valueChangeCallback = async (valueOption) => {
const execFlag = panelMenuResultsHandler.checkMenuExec();
let callbackResult = [];
if (execFlag) {
const valueList = keyList.map((key) => this.getValue(key));
callbackResult = await callback({
key: keyList,
triggerKey: valueOption?.key,
value: isArrayKey ? valueList : valueList[0],
addStoreValue: (...args) => {
return panelMenuResultsHandler.handlerResult(execFlag, args);
},
});
}
panelMenuResultsHandler.handlerResult(execFlag, callbackResult);
};
if (once)
keyList.forEach((key) => {
const listenerId = this.addValueChangeListener(key, (key, newValue, oldValue) => {
return valueChangeCallback({
key,
newValue,
oldValue,
});
});
listenerIdList.push(listenerId);
});
await valueChangeCallback();
const result = {
checkMenuExec: panelMenuResultsHandler.checkMenuExec.bind(panelMenuResultsHandler),
keyList,
reload() {
this.clearStoreNodeList();
this.execDestoryFnAndClear();
valueChangeCallback();
},
clear() {
panelMenuResultsHandler.clearStoreNodeList();
this.execDestoryFnAndClear();
this.removeValueChangeListener();
this.clearOnceExecMenuData();
},
clearStoreNodeList: panelMenuResultsHandler.clearStoreNodeList.bind(panelMenuResultsHandler),
execDestoryFnAndClear: panelMenuResultsHandler.execDestoryFnAndClear.bind(panelMenuResultsHandler),
removeValueChangeListener: () => {
listenerIdList.forEach((listenerId) => {
this.removeValueChangeListener(listenerId);
});
},
clearOnceExecMenuData() {
if (once) Panel.$data.onceExecMenuData.delete(storageKey);
},
};
this.$data.onceExecMenuData.set(storageKey, result);
return result;
},
async execMenu(key, callback, isReverse = false, once = false) {
return await this.exec(
key,
async (...args) => {
return await callback(...args);
},
(keyList) => {
return keyList.every((__key__) => {
let flag = !!this.getValue(__key__);
if (Panel.$data.contentConfigInitDisabledKeys.includes(__key__)) {
flag = false;
log.warn(`.execMenu${once ? "Once" : ""} ${__key__} 被禁用`);
}
if (isReverse) flag = !flag;
return flag;
});
},
once
);
},
async execMenuOnce(key, callback, isReverse = false, listenUrlChange = false) {
const result = await this.execMenu(key, callback, isReverse, true);
if (listenUrlChange) {
if (result) {
const urlChangeCallback = () => {
result.reload();
};
this.removeUrlChangeWithExecMenuOnceListener(key);
this.addUrlChangeWithExecMenuOnceListener(key, urlChangeCallback);
}
}
return result;
},
async execMoreMenu(menus, allExecCallback, isReverse = false, once = false, listenUrlChange = false) {
const results = await Promise.all(
menus.map(async ([key, callback]) => {
return await this.execMenu(
key,
(...args) => {
return callback(...args);
},
isReverse,
once
);
})
);
const panelMenuResultsHandler = new PanelMenuResultsHandler({
keyList: menus.map(([key]) => key),
getValue: (key) => {
const value = this.getValue(key);
return Boolean(value);
},
});
const listenerIdList = [];
const __destory__ = (removeListener = false) => {
panelMenuResultsHandler.clearStoreNodeList();
panelMenuResultsHandler.execDestoryFnAndClear();
if (removeListener) {
for (const listenerId of listenerIdList) this.removeValueChangeListener(listenerId);
for (const result of results) if (result) this.removeUrlChangeWithExecMenuOnceListener(result.keyList);
}
};
const __allExecCallback__ = () => {
const allExecFlag = results.every((result) => {
if (result) return result.checkMenuExec();
else return true;
});
__destory__(false);
if (allExecFlag) {
const execResult = allExecCallback();
panelMenuResultsHandler.handlerResult(allExecFlag, execResult);
}
};
__allExecCallback__();
for (const result of results)
if (result) {
const listenerId = this.addValueChangeListener(result.keyList[0], () => {
__allExecCallback__();
});
listenerIdList.push(listenerId);
if (listenUrlChange) {
const urlChangeCallback = () => {
result.reload();
};
this.removeUrlChangeWithExecMenuOnceListener(result.keyList);
this.addUrlChangeWithExecMenuOnceListener(result.keyList, urlChangeCallback);
}
}
return {
clear() {
for (const result of results) result?.clear();
this.execDestoryFnAndClear();
this.removeValueChangeListener();
},
execDestoryFnAndClear() {
for (const result of results) result?.execDestoryFnAndClear();
__destory__(false);
},
removeValueChangeListener() {
for (const result of results) result?.removeValueChangeListener();
__destory__(true);
},
};
},
async execMoreMenuOnce(menus, allExecCallback, isReverse = false, listenUrlChange = false) {
return await this.execMoreMenu(menus, allExecCallback, isReverse, true, listenUrlChange);
},
deleteExecMenuOnce(key) {
key = this.transformKey(key);
this.$data.onceExecMenuData.delete(key);
this.$data.urlChangeReloadMenuExecOnce.delete(key);
return PopsPanelStorageApi.removeValueChangeListener(key);
},
onceExec(key, callback, runWithMenuEnable = false) {
key = this.transformKey(key);
if (typeof key !== "string") throw new TypeError("key 必须是字符串");
if (this.$data.onceExecData.has(key)) return;
if (runWithMenuEnable) {
if (
(Array.isArray(key) ? key : [key]).findIndex((it) => {
if (!!!Panel.getValue(it)) return true;
}) !== -1
)
return;
}
callback();
this.$data.onceExecData.set(key, 1);
},
deleteOnceExec(key) {
key = this.transformKey(key);
this.$data.onceExecData.delete(key);
},
addUrlChangeWithExecMenuOnceListener(key, callback) {
key = this.transformKey(key);
this.$data.urlChangeReloadMenuExecOnce.set(key, callback);
return {
off: () => {
return this.removeUrlChangeWithExecMenuOnceListener(key);
},
};
},
removeUrlChangeWithExecMenuOnceListener(key) {
key = this.transformKey(key);
this.$data.urlChangeReloadMenuExecOnce.delete(key);
},
hasUrlChangeWithExecMenuOnceListener(key) {
key = this.transformKey(key);
return this.$data.urlChangeReloadMenuExecOnce.has(key);
},
async emitUrlChangeWithExecMenuOnceEvent(config) {
const values = this.$data.urlChangeReloadMenuExecOnce.values();
for (const callback of values) await callback(config);
},
showPanel(
content,
title = `${SCRIPT_NAME}-设置`,
preventDefaultContentConfig = false,
preventRegisterSearchPlugin = false
) {
this.$data.$panel = null;
this.$data.panelContent = [];
const checkHasBottomVersionContentConfig =
content.findIndex((it) => {
return (
(typeof it.isBottom === "function" ? it.isBottom() : Boolean(it.isBottom)) && it.id === "script-version"
);
}) !== -1;
if (!preventDefaultContentConfig && !checkHasBottomVersionContentConfig)
content.push(...PanelContent.getDefaultBottomContentConfig());
const $panel = __pops__.panel({
title: {
text: title,
position: "center",
html: false,
style: "",
},
content,
btn: {
close: {
enable: true,
callback: (details) => {
details.close();
this.$data.$panel = null;
},
},
},
mask: {
enable: true,
clickEvent: {
toClose: true,
toHide: false,
},
clickCallBack: (originalRun) => {
originalRun();
this.$data.$panel = null;
},
},
width: PanelUISize.setting.width,
height: PanelUISize.setting.height,
drag: true,
only: true,
style: `
.pops-switch-shortcut-wrapper{
margin-right: 5px;
display: inline-flex;
}
.pops-switch-shortcut-wrapper:hover .pops-bottom-icon{
cursor: pointer;
}
`,
...this.$data.panelConfig,
});
this.$data.$panel = $panel;
this.$data.panelContent = content;
if (!preventRegisterSearchPlugin)
this.registerConfigSearch({
$panel,
content,
});
return {
$panel,
content,
};
},
registerConfigSearch(config) {
const { $panel, content } = config;
const translateCallback = (text, translateMap) => {
if (typeof config.translateCallback === "function") return config.translateCallback(text, translateMap);
else {
if (typeof translateMap === "object" && translateMap)
for (const key in translateMap) text = text.replaceAll(`{{${key}}}`, translateMap[key]);
return text;
}
};
const asyncQueryProperty = async (target, handler) => {
if (target == null) return;
const handleResult = await handler(target);
if (handleResult && typeof handleResult.isFind === "boolean" && handleResult.isFind) return handleResult.data;
return await asyncQueryProperty(handleResult.data, handler);
};
const scrollToElementAndListen = ($el, callback) => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
callback?.();
observer.disconnect();
}
});
},
{
root: null,
threshold: 1,
}
);
observer.observe($el);
$el.scrollIntoView({
behavior: "smooth",
block: "center",
});
};
const addFlashingClass = ($el) => {
const flashingClassName = "pops-flashing";
domUtils.onAnimationend($el, () => {
$el.classList.remove(flashingClassName);
});
$el.classList.add(flashingClassName);
};
const dbclick_callback = (evt) => {
if (evt.type === "dblclick" && isMobileTouch) return;
domUtils.preventEvent(evt);
const $alert = __pops__.alert({
title: {
text: translateCallback("搜索配置"),
position: "center",
},
content: {
text: `
`,
html: true,
},
btn: { ok: { enable: false } },
mask: { clickEvent: { toClose: true } },
width: PanelUISize.settingMiddle.width,
height: "auto",
drag: true,
style: `
${__pops__.config.cssText.panelCSS}
.search-wrapper{
border-bottom: 1px solid rgb(235, 238, 245, 1);
}
.pops-content:has(.search-result-wrapper:empty) .search-wrapper{
border-bottom: 0;
}
.search-config-text{
width: 100%;
border: 0;
height: 32px;
padding: 0px 10px;
outline: none;
}
.search-result-wrapper{
max-height: 400px;
overflow: auto;
}
.search-result-item{
cursor: pointer;
padding: 5px 10px;
display: flex;
flex-direction: column;
}
.search-result-item:hover{
background-color: #D8F1FD;
}
.search-result-item-path{
display: flex;
align-items: center;
flex-wrap: wrap;
}
.search-result-item-description{
font-size: 0.8em;
color: #6c6c6c;
}
${config.searchDialogStyle ?? ""}
`,
});
const $searchInput = $alert.$shadowRoot.querySelector(".search-config-text");
const $searchResultWrapper = $alert.$shadowRoot.querySelector(".search-result-wrapper");
$searchInput.focus();
const clearSearchResult = () => {
domUtils.empty($searchResultWrapper);
};
const createSearchResultItem = (pathInfo) => {
const searchPath = utils.queryProperty(pathInfo, (target) => {
if (target?.next)
return {
isFind: false,
data: target.next,
};
else
return {
isFind: true,
data: target,
};
});
const $item = domUtils.createElement("div", {
className: "search-result-item",
innerHTML: `
${searchPath.matchedData?.path}
${searchPath.matchedData?.description ?? ""}
`,
});
const panelHandlerComponents = __pops__.fn.PanelHandlerComponents();
domUtils.on($item, "click", () => {
const $targetAsideItem = $panel.$shadowRoot.querySelectorAll(
"aside.pops-panel-aside .pops-panel-aside-top-container li"
)[pathInfo.index];
if (!$targetAsideItem) {
qmsg.default.error(translateCallback(`左侧项下标{{index}}不存在`, { index: pathInfo.index }));
return;
}
$targetAsideItem.scrollIntoView({
behavior: "smooth",
block: "center",
});
$targetAsideItem.click();
asyncQueryProperty(pathInfo.next, async (target) => {
if (target?.next) {
const $findDeepMenu = await domUtils.waitNode(() => {
return Array.from($panel.$shadowRoot.querySelectorAll(".pops-panel-deepMenu-nav-item")).find(
($deepMenu) => {
const viewConfig = Reflect.get($deepMenu, panelHandlerComponents.$data.nodeStoreConfigKey);
return typeof viewConfig === "object" && viewConfig != null && viewConfig.text === target.name;
}
);
}, 2500);
if ($findDeepMenu) $findDeepMenu.click();
else {
qmsg.default.error(translateCallback("未找到对应的二级菜单"));
return {
isFind: true,
data: target,
};
}
return {
isFind: false,
data: target.next,
};
} else {
const $findTargetMenu = await domUtils.waitNode(() => {
return Array.from($panel.$shadowRoot.querySelectorAll(`li:not(.pops-panel-deepMenu-nav-item)`)).find(
($menuItem) => {
return (
Reflect.get($menuItem, panelHandlerComponents.$data.nodeStoreConfigKey) ===
target.matchedData?.formConfig
);
}
);
}, 2500);
if ($findTargetMenu) {
scrollToElementAndListen($findTargetMenu);
const $fold = $findTargetMenu.closest(`.pops-panel-forms-fold[data-fold-enable]`);
if ($fold) {
$fold.querySelector(".pops-panel-forms-fold-container").click();
await utils.sleep(500);
}
scrollToElementAndListen($findTargetMenu, () => {
addFlashingClass($findTargetMenu);
});
} else qmsg.default.error(translateCallback("未找到对应的菜单项"));
return {
isFind: true,
data: target,
};
}
});
});
return $item;
};
const execSearch = (searchText) => {
const searchTextRegExp = new RegExp(searchText, "i");
const searchConfigResult = [];
const loopContentConfig = (configList, path) => {
for (let index = 0; index < configList.length; index++) {
const configItem = configList[index];
const childViewConfig = configItem.views;
if (childViewConfig && Array.isArray(childViewConfig)) {
const deepMenuPath = utils.deepClone(path);
if (configItem.type === "deepMenu") {
const deepNext = utils.queryProperty(deepMenuPath, (target) => {
if (target?.next)
return {
isFind: false,
data: target.next,
};
else
return {
isFind: true,
data: target,
};
});
deepNext.next = { name: configItem.text };
}
loopContentConfig(childViewConfig, deepMenuPath);
} else {
let text;
let description;
if (configItem.type === "own") {
let searchConfig = Reflect.get(configItem.attributes || {}, ATTRIBUTE_PLUGIN_SEARCH_CONFIG);
if (searchConfig) {
if (typeof searchConfig === "function") searchConfig = searchConfig();
if (typeof searchConfig.text === "string") text = searchConfig.text;
if (typeof searchConfig.desc === "string") description = searchConfig.desc;
}
} else {
text = configItem.text;
description = Reflect.get(configItem, "description");
}
const delayMatchedTextList = [text, description];
const matchedIndex = delayMatchedTextList.findIndex((configText) => {
if (typeof configText !== "string") return;
return configText.match(searchTextRegExp);
});
if (matchedIndex !== -1) {
const matchedPath = utils.deepClone(path);
const deepNext = utils.queryProperty(matchedPath, (target) => {
if (target?.next)
return {
isFind: false,
data: target.next,
};
else
return {
isFind: true,
data: target,
};
});
deepNext.next = {
name: text,
matchedData: {
path: "",
formConfig: configItem,
matchedText: delayMatchedTextList[matchedIndex],
description,
},
};
const pathList = [];
utils.queryProperty(matchedPath, (target) => {
const name = target?.name;
if (typeof name === "string" && name.trim() !== "") pathList.push(name);
if (target?.next)
return {
isFind: false,
data: target.next,
};
else
return {
isFind: true,
data: target,
};
});
const pathStr = pathList.join(CommonUtil.escapeHtml(" - "));
deepNext.next.matchedData.path = pathStr;
searchConfigResult.push(matchedPath);
}
}
}
};
for (let index = 0; index < content.length; index++) {
const leftContentConfigItem = content[index];
if (!leftContentConfigItem.views) continue;
if (leftContentConfigItem.isBottom && leftContentConfigItem.id === "script-version") continue;
const rightContentConfigList = leftContentConfigItem.views;
if (rightContentConfigList && Array.isArray(rightContentConfigList)) {
let text = leftContentConfigItem.title;
if (typeof text === "function") text = text();
loopContentConfig(rightContentConfigList, {
index,
name: text,
});
}
}
const $fragment = document.createDocumentFragment();
for (const pathInfo of searchConfigResult) {
const $resultItem = createSearchResultItem(pathInfo);
$fragment.appendChild($resultItem);
}
clearSearchResult();
$searchResultWrapper.append($fragment);
};
domUtils.on(
$searchInput,
"input",
utils.debounce((evt2) => {
domUtils.preventEvent(evt2);
const searchText = domUtils.val($searchInput).trim();
if (searchText === "") {
clearSearchResult();
return;
}
execSearch(searchText);
}, 200)
);
};
$panel.$shadowRoot
.querySelectorAll(`aside.pops-panel-aside .pops-panel-aside-item:not(#script-version)`)
.forEach(($asideItem) => {
domUtils.on($asideItem, "dblclick", dbclick_callback);
});
const clickMap = new WeakMap();
let isDoubleClick = false;
let timer = void 0;
let isMobileTouch = false;
domUtils.on(
$panel.$shadowRoot,
"touchend",
`aside.pops-panel-aside .pops-panel-aside-item:not(#script-version)`,
(evt, $selector) => {
isMobileTouch = true;
clearTimeout(timer);
timer = void 0;
if (isDoubleClick && clickMap.has($selector)) {
isDoubleClick = false;
clickMap.delete($selector);
dbclick_callback(evt);
} else {
timer = setTimeout(() => {
isDoubleClick = false;
}, 200);
isDoubleClick = true;
clickMap.set($selector, evt);
}
},
{ capture: true }
);
$panel.$shadowRoot.appendChild(
domUtils.createElement("style", {
type: "text/css",
textContent: `
.pops-flashing{
animation: double-blink 1.5s ease-in-out;
}
@keyframes double-blink {
0% {
background-color: initial;
}
25% {
background-color: yellow;
}
50% {
background-color: initial;
}
75% {
background-color: yellow;
}
100% {
background-color: initial;
}
}
`,
})
);
},
transformKey(key) {
if (Array.isArray(key))
if (key.length > 1) {
const keyArray = key.sort();
return JSON.stringify(keyArray);
} else return key[0];
else return key;
},
getDynamicValue(key, defaultValue) {
let isInit = false;
let __value = defaultValue;
const listenerId = this.addValueChangeListener(key, (_, newValue) => {
__value = newValue;
});
return {
get value() {
if (!isInit) {
isInit = true;
__value = Panel.getValue(key, defaultValue);
}
return __value;
},
destory() {
Panel.removeValueChangeListener(listenerId);
},
};
},
};
var PanelSettingConfig = {
qmsg_config_position: {
key: "qmsg-config-position",
defaultValue: "bottom",
},
qmsg_config_maxnums: {
key: "qmsg-config-maxnums",
defaultValue: 3,
},
qmsg_config_showreverse: {
key: "qmsg-config-showreverse",
defaultValue: false,
},
httpx_cookie_manager_enable: {
key: "httpx-use-cookie-enable",
defaultValue: false,
},
httpx_cookie_manager_use_document_cookie: {
key: "httpx-use-document-cookie",
defaultValue: false,
},
};
var utils = _whitesev_utils.default.noConflict();
var domUtils = _whitesev_domutils.default.noConflict();
var __pops__ = _whitesev_pops.default;
var log = new utils.Log(_GM_info, _unsafeWindow.console || _monkeyWindow.console);
var SCRIPT_NAME = _GM_info?.script?.name || void 0;
var AnyTouch = _whitesev_pops.default.fn.Utils.AnyTouch();
log.config({
debug: false,
logMaxCount: 250,
autoClearConsole: true,
tag: true,
});
var getPageMaxZIndex = () => {
const deviation = 100;
const popsZIndex = _whitesev_pops.default.fn.InstanceUtils.getPopsMaxZIndex()?.zIndex ?? 0;
const pointZIndex = utils.getMaxZIndexNodeInfoFromPoint()[0]?.zIndex ?? 0;
return Math.max(deviation, popsZIndex, pointZIndex);
};
qmsg.default.config({
isHTML: true,
autoClose: true,
showClose: false,
consoleLogContent(qmsgInst) {
const qmsgType = qmsgInst.setting.type;
if (qmsgType === "loading") return false;
const content = qmsgInst.setting.content;
if (qmsgType === "warning") log.warn(content);
else if (qmsgType === "error") log.error(content);
else log.info(content);
return false;
},
get position() {
return Panel.getValue(
PanelSettingConfig.qmsg_config_position.key,
PanelSettingConfig.qmsg_config_position.defaultValue
);
},
get maxNums() {
return Panel.getValue(
PanelSettingConfig.qmsg_config_maxnums.key,
PanelSettingConfig.qmsg_config_maxnums.defaultValue
);
},
get showReverse() {
return Panel.getValue(
PanelSettingConfig.qmsg_config_showreverse.key,
PanelSettingConfig.qmsg_config_showreverse.defaultValue
);
},
get zIndex() {
return getPageMaxZIndex();
},
});
__pops__.GlobalConfig.setGlobalConfig({
zIndex: () => {
return getPageMaxZIndex();
},
mask: {
enable: true,
clickEvent: {
toClose: false,
toHide: false,
},
},
drag: true,
});
var MenuRegister = new utils.GM_Menu({
GM_getValue: _GM_getValue,
GM_setValue: _GM_setValue,
GM_registerMenuCommand: _GM_registerMenuCommand,
GM_unregisterMenuCommand: _GM_unregisterMenuCommand,
});
var httpx = new utils.Httpx({
xmlHttpRequest: _GM_xmlhttpRequest,
logDetails: false,
});
httpx.interceptors.request.use((data) => {
return data;
});
httpx.interceptors.response.use(
(response) => {
return response;
},
(data) => {
log.error("[Httpx-HttpxRequest.response] 响应错误", { data });
if (data.type === "onabort") qmsg.default.warning("请求取消", { consoleLogContent: true });
else if (data.type === "onerror") qmsg.default.error("请求异常", { consoleLogContent: true });
else if (data.type === "ontimeout") qmsg.default.error("请求超时", { consoleLogContent: true });
else qmsg.default.error("其它错误", { consoleLogContent: true });
return data;
}
);
(_unsafeWindow.Object.defineProperty,
_unsafeWindow.Object.keys,
_unsafeWindow.Object.values,
_unsafeWindow.Function.prototype.apply,
_unsafeWindow.Function.prototype.call,
_unsafeWindow.Element.prototype.appendChild,
_unsafeWindow.setTimeout.bind(_unsafeWindow),
_unsafeWindow.clearTimeout.bind(_unsafeWindow),
_unsafeWindow.setInterval.bind(_unsafeWindow),
_unsafeWindow.clearInterval.bind(_unsafeWindow));
var addStyle = domUtils.addStyle.bind(domUtils);
CommonUtil.addBlockCSS.bind(CommonUtil);
_whitesev_domutils.default.selector.bind(_whitesev_domutils.default);
_whitesev_domutils.default.selectorAll.bind(_whitesev_domutils.default);
var cookieManager = new utils.CookieManagerService({ baseCookieHandler: "GM_cookie" });
if (!cookieManager.isSupportGM_cookie)
if (cookieManager.isSupportCookieStore) cookieManager.setOptions({ baseCookieHandler: "cookieStore" });
else cookieManager.setOptions({ baseCookieHandler: "document.cookie" });
new utils.DocumentCookieHandler();
var JianshuRouter = {
isGoWild() {
return window.location.pathname === "/go-wild";
},
};
var waitForElementToRemove = function (selectorText = "") {
domUtils.waitNodeList(selectorText).then((nodeList) => {
nodeList.forEach((item) => item.remove());
});
};
var Jianshu = {
init() {
this.addCSS();
Panel.execMenu("JianShuAutoJumpRedirect_PC", () => {
this.jumpRedirect();
});
Panel.execMenu("JianShuRemoveClipboardHijacking", () => {
this.removeClipboardHijacking();
});
Panel.execMenu("JianShuAutoExpandFullText", () => {
this.autoExpandFullText();
});
Panel.execMenu("JianShuArticleCenter", () => {
return this.articleCenter();
});
Panel.execMenu("JianShuShieldRelatedArticles", () => {
return this.blockRelatedArticles();
});
Panel.execMenu("jianshu-shieldClientDialog", () => {
this.blockClientDialog();
});
Panel.execMenuOnce("JianShuShieldUserComments", () => {
return this.blockUserComments();
});
Panel.execMenuOnce("JianShuShieldRecommendedReading", () => {
return this.blockRecommendedReading();
});
Panel.execMenuOnce("jianshu-shieldTopNav", () => {
return this.blockTopNav();
});
Panel.execMenuOnce("jianshu-shieldBottomToolbar", () => {
return this.blockBottomToolbar();
});
},
addCSS() {
log.info("添加屏蔽CSS");
return addStyle(block_default);
},
articleCenter() {
log.info("全文居中");
let result = [];
result.push(
CommonUtil.addBlockCSS("div[role=main] aside", "div._3Pnjry"),
addStyle(`
div[role=main] aside,
div._3Pnjry{
display: none !important;
}
div._gp-ck{
width: 100% !important;
}`)
);
waitForElementToRemove("div[role=main] aside");
waitForElementToRemove("div._3Pnjry");
domUtils.waitNodeList("div._gp-ck").then((nodeList) => {
nodeList.forEach((item) => {
item.style["width"] = "100%";
});
});
return result;
},
removeClipboardHijacking() {
log.info("去除剪贴板劫持");
const stopNativePropagation = (event) => {
event.stopPropagation();
};
window.addEventListener("copy", stopNativePropagation, true);
document.addEventListener("copy", stopNativePropagation, true);
},
autoExpandFullText() {
domUtils.waitNode(`div#homepage div[class*="dialog-"]`).then((element) => {
element.style["visibility"] = "hidden";
log.info("自动展开全文");
utils.mutationObserver(element, {
callback: (mutations) => {
if (mutations.length == 0) return;
mutations.forEach((mutationItem) => {
if (mutationItem.target.style["display"] != "none") {
log.success("自动展开全文-自动点击");
document.querySelector('div#homepage div[class*="dialog-"] .cancel')?.click();
}
});
},
config: {
childList: false,
attributes: true,
characterData: true,
subtree: true,
},
});
});
},
jumpRedirect() {
if (JianshuRouter.isGoWild()) {
log.success("去除简书拦截其它网址的url并自动跳转");
window.stop();
let search = window.location.href.replace(window.location.origin + "/", "");
search = decodeURIComponent(search);
let newURL = search.replace(/^go-wild\?ac=2&url=/gi, "").replace(/^https:\/\/link.zhihu.com\/\?target\=/gi, "");
window.location.href = newURL;
}
},
blockRelatedArticles() {
log.info("屏蔽相关文章");
return CommonUtil.addBlockCSS('div[role="main"] > div > section:nth-child(2)');
},
blockClientDialog() {
log.info("【屏蔽】客户端弹窗");
CommonUtil.addBlockCSS(
'div:has(>div[class*="-mask"]:not([class*="-mask-hidden"]) + div[tabindex="-1"][role="dialog"])'
);
domUtils
.waitNode(`div[class*="-mask"]:not([class*="-mask-hidden"]) + div[tabindex="-1"][role="dialog"]`)
.then((element) => {
log.success("弹窗出现");
utils
.waitPropertyByInterval(
element,
() => {
return utils.getReactInstance(element)?.reactInternalInstance?.return?.return?.memoizedProps?.onClose;
},
250,
1e4
)
.then(() => {
utils
.getReactInstance(element)
.reactInternalInstance.return.return.memoizedProps.onClose(new Event("click"));
log.success("调用函数关闭弹窗");
});
});
},
blockUserComments() {
log.info("屏蔽评论区");
return CommonUtil.addBlockCSS("div#note-page-comment");
},
blockRecommendedReading() {
log.info("屏蔽底部推荐阅读");
return CommonUtil.addBlockCSS('div[role="main"] > div > section:last-child');
},
blockTopNav() {
log.info("【屏蔽】顶部导航栏");
return CommonUtil.addBlockCSS("header");
},
blockBottomToolbar() {
log.info("【屏蔽】底部工具栏");
return CommonUtil.addBlockCSS("footer");
},
};
var M_Jianshu = {
init() {
this.addCSS();
Panel.execMenu("JianShuAutoJumpRedirect_Mobile", () => {
Jianshu.jumpRedirect();
});
Panel.execMenu("JianShuHijackSchemeScriptLabel_Mobile", () => {
this.handlePrototype();
});
Panel.execMenu("JianShuRemoveClipboardHijacking_Mobile", () => {
Jianshu.removeClipboardHijacking();
});
Panel.execMenu("JianShuAutoExpandFullText_Mobile", () => {
Jianshu.autoExpandFullText();
});
Panel.execMenuOnce("JianShuremoveFooterRecommendRead", () => {
return this.blockeFooterRecommendRead();
});
Panel.execMenu("JianShuShieldUserCommentsMobile", () => {
return this.blockUserComments();
});
},
addCSS() {
Jianshu.addCSS();
},
blockeFooterRecommendRead() {
log.info("屏蔽底部推荐阅读");
return CommonUtil.addBlockCSS("#recommended-notes");
},
handlePrototype() {
log.info("处理原型添加script标签");
let originalAppendChild = Node.prototype.appendChild;
_unsafeWindow.Node.prototype.appendChild = function (element) {
if (element.src && !element.src.includes("jianshu.io") && !["img"].includes(element.localName)) {
log.success(["禁止添加的元素", element]);
return null;
} else return originalAppendChild.call(this, element);
};
},
blockUserComments() {
log.info("屏蔽评论区");
return CommonUtil.addBlockCSS("#comment-main");
},
};
var UISelect = function (text, key, defaultValue, data, selectCallBack, description, valueChangeCallBack) {
const result = {
text,
type: "select",
description,
attributes: {},
props: {},
getValue() {
return this.props[PROPS_STORAGE_API].get(key, defaultValue);
},
callback(isSelectedInfo) {
if (isSelectedInfo == null) return;
const value = isSelectedInfo.value;
log.info(`选择:${isSelectedInfo.text}`);
if (typeof selectCallBack === "function") {
if (selectCallBack(isSelectedInfo)) return;
}
this.props[PROPS_STORAGE_API].set(key, value);
if (typeof valueChangeCallBack === "function") valueChangeCallBack(isSelectedInfo);
},
data,
};
Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
PanelComponents.initComponentsStorageApi("select", result, {
get(key, defaultValue) {
return Panel.getValue(key, defaultValue);
},
set(key, value) {
Panel.setValue(key, value);
},
});
return result;
};
var PanelComponents = {
$data: {
__storeApiFn: null,
get storeApiValue() {
if (!this.__storeApiFn) this.__storeApiFn = new _whitesev_utils.default.Dictionary();
return this.__storeApiFn;
},
},
getStorageApi(type) {
if (!this.hasStorageApi(type)) return;
return this.$data.storeApiValue.get(type);
},
hasStorageApi(type) {
return this.$data.storeApiValue.has(type);
},
setStorageApi(type, storageApiValue) {
this.$data.storeApiValue.set(type, storageApiValue);
},
initComponentsStorageApi(type, config, storageApiValue) {
let propsStorageApi;
if (this.hasStorageApi(type)) propsStorageApi = this.getStorageApi(type);
else propsStorageApi = storageApiValue;
this.setComponentsStorageApiProperty(config, propsStorageApi);
},
setComponentsStorageApiProperty(config, storageApiValue) {
Reflect.set(config.props, PROPS_STORAGE_API, storageApiValue);
},
};
var UISwitch = function (
text,
key,
defaultValue = false,
clickCallBack,
description,
afterAddToUListCallBack,
disabled,
valueChangeCallBack,
shortCutOption
) {
if (shortCutOption && typeof shortCutOption.defaultValue === "object" && shortCutOption.defaultValue != null) {
const shortCutKey = shortCutOption.key ?? key;
shortCutOption.handler.add({
key: shortCutKey,
name: text,
});
shortCutOption.handler.shortCut.initConfig(shortCutKey, shortCutOption.defaultValue);
}
const result = {
text,
type: "switch",
description,
disabled,
attributes: {},
props: {},
getValue() {
return this.props[PROPS_STORAGE_API].get(key, defaultValue);
},
callback(event, __value) {
const value = Boolean(__value);
log.success(`${value ? "开启" : "关闭"} ${text}`);
if (typeof clickCallBack === "function") {
if (clickCallBack(event, value)) return;
}
this.props[PROPS_STORAGE_API].set(key, value);
if (typeof valueChangeCallBack === "function") valueChangeCallBack(event, value);
},
afterAddToUListCallBack: (...args) => {
afterAddToUListCallBack?.(...args);
if (shortCutOption) {
const shortCut = shortCutOption.handler.shortCut;
const shortCutKey = shortCutOption.key ?? key;
const [_, container] = args;
const $leftMainText = container.target?.querySelector(".pops-panel-item-left-main-text");
if (!$leftMainText) return;
const renderKeyboard = () => {
const tooltipShowText = shortCutOption.handler.shortCut.getShowText(shortCutKey, "暂未录入快捷键");
const $wrapper = domUtils.createElement(
"div",
{
className: "pops-switch-shortcut-wrapper",
innerHTML: `
`,
},
{ style: "margin-right: 5px;display: inline-flex;" }
);
const $icon = $wrapper.querySelector(".pops-bottom-icon");
domUtils.on(
$icon,
"click",
function (evt) {
shortCutOption.handler.shortCut.deleteOption(shortCutKey);
$tooltip.toolTip.offEvent();
$tooltip.toolTip.close();
$tooltip.toolTip.destory();
$wrapper.remove();
},
{ once: true }
);
const $tooltip = __pops__.tooltip({
$target: $icon,
content: () => {
return tooltipShowText;
},
className: "github-tooltip",
isFixed: true,
only: true,
});
domUtils.empty($leftMainText);
domUtils.append($leftMainText, $wrapper, text);
};
__pops__.rightClickMenu({
$target: $leftMainText,
only: true,
data: [
{
text: () => {
if (shortCutOption.handler.shortCut.hasOption(shortCutKey)) return "修改快捷键";
else return "添加快捷键";
},
icon: __pops__.config.iconSVG.keyboard,
callback(clickEvent, contextMenuEvent, $li, $listenerRootNode) {
if (shortCut.isWaitKeyboardPress()) {
qmsg.default.warning("请先执行当前的录入操作");
return;
}
const $loading = qmsg.default.loading("请按下快捷键...", {
showClose: true,
onClose() {
shortCut.cancelEnterShortcutKeys();
},
});
shortCut.enterShortcutKeys(shortCutKey).then(({ status, option, key: isUsedKey }) => {
$loading.close();
if (status) {
log.success("录入快捷键", option);
qmsg.default.success("录入成功");
renderKeyboard();
} else
qmsg.default.error(
`快捷键 ${shortCut.translateKeyboardValueToButtonText(option)} 已被 ${isUsedKey} 占用`
);
});
},
},
],
});
if (!shortCut.hasOption(shortCutKey)) return;
renderKeyboard();
}
},
};
Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
PanelComponents.initComponentsStorageApi("switch", result, {
get(key, defaultValue) {
return Panel.getValue(key, defaultValue);
},
set(key, value) {
Panel.setValue(key, value);
},
});
return result;
};
var SettingUIMobile = {
id: "jianshu-panel-config-mobile",
title: "移动端",
views: [
{
text: "",
type: "container",
views: [
{
text: "功能",
type: "deepMenu",
views: [
{
text: "",
type: "container",
views: [
UISwitch("自动展开全文", "JianShuAutoExpandFullText_Mobile", true),
UISwitch("重定向链接", "JianShuAutoJumpRedirect_Mobile", true, void 0, "自动跳转简书拦截的Url链接"),
],
},
],
},
{
text: "屏蔽",
type: "deepMenu",
views: [
{
text: "",
type: "container",
views: [
UISwitch("【屏蔽】底部推荐阅读", "JianShuremoveFooterRecommendRead", false),
UISwitch("【屏蔽】评论区", "JianShuShieldUserCommentsMobile", false),
],
},
],
},
{
text: "劫持/拦截",
type: "deepMenu",
views: [
{
text: "",
type: "container",
views: [
UISwitch("拦截-剪贴板", "JianShuRemoveClipboardHijacking_Mobile", true, void 0, "去除禁止复制"),
UISwitch(
"劫持-唤醒/跳转App",
"JianShuHijackSchemeScriptLabel_Mobile",
true,
void 0,
"去除简书唤醒调用App"
),
],
},
],
},
],
},
],
};
var SettingUICommon = {
id: "jianshu-panel-common",
title: "通用",
views: [
{
text: "Toast配置",
type: "container",
views: [
UISelect(
"Toast位置",
"qmsg-config-position",
"bottom",
[
{
value: "topleft",
text: "左上角",
},
{
value: "top",
text: "顶部",
},
{
value: "topright",
text: "右上角",
},
{
value: "left",
text: "左边",
},
{
value: "center",
text: "中间",
},
{
value: "right",
text: "右边",
},
{
value: "bottomleft",
text: "左下角",
},
{
value: "bottom",
text: "底部",
},
{
value: "bottomright",
text: "右下角",
},
],
(isSelectedInfo) => {
log.info("设置当前Qmsg弹出位置" + isSelectedInfo.text);
},
"Toast显示在页面九宫格的位置"
),
UISelect(
"最多显示的数量",
"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,
"限制Toast显示的数量"
),
UISwitch("逆序弹出", "qmsg-config-showreverse", false, void 0, "修改Toast弹出的顺序"),
],
},
],
};
var SettingUIPC = {
id: "jianshu-panel-config-pc",
title: "桌面端",
views: [
{
text: "",
type: "container",
views: [
{
text: "功能",
type: "deepMenu",
views: [
{
text: "",
type: "container",
views: [
UISwitch("全文居中", "JianShuArticleCenter", true),
UISwitch("自动展开全文", "JianShuAutoExpandFullText", true),
UISwitch("重定向链接", "JianShuAutoJumpRedirect_PC", true, void 0, "自动跳转简书拦截的Url链接"),
],
},
],
},
{
text: "屏蔽",
type: "deepMenu",
views: [
{
text: "",
type: "container",
views: [
UISwitch("【屏蔽】底部推荐阅读", "JianShuShieldRecommendedReading", false),
UISwitch("【屏蔽】评论区", "JianShuShieldUserComments", false),
UISwitch("【屏蔽】相关文章", "JianShuShieldRelatedArticles", false),
UISwitch(
"【屏蔽】客户端弹窗",
"jianshu-shieldClientDialog",
true,
void 0,
"弹出的【扫码安装简书客户端 畅享全文阅读体验】"
),
UISwitch("【屏蔽】顶部导航栏", "jianshu-shieldTopNav", false),
UISwitch(
"【屏蔽】底部工具栏",
"jianshu-shieldBottomToolbar",
false,
void 0,
"屏蔽掉底部悬浮的评论输入框、评论、点赞..."
),
],
},
],
},
{
text: "劫持/拦截",
type: "deepMenu",
views: [
{
text: "",
type: "container",
views: [UISwitch("拦截-剪贴板", "JianShuRemoveClipboardHijacking", true, void 0, "去除禁止复制")],
},
],
},
],
},
],
};
PanelContent.addContentConfig([SettingUICommon, SettingUIPC, SettingUIMobile]);
Panel.init();
var isMobile = utils.isPhone();
var CHANGE_ENV_SET_KEY = "change_env_set";
var chooseMode = _GM_getValue(CHANGE_ENV_SET_KEY);
MenuRegister.add({
key: CHANGE_ENV_SET_KEY,
text: `⚙ 自动: ${isMobile ? "移动端" : "PC端"}`,
autoReload: false,
isStoreValue: false,
showText(text) {
if (chooseMode == null) return text;
return text + ` 手动: ${chooseMode == 1 ? "移动端" : chooseMode == 2 ? "PC端" : "未知"}`;
},
callback: () => {
let allowValue = [0, 1, 2];
let chooseText = window.prompt("请输入当前脚本环境判定\n\n自动判断: 0\n移动端: 1\nPC端: 2", "0");
if (!chooseText) return;
let chooseMode = parseInt(chooseText);
if (isNaN(chooseMode)) {
qmsg.default.error("输入的不是规范的数字");
return;
}
if (!allowValue.includes(chooseMode)) {
qmsg.default.error("输入的值必须是0或1或2");
return;
}
if (chooseMode == 0) _GM_deleteValue(CHANGE_ENV_SET_KEY);
else _GM_setValue(CHANGE_ENV_SET_KEY, chooseMode);
},
});
if (chooseMode != null) {
log.info(`手动判定为${chooseMode === 1 ? "移动端" : "PC端"}`);
if (chooseMode == 1) M_Jianshu.init();
else if (chooseMode == 2) Jianshu.init();
else {
qmsg.default.error("意外,手动判定的值不在范围内");
_GM_deleteValue(CHANGE_ENV_SET_KEY);
}
} else if (isMobile) {
log.info("自动判定为移动端");
M_Jianshu.init();
} else {
log.info("自动判定为PC端");
Jianshu.init();
}
})(DOMUtils, pops, Utils, Qmsg);