Greasy Fork

Greasy Fork is available in English.

Google Search Sidebar

Add a sidebar on Google Search for easier information filtering

当前为 2025-02-21 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Google Search Sidebar
// @namespace    http://tampermonkey.net/
// @version      2.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
// @license      MIT
// ==/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年',
                years3: '過去3年'
            }
        },
        'ja': {
            languageSection: ' 言語フィルター …',
            timeSection: ' 期間フィルター',
            advancedSearch: '検索オプション',
            languages: {
                any: '言語指定なし',
                zhTW: '繁体中国語で検索',
                zh: '中国語で検索',
                ja: '日本語で検索',
                en: '英語で検索'
            },
            times: {
                any: '期間指定なし',
                hour: '1時間以内',
                day: '24時間以内',
                week: '1週間以内',
                month: '1か月以内',
                months3: '3か月以内',
                year: '1年以内',
                years3: '3年以内'
            }
        },
        'en': {
            languageSection: 'Language Filter …',
            timeSection: 'Time Filter',
            advancedSearch: 'Advanced Search',
            languages: {
                any: 'Any Language',
                zhTW: 'Traditional Chinese',
                zh: 'All Chinese',
                ja: 'Japanese',
                en: 'English'
            },
            times: {
                any: 'Any Time',
                hour: 'Past Hour',
                day: 'Past 24 Hours',
                week: 'Past Week',
                month: 'Past Month',
                months3: 'Past 3 Months',
                year: 'Past Year',
                years3: 'Past 3 Years'
            }
        }
    };

    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: '&lr=' },
        { 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: '&tbs=' },
        { 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' },
        { text: locale.times.years3, param: '&tbs=qdr:y3' }
    ];


// advanced search

    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: 14px;
            padding: 5px;
            margin-top: 0px;
            background: #ecedef;
            border-radius: 8px;
            text-align: center;
            transition: background-color 0.2s;
        `;
        link.addEventListener('mouseover', () => {
            link.style.backgroundColor = '#1a73e8';
            link.style.color = '#FFFFFF';
        });
        link.addEventListener('mouseout', () => {
            link.style.backgroundColor = '#ecedef';
            link.style.color = '#1a73e8';
        });
        return link;
    }

    function createFilterSection(title, filters, collapsible = false, defaultExpanded = false) {
        const section = document.createElement('div');
        section.style.cssText = `
            margin: 3px 0;
            padding: 3px;
            background: #ecedef;
            border-radius: 8px;
        `;

        const titleElement = document.createElement('h3');
        titleElement.textContent = title;
        titleElement.style.cssText = `
            margin: 0 0 2px 0;
            padding-top: 5px;
            font-size: 14px;
            color: #202124;
            font-weight: 500;
            cursor: ${collapsible ? 'pointer' : 'default'};
            text-align: center;
        `;
        section.appendChild(titleElement);

        const linkContainer = document.createElement('div');
        linkContainer.style.cssText = `
            display: flex;
            flex-direction: column;
            gap: 3px;
            ${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: 2px 2px;
                border-radius: 8px;
                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 = '#1a73e8';
                link.style.color = '#FFFFFF';
            });
            link.addEventListener('mouseout', () => {
                if (urlParams.get('lr') !== filter.param.replace('&lr=', '') &&
                    urlParams.get('tbs') !== filter.param.replace('&tbs=', '')) {
                    link.style.color = '#888888';
                    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);

        // Preserve existing parameters
        const existingParams = new URLSearchParams(url.search);

        // Remove existing language or time parameters if "any" is selected
        if (param.includes('lr=')) {
            existingParams.delete('lr');
        }
        if (param.includes('tbs=')) {
            existingParams.delete('tbs');
        }

        // Add new parameters
        if (param) {
            const cleanParam = param.startsWith('&') ? param.substring(1) : param;
            const [key, value] = cleanParam.split('=');
            if (value) {
                existingParams.set(key, value);
            } else {
                existingParams.delete(key);
            }
        }

        url.search = existingParams.toString();
        return url.toString();
    }

    function getAdvancedSearchUrl() {
        const url = new URL('https://www.google.com/advanced_search');
        const currentParams = new URLSearchParams(window.location.search);
        return url.toString();
    }

// sidebar position

    const observer = new MutationObserver((mutations, obs) => {
        const searchResults = document.getElementById('search');
        if (searchResults) {
            const sidebar = document.createElement('div');
            sidebar.style.cssText = `
                position: fixed;
                top: 23px;
                left: 7px;
                width: 160px;
                z-index: 1000;
            `;

            sidebar.appendChild(createFilterSection(locale.languageSection, languageFilters, true, window.location.href.includes('&lr=')));
            sidebar.appendChild(createFilterSection(locale.timeSection, timeFilters, true, window.location.href.includes('&tbs=')));
            sidebar.appendChild(createAdvancedSearchLink());

            document.body.appendChild(sidebar);
            obs.disconnect();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();

(function() {
    'use strict';

    function createScrollButton(innerHTML, onClick) {
        var button = document.createElement('div');
        button.innerHTML = innerHTML;
        button.style.width = '30px';
        button.style.height = '34px';
        button.style.cursor = 'pointer';
        button.style.backgroundColor = 'transparent';
        button.style.color = 'grey';
        button.style.textAlign = 'center';
        button.style.fontSize = '30px';
        button.style.borderRadius = '0px';
        button.style.userSelect = 'none';
        button.style.marginBottom = '0px';
        button.style.opacity = '0.2';
        button.style.transition = 'all 0s';
        button.style.filter = 'drop-shadow(0 0 0 grey)';
        button.addEventListener('click', onClick);
        button.addEventListener('mouseover', function() {
            button.style.opacity = '1';
            button.style.filter = 'drop-shadow(0 0 2px grey)';
            button.style.color = '#FFF';
        });
        button.addEventListener('mouseout', function() {
            button.style.opacity = '0.1';
            button.style.filter = 'drop-shadow(0 0 0 grey)';
            button.style.color = 'grey';
        });
        return button;
    }

// scroll bar

    function addScrollButtonsToSidebar() {
        const sidebar = document.querySelector('div[style*="position: fixed"]');
        if (!sidebar) return;
        const scrollContainer = document.createElement('div');
        scrollContainer.style.cssText = `
            position: absolute;
            top: 3px;
            left: 130px;
            display: flex;
            flex-direction: column;
            align-items: center;
            margin-top: 0px;
        `;

        scrollContainer.appendChild(createScrollButton('⇑', function() {
            window.scrollTo({ top: 0, behavior: 'instant' });
        }));
        scrollContainer.appendChild(createScrollButton('⇡', function() {
            window.scrollBy({ top: -window.innerHeight, behavior: 'instant' });
        }));
        scrollContainer.appendChild(createScrollButton('⇣', function() {
            window.scrollBy({ top: window.innerHeight, behavior: 'instant' });
        }));
        scrollContainer.appendChild(createScrollButton('⇓', function() {
            window.scrollTo({ top: document.body.scrollHeight, behavior: 'instant' });
        }));

        sidebar.appendChild(scrollContainer);
    }

    const observer = new MutationObserver((mutations, obs) => {
        const sidebar = document.querySelector('div[style*="position: fixed"]');
        if (sidebar) {
            addScrollButtonsToSidebar();
            obs.disconnect();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();