Simple Search Engines

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

当前为 2024-06-11 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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);
        }
    });
})();