在搜索结果页右侧添加 网站 / 文件类型过滤,支持记忆与高亮
// ==UserScript==
// @name 搜索引擎增强,支持bing,google,帮助过滤搜索结果
// @namespace https://github.com/yourname/bing-site-filetype
// @version 2.0
// @description 在搜索结果页右侧添加 网站 / 文件类型过滤,支持记忆与高亮
// @author timor
// @match *://www.bing.com/search*
// @match *://www.google.com/search*
// @grant none
// @run-at document-end
// @license MIT
// ==/UserScript==
(function () {
'use strict';
console.log('✅ Bing 互斥按钮脚本已运行');
/* --------------------------
1. 配置:按钮定义
-------------------------- */
const CONFIG = [
// site 组
{ label: 'Github', suffix: ' site:github.com', group: 'site' },
{ label: 'StackOverFlow', suffix: ' site:stackoverflow.com', group: 'site' },
{ label: '知乎', suffix: ' site:zhihu.com', group: 'site' },
{ label: 'B站', suffix: ' site:bilibili.com', group: 'site' },
{ label: '52pojie', suffix: ' site:52pojie.cn', group: 'site' },
{ label: '贴吧', suffix: ' site:tieba.baidu.com', group: 'site' },
{ label: 'V2EX', suffix: ' site:v2ex.com', group: 'site' },
{ label: '6Park', suffix: ' site:6park.com', group: 'site' },
{ label: '佳礼', suffix: ' site:cari.com.my', group: 'site' },
{ label: '清除网站筛选', suffix: '', group: 'site', clear: true },
// filetype 组
{ label: 'PDF', suffix: ' filetype:pdf', group: 'filetype' },
{ label: 'PPT', suffix: ' filetype:ppt', group: 'filetype' },
{ label: '清除文件筛选', suffix: '', group: 'filetype', clear: true }
];
const GROUPS = [...new Set(CONFIG.map(b => b.group))];
/* --------------------------
2. 工具函数
-------------------------- */
function getKW() {
return new URLSearchParams(location.search).get('q') || '';
}
function buildURL(kw) {
kw = kw.trim();
const encoded = encodeURIComponent(kw);
const host = location.hostname;
console.log(host)
if (host.includes('bing.com')) {
return 'https://www.bing.com/search?q=' + encoded;
} else if (host.includes('google')) {
return 'https://www.google.com/search?q=' + encoded;
} else if (host.includes('duckduckgo')) {
return 'https://duckduckgo.com/?q=' + encoded;
} else if (host.includes('baidu.com')) {
return 'https://www.baidu.com/s?wd=' + encoded;
} else {
// 默认回退到 Bing
return 'https://www.bing.com/search?q=' + encoded;
}
}
// 安全删除同组后缀(更稳健的正则)
function stripGroup(kw, group) {
const list = CONFIG.filter(b => b.group === group).map(b => b.suffix.trim());
for (const s of list) {
if (!s) continue;
const re = new RegExp(`\\s*${s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')}\\b`, 'gi');
kw = kw.replace(re, '');
}
return kw.trim();
}
/* --------------------------
3. 持久化状态管理
-------------------------- */
const map = JSON.parse(localStorage.getItem('bing_suffix_map') || '{}');
function saveMap() {
localStorage.setItem('bing_suffix_map', JSON.stringify(map));
}
function syncMapOnLoad() {
const raw = getKW();
for (const g of GROUPS) {
const hit = CONFIG.filter(b => b.group === g && b.suffix)
.find(b => raw.includes(b.suffix.trim()));
if (hit) map[g] = hit.suffix;
}
saveMap();
}
/* --------------------------
4. 逻辑:应用后缀
-------------------------- */
function applySuffix(group, newSuffix) {
let raw = getKW();
raw = stripGroup(raw, group);
if (newSuffix) raw += newSuffix;
map[group] = newSuffix || '';
saveMap();
location.href = buildURL(raw);
}
/* --------------------------
5. 渲染 UI 面板
-------------------------- */
function renderBar() {
const old = document.getElementById('site-filetype-bar');
if (old) old.remove();
const bar = document.createElement('div');
bar.id = 'site-filetype-bar';
Object.assign(bar.style, {
position: 'fixed',
right: '16px',
top: '120px',
width: '180px',
background: 'rgba(255,255,255,0.9)',
backdropFilter: 'blur(6px)',
border: '1px solid #ddd',
borderRadius: '8px',
padding: '8px',
display: 'flex',
flexDirection: 'column',
gap: '6px',
zIndex: 999
});
GROUPS.forEach(group => {
const title = document.createElement('div');
title.textContent = group === 'site' ? '🌐 站点筛选' : '📄 文件类型';
title.style.cssText = 'font-weight:bold;color:#333;margin-top:4px';
bar.appendChild(title);
CONFIG.filter(b => b.group === group).forEach(btn => {
const el = document.createElement('button');
el.textContent = btn.label;
el.className = 'b_searchbtn';
Object.assign(el.style, {
padding: '6px 10px',
fontSize: '13px',
borderRadius: '4px',
border: '1px solid #ddd',
background: map[group] === btn.suffix && !btn.clear ? '#0078d4' : '#fff',
color: map[group] === btn.suffix && !btn.clear ? '#fff' : '#333',
cursor: 'pointer'
});
el.onmouseenter = () => el.style.borderColor = '#0078d4';
el.onmouseleave = () => el.style.borderColor = '#ddd';
el.onclick = () => applySuffix(btn.group, btn.suffix);
bar.appendChild(el);
});
});
// 允许拖动
let startY, startTop;
bar.addEventListener('mousedown', e => {
if (e.target.tagName === 'BUTTON') return;
startY = e.clientY;
startTop = parseInt(bar.style.top);
const move = e2 => {
bar.style.top = `${startTop + e2.clientY - startY}px`;
};
document.addEventListener('mousemove', move);
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', move);
}, { once: true });
});
document.body.appendChild(bar);
}
/* --------------------------
6. 监听 URL 变化(SPA)
-------------------------- */
(function (history) {
const pushState = history.pushState;
history.pushState = function () {
const ret = pushState.apply(this, arguments);
window.dispatchEvent(new Event('urlchange'));
return ret;
};
})(window.history);
window.addEventListener('urlchange', () => { syncMapOnLoad(); renderBar(); });
window.addEventListener('popstate', () => { syncMapOnLoad(); renderBar(); });
/* --------------------------
初始化
-------------------------- */
window.addEventListener('load', () => {
setTimeout(() => {
syncMapOnLoad();
renderBar();
}, 400);
});
})();