Simple Search Engines

简洁的适配国内地区的搜索切换脚本.

目前為 2024-06-11 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Simple Search Engines
// @namespace    https://www.iklfy.com
// @version      0.2.3
// @description  简洁的适配国内地区的搜索切换脚本.
// @author       Ancient
// @match        *://cn.bing.com/search*
// @match        *://www.baidu.com/s*
// @match        *://www.yandex.com/search*
// @match        *://www.sogou.com/web*
// @match        *://www.zhihu.com/search*
// @match        *://so.csdn.net/so/search*
// @grant        none
// @license      MIT
// ==/UserScript==
/**
 * 搜索引擎配置管理器类,用于根据浏览器类型动态调整搜索引擎配置,
 * 并在页面上创建一个搜索引擎切换容器,提升用户体验。
 */
class SearchEngineManager
{
    urlMapsConfig = {};

    /**
     * 构造函数,初始化搜索引擎配置。
     * @param urlMapsConfig
     */
    constructor(urlMapsConfig)
    {
        this.urlMapsConfig = urlMapsConfig;
    }

    /**
     * 从URL查询字符串中提取指定变量的值。
     *
     * @param {string} variable - 要提取的查询参数名。
     * @return {string|null} - 查询参数的值,若不存在则返回null。
     */
    getQueryVariable(variable)
    {
        let query = window.location.search.substring(1);
        if (!query) {
            return null;
        }
        const pairs = query.split('&');
        for (const pair of pairs) {
            const [key, value] = pair.split('=');
            if (key === variable) {
                return decodeURIComponent(value);
            }
        }
        return null;
    }

    /**
     * 根据当前URL获取关键词。
     *
     * @return {string} - 当前搜索的关键词。
     */
    getKeywords()
    {
        for (const item of this.urlMapsConfig) {
            if (item.testUrl.test(window.location.href)) {
                return this.getQueryVariable(item.keyName);
            }
        }
        return '';
    }

    /**
     * 检测是否为Firefox浏览器并相应调整配置。
     */
    checkAndAdjustForFirefox()
    {
        if (/Firefox/.test(navigator.userAgent)) {
            console.info('[ Firefox ] 🚀');
            if (this.urlMapsConfig.length > 0) {
                this.urlMapsConfig[0].searchUrl = 'https://www.baidu.com/baidu?wd=';
                this.urlMapsConfig[0].testUrl   = /https:\/\/www\.baidu\.com\/baidu.*/;
            }
        }
    }

    /**
     * 添加样式
     */
    addStyleToHead()
    {
        // 检查是否已存在该样式,如果不存在再进行添加
        if (!document.getElementById('search-container-style')) {
            const style       = document.createElement('style');
            style.id          = 'search-container-style';
            // 将样式内容赋值给style节点的textContent,代替innerHTML,提高安全性
            style.textContent = `
                #search-container{width:80px;background-color:#f1f6f9d9;z-index:99999;position:fixed;display:flex;align-items:center;justify-content:center;padding:10px 0;top:150px;left:50px;border-radius:10px}
                #search-container ul{padding:initial;margin:initial}
                #search-container li.title{font-weight:700;user-select:none}
                #search-container li{display:block;margin:8px 0;text-align:center}
                #search-container a{color:#24578f;display:block}
            `;
            // 将style节点添加到head中
            document.getElementsByTagName('head')[0].appendChild(style);
        }
    }

    /**
     * 添加容器
     */
    createSearchContainer()
    {
        this.checkAndAdjustForFirefox();
        // div#search-container
        const container = document.createElement('div');
        container.id    = 'search-container';
        document.body.insertBefore(container, document.body.firstChild);
        //document.body.insertAdjacentElement('afterbegin', container);
        // ul
        const ul = document.createElement('ul');
        container.appendChild(ul);
        // li.title
        let titleLi         = document.createElement('li');
        titleLi.textContent = 'Engine';
        titleLi.className   = 'title';
        ul.appendChild(titleLi);
        // 优化DOM操作
        const fragment = document.createDocumentFragment();
        // 搜索列表
        this.urlMapsConfig.forEach(item => {
            // li > a
            const li      = document.createElement('li');
            const a       = document.createElement('a');
            a.textContent = item.name;
            a.className   = 'search-engine-a';
            a.href        = `${item.searchUrl}${this.getKeywords()}`;
            // ul > li > a
            li.appendChild(a);
            fragment.appendChild(li);
        });
        ul.appendChild(fragment);
    }

    /**
     * 初始化并运行搜索容器的创建流程。
     */
    initialize()
    {
        this.addStyleToHead();
        this.createSearchContainer();
    }
}

(function ()
{
    'use strict';
    /**
     * 用于配置URL映射的对象。每个映射包含名称、键名、搜索URL字符串和测试URL的正则表达式。
     *
     * @typedef {Object} urlMapsConfig
     * @property {string} name - 映射的名称。不能为空。
     * @property {string} keyName - 映射的键名。不能为空。
     * @property {string} searchUrl - 用于搜索的URL字符串。必须是合法的URL格式。
     * @property {RegExp} testUrl - 用于测试URL是否匹配的正则表达式对象。必须是有效的正则表达式。
     */
    const urlMapsConfig = [
        {
            name: 'Bing', searchUrl: 'https://cn.bing.com/search?q=', keyName: 'q', testUrl: /https:\/\/cn.bing.com\/search.*/
        }, {
            name: '百度', searchUrl: 'https://www.baidu.com/s?wd=', keyName: 'wd', testUrl: /https:\/\/www.baidu.com\/s.*/
        }, {
            name: 'Yandex', searchUrl: 'https://www.yandex.com/search/?text=', keyName: 'text', testUrl: /https:\/\/www.yandex.com\/search.*/
        }, {
            name: '搜狗', searchUrl: 'https://www.sogou.com/web?query=', keyName: 'query', testUrl: /https:\/\/www.sogou.com\/web.*/
        }, {
            name: '知乎', searchUrl: 'https://www.zhihu.com/search?q=', keyName: 'q', testUrl: /https:\/\/www.zhihu.com\/search.*/
        }, {
            name: 'CSDN', searchUrl: 'https://so.csdn.net/so/search?q=', keyName: 'q', testUrl: /https:\/\/so.csdn.net\/so\/search.*/
        }
    ];
    // 创建实例并运行
    const manager       = new SearchEngineManager(urlMapsConfig);
    // 监听绑定
    window.addEventListener('load', () =>
    {
        try {
            manager.initialize();
        } catch (error) {
            console.error('Error:', error);
        }
    });
})();