// ==UserScript==
// @name Google Search Sidebar
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Add a sidebar on Google Search for easier information filtering
// @author Kamiya Minoru
// @icon https://www.google.com/favicon.ico
// @match https://www.google.com/*
// @match https://www.google.com.tw/*
// @match https://www.google.co.jp/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const userLang = navigator.language || navigator.userLanguage;
const i18n = {
'zh-TW': {
languageSection: '語言過濾',
timeSection: '時間過濾',
advancedSearch: '進階搜尋',
languages: {
any: '不限語言搜尋',
zhTW: '以繁體中文搜尋',
zh: '以中文搜尋',
ja: '以日文搜尋',
en: '以英文搜尋'
},
times: {
any: '不限時間',
hour: '過去1小時',
day: '過去24小時',
week: '過去7天',
month: '過去1個月',
months3: '過去3個月',
year: '過去1年'
}
},
'ja': {
languageSection: '言語フィルター',
timeSection: '期間フィルター',
advancedSearch: '検索オプション',
languages: {
any: '言語指定なし',
zhTW: '繁体中国語で検索',
zh: '中国語で検索',
ja: '日本語で検索',
en: '英語で検索'
},
times: {
any: '期間指定なし',
hour: '1時間以内',
day: '24時間以内',
week: '1週間以内',
month: '1か月以内',
months3: '3か月以内',
year: '1年以内'
}
},
'en': {
languageSection: 'Language Filter',
timeSection: 'Time Filter',
advancedSearch: 'Advanced Search',
languages: {
any: 'Any Language',
zhTW: 'Traditional Chinese Only',
zh: 'Chinese Only',
ja: 'Japanese Only',
en: 'English Only'
},
times: {
any: 'Any Time',
hour: 'Past Hour',
day: 'Past 24 Hours',
week: 'Past Week',
month: 'Past Month',
months3: 'Past 3 Months',
year: 'Past Year'
}
}
};
function getLocale() {
if (i18n[userLang]) {
return i18n[userLang];
}
const primaryLang = userLang.split('-')[0];
if (i18n[primaryLang]) {
return i18n[primaryLang];
}
return i18n['en'];
}
const locale = getLocale();
const languageFilters = [
{ text: locale.languages.any, param: '' },
{ text: locale.languages.zhTW, param: '&lr=lang_zh-TW' },
{ text: locale.languages.zh, param: '&lr=lang_zh' },
{ text: locale.languages.ja, param: '&lr=lang_ja' },
{ text: locale.languages.en, param: '&lr=lang_en' }
];
const timeFilters = [
{ text: locale.times.any, param: '' },
{ text: locale.times.hour, param: '&tbs=qdr:h' },
{ text: locale.times.day, param: '&tbs=qdr:d' },
{ text: locale.times.week, param: '&tbs=qdr:w' },
{ text: locale.times.month, param: '&tbs=qdr:m' },
{ text: locale.times.months3, param: '&tbs=qdr:m3' },
{ text: locale.times.year, param: '&tbs=qdr:y' }
];
function createAdvancedSearchLink() {
const link = document.createElement('a');
link.textContent = locale.advancedSearch;
link.href = getAdvancedSearchUrl();
link.style.cssText = `
display: block;
color: #1a73e8;
text-decoration: none;
font-size: 13px;
padding: 8px;
margin-top: 10px;
background: #f8f9fa;
border-radius: 8px;
text-align: center;
transition: background-color 0.2s;
`;
link.addEventListener('mouseover', () => {
link.style.backgroundColor = '#e8f0fe';
});
link.addEventListener('mouseout', () => {
link.style.backgroundColor = '#f8f9fa';
});
return link;
}
function createFilterSection(title, filters, collapsible = false, defaultExpanded = false) {
const section = document.createElement('div');
section.style.cssText = `
margin: 10px 0;
padding: 8px;
background: #f8f9fa;
border-radius: 8px;
`;
const titleElement = document.createElement('h3');
titleElement.textContent = title;
titleElement.style.cssText = `
margin: 0 0 8px 0;
font-size: 13px;
color: #202124;
font-weight: 500;
cursor: ${collapsible ? 'pointer' : 'default'};
`;
section.appendChild(titleElement);
const linkContainer = document.createElement('div');
linkContainer.style.cssText = `
display: flex;
flex-direction: column;
gap: 6px;
${collapsible && !defaultExpanded ? 'display: none;' : ''}
`;
filters.forEach(filter => {
const link = document.createElement('a');
link.textContent = filter.text;
link.href = getCurrentUrlWithParam(filter.param);
link.style.cssText = `
color: #888888; // 非高亮文字顏色
text-decoration: none;
font-size: 12px;
padding: 3px 6px;
border-radius: 4px;
transition: background-color 0.2s, color 0.2s;
`;
const urlParams = new URL(window.location.href).searchParams;
if (urlParams.get('lr') === filter.param.replace('&lr=', '') ||
urlParams.get('tbs') === filter.param.replace('&tbs=', '')) {
link.style.color = '#1a73e8'; // 高亮文字顏色
link.style.fontWeight = 'bold';
}
link.addEventListener('mouseover', () => {
link.style.backgroundColor = '#e8f0fe';
});
link.addEventListener('mouseout', () => {
if (urlParams.get('lr') !== filter.param.replace('&lr=', '') &&
urlParams.get('tbs') !== filter.param.replace('&tbs=', '')) {
link.style.backgroundColor = 'transparent';
}
});
linkContainer.appendChild(link);
});
section.appendChild(linkContainer);
if (collapsible) {
titleElement.addEventListener('click', () => {
linkContainer.style.display = linkContainer.style.display === 'none' ? 'flex' : 'none';
});
}
return section;
}
function getCurrentUrlWithParam(param) {
const url = new URL(window.location.href);
// Remove existing language or time parameters
url.searchParams.delete('lr');
url.searchParams.delete('tbs');
// Add new parameters
if (param) {
const cleanParam = param.startsWith('&') ? param.substring(1) : param;
const [key, value] = cleanParam.split('=');
url.searchParams.set(key, value);
}
return url.toString();
}
function getAdvancedSearchUrl() {
const url = new URL('https://www.google.com/advanced_search');
const currentParams = new URLSearchParams(window.location.search);
// Append current search parameters to the advanced search URL
currentParams.forEach((value, key) => {
url.searchParams.set(key, value);
});
return url.toString();
}
function addFiltersToPage() {
const searchResults = document.getElementById('search');
if (!searchResults) return;
const sidebar = document.createElement('div');
sidebar.style.cssText = `
position: fixed;
top: 12px;
left: 10px;
width: 160px;
z-index: 1000;
`;
sidebar.appendChild(createFilterSection(locale.languageSection, languageFilters));
sidebar.appendChild(createFilterSection(locale.timeSection, timeFilters, true, window.location.href.includes('&tbs=')));
sidebar.appendChild(createAdvancedSearchLink());
document.body.appendChild(sidebar);
}
const observer = new MutationObserver((mutations, obs) => {
const searchResults = document.getElementById('search');
if (searchResults) {
addFiltersToPage();
obs.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
})();