// ==UserScript== // @name 福利吧小助手 // @namespace https://greasyfork.org/zh-CN/users/860681-aoguai // @version 1.0.7.1 // @description 一个用于增强福利吧功能的油猴脚本,提供链接提取、界面美化、编解码等功能。 // @author aoguai // @match http://fulibus.net/* // @match https://fulibus.net/* // @match http://fuliba2025.net/* // @match https://fuliba2025.net/* // @match http://*.fuliba2025.net/* // @match https://*.fuliba2025.net/* // @match http://fuliba2020.net/* // @match https://fuliba2020.net/* // @match http://fuliba2021.net/* // @match https://fuliba2021.net/* // @match http://fuliba2022.net/* // @match https://fuliba2022.net/* // @match http://fuliba2023.net/* // @match https://fuliba2023.net/* // @match http://fuliba2024.net/* // @match https://fuliba2024.net/* // @match http://fuliba2026.net/* // @match https://fuliba2026.net/* // @match http://fuliba2027.net/* // @match https://fuliba2027.net/* // @match http://fuliba2028.net/* // @match https://fuliba2028.net/* // @match http://fuliba2029.net/* // @match https://fuliba2029.net/* // @match http://fuliba2030.net/* // @match https://fuliba2030.net/* // @match http://f.uliba.net/* // @match https://f.uliba.net/* // @match http://*.wnflb2020.com/* // @match https://*.wnflb2020.com/* // @match http://*.wnflb2021.com/* // @match https://*.wnflb2021.com/* // @match http://*.wnflb2022.com/* // @match https://*.wnflb2022.com/* // @match http://*.wnflb2024.com/* // @match https://*.wnflb2024.com/* // @match http://*.wnflb2025.com/* // @match https://*.wnflb2025.com/* // @match http://*.wnflb2026.com/* // @match https://*.wnflb2026.com/* // @match http://*.wnflb2027.com/* // @match https://*.wnflb2027.com/* // @match http://*.wnflb2028.com/* // @match https://*.wnflb2028.com/* // @match http://*.wnflb2029.com/* // @match https://*.wnflb2029.com/* // @match http://*.wnflb2030.com/* // @match https://*.wnflb2030.com/* // @match http://www.wnflb2020.com/* // @match https://www.wnflb2020.com/* // @match http://www.wnflb2021.com/* // @match https://www.wnflb2021.com/* // @match http://www.wnflb2022.com/* // @match https://www.wnflb2022.com/* // @match http://www.wnflb2024.com/* // @match https://www.wnflb2024.com/* // @match http://www.wnflb2025.com/* // @match https://www.wnflb2025.com/* // @match http://www.wnflb2026.com/* // @match https://www.wnflb2026.com/* // @match http://www.wnflb2027.com/* // @match https://www.wnflb2027.com/* // @match http://www.wnflb2028.com/* // @match https://www.wnflb2028.com/* // @match http://www.wnflb2029.com/* // @match https://www.wnflb2029.com/* // @match http://www.wnflb2030.com/* // @match https://www.wnflb2030.com/* // @match http://wnflb2023.com/* // @match https://wnflb2023.com/* // @match http://*.wnflb2023.com/* // @match https://*.wnflb2023.com/* // @icon data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAC1puYAeG/TAHxz1CF5cNJJe3HQBnt1ygJ7c8wIgXy9AXt0zAd4c8oGdm7bAHp9qAAAAAAAAAAAAAAAAAAAAAAAioDaACckvAB4cNSQeW/V1X5z1CZ7cNV9em/Vsnpv1ZR4b9OyeW/UpH1x1SN8cNYArL9QAJOI2wB3a9YAf3LYL31v2YF+cdc5enDWtHlv1cR9cdeHe2/X6H1w2c57b9jzenDVmnlv1vN7b9aWhXfNA39z0gCEe9kAh3/YBHxw2Kp8btn/fG/Y1ntv1915cNXAe2/Y2Hpv1+98b9nwfG/Z+X1x14F4b9XRem/X73lw0kB4b9QAhnvcAI+N1wF9cNqLe27Z/npv1/95b9b/eHDT2Hhv1fJ6cNWxfG/Z1npv1/97cNe5eHHUl3hu1f93b9Ope3TMB7C74wB/ctsAgXTcD3pw15h5b9b8eG7V/3Zu0/x3btT+enDWx3pv1+t5b9b2enHTUHlx00l3b9T4d2/U1Hpy0BkAAAAAfXLWAIF02B98cNejeW/W+npv1/95b9b/eW/W8nlw1KZ8cdeTeW/W83lv1eB6b9fjeW/W/3lw1at+dtAKrafkAHdr1wB8cdd5e2/Y/3pv1v96b9f/em/X/Hlv1el5b9Tae3DX2Xpw1ux6b9f/enDX5Hxx2KB8c9YneG/WAKyn5AB6btcAe3HWTHlv1ud6cNbeeG/U+3pv1vR7cdaleG/U8npw1cx5b9bzeW/V9Xty1FaXiucCgXXaAMK95wAAAAAAfnbWAIR+1gJ7cdZZe3HVq3lw1KB7cNe0f3PZXXpv1uV5cNTKem/W5Xlv1OF5cdE3dnDTAIN1zAAAAAAAAAAAAAAAAACDd9wAmo3xAXty1kl6cNScenDVe31x1298cNjVfXDYwntv1/R5btX/eG/Uw3px0RR6cdEAAAAAAAAAAAAAAAAAAAAAAIF02gCBdNskem/W33lu1v97cNfRe2/X53xu2f58b9j6e2/Y/3lv1ct8c9QVfXPUAAAAAAAAAAAAAAAAAAAAAAB+cdkAf3LZRHlv1fd3btT/eG/V+npv1/58btr/e2/Y/3pv1/94b9Ovf3nMBH52zQAAAAAAAAAAAAAAAAAAAAAAfHLWAH1z1zF4b9Tjd2/U4Hpx1Yp7cdeMe3DYpHlv1vF5b9X+eHDSgW5Z6QB8eMoAAAAAAAAAAAAAAAAAAAAAAH521gB/d9YEeXLUMnly1Cx+e9IEf3vUAn921wd9ctg/e3HWVnp00xJ5ctMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AcAAPgDAADAAQAAgAEAAIAAAADAAAAAwAAAAMABAADAAwAAwAcAAOADAADwAwAA8AMAAPAHAADwBwAA//8AAA== // @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js // @require https://greasyfork.org/scripts/403716-gm-config-cn/code/GM_config_CN.js // @connect keyfc.net // @connect fuliba123.com // @grant GM_info // @grant GM_setClipboard // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @grant GM_addStyle // @run-at document-start // @early-start // @noframes // @license GNU General Public License v3.0 or later // @namespace https://greasyfork.org/scripts/522004 // @supportURL https://greasyfork.org/scripts/522004 // @homepageURL https://greasyfork.org/scripts/522004 // ==/UserScript== (function () { "use strict"; if (typeof GM_config == "undefined") { console.error( "福利吧小助手:\nGM_config 库文件加载失败,脚本无法正常工作,请尝试刷新网页以重新加载。", ); } else if (typeof document.arrive == "undefined") { console.error( "福利吧小助手:\narrive 库文件加载失败,脚本可能无法正常工作,请尝试刷新网页以重新加载。", ); } else if (typeof CryptoJS == "undefined") { console.warn( "福利吧小助手:\nCryptoJS 库文件加载失败,BASE64 编解码功能无法使用。", ); } else { console.debug("福利吧小助手:\n脚本工作环境正常。"); } const icon = { settings: '', code: '', signature: '', collapsedYes: "static/image/common/collapsed_yes.gif", collapsedNo: "static/image/common/collapsed_no.gif", }; const darkColorFields = { darkBgColor: "--good-boy-dark-bg", darkSurfaceColor: "--good-boy-dark-surface", darkInputColor: "--good-boy-dark-input", darkHoverColor: "--good-boy-dark-hover", darkTextColor: "--good-boy-dark-text", darkMutedColor: "--good-boy-dark-muted", darkBorderColor: "--good-boy-dark-border", darkAccentColor: "--good-boy-dark-accent", }; const style = { // 设置面板样式 settings: ` #myGoodBoyConfig { --primary: #4A90E2; --text: #333; --bg: #FFF; --border: #E0E0E0; font-family: system-ui, sans-serif; line-height: 1.6; } #myGoodBoyConfig .section_header { padding: 1rem; background: var(--primary); border-radius: 0.4rem; } #myGoodBoyConfig input[type="checkbox"] { width: 1.2em; height: 1.2em; } #myGoodBoyConfig input[type="color"] { width: 3rem; height: 2rem; padding: 2px; vertical-align: middle; cursor: pointer; } #myGoodBoyConfig button { padding: 0.6em 1.2em; border-radius: 0.4em; transition: opacity 0.2s; } #myGoodBoyConfig button:hover { opacity: 0.9; } `, // 代码面板样式 codePanel: ` #myCodePanel { width: 35vw !important; max-width: 25rem; min-height: 20rem; border-radius: 0.8rem; box-shadow: 0 0.2rem 1rem rgba(0,0,0,0.1); } .nav-tabs { display: flex; width: 100%; justify-content: space-evenly; margin: 10px 0; } .section_header { flex: 1; min-width: 120px; text-align: center; padding: 2px 12px; box-sizing: border-box; border-bottom: 2px solid transparent; } .section_header.active { font-weight: bold; } #myCodePanel textarea { width: 100%; min-height: 10em; padding: 0.8em; border-radius: 0.4em; } #myCodePanel button { padding: 0.6em 1.2em; border-radius: 0.4em; } .config_var { width: 95%; box-sizing: border-box; margin: 10px auto; padding: 5px; } .config_var textarea { width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-family: monospace; resize: vertical; } #myCodePanel .section_desc { width: 100% } `, // 明暗状态由原站维护,插件仅覆盖深色配色。 night: ` html.darking:root { --tb--bdbg: var(--good-boy-dark-bg); --tb--dark: var(--good-boy-dark-bg); --tb--white: var(--good-boy-dark-bg); --tb--tint: var(--good-boy-dark-surface); --tb--base: var(--good-boy-dark-text); --tb--gray: var(--good-boy-dark-muted); --tb--line: var(--good-boy-dark-border); --tb--main: var(--good-boy-dark-accent); scrollbar-color: var(--good-boy-dark-muted) var(--good-boy-dark-bg); } html.darking, html.darking body, html.darking #hd, html.darking #pt.bm, html.darking #pt { background: var(--good-boy-dark-bg) !important; color: var(--good-boy-dark-text) !important; } html.darking .bm, html.darking .bmw, html.darking .bdl, html.darking .bdl dt, html.darking .bdl dd.bdl_a a, html.darking .tb .a a, html.darking .pn, html.darking .fl .bm_h, html.darking .ct2_a, html.darking .ct3_a, html.darking .t_table, html.darking table.plhin { background: var(--good-boy-dark-bg) !important; } html.darking .bdl dl.a, html.darking .tl .th, html.darking .tl .ts th, html.darking .tl .ts td, html.darking .pg a, html.darking .pg strong, html.darking .pgb a, html.darking .pg label, html.darking .bml .bm_h, html.darking #scrolltop a, html.darking .bmn, html.darking .bm_h, html.darking td.pls, html.darking .ad td.plc, html.darking div.exfm, html.darking .tb a, html.darking .tb_h, html.darking .ttp li.a a, html.darking div.uo a, html.darking input#addsubmit_btn, html.darking #gh .bm .bm_h, html.darking .jump_bdl li, html.darking .newthread tr th, html.darking .newthread tr td, html.darking .tl .threadpre td, html.darking .tl .threadpre:hover td, html.darking .nfl .f_c, html.darking #myCodePanel { background: var(--good-boy-dark-bg) !important; } html.darking #threadlist .bm_c, html.darking #threadlisttableid, html.darking #threadlisttableid tbody, html.darking #threadlisttableid tr, html.darking #threadlisttableid th, html.darking #threadlisttableid td, html.darking #ucbody, html.darking .bbda, html.darking #modmenu, html.darking .xi2.pbm, html.darking .mbm.pbm.bbs { background-color: var(--good-boy-dark-bg) !important; } html.darking #nv, html.darking #nv li.a, html.darking #nv li.hover, html.darking .card_gender_0, html.darking .card .o a, html.darking .tbn li.a, html.darking #p_btn a, html.darking #p_btn i { background: var(--good-boy-dark-surface) !important; } html.darking #toptb, html.darking .tedt .bar, html.darking .edt .bar, html.darking .edt .bbar, html.darking #post_extra_tb label.a, html.darking #extcreditmenu.a, html.darking #g_upmine.a, html.darking .tl #forumnewshow, html.darking .jump_bdl .a a, html.darking .jump_bdl .a a:hover, html.darking .psth, html.darking .pl .quote, html.darking .pm_tac, html.darking .pm .c, html.darking .pml .hover, html.darking #uhd, html.darking #flw_header .bar, html.darking .ttp a, html.darking .ttp strong, html.darking .bmw .bm_h, html.darking .GzList ul li a, html.darking .m_c, html.darking .m_c .o, html.darking .dt th, html.darking .section_header_holder p, html.darking #fx_checkin_menu, html.darking #fx_checkin_menub, html.darking .pl .blockcode ol li:hover, html.darking .blk, html.darking .tip, html.darking .flb { background: var(--good-boy-dark-surface) !important; } html.darking #nv li a:hover, html.darking #nv li.hover a, html.darking #nv li.hover a:hover, html.darking #nv > a, html.darking .p_pop, html.darking .p_pof, html.darking .sllt, html.darking .tl #forumnewshow a:hover, html.darking #autopbn:hover, html.darking .pgbtn a:hover, html.darking #hiddenpoststip, html.darking .p_pop a:hover, html.darking .p_pop a.a, html.darking .tps a:hover { background: var(--good-boy-dark-hover) !important; } html.darking #threadlist > div > table > tbody > tr:hover > *, html.darking #threadlist > div > form > table > tbody > tr:not(.threadpre):hover > *, html.darking div.tl > form > table > tbody > tr:hover > *, html.darking #threadlisttableid tbody:hover th, html.darking #threadlisttableid tbody:hover td, html.darking #threadlisttableid tbody[id^="normalthread"]:hover th, html.darking #threadlisttableid tbody[id^="normalthread"]:hover td, html.darking #threadlisttableid tbody[id^="stickthread"]:hover th, html.darking #threadlisttableid tbody[id^="stickthread"]:hover td, html.darking .tl tr:hover th, html.darking .tl tr:hover td { background-color: var(--good-boy-dark-hover) !important; } html.darking a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking #um, html.darking button, html.darking .pg label, html.darking div#forum_rules_37 font, html.darking .article-content, html.darking .article-content h2, html.darking .article-content h3 { color: var(--good-boy-dark-text) !important; } html.darking #um a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking #toptb a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking #toptb .xi2 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking #nv li a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking #nv li.a a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking #nv li.hover a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .bm_h h2:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .bm_h h2 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .bm_h .xs2:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .bm_h .xs2 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .bmw .bm_h a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking a.pn:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .pn span:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .pn strong:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .p_pop a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .excerpt h2 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])) { color: var(--good-boy-dark-text) !important; } html.darking .tps a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .chart em:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .xg1:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .xg1 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .xg2:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .xg2 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking p .xi2 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .by cite a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .by .xi2 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .authi em:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .authi .xg1:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .excerpt .note:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .excerpt .meta:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .excerpt .meta a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .pagination span:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])) { color: var(--good-boy-dark-muted) !important; } html.darking .xi2:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .xi2 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .xi3 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking p .xi2 a:hover:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking #nv li a:hover:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .p_pop a:hover:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .p_pop a.a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .excerpt h2 a:hover:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .excerpt:hover h2 a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .excerpt .meta a:hover:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .article-content a:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])), html.darking .article-content a:hover:where(:not(.my_good_boy_li > a, [style*="--good-boy-post-color:"])) { color: var(--good-boy-dark-accent) !important; text-decoration-color: currentColor; } html.darking #threadlisttableid th a.xst:hover:where(:not([style*="--good-boy-post-color:"])), html.darking #threadlisttableid th a.s.xst:hover:where(:not([style*="--good-boy-post-color:"])), html.darking #threadlisttableid tbody:hover a.xst:where(:not([style*="--good-boy-post-color:"])), html.darking #threadlisttableid tbody[id^="normalthread"]:hover th a.xst:where(:not([style*="--good-boy-post-color:"])), html.darking #threadlisttableid tbody[id^="stickthread"]:hover th a.xst:where(:not([style*="--good-boy-post-color:"])) { color: var(--good-boy-dark-accent) !important; } html.darking .tedt .pt, html.darking .px, html.darking .pt, html.darking .ps, html.darking select, html.darking input, html.darking textarea, html.darking #myCodePanel textarea, html.darking #myCodePanel button, html.darking .search-input, html.darking .ipt, html.darking #comment, html.darking #scbar, html.darking .scbar_txt, html.darking #scform input.scbar_txt, html.darking #fjump_menu .px { background-color: var(--good-boy-dark-input) !important; border-color: var(--good-boy-dark-border) !important; color: var(--good-boy-dark-text) !important; } html.darking .pl .blockcode { background: var(--good-boy-dark-input) !important; } html.darking input::placeholder, html.darking textarea::placeholder, html.darking #comment::placeholder { color: var(--good-boy-dark-muted) !important; } html.darking .search-input:focus, html.darking .ipt:focus, html.darking #comment:focus, html.darking .px:focus, html.darking .pt:focus { border-color: var(--good-boy-dark-accent) !important; box-shadow: 0 0 0 2px var(--good-boy-dark-border); } html.darking input[type="checkbox"], html.darking input[type="radio"] { accent-color: var(--good-boy-dark-accent); } html.darking .bm, html.darking .bmw, html.darking .bm_h, html.darking .fl .bm_h, html.darking #nv, html.darking #nv li, html.darking .pn, html.darking .p_pop, html.darking .p_pop a, html.darking #pt, html.darking #pt.bm, html.darking #uhd, html.darking #ucbody, html.darking .bbda, html.darking #modmenu, html.darking .xi2.pbm, html.darking .mbm.pbm.bbs, html.darking #threadlisttableid, html.darking #threadlisttableid tbody, html.darking #threadlisttableid tr, html.darking #threadlisttableid th, html.darking #threadlisttableid td { border-color: var(--good-boy-dark-border) !important; } html.darking .widget, html.darking .single .content, html.darking .pagecontent, html.darking .page-template-sidebar .content, html.darking .excerpt-minic-index, html.darking .tinycat .excerpt, html.darking .pagemenu, html.darking .container-user, html.darking .loop-flash { background: var(--good-boy-dark-bg) !important; box-shadow: inset 0 0 0 1px var(--good-boy-dark-border), 0 10px 28px rgba(0,0,0,.45); } html.darking .excerpts-title { background: var(--good-boy-dark-bg) !important; box-shadow: inset 0 1px 0 var(--good-boy-dark-border), inset 1px 0 0 var(--good-boy-dark-border), inset -1px 0 0 var(--good-boy-dark-border); } html.darking .excerpt { background: var(--good-boy-dark-bg) !important; border-bottom-color: var(--good-boy-dark-border); box-shadow: inset 1px 0 0 var(--good-boy-dark-border), inset -1px 0 0 var(--good-boy-dark-border); } html.darking .excerpt:hover { background: var(--good-boy-dark-hover) !important; box-shadow: inset 2px 0 0 var(--good-boy-dark-accent), inset -1px 0 0 var(--good-boy-dark-border); } html.darking .article-content blockquote { background: var(--good-boy-dark-surface); border-left-color: var(--good-boy-dark-accent); color: var(--good-boy-dark-text); } html.darking .article-content-fold .-fold { background-image: linear-gradient(transparent, var(--good-boy-dark-bg)); } html.darking .pagination a { background: var(--good-boy-dark-surface) !important; color: var(--good-boy-dark-text) !important; border-color: var(--good-boy-dark-border) !important; } html.darking .pagination a:hover, html.darking .pagination a:focus { background: var(--good-boy-dark-hover) !important; color: var(--good-boy-dark-accent) !important; border-color: var(--good-boy-dark-border) !important; } html.darking .pnc, html.darking a.pnc, html.darking button.pnc, html.darking .pg strong, html.darking .pagination .active a, html.darking .pagination .active span, html.darking .pagination li.next-page a { background: var(--good-boy-dark-accent) !important; border-color: var(--good-boy-dark-accent) !important; } html.darking .pnc, html.darking a.pnc, html.darking button.pnc, html.darking .pnc span, html.darking .pnc strong, html.darking .pnc em, html.darking a.pnc strong, html.darking .pg strong, html.darking .pagination .active a, html.darking .pagination .active span, html.darking .pagination li.next-page a { color: var(--good-boy-dark-bg) !important; } html.darking ul.cl.nav li a, html.darking ul.cl.nav li a:hover { background-repeat: no-repeat !important; background-position: 50% 5px !important; } html.darking .ignore_notice { right: -3px !important; top: -53px !important; } html.darking .signature_switch_div { background-color: var(--good-boy-dark-accent) !important; color: var(--good-boy-dark-bg); box-shadow: 0 0 3px var(--good-boy-dark-accent); } html.darking .header { background: var(--good-boy-dark-surface) !important; box-shadow: 0 1px 0 var(--good-boy-dark-border), 0 8px 24px rgba(0,0,0,.35); } html.darking .site-nav .sub-menu { border-color: var(--good-boy-dark-border) !important; } html.darking #toptb { border-bottom-color: var(--good-boy-dark-border) !important; } html.darking .site-nav > li > a:hover, html.darking .site-nav li.current-menu-item > a, html.darking .site-nav li.current-menu-parent > a { color: var(--good-boy-dark-accent) !important; } html.darking ::-webkit-scrollbar-track, html.darking ::-webkit-scrollbar-corner { background: var(--good-boy-dark-bg); } html.darking ::-webkit-scrollbar-thumb { background: var(--good-boy-dark-muted); border-color: var(--good-boy-dark-bg); } html.darking ::-webkit-scrollbar-thumb:hover { background: var(--good-boy-dark-accent); } /* 原站组件含 ID 和渐变,需同时覆盖优先级及完整背景。 */ html.darking .bm, html.darking .bmw, html.darking #sd_bdl.bdl, html.darking #threadlist.tl, html.darking #postlist.pl { background: var(--good-boy-dark-bg) !important; border-color: var(--good-boy-dark-border) !important; box-shadow: inset 0 1px 0 var(--good-boy-dark-border), 0 10px 28px rgba(0,0,0,.45) !important; } html.darking .pgs, html.darking .bm.bw0, html.darking .bm.pgs { background: var(--good-boy-dark-bg) !important; border-color: var(--good-boy-dark-border) !important; box-shadow: none !important; } html.darking .bdl dt, html.darking .bdl dl.a, html.darking .bdl dd, html.darking .bdl dd a, html.darking .fl_tb, html.darking .fl_tb td, html.darking .fl_tb th { background: var(--good-boy-dark-bg) !important; border-color: var(--good-boy-dark-border) !important; color: var(--good-boy-dark-text) !important; } html.darking .bdl_h, html.darking .bm_h, html.darking .fl .bm_h, html.darking .bmw .bm_h, html.darking .bml .bm_h, html.darking #threadlist .th, html.darking #f_pst .bm_h { background: var(--good-boy-dark-surface) !important; border-color: var(--good-boy-dark-border) !important; color: var(--good-boy-dark-text) !important; } html.darking .bdl dt a, html.darking .fl_tb h2 a, html.darking #threadlisttableid th { color: var(--good-boy-dark-text) !important; } html.darking .bdl dd.bdl_a a { background: var(--good-boy-dark-surface) !important; border-left-color: var(--good-boy-dark-accent) !important; color: var(--good-boy-dark-accent) !important; } html.darking .bdl dd a:hover, html.darking .fl_tb tr:hover td, html.darking .pg a:hover, html.darking .pgb a:hover { background: var(--good-boy-dark-hover) !important; } html.darking .pg a, html.darking .pg label, html.darking .pgb a { background: var(--good-boy-dark-surface) !important; border-color: var(--good-boy-dark-border) !important; color: var(--good-boy-dark-text) !important; } html.darking .pg a:hover, html.darking .pgb a:hover { border-color: var(--good-boy-dark-accent) !important; } html.darking #fjump_menu, html.darking #visitedforums_menu, html.darking #filter_special_menu, html.darking #filter_dateline_menu, html.darking #filter_reward_menu { background: var(--good-boy-dark-surface) !important; border-color: var(--good-boy-dark-border) !important; } html.darking .fl_tb tr:hover td.fl_icn, html.darking .fl_tb tr:hover td.fl_g, html.darking #threadlisttableid tbody[id^="normalthread"]:hover td.icn, html.darking #threadlisttableid tbody[id^="stickthread"]:hover td.icn { box-shadow: inset 2px 0 0 var(--good-boy-dark-accent); } html.darking #threadlisttableid .icn, html.darking #threadlisttableid .o { background-color: var(--good-boy-dark-bg) !important; } html.darking .bm_h .y, html.darking .bm_h .y a, html.darking .bm_h span.o, html.darking #threadlist .tf a, html.darking #threadlist .th .by, html.darking #threadlist .th .num, html.darking #threadlisttableid .by, html.darking #threadlisttableid .num, html.darking #threadlisttableid td em, html.darking #threadlisttableid td em a, html.darking #threadlisttableid .by cite a { color: var(--good-boy-dark-muted) !important; } html.darking .bdl dd a:hover, html.darking .fl_tb tr:hover h2 a, html.darking .fl_tb td.fl_g:hover dt a, html.darking .fl_tb .xi1, html.darking .fl_g .xi1, html.darking .bm_h h2 a:hover, html.darking .bm_h .xs2 a:hover, html.darking .bmw .bm_h a:hover, html.darking .bm_h .xi1, html.darking .bm_h .y .xi1, html.darking #number_favorite, html.darking #threadlist .tf a:hover, html.darking #threadlisttableid .by cite a:hover, html.darking #threadlisttableid .num a, html.darking #threadlisttableid .num a:hover, html.darking .pg a:hover, html.darking .pgb a:hover, html.darking #um .new, html.darking #toptb .new, html.darking #toptb a:hover, html.darking #toptb a.dz-dark-toggle, html.darking #toptb a.dz-dark-toggle:hover { color: var(--good-boy-dark-accent) !important; } html.darking .tl #forumnewshow, html.darking .tl #forumnewshow a, html.darking #threadlist #checknewline tr td, html.darking #threadlist #checknewline tr th { background: var(--good-boy-dark-surface) !important; border-color: var(--good-boy-dark-border) !important; color: var(--good-boy-dark-accent) !important; } html.darking .tl #forumnewshow a:hover { background: var(--good-boy-dark-hover) !important; } html.darking #threadlisttableid .newthread tr th, html.darking #threadlisttableid .newthread tr td { background: var(--good-boy-dark-surface) !important; } html.darking #threadlisttableid .newthread .new em, html.darking #threadlisttableid .newthread .new em a, html.darking #threadlist #checknewline a { color: var(--good-boy-dark-accent) !important; } /* 状态图标是透明 GIF,以原图作遮罩着色,不改图片及点击行为。 */ html.darking #threadlisttableid td.icn img:is([src$="/pin_3.gif"], [src$="/folder_lock.gif"], [src$="/folder_common.gif"], [src$="/rewardsmall.gif"]) { object-position: -99999px 0; background: var(--good-boy-dark-accent) !important; } html.darking #threadlisttableid td.icn img[src$="/pin_3.gif"] { mask: url("/static/image/common/pin_3.gif") center / contain no-repeat; } html.darking #threadlisttableid td.icn img[src$="/folder_lock.gif"] { mask: url("/static/image/common/folder_lock.gif") center / contain no-repeat; } html.darking #threadlisttableid td.icn img[src$="/folder_common.gif"] { mask: url("/static/image/common/folder_common.gif") center / contain no-repeat; background: var(--good-boy-dark-muted) !important; } html.darking #threadlisttableid td.icn img[src$="/rewardsmall.gif"] { mask: url("/static/image/common/rewardsmall.gif") center / contain no-repeat; } html.darking #f_pst, html.darking #f_pst .bm_c, html.darking #fastpostform, html.darking #fastposteditor, html.darking #fastpostform .tedt, html.darking #postform, html.darking #postbox, html.darking #editorbox, html.darking #seccheck, html.darking .edt, html.darking .tedt, html.darking #seccheck_fastpost, html.darking #attachlist, html.darking #attach_tblheader td { background: var(--good-boy-dark-bg) !important; border-color: var(--good-boy-dark-border) !important; color: var(--good-boy-dark-text) !important; } html.darking #fastpostform .tedt .bar, html.darking .edt .bar, html.darking .tedt .bar, html.darking .edt .bbar, html.darking .tedt .bbar, html.darking .edt .cst, html.darking .tedt .cst, html.darking .edt .btn, html.darking .tedt .btn, html.darking .edt .b2r, html.darking .edt .b1r, html.darking #post_extra_tb { background: var(--good-boy-dark-surface) !important; border-color: var(--good-boy-dark-border) !important; color: var(--good-boy-dark-muted) !important; } html.darking #f_pst .px, html.darking #fastpostform input.px, html.darking #fastpostform .tedt .area, html.darking #fastpostform .tedt .pt, html.darking #fastpostmessage, html.darking .edt .area, html.darking .tedt .area, html.darking #e_textarea, html.darking textarea[name="message"], html.darking .editorrow { background: var(--good-boy-dark-input) !important; border-color: var(--good-boy-dark-border) !important; color: var(--good-boy-dark-text) !important; } html.darking #fastpostmessage:focus { border-color: var(--good-boy-dark-accent) !important; color: var(--good-boy-dark-text) !important; } html.darking #f_pst .pbt, html.darking #f_pst .pnpost, html.darking #fastpostform .tedt .bar .y a, html.darking #fastpostform .pipe, html.darking .edt a, html.darking .tedt a, html.darking #post_extra_tb label, html.darking #post_extra_tb span { color: var(--good-boy-dark-muted) !important; } html.darking #fastpostform .tedt .bar .y a:hover, html.darking #fastpostform .fpd a:hover, html.darking #fastpostform .fpd a.hover, html.darking .edt a:hover, html.darking .edt a.hover, html.darking .tedt a:hover, html.darking .tedt a.hover, html.darking #post_extra_tb label.a, html.darking #post_extra_tb label:hover { background-color: var(--good-boy-dark-hover) !important; border-color: var(--good-boy-dark-border) !important; color: var(--good-boy-dark-accent) !important; } html.darking .pnc, html.darking a.pnc, html.darking button.pnc { box-shadow: 0 2px 8px rgba(0,0,0,.22) !important; } html.darking .pnc:hover { filter: none; } html.darking .pn:not(.pnc) { background: var(--good-boy-dark-surface) !important; border-color: var(--good-boy-dark-border) !important; } html.darking .pn:not(.pnc), html.darking a.pn:not(.pnc), html.darking .pn:not(.pnc) strong, html.darking .pn:not(.pnc) span, html.darking .pn:not(.pnc) em { color: var(--good-boy-dark-text) !important; } html.darking .pn:not(.pnc):hover { border-color: var(--good-boy-dark-accent) !important; } /* 发帖、回复按钮的文字原在位图中,复用原站标题并保留图片占位。 */ html.darking :is(#newspecial, #newspecialtmp, #post_reply, #post_replytmp)[title] { position: relative; background: var(--good-boy-dark-accent) !important; } html.darking :is(#newspecial, #newspecialtmp, #post_reply, #post_replytmp)[title] > img { opacity: 0; } html.darking :is(#newspecial, #newspecialtmp, #post_reply, #post_replytmp)[title]::after { content: attr(title); position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--good-boy-dark-bg); font-weight: bold; white-space: nowrap; pointer-events: none; } `, blocked: ` .my-blocked { position: relative; opacity: 0.6; transition: opacity 0.3s; } .my-blocked::after { content: "🚫"; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: 1.2em; } .my-blocked-alert { background: #fff0f0; border-left: 3px solid #ff4d4d; padding: 8px; margin: 5px 0; } `, // 基础样式 basic: ` :root { --link-color: #369; --hover-color: #2B65B7; } #threadlist #threadlisttableid a.xst[style*="--good-boy-post-color:"] { color: var(--good-boy-post-color) !important; } .container.my-custom-width { max-width: none !important; width: var(--custom-content-width) !important; margin-left: auto !important; margin-right: auto !important; } .my_good_boy_div_float { position: absolute; width: 20rem; padding: 1rem; margin: 0 1rem 1rem 0; border: 0.1rem solid var(--border); border-radius: 0.8rem; box-shadow: 0 0.2rem 0.8rem rgba(0,0,0,0.1); } .my_good_boy_div_basic { margin-top: 16px; } .my_good_boy_p { color: #FF4D4F; font-size: 15px; margin: 8px 0; } .my_good_boy_p_float { margin-left: 0 !important; } .my_good_boy_li { list-style-type: disc; white-space: nowrap; margin: 8px 0; } html .my_good_boy_li > a.my_good_boy_a, html .my_good_boy_li > a.my_good_boy_a_copy { color: var(--link-color) !important; } .my_good_boy_a { color: var(--link-color); text-decoration: none; } .my_good_boy_a:hover { color: var(--hover-color); text-decoration: underline; } html .my_good_boy_li > a.my_good_boy_a:not(.a_copy_completed):not(.link_faild):hover { color: var(--hover-color) !important; } .my_good_boy_a_copy { display: inline-block; font-size: 15px; margin-left: 20px; text-decoration: underline; cursor: pointer; } .a_copy_completed, html .my_good_boy_li > a.a_copy_completed, html .my_good_boy_li > a.a_copy_completed:hover { color: #FF6600 !important; animation: fadeIn 0.3s; } .link_faild, html .my_good_boy_li > a.link_faild, html .my_good_boy_li > a.link_faild:hover { color: #999 !important; text-decoration: line-through !important; } .show_more_link { text-decoration: underline; color: var(--primary-color); cursor: pointer; } #settingsIcon { display: block; float: right; cursor: pointer; margin: 4px 5px; width: 17px; height: 17px; transition: transform 0.2s; } #settingsIcon:hover { opacity: 1; } .expand_box { bottom: 0; height: 60px; position: fixed; right: -6vw; transition: all 0.3s ease; width: 12vw; z-index: 999; } .show_expand_box, .expand_box:focus-within { right: 0; width: 6vw; } #myCodeSpan { border-radius: 50%; color: #FFF; cursor: pointer; display: block; height: 38px; width: 38px; position: absolute; right: 1vw; transition: all 0.3s ease; background-color: #787878; bottom: 10px; } #myCodeSpan:hover, #myCodeSpan:focus-visible { box-shadow: 0 0 10px rgba(0,255,0,0.5); color: var(--good-boy-dark-accent, #00A1D6); transform: scale(1.1); } .signature_switch_div { position: absolute; top: 5px; z-index: 100; width: 16px; height: 16px; display: block; position: relative; top: -5px; cursor: pointer; transition: all 0.2s; } .signature_switch_div:hover { transform: scale(1.1); } #settingsIcon > svg, .signature_switch_div > svg { display: block; width: 100%; height: 100%; pointer-events: none; } #myCodeSpan > svg { display: block; width: 32px; height: 32px; margin: 3px; pointer-events: none; } .signature_switch_div .signature_plus { display: none; } .signature_switch_close .signature_plus { display: inline; } #settingsIcon:focus-visible, #myCodeSpan:focus-visible, .signature_switch_div:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; } .signature_hide { display: none; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } `, }; // 定义网站地址常量 const SITE_URLS = { HOME: "", FORUM: "", }; const reg = { url: /^[\s\S]*?(magnet:\?xt=urn:btih:(?:[a-z0-9]{40}|[a-z0-9]{32})|ftp:\/\/\S*|ed2k:\/\/\S*|thunder:\/\/\S*|flashget:\/\/\S*|qqdl:\/\/\S*|xfplay:\/\/\S*|https?:\/\/[\w零一二三四五六七八九壹贰叁肆伍陆柒捌玖-]*\.?[\w零一二三四五六七八九壹贰叁肆伍陆柒捌玖-]+\.+\w+\S*)/i, download: /^[\s\S]*?(magnet:\?xt=urn:btih:(?:[a-z0-9]{40}|[a-z0-9]{32})|ed2k:\/\/\S*|thunder:\/\/\S*)/i, www: /^[\s\S]*?(www\.[\w-]+\.[a-z]+[\w#=%+?/-]*)/i, baidupan: /^https?:\/\/pan\.baidu\.com\/s(?:hare)?\/[\w=?&-]+$/i, baidupanIncludeCode: /^https?:\/\/pan\.baidu\.com\/s(?:hare)?\/[\w=?#&-]+$/i, code: /(?:提取)+[^a-z0-9解压]*([a-z0-9]{4})[^a-z0-9]*/i, baidupanCode: /^(?:(?:下载)?链接\S+\s+)?(?:[^解压]+\s+)?[^a-z0-9解压]*([a-z0-9]{4})[^a-z0-9]*(?:app)?[^a-z0-9]*$/i, singleCharCode: /^[a-z0-9]$/i, missingHeaderBaidupan: /^[\s\S]*?[^/\w-]?((? [v, k]), ); // 预编译正则表达式 const MAGNET_PREFIX_REGEX = /^magnet:\?xt=urn:btih:/; const TRIM_REGEX = /^\s+|\s+$/g; const HTTP_PROTOCOL_REGEX = /^https?\/\//i; const URL_PROTOCOL_REGEX = /^(https?:\/\/|ed2k:\/\/)/; const CONFIG = { DEFAULT_ACCESS_NUM: 90, MAX_ACCESS_NUM: 90, LEVEL_ACCESS_MAP: { "-1": 0, 0: 5, }, }; const IMAGE_SELECTOR = [ 'img[src*="avatar"]', 'img[id*="aimg"]', "img.thumb", 'img[decoding="async"]', ".article-content img", ].join(","); const THREAD_LINK_SELECTOR = ".tl th a.xst, .tl td.fn a"; const VISITED_THREAD_IDS_KEY = "visitedThreadIds"; const DEFAULT_VISITED_THREAD_RECORD_LIMIT = 100; const status = { collapsedGwwzEnable: GM_config.getValue("collapsedGwwzEnable", false), accessNum: GM_config.getValue("accessNumber", 90), checkDate: GM_config.getValue("checkDate", "2000-1-1"), oldUrl: location.href, timer1: null, timer2: null, timer3: null, timer4: null, }; class Good_Boy { constructor(config, linkArray = []) { this.config = config; this.floatEnable = this.isMobilePage() ? false : config.get("displayPosition") == "右侧"; this.linkArray = linkArray; this.totalCount = linkArray.length; this.currentCount = 0; this.showMoreStatus = false; this.insertUlStatus = false; this.ulNode = document.createElement("ul"); } insertLinkItem(linkArray = []) { this.linkArray = this.linkArray.concat(linkArray); this.totalCount = this.linkArray.length; const maxLinkNumber = this.config.get("maxLinkNumber"); for ( let i = this.currentCount; i < this.totalCount && i < maxLinkNumber; i++ ) { const tempA = this.createLink(this.linkArray[i]); tempA && this.ulNode.appendChild(tempA); } this.currentCount = Math.min(this.totalCount, maxLinkNumber); if (maxLinkNumber > -1 && this.totalCount > maxLinkNumber) { const message = `[共提取到 ${this.totalCount} 个链接,仅显示前 ${maxLinkNumber} 个]:`; const pElement = this.ulNode.getElementsByTagName("p")[0]; pElement && (pElement.textContent = message); if (!this.showMoreStatus) { this.ulNode.appendChild(this.showMoreLink()); this.showMoreStatus = true; } } else { const message = `[共提取到 ${this.totalCount} 个链接]:`; const pElement = this.ulNode.getElementsByTagName("p")[0]; pElement && (pElement.textContent = message); } } isMobilePage() { return /android|webos|iphone|ipod|blackberry/i.test(navigator.userAgent); } getCutLinkText(linkText) { if (this.isMobilePage()) { return linkText.length >= 35 ? linkText.slice(0, 15) + " ... " + linkText.slice(-10) : linkText; } else if (this.floatEnable) { return linkText.length >= 50 ? linkText.slice(0, 25) + " ... " + linkText.slice(-15) : linkText; } return linkText.length >= 80 ? linkText.slice(0, 45) + " ... " + linkText.slice(-25) : linkText; } managePrefixCode(inputLink) { const { prefixCode, prefixHash, prefixReplace, prefixUrl, redircdn } = reg; if (prefixCode.test(inputLink)) { return inputLink.replace(prefixReplace, ""); } else if (prefixHash.test(inputLink)) { return inputLink.replace(prefixReplace, "magnet:?xt=urn:btih:"); } else if (prefixUrl.test(inputLink)) { return inputLink.replace(prefixReplace, "http://"); } else if (redircdn.test(inputLink)) { return inputLink.replace(redircdn, "$1").replace("______", "."); } return inputLink; } createLink(linkHref) { const { baidupanIncludeCode } = reg; if (linkHref.length > 2000) { return null; } const linkLi = document.createElement("li"); linkLi.className = "my_good_boy_li"; linkLi.style.setProperty("--link-color", this.config.get("linkColor")); const linkA = document.createElement("a"); linkA.title = "点击访问"; linkHref = this.managePrefixCode(linkHref); linkA.className = "my_good_boy_a"; linkA.href = encodeURI(linkHref); linkA.target = "_blank"; linkA.textContent = this.getCutLinkText(linkHref); linkLi.appendChild(linkA); if (this.config.get("copyEnable")) { const copyA = document.createElement("a"); copyA.className = "my_good_boy_a_copy"; copyA.title = "复制链接"; copyA.href = "javascript:;"; copyA.target = "_self"; copyA.textContent = "复制"; copyA.addEventListener( "click", function () { GM_setClipboard(linkHref, "text"); this.classList.add("a_copy_completed"); this.title = "复制成功"; status.timer1 && clearTimeout(status.timer1); status.timer1 = setTimeout(() => { this.classList.remove("a_copy_completed"); this.title = "复制链接"; }, 2000); }, { passive: true }, ); linkLi.appendChild(copyA); if ( baidupanIncludeCode.test(linkHref) && /(?:#|\?pwd=)[a-z0-9]{4}$/i.test(linkHref) ) { const codeMatch = /#([a-z0-9]{4})$/i.exec(linkHref); if (!codeMatch) return linkLi; const codeValue = codeMatch[1]; const codeCopyA = document.createElement("a"); codeCopyA.className = "my_good_boy_a_copy"; codeCopyA.title = "复制提取码"; codeCopyA.href = "javascript:;"; codeCopyA.target = "_self"; codeCopyA.textContent = "复制提取码"; codeCopyA.addEventListener( "click", function () { GM_setClipboard(codeValue, "text"); this.classList.add("a_copy_completed"); this.title = "复制成功"; status.timer2 && clearTimeout(status.timer2); status.timer2 = setTimeout(() => { this.classList.remove("a_copy_completed"); this.title = "复制提取码"; }, 2000); }, { passive: true }, ); linkLi.appendChild(codeCopyA); } } return linkLi; } showMoreLink() { const linkLi = document.createElement("li"); linkLi.className = "my_good_boy_li"; linkLi.style.setProperty("--link-color", this.config.get("linkColor")); const linkA = document.createElement("a"); linkA.className = "my_good_boy_a show_more_link"; linkA.href = "javascript:;"; linkA.title = "显示全部提取链接"; linkA.textContent = "显示全部"; linkA.target = "_self"; linkA.addEventListener( "click", () => { this.ulNode.removeChild(linkLi); for (; this.currentCount < this.totalCount; this.currentCount++) { const tempA = this.createLink(this.linkArray[this.currentCount]); tempA && this.ulNode.appendChild(tempA); } this.ulNode.getElementsByTagName("p")[0].textContent = "[共提取到 " + this.totalCount + " 个链接]:"; this.showMoreStatus = false; }, false, ); linkLi.appendChild(linkA); return linkLi; } createGoodBoyFrame(container, linkArray = []) { if (this.insertUlStatus) { this.insertLinkItem(linkArray); } else { const goodBoyDiv = document.createElement("div"); goodBoyDiv.className = this.floatEnable ? "my_good_boy_div_float" : "my_good_boy_div_basic"; const goodBoyP = document.createElement("p"); goodBoyP.className = this.floatEnable ? "my_good_boy_p my_good_boy_p_float" : "my_good_boy_p"; this.ulNode.appendChild(goodBoyP); this.insertLinkItem(linkArray); goodBoyDiv.appendChild(this.ulNode); container.appendChild(goodBoyDiv); this.insertUlStatus = true; } } createGoodBoyElement(container, linkObj, textObj, nodeTextArray) { const backLinkArray = this.autoEvent( container, linkObj.linkObjArray, linkObj.baiduObjArray, linkObj.linksArray, textObj.hideTextArray, textObj.showTextArray, nodeTextArray, ); backLinkArray.length > 0 && this.createGoodBoyFrame(container, backLinkArray); } managePrefix(inputLinkArray) { const { prefixLinkMagnet, prefixLinkMagnetReplace } = reg; const returnLinkArray = []; inputLinkArray.forEach((item) => { returnLinkArray.push( prefixLinkMagnet.test(item) ? item.replace(prefixLinkMagnetReplace, "") : item, ); }); return returnLinkArray; } pickUpMagnet(item) { const { url, download, hash, md5 } = reg; let tempLink = ""; const item1 = item.replace(/[^a-z0-9:=?/\\.&%|-]/gi, ""); if (download.test(item1)) { tempLink = download.exec(item1)[1]; } else { const item2 = item.replace(/[^a-z0-9:=?/\\.,,。|\s+-]/gi, ""); if (!md5.test(item2) && hash.test(item2) && !url.test(item2)) { const tempHash = hash.exec(item2)[1]; if (!/^(?:[a-z]+|[0-9]+)$/i.test(tempHash)) { tempLink = "magnet:?xt=urn:btih:" + tempHash; } } } return tempLink; } manageText(textArray) { const { url, download, www, missingHeaderBaidupan, missingHeaderBaidupanTest, pan, hash, md5, } = reg; const tempArray = []; textArray.forEach((item) => { let tempLink = ""; if (pan.test(item)) { tempLink = "https://" + pan.exec(item)[1]; } else if (/magnet:|ed2k:|thunder:/i.test(item)) { if (download.test(item)) { tempLink = download.exec(item)[1]; } } else if (url.test(item)) { tempLink = url.exec(item)[1]; } else if (www.test(item)) { tempLink = "http://" + www.exec(item)[1]; } else if (missingHeaderBaidupan.test(item)) { const linkMissValue = missingHeaderBaidupan.exec(item)[1]; tempLink = /^\//i.test(linkMissValue) ? "https://pan.baidu.com" + linkMissValue : "https://pan.baidu.com/" + linkMissValue; } else if (missingHeaderBaidupanTest.test(item)) { tempLink = "https://pan.baidu.com/s/" + missingHeaderBaidupanTest.exec(item)[1]; } else if (!md5.test(item) && hash.test(item)) { tempLink = "magnet:?xt=urn:btih:" + hash.exec(item)[1]; } else if (item.length > 32) { tempLink = this.pickUpMagnet(item); } filterLink(tempLink) && tempArray.push(tempLink); }); return this.managePrefix(tempArray); } decodeLink(inputLinkArray) { const { url } = reg; inputLinkArray.forEach((item, index) => { if (url.test(item)) { inputLinkArray[index] = decodeURI(item); } }); return inputLinkArray; } pickUpBaidupanCode(textArray) { const { code, baidupanCode, singleCharCode } = reg; const codeArray = []; const charArray = []; textArray.forEach((item) => { let tempValue = ""; if (code.test(item)) { tempValue = code.exec(item)[1]; } else if (baidupanCode.test(item)) { tempValue = baidupanCode.exec(item)[1]; } else if (singleCharCode.test(item)) { charArray.push(item); } if ( tempValue && !/\d{4}/i.test(tempValue) && codeArray.indexOf(tempValue) === -1 ) { codeArray.push(tempValue); } }); while (charArray.length > 0 && charArray.length % 4 === 0) { const tempCode = charArray.splice(0, 4); const charValue = tempCode.join(""); if (!/\d{4}/i.test(charValue) && codeArray.indexOf(charValue) === -1) { codeArray.push(charValue); } } return codeArray; } manageRepeatArray(inputLinkArray) { const outputLinkArray = []; for (let i = 0, l = inputLinkArray.length; i < l; i++) { for (let j = i + 1; j < l; j++) { if ( getPureLink(inputLinkArray[i]) === getPureLink(inputLinkArray[j]) ) { ++i; j = i; } } outputLinkArray.push(inputLinkArray[i]); } return outputLinkArray; } splitWrap(textArray) { let tempArray = []; textArray.forEach((text) => { if (text) { tempArray = tempArray.concat(text.split("\n").filter((v) => v)); } }); return tempArray; } autoEvent( container, linkObjArray, baiduObjArray, _linksArray, hideTextArray, showTextArray, nodeTextArray, ) { const { url, baidupan, hash, md5 } = reg; const linkArray = []; const linkTextArray = []; linkObjArray.forEach((item) => { linkArray.push(item.eleValue); linkTextArray.push(item.eleContainer); }); if (hideTextArray.length > 0) { hideTextArray = this.splitWrap(hideTextArray); } if (showTextArray.length > 0) { showTextArray = this.splitWrap(showTextArray); } if (nodeTextArray.length > 0) { nodeTextArray = this.splitWrap(nodeTextArray); } const concatTextArray = this.manageRepeatArray( this.decodeLink(hideTextArray), ); let concatLinkArray = this.manageRepeatArray( this.decodeLink(this.manageText(linkArray.concat(concatTextArray))), ); const generalTextArray = showTextArray.concat( nodeTextArray.concat(linkTextArray), ); const indexA = []; const codeB = this.pickUpBaidupanCode(concatTextArray); let codeC = []; if (codeB.length > 0) { for (let j = 0; j < concatLinkArray.length; j++) { if (baidupan.test(concatLinkArray[j])) { indexA.push(j); } } if (codeB.length === indexA.length) { for (let k = 0; k < indexA.length; k++) { if (!/\?pwd=/.test(concatLinkArray[indexA[k]])) { concatLinkArray[indexA[k]] += "?pwd=" + codeB[k]; } } } else if (codeB.length === baiduObjArray.length) { for (let l = 0; l < baiduObjArray.length; l++) { if (!/\?pwd=/.test(baiduObjArray[l].ele.href)) { baiduObjArray[l].ele.textContent = baiduObjArray[l].eleContainer + "?pwd=" + codeB[l]; baiduObjArray[l].ele.href = baiduObjArray[l].eleValue + "?pwd=" + codeB[l]; } } } } else { codeC = this.pickUpBaidupanCode(generalTextArray); if (codeC.length > 0) { for (let j = 0; j < concatLinkArray.length; j++) { if (baidupan.test(concatLinkArray[j])) { indexA.push(j); } } if (codeC.length === indexA.length) { for (let k = 0; k < indexA.length; k++) { concatLinkArray[indexA[k]] += "?pwd=" + codeC[k]; } } else if (codeC.length === baiduObjArray.length) { for (let l = 0; l < baiduObjArray.length; l++) { baiduObjArray[l].ele.textContent = baiduObjArray[l].eleContainer + "?pwd=" + codeC[l]; baiduObjArray[l].ele.href = baiduObjArray[l].eleValue + "?pwd=" + codeC[l]; } } } } let hashArray = []; generalTextArray.forEach((item) => { if (!url.test(item)) { if (!md5.test(item) && hash.test(item)) { hashArray.push("magnet:?xt=urn:btih:" + hash.exec(item)[1]); } else if (item.length > 32) { let temp_link = this.pickUpMagnet(item); temp_link && hashArray.push(temp_link); } } }); hashArray = this.decodeLink(hashArray); concatLinkArray = this.manageRepeatArray( concatLinkArray.concat(hashArray), ); const hideCodeTextArray = this.decodeCoreValues( container, hideTextArray.concat(generalTextArray), ); if (hideCodeTextArray.length > 0) { hideCodeTextArray.forEach((item, index) => { if (!url.test(item)) { if (!md5.test(item) && hash.test(item)) { hideCodeTextArray[index] = "magnet:?xt=urn:btih:" + hash.exec(item)[1]; } else if (item.length > 32) { const tempLink = this.pickUpMagnet(item); if (tempLink) { hideCodeTextArray[index] = tempLink; } } } }); concatLinkArray = this.manageRepeatArray( concatLinkArray.concat(hideCodeTextArray), ); } concatLinkArray = concatLinkArray .map((item) => { if (generalTextArray.includes(item)) { return null; } return item; }) .filter((v) => v); return concatLinkArray; } foyuPromise(encoded) { return new Promise(function (resolve, _reject) { GM_xmlhttpRequest({ method: "POST", url: "https://keyfc.net/bbs/tools/tudou.aspx", data: "orignalMsg=" + encoded.replace(/\s/g, "") + "&action=Decode", headers: { "Content-Type": "application/x-www-form-urlencoded", }, onload: function (res) { if (res.status == 200 && res.readyState == 4) { resolve( res.responseText.replace(/^<\/Message><\/BUDDHIST>$/g, ""), ); } else { console.warn("福利吧小助手:\n自动解码出错!"); resolve(""); } }, onerror: function () { console.error("福利吧小助手:\n网络链接出错!"); resolve(""); }, }); }); } async foyuDecode(container, encoded) { await this.foyuPromise(encoded).then((data) => { data && this.createGoodBoyFrame(container, [data]); }); } decodeCoreValues(container, textArray) { const { core, baijia, foyu } = reg; const decodeArray = []; textArray.forEach((item) => { if (core.test(item)) { decodeArray.push(coreValuesDecode(core.exec(item)[1])); } else if (baijia.test(item)) { decodeArray.push(baijiaDecode(baijia.exec(item)[1])); } else if (foyu.test(item)) { this.foyuDecode(container, foyu.exec(item)[1]); } }); return decodeArray; } } function randBin() { return Math.random() >= 0.5; } function str2Utf8(str) { const ENCODED_CHARS = /[A-Za-z0-9\-_.!~*'()]/g; return Array.from(str) .map((char) => { if (ENCODED_CHARS.test(char)) { ENCODED_CHARS.lastIndex = 0; return char.codePointAt(0).toString(16); } const code = char.charCodeAt(0); if (code < 128) { return code.toString(16).padStart(2, "0"); } if (code < 2048) { return ( ((code >> 6) | 0xc0).toString(16) + ((code & 0x3f) | 0x80).toString(16) ); } return ( ((code >> 12) | 0xe0).toString(16) + (((code >> 6) & 0x3f) | 0x80).toString(16) + ((code & 0x3f) | 0x80).toString(16) ); }) .join("") .toUpperCase(); } function hex2Duo(hexs) { const duo = new Array(hexs.length * 2); let index = 0; const len = hexs.length; for (let i = 0; i < len; i++) { const n = hexs[i] >= "a" ? hexs.charCodeAt(i) - 87 : hexs[i] >= "A" ? hexs.charCodeAt(i) - 55 : hexs.charCodeAt(i) - 48; if (n < 10) { duo[index++] = n; } else { if (randBin()) { duo[index++] = 10; duo[index++] = n - 10; } else { duo[index++] = 11; duo[index++] = n - 6; } } } return duo.slice(0, index); } function utf82Str(utfs) { const len = utfs.length * 2; const arr = new Array(len); for (let i = 0, j = 0; i < utfs.length; i++) { arr[j++] = "%"; arr[j++] = utfs[i]; } return decodeURIComponent(arr.join("")); } function duo2Hex(duo) { const l = duo.length; const result = new Array(l); let pos = 0; for (let i = 0; i < l; i++) { const curr = duo[i]; if (curr < 10) { result[pos++] = curr.toString(16).toUpperCase(); } else if (curr === 10) { result[pos++] = (duo[++i] + 10).toString(16).toUpperCase(); } else { result[pos++] = (duo[++i] + 6).toString(16).toUpperCase(); } } return result.slice(0, pos).join(""); } function duo2Values(duo) { return duo.map((d) => coreValues[2 * d] + coreValues[2 * d + 1]).join(""); } function coreValuesEncode(str) { return duo2Values(hex2Duo(str2Utf8(str))); } function coreValuesDecode(encoded) { const duo = []; for (let c of encoded) { let i = coreValues.indexOf(c); if (i === -1) { continue; } else if (i & 1) { continue; } else { duo.push(i >> 1); } } const hexs = duo2Hex(duo); let str; try { str = utf82Str(hexs); } catch (e) { throw e; } return str; } function isFloat(str) { return str == "右侧"; } function baijiaEncode(str) { const cleaned = str.replace(TRIM_REGEX, ""); const value = cleaned.replace(MAGNET_PREFIX_REGEX, ""); return Array.from(value) .map((char) => reverseMap.get(char) || "") .join(""); } function baijiaDecode(encoded) { const decoded = Array.from(encoded) .map((char) => baijiaValues.get(char) || "") .join(""); if (HTTP_PROTOCOL_REGEX.test(decoded)) { return decoded.replace(/^(https?)/, "$1:"); } if (URL_PROTOCOL_REGEX.test(decoded)) { return decoded; } return `magnet:?xt=urn:btih:${decoded}`; } function convertChineseNumber(textString) { return textString.replace( /[零一二三四五六七八九壹贰叁肆伍陆柒捌玖]/gi, (arg0) => { let index = "零一二三四五六七八九".indexOf(arg0); if (index === -1) { index = "壹贰叁肆伍陆柒捌玖".indexOf(arg0) + 1; } return index; }, ); } function filterLink(inputLink) { let status = true; if (inputLink) { for (let i in filterReg) { if (filterReg[i].test(inputLink)) { status = false; break; } } } else { status = false; } return status; } function getPureLink(inputLink, depthBoolean) { const { baidupanIncludeCode } = reg; let outputLink = inputLink; if (depthBoolean) { if (baidupanIncludeCode.test(outputLink)) { if (/#[a-z0-9]{4}$/i.test(outputLink)) { outputLink = outputLink.replace(/#[a-z0-9]{4}$/i, ""); } } else if ( /^https?:\/\/www\.bilibili\.com\/video\/av\d+\?from=search/i.test( outputLink, ) ) { outputLink = outputLink.replace(/\?from=search.*/, ""); } } let returnLink = outputLink .replace( /(^(?:\s+)?(?:\[url\])?(?:\s+)?(?:https?:\/\/)?|(?:\/+)?(?:\s+)?(?:\[\/url\])?$|%C2%A0$)/gi, "", ) .replace(/%C2%A0/gi, "%20"); try { returnLink = decodeURI(returnLink); } catch (e) { console.warn("福利吧小助手:\n出现编码转换错误。", outputLink); console.warn(e); returnLink = outputLink .replace( /(^(?:\s+)?(?:\[url\])?(?:\s+)?(?:https?:\/\/)?|(?:\/+)?(?:\s+)?(?:\[\/url\])?$|%C2%A0$)/gi, "", ) .replace(/%C2%A0/gi, "%20"); } return returnLink; } function contrastTextAndLink(textValue, linkValue) { let sta = true; textValue = getPureLink(textValue, true); linkValue = getPureLink(linkValue, true); if (/\s...\s/i.test(textValue)) { sta = false; } else if (textValue === linkValue) { sta = false; } return sta; } function findLink(container) { const { baidupan } = reg; const linkA = container.getElementsByTagName("a"); const tempObj = { linkObjArray: [], baiduObjArray: [], linksArray: [], }; const tempBaiduArray = []; for (let i = 0; i < linkA.length; i++) { if (linkA[i].closest("div.aimg_tip")) { continue; } const tempLink = linkA[i].href; if (filterLink(tempLink)) { const tempImg = linkA[i].querySelectorAll("img"); if ( tempImg.length === 0 || (tempImg.length > 0 && tempImg[0].src !== tempLink && tempImg[0].getAttribute("file") !== tempLink) ) { const tempText = linkA[i].innerText.replace(/^\s+|\s+$/gi, ""); if (contrastTextAndLink(tempText, tempLink)) { const linkObj = { eleValue: tempLink, eleContainer: tempText, ele: linkA[i], }; tempObj.linkObjArray.push(linkObj); } else if (baidupan.test(tempLink)) { const baiduObj = { eleValue: tempLink, eleContainer: tempText, ele: linkA[i], }; if (tempBaiduArray.indexOf(tempLink) === -1) { tempBaiduArray.push(tempLink); tempObj.baiduObjArray.push(baiduObj); } } } tempObj.linksArray.indexOf(tempLink) === -1 && tempObj.linksArray.push(tempLink); } } return tempObj; } // 定义常量,提高可读性和性能 const GRAY_THRESHOLD = 192; const OPACITY_THRESHOLD = 0.2; const GRAY_COEFFICIENTS = { R: 0.299, G: 0.587, B: 0.114, }; function judgeColor(color) { // 使用一个正则表达式同时匹配 rgb 和 rgba const matches = color.match( /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d*\.?\d+))?\)/i, ); if (!matches) return false; const [, r, g, b, a = 1] = matches.map((val) => +val); // 如果是 rgba 且透明度低于阈值,直接返回 if (a <= OPACITY_THRESHOLD) return true; // 计算灰度值 const grayLevel = r * GRAY_COEFFICIENTS.R + g * GRAY_COEFFICIENTS.G + b * GRAY_COEFFICIENTS.B; return grayLevel > GRAY_THRESHOLD; } // rgb 转 rgba 函数 function rgb2Rgba(colorString) { const matches = colorString.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/i); if (!matches) return colorString; const [, r, g, b] = matches; return `rgba(${r}, ${g}, ${b}, 1)`; } function display_Text(container, newTextColor, newTextBackgroundColor) { const { url } = reg; const tempObj = { hideTextArray: [], showTextArray: [], }; // 处理font标签 const textFont = container.getElementsByTagName("font"); for (let i = 0; i < textFont.length; i++) { if ( textFont[i].closest(".quote") || textFont[i].getElementsByClassName("aimg_tip").length !== 0 ) { continue; } const tempText = textFont[i].innerText.replace(/^\s+|\s+$/gi, ""); if (!/^\s*$/i.test(tempText)) { const textColor = window.getComputedStyle(textFont[i]).color; const textBackgroundColor = window.getComputedStyle( textFont[i], ).backgroundColor; if (judgeColor(textColor) && judgeColor(textBackgroundColor)) { textFont[i].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (rgb2Rgba(textBackgroundColor) === rgb2Rgba(textColor)) { textFont[i].style.backgroundColor = newTextBackgroundColor; textFont[i].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (!judgeColor(textBackgroundColor) && !judgeColor(textColor)) { textFont[i].style.backgroundColor = newTextBackgroundColor; textFont[i].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if ( textFont[i].childNodes.length === 1 && textFont[i].childNodes[0].nodeType === 3 ) { if (GM_config.get("extractEnable")) { if (url.test(tempText)) { textFont[i].innerHTML = textFont[i].innerHTML.replace( url, (_arg0, arg1) => { return ( '' + convertChineseNumber(arg1) + "" ); }, ); } } !tempObj.showTextArray.includes(tempText) && tempObj.showTextArray.push(tempText); } else if (textFont[i].childNodes.length > 1) { const tFont = textFont[i].childNodes; for (let child_index in tFont) { analysisText(tFont[child_index]); } !tempObj.showTextArray.includes(tempText) && tempObj.showTextArray.push(tempText); } } } // 处理table标签 const tempTable = container.getElementsByTagName("table"); for (let j = 0; j < tempTable.length; j++) { if ( !judgeColor(tempTable[j].style.backgroundColor) && !judgeColor(tempTable[j].style.color) ) { tempTable[j].style.backgroundColor = newTextBackgroundColor; } else if ( judgeColor(tempTable[j].style.color) && judgeColor(tempTable[j].style.backgroundColor) ) { tempTable[j].style.color = newTextColor; } } // 处理blockquote标签 const blockquotes = container.getElementsByTagName("blockquote"); for (let k = 0; k < blockquotes.length; k++) { // 检查blockquote本身的颜色 const blockquoteColor = window.getComputedStyle(blockquotes[k]).color; const blockquoteBgColor = window.getComputedStyle( blockquotes[k], ).backgroundColor; if ( judgeColor(blockquoteColor) || rgb2Rgba(blockquoteBgColor) === rgb2Rgba(blockquoteColor) ) { blockquotes[k].style.color = newTextColor; const tempText = blockquotes[k].innerText.replace(/^\s+|\s+$/gi, ""); if (!/^\s*$/i.test(tempText)) { tempObj.hideTextArray.push(tempText); } } } // 处理strong标签 const strongs = container.getElementsByTagName("strong"); for (let m = 0; m < strongs.length; m++) { const strongColor = window.getComputedStyle(strongs[m]).color; const strongBgColor = window.getComputedStyle(strongs[m]).backgroundColor; const tempText = strongs[m].innerText.replace(/^\s+|\s+$/gi, ""); if (!/^\s*$/i.test(tempText)) { if (judgeColor(strongColor) && judgeColor(strongBgColor)) { strongs[m].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (rgb2Rgba(strongBgColor) === rgb2Rgba(strongColor)) { strongs[m].style.backgroundColor = newTextBackgroundColor; strongs[m].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (!judgeColor(strongBgColor) && !judgeColor(strongColor)) { strongs[m].style.backgroundColor = newTextBackgroundColor; strongs[m].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } } } // 处理span标签 (通常用于隐藏文本) const spans = container.getElementsByTagName("span"); for (let n = 0; n < spans.length; n++) { const spanColor = window.getComputedStyle(spans[n]).color; const spanBgColor = window.getComputedStyle(spans[n]).backgroundColor; const tempText = spans[n].innerText.replace(/^\s+|\s+$/gi, ""); if (!/^\s*$/i.test(tempText)) { if (judgeColor(spanColor) && judgeColor(spanBgColor)) { spans[n].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (rgb2Rgba(spanBgColor) === rgb2Rgba(spanColor)) { spans[n].style.backgroundColor = newTextBackgroundColor; spans[n].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } else if (spanColor === "rgb(255, 255, 255)") { spans[n].style.color = newTextColor; tempObj.hideTextArray.push(tempText); } } } return tempObj; } function text2A(node) { const { url } = reg; const tempSpan = document.createElement("span"); tempSpan.innerHTML = node.nodeValue.replace(url, (arg0, arg1) => { if (arg1.length > 2000) { return arg0; } return arg0.replace( arg1, '' + convertChineseNumber(arg1) + "", ); }); node.parentNode.replaceChild(tempSpan, node); } function analysisText(domPoint) { const { url } = reg; const nodeList = domPoint.childNodes; const nodeTextArray = []; for (let i in nodeList) { if (nodeList[i].nodeType === 3) { const tempText = nodeList[i].nodeValue.replace(/^\s+|\s+$/gi, ""); if (!/^\s*$/i.test(tempText)) { if (GM_config.get("extractEnable")) { if (url.test(tempText)) { text2A(nodeList[i]); } } nodeTextArray.push(tempText); } } else if ( nodeList[i].nodeType === 1 && !nodeList[i].className.includes("quote") && !nodeList[i].className.includes("pstatus") && !nodeList[i].className.includes("aimg_tip") && !nodeList[i].className.includes("blockcode") && nodeList[i].nodeName !== "FONT" && nodeList[i].nodeName !== "A" && nodeList[i].nodeName !== "SCRIPT" && nodeList[i].childNodes.length > 0 ) { const recursiveArray = analysisText(nodeList[i]); for (let j in recursiveArray) { nodeTextArray.push(recursiveArray[j]); } } } return nodeTextArray; } function isMobilePage() { return /android|webos|iphone|ipod|blackberry/i.test(navigator.userAgent); } function getNowDate() { const now = new Date(); return now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate(); } function getUpdateTime(unixTime) { if (!unixTime) return ""; const formatter = new Intl.DateTimeFormat("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false, }); return ` (更新时间: ${formatter .format(new Date(unixTime)) .replace(/\//g, "-")})`; } // 用户权限相关的纯函数 function calculateUserAccess(level) { if (level in CONFIG.LEVEL_ACCESS_MAP) { return CONFIG.LEVEL_ACCESS_MAP[level]; } return level >= 1 && level <= 8 ? level * 10 : CONFIG.DEFAULT_ACCESS_NUM; } // 样式处理相关的纯函数 function getPostStyle(type, color = "") { const styles = { blocked: "color: #999; font-weight: bold; text-decoration-line: line-through; text-decoration-color: #000000;", restricted: "color: #999; font-weight: bold;", highlighted: `font-weight: bold; color: ${color}; --good-boy-post-color: ${color};`, }; return styles[type] || ""; } // 数字提取工具函数 function extractNumber(text) { if (!text) return 0; return Number(text.replace("+", "")); } // 获取最大点赞数 function getMaxAgreeCount(fontElements) { return Array.from(fontElements) .map((el) => extractNumber(el.textContent)) .reduce((max, current) => Math.max(max, current), 0); } function normalizeVisitedThreadIdList(inputList) { const outputList = []; inputList.forEach((item) => { const threadId = String(item); if (/^\d+$/.test(threadId) && !outputList.includes(threadId)) { outputList.push(threadId); } }); return outputList; } function getThreadIdFromUrl(inputUrl) { if (!inputUrl) return ""; const urlText = String(inputUrl); const pathMatch = /(?:^|[/?#&])thread-(\d+)-\d+(?:-\d+)?\.html/i.exec( urlText, ); if (pathMatch) return pathMatch[1]; try { const urlObj = new URL(urlText, location.href); const tid = urlObj.searchParams.get("tid"); return /^\d+$/.test(tid) ? tid : ""; } catch (e) { const tidMatch = /[?&]tid=(\d+)/i.exec(urlText); return tidMatch ? tidMatch[1] : ""; } } function getVisitedThreadIdList() { const storedValue = GM_getValue(VISITED_THREAD_IDS_KEY, []); if (Array.isArray(storedValue)) { return normalizeVisitedThreadIdList(storedValue); } if (typeof storedValue === "string") { try { const parsedValue = JSON.parse(storedValue); if (Array.isArray(parsedValue)) { return normalizeVisitedThreadIdList(parsedValue); } } catch (e) { return normalizeVisitedThreadIdList(storedValue.split(",")); } } return []; } function getVisitedThreadRecordLimit() { const recordLimit = Number(GM_config.get("visitedRecordLimit")); return Number.isFinite(recordLimit) && recordLimit >= 0 ? Math.floor(recordLimit) : DEFAULT_VISITED_THREAD_RECORD_LIMIT; } function rememberVisitedThreadId(threadId) { const normalizedThreadId = String(threadId || ""); if (!/^\d+$/.test(normalizedThreadId)) return; const visitedThreadRecordLimit = getVisitedThreadRecordLimit(); if (visitedThreadRecordLimit === 0) { GM_setValue(VISITED_THREAD_IDS_KEY, []); return; } const visitedThreadIds = getVisitedThreadIdList().filter( (item) => item !== normalizedThreadId, ); visitedThreadIds.unshift(normalizedThreadId); if (visitedThreadIds.length > visitedThreadRecordLimit) { visitedThreadIds.length = visitedThreadRecordLimit; } GM_setValue(VISITED_THREAD_IDS_KEY, visitedThreadIds); } function applySavedVisitedThreadStyle( link, visitedThreadIds = new Set(getVisitedThreadIdList()), ) { const threadId = getThreadIdFromUrl(link?.href); if (!threadId || !visitedThreadIds.has(threadId)) return; const visitedColor = GM_config.get("visitedColor"); if ( link.style.getPropertyValue("color") === visitedColor && link.style.getPropertyPriority("color") === "important" ) { return; } link.style.setProperty("color", visitedColor, "important"); } function applySavedVisitedThreadStyles(container = document) { const visitedThreadIds = new Set(getVisitedThreadIdList()); if (visitedThreadIds.size === 0) return; container .querySelectorAll?.(THREAD_LINK_SELECTOR) .forEach((link) => applySavedVisitedThreadStyle(link, visitedThreadIds)); if (container.matches?.(THREAD_LINK_SELECTOR)) { applySavedVisitedThreadStyle(container, visitedThreadIds); } } function recordVisitedThreadFromEvent(event) { const link = event.target.closest?.("a[href]"); const threadId = getThreadIdFromUrl(link?.href); if (!threadId) return; rememberVisitedThreadId(threadId); applySavedVisitedThreadStyle(link, new Set([threadId])); } function initSavedVisitedThreadMarker() { if ( !document.body || document.body.dataset.goodBoyVisitedReady === "true" ) { return; } document.body.dataset.goodBoyVisitedReady = "true"; applySavedVisitedThreadStyles(); document.body.addEventListener("click", recordVisitedThreadFromEvent, true); document.body.addEventListener( "auxclick", recordVisitedThreadFromEvent, true, ); if (typeof document.arrive === "function") { document.arrive( THREAD_LINK_SELECTOR, { onceOnly: false, existing: true }, function () { applySavedVisitedThreadStyle(this); }, ); } } function highlightPost() { // 确保DOM已经加载 if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => initHighlight()); } else { initHighlight(); } } // 初始化高亮功能 function initHighlight() { try { updateUserAccessLevel(); initializePostHighlighting(); } catch (error) { console.warn("福利吧小助手:\n执行出错:", error.message); } } // 更新用户访问权限 function updateUserAccessLevel() { try { const upMineElement = document.getElementById("g_upmine"); if (!upMineElement) return; const levelMatch = /LV\.(-?[0-9])/.exec(upMineElement.textContent); if (!levelMatch) return; const newAccessNum = calculateUserAccess(levelMatch[1]); if (status.accessNum !== newAccessNum) { status.accessNum = newAccessNum; GM_config.setValue("accessNumber", newAccessNum); } } catch (error) { console.warn( "福利吧小助手:\n识别用户阅读权限出错,将使用存储中记录的用户阅读权限。", ); } console.debug("福利吧小助手:\n用户阅读权限:", status.accessNum); } // 初始化帖子高亮 function initializePostHighlighting() { // 缓存选择器字符串,避免重复创建字符串 const THREADLIST_SELECTOR = "#threadlisttableid"; // 立即检查目标元素 const threadlist = document.querySelector(THREADLIST_SELECTOR); if (threadlist) { processThreadList(threadlist); return; } // 创建单个共享的 MutationObserver 配置对象 const observerConfig = { childList: true, subtree: true, }; // 使用函数声明提升性能,避免在回调中重复创建函数 function threadlistCallback(mutations, obs) { const threadlist = document.querySelector(THREADLIST_SELECTOR); if (threadlist) { processThreadList(threadlist); obs.disconnect(); } } function bodyCallback(mutations, obs) { if (document.body) { obs.disconnect(); const observer = new MutationObserver(threadlistCallback); observer.observe(document.body, observerConfig); } } // 主逻辑 if (document.body) { const observer = new MutationObserver(threadlistCallback); observer.observe(document.body, observerConfig); } else { const bodyObserver = new MutationObserver(bodyCallback); bodyObserver.observe(document.documentElement, { childList: true, }); } } // 处理帖子列表 function processThreadList(tbody) { const items = tbody.querySelectorAll(".common, .new, .lock"); items.forEach(processPostItem); } // 处理单个帖子 function processPostItem(item) { const titleElement = item.querySelector(".xst"); if (!titleElement) return; const applyVisitedStyle = () => { if (GM_config.get("visitedEnable")) { applySavedVisitedThreadStyle(titleElement); } }; // 如果已经是加粗样式,跳过处理 if (titleElement.style.fontWeight === "700") { applyVisitedStyle(); return; } const readNum = extractNumber(item.querySelector(".xw1")?.textContent); // 处理阅读权限 if (readNum > CONFIG.MAX_ACCESS_NUM) { titleElement.setAttribute("style", getPostStyle("blocked")); applyVisitedStyle(); return; } if (readNum > status.accessNum) { titleElement.setAttribute("style", getPostStyle("restricted")); applyVisitedStyle(); return; } // 处理点赞数 const agreeNum = getMaxAgreeCount(item.querySelectorAll("font")); if (agreeNum >= GM_config.get("agreeThreshold")) { titleElement.setAttribute( "style", getPostStyle("highlighted", GM_config.get("agreeColor")), ); applyVisitedStyle(); return; } // 处理回复数 const replyNum = extractNumber( item.parentNode.querySelector("td.num a")?.textContent, ); if (replyNum >= GM_config.get("replyTHreshold")) { titleElement.setAttribute( "style", getPostStyle("highlighted", GM_config.get("replyColor")), ); } applyVisitedStyle(); } function createSettingsIcon() { const iconSpan = document.createElement("span"); iconSpan.id = "settingsIcon"; iconSpan.title = "打开福利吧小助手设置"; iconSpan.innerHTML = icon.settings; iconSpan.tabIndex = 0; iconSpan.setAttribute("role", "button"); iconSpan.setAttribute("aria-label", iconSpan.title); iconSpan.addEventListener("click", () => { GM_config.open(); }); iconSpan.addEventListener("keydown", (event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); iconSpan.click(); } }); return iconSpan; } function operateCode(inputId, outputId, callback, waitStr = "") { const str = document.getElementById(inputId).value; if (str) { document.getElementById(outputId).value = waitStr; const result = callback(str, outputId); if (result) { document.getElementById(outputId).value = result; } } } function copyCode(copyBtn, resultId) { const copyStr = document.getElementById(resultId).value; if (copyStr) { GM_setClipboard(copyStr, "text"); copyBtn.title = "复制成功"; copyBtn.classList.add("a_copy_completed"); status.timer3 && clearTimeout(status.timer3); status.timer3 = setTimeout(() => { copyBtn.classList.remove("a_copy_completed"); copyBtn.title = "将输出的结果复制到剪贴板中"; }, 2000); } } function createSwitchSpan(keyValue, domArray) { const switchSpan = document.createElement("span"); switchSpan.className = "o"; const switchImg = document.createElement("img"); switchImg.id = keyValue + "_user_img"; switchImg.title = "收起/展开"; switchImg.alt = "收起/展开"; if (status[keyValue]) { domArray.forEach(function (item) { item.classList.add("collapsed_hide"); }); switchImg.src = icon.collapsedYes; } else { switchImg.src = icon.collapsedNo; } switchImg.addEventListener("click", () => { if (status[keyValue]) { domArray.forEach((item) => { item.classList.remove("collapsed_hide"); }); switchImg.src = icon.collapsedNo; status[keyValue] = false; } else { domArray.forEach((item) => { item.classList.add("collapsed_hide"); }); switchImg.src = icon.collapsedYes; status[keyValue] = true; } GM_config.setValue(keyValue, status[keyValue]); }); switchSpan.appendChild(switchImg); return switchSpan; } function createSignatureSwitch(signature, switchStatus = true) { const switchDiv = document.createElement("div"); switchDiv.className = "signature_switch_div"; switchDiv.innerHTML = icon.signature; switchDiv.tabIndex = 0; switchDiv.setAttribute("role", "button"); if (switchStatus) { switchDiv.title = "收起"; } else { switchDiv.title = "展开"; switchDiv.classList.add("signature_switch_close"); signature.classList.add("signature_hide"); } switchDiv.setAttribute("aria-label", switchDiv.title); switchDiv.setAttribute("aria-expanded", String(switchStatus)); switchDiv.addEventListener("keydown", (event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); switchDiv.click(); } }); switchDiv.addEventListener( "click", () => { if (switchStatus) { switchStatus = false; switchDiv.title = "展开"; switchDiv.classList.add("signature_switch_close"); signature.classList.add("signature_hide"); } else { switchStatus = true; switchDiv.title = "收起"; switchDiv.classList.remove("signature_switch_close"); signature.classList.remove("signature_hide"); } switchDiv.setAttribute("aria-label", switchDiv.title); switchDiv.setAttribute("aria-expanded", String(switchStatus)); }, false, ); return switchDiv; } function applyContentBlocking() { if (!GM_config.get("blockUsersEnable")) return; const blockList = GM_config.get("blockUsersList") .split(",") .map((s) => s.trim()); GM_addStyle(style.basic); // 主题列表处理 blockList.forEach((userId) => { // 通过 uid 匹配 (从 space-uid-XXXXX.html 提取) const xpathByUid = `//tbody[contains(@id, 'normalthread')][.//td[contains(@class,'by')]//a[contains(@href, "space-uid-")]]`; const postsByUid = document.evaluate( xpathByUid, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null, ); // 处理 uid 匹配结果 Array.from({ length: postsByUid.snapshotLength }, (_, i) => postsByUid.snapshotItem(i), ).forEach((post) => { const userLinks = post.querySelectorAll('a[href*="space-uid-"]'); for (const link of userLinks) { const href = link.getAttribute("href"); const uidMatch = href.match(/space-uid-(\d+)\.html/); if (uidMatch && uidMatch[1] === userId) { post.innerHTML = `
${GM_config.get("blockDisplayText")} (UID: ${userId})
`; } } }); }); // 内容页处理 blockList.forEach((userId) => { const selectors = [ `//table[tbody/tr/td[1]//a[text()="${userId}"]]`, `//table[tbody/tr/td[1]/div[1]//font[text()="${userId}"]]`, ]; selectors.forEach((xpath) => { const replies = document.evaluate( xpath, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null, ); Array.from({ length: replies.snapshotLength }, (_, i) => replies.snapshotItem(i), ).forEach((reply) => { reply.style.backgroundColor = "#ffe6e6"; reply.innerHTML = ` ${reply.textContent.substr(0, 50)}...
已屏蔽用户 ${userId} 的回复
`; }); }); }); } function allInit() { const isMobile = isMobilePage(); const { baseImage } = reg; const { checkDate, oldUrl } = status; // 注册菜单命令并添加基本样式 try { GM_registerMenuCommand("福利吧小助手 - 设置", () => GM_config.open()); } catch (e) { console.error("福利吧小助手:\n在扩展中注册菜单项出错!"); console.error(e); } // 主初始化逻辑 const executeInitialization = () => { GM_addStyle(style.basic); if (GM_config.get("enableAdPurge")) { // 同时隐藏已有和延迟插入的红包弹窗及其遮罩。 GM_addStyle(` #flhb-mask { display: none !important; } `); } const darkPalette = GM_config.get("darkPalette"); if (darkPalette !== "官方配色") { const colors = Object.entries(darkColorFields) .map(([name, property]) => { const color = darkPalette === "自定义" ? GM_config.get(name) : GM_config.fields[name].default; return `${property}: ${color};`; }) .join("\n"); GM_addStyle(`html.darking { ${colors} }\n${style.night}`); } // 处理自动签到功能 if (GM_config.get("autoCheckInEnable")) { if (isMobile) { document.body.arrive( ".bg > a", { onceOnly: true, existing: true }, function (item) { if (item.textContent === "签到领奖") { const nowDate = getNowDate(); if (nowDate !== checkDate) { GM_config.setValue("checkDate", nowDate); setTimeout(() => item.click(), 2000); console.info("福利吧小助手:\n完成自动签到。"); } } }, ); } else { document.arrive( "#fx_checkin_topb", { fireOnAttributesModification: true, onceOnly: true, existing: true, }, function () { if (this.getElementsByTagName("img")[0].alt !== "已签到") { const clickAction = () => this.click(); typeof fx_checkin === "function" ? clickAction() : setTimeout(clickAction, 2000); console.info("福利吧小助手:\n自动签到完成。"); } }, ); } } // 处理论坛显示页面 const isForumDisplayPage = oldUrl.indexOf("mod=forumdisplay") !== -1 || /forum-\d+-\d+\.html/i.test(oldUrl); if (isForumDisplayPage) { if (GM_config.get("highlightEnable")) { if (isMobile) { document.body.arrive( ".threadlist li", { fireOnAttributesModification: true, existing: true }, function () { let replyNum = this.querySelector( "span.num.icon.iconfont", ).textContent; if (replyNum) { replyNum = Number(/\d+/.exec(replyNum)[0]); if (replyNum >= GM_config.get("replyTHreshold")) { this.querySelector("div.thread-item-sub").setAttribute( "style", `font-weight: bold; color: ${GM_config.get( "replyColor", )};`, ); } } }, ); } else { highlightPost(); } } if (!isMobile) { const isForum37 = oldUrl.indexOf("fid=37") !== -1 || /forum-37-/i.test(oldUrl); if (isForum37) { document.arrive( "div.bm_h.cl", { fireOnAttributesModification: true, onceOnly: true, existing: true, }, function () { if (this.querySelectorAll("span.y").length === 0) { const themeArray = [this.nextElementSibling]; this.insertBefore( createSwitchSpan("collapsedGwwzEnable", themeArray), this.childNodes[0], ); } }, ); } // 访问的链接样式 if (GM_config.get("visitedEnable")) { const visitedStyle = `.tl th a.xst:visited, .tl td.fn a:visited, #threadlist #threadlisttableid th a.xst:visited, #threadlist #threadlisttableid td.fn a:visited { color: ${GM_config.get( "visitedColor", )} !important; }`; GM_addStyle(visitedStyle); initSavedVisitedThreadMarker(); console.debug("福利吧小助手:\n完成注入自定义己访问帖子的样式。"); } } } const isThreadViewPage = oldUrl.indexOf("mod=viewthread") !== -1 || /thread-\d+-\d+(?:-\d+)?\.html/i.test(oldUrl); if (isThreadViewPage) { if (GM_config.get("visitedEnable")) { rememberVisitedThreadId(getThreadIdFromUrl(oldUrl)); } const processThreadPage = () => { if (isMobile) { const phoneBackgroundColor = window.getComputedStyle( document.body, ).backgroundColor; const messageDom = document.querySelectorAll(".message"); for (let messageEle of messageDom) { const textObj = display_Text( messageEle, GM_config.get("textColor"), phoneBackgroundColor, ); const textArray = analysisText(messageEle); const linkObj = findLink(messageEle); const goodBoyObj = new Good_Boy(GM_config); goodBoyObj.createGoodBoyElement( messageEle, linkObj, textObj, textArray, ); } } else { // 处理图片 const tFImg = document.querySelectorAll("td.t_f img"); for (let imgEle of tFImg) { let baseSrc = imgEle.getAttribute("file"); if (baseImage.test(baseSrc)) { baseSrc = baseSrc.replace(baseImage, "$1"); imgEle.setAttribute("file", baseSrc); imgEle.src = baseSrc; } } const zoomImg = document.querySelectorAll(".t_f img.zoom"); for (let zoomEle of zoomImg) { zoomEle.removeAttribute("height"); } const htmlBackgroundColor = window.getComputedStyle( document.body, ).backgroundColor; const tFDom = document.querySelectorAll("td.t_f"); for (let domEle of tFDom) { const goodBoyObj = new Good_Boy(GM_config); const textObj = display_Text( domEle, GM_config.get("textColor"), htmlBackgroundColor, ); const textArray = analysisText(domEle); const linkObj = findLink(domEle); if (isFloat(GM_config.get("displayPosition"))) { const favatarBtn = domEle .closest("table.plhin") .querySelector("div.favatar"); goodBoyObj.createGoodBoyElement( favatarBtn, linkObj, textObj, textArray, ); } else { const mainBtn = domEle.closest("div.pcb").querySelector("div.t_fsz") || domEle.closest("div.pcb").querySelector("div.pcbs"); goodBoyObj.createGoodBoyElement( mainBtn, linkObj, textObj, textArray, ); } } const signA = document.querySelectorAll("div.sign a"); for (let signEle of signA) { if ( /member\.php\?mod=logging&action=logout/i.test(signEle.href) ) { signEle.style.display = "none"; } } if (GM_config.get("signatureEnable")) { const sign = document.querySelectorAll("div.sign"); const signStatus = GM_config.get("signatureSwitch") == "展开"; for (let signItem of sign) { const signatureContainer = signItem.closest("td.plc, div.pcb"); if (signatureContainer) { signatureContainer.insertBefore( createSignatureSwitch(signItem, signStatus), signatureContainer.firstChild, ); } } } } }; processThreadPage(); } if (!isMobile) { document.arrive( "#sslct", { fireOnAttributesModification: true, onceOnly: true, existing: true, }, (item) => item.after(createSettingsIcon()), ); document.arrive( ".site-nav.site-navbar", { fireOnAttributesModification: true, onceOnly: true, existing: true, }, (nav) => { const searchLi = nav.querySelector(".navto-search"); searchLi ? nav.insertBefore(createSettingsIcon(), searchLi) : nav.appendChild(createSettingsIcon()); }, ); if (GM_config.get("codeEnable")) { const expandBox = document.createElement("div"); expandBox.className = "expand_box"; expandBox.onmouseenter = function () { this.classList.add("show_expand_box"); }; expandBox.onmouseleave = function () { this.classList.remove("show_expand_box"); }; const createCodePanel = () => { const fragment = document.createDocumentFragment(); const codeFrame = document.createElement("div"); fragment.appendChild(codeFrame); // 提取共用的field配置 const createTextField = (label, section = null) => ({ label: label, section: section, type: "textarea", labelPos: "above", placeholder: "请输入内容", default: "", save: false, }); const createButtonField = (label, title, clickHandler) => ({ label: label, title: title, type: "button", click: clickHandler, }); // 创建编解码处理器工厂 const createCodeHandler = (inputId, outputId, processor, processingMsg) => () => operateCode(inputId, outputId, processor, processingMsg); // 提取重复的URL配置 const links = { baijia: `${SITE_URLS.HOME}/anhao.html`, coreValues: "https://github.com/sym233/core-values-encoder", yflc: "http://keyfc.net/bbs/tools/tudoucode.aspx", }; // 优化与佛论禅的网络请求处理 const createYflcProcessor = (action) => (str, resultId) => { const handleResponse = (res) => { const outputEl = document.getElementById(resultId); if (res.status === 200 && res.readyState === 4) { outputEl.value = res.responseText.replace( /^<\/Message><\/BUDDHIST>$/g, "", ); } else { outputEl.value = `${action}出现错误!`; } }; GM_xmlhttpRequest({ method: "POST", url: "https://keyfc.net/bbs/tools/tudou.aspx", data: `orignalMsg=${ action === "编码" ? encodeURIComponent(str) : str.replace(/\s/g, "") }&action=${action}`, headers: { "Content-Type": "application/x-www-form-urlencoded", }, onload: handleResponse, onerror: () => (document.getElementById(resultId).value = "网络连接出错!"), }); return false; }; // 配置面板 const codePanel = new GM_configStruct({ id: "myCodePanel", title: "编解码工具", isTabs: true, skin: "tab", frame: codeFrame, css: style.codePanel, fields: { // 百家姓 bjxInput: { ...createTextField("输入:", [ "百家姓", GM_config.create("a", { textContent: "百家姓编码原示例地址", title: "点击跳转到百家姓编码示例", target: "_blank", href: links.baijia, }), ]), }, bjxEncodeBtn: createButtonField( "编码 >>>", '使用"百家姓"进行编码', createCodeHandler( "myCodePanel_field_bjxInput", "myCodePanel_field_bjxOutput", baijiaEncode, "编码中...", ), ), bjxDecodeBtn: createButtonField( "解码 >>>", '对"百家姓"的编码进行解码', createCodeHandler( "myCodePanel_field_bjxInput", "myCodePanel_field_bjxOutput", baijiaDecode, "解码中...", ), ), bjxOutput: createTextField("输出:"), bjxCopyBtn: createButtonField( "复制结果", "将输出的结果复制到剪贴板中", function () { copyCode(this, "myCodePanel_field_bjxOutput"); }, ), // 社会主义核心价值观 coreValuesInput: { ...createTextField("输入:", [ "社会主义核心价值观", GM_config.create("a", { textContent: "社会主义核心价值观编码项目地址", title: "点击跳转到社会主义核心价值观编码项目", target: "_blank", href: links.coreValues, }), ]), }, coreValuesEncodeBtn: createButtonField( "编码 >>>", '使用"社会主义核心价值观"进行编码', createCodeHandler( "myCodePanel_field_coreValuesInput", "myCodePanel_field_coreValuesOutput", coreValuesEncode, "编码中...", ), ), coreValuesDecodeBtn: createButtonField( "解码 >>>", '对"社会主义核心价值观"的编码进行解码', createCodeHandler( "myCodePanel_field_coreValuesInput", "myCodePanel_field_coreValuesOutput", coreValuesDecode, "解码中...", ), ), coreValuesOutput: createTextField("输出:"), coreValuesCopyBtn: createButtonField( "复制结果", "将输出的结果复制到剪贴板中", function () { copyCode(this, "myCodePanel_field_coreValuesOutput"); }, ), // 与佛论禅 yflcInput: { ...createTextField("输入:", [ "与佛论禅", GM_config.create("a", { textContent: "与佛论禅编码原工具地址", title: "点击跳转到与佛论禅编码工具", target: "_blank", href: links.yflc, }), ]), }, yflcEncodeBtn: createButtonField( "编码 >>>", '使用"与佛论禅"进行编码', createCodeHandler( "myCodePanel_field_yflcInput", "myCodePanel_field_yflcOutput", createYflcProcessor("编码"), "联网进行编码中...", ), ), yflcDecodeBtn: createButtonField( "解码 >>>", '对"与佛论禅"的编码进行解码', createCodeHandler( "myCodePanel_field_yflcInput", "myCodePanel_field_yflcOutput", createYflcProcessor("解码"), "联网进行解码中...", ), ), yflcOutput: createTextField("输出:"), yflcCopyBtn: createButtonField( "复制结果", "将输出的结果复制到剪贴板中", function () { copyCode(this, "myCodePanel_field_yflcOutput"); }, ), // BASE64 base64Input: { ...createTextField("输入:", [ "BASE64", GM_config.create("a", { textContent: "crypto-js 项目地址", title: "点击跳转到 crypto-js 项目", target: "_blank", href: "https://github.com/brix/crypto-js", }), ]), }, base64EncodeBtn: createButtonField( "编码 >>>", '使用"BASE64"进行编码', createCodeHandler( "myCodePanel_field_base64Input", "myCodePanel_field_base64Output", (str) => { if (!window.CryptoJS) return "crypto-js 库文件不存在!"; const words = CryptoJS.enc.Utf8.parse(str); return CryptoJS.enc.Base64.stringify(words); }, "编码中...", ), ), base64DecodeBtn: createButtonField( "解码 >>>", '对"BASE64"的编码进行解码', createCodeHandler( "myCodePanel_field_base64Input", "myCodePanel_field_base64Output", (str) => { if (!window.CryptoJS) return "crypto-js 库文件不存在!"; const words = CryptoJS.enc.Base64.parse(str); return words.toString(CryptoJS.enc.Utf8); }, "解码中...", ), ), base64Output: createTextField("输出:"), base64CopyBtn: createButtonField( "复制结果", "将输出的结果复制到剪贴板中", function () { copyCode(this, "myCodePanel_field_base64Output"); }, ), }, events: { open: function (doc) { const config = this; const closeBtn = doc.getElementById(config.id + "_closeBtn"); const resetLink = doc.getElementById( config.id + "_resetLink", ); if (closeBtn) closeBtn.title = "关闭面板"; if (resetLink) { resetLink.textContent = "清空内容"; resetLink.title = "清空所有内容"; } }, }, }); // 创建打开面板的按钮 const codeSpan = document.createElement("span"); codeSpan.id = "myCodeSpan"; codeSpan.title = "打开编解码工具"; codeSpan.innerHTML = icon.code; codeSpan.tabIndex = 0; codeSpan.setAttribute("role", "button"); codeSpan.setAttribute("aria-label", codeSpan.title); codeSpan.addEventListener("keydown", (event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); codeSpan.click(); } }); codeSpan.addEventListener("click", () => codePanel.open(), { passive: true, }); // 一次性添加所有DOM元素 document.body.appendChild(fragment); expandBox.appendChild(codeSpan); return codePanel; }; createCodePanel(); document.body.appendChild(expandBox); } if (document.head && !document.head.querySelector("title")) { console.warn("福利吧小助手:\n网页加载失败。即将自动刷新重载。"); status.timer4 && clearTimeout(status.timer4); status.timer4 = setTimeout(() => location.reload(), 2000); } } const isFulibaDomain = location.hostname.includes("fulibus.net") || location.hostname.includes("fuliba") || location.hostname.includes("f.uliba.net"); if (isFulibaDomain) { removeElements(); adjustStyles(); simplifyNavigation(); removePromotions(); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", processText); } else { processText(); } } // 在论坛页面移除广告位 if (!isFulibaDomain) { removeGzListAds(); } // 头像勋章移除功能初始化 if (GM_config.get("removeAvatarMedalEnable")) { handleAvatarAndMedal(); } // 头像/图片模糊功能初始化 if (GM_config.get("blurAvatarEnable")) { handleAvatarBlur(); } // 屏蔽功能初始化 if (GM_config.get("blockUsersEnable")) { // 初始执行 applyContentBlocking(); // 动态内容监听 new MutationObserver((mutations) => { mutations.forEach(() => applyContentBlocking()); }).observe(document.body, { childList: true, subtree: true, }); } }; // 动态内容检测 if (document.body) { executeInitialization(); } else { new MutationObserver((mutations, observer) => { if (document.body) { observer.disconnect(); executeInitialization(); } }).observe(document.documentElement, { childList: true }); } } function handleAvatarAndMedal() { if (!GM_config.get("removeAvatarMedalEnable")) return; // 添加CSS隐藏样式 const styleId = "avatarMedalHideStyle"; if (!document.getElementById(styleId)) { const styleNode = GM_addStyle(` .avatar, .md_ctrl { display: none !important; } `); styleNode && (styleNode.id = styleId); } // 确保在body存在后初始化Observer function initObserver() { if (!document.body) { setTimeout(initObserver, 100); return; } const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType === 1) { // 处理新增节点 const elements = node.querySelectorAll?.(".avatar, .md_ctrl") || []; elements.forEach((el) => (el.style.display = "none")); // 处理节点自身 if (node.matches(".avatar, .md_ctrl")) { node.style.display = "none"; } } }); }); }); // 使用document.documentElement作为备选 const observeTarget = document.body || document.documentElement; observer.observe(observeTarget, { childList: true, subtree: true, }); } // 延迟初始化确保body存在 if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initObserver); } else { initObserver(); } } function handleAvatarBlur() { if (!document.body) { document.addEventListener("DOMContentLoaded", handleAvatarBlur); return; } const handleHover = (e) => { const img = e.target.closest(IMAGE_SELECTOR); if (!img || !img.dataset.blurEnabled) return; if (e.type === "mouseenter") { img.style.filter = "none"; } else if (e.type === "mouseleave") { img.style.filter = "blur(10px)"; } }; // 先移除旧的事件监听避免重复绑定 document.body.removeEventListener("mouseenter", handleHover, true); document.body.removeEventListener("mouseleave", handleHover, true); // 使用捕获阶段确保动态加载的元素也能触发 document.body.addEventListener("mouseenter", handleHover, true); document.body.addEventListener("mouseleave", handleHover, true); const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { // 处理新增节点及其子节点 const images = node.matches(IMAGE_SELECTOR) ? [node] : node.querySelectorAll(IMAGE_SELECTOR); images.forEach((img) => processAvatar(img)); } }); }); }); observer.observe(document.body, { childList: true, subtree: true, }); // 初始化现有图片 document.querySelectorAll(IMAGE_SELECTOR).forEach(processAvatar); } function processAvatar(img) { if (!img.dataset.blurEnabled) { img.style.filter = "blur(10px)"; img.style.transition = "filter 0.3s ease-in-out"; img.dataset.blurEnabled = "true"; // 确保鼠标移出时恢复模糊 img.addEventListener("mouseleave", () => { if (img.dataset.blurEnabled === "true") { img.style.filter = "blur(10px)"; } }); } } // 移除多余元素 function removeElements() { if (!GM_config.get("enableAdPurge")) return; if (document.querySelector(".content .wxs-tabs")) { if (!document.getElementById("fulibaPromotionTabsStyle")) { // 保留面板节点,避免破坏站点分栏脚本缓存的引用。 const promotionStyle = GM_addStyle(` .content .wxs-tabs [data-wxs-tab="shop"], .content .wxs-tabs [data-wxs-tab="card"], .content [data-wxs-pane="shop"], .content [data-wxs-pane="card"] { display: none !important; } `); promotionStyle.id = "fulibaPromotionTabsStyle"; } const url = new URL(location.href); const tab = url.searchParams.get("tab"); if (url.pathname === "/" && (tab === "shop" || tab === "card")) { url.searchParams.delete("tab"); location.replace(url.href); return; } } // 移除预定义列表中的元素 ELEMENTS_TO_REMOVE.forEach((selector) => { const element = document.querySelector(selector); if (element && element.parentNode) { if (selector === ".topbar") { // 原站桌面端只显示顶栏入口,删除顶栏后启用已有主导航入口。 document .querySelector(".site-navbar .navto-darkmode") ?.style.setProperty("display", "inline-block", "important"); } element.parentNode.removeChild(element); } }); // 更精准地定位并移除.content中的广告链接 const contentDiv = document.querySelector(".content"); if (contentDiv) { // 查找所有直接子元素和li包裹中的a标签 const adLinks = contentDiv.querySelectorAll( ":scope > a, :scope > br + a, :scope > li > a", ); adLinks.forEach((adLink) => { // 检查这个链接是否为广告链接 if (isAdvertisementLink(adLink)) { const removeTarget = adLink.parentElement?.parentElement === contentDiv && adLink.parentElement.tagName.toLowerCase() === "li" ? adLink.parentElement : adLink; contentDiv.removeChild(removeTarget); } }); } } // 判断链接是否为广告 function isAdvertisementLink(link) { // 检查是否包含图片 const hasImage = link.querySelector("img") !== null; // 检查链接是否在文章内容前面 const isBeforeContent = link.nextElementSibling && (link.nextElementSibling.classList.contains("title") || link.nextElementSibling.classList.contains("excerpt")); // 检查链接URL是否包含常见广告域名关键词 const href = link.href.toLowerCase(); const isAdDomain = href.includes("ad") || href.includes("click") || href.includes("haha") || href.includes("cq.com"); // 检查链接是否只包含一个图片元素且没有其他内容 const hasOnlyImage = link.children.length === 1 && link.children[0].tagName.toLowerCase() === "img"; return hasImage && (isBeforeContent || isAdDomain || hasOnlyImage); } // 样式调整函数 function adjustStyles() { const header = document.querySelector(".header"); header && (header.style.padding = "20px 0"); const containers = document.querySelectorAll(".container, .main-wrapper"); const contents = document.querySelectorAll(".content"); if (!containers.length || !contents.length) return; if (GM_config.get("customStyleEnable")) { const widthInput = parseInt(GM_config.get("contentWidth")); const validWidth = isNaN(widthInput) ? 80 : Math.min(Math.max(widthInput, 30), 100); containers.forEach((container) => { container.style.cssText = ` max-width: none !important; min-width: 0 !important; width: 100% !important; margin-left: auto !important; margin-right: auto !important; `; }); contents.forEach((content) => { content.style.cssText = ` width: ${validWidth}% !important; margin-left: auto !important; margin-right: auto !important; max-width: none !important; `; }); } else { // 恢复默认状态 containers.forEach((container) => { container.style.cssText = ""; // 清空内联样式 container.style.removeProperty("max-width"); container.style.removeProperty("width"); }); contents.forEach((content) => { content.style.cssText = ""; // 清空内联样式 if (GM_config.get("enableAdPurge")) { content.style.marginLeft = "190px"; content.style.marginRight = "190px"; } }); } } // 优化导航栏 function simplifyNavigation() { if (!GM_config.get("enableAdPurge")) return; const navSelectors = ["ul.site-nav.site-navbar", "ul.m-navbar"]; navSelectors.forEach((selector) => { const nav = document.querySelector(selector); if (!nav) return; function shouldKeepSpecific(element) { const text = element.textContent.trim(); return ( Boolean(element.querySelector('[data-dztheme="toggle"]')) || KEEP_KEYWORDS.some((keyword) => text.includes(keyword)) ); } function shouldBlock(element) { const text = element.textContent.toLowerCase(); const link = element.querySelector("a")?.href.toLowerCase() || ""; return BLOCK_KEYWORDS.some( (keyword) => text.includes(keyword.toLowerCase()) || link.includes(keyword.toLowerCase()), ); } function shouldKeep(element) { const text = element.textContent; const isSearch = element.classList.contains("navto-search"); const isSpecificItem = shouldKeepSpecific(element); return ( isSpecificItem || isSearch || ((text.includes("福") || text.includes("关于")) && !shouldBlock(element)) ); } function processNavItems(parent) { Array.from(parent.children).forEach((item) => { if (item.tagName.toLowerCase() === "li") { const subMenu = item.querySelector(".sub-menu"); if (subMenu) { processNavItems(subMenu); } // 如果子菜单为空且父项不是特定保留项,则删除父项 if ( subMenu && subMenu.children.length === 0 && !shouldKeepSpecific(item) ) { item.remove(); } // 如果不应保留,则删除 else if (!shouldKeep(item)) { item.remove(); } } }); } processNavItems(nav); }); } // 移除推广内容 function removePromotions() { if (!GM_config.get("enableAdPurge")) return; const content = document.querySelector(".content"); if (!content) return; Array.from(content.querySelectorAll("article")).forEach((article) => { const likeCount = parseInt( article.querySelector(".post-like span")?.textContent, ); if (likeCount > 100) article.remove(); }); } // 移除论坛广告位 function removeGzListAds() { if (!GM_config.get("enableAdPurge")) return; // 查找所有论坛广告位容器 const forumAdElements = document.querySelectorAll(".GzList, .flbcube"); forumAdElements.forEach((forumAdElement) => { // 移除广告位及其父容器 const parentContainer = forumAdElement.closest("div.wp.cl"); if (parentContainer && parentContainer.parentNode) { parentContainer.parentNode.removeChild(parentContainer); } else if (forumAdElement.parentNode) { // 如果没有找到父容器,直接移除广告位元素 forumAdElement.parentNode.removeChild(forumAdElement); } }); } // 处理正文内容 function processText() { // 获取网页背景颜色 const htmlBackgroundColor = window.getComputedStyle( document.body, ).backgroundColor; // 处理段落 const paragraphs = document.querySelectorAll("p"); paragraphs.forEach((p) => { const text = p.innerText; if (text) { // 处理百家姓编码 const surnameMatches = findSurnameEncoding(text); if (surnameMatches.length > 0) { let newText = p.innerHTML; const decodedContent = document.createDocumentFragment(); surnameMatches.forEach((match) => { const decoded = processSurnameEncoding(match); if (decoded) { const isValid = isValidDecodedLink(decoded); const container = document.createElement("div"); // 根据有效性添加不同内容 if (isValid) { // 有效链接:显示可点击链接和复制按钮 const link = document.createElement("a"); link.href = decoded; link.textContent = decoded; link.target = "_blank"; link.className = "my_good_boy_a"; container.appendChild(link); if (GM_config.get("copyEnable")) { const copyBtn = document.createElement("a"); copyBtn.className = "my_good_boy_a_copy"; copyBtn.title = "复制链接"; copyBtn.href = "javascript:;"; copyBtn.textContent = "复制"; copyBtn.addEventListener("click", function () { GM_setClipboard(decoded, "text"); this.classList.add("a_copy_completed"); this.title = "复制成功"; status.timer1 && clearTimeout(status.timer1); status.timer1 = setTimeout(() => { this.classList.remove("a_copy_completed"); this.title = "复制链接"; }, 2000); }); container.appendChild(copyBtn); } } else { const warning = document.createElement("span"); warning.textContent = "(未通过磁力验证,原文提供编码可能存在错误)"; warning.style.color = "red"; const content = document.createElement("code"); content.textContent = decoded; container.appendChild(warning); container.appendChild(content); } newText = newText.replace(match, ""); decodedContent.appendChild(container); } }); p.innerHTML = newText; p.appendChild(decodedContent); } // 处理YouTube链接 if (text.includes("watch?v=")) { const videoId = text.split("?v=")[1]?.split(/[\s&]/)[0]; if (videoId) { const link = document.createElement("a"); link.href = `https://www.youtube.com/watch?v=${videoId}`; link.textContent = "油管链接"; link.target = "_blank"; link.className = "my_good_boy_a"; p.appendChild(link); } } } // 处理好孩子看不见 display_Text(p, GM_config.get("textColor"), htmlBackgroundColor); }); // 处理好孩子看不见 const blockquotes = document.querySelectorAll("blockquote"); blockquotes.forEach((blockquote) => { display_Text(blockquote, GM_config.get("textColor"), htmlBackgroundColor); }); const contentDivs = document.querySelectorAll("div.t_f, div.pcb"); contentDivs.forEach((div) => { display_Text(div, GM_config.get("textColor"), htmlBackgroundColor); }); } // 查找百家姓编码 function findSurnameEncoding(text) { return text.match(new RegExp(reg.baijia, "gi")) || []; } // 处理百家姓编码 function processSurnameEncoding(encodedText) { try { return baijiaDecode(encodedText); } catch (e) { console.warn("福利吧小助手:\n百家姓解码出错:", e); return null; } } // 验证解码结果 function isValidDecodedLink(decoded) { // 验证是否是磁力链接 if (decoded.startsWith("magnet:?xt=urn:btih:")) { const hash = decoded.replace("magnet:?xt=urn:btih:", "").split("&")[0]; // 获取hash并忽略其他参数 return /^[a-fA-F0-9]{40}$/i.test(hash); // 验证是否是40位的十六进制字符 } // 验证是否是http/https链接 if (decoded.startsWith("http://") || decoded.startsWith("https://")) { try { new URL(decoded); return true; } catch (e) { return false; } } // 验证是否是其他支持的协议链接 const supportedProtocols = [ "ed2k://", "thunder://", "flashget://", "qqdl://", "xfplay://", "ftp://", ]; return supportedProtocols.some((protocol) => decoded.startsWith(protocol)); } GM_config.init({ id: "myGoodBoyConfig", title: GM_config.create("a", { textContent: "福利吧小助手-设置 ver." + GM_info.script.version, title: "点击跳转到脚本页面" + getUpdateTime(GM_info.script.lastModified), target: "_blank", href: "https://sleazyfork.org/zh-CN/scripts/381494", }), skin: "tab", css: style.settings, frameStyle: { width: "400px", height: "720px", }, fields: { autoCheckInEnable: { label: "自动签到", title: "进入论坛后自动点击签到按钮", labelPos: "right", type: "checkbox", default: true, section: GM_config.create("a", { textContent: "作者: aoguai", title: "点击反馈问题", target: "_blank", href: location.origin + "/home.php?mod=spacecp&ac=pm&op=showmsg&touid=90713", }), }, enableAdPurge: { label: "净化页面广告", title: "移除页面广告和推广内容", labelPos: "right", type: "checkbox", default: true, }, customStyleEnable: { label: "启用调整页面样式", title: "启用后应用脚本的页面样式调整,关闭则保留原站样式", labelPos: "right", type: "checkbox", default: true, }, contentWidth: { label: "内容区域宽度(%)", title: "输入30-100之间的数字控制内容区域宽度(默认80%)", type: "text", default: "80", validate: function (value) { const num = Number(value); return !isNaN(num) && num >= 30 && num <= 100; }, dependent: ["customStyleEnable"], }, darkPalette: { label: "深色模式配色", title: "由网站主题按钮切换明暗;此处只选择深色配色,保存后生效", type: "select", options: ["官方配色", "插件默认", "自定义"], default: "插件默认", }, darkBgColor: { label: "页面及基础面板背景", type: "text", attr: { type: "color" }, default: "#282A36", }, darkSurfaceColor: { label: "导航及次级面板背景", type: "text", attr: { type: "color" }, default: "#40444D", }, darkInputColor: { label: "输入框及代码区域背景", type: "text", attr: { type: "color" }, default: "#38383D", }, darkHoverColor: { label: "列表及菜单悬停背景", type: "text", attr: { type: "color" }, default: "#1A1A1A", }, darkTextColor: { label: "正文及主要文字", type: "text", attr: { type: "color" }, default: "#C0C0C0", }, darkMutedColor: { label: "次要说明文字", type: "text", attr: { type: "color" }, default: "#666666", }, darkBorderColor: { label: "边框及分隔线", type: "text", attr: { type: "color" }, default: "#40444D", }, darkAccentColor: { label: "主题及控件强调色", type: "text", attr: { type: "color" }, default: "#00A1D6", }, restoreOfficialPalette: { label: "恢复官方配色", title: "保留自定义颜色,保存后使用官方配色", type: "button", click: () => { const palette = GM_config.fields.darkPalette.node; palette.value = "官方配色"; palette.onchange(); }, }, restorePluginPalette: { label: "恢复插件默认", title: "重置八项配色并选择插件默认,保存后生效", type: "button", click: () => { Object.keys(darkColorFields).forEach((name) => { GM_config.fields[name].reset(); }); GM_config.fields.darkPalette.reset(); GM_config.fields.darkPalette.node.onchange(); }, }, codeEnable: { label: "显示编解码工具的悬浮按钮", title: "将鼠标移至网页右下角弹出 (仅支持电脑端页面)", labelPos: "right", type: "checkbox", default: false, }, copyEnable: { label: "显示复制按钮", title: "在所提取链接后显示复制按钮", labelPos: "right", type: "checkbox", default: true, }, extractEnable: { label: "识别文字中的网址并转换为超链接", title: "将帖子中网址文字转换为可点击访问的超链接", labelPos: "right", type: "checkbox", default: true, }, displayPosition: { label: "自定义提取链接的显示位置: ", title: "指定所提取链接的显示位置 (仅支持电脑端页面)", labelPos: "left", type: "select", options: ["底部", "右侧"], default: "底部", }, maxLinkNumber: { label: "所提取链接的最大显示数量: ", title: "每个楼层所提取链接的默认最大显示数量值 (-1 表示无限制)", labelPos: "left", type: "int", size: 18, default: 5, }, linkColor: { label: "自定义提取链接的文字颜色: ", title: "设定所提取链接显示的文字颜色", labelPos: "left", type: "text", size: 18, default: "#369", }, textColor: { label: "自定义隐藏文字的高亮颜色: ", title: "设定将隐藏文字高亮的颜色", labelPos: "left", type: "text", size: 18, default: "#FF33CC", }, signatureEnable: { label: "显示签名档折叠图标", title: "在签名档的左上方显示一个折叠图标", labelPos: "right", type: "checkbox", default: true, line: "start", }, signatureSwitch: { label: "签名档的默认折叠状态: ", title: "自定义签名档默认的折叠状态", labelPos: "left", type: "select", options: ["展开", "收起"], default: "展开", line: "end", }, highlightEnable: { label: "启用热帖高亮功能", title: "在帖子列表页面开启热帖高亮 (移动端页面只支持回复高亮)", labelPos: "right", type: "checkbox", default: true, line: "start", }, agreeThreshold: { label: "按分享值高亮的阈值: ", title: "分享值>=阈值时高亮", labelPos: "left", type: "unsigned int", size: 18, default: 20, }, agreeColor: { label: "按分享值高亮的颜色: ", title: "优化级高", labelPos: "left", type: "text", size: 18, default: "#EE1B2E", }, replyTHreshold: { label: "按回复数高亮的阈值: ", title: "回复数>=阈值时高亮", labelPos: "left", type: "unsigned int", size: 18, default: 50, }, replyColor: { label: "按回复数高亮的颜色: ", title: "优化级低", labelPos: "left", type: "text", size: 18, default: "#2B65B7", line: "end", }, visitedEnable: { label: "标记已打开过的帖子", title: "允许将已打开过的帖子设置成自定义的颜色", labelPos: "right", type: "checkbox", default: false, line: "start", }, visitedRecordLimit: { label: "最多记录已打开过的帖子数量: ", title: "记录最近打开过的帖子ID数量,超过后自动清理最早记录", labelPos: "left", type: "unsigned int", size: 18, default: DEFAULT_VISITED_THREAD_RECORD_LIMIT, dependent: ["visitedEnable"], }, visitedColor: { label: "自定义已打开过的帖子的颜色", title: "优先级最高", labelPos: "left", type: "text", size: 18, default: "#666", line: "end", }, blurAvatarEnable: { label: "启用头像/图片模糊效果", title: "启用后用户头像与帖子中的图片会显示为模糊,鼠标悬停时清晰", labelPos: "right", type: "checkbox", default: false, }, removeAvatarMedalEnable: { label: "移除用户头像和勋章", title: "启用后会隐藏所有用户头像和获得的勋章", labelPos: "right", type: "checkbox", default: false, }, blockUsersEnable: { label: "启用帖子屏蔽功能", title: "启用后根据屏蔽ID列表过滤主题帖", labelPos: "right", type: "checkbox", default: false, }, blockUsersList: { label: "屏蔽用户ID列表", title: "输入要屏蔽的用户ID,多个用英文逗号分隔", type: "textarea", default: "", placeholder: "例如: user1,user2,user3", }, blockDisplayText: { label: "屏蔽提示文字", title: "被屏蔽帖子显示的文字内容", type: "text", default: "该帖子已根据您的屏蔽设置隐藏", }, }, events: { init: async () => { console.debug("福利吧小助手:\n配置信息读取完成。"); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", allInit); } else { allInit(); } }, open: () => { const palette = GM_config.fields.darkPalette.node; palette.onchange = () => { const isCustom = palette.value === "自定义"; Object.keys(darkColorFields).forEach((name) => { GM_config.fields[name].wrapper.style.display = isCustom ? "" : "none"; }); }; palette.onchange(); }, reset: () => { GM_config.fields.darkPalette.node?.onchange?.(); }, save: () => { if (GM_config.get("enableAdPurge")) { removeElements(); simplifyNavigation(); removePromotions(); removeGzListAds(); } else { location.reload(); } // 强制重新应用样式 if (document.getElementById("customStyle")) { document.getElementById("customStyle").remove(); } // 处理头像勋章显示状态 const styleNode = document.getElementById("avatarMedalHideStyle"); if (GM_config.get("removeAvatarMedalEnable")) { if (!styleNode) handleAvatarAndMedal(); } else { styleNode?.remove(); } // 清理旧的模糊状态 document.querySelectorAll(IMAGE_SELECTOR).forEach((img) => { img.style.filter = ""; img.style.transition = ""; delete img.dataset.blurEnabled; }); // 重新初始化功能 if (GM_config.get("blurAvatarEnable")) { handleAvatarBlur(); } else { document.querySelectorAll('img[src*="avatar"]').forEach((img) => { img.style.filter = ""; img.style.transition = ""; }); } location.reload(); }, }, }); })();