Instant Search Switcher

Changes search engine from one site to another without deleting search query. Draggable UI.

// ==UserScript==
// @name         Instant Search Switcher
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Changes search engine from one site to another without deleting search query. Draggable UI.
// @author       Faisal Bhuiyan
// @match              *://*.bing.com/*search*
// @match              *://*.bing.com/chat*
// @match        https://www.google.com/search*
// @match        https://www.qwant.com/*
// @match              *://yandex.com/*search*
// @match              *://search.brave.com/*search*
// @match              *://duckduckgo.com/*
// @license      MIT
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==
 
(function () {
    'use strict';
 
    const searchEngines = [
        {
            name: 'Google',
            url: 'https://www.google.com/search',
            param: 'q'
        },
        {
            name: 'Bing',
            url: 'https://www.bing.com/search',
            param: 'q'
        },
        {
            name: 'Copilot',
            url: 'https://www.bing.com/chat',
            param: 'q',
            additionalParams: '&sendquery=1&FORM=SCCODX'
        },
        {
            name: 'Qwant',
            url: 'https://www.qwant.com/',
            param: 'q'
        },
        {
            name: 'Brave',
            url: 'https://search.brave.com/search',
            param: 'q'
        },
        {
            name: 'Yandex',
            url: 'https://yandex.com/search',
            param: 'text'
        },
        {
            name: 'Perplexity',
            url: 'https://www.perplexity.ai/search',
            param: 'q'
        },
        {
            name: 'Phind',
            url: 'https://www.phind.com/search',
            param: 'q'
        },
        {
            name: 'Morphic',
            url: 'https://morphic.sh/search',
            param: 'q'
        },
        {
            name: 'DuckDuckGo',
            url: 'https://duckduckgo.com/',
            param: 'q'
        }
    ];
    
    // Default position - define this as a constant
    const defaultPosition = { top: '20px', right: '5rem', left: 'auto' };
    
    // Try to get saved position or use default
    let position;
    try {
        const savedPosition = GM_getValue('switcherPosition');
        // Only use saved position if it exists and has been manually set
        position = (savedPosition && GM_getValue('positionModified')) ? savedPosition : defaultPosition;
    } catch (e) {
        // If GM_getValue is not available, use defaultPosition
        position = defaultPosition;
    }
 
    // Create container for draggable functionality
    const container = document.createElement('div');
    container.style.position = 'fixed';
    container.style.top = position.top;
    container.style.right = position.right;
    container.style.left = position.left;
    container.style.zIndex = '9999';
    container.style.cursor = 'move';
    container.style.userSelect = 'none';
    container.style.touchAction = 'none';
    container.style.display = 'flex';
    container.style.flexDirection = 'row';
    container.style.alignItems = 'center';
    container.style.padding = '4px';
    container.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
    container.style.borderRadius = '4px';
    container.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)';
 
    // Create a handle for dragging
    const dragHandle = document.createElement('div');
    dragHandle.innerHTML = '⋮⋮'; // vertical dots as drag indicator
    dragHandle.style.marginRight = '5px';
    dragHandle.style.cursor = 'move';
    dragHandle.style.color = '#555';
    dragHandle.style.fontSize = '16px';
    dragHandle.style.paddingRight = '5px';
    dragHandle.title = 'Drag to move';
    
    // Create reset button
    const resetButton = document.createElement('div');
    resetButton.innerHTML = '↺'; // reset icon
    resetButton.style.marginLeft = '5px';
    resetButton.style.cursor = 'pointer';
    resetButton.style.color = '#555';
    resetButton.style.fontSize = '16px';
    resetButton.style.padding = '0 5px';
    resetButton.title = 'Reset position';
    resetButton.style.display = 'flex';
    resetButton.style.alignItems = 'center';
    resetButton.style.justifyContent = 'center';
 
    // Create the floating select box
    const selectBox = document.createElement('select');
    selectBox.style.fontSize = '16px';
    selectBox.style.padding = '5px';
    selectBox.style.borderRadius = '4px';
    selectBox.style.backgroundColor = '#fff';
    selectBox.style.color = '#000'; // Force black text
    selectBox.style.border = '1px solid #ccc';
    selectBox.style.outline = 'none';
    selectBox.style.cursor = 'pointer';
 
    // Add CSS to ensure text remains visible in both light and dark themes
    const style = document.createElement('style');
    style.textContent = `
        .search-switcher-select {
            color: #000 !important;
            -webkit-text-fill-color: #000 !important;
        }
        .search-switcher-select option {
            background-color: #fff !important;
            color: #000 !important;
            -webkit-text-fill-color: #000 !important;
        }
    `;
    document.head.appendChild(style);
 
    selectBox.className = 'search-switcher-select';
 
    // Add an empty option as the first element
    const emptyOption = document.createElement('option');
    emptyOption.value = '';
    emptyOption.textContent = 'Select';
    emptyOption.disabled = true;
    emptyOption.selected = true;
    selectBox.appendChild(emptyOption);
 
    // Add search engines to the select box
    searchEngines.forEach(engine => {
        const option = document.createElement('option');
        option.value = engine.url;
        option.textContent = engine.name;
        selectBox.appendChild(option);
    });
 
    // Add elements to container
    container.appendChild(dragHandle);
    container.appendChild(selectBox);
    container.appendChild(resetButton);
 
    // Append the container to the body
    document.body.appendChild(container);
 
    // Reset button functionality
    resetButton.addEventListener('click', (e) => {
        e.stopPropagation();
        
        // Apply default position
        container.style.top = defaultPosition.top;
        container.style.right = defaultPosition.right;
        container.style.left = defaultPosition.left;
        
        // Save the default settings
        try {
            GM_setValue('switcherPosition', defaultPosition);
            GM_setValue('positionModified', false);
        } catch (e) {
            // Silently fail if GM_setValue is not available
        }
    });
 
    // Detect changes to the select box
    selectBox.addEventListener('change', () => {
        const selectedEngine = searchEngines[selectBox.selectedIndex - 1];
        // Try to get query parameter from either 'q' or 'text' depending on current search engine
        const currentQuery = new URLSearchParams(window.location.search).get('q') ||
                           new URLSearchParams(window.location.search).get('text');
 
        if (currentQuery && selectedEngine) {
            const additionalParams = selectedEngine.additionalParams || '';
            window.location.href = `${selectedEngine.url}?${selectedEngine.param}=${encodeURIComponent(currentQuery)}${additionalParams}`;
        }
    });
 
    // Make the container draggable
    let isDragging = false;
    let dragOffsetX, dragOffsetY;
 
    // Function to start dragging
    function startDrag(clientX, clientY) {
        isDragging = true;
        
        // Calculate the offset of the mouse within the container
        const rect = container.getBoundingClientRect();
        dragOffsetX = clientX - rect.left;
        dragOffsetY = clientY - rect.top;
        
        // Prevent text selection during drag
        document.body.style.userSelect = 'none';
    }
 
    // Function to handle dragging
    function drag(clientX, clientY) {
        if (!isDragging) return;
        
        // Calculate new position based on mouse/touch position and offset
        const newLeft = clientX - dragOffsetX;
        const newTop = clientY - dragOffsetY;
        
        // Ensure the element stays within viewport bounds
        const maxX = window.innerWidth - container.offsetWidth;
        const maxY = window.innerHeight - container.offsetHeight;
        
        const boundedLeft = Math.max(0, Math.min(newLeft, maxX));
        const boundedTop = Math.max(0, Math.min(newTop, maxY));
        
        // Set new position
        container.style.left = boundedLeft + 'px';
        container.style.top = boundedTop + 'px';
        container.style.right = 'auto'; // Clear right positioning to avoid conflicts
    }
 
    // Function to end dragging
    function endDrag() {
        if (isDragging) {
            isDragging = false;
            document.body.style.userSelect = '';
            
            // Save the new position and mark as modified
            try {
                GM_setValue('switcherPosition', {
                    top: container.style.top,
                    right: 'auto',
                    left: container.style.left
                });
                GM_setValue('positionModified', true);
            } catch (e) {
                // Silently fail if GM_setValue is not available
            }
        }
    }
 
    // Mouse event listeners
    dragHandle.addEventListener('mousedown', (e) => {
        e.preventDefault();
        startDrag(e.clientX, e.clientY);
    });
 
    document.addEventListener('mousemove', (e) => {
        drag(e.clientX, e.clientY);
    });
 
    document.addEventListener('mouseup', endDrag);
 
    // Touch event listeners
    dragHandle.addEventListener('touchstart', (e) => {
        if (e.touches.length === 1) {
            e.preventDefault();
            const touch = e.touches[0];
            startDrag(touch.clientX, touch.clientY);
        }
    });
 
    document.addEventListener('touchmove', (e) => {
        if (isDragging && e.touches.length === 1) {
            e.preventDefault();
            const touch = e.touches[0];
            drag(touch.clientX, touch.clientY);
        }
    });
 
    document.addEventListener('touchend', endDrag);
    document.addEventListener('touchcancel', endDrag);
 
    // Prevent selectbox from triggering drag
    selectBox.addEventListener('mousedown', (e) => {
        e.stopPropagation();
    });
 
    selectBox.addEventListener('touchstart', (e) => {
        e.stopPropagation();
    });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址