// ==UserScript==
// @name Weibo.cn/pub 关键词白字(WAP稳妥版)
// @namespace local.weibo.kwwhite
// @version 1.0.0
// @description 在 weibo.cn(含 /pub)把指定关键词渲染为白色;适配早期WAP结构,附带轮询确保生效
// @author you
// @match *://*.weibo.cn/*
// @match *://weibo.cn/*
// @match *://rebang.today/*
// @run-at document-end
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
/** ① 在这里添加/删除你的关键词 **/
const KEYWORDS = ["日本","中国","乌克兰","以色列","巴勒斯坦","加沙","北京","上海","广东","深圳","广州","南京","杭州","新疆","柬埔寨","网红","警方","官方","官宣","通报","美学","建议","如何","看待","知道","终于","为啥","回应","怎么","评论","评价","是不是","偷偷","啥样","体制内","原生家庭","王安宇","沈月","田栩宁","严浩翔","孙怡","董子健","韩庚","卢靖姗","文章","马伊琍","胡兵","瞿颖","谢霆锋","金晨","黄圣依","檀健次","蔡徐坤", "王一博", "肖战","王俊凯", "王源", "易烊千玺","贾玲", "黄磊", "脱口秀","黄子韬", "沈腾", "赵丽颖","迪丽热巴","热巴","周杰伦","赵露思","邓超","鹿晗","陈赫","王鹤棣","虞书欣","白鹿","小米","华为","鞠婧炜","五月天","中医","中药","雷军","卢伟冰","罗永浩","余承东","陈乔恩","岳云鹏","郭德纲","王楚钦","孙颖莎","张艺兴","时代少年团","iPhone","黄晓明","angelababy","张天爱","吴彦祖","王力宏","韩安冉","李乃文","王冕","服务员","临时工","后续","向佐","向太","张家辉","周星驰","马柏全","张晚意","陈紫函","戴向宇","幽门螺旋杆菌","葛夕","白敬亭","辛芷蕾","黄灿灿","李荣浩","杨丞琳","高圆圆","赵又廷","关晓彤","刘涛","李沁","薛之谦","李一桐","淘宝","美团","马思纯","于正","王晶","王菲","窦靖童","周冬雨","杨蓉","章若楠","台风","航母","唐嫣","白百何"];
/** ② 可选:是否不区分大小写(中文一般无所谓) **/
const CASE_INSENSITIVE = true;
/** ③ 轮询间隔(毫秒)。为兼容 /pub 的局部刷新,建议保留 **/
const POLL_MS = 1000;
// --- 正则 ---
const regex = buildRegex(KEYWORDS, CASE_INSENSITIVE);
if (!regex) return;
// 初次执行 + 轮询
runOnce();
setInterval(runOnce, POLL_MS);
function runOnce() {
// 老页尽量少过滤,直接全页扫;但跳过常见不可见容器
highlightIn(document.body);
}
function buildRegex(words, ci) {
const parts = (words || []).filter(Boolean).map(escapeRegExp);
if (!parts.length) return null;
return new RegExp("(" + parts.join("|") + ")", ci ? "gi" : "g");
}
function escapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
// 核心:扫描文本节点,把命中片段用 <span style="color:#fff !important" data-kw-hit> 包裹
function highlightIn(root) {
if (!root || !regex) return;
const ign = node => {
if (node.nodeType !== 1) return false;
const tag = node.tagName;
// 最小化忽略清单:脚本/样式/内嵌对象
return tag === "SCRIPT" || tag === "STYLE" || tag === "IFRAME" || tag === "OBJECT" || tag === "NOSCRIPT";
};
if (ign(root)) return;
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
const nodes = [];
let n;
while ((n = walker.nextNode())) nodes.push(n);
for (const textNode of nodes) {
const parent = textNode.parentNode;
if (!parent) continue;
// 如果父节点已经是我们包裹过的片段,跳过
if (parent.nodeType === 1 && parent.hasAttribute && parent.hasAttribute("data-kw-hit")) continue;
const text = textNode.nodeValue;
if (!text || !regex.test(text)) continue;
regex.lastIndex = 0;
const frag = document.createDocumentFragment();
let last = 0;
text.replace(regex, (m, _g1, idx) => {
// 前段原样
if (idx > last) frag.appendChild(document.createTextNode(text.slice(last, idx)));
// 命中片段 -> 白字
const span = document.createElement("span");
span.setAttribute("style", "color:#fff !important;");
span.setAttribute("data-kw-hit", "1");
span.textContent = m;
frag.appendChild(span);
last = idx + m.length;
return m;
});
// 末尾残余
if (last < text.length) frag.appendChild(document.createTextNode(text.slice(last)));
parent.replaceChild(frag, textNode);
}
}
})();