// ==UserScript== // @name 替换网页字体 // @author ai // @version 2.6 // @include * // @run-at document-start // @early-start // @grant GM_getValues // @grant GM_setValues // @grant GM_addValueChangeListener // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @description 替换网页字体 // ==/UserScript== (function() { 'use strict'; const DEFAULT = { enabled: true, font: '微软雅黑', exclusions: 'code' }; class FontManager { constructor() { this.config = GM_getValues(DEFAULT); GM_setValues(this.config); this.css = new CSSStyleSheet(); this.shadowdom = []; this.menus = []; this.init(); } init() { this.addListener(); this.shadowRootHook(); this.getWebfont(); this.refresh(); this.updateMenu(); } addListener() { Object.keys(DEFAULT).forEach(k => GM_addValueChangeListener(k, (key, oldV, newV) => { this.config[key] = newV; this.refresh(); if (key === 'enabled') this.updateMenu(); })); } shadowRootHook() { const _this = this; const origAttachShadow = Element.prototype.attachShadow; Element.prototype.attachShadow = function(...args) { const rv = origAttachShadow.apply(this, args); if (_this.config.enabled) _this.applyCSS(rv); _this.shadowdom.push(rv); return rv; }; } getWebfont() { const update = (e) => { const f = new Set(); document.fonts.forEach(i => f.add(`"${i.family}"`)); const webFonts = [...f].join(','); if (webFonts !== this.webFonts) { this.webFonts = webFonts; this.refresh(); } }; document.fonts.ready.then(update); document.fonts.addEventListener('loadingdone', update); } refresh() { const { font, exclusions } = this.config; const fonts = this.webFonts ? `${font}, ${this.webFonts}` : font; const ex = `i, [class*="ico"], [class*="fa-"], [class*="symbol"]${exclusions ? ', ' + exclusions : ''}`; this.css.replaceSync(` @scope (:host, body) to (${ex}) { * { font-family: ${fonts} !important; } } `); this.applyCSS(document); this.shadowdom.forEach(e => this.applyCSS(e)); } applyCSS(e) { const { enabled } = this.config; const currentSheets = [...e.adoptedStyleSheets].filter(s => s !== this.css); if (enabled) { e.adoptedStyleSheets = [...currentSheets, this.css]; } else { e.adoptedStyleSheets = currentSheets; } } updateMenu() { if (window.top !== window) return this.menus.forEach(GM_unregisterMenuCommand); const reg = (txt, fn) => this.menus.push(GM_registerMenuCommand(txt, fn)); const set = (k, v) => GM_setValues({ [k]: v }); reg(this.config.enabled ? '❌ 禁用字体替换' : '✅ 启用字体替换', () => set('enabled', !this.config.enabled)); reg('🖋️ 配置字体名称', () => { const res = prompt('请输入字体名称', this.config.font); if (res) set('font', res); }); reg('🚫 配置排除元素', () => { const res = prompt('请输入排除的选择器', this.config.exclusions); if (res !== null) set('exclusions', res); }); reg('🗑️ 恢复默认值', () => confirm('确定重置吗?') && GM_setValues(DEFAULT)); } } new FontManager(); })();