// ==UserScript==
// @name 必应/Edge 美化 & 效能工具 (Robust & Final Fix)
// @namespace Violentmonkey Scripts
// @version 17.8
// @description 确保在页面动态重绘后脚本依然生效。修复了网址屏蔽功能中(1)移除规则后部分结果仍被隐藏的问题;(2)因Bing链接格式不标准导致无法创建屏蔽规则的问题。
// @author WJH
// @match https://*.bing.com/*
// @match https://*.msn.cn/*
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
// --- 1. CSS样式注入 (粒子特效已增强) ---
const styles = `
/* --- A. 通用UI样式 --- */
#userscript-ui-container { position: fixed; bottom: 25px; right: 25px; z-index: 10001; display: flex; flex-direction: column; align-items: flex-end; gap: 15px; }
.userscript-btn { width: 44px; height: 44px; background-color: rgba(255, 255, 255, 0.9); border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; box-shadow: 0 4px 12px rgba(0,0,0,0.15); transition: all 0.2s ease; }
.userscript-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(0,0,0,0.2); }
.userscript-btn svg { width: 24px; height: 24px; color: #333; }
#go-to-top-btn { opacity: 0; pointer-events: none; transform: translateY(10px); }
#go-to-top-btn.visible { opacity: 1; pointer-events: auto; transform: translateY(0); }
#manage-blocklist-btn { transform: scale(0.9); opacity: 0.7; }
#manage-blocklist-btn:hover { opacity: 1; transform: scale(1); }
/* --- B. 必应搜索 (bing.com) 美化样式 --- */
body[data-host="bing"] {
background-image: url('https://raw.githubusercontent.com/WJH-makers/markdown_photos/main/images/sea.png') !important;
background-size: cover !important; background-position: center center !important;
background-attachment: fixed !important; background-repeat: no-repeat !important;
}
/* B.1 搜索结果样式 */
#b_content { padding-top: 20px !important; }
#b_results { background: none !important; box-shadow: none !important; border: none !important; }
.sb_count { font-size: 13px !important; color: #f0f0f0 !important; text-shadow: 1px 1px 2px rgba(0,0,0,0.6); }
body[data-host="bing"] #b_pag { opacity: 0; pointer-events: none; height: 10px; }
body[data-host="bing"] #infinite-scroll-loader { text-align: center; padding: 20px; color: #FFF; text-shadow: 1px 1px 2px #000; font-weight: bold; min-height: 24px; }
body[data-host="bing"] #b_results > li.b_algo,
body[data-host="bing"] #b_results > li.b_ans:not(:has(.lite-entcard-main)) {
background-color: rgba(255, 255, 255, 0.85) !important; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(224, 224, 224, 0.7); border-radius: 12px !important;
padding: 20px 24px 20px 50px !important;
margin-bottom: 16px !important;
max-width: 1500px !important;
min-width: 1000px;
box-shadow: 0 1px 4px rgba(0,0,0,0.05); transition: all 0.2s ease-in-out;
position: relative;
display: block !important; box-sizing: border-box;
}
body[data-host="bing"] #b_results > li.b_algo:hover,
body[data-host="bing"] #b_results > li.b_ans:not(:has(.lite-entcard-main)):hover {
box-shadow: 0 5px 15px rgba(0,0,0,0.1) !important; transform: translateY(-2px); border-color: rgba(208, 208, 208, 0.9);
}
/* B.2 隐藏广告和无关模块 */
body[data-host="bing"] .b_header_bg, body[data-host="bing"] #est_switch,
body[data-host="bing"] #b_pole, body[data-host="bing"] li.b_ad, body[data-host="bing"] #b_footer,
body[data-host="bing"] li.b_msg, body[data-host="bing"] #adstop_gradiant_separator,
body[data-host="bing"] .b_rrsr, body[data-host="bing"] #b_context,
body[data-host="bing"] li.b_ans:has(.rqnaContainerwithfeedback),
body[data-host="bing"] li.b_ans:has(.lite-entcard-main),
body[data-host="bing"] li.b_ans:has(#brsv3), body[data-host="bing"] li.b_ans:has(#inline_rs),
body[data-host="bing"] li.b_ans:has(#brs_list),
body[data-host="bing"] div[data-tag="RichRSRailOuterWrapper"]
{ display: none !important; }
/* B.3 效能搜索工具栏样式 */
#power-search-toolbar { background-color: rgba(255, 255, 255, 0.88); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); padding: 10px 16px; border-radius: 10px; margin: 0px 0px 50px; max-width: 960px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); border: 1px solid rgba(224, 224, 224, 0.7); }
.power-search-group { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.power-search-group:not(:last-child) { margin-bottom: 12px; }
.power-search-group-label { font-size: 14px; font-weight: bold; color: #333; margin-right: 8px; flex-shrink: 0; }
.power-search-btn { font-size: 13px; color: #444; text-decoration: none; padding: 5px 12px; border-radius: 16px; background-color: rgba(0,0,0,0.05); border: 1px solid rgba(0,0,0,0.08); transition: all 0.2s ease; }
.power-search-btn:hover { background-color: rgba(0,0,0,0.1); border-color: rgba(0,0,0,0.15); }
.power-search-btn.active { font-weight: bold; color: #fff !important; background-color: #0078d4; border-color: #0078d4; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.filter-divider { width: 100%; height: 1px; background-color: rgba(0,0,0,0.1); margin: 12px 0 12px 0; }
/* B.4 网站屏蔽功能样式 */
.block-site-btn { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 28px; height: 28px; border-radius: 50%; background-color: rgba(0,0,0,0.05); border: 1px solid rgba(0,0,0,0.1); display: flex; justify-content: center; align-items: center; cursor: pointer; opacity: 0.2; transition: all 0.2s ease; }
li.b_algo:hover .block-site-btn, li.b_ans:hover .block-site-btn { opacity: 0.6; }
.block-site-btn:hover { opacity: 1; background-color: #f44336; border-color: #f44336; color: white; transform: translateY(-50%) scale(1.1); }
.block-site-btn svg { width: 16px; height: 16px; }
/* B.5 屏蔽列表管理弹窗样式 */
#blocklist-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 20000; display: none; justify-content: center; align-items: center; }
#blocklist-modal { background-color: #fff; padding: 24px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); width: 90%; max-width: 500px; max-height: 80vh; display: flex; flex-direction: column; }
#blocklist-modal h2 { margin: 0 0 16px; font-size: 20px; color: #333; }
#blocklist-modal-list { list-style: none; padding: 0; margin: 0; overflow-y: auto; flex-grow: 1; border: 1px solid #eee; border-radius: 8px; }
#blocklist-modal-list li { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border-bottom: 1px solid #eee; font-size: 15px; color: #444; word-break: break-all; }
#blocklist-modal-list li:last-child { border-bottom: none; }
.remove-block-btn { font-size: 13px; color: #0078d4; background: none; border: none; cursor: pointer; font-weight: bold; margin-left: 10px; flex-shrink: 0; }
.remove-block-btn:hover { text-decoration: underline; }
#blocklist-modal-close { margin-top: 20px; padding: 8px 16px; background-color: #0078d4; color: white; border: none; border-radius: 8px; cursor: pointer; align-self: flex-end; }
/* --- C & D 部分样式无需修改 --- */
body[data-host="msn"] { background: #F7F7F7 !important; }
body[data-host="msn"] .consumption-page-structure { display: flex !important; justify-content: center !important; grid-template-columns: 1fr !important; }
body[data-host="msn"] .consumption-page-gridarea_content { width: 90% !important; max-width: 1200px !important; }
body[data-host="msn"] div[data-t*="AboveRiverBlock"], body[data-host="msn"] views-native-ad, body[data-host="msn"] .feed-section[data-t*="River"], body[data-host="msn"] .ad-slot-placeholder, body[data-host="msn"] common-header, body[data-host="msn"] .consumption-page-gridarea_action, body[data-host="msn"] views-right-rail, body[data-host="msn"] .railBelow_c2, body[data-host="msn"] .consumption-page-feed-wrapper, body[data-host="msn"] ms-toast, body[data-host="msn"] .consumption-page-eocb, body[data-host="msn"] common-footer, body[data-host="msn"] #footer { display: none !important; visibility: hidden !important; }
/* [MODIFIED] 增强粒子效果 */
.sparkle-particle {
position: fixed;
width: 12px; /* 增大尺寸 */
height: 12px; /* 增大尺寸 */
border-radius: 50%;
pointer-events: none;
z-index: 99999;
opacity: 1;
/* 增长动画时间 */
transition: transform 1.5s cubic-bezier(0.17, 0.84, 0.44, 1), opacity 1.5s cubic-bezier(0.32, 0, 0.67, 0);
}
`;
GM_addStyle(styles);
// --- 2. JavaScript 逻辑部分 (已重构以提升性能和修复Bug) ---
const BLOCKLIST_KEY = 'userscript_site_blocklist_v2_prefixes';
const blocklistManager = {
_blocklist: null,
getBlocklist: function() {
if (this._blocklist === null) {
this._blocklist = JSON.parse(localStorage.getItem(BLOCKLIST_KEY) || '[]');
}
return this._blocklist;
},
saveBlocklist: function(list) {
const uniqueList = Array.from(new Set(list));
this._blocklist = uniqueList;
localStorage.setItem(BLOCKLIST_KEY, JSON.stringify(uniqueList));
},
addRule: function(rule) {
const list = this.getBlocklist();
const cleanRule = rule.replace(/^https?:\/\//, '').replace(/\/$/, '');
if (cleanRule && !list.includes(cleanRule)) {
list.push(cleanRule);
this.saveBlocklist(list);
}
},
removeRule: function(rule) {
let list = this.getBlocklist();
list = list.filter(item => item !== rule);
this.saveBlocklist(list);
}
};
// [OPTIMIZED & FIXED v2] Centralized function to manage element visibility
function applyBlocklistToElement(result, blocklist) {
const unwantedSelectors = '#brsv3, #inline_rs, .rqnaContainerwithfeedback, .lite-entcard-main';
const cite = result.querySelector('cite');
if (!cite) {
if (result.querySelector(unwantedSelectors)) {
result.style.setProperty('display', 'none', 'important');
}
return;
}
try {
// [FIX] Clean the cite text to get a valid URL, removing extra characters added by Bing.
const rawCiteText = cite.textContent;
const urlText = rawCiteText.split(' ')[0]; // Takes only the part before the first space.
const normalizedUrl = (urlText.startsWith('http') ? urlText : 'http://' + urlText)
.replace(/^https?:\/\//, '')
.replace(/^www\./, '');
const isBlockedByUser = blocklist.some(rule => normalizedUrl.startsWith(rule));
if (isBlockedByUser) {
result.style.setProperty('display', 'none', 'important');
} else {
const isUnwantedModule = result.querySelector(unwantedSelectors);
if (isUnwantedModule) {
result.style.setProperty('display', 'none', 'important');
} else {
result.style.removeProperty('display');
}
}
} catch (e) {
// Invalid URL, ignore this element
}
}
function applyBlocklist() {
const blocklist = blocklistManager.getBlocklist();
document.querySelectorAll('li.b_algo, li.b_ans').forEach(result => applyBlocklistToElement(result, blocklist));
}
// [FIXED] Injects button and handles creation of block rules
function injectBlockButtonToElement(result) {
if (result.dataset.blockBtnInjected || !result.matches('li.b_algo, li.b_ans:not(:has(.lite-entcard-main))')) return;
result.dataset.blockBtnInjected = 'true';
const cite = result.querySelector('cite');
if (!cite) return;
const btn = document.createElement('div');
btn.className = 'block-site-btn';
btn.title = `屏蔽此网站或路径...`;
btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" /></svg>`;
btn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
try {
// [FIX] Clean the cite text to get a valid URL before parsing.
const rawCiteText = cite.textContent;
const cleanUrlText = rawCiteText.split(' ')[0]; // Takes only the part before the first space.
const url = new URL(cleanUrlText.startsWith('http') ? cleanUrlText : 'http://' + cleanUrlText);
const hostname = url.hostname.replace(/^www\./, '');
const pathSegments = url.pathname.split('/').filter(Boolean);
let ruleToBlock = pathSegments.length > 0 ? `${hostname}/${pathSegments[0]}` : hostname;
if (confirm(`确定要屏蔽此前缀吗?\n\n${ruleToBlock}`)) {
blocklistManager.addRule(ruleToBlock);
applyBlocklist();
}
} catch (error) {
console.error("Error creating block rule:", error);
alert('无法从此链接生成屏蔽规则,链接可能格式不正确。');
}
});
result.appendChild(btn);
}
function injectBlockButtons() {
document.querySelectorAll('li.b_algo, li.b_ans').forEach(injectBlockButtonToElement);
}
// --- Modal and UI functions (Unchanged) ---
function createBlocklistModal() {
if (document.getElementById('blocklist-modal-overlay')) return;
const modalOverlay = document.createElement('div');
modalOverlay.id = 'blocklist-modal-overlay';
modalOverlay.innerHTML = `<div id="blocklist-modal"><h2>已屏蔽的规则</h2><ul id="blocklist-modal-list"></ul><button id="blocklist-modal-close">关闭</button></div>`;
document.body.appendChild(modalOverlay);
const modal = modalOverlay.querySelector('#blocklist-modal');
const closeBtn = modalOverlay.querySelector('#blocklist-modal-close');
modal.addEventListener('click', e => e.stopPropagation());
modalOverlay.addEventListener('click', () => modalOverlay.style.display = 'none');
closeBtn.addEventListener('click', () => modalOverlay.style.display = 'none');
}
function populateAndShowModal() {
const modalOverlay = document.getElementById('blocklist-modal-overlay');
const listElement = document.getElementById('blocklist-modal-list');
if (!modalOverlay || !listElement) return;
listElement.innerHTML = '';
const blocklist = blocklistManager.getBlocklist();
if (blocklist.length === 0) {
listElement.innerHTML = '<li>列表为空</li>';
} else {
blocklist.sort().forEach(rule => {
const li = document.createElement('li');
li.textContent = rule;
const removeBtn = document.createElement('button');
removeBtn.className = 'remove-block-btn';
removeBtn.textContent = '移除';
removeBtn.onclick = () => {
blocklistManager.removeRule(rule);
applyBlocklist();
populateAndShowModal();
};
li.appendChild(removeBtn);
listElement.appendChild(li);
});
}
modalOverlay.style.display = 'flex';
}
function createPowerSearchToolbar() {
let toolbar = document.getElementById('power-search-toolbar');
if (!toolbar) {
toolbar = document.createElement('div');
toolbar.id = 'power-search-toolbar';
const container = document.getElementById('b_tween');
if (!container) return;
container.after(toolbar);
}
toolbar.innerHTML = '';
toolbar.appendChild(createTimeFilterGroup());
toolbar.appendChild(document.createElement('div')).className = 'filter-divider';
const searchInput = document.getElementById('sb_form_q');
if (!searchInput) return;
const parsed = parseQuery(searchInput.value);
const modeGroup = createGroup('搜索模式:');
const modes = [
{ label: '智能排序', mode: 'smart', isDefault: true },
{ label: '精确匹配', mode: 'exact' },
{ label: '标题包含', mode: 'intitle' }
];
modes.forEach(modeInfo => {
const btn = createPowerButton(modeInfo.label, generateQuery({ ...parsed, mode: modeInfo.mode }));
if (parsed.mode === modeInfo.mode || (parsed.mode === 'smart' && modeInfo.isDefault)) {
btn.classList.add('active');
}
modeGroup.appendChild(btn);
});
toolbar.appendChild(modeGroup);
toolbar.appendChild(document.createElement('div')).className = 'filter-divider';
const filetypeGroup = createGroup(null);
['PDF', 'PPT', 'DOC', 'XLS'].forEach(type => {
const filetype = type.toLowerCase();
const btn = createPowerButton(type, generateQuery({ ...parsed, filetype }));
if (parsed.filetype === filetype) btn.classList.add('active');
filetypeGroup.appendChild(btn);
});
const siteGroup = createGroup(null);
[
{ name: '知乎', domain: 'zhihu.com' },
{ name: 'Bilibili', domain: 'bilibili.com' },
{ name: '少数派', domain: 'sspai.com' },
{ name: 'GitHub', domain: 'github.com' },
{ name: '维基百科', domain: 'wikipedia.org' },
{ name: '豆瓣', domain: 'douban.com' }
].forEach(site => {
const btn = createPowerButton(site.name, generateQuery({ ...parsed, site: site.domain }));
if (parsed.site === site.domain) btn.classList.add('active');
siteGroup.appendChild(btn);
});
const filterContainer = document.createElement('div');
filterContainer.style.display = 'flex';
filterContainer.style.flexDirection = 'column';
filterContainer.style.gap = '12px';
filterContainer.appendChild(filetypeGroup);
filterContainer.appendChild(siteGroup);
const filterWrapper = document.createElement('div');
filterWrapper.style.display = 'flex';
filterWrapper.style.alignItems = 'flex-start';
filterWrapper.appendChild(createGroup('筛选器:')).querySelector('.power-search-group-label').style.paddingTop = '5px';
filterWrapper.appendChild(filterContainer);
toolbar.appendChild(filterWrapper);
}
function createTimeFilterGroup() {
const group = createGroup('时间范围:');
const filters = [
{ label: '全部', param: '' },
{ label: '一天内', param: 'ez1' },
{ label: '一周内', param: 'ez2' },
{ label: '一月内', param: 'ez3' },
{ label: '一年内', param: 'ez4' }
];
const currentUrl = new URL(window.location.href);
const currentFilterParam = currentUrl.searchParams.get('filters');
filters.forEach(filter => {
const btn = document.createElement('a');
btn.className = 'power-search-btn';
btn.textContent = filter.label;
const targetUrl = new URL(window.location.href);
if (filter.param) {
targetUrl.searchParams.set('filters', `ex1:"${filter.param}"`);
} else {
targetUrl.searchParams.delete('filters');
}
btn.href = targetUrl.toString();
if (currentFilterParam === targetUrl.searchParams.get('filters')) {
btn.classList.add('active');
}
group.appendChild(btn);
});
return group;
}
function parseQuery(query) {
let base = query;
let mode = 'smart';
const siteMatch = base.match(/\s*site:(\S+)/i);
const site = siteMatch ? siteMatch[1] : null;
base = base.replace(/\s*site:\S+/ig, '');
const filetypeMatch = base.match(/\s*filetype:(\S+)/i);
const filetype = filetypeMatch ? filetypeMatch[1] : null;
base = base.replace(/\s*filetype:\S+/ig, '');
if (base.trim().startsWith('intitle:')) {
mode = 'intitle';
base = base.replace(/^intitle:/i, '');
} else if (base.trim().startsWith('"') && base.trim().endsWith('"')) {
mode = 'exact';
base = base.replace(/^"|"$/g, '');
}
return { base: base.trim(), mode, filetype, site };
}
function generateQuery(parts) {
let query = parts.base;
switch (parts.mode) {
case 'exact': query = `"${parts.base}"`; break;
case 'intitle': query = `intitle:${parts.base}`; break;
}
if (parts.filetype) query += ` filetype:${parts.filetype}`;
if (parts.site) query += ` site:${parts.site}`;
return query.trim();
}
function createPowerButton(label, query) {
const btn = document.createElement('a');
btn.className = 'power-search-btn';
btn.textContent = label;
btn.href = `/search?q=${encodeURIComponent(query)}`;
return btn;
}
function createGroup(labelText) {
const group = document.createElement('div');
group.className = 'power-search-group';
if (labelText) {
const label = document.createElement('span');
label.className = 'power-search-group-label';
label.textContent = labelText;
group.appendChild(label);
}
return group;
}
function handleBingInfiniteScroll(resultsContainer) {
if (!resultsContainer) return;
let isLoading = false;
const getNextPageUrl = () => document.querySelector('#b_pag a.sb_pagN')?.href;
const loader = document.createElement('div');
loader.id = 'infinite-scroll-loader';
resultsContainer.insertAdjacentElement('afterend', loader);
const observer = new IntersectionObserver(async (entries) => {
if (!entries?.[0]?.isIntersecting || isLoading) return;
isLoading = true;
const nextPageUrl = getNextPageUrl();
if (!nextPageUrl) {
observer.disconnect();
loader.textContent = '已加载全部结果';
return;
}
loader.textContent = '正在加载更多结果...';
try {
const response = await fetch(nextPageUrl);
const html = await response.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
doc.querySelectorAll('#b_results > li').forEach(result => resultsContainer.appendChild(result));
const oldPagination = document.getElementById('b_pag');
if (oldPagination) oldPagination.innerHTML = doc.getElementById('b_pag')?.innerHTML || '';
} catch (error) {
loader.textContent = '加载失败';
console.error('Infinite scroll failed:', error);
} finally {
isLoading = false;
if (loader.textContent === '正在加载更多结果...') loader.textContent = '';
}
}, { threshold: 1.0 });
observer.observe(loader);
}
function injectUI() {
if (document.getElementById('userscript-ui-container')) return;
const container = document.createElement('div');
container.id = 'userscript-ui-container';
container.innerHTML = `<div id="manage-blocklist-btn" class="userscript-btn" title="管理屏蔽列表"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path d="M10 12.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5z" /><path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.022 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd" /></svg></div><div id="go-to-top-btn" class="userscript-btn" title="返回顶部"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M11.47 2.47a.75.75 0 011.06 0l7.5 7.5a.75.75 0 11-1.06 1.06l-6.22-6.22V21a.75.75 0 01-1.5 0V4.81l-6.22 6.22a.75.75 0 11-1.06-1.06l7.5-7.5z" clip-rule="evenodd" /></svg></div>`;
document.body.appendChild(container);
createBlocklistModal();
document.getElementById('manage-blocklist-btn').addEventListener('click', populateAndShowModal);
}
function handleGoToTop() {
const btn = document.getElementById('go-to-top-btn');
if (!btn) return;
window.addEventListener('scroll', () => {
btn.classList.toggle('visible', window.scrollY > 100);
});
btn.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
}
function addParticleClickEffect() {
const colors = ['#FF69B4', '#1E90FF', '#00CED1', '#FFD700', '#7CFC00', '#BA55D3', '#FF4500'];
document.body.addEventListener('click', function(e) {
if (e.target.closest('a, button, #go-to-top-btn, .block-site-btn, #manage-blocklist-btn, #blocklist-modal')) return;
for (let i = 0; i < 25; i++) {
const particle = document.createElement('div');
particle.className = 'sparkle-particle';
document.body.appendChild(particle);
particle.style.left = `${e.clientX}px`;
particle.style.top = `${e.clientY}px`;
particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
const angle = Math.random() * Math.PI * 2;
const distance = Math.random() * 150 + 80;
requestAnimationFrame(() => {
particle.style.transform = `translate(${Math.cos(angle) * distance}px, ${Math.sin(angle) * distance}px) scale(0)`;
particle.style.opacity = '0';
});
setTimeout(() => particle.remove(), 1500);
}
});
}
// --- [MODIFIED] 初始化逻辑,采用持续观察模式以增强稳定性 ---
function initialize() {
const hostname = window.location.hostname;
if (hostname.includes('bing.com')) { document.body.dataset.host = 'bing'; }
else if (hostname.includes('msn.cn')) { document.body.dataset.host = 'msn'; }
injectUI();
handleGoToTop();
addParticleClickEffect();
if (hostname.includes('bing.com')) {
const runInitializationTasks = (container) => {
if (container.dataset.initialized) return;
container.dataset.initialized = 'true';
applyBlocklist();
injectBlockButtons();
createPowerSearchToolbar();
handleBingInfiniteScroll(container);
const resultsObserver = new MutationObserver((mutationsList) => {
const blocklist = blocklistManager.getBlocklist();
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.matches('li.b_algo, li.b_ans')) {
applyBlocklistToElement(node, blocklist);
injectBlockButtonToElement(node);
}
node.querySelectorAll('li.b_algo, li.b_ans').forEach(result => {
applyBlocklistToElement(result, blocklist);
injectBlockButtonToElement(result);
});
}
});
}
}
});
resultsObserver.observe(container, { childList: true, subtree: true });
};
const mainObserver = new MutationObserver(() => {
const resultsContainer = document.getElementById('b_results');
if (resultsContainer) {
runInitializationTasks(resultsContainer);
}
});
mainObserver.observe(document.body, { childList: true, subtree: true });
const initialResultsContainer = document.getElementById('b_results');
if (initialResultsContainer) {
runInitializationTasks(initialResultsContainer);
}
}
}
// --- 启动脚本 ---
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
})();