// ==UserScript==
// @name 聚合搜索 - 增强版(支持暗色模式)
// @description 整合百度、必应、谷歌搜索,支持无刷新自动翻页,根据系统主题自动切换暗色模式
// @version 0.5.0
// @author yinbao77
// @website https://github.com/yinbao77
// @match *://www.baidu.com/s*
// @match *://www.bing.com/search*
// @match *://cn.bing.com/search*
// @match *://www.google.com.hk/search*
// @match *://www.google.com/search*
// @namespace https://gf.qytechs.cn/users/1489016
// ==/UserScript==
// 主要搜索引擎配置
const mainSearchEngines = [
{
name: '百度',
searchUrl: 'https://www.baidu.com/s?wd=',
keyName: 'wd',
testUrl: /https:\/\/www\.baidu\.com\/s.*/,
nextPageSelector: '#page .n',
resultsSelector: '#content_left',
pageParamName: 'pn'
},
{
name: '必应',
searchUrl: 'https://www.bing.com/search?q=',
keyName: 'q',
testUrl: /https:\/\/(www|cn)\.bing\.com\/search.*/,
nextPageSelector: '.sb_pagN',
resultsSelector: '#b_results',
pageParamName: 'first'
},
{
name: 'Google',
searchUrl: 'https://www.google.com/search?q=',
keyName: 'q',
testUrl: /https:\/\/www\.google\.com(\.hk)?\/search.*/,
nextPageSelector: '#pnnext',
resultsSelector: '#search',
pageParamName: 'start'
}
];
// 专业搜索配置(已移除)
// 暗色模式相关变量
let isDarkMode = false;
// 主题配置
const themes = {
light: {
background: '#EEEEEE',
border: '#ccc',
shadow: 'rgba(0, 0, 0, 0.1)',
textPrimary: '#333',
textSecondary: '#666',
textMuted: '#999',
activeBackground: '#4CAF50',
hoverBackground: '#f5f5f5',
separatorBorder: '#ddd',
specialBorder: '#eee',
specialText: '#666',
specialHover: '#f0f0f0',
specialHoverText: '#333',
toggleOnBackground: '#e8f5e8',
toggleOffBackground: '#f5f5f5',
pageIndicatorBackground: 'linear-gradient(90deg, #f0f0f0, #e0e0e0, #f0f0f0)',
loadingBackground: 'rgba(0, 0, 0, 0.8)',
loadingText: 'white'
},
dark: {
background: '#2d2d2d',
border: '#555',
shadow: 'rgba(0, 0, 0, 0.3)',
textPrimary: '#e0e0e0',
textSecondary: '#b0b0b0',
textMuted: '#888',
activeBackground: '#4CAF50',
hoverBackground: '#3a3a3a',
separatorBorder: '#444',
specialBorder: '#3a3a3a',
specialText: '#a0a0a0',
specialHover: '#3a3a3a',
specialHoverText: '#e0e0e0',
toggleOnBackground: '#2d4a2d',
toggleOffBackground: '#3a3a3a',
pageIndicatorBackground: 'linear-gradient(90deg, #3a3a3a, #2d2d2d, #3a3a3a)',
loadingBackground: 'rgba(0, 0, 0, 0.9)',
loadingText: 'white'
}
};
// 检测系统主题
function detectSystemTheme() {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light';
}
// 获取当前主题配置
function getCurrentTheme() {
return isDarkMode ? themes.dark : themes.light;
}
// 监听系统主题变化
function initThemeDetection() {
// 初始化主题
isDarkMode = detectSystemTheme() === 'dark';
// 监听主题变化
if (window.matchMedia) {
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkModeQuery.addEventListener('change', (e) => {
isDarkMode = e.matches;
updateTheme();
});
}
}
// 更新主题样式
function updateTheme() {
const box = document.getElementById('search-app-box');
if (box) {
// 重新应用主题样式
applyThemeToBox(box);
}
// 更新加载指示器样式
const loadingIndicator = document.getElementById('loading-indicator');
if (loadingIndicator) {
const theme = getCurrentTheme();
loadingIndicator.style.background = theme.loadingBackground;
loadingIndicator.style.color = theme.loadingText;
}
// 更新页面分隔符样式
const pageIndicators = document.querySelectorAll('[data-page-indicator]');
pageIndicators.forEach(indicator => {
const theme = getCurrentTheme();
indicator.style.background = theme.pageIndicatorBackground;
indicator.style.color = theme.textSecondary;
});
}
// 为搜索框应用主题
function applyThemeToBox(box) {
const theme = getCurrentTheme();
// 主容器样式
box.style.backgroundColor = theme.background;
box.style.borderColor = theme.border;
box.style.boxShadow = `0 4px 8px ${theme.shadow}`;
// 更新所有子元素的样式
const author = box.querySelector('[data-author]');
if (author) {
author.style.color = theme.textSecondary;
}
const title = box.querySelector('[data-title]');
if (title) {
title.style.color = theme.textPrimary;
}
// 更新主要搜索引擎链接样式
const mainLinks = box.querySelectorAll('[data-main-link]');
mainLinks.forEach(link => {
if (link.dataset.active === 'true') {
link.style.backgroundColor = theme.activeBackground;
link.style.color = '#fff';
} else {
link.style.color = theme.textPrimary;
link.onmouseover = function() {
this.style.backgroundColor = theme.hoverBackground;
};
link.onmouseout = function() {
this.style.backgroundColor = '';
};
}
link.style.borderTopColor = theme.separatorBorder;
});
// 更新自动翻页开关样式
const autoPageToggle = box.querySelector('[data-auto-page-toggle]');
if (autoPageToggle) {
autoPageToggle.style.borderTopColor = theme.separatorBorder;
autoPageToggle.style.color = theme.textPrimary;
const isEnabled = autoPageToggle.dataset.enabled === 'true';
autoPageToggle.style.backgroundColor = isEnabled ? theme.toggleOnBackground : theme.toggleOffBackground;
}
}
// 增强的URL参数获取函数 - 支持特殊字符
function getQueryVariable(variable) {
try {
const urlParams = new URLSearchParams(window.location.search);
const value = urlParams.get(variable);
return value ? decodeURIComponent(value) : '';
} catch (e) {
// 如果URLSearchParams失败,使用传统方法
const query = window.location.search.substring(1);
const vars = query.split('&');
for (let i = 0; i < vars.length; i++) {
const pair = vars[i].split('=');
if (pair[0] === variable) {
try {
return decodeURIComponent(pair[1] || '');
} catch (e) {
return pair[1] || '';
}
}
}
return '';
}
}
// 从URL中获取搜索关键词
function getKeywords() {
const currentUrl = window.location.href;
const engine = mainSearchEngines.find(item => item.testUrl.test(currentUrl));
if (engine) {
const keywords = getQueryVariable(engine.keyName);
return keywords.trim();
}
return '';
}
// 获取当前搜索引擎
function getCurrentEngine() {
const currentUrl = window.location.href;
return mainSearchEngines.find(item => item.testUrl.test(currentUrl));
}
// 安全的URL编码函数
function safeEncodeURIComponent(str) {
try {
return encodeURIComponent(str);
} catch (e) {
// 如果编码失败,尝试替换特殊字符
return str.replace(/[^\w\s\u4e00-\u9fff]/g, function(char) {
try {
return encodeURIComponent(char);
} catch (e) {
return char;
}
});
}
}
// 自动翻页相关变量
let isAutoPageEnabled = true;
let isLoading = false;
let currentPage = 1;
let maxPages = 10; // 最多自动翻10页,防止无限加载
// 获取下一页URL
function getNextPageUrl() {
const currentEngine = getCurrentEngine();
if (!currentEngine) return null;
const currentUrl = new URL(window.location.href);
const params = currentUrl.searchParams;
let nextPageValue;
if (currentEngine.name === '百度') {
// 百度: pn参数,每页10个结果
const currentPn = parseInt(params.get('pn') || '0');
nextPageValue = currentPn + 10;
params.set('pn', nextPageValue.toString());
} else if (currentEngine.name === '必应') {
// 必应: first参数,每页约10个结果
const currentFirst = parseInt(params.get('first') || '1');
nextPageValue = currentFirst + 10;
params.set('first', nextPageValue.toString());
} else if (currentEngine.name === 'Google') {
// Google: start参数,每页10个结果
const currentStart = parseInt(params.get('start') || '0');
nextPageValue = currentStart + 10;
params.set('start', nextPageValue.toString());
}
return currentUrl.toString();
}
// 加载下一页内容
async function loadNextPage() {
if (isLoading || currentPage >= maxPages) return;
const nextUrl = getNextPageUrl();
if (!nextUrl) return;
isLoading = true;
currentPage++;
// 显示加载状态
showLoadingIndicator();
try {
const response = await fetch(nextUrl);
const html = await response.text();
// 创建临时DOM来解析HTML
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const currentEngine = getCurrentEngine();
const newResults = doc.querySelector(currentEngine.resultsSelector);
if (newResults) {
// 获取新结果的子元素
const newItems = Array.from(newResults.children);
const currentResults = document.querySelector(currentEngine.resultsSelector);
if (currentResults && newItems.length > 0) {
// 添加分页标识
const pageIndicator = document.createElement('div');
const theme = getCurrentTheme();
pageIndicator.setAttribute('data-page-indicator', 'true');
pageIndicator.style = `
margin: 20px 0;
padding: 10px;
background: ${theme.pageIndicatorBackground};
text-align: center;
font-size: 14px;
color: ${theme.textSecondary};
border-radius: 5px;
position: relative;
`;
pageIndicator.innerHTML = `━━━ 第 ${currentPage} 页 ━━━`;
currentResults.appendChild(pageIndicator);
// 添加新结果
newItems.forEach(item => {
// 跳过分页导航等无关元素
if (item.classList.contains('result') ||
item.classList.contains('b_algo') ||
item.classList.contains('g') ||
item.id.includes('result') ||
!item.classList.contains('page')) {
currentResults.appendChild(item);
}
});
// 更新URL历史记录(不刷新页面)
history.pushState({page: currentPage}, '', nextUrl);
console.log(`已加载第 ${currentPage} 页内容`);
}
}
} catch (error) {
console.error('加载下一页失败:', error);
} finally {
isLoading = false;
hideLoadingIndicator();
}
}
// 显示加载指示器
function showLoadingIndicator() {
let indicator = document.getElementById('loading-indicator');
const theme = getCurrentTheme();
if (!indicator) {
indicator = document.createElement('div');
indicator.id = 'loading-indicator';
indicator.style = `
position: fixed;
bottom: 50px;
left: 50%;
transform: translateX(-50%);
background: ${theme.loadingBackground};
color: ${theme.loadingText};
padding: 10px 20px;
border-radius: 20px;
font-size: 14px;
z-index: 10000;
animation: fadeIn 0.3s ease-in-out;
`;
indicator.innerHTML = '🔄 大宝子疯狂加载下一页...';
document.body.appendChild(indicator);
// 添加CSS动画
if (!document.getElementById('loading-animation-style')) {
const style = document.createElement('style');
style.id = 'loading-animation-style';
style.textContent = `
@keyframes fadeIn {
from { opacity: 0; transform: translateX(-50%) translateY(10px); }
to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
@keyframes fadeOut {
from { opacity: 1; transform: translateX(-50%) translateY(0); }
to { opacity: 0; transform: translateX(-50%) translateY(-10px); }
}
`;
document.head.appendChild(style);
}
} else {
// 更新已存在的指示器颜色
indicator.style.background = theme.loadingBackground;
indicator.style.color = theme.loadingText;
}
indicator.style.display = 'block';
}
// 隐藏加载指示器
function hideLoadingIndicator() {
const indicator = document.getElementById('loading-indicator');
if (indicator) {
indicator.style.animation = 'fadeOut 0.3s ease-in-out';
setTimeout(() => {
indicator.style.display = 'none';
}, 300);
}
}
// 检测是否滚动到底部
function isScrolledToBottom() {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight;
// 提前200px开始加载
return scrollTop + windowHeight >= documentHeight - 200;
}
// 滚动事件监听
function initAutoPageScroll() {
let scrollTimer = null;
window.addEventListener('scroll', () => {
if (scrollTimer) clearTimeout(scrollTimer);
scrollTimer = setTimeout(() => {
if (isAutoPageEnabled && isScrolledToBottom() && !isLoading) {
loadNextPage();
}
}, 100); // 防抖,避免频繁触发
});
}
function jumpToSearch(targetUrl) {
const keywords = getKeywords();
if (!keywords) {
alert('未检测到搜索关键词');
return;
}
const encodedKeywords = safeEncodeURIComponent(keywords);
window.open(targetUrl + encodedKeywords, '_blank');
}
// 添加搜索框
function addBox() {
const currentEngine = getCurrentEngine();
const theme = getCurrentTheme();
const div = document.createElement('div');
div.id = 'search-app-box';
div.style = `
position: fixed;
top: 50%;
left: 20px;
transform: translateY(-50%);
width: 120px;
background-color: ${theme.background};
font-size: 12px;
z-index: 99999;
border: 1px solid ${theme.border};
border-radius: 8px;
box-shadow: 0 4px 8px ${theme.shadow};
padding-bottom: 10px;
font-family: 'Microsoft YaHei', Arial, sans-serif;
transition: background-color 0.3s ease, border-color 0.3s ease;
`;
document.body.appendChild(div);
// 作者信息
const author = document.createElement('div');
author.setAttribute('data-author', 'true');
author.innerText = "by 丶恩嗯";
author.style = `text-align: center; font-size: 12px; color: ${theme.textSecondary}; margin: 8px 0; transition: color 0.3s ease;`;
div.appendChild(author);
// 标题
const title = document.createElement('span');
title.setAttribute('data-title', 'true');
title.innerText = "🔍聚合搜索";
title.style = `display: block; text-align: center; margin: 10px 0; font-size: 14px; font-weight: bold; color: ${theme.textPrimary}; transition: color 0.3s ease;`;
div.appendChild(title);
// 主要搜索引擎列表
mainSearchEngines.forEach(engine => {
const a = document.createElement('a');
a.href = 'javascript:;';
a.innerText = engine.name;
a.setAttribute('data-main-link', 'true');
// 当前搜索引擎高亮显示
if (currentEngine && currentEngine.name === engine.name) {
a.setAttribute('data-active', 'true');
a.style = `
display: block;
padding: 8px 0;
text-align: center;
color: #fff;
text-decoration: none;
border-top: 1px solid ${theme.separatorBorder};
background-color: ${theme.activeBackground};
font-weight: bold;
transition: all 0.3s ease;
`;
} else {
a.setAttribute('data-active', 'false');
a.style = `
display: block;
padding: 8px 0;
text-align: center;
color: ${theme.textPrimary};
text-decoration: none;
border-top: 1px solid ${theme.separatorBorder};
transition: all 0.3s ease;
`;
// 添加悬停效果
a.onmouseover = function() {
this.style.backgroundColor = theme.hoverBackground;
};
a.onmouseout = function() {
this.style.backgroundColor = '';
};
}
a.onclick = () => {
if (currentEngine && currentEngine.name === engine.name) {
return; // 当前引擎不需要跳转
}
jumpToSearch(engine.searchUrl);
};
div.appendChild(a);
});
// 自动翻页开关
const autoPageToggle = document.createElement('div');
autoPageToggle.setAttribute('data-auto-page-toggle', 'true');
autoPageToggle.setAttribute('data-enabled', isAutoPageEnabled.toString());
autoPageToggle.style = `
border-top: 1px solid ${theme.separatorBorder};
margin: 5px 0;
text-align: center;
font-size: 10px;
color: ${theme.textPrimary};
padding: 8px 0;
cursor: pointer;
background-color: ${isAutoPageEnabled ? theme.toggleOnBackground : theme.toggleOffBackground};
transition: all 0.3s ease;
`;
autoPageToggle.innerHTML = `🔄 自动翻页: ${isAutoPageEnabled ? 'ON' : 'OFF'}`;
autoPageToggle.onclick = () => {
isAutoPageEnabled = !isAutoPageEnabled;
autoPageToggle.setAttribute('data-enabled', isAutoPageEnabled.toString());
autoPageToggle.innerHTML = `🔄 自动翻页: ${isAutoPageEnabled ? 'ON' : 'OFF'}`;
autoPageToggle.style.backgroundColor = isAutoPageEnabled ? theme.toggleOnBackground : theme.toggleOffBackground;
// 保存设置到localStorage
try {
localStorage.setItem('autoPageEnabled', isAutoPageEnabled.toString());
} catch (e) {
// 忽略localStorage错误
}
};
div.appendChild(autoPageToggle);
// 添加拖拽功能
let isDragging = false;
let startX, startY, initialX, initialY;
div.onmousedown = function(e) {
if (e.target === div || e.target === title || e.target === author) {
isDragging = true;
startX = e.clientX;
startY = e.clientY;
const rect = div.getBoundingClientRect();
initialX = rect.left;
initialY = rect.top;
div.style.cursor = 'move';
e.preventDefault();
}
};
document.onmousemove = function(e) {
if (isDragging) {
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
div.style.left = (initialX + deltaX) + 'px';
div.style.top = (initialY + deltaY) + 'px';
}
};
document.onmouseup = function() {
if (isDragging) {
isDragging = false;
div.style.cursor = '';
}
};
}
// 检测页面是否为搜索结果页
function isSearchPage() {
return mainSearchEngines.some(engine => engine.testUrl.test(window.location.href));
}
// 主函数
(function() {
'use strict';
if (!isSearchPage()) {
return;
}
// 初始化主题检测
initThemeDetection();
// 从localStorage读取自动翻页设置
try {
const saved = localStorage.getItem('autoPageEnabled');
if (saved !== null) {
isAutoPageEnabled = saved === 'true';
}
} catch (e) {
// 忽略localStorage错误
}
// 延迟执行,确保页面加载完成
setTimeout(() => {
// 检查是否已存在搜索框,避免重复添加
if (!document.getElementById('search-app-box')) {
addBox();
}
// 初始化自动翻页功能
initAutoPageScroll();
console.log('聚合搜索增强版已加载(支持暗色模式)');
console.log('自动翻页功能已启用,滚动到底部将自动加载下一页');
console.log(`当前主题模式: ${isDarkMode ? '暗色' : '亮色'}`);
}, 1000);
})();